阅读 674

JS+Antv G2制作疫情地图

历经千辛万苦,终于把这地图给做出来了,但是。。。emm,很丑,有很多需要优化的地方,这个地图一开始一直在antv L7的实例中找,没有找到,经过查找资料,参考这篇博客才顿悟,原来可以获取中国地图的数据,做一个图表,以下是我的思路:

html

<div id='map'></div>
复制代码

css,g2-tooltip和 g2-tooltip-list是官网复制的样式,是当鼠标滑过是显示的div的样式

<style>
    .labelclass {
      width: 50px;
      height: 50px;
      border-radius: 50%;
      color: #9c0a0d;
      text-align: center;
      line-height: 50px;
    }
    .g2-tooltip{
      position: absolute;
        visibility: hidden;
        border: 1px solid #efefef;
        background-color: white;
        color: #000;
        opacity: 0.8;
        padding: 5px 15px;
        transition: top 200ms,left 200ms;
    }
    .g2-tooltip-list{
    margin: 10px;
  }
  </style>
复制代码

引入需要的js文件

<script src="jquery-3.4.1.min.js"></script>
  <script src="https://gw.alipayobjects.com/os/lib/antv/g2/3.4.10/dist/g2.min.js"></script>
  <script src="https://unpkg.com/@antv/data-set"></script>
复制代码

为了避免回调地狱混乱思路,我封装了promise类型的ajax

function $get(url) {
    return new Promise((resolve, rejected) => {
        $.get(url, res => {
            resolve(res)
        })
    })
}
复制代码

封装getColor函数,判断数据的区域得到不同的颜色

function getColor(v) {
    const trend = ['#ffefd7', '#ffd2a0', '#fe8664', '#e64b47', '#c91014', '#9c0a0d',];
    return v > 9999
        ? trend[5]
        : v > 999
            ? trend[4]
            : v > 499
                ? trend[3]
                : v > 99
                    ? trend[2]
                    : v > 9
                        ? trend[1]
                        : trend[0];
}
复制代码

先来做地图的框架

let mapData = await $get('https://geo.datav.aliyun.com/areas/bound/100000_full.json')
   
    var chart = new G2.Chart({
        container: 'map',
        height: 1000,
        width: 1000,
        padding: [55, 20]
    });
    chart.scale({
        //sync:当 chart 存在不同数据源的 view 时,用于统一相同数据属性的值域范围
        //nice:默认为 true,用于优化数值范围,使绘制的坐标轴刻度线均匀分布。例如原始数据的范围为 [3, 97],如果 nice 为 true,那么就会将数值范围调整为 [0, 100]
        x: { sync: true, nice: false },
        y: { sync: true, nice: false }
    })
    //chart.coord().reflect('' | 'x' | 'y'): 坐标系转置,将 x 或者 y 的起始、结束值倒置。
    chart.coord().reflect();
    chart.axis(false);

    //处理数据
    var ds = new DataSet();
    // createView创建并返回一个数据视图实例
    var dv = ds.createView('back')
        .source(mapData, {
            type: 'GeoJSON'
        })
        .transform({
            //geo.projection 地理映射
            type: 'geo.projection',
            projection: 'geoMercator',
            as: ['x', 'y', 'centroidX', 'centroidY']
        });
    //绘制地图
    var bgView = chart.view();
    bgView.source(dv);
    bgView.polygon()
        .position('x*y')
        .style({
            fill: '#000088',//地图颜色
            stroke: '#b1b1b1',//界线颜色
            lineWidth: 0.5,//线粗细
            fillOpacity: 1
        })
复制代码

好了,地图框架出来了

接下来要将疫情数据导出到图表上,我是从网站上得到的数据,所以还要进行处理

  let res = await $get('http://lovebridge.migu.cn:18188/api/map?url=http:%2F%2Fgarnetcdn.migu.cn%2Flovebridge.html')
        res = res.data.country[0].province//处理数据,得到一个数组
复制代码

将疫情数据和地图串联起来,只能通过地名,但是疫情书籍里地名没有显示市、省,所以我用了find和slice方法,找到对应的对象,处理我需要的数据

//处理疫情数据,显示数据
    var userData = ds.createView().source(res)
    userData.transform({
        //数据转换,type相当于数组转换的方法
        type: 'map',
        //由于我从网上获得的数据中市省都去掉了,所以我通过查找匹配前两个字符一样的得到经纬度
        callback: function (obj, index) {
            let one = dv.rows.find(item => { return item.name.slice(0, 2) === obj.na.slice(0, 2) })
            obj.x = one.centroidX * 1;
            obj.y = one.centroidY * 1;
            obj.data = parseInt(obj.tn.split(',')[0])
            one.data = obj.data
            return obj;
        }
    });
    //如下是数据显示及样式
    var pointView = chart.view();
    pointView.source(userData);
    pointView.intervalStack()
        .position('x*y').label('na', {
            offset: -5,
            textStyle: {
                fontSize: 10,
                fill: '#000',
            },
            formatter: (text, item) => {
                const d = item.point;
                return d.na + '\n' + d.data;
            }
        })
