第二个故事 - 空间数据的可视化

你手头或许有包含地理坐标的数据,比如产品在不同地区被使用的情况。苦于手头缺乏可用的工具,你或许只能利用这些数据做扇形图或者直方图。现在你知道ECharts支持空间数据的可视化,那么为什么不试着在地图上展示你的数据呢?

现在请跟随笔者完成下面这张图表,它描述的是2017年全国主要城市的工业及居民用电情况(笔者非常热爱自己的国家,但是由于统计方式的不同,数据中不包含香港澳门及台湾的结果)。

数据来源:《中国城市统计年鉴-2018》(收录了2017年全国各级城市社会经济发展等方面的主要统计数据)。

image

正式开始

  1. 数据获取:

    本次的数据来源已经给出。笔者通过钞能力获得了年鉴的 pdf 版本(412页),其中342-349页为主要城市用水、用电情况。笔者使用 wps 将需要的内容分割出来,并转化为 word 版本,将之粘贴到 excel 表中,调整格式,最终得到了可用的数据(笔者这种脱裤子放屁的操作源于 pdf 转 excel 的结果完全无法使用,格式全崩,但是 word 结果非常完美,粘贴到 excel 表中即可使用)。并且从网络上可以免费获得全国主要城市区县的经纬度数据。

    现在,你已经得到了本次绘制图表所需的全部数据。

  1. 数据分析:

    现在我们简要分析一下数据的准确性,并将其转化为 [json] 格式,以供直接使用。

    我们注意到部分城市的数据出现了缺失(这很正常),由于其数量不多,故填充为0。现在我们检查一下,是否共计31个省市自治区(答案是“是的”),现在应该没有问题了,我们使用表格工具,将excel表内容转化为 [json] 格式。

    两个数据分别是:

    1. 居民用电数据
    2. 工业用电数据

    数据准备全部完成,接下来我们准备绘制初始图表。

  1. 初始图表+图表美化:

    有了上个散点图的基础,地图的绘制也变得相当简单。散点还是散点,只是坐标系换成了地图,坐标值是经纬度。

    1. 我们引入ECharts,使用 [jsonp] 方式引入经纬坐标、工业和居民用电的数据。

      笔者认为有必要详细介绍一下[jsonp]方法(尽量用通俗语句):

      1. 为什么我不能随意引用数据?因为浏览器出于安全考虑,会禁止引用非同源的数据(非同源也就是地址不同,外部的数据难以保证安全性),但是函数并不受限制;

      2. 那我应该怎么办?所以我们需要做的就是将数据封装在函数中,读者如果阅读前文提供的 [json] 数据,就会发现数据全都被封装在了一个函数中(比如居民用电数据就被封装在 'cityValueHousehold' 这个函数的括号中);

      3. 我接下来该如何引用数据?你首先需要创建一个回调函数,并且函数名称要与 [json] 数据中的函数名相同。然后在后文新建一个引入数据来源并且调用回调函数,这样你回调函数中的 [data] 变量就是你的数据本身了,这时你可以将数据赋给一个全局变量(下方代码中经纬度和工业用电量),或者直接调用它(居民用电量);

      4. 如果还有不懂,请联系作者([email protected])。

      
      
      
          
          ECharts
          
          
          
          
          
          
          
          
          
      
      
          
          
  1. 我们在回调函数 [cityValueHousehold(data)] 中完成后续的代码,使用 [option] 来表述图表信息。与之前不同的是,我们使用 [option.geo] 绘制地图,将其作为坐标系,在地图上绘制散点。并且由于我们有两份数据,所以我们在地图上绘制两次散点(只需要在 series 中用逗号隔开,并用中括号包裹即可 [{},{}] ):

    var option = {
      //地图背景
      backgroundColor: 'rgba(1, 1, 1, 1)',
      //图表标题,已经设置了部分的规则
      title: {
        text: '2017年全国主要城市用电情况',//标题内容
        x:'center',//居中   
        top : 30 ,//距离上端30像素
        //标题字体设置
        textStyle: {
          color: '#fff',//字体颜色
          fontSize:30,//字体大小
        }
      },
      //提示框组件
      tooltip: {},
      
      //图例组件
      //legend: {},
      
      //视觉映射组件
      //visualMap: {},
      
      //geo组建绘制地图,下方详细介绍各设置
      geo: {
        //地图:中国
        map: 'china',
        //是否允许缩放,拖动
        roam: false,
        //缩放设置
        scaleLimit:{
          min:1//缩放最小程度为1,即最初状态
        },
        //文字设置(指各省名称)
        label: {
          //仅当选中状态下有设置
          emphasis: {
            color: '#fff', //字体颜色白色
            show: true,    //是否显示文字,当前‘是’
          }
        },
        //距离顶端75个像素
        top: 75,
        //各板块设置
        itemStyle: {
          //通常状态下的设置
          normal: {
            areaColor: '#2a333d', //板块颜色
            borderColor: '#111'   //板块边框颜色
          },
          //选中状态下的设置
          emphasis: {
            areaColor: 'rgba(10,10,10,1)', //板块颜色
            borderColor: '#fff',           //边框颜色
            shadowBlur: 3,                                 //阴影宽度,3像素
            shadowColor: '#ffff99',                //阴影颜色
          }
        }
      },
      series: [
        {
          //浮点图表示城市工业用电情况
          //系列名称,用于tooltip的显示,legend 的图例筛选
          name: '工业',
          //类型:散点图
          type: 'scatter',
          //系列使用的坐标系,可选'cartesian2d'笛卡尔坐标系,'polar'极坐标系,'geo'当前的地理坐标系
          coordinateSystem: 'geo',
          //引入数据,当前为空
          data: [],
          //字体设置
          label: {
            //通常状态
            normal: {
              show: false //不显示文字
            },
            //选中状态
            emphasis: {
              show: false //不显示文字
            }
          },
          //图形设置
          itemStyle: {
            //通常状态
            normal: {
              color:'#eac736',             //圆形的颜色
              shadowBlur: 20,        //阴影宽度
              shadowColor: '#ffff99',//阴影颜色
              borderColor: '#fff',     //边框颜色
              borderWidth: 1               //边框宽度
            },
            //选中状态
            emphasis: {
              borderColor: '#fff',     //边框颜色
              borderWidth: 2               //边框宽度
            }
          },
        },
        {
          //浮点图表示城市居民用电情况
          //内容完全相同,不再做额外注释
          name: '居民',
          type: 'scatter',
          coordinateSystem: 'geo',
          //引入数据,当前为空
          data: [],
          label: {
            normal: {
              show: false
            },
            emphasis: {
              show: false
            }
          },
          itemStyle: {
            normal: {
              color:'#50a3ba',
              shadowBlur: 20,
              shadowColor: '#ffff99',
              borderColor: '#fff',
              borderWidth: 1
            },
            emphasis: {
              borderColor: '#fff',
              borderWidth: 2
            }
          },
        }
      ]
    }
    

    保存代码,现在可以看到地图已绘制好,选中区域显示省(自治区)名称(简称):

    image
  1. 接下来我们引入数据,在地图上绘制我们的散点图。这里需要引入一个函数 [convertData()],这个函数可以为我们数据中的城市和用电量之间增加经纬度值,即使数据变为:

    这样的数据即可用于我们的图表(为什么不直接令最初的数据就是这个结构?)。

    //引入居民用电量的方法,函数中的 data 即为对应 json 中的数据
    function cityValueHousehold(data){
    
      //函数converData(),可在地区的经纬度后添加value值
      var convertData = function (data) {
        var res = [];
        for (var i = 0; i < data.length; i++) {
          var geoCoord = geoCoordMap[data[i].name];
          //console.log(geoCoord);
          if (geoCoord) {
            res.push({
              name: data[i].name,
              value: geoCoord.concat(data[i].value)
            });
          }
        }
        return res;
      };
      //console.log(convertData(data));
      var option = {
        //地图背景
        backgroundColor: 'rgba(1, 1, 1, 1)',
        //图表标题,已经设置了部分的规则
        title: {
          text: '2017年全国主要城市用电情况',//标题内容
          x:'center',//居中     
          top : 30 ,//距离上端30像素
          //标题字体设置
          textStyle: {
            color: '#fff',//字体颜色
            fontSize:30,//字体大小
          }
        },
        //提示框组件
        tooltip: {},
        //geo组建绘制地图,下方详细介绍各设置
        geo: {
          //地图:中国
          map: 'china',
          //是否允许缩放,拖动
          roam: false,
          //缩放设置
          scaleLimit:{
            min:1//缩放最小程度为1,即最初状态
          },
          //文字设置(指各省名称)
          label: {
            //仅当选中状态下有设置
            emphasis: {
              color: '#fff', //字体颜色白色
              show: true,    //是否显示文字,当前‘是’
            }
          },
          //距离顶端75个像素
          top: 75,
          //各板块设置
          itemStyle: {
            //通常状态下的设置
            normal: {
              areaColor: '#2a333d', //板块颜色
              borderColor: '#111'   //板块边框颜色
            },
            //选中状态下的设置
            emphasis: {
              areaColor: 'rgba(10,10,10,1)', //板块颜色
              borderColor: '#fff',           //边框颜色
              shadowBlur: 3,                 //阴影宽度,3像素
              shadowColor: '#ffff99',        //阴影颜色
            }
          }
        },
        series: [
          {
            //浮点图表示城市工业用电情况
            //系列名称,用于tooltip的显示,legend 的图例筛选
            name: '工业',
            //类型:散点图
            type: 'scatter',
            //系列使用的坐标系,可选'cartesian2d'笛卡尔坐标系,'polar'极坐标系,'geo'当前的地理坐标系
            coordinateSystem: 'geo',
            //引入数据,使用函数处理原数据 elcforIndustrialValue ,为其增加经纬值 
            data: convertData(elcforIndustrialValue),
            //字体设置
            label: {
              //通常状态
              normal: {
                show: false   //不显示文字
              },
              //选中状态
              emphasis: {
                show: false   //不显示文字
              }
            },
            //图形设置
            itemStyle: {
              //通常状态
              normal: {
                color:'#eac736',       //圆形的颜色
                shadowBlur: 20,        //阴影宽度
                shadowColor: '#ffff99',//阴影颜色
                borderColor: '#fff',   //边框颜色
                borderWidth: 1         //边框宽度
              },
              //选中状态
              emphasis: {
                borderColor: '#fff',   //边框颜色
                borderWidth: 2         //边框宽度
              }
            },
          },
          {
            //浮点图表示城市居民用电情况
            //内容完全相同,不再做额外注释
            name: '居民',
            type: 'scatter',
            coordinateSystem: 'geo',
            //引入数据,使用函数处理原数据 data ,为其增加经纬值
            data: convertData(data),
            label: {
              normal: {
                show: false
              },
              emphasis: {
                show: false
              }
            },
            itemStyle: {
              normal: {
                color:'#50a3ba',
                shadowBlur: 20,
                shadowColor: '#ffff99',
                borderColor: '#fff',
                borderWidth: 1
              },
              emphasis: {
                borderColor: '#fff',
                borderWidth: 2
              }
            },
          }
        ]
      }
      // 使用刚指定的配置项和数据显示图表。
      myChart.setOption(option);
    };
    

    不要着急,现在地图中虽然有了散点,但是大小完全相同,无法体现其 [value] (用电量)上的差异,我们使用 [symbolSize] 规则对其进行设置。并且由于数值过大,我们将结果除以100(先开方,但开方并不是由于数值过大):

    data: convertData(elcforIndustrialValue),
          //图标大小设置,使用回调函数,以数值的开方结果/100作为圆形的半径
    symbolSize: function (val) {
          return Math.sqrt( val[2] )/100;//工业用电较多,除以100
    },
    

    将这段代码补充到 [data] 之下,现在你已经绘制出了你想要的图表。蓝色点代表居民用电,黄色点代表工业用电。

    image
  2. 不要着急,我们还需要补充一些东西。

    1. [legend] 图例组件。有了图例,只需要点击开启或关闭,整个系列的散点也会相应开启或关闭,便于查看图表结果(交互式图表的基本要求):

      //图例组件
      legend: [{
        //图例列表的布局朝向,'horizontal'横排,'vertical'纵排
        orient: 'vertical',
        //竖直方向位置,当前置底
        y: 'bottom',
        //水平方向位置,当前居右
        x:'right',
        //对应名称 name 为‘工业’的系列
        data:['工业'],
        //文字设置
        textStyle: {
          color: '#fff'//字体白色
        }
      },{
        orient: 'vertical',
        y: 'bottom',
        //水平方向位置,当前居左
        x:'left',
        //对应名称 name 为‘居民’的系列
        data:['居民'],
        textStyle: {
          color: '#fff'
        }
      }],
      
    2. [visualMap] 视觉映射组件。图例可以将数据映射到视觉元素上(元素大小、颜色等),且优先级高于系列(series)中的元素设置。本例中笔者设置了元素的颜色根据其数值大小渐变,但是由于不便于比较工业和居民用电情况,最终将其注释掉,使之不生效。读者可以尝试将其打开,查看效果:

      visualMap: {
        //组件定义域,最大最小值设置,根据数据所处位置赋予其渐变效果
        min: 0,
        max: 10000000,
        //是否显示拖拽用的手柄
        calculable: true,
        //颜色范围,三段
        inRange: {
          color: ['#50a3ba', '#eac736', '#d94e5d']
        },
        //文字规则,颜色白色
        textStyle: {
          color: '#fff'
        }
      },
      

      笔者该例中未使用,但是 visualMap 在只有一套数据的图表中相当好用。

    3. [tooltip]提示框组件。十分重要的组件,通过设置回调函数,当鼠标落于图形上时,可以显示当前图形的部分信息:

      //提示框组件
      tooltip: {
        //触发类型,'item'图形触发,鼠标落于图形上显示;'axis'坐标轴触发;'none'什么都不触发
        trigger: 'item',
        //回调函数,显示当前图形对应的名称及其第三个 value 值(用电量)
        formatter: function (params) {
          return params.name + ' : ' + params.value[2];
        }
      },
      
    4. 现在给出最终的完整代码,读者可以根据官方文档自行修改,或者使用自己的数据,生成自己的专属图表:

      
      
      
          
          ECharts
          
          
          
          
          
          
          
          
          
      
      
          
          

      最终的图表如下。左右下角新增了两个图例,点击可开启或关闭:

      image
  1. 图表的意义-它述说了怎样的故事:
    1. 对于绝大多数的城市,居民的用电量都小于工业用电量,三亚除外;

    2. 工业用电量沿海城市较多。其中珠三角、长三角的居民用电量较多,而山东滨州、河北唐山的居民用电量相当少,说明后者是纯工业城市,前者第三产业比较发达;

    3. 对于内陆城市,重庆、包头、鄂尔多斯、乌兰察布、通辽相对较多,显然内蒙古的工业城市要远多于其他地区;

  1. 地球处于浩瀚星空:

    ECharts支持绘制三维图形。笔者将地图(坐标系)换成了三维地球,散点图换做了三维柱形图(bar3D),作出了如下的可视化图形。

    或许你的老板比较喜欢这种图形,但笔者认为这个图形并不便于反映数据(虽然它可能真的COOL!)。

    如果大家有兴趣,可以访问这个页面自行观察。如果你想绘制三维图形,可以访问ECharts官方GL实例,对照文档,自行学习三维可视化(因为笔者本人也知之甚少)。

    image

