中国疫情地图相比世界疫情地图承载更多功能,因此其技术实现的难度也更高,相比世界地图echarts的绘制,中国疫情地图包含大量同步异步函数的使用,同时包含下钻(点击省份进入各省地图)和返回(回到中国地图),以及一些预想好的点击事件,其难度相对较高。
中国地图并非使用像世界地图在echarts中直接调用。而是采用自定义地图,首先找到自定义的地图数据(json格式),包括中国地图以及各省份地图:
数字代表各省的adcode前缀,如北京adcode为110000.
一开始我采取的方法是在页面加载时将json文件全部引入,下钻时直接调用,在开发环境中这一方法并无大碍,但在服务器部署时,我发现这会导致网站在前期非常卡顿(由于在下载json数据),于是在优化过程中改为了按需引入json文件。
设计过程如下:
currentName:'', // 当前地图的名字
currentLevel:1, // 当前地图层级,1代表中国看省份,2代表各省份看市,一共两个层级
currentJson:null, // 针对当前地图需要的json数据
chart:null, // 地图
options:{}, // 地图的设置
首先在vue的数据中声明如上数据,currentName代表当前地图的名字(中国、山东等);currentLevel代表当前地图层级,共两层,1代表中国地图,2代表各省份地图。currentJson代表当前地图所使用的自定义地图json数据,chart是地图对象,options代表地图的设置。
我所设想的下钻思路是:
首先绘制中国地图,点击事件中触发goDown()函数,进入下一层级及省份地图,将文件全局声明的options调整为对应省份的json文件,重新绘制地图。
这其中有若干异步问题,在函数中我们具体分析:
明确了思路即可编写对应函数,首先确定一些共用的地图设置,编为初始化options:
initOption(){
this.options={
grid:{
width:'100%',
height:'100%',
left:'0%',
bottom:'0%',
containLabel:true
},
/*geo: {
map: 'china',
roam: true,
label: {
emphasis: {
show: true
},
}
},*/
tooltip:{
trigger:'item',
formatter:function(val){
if(val.data==null) return;
return val.name+': '+val.value
}
},
visualMap:{
min:0,
max:10000,
text:['max','min'],
realtime:false,
calcuable:true,
color:['#ff4500','#fffafa'],
},
series:[
{
type:'map',
map:'',
//roam:false,
scale:true,
//data:data,
label:{
show:true
},
zoom:1.2,
itemStyle:{
borderWidth:0.5,
borderColor:'#000',
borderType:'solid'
},
emphasis:{
label:{
show:true,
color:'#fff'
},
itemStyle:{
areaColor:'#FF6347',
}
},
//data:data
}
]
}
},
之后编写绘制地图的函数,这个函数会在页面创始时绘制,同时每次变更层级时再次调用。
async drawChart(){
if(this.chart){
if(this.currentLevel==1){
this.btn_flag=true
}else {
this.btn_flag=false
}
//已经存在chart了。存在于返回上一层级或者进入下一层级
this.chart.setOption(this.options)
//每次重置地图时,重置点击事件,通过现有层级判断点击的事件
this.chart.off('click')
if(this.currentLevel==1){
this.chart.on('click',(params)=>{
if(params.componentSubType=='map'){
//console.log(params.name)
this.$emit('mapClick',params)
this.provinceName=params.name
this.goDown(params.name)
}
})
}else{
this.chart.on('click',(params)=>{
if(params.componentSubType=='map'){
//this.citydata=[]
console.log(this.citydata)
this.getCityData(params.name)
this.drawer=true
}
})
}
this.resize()
/*var _this=this
window.addEventListener('resize',function() {
_this.chart.resize()
})*/
}else{
//第一次打开页面
const map1=document.getElementById('chart2')
this.currentName='中国'
this.currentLevel=1
this.currentJson=chinaMapJson
echarts.registerMap('china',chinaMapJson)
this.initOption()
this.options.series[0].map='china'
//console.log(this.options) //该语句执行了,而且name属性确实为china,但是不显示图像,why
//数据的插入
this.options.series[0].data=this.chinaChartdata
this.chart=echarts.init(map1)
this.chart.setOption(this.options)
this.resize()
console.log(this.chart) //chart不是空的
/*var _this=this
window.addEventListener('resize',function() {
_this.chart.resize()
})*/
//点击事件
this.chart.on('click',(params)=>{
if(params.componentSubType=='map'){
console.log(params.name)
this.$emit('mapClick',params)
this.provinceName=params.name
this.goDown(params.name)
}
})
}
},
注意到这个函数声明为async,即异步函数,由于在绘图中存在等待数据的行为,即await,故声明为async。函数逻辑为:首先判断chart是否为空,为空则表明第一次绘图,初始化若干属性,并调用中国json文件的数据进行绘图。若不为空,即下钻或者上卷,这时被设计为只有在goDown()或returnToFrst()函数中才会调用,此时设置已经在对应函数中修改,因此直接传入设置即可。
注意点击事件,在初始化时点击事件为进入下一层级,即goDown()。而在后续绘图时,需要判断是否第一层级,据此来设置点击事件为显示详细数据或进入下一层级。
根据我们现有的json文件名,需要设计一个地区名和文件名映射的函数,结构很简单,switch进行判断:
getMapJson(name){
if (name=='china'){
return chinaMapJson
}else{
switch (name){
/*case '北京':
return beijingMapJson
case '天津':
return tianjinMapJson
case '河北':
return hebeiMapJson
case '湖北':
return hubeiMapJson
case '上海':
return shanghaiMapJson
case '山东':
return shandongMapJson
case '辽宁':
return liaoningMapJson
case '河南':
return henanMapJson
case '黑龙江':
return heilongjiangMapJson
case '吉林':
return jilinMapJson
case '江苏':
return jiangsuMapJson
case '浙江':
return zhejiangMapJson
case '安徽':
return anhuiMapJson
case '内蒙古':
return neimengguMapJson
case '山西':
return shan1xiMapJson
case '陕西':
return shan3xiMapJson
case '甘肃':
return gansuMapJson
case '宁夏':
return ningxiaMapJson
case '新疆':
return xinjiangMapJson
case '广西':
return guangxiMapJson
case '四川':
return sichuanMapJson
case '重庆':
return chongqingMapJson
case '湖南':
return hunanMapJson
case '西藏':
return xizangMapJson
case '江西':
return jiangxiMapJson
case '广东':
return guangdongMapJson
case '福建':
return fujianMapJson
case '云南':
return yunnanMapJson
case '香港':
return xianggangMapJson
case '台湾':
return taiwanMapJson
case '海南':
return hainanMapJson
case '贵州':
return guizhouMapJson
case '青海':
return qinghaiMapJson*/
case '北京':
return '11'
case '天津':
return '12'
case '河北':
return '13'
case '湖北':
return '42'
case '上海':
return '31'
case '山东':
return '37'
case '辽宁':
return '21'
case '河南':
return '41'
case '黑龙江':
return '23'
case '吉林':
return '22'
case '江苏':
return '32'
case '浙江':
return '33'
case '安徽':
return '34'
case '内蒙古':
return '15'
case '山西':
return '14'
case '陕西':
return '61'
case '甘肃':
return '62'
case '宁夏':
return '64'
case '新疆':
return '65'
case '广西':
return '45'
case '四川':
return '51'
case '重庆':
return '50'
case '湖南':
return '43'
case '西藏':
return '54'
case '江西':
return '36'
case '广东':
return '44'
case '福建':
return '35'
case '云南':
return '53'
case '香港':
return '81'
case '台湾':
return '71'
case '海南':
return '46'
case '贵州':
return '52'
case '青海':
return '63'
}
}
},
注意注释掉的部分,这部分为初始版本中一开始将json文件全部引入的方法,直接对应到对应的json文件,goDown()函数调用返回结果直接使用即可。而后续版本改为返回adcode的前缀字符串。
之后我们聚焦goDown()函数:
async goDown(name){
if(this.currentLevel!=1){
return false
}
this.currentLevel=2
const mapname=name
this.currentName=name
//this.currentJson=this.getMapJson(name)
const n1=this.getMapJson(name)
await this.getJsonByname(n1).then(res=>{
echarts.registerMap(this.currentName,res)
})
console.log(this.currentJson)
this.options.series[0].map=this.currentName
//this.loadProvinceData(name)
this.loadProvinceChartData(name)
/*this.chart.on('click',(params)=>{
if(params.componentSubType=='map'){
console.log(params.name)
this.$emit('mapClick',params)
this.drawer=true
}
})*/
},
注意函数中为了保证设置和加载数据在获取对应文件之后,使用了await,而使用await必须在async中,因此声明为async函数。await即等待该方法体执行完(返回promise对象)才执行后续语句。
具体的获取对应文件的函数如下:
getJsonByname(name){
const jsondata=import('../../assets/echarts-mapJson-master/geometryProvince/'+name+'.json')
return jsondata
},
简单的引入。但是需要注意,这里的jsondata实际为一个promise对象,要想获得对象的数据而非其对象本身,需要在then函数中处理,即上面函数中的
await this.getJsonByname(n1).then(res=>{
echarts.registerMap(this.currentName,res)
})
到此,绘图的函数基本解决。编写axios请求后端数据的函数即可:
loadChinaData(){
var _this=this
this.$axios.get('/user/china_data').then(resp=>{
if(resp&&resp.status===200){
_this.chinaChartdata=resp.data
}
}).then(()=>{
this.initChart()
})
},
及
loadProvinceChartData(name){
var _this=this
this.$axios.get('/user/pro_data?pro='+name).then(resp=>{
if(resp&&resp.status===200){
_this.provincedata=resp.data.data
for(var i=0;i<_this.provincedata.length;i++){
var obj={
name:_this.provincedata[i].mapName,
value:_this.provincedata[i].econNum
}
_this.provinceChartdata.push(obj)
_this.options.series[0].data=_this.provinceChartdata
_this.drawChart()
}
}
})
},
效果如下: