如果让你展示国内各省市疫情数据你会怎么做

2020 年初始至今,冠状病毒肆虐,好在国内疫情已经逐步减少,武汉也解封了,而国外疫情却日益严重,不容乐观。

做个可视化地图模拟展示一下疫情

我们可以看到,各大 APP 都有实时显示全国乃至世界的疫情数据的地图表,那么它们是如何实现的呢?

让我们以中国为例,实现一下展示效果。由于平时用 Vue 较多,可视化框架之前在项目中用过 echarts,就用这两个来演示。(注:本次数据都是随机生成,非官方真实数据)

用 vue-cli 快速搭建

vue create vue-echarts-map-china

引入 echarts

yarn add echarts -D 

如何展示中国各省数据

ProvinceChart.vue

<template>
  <div class="chart" ref="chart" style="width: 100%; height: 800px"></div>
</template>

我们首先需要给 echarts 一个容器

引用 js/json 地图数据

echats 内置了 china.js 和 china.json 及其省市地图包,china.js 引入即可使用 china.json 需要注册到 echarts,两种方式任选

import echarts from "echarts";
import "echarts/theme/macarons"; // echarts 主题

// 1. china.js 直接使用
import china from "echarts/map/js/china";

// 2. china.json 需注册
import china from "echarts/map/json/china";
echarts.registerMap("china", china);

主要结构

export default {
  name: "chart",
  data() {
    return {
      chart: null
    };
  },
  mounted() {
    if (!this.chart) {
      this.drawChinaMap();
    }
  },
  beforeDestroy() {
    if (!this.chart) {
      return;
    }
    this.chart.dispose();
    this.chart = null;
  },
  methods: {
    drawChinaMap(){
      ...
    }
  }
};

drawChinaMap 方法详解

首先找到我们的容器用 echarts.init() 初始化 echarts

this.chart = echarts.init(document.querySelector(".chart"), "macarons");
this.chart.setOption({
  ...
})

通过 setOption 传入我们需要设定的选项参数

图表类型及参数

最主要的是设置图表类型了

series: [
  {
    name: "数据",
    type: "map",
    mapType: "china", // 对应我们注册好的 "china"
    roam: true, // 是否可缩放
    label: { // 文本标签
      normal: {
        show: true //省份名称
      },
      emphasis: {
        show: false
      }
    },
    // 地图默认样式
    itemStyle: {
      normal: {
        show: true,
        areaColor: "#CECECE",
        borderColor: "#FCFCFC",
        borderWidth: "1"
      },
      emphasis: {
        show: true,
        areaColor: "#C8A5DF"
      }
    },
    data: [
      ...
    ]
  }
]

最重要的是 34 个省份的数据,这里我们模拟一下(注意格式和 name 是固定的)

data: [
  { name: "北京", value: Math.round(Math.random() * 1000) },
  { name: "天津", value: Math.round(Math.random() * 1000) },
  { name: "上海", value: Math.round(Math.random() * 1000) },
  { name: "重庆", value: Math.round(Math.random() * 1000) },
  { name: "河北", value: Math.round(Math.random() * 1000) },
  { name: "河南", value: Math.round(Math.random() * 1000) },
  { name: "云南", value: Math.round(Math.random() * 1000) },
  { name: "辽宁", value: Math.round(Math.random() * 1000) },
  { name: "黑龙江", value: Math.round(Math.random() * 1000) },
  { name: "湖南", value: Math.round(Math.random() * 1000) },
  { name: "安徽", value: Math.round(Math.random() * 1000) },
  { name: "山东", value: Math.round(Math.random() * 1000) },
  { name: "新疆", value: Math.round(Math.random() * 1000) },
  { name: "江苏", value: Math.round(Math.random() * 1000) },
  { name: "浙江", value: Math.round(Math.random() * 1000) },
  { name: "江西", value: Math.round(Math.random() * 1000) },
  { name: "湖北", value: 9999 },
  { name: "广西", value: Math.round(Math.random() * 1000) },
  { name: "甘肃", value: Math.round(Math.random() * 1000) },
  { name: "山西", value: Math.round(Math.random() * 1000) },
  { name: "内蒙古", value: Math.round(Math.random() * 1000) },
  { name: "陕西", value: Math.round(Math.random() * 1000) },
  { name: "吉林", value: Math.round(Math.random() * 1000) },
  { name: "福建", value: Math.round(Math.random() * 1000) },
  { name: "贵州", value: Math.round(Math.random() * 1000) },
  { name: "广东", value: Math.round(Math.random() * 1000) },
  { name: "青海", value: Math.round(Math.random() * 1000) },
  { name: "西藏", value: Math.round(Math.random() * 1000) },
  { name: "四川", value: Math.round(Math.random() * 1000) },
  { name: "宁夏", value: Math.round(Math.random() * 1000) },
  { name: "海南", value: Math.round(Math.random() * 1000) },
  { name: "台湾", value: Math.round(Math.random() * 1000) },
  { name: "香港", value: Math.round(Math.random() * 1000) },
  { name: "澳门", value: Math.round(Math.random() * 1000) }
] //数据

其他选项设置

backgroundColor: "#FFFFFF",
title: {
  text: "全国省市地图大数据",
  subtext: "虚构数据",
  x: "center"
},
tooltip: {
  trigger: "item"
},
toolbox: { // 右侧复位下载
  show: true,
  orient: "vertical",
  right: "20",
  top: "center",
  feature: {
    mark: { show: true },
    restore: { show: true },
    saveAsImage: { show: true }
  }
},

// 左侧小导航图标
visualMap: {
  show: true,
  x: "left",
  y: "center",
  splitList: [
    { start: 10000 },
    { start: 1000, end: 9999 },
    { start: 500, end: 999 },
    { start: 100, end: 499 },
    { start: 10, end: 99 },
    { start: 0, end: 9 }
  ],
  color: [
    "#a50026",
    "#d73027",
    "#f46d43",
    "#fdae61",
    "#fee090",
    "#ffffbf",
    "#f0f0f0"
  ]
},

效果如下:

如何展示中国各市数据

如果要展示各市单独数据较为简单,只需要把地图和对应数据变成省即可

import hubei from "echarts/map/json/province/hubei";
echarts.registerMap("hubei", hubei);

series:[
  ...
  mapType: "hubei",
  ...
]

你还可以做到点击省切换成市的效果,网上也有相应教程,实现方法也简单,这里不作详细介绍。

但是我想要看整个中国地图下的市的疫情情况怎么办?

合并省市地图

由于官方只提供了中国地图和省市分开的地图 json 或 js 包,而没有合并在一起的,我们没有现成的数据。

然而我注意到,china 和各省的 json 包只是 features 不同,如下图所示:

china.features = [...new Set([...china.features, ...hubei.features])];

最终,这种方法被我放弃了,原因如下:

  1. 视觉效果差:

如下图,有字根本看不清,没字也会很密集

  1. 渲染效果差:

目前只实现了一个省,可预见的是加上所有省份数据量很大(features 长度最终会是 533 ),渲染会卡顿

  1. 有引入延迟问题:

由于需要将 34 个省 JSON 包分别引入后合并到 china 内部,通常 echarts 组件已经渲染完毕

使用中国地图配合市散点图

我找到官方提供的scatter-visualMap-piecewise示例,感觉这种方式也能满足我的要求,且呈现效果更好

选项变化

与原选项主要区别是 series 使用 scatter 散点类型,同时引入 geo 地理坐标系组件

geo: {
  map: "china",
  roam: true,
  itemStyle: {
    // 定义样式
    normal: {
      // 普通状态下的样式
      areaColor: "#323c48",
      borderColor: "#111"
    },
    emphasis: {
      // 高亮状态下的样式
      areaColor: "#2a333d"
    }
  }
},
series: [
  {
    name: "确诊人数",
    symbolSize: 10, // 点坐标大小
    type: "scatter",
    data: this.cityData,
    coordinateSystem: "geo"
  }
]

其数据格式如下

// this.cityData
{
  ...
  { name: "武汉", value: [114.31, 30.52, 8888 }], // { name: name, value: [x, y, value}],
  { name: "大庆", value: [125.03, 46.58, Math.round(Math.random() * 100) }]
}

示例小问题

需要注意的是,官方示例中 tooltip 的悬浮数据是有问题的,它不是 pm2.5 的 value,而是极坐标的 Y 的值

想要其展示正确的数据需要在 tooltip 设置 formatter:以回调函数格式设置数据为 data 的第三个数据

tooltip: {
  trigger: "item",
  formatter: params => {
    return (
      params.seriesName + "<br/>" + params.name + ":" + params.data.value[2]
    );
  }
}

另外,示例中 geo 数据也是不完整的,可通过 datav 获取完整中国 geo 数据。

最终效果如下:

总结

至此,我们完成了国内各省市疫情模拟数据的可视化,如果想要展示世界数据也是类似,只需找到相应地图包替换,并处理对应数据即可。echarts 提供了完整的文档,按需查找对应选项即可完成相应需求,当然还有很多不错的库和方法等我们去探究。

如需查看完整代码:请点这里

看到这里了,不点个赞吗😄!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容