使用Echarts制作地图

5,471 阅读4分钟

前言

最近做一个监控大屏项目,需要通过地图进行全省监控,在此记录制作过程。

image.png

获取地图数据

Echarts是一个图表渲染库,需要根据提供的地图数据进行地图渲染。

地图数据遵循GeoJSON数据规范,规范中制定了一系列用于描述地图地理信息的字段,数据格式仍然是JSON。

如何获取地图数据?

  1. 打开阿里云DataV数据可视化平台 datav.aliyun.com/portal/scho…
  2. 选择需要的省份地图,点击下载 image.png

项目初始化

  1. 使用vue-cli新建vue工程
vue create map
  1. 安装echarts包
npm install echarts -S
  1. 新建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(); // 自动刷新
    },
  },
};

image.png

如何在地图的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,
        },
      ];
    },
  }

image.png

现在,杭州市的业务量从原来的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,
},

image.png

如何在地图上叠加告警提示?

最后还有一个告警的需求:如果某个地市的实时成功率下降了,则要在地图上标注出来,这要怎么做呢?

首先,告警提示我们可以使用effectScatter图表,也叫涟漪散点图,样子是这样的:

image.png

那如何把散点图放置在地图上呢?

完善数据集

既然基于实时成功率告警,我们先把数据添加上来。

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;
    }
  }

这样我们就可以在地图上看到告警标识了。

image.png

把告警内容显示出来

最后,我们再把告警内容显示出来,还是在刚才的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;
    }
}

image.png

尾声

做这个需求的时候发现网上好的内容不多,就想着自己来写,希望能帮助更多的人。