前言
最近做一个监控大屏项目,需要通过地图进行全省监控,在此记录制作过程。
获取地图数据
Echarts是一个图表渲染库,需要根据提供的地图数据进行地图渲染。
地图数据遵循GeoJSON数据规范,规范中制定了一系列用于描述地图地理信息的字段,数据格式仍然是JSON。
如何获取地图数据?
- 打开阿里云DataV数据可视化平台 datav.aliyun.com/portal/scho…
- 选择需要的省份地图,点击下载
项目初始化
- 使用vue-cli新建vue工程
vue create map
- 安装echarts包
npm install echarts -S
- 新建Map组件
<template>
<!-- 画布容器 -->
<div id="main"></div>
</template>
<script>
// 引入资源
import * as echarts from 'echarts';
import geoJson from '@/assets/zhejiang.json';
export default {};
</script>
<style scoped>
#main {
width: 800px;
height: 1000px;
margin: 0 auto;
}
</style>
地图绘制
数据准备
export default {
data() {
return {
// 后台提供的数据集
dataset: {
business: {
name: '业务量',
data: [
{ name: '杭州市', value: 13000 },
{ name: '湖州市', value: 1000 },
{ name: '嘉兴市', value: 1000 },
{ name: '绍兴市', value: 2000 },
{ name: '宁波市', value: 3000 },
{ name: '舟山市', value: 500 },
{ name: '金华市', value: 3000 },
{ name: '衢州市', value: 1000 },
{ name: '丽水市', value: 2000 },
{ name: '台州市', value: 200 },
{ name: '温州市', value: 6000 },
],
},
},
};
},
computed: {
// 生成图表数据
series() {
return [
{
name: '业务量',
type: 'map', // 图表类型
map: 'ZJ', // 已注册的地图
label: {
show: true, // 显示标注文本
},
data: this.dataset.business.data,
}
];
},
}
}
开始绘制
export default {
methods: {
mapInit() {
// 画布初始化
var chartDom = document.getElementById('main');
var myChart = echarts.init(chartDom);
// 隐藏数据刷新动画
myChart.hideLoading();
// 注册可用的地图
echarts.registerMap('ZJ', geoJson);
myChart.setOption({
// 提示框组件
tooltip: {
trigger: 'item',
formatter: '{b}<br/>{a}: {c}',
},
// 地理坐标系组件
geo: {
map: 'ZJ',
},
// 视觉映射组件(将数据映射到颜色等视觉元素)
visualMap: {
type: 'piecewise', // 分段型
min: 0, // 最小值
max: 20000, // 最大值
text: ['业务量'], // 两端的文本
splitNumber: 5, // 分段数量
maxOpen: true, // 显示 >max 部分
align: 'left', // 图形在左,文字在右
showLabel: true, // 显示标注文字
orient: 'horizontal', // 水平摆放
inRange: {
color: ['lightskyblue', 'yellow', 'orange'], //图元颜色区间(自动根据数据进行渐变)
},
},
series: this.series, // 图表数据
});
},
},
mounted() {
this.mapInit(); // 初始化加载
},
watch: {
dataset() {
this.mapInit(); // 自动刷新
},
},
};
如何在地图的tooltip上显示多种数据?
目前我们在地图上只能显示一个业务量,如果我们还想在上面显示成功率、平均延时等其它数据该怎么做呢?
完善数据集
首先我们完善数据集,把成功率和平均时延加上。
dataset: {
business: {
name: '业务量',
data: [
{ name: '杭州市', value: 13000 },
{ name: '湖州市', value: 1000 },
{ name: '嘉兴市', value: 1000 },
{ name: '绍兴市', value: 2000 },
{ name: '宁波市', value: 3000 },
{ name: '舟山市', value: 500 },
{ name: '金华市', value: 3000 },
{ name: '衢州市', value: 1000 },
{ name: '丽水市', value: 2000 },
{ name: '台州市', value: 200 },
{ name: '温州市', value: 6000 },
],
},
success: {
name: '成功率',
data: [
{ name: '杭州市', value: 100 },
{ name: '湖州市', value: 100 },
{ name: '嘉兴市', value: 100 },
{ name: '绍兴市', value: 100 },
{ name: '宁波市', value: 100 },
{ name: '舟山市', value: 100 },
{ name: '金华市', value: 100 },
{ name: '衢州市', value: 100 },
{ name: '丽水市', value: 99 },
{ name: '台州市', value: 100 },
{ name: '温州市', value: 100 },
],
},
delay: {
name: '平均时延',
data: [
{ name: '杭州市', value: 50 },
{ name: '湖州市', value: 80 },
{ name: '嘉兴市', value: 50 },
{ name: '绍兴市', value: 200 },
{ name: '宁波市', value: 50 },
{ name: '舟山市', value: 80 },
{ name: '金华市', value: 90 },
{ name: '衢州市', value: 40 },
{ name: '丽水市', value: 70 },
{ name: '台州市', value: 50 },
{ name: '温州市', value: 30 },
],
},
}
添加多个series会自动累加
你可能会想到在series数组中把成功率和平均延时加进去,但这样不会生效。
computed: {
series() {
return [
{
name: '业务量',
type: 'map',
map: 'ZJ',
label: {
show: true,
},
data: this.dataset.business.data,
},
{
name: '成功率',
type: 'map',
map: 'ZJ',
data: this.dataset.success.data,
},
{
name: '平均时延',
type: 'map',
map: 'ZJ',
data: this.dataset.delay.data,
},
];
},
}
现在,杭州市的业务量从原来的13000变成13150了,这显然是三个series中杭州的value求和的结果。没错,echarts默认会在地图组件中对多个series中相同的name的value进行求和展示。
除了求和,echarts也可配置其它的处理方式,比如最大值、最小值等。
通过函数进行tooltip展示
tooltip的formatter除了可以通过占位符来配置内容,还可以接受一个函数来自定义展示文本,既然可以用函数,那我们的问题就可以完美解决。
tooltip: {
trigger: 'item',
formatter: '{b}<br/>{a}: {c}', // 支持占位符和函数两种方式
},
我们先定义一个tooltipFormatter
函数,当地图上选中杭州时,直接从dataset中把杭州的所有指标拿出来进行格式化展示。
methods: {
tooltipFormatter(params) {
// params传递当前点击区域的series数据,部分字段:seriesName 系列名 name 数据项 value 数据值
const { name } = params; // 以选中杭州为例,这里name=杭州
let str = `${name}<br/>`;
// 遍历dataset,将各个维度的数据拼接到tooltip上
Object.values(this.dataset).forEach((item) => {
const { name: seriesName, data } = item;
// 判断杭州对应的value是否有值
const filterData = data.filter((v) => v.name === name);
const value = filterData.length > 0 ? filterData[0].value : null;
// 如果有值,则按照下面的规则进行字符串拼接
if (value) {
switch (seriesName) {
case '业务量':
str = `${str}${seriesName}: ${value}<br/>`;
break;
case '成功率':
str = `${str}${seriesName}: ${value}%<br/>`;
break;
case '平均时延':
str = `${str}${seriesName}: ${value}ms<br/>`;
break;
}
} else {
// 对null特殊处理
str = `${str}${seriesName}: ${value}<br/>`;
}
});
return str;
},
然后把tooltipFormatter
函数配置到tooltip中,效果就可以实现了。
tooltip: {
trigger: 'item',
formatter: this.tooltipFormatter,
},
如何在地图上叠加告警提示?
最后还有一个告警的需求:如果某个地市的实时成功率下降了,则要在地图上标注出来,这要怎么做呢?
首先,告警提示我们可以使用effectScatter
图表,也叫涟漪散点图,样子是这样的:
那如何把散点图放置在地图上呢?
完善数据集
既然基于实时成功率告警,我们先把数据添加上来。
data() {
return {
dataset: {
// ...
curSuccess: {
name: '实时成功率',
data: [
{ name: '杭州市', value: 100 },
{ name: '湖州市', value: 100 },
{ name: '嘉兴市', value: 100 },
{ name: '绍兴市', value: 100 },
{ name: '宁波市', value: 100 },
{ name: '舟山市', value: 100 },
{ name: '金华市', value: 100 },
{ name: '衢州市', value: 100 },
{ name: '丽水市', value: 80 },
{ name: '台州市', value: 100 },
{ name: '温州市', value: 100 },
],
},
},
};
},
在series中添加"effectScatter"类型数据
series() {
return [
// ...
{
name: '告警',
type: 'effectScatter',
coordinateSystem: 'geo', // 使用地理坐标系,前面我们注册过
symbolSize: 12, // 散点的大小
rippleEffect: {
color: 'red', // 涟漪的颜色
},
itemStyle: {
color: 'red', // 散点的颜色
opacity: 0.1, // 散点的透明度
},
data: this.convertScatterData(), // 下面重点介绍这里
},
];
},
初始化图形坐标
要想把散点图放置在地图上,首先要确定放在哪里,因而需要在data中指定落在地图上的坐标点。
所以,我们首先需要初始化一份坐标数据。正好,之前geoJSON中存在各个地市的市中心坐标,我们就把散点图放在那里。
data() {
return {
coordinate: []
},
methods: {
coordinateInit() {
geoJson.features.forEach((item) =>
this.coordinate.push({
name: item.properties.name,
value: item.properties.center,
})
);
}
},
mounted() {
this.coordinateInit();
}
为"effectScatter"生成data数据
散点图要落在地图上,数据格式是这样的:{name: '杭州', value: [x, y, 99]}
。其中x,y就是散点图要落在地图上的坐标点,接下来我们就需要把dataset中的实时成功率和刚才初始化的图形坐标合在一起,实现告警标识。
methods: {
convertScatterData() {
let res = [];
let curSuccessData = this.dataset.curSuccess.data;
// 遍历实时成功率数据
curSuccessData.forEach((item) => {
let { name, value } = item;
// 把地图坐标放进来
let coordinate = this.coordinate.find((e) => {
return e.name === name;
});
// 实时成功率<90时需要告警
if (value < 90) {
res.push({ name, value: [...coordinate.value, value] });
}
});
return res;
}
}
这样我们就可以在地图上看到告警标识了。
把告警内容显示出来
最后,我们再把告警内容显示出来,还是在刚才的tooltipFormatter
中进行添加。
methods: {
tooltipFormatter(params) {
const { name } = params;
// ...
if (value) {
switch (seriesName) {
// ...
case '实时成功率':
if (value < 90) {
str = `${str}<div style="color: red;">告警:近1分钟成功率为${value}%</div>`;
}
break;
}
//...
return str;
}
}
尾声
做这个需求的时候发现网上好的内容不多,就想着自己来写,希望能帮助更多的人。