动态中国城市疫情地图的实现,主要是参照Echarts官网官方案例的方法:Examples - Apache ECharts ,但是官方给的案例数据都是静态写在Option的Data中的无法根据疫情实时变化而自动更新,所以我在此基础上使用了Jsonp访问疫情数据网站获取各城市实时数据。
先上效果图:
先在views文件夹下创建一个Vue文件Citys.vue,创建一个放地图的容器div。
<template>
<div id="chart-city" style="width: 60vw; height: 85vh"></div>
</template>
下载引入所有需要使用到的包,这里需要用到的是Echarts、Echarts-gl和Echarts-stat,在终端使用npm分别下载引入就好。
import echarts from "echarts";
require("echarts/extension/bmap/bmap");
import "echarts-gl/dist/echarts-gl.min.js";
import "echarts-stat/dist/ecStat.min.js";
import "echarts/dist/echarts.min.js";
import "echarts/dist/extension/dataTool.min.js";
将所有你想要展示的城市的坐标点放在geoCoordMap中,具体形式如下:
城市名:[经度,纬度] (注意经纬顺序不要搞错不然出不来!)
var geoCoordMap = {
荆州: [112.239741, 30.335165],
合肥: [117.17, 31.52],
安庆: [117.02, 30.31],
...
}
引入百度地图:
由于项目需要使用百度地图,所以需要申请api,具体申请可以在这个网页申请百度地图开放平台,使用接口所需要的密钥就是创建应用后的AK,一键复制即可使用。
在src文件夹下创建一个map.js的文件,里面存放接百度地图接口的函数loadBMap(),代码如下:
export function loadBMap(ak) {
return new Promise(function(resolve, reject) {
if (typeof BMap !== 'undefined') {
resolve(BMap)
return true
}
window.onBMapCallback = function() {
resolve(BMap)
}
let script = document.createElement('script')
script.type = 'text/javascript'
script.src =
'http://api.map.baidu.com/api?v=2.0&ak='+ ak +'&__ec_v__=20190126&callback=onBMapCallback'
script.onerror = reject
document.head.appendChild(script)
})
}
在Citys.vue中引用这个函数:
import { loadBMap } from "../map.js";
同时将其添加在mounted()中,在加载百度地图后再对画的图进行初始化:
loadBMap("密钥内容").then(() => {
this.init();
this.getData();
});
其中init函数的内容就是要通过id指定画图的元素,init()代码如下:
init() {
this.myChart = echarts.init(document.getElementById("chart-city"));
},
关于百度地图河流道路城市等样式的调节均可在option的bmap中调节,我设置的bmap属性如下:
bmap: {
center: [104.95, 37.57],
zoom: 5,
roam: true,
mapStyle: {
styleJson: [
{
featureType: "water",
elementType: "all",
stylers: {
color: "#d1d1d1",
},
},
{
featureType: "land",
elementType: "all",
stylers: {
color: "#f3f3f3",
},
},
{
featureType: "railway",
elementType: "all",
stylers: {
visibility: "off",
},
},
{
featureType: "highway",
elementType: "all",
stylers: {
color: "#fdfdfd",
},
},
{
featureType: "highway",
elementType: "labels",
stylers: {
visibility: "off",
},
},
{
featureType: "arterial",
elementType: "geometry",
stylers: {
color: "#fefefe",
},
},
{
featureType: "arterial",
elementType: "geometry.fill",
stylers: {
color: "#fefefe",
},
},
{
featureType: "poi",
elementType: "all",
stylers: {
visibility: "off",
},
},
{
featureType: "green",
elementType: "all",
stylers: {
visibility: "off",
},
},
{
featureType: "subway",
elementType: "all",
stylers: {
visibility: "off",
},
},
{
featureType: "manmade",
elementType: "all",
stylers: {
color: "#d1d1d1",
},
},
{
featureType: "local",
elementType: "all",
stylers: {
color: "#d1d1d1",
},
},
{
featureType: "arterial",
elementType: "labels",
stylers: {
visibility: "off",
},
},
{
featureType: "boundary",
elementType: "all",
stylers: {
color: "#d3d3d3",
},
},
{
featureType: "building",
elementType: "all",
stylers: {
color: "#d1d1d1",
},
},
{
featureType: "label",
elementType: "labels.text.fill",
stylers: {
color: "#999999",
},
},
],
},
},
设置好bmap后在option中的series中添加:
coordinateSystem: "bmap",
动态获取城市疫情数据:
关于获取疫情各城市的数据需要访问处理json数据,所以这里采用的是jsonp,在终端用npm下载引入即可:
import jsonp from "jsonp";
可以发现,对新浪疫情数据网站网址访问返回的json数据的形式是这样的:
所有的城市数据都在list中item.city中,但由于存在地级市所以对list中的item的name也要全部获取到,所以最终getData()的代码如下:
getData() {
jsonp(
"https://interface.sina.cn/news/wap/fymap2020_data.d.json?_=1585397547299",
(err, data) => {
console.log(data.data.list);
var citylist = new Array();
var lists = data.data.list.map((item) => {
var citys = item.city.map((items) => {
return { name: items.name, value: items.conNum };
});
citys.forEach((element) => {
citylist.push(element);
});
return { name: item.name, value: item.value };
});
lists.forEach((element) => {
citylist.push(element);
});
option.series[0].data = convertData(citylist);
this.myChart.setOption(option);
}
);
}
这其中convertData()函数就是echarts官网给的案例中的,其功能是将获取到城市的疫情数据去与想要展现的坐标点的城市名遍历匹配,添加到value字段中,具体代码如下:
var convertData = function (data) {
var res = [];
for (var i = 0; i < data.length; i++) {
var geoCoord = geoCoordMap[data[i].name];
if (geoCoord) {
res.push({
name: data[i].name,
value: geoCoord.concat(data[i].value),
});
}
}
return res;
};
根据以上步骤已经能够显示一张大小点一模一样的疫情地图了,为了让人数的多少更加直观,这里给symbolSize添加了对value中累计确诊人数数值大小判断的函数,代码如下:
symbolSize: function (val) {
if (val[2] > 12000) {
return 35;
}else if (val[2] > 2000 && val[2] <= 12000) {
return 30;
} else if (val[2] > 1133 && val[2] <= 2000) {
return val[2] / 90;
} else if (val[2] > 505 && val[2] <= 1133) {
return val[2] / 70;
} else if (val[2] > 200 && val[2] <= 505) {
return val[2] / 35;
} else if (val[2] > 0 && val[2] <= 200) {
return val[2] / 15;
}
},
关于突出显示top5的城市,这边写的是静态的,将前五的数据放在数组中,然后将其添加给option中series的第二块数据,再在series中设置突出的样式形态。
getData()中的数据附值:
var top5 = new Array(
{ name: "武汉", value: 50363 },
{ name: "荆州", value: 1580 },
{ name: "广州", value: 1133 },
{ name: "上海", value: 1987 },
{ name: "香港", value: 11770 }
);
option.series[1].data = convertData(top5);
series中的样式设置:
{
name: "Top 5",
type: "effectScatter",
coordinateSystem: "bmap",
symbolSize: function (val) {
if (val[2] > 10000) {
return 35;
} else if (val[2] > 1133 && val[2] <= 2000) {
return val[2] / 90;
} else if (val[2] > 505 && val[2] <= 1133) {
return val[2] / 70;
} else if (val[2] > 200 && val[2] <= 505) {
return val[2] / 35;
} else if (val[2] > 0 && val[2] <= 200) {
return val[2] / 15;
}
},
encode: {
value: 2,
},
showEffectOn: "render",
rippleEffect: {
brushType: "stroke",
},
hoverAnimation: true,
label: {
formatter: "{b}",
position: "bottom",
show: true,
},
itemStyle: {
color: "#660208",
shadowBlur: 10,
shadowColor: "#494949",
},
zlevel: 1,
},
最终的代码布局长这样: