我们经常在一些新闻报道和商业杂志上看到运用地图来展示商业现象的做法。这样利用地图来反映和分析数据的形式,叫数据地图,它可以直观的表达出数据之间的空间关系。

在数据地图中,流向地图属于高频应用场景,但实现起来并不容易,本文来将以Vue这个热门的Web技术为例,探讨如何在项目中快速开发出炫酷的流向地图。

什么是流向地图?

流向地图也称迁徙图,可以在地图上显示信息或物体从一个位置到另一个位置的移动及其数量,通常用来显示人物、动物和产品的迁移数据。单一流向线所代表的移动规模或数量由其粗细度表示,有助显示迁移活动的地理分布。

流向地图多应用于区际贸易、交通流向、人口迁移、购物消费行为、通讯信息流动、航空线路等场景,也可应用企业货物运输,供应链管理。

(流向地图,GIF动图)

实现方案A:Echats

Echarts是百度的开源图表库,其中就包含地图组件。使用Echarts进行地图可视化会稍显复杂,需要有一定JS基础才能上手。

1、 首先打开vue项目,cmd进入命令安装echarts依赖包,默认下载最新版本

npm install echarts –save

2、 进入src目录里的main.js全局引入echarts,以及引入中国地图文件,这样就可以在任何组件中使用了

import \* as echarts from 'echarts';

import "echarts/map/js/china.js";

3、 引入相关文件后就开始创建地图实例。在Echarts中,数据需要预先进行清洗,再放入代码中。代码块主要分为三部分:字段定义地理位置、字段赋值以及图表框架搭建,部分代码如下所示:

public render() {      //图表绘制方法
    this.chart.clear();
    const isMock = !this.items.length;
    const items = isMock ? Visual.mockItems : this.items;
    this.container.style.opacity = isMock ? '0.3' : '1';
    const options = this.properties;
    let planePath = options.effect ? options.symbol : options.symbolStyle;
    let departureValue = isMock ? ['北京', '上海', '广州市'] : this.legendData;
    let color = isMock ? ['#a6c84c', '#ffa022', '#46bee9'] : options.palette;
    var series = [];
    items.map((item: any, i: number) => {
      if (item.length) {
        let j = i % color.length;
        series.push({
            name: item[0].fromName,
            type: 'lines',
            zlevel: 1,
            effect: {
              show: true,
              period: options.period,
              trailLength: 0.7,
              color: color[j],
              symbolSize: 4},
            lineStyle: {
              normal: {
                color: color[j],
                width: 0.1,
                curveness: 0.2 } },
            data: item },
          {
            name: item[0].fromName,
            type: 'lines',
            zlevel: 2,
            symbol: ['none', 'arrow'],
            symbolSize: 10,
            effect: {
              show: true,
              period: options.period,
              trailLength: 0,
              symbol: planePath,
              symbolSize: options.symbolSize },
            lineStyle: {
              normal: {
                color: color[j],
                width: 1,
                opacity: 0.6,
                curveness: 0.2 } },
            data: item},
          {
            name: item[0].fromName,
            type: 'effectScatter',
            coordinateSystem: 'geo',
            zlevel: 2,
            rippleEffect: {
              brushType: 'stroke' },
            label: {
              normal: {
                show: true,
                position: "right", //显示位置
                offset: [5, 0], //偏移设置
                formatter: "{b}" //圆环显示文字 },
              emphasis: {
                show: true  } },
            symbolSize: options.pointSize,
            itemStyle: {
              normal: {
                color: color[j]  }  },
            data: this.parseData(item)  }  );  } });
    var option = {
      tooltip: {
        trigger: 'item',
        formatter: function (params, ticket, callback) {
          if (params.seriesType == "lines") {
            return params.data.fromName + " --> " + params.data.toName + "<br />" + params.data.value;
          } else {
            return params.name; }  } },
      legend: {
        show: options.showLegend,
        orient: 'vertical',
        top: 'bottom',
        left: 'right',
        data: departureValue,
        textStyle: {
          color: '#fff'   },
        selectedMode: 'multiple',   },
      geo: {
        map: options.mapName,
        label: {
          emphasis: {
            sfalsehow: true,
            color: '#fff'  } },
        roam: options.roam,
        layoutCenter: ["50%", "50%"], //地图位置
        layoutSize: "125%",
        itemStyle: {
          normal: {
            borderColor: options.borderColor,
            borderWidth: 1,
            areaColor: {
              type: 'radial',
              x: 0.5,
              y: 0.5,
              r: 0.8,
              colorStops: [{
                offset: 0,
                color: options.startColor // 0% 处的颜色
              }, {
                offset: 1,
                color: options.endColor // 100% 处的颜色  }], },
            shadowColor: options.shadowColor,
            shadowOffsetX: -2,
            shadowOffsetY: 2,
            shadowBlur: 10  },
          emphasis: {
            areaColor: options.emphasisColor,
            borderWidth: 0  }   }  },
      series: series   };
    this.chart.setOption(option);  }

(模拟数据)

写了大约300多行代码,完成了Echarts的流向地图,效果如下:

(流向地图,GIF动图)

小结:

使用代码开发,让Echarts在实现地图可视化的过程中具有极大的自由度(任何用代码开发的操作都是如此)。虽然稍学习一下都能很快掌握简单的JS技巧,但要深入做一些和数据的交互,会有难度;涉及到后端数据整理和传输,复杂度会更高一些,不在本文的示例范围。总体来看,Echarts作为一款国产工具,可以说瑕不掩瑜,推荐有编程基础的读者使用。

实现方案B:嵌入式商业智能软件

除了Echarts之外,还有更快的数据地图制作方法,那就是利用一些可视化地图制作软件,比如一些BI工具Wyn Enterprise、tableau等。那具体如何实现呢?以下,我们以Wyn Enterprise嵌入式商业智能和报表软件作为工具、以一个企业的区域贸易的销量情况为场景,做一些具体介绍。

(原始数据)

首先导入数据,然后创建新的仪表板,拖拽数据字段制作图表。这里有两种方式来识别地理信息:一种是让系统根据位置名称(如城市名)来识别,只绑定位置名称,系统会自动根据位置名称识别对应的经纬度,另一种是直接通过经纬度数据来识别,绑定数据系统会自动识别,一键生成流向地图。

(拖拽式设计流向地图,GIF动图)

我们使用Wyn Enterprise就这样简单拖拽,实现了一个流向地图。地图还自动支持数据过滤,钻取联动分析等功能,最终用户也可以根据自己的个人爱好或者分析目标、设置图表颜色或者其他酷炫的动态效果。

流向地图在Wyn Enterprise可视化大屏中的一个示例:

(Wyn Enterprise可视化大屏)

最后,我们只需要在VUE项目里调用这个仪表板地址即可实现项目需求。借助强大的开发工具,开发效率有了大幅提升。