复制代码

需要按确诊人数的数量来让不同区域显示相应的颜色,我前面已经将数据存在userData上了

bgView.polygon().position('x*y').color('data', getColor)//传入回调函数,使不同的数据显示不一样的背景颜色
复制代码

当鼠标划过时,显示相应的数据,只有chart有tooltip方法

chart.tooltip({
        showTitle: false, // 默认标题不显示
        containerTpl:
            `<div class="g2-tooltip"><ul class="g2-tooltip-list"></ul>
      </div>`,
        itemTpl: '<li >确诊{value}例</li>'
    })
复制代码

取消动画,将图表渲染至画布

 chart.animate(false)
chart.render();
复制代码

成品

下面是总的js代码

function $get(url) {
    return new Promise((resolve, rejected) => {
        $.get(url, res => {
            resolve(res)
        })
    })
}
function getColor(v) {
    const trend = ['#ffefd7', '#ffd2a0', '#fe8664', '#e64b47', '#c91014', '#9c0a0d',];
    return v > 9999
        ? trend[5]
        : v > 999
            ? trend[4]
            : v > 499
                ? trend[3]
                : v > 99
                    ? trend[2]
                    : v > 9
                        ? trend[1]
                        : trend[0];
}
async function aa() {
    let mapData = await $get('https://geo.datav.aliyun.com/areas/bound/100000_full.json')
   
    var chart = new G2.Chart({
        container: 'map',
        // forceFit: true,
        height: 1000,
        width: 1000,
        padding: [55, 20]
    });
    // // force sync scales
    chart.scale({
        //sync:当 chart 存在不同数据源的 view 时,用于统一相同数据属性的值域范围
        //nice:默认为 true,用于优化数值范围,使绘制的坐标轴刻度线均匀分布。例如原始数据的范围为 [3, 97],如果 nice 为 true,那么就会将数值范围调整为 [0, 100]
        x: { sync: true, nice: false },
        y: { sync: true, nice: false }
    })
    //chart.coord().reflect('' | 'x' | 'y'): 坐标系转置,将 x 或者 y 的起始、结束值倒置。
    chart.coord().reflect();
    chart.axis(false);

    //处理数据,绘制地图
    var ds = new DataSet();
    // createView创建并返回一个数据视图实例
    var dv = ds.createView('back')
        .source(mapData, {
            type: 'GeoJSON'
        })
        .transform({
            //geo.projection 地理映射
            type: 'geo.projection',
            projection: 'geoMercator',
            as: ['x', 'y', 'centroidX', 'centroidY']
        });

    var bgView = chart.view();
    bgView.source(dv);
    bgView.polygon()
        .position('x*y')
        .style({
            fill: '#000088',//地图颜色
            stroke: '#b1b1b1',//界线颜色
            lineWidth: 0.5,//线粗细
            fillOpacity: 1
        })
        .color('data', getColor)//传入回调函数,使不同的数据显示不一样的背景颜色

        let res = await $get('http://lovebridge.migu.cn:18188/api/map?url=http:%2F%2Fgarnetcdn.migu.cn%2Flovebridge.html')
        res = res.data.country[0].province//处理数据,得到一个数组
    //处理疫情数据,显示数据
    var userData = ds.createView().source(res)
    userData.transform({
        //数据转换,type相当于数组转换的方法
        type: 'map',
        //由于我从网上获得的数据中市省都去掉了,所以我通过查找匹配前两个字符一样的得到经纬度
        callback: function (obj, index) {
            let one = dv.rows.find(item => { return item.name.slice(0, 2) === obj.na.slice(0, 2) })
            obj.x = one.centroidX * 1;
            obj.y = one.centroidY * 1;
            obj.data = parseInt(obj.tn.split(',')[0])
            one.data = obj.data
            return obj;
        }
    });
    //如下是数据显示及样式
    var pointView = chart.view();
    pointView.source(userData);
    pointView.intervalStack()
        .position('x*y').label('na', {
            offset: -5,
            textStyle: {
                fontSize: 10,
                fill: '#000',
            },
            formatter: (text, item) => {
                const d = item.point;
                return d.na + '\n' + d.data;
            }
        })


    //图例显示在右边
    chart.legend('data', {
        position: 'right',
    })

    //当鼠标划过时,显示相应的数据,只有chart有tooltip方法
    chart.tooltip({
        showTitle: false, // 默认标题不显示
        containerTpl:
            `<div class="g2-tooltip"><ul class="g2-tooltip-list"></ul>
      </div>`,
        itemTpl: '<li >确诊{value}例</li>'
    })
    chart.animate(false)
    chart.render();
}
aa()
复制代码