故事结束

第二个故事主要讲解如何将空间数据可视化。

地图是一个非常直观的可视化类型。

不论怎样,地图都是一种理解数据的极佳手段。它们是真实世界按比例缩小后的版本,而且它们无处不在。

现在你应该知道如何将手头的空间数据可视化,本例将数据以散点大小(也就是面积)加以展示,除此之外,你还可以将数据以区域颜色反映出来。总而言之,地图具有极强的直观性,可以为你(的努力)带来丰厚的回报:

一方面丰富了我们呈现数据的形式,另一方面它也比静态图表更加利于我们深入地探索数据。

并且,你的空间数据往往还附带时间信息。笔者曾完成了一个动画,展示了自己负责的产品,在半年中的每一天被各个地区调用的情况。这个动画在年终总结会议中可以说赚足了眼球。后文中将会介绍这个方法,但是因为需要额外的 javaScript 编码,所以学习的难度可能相对较大,笔者也会尽量描述清晰。

故事的最后,笔者想强调一点:散点图通过各点「面积」体现相应数值的大小,而非直径(半径)。所以在设置散点半径时务必使用数值的「开平方」结果。如果你直接将数值作为散点的半径,数值上的差别将被放大,你的读者将可能对图表产生误解,进而无法了解实际情况。在绘制散点图时请牢记这一点。

好了,放松下你的头脑,接下来才是重头戏。

你可能感兴趣的:(第二个故事 - 空间数据的可视化)