echarts 散点世界地图

7,146 阅读5分钟

背景

最近接了个需求,需要地图上展示国内国外业务的数据信息。项目里用的是ECharts,于是去官网上查了一下例子。认为散点地图类型是最符合样式要求的。

开发

这次算第一次使用ECharts,看文档还是觉得挺头疼的,配置也太多了吧。。 看的眼花缭乱的。 散点地图里提供两种地图样式,一种是世界地图,一种是中国地图。其中世界地图如下图所示,是不展示中国省份的。但是我这个需求想要的结果是世界地图+中国详细省份。

最后效果:

贴代码,代码里解释配置项

import React from 'react';
import echarts from 'echarts';
import nameMap from './nameMap.json';//因为世界地图默认展示的国家名都是英文的,要展示中文的话,我们要加一个中英文名映射文件,作为参数传进去
import world from 'echarts/map/json/world.json';
import china from 'echarts/map/json/china.json';
import worldJson from './world.json';//自定义的地图文件世界地图+中国各省地图(我是把china和world两个json拼接起来就行了)   

<!--要想展示出来地图,地图json+ echarts.registerMap(mapName, mapJson)注册地图

echarts的npm包里是带着世界地图和中国地图的
有两种用法
import world from 'echarts/map/json/world.json';
import china from 'echarts/map/json/china.json';

 echarts.registerMap('world', world);/* 注册world地图 */
 echarts.registerMap('china', china);/* 注册world地图 */
或者
import world from 'echarts/map/js/world.js';
import china from 'echarts/map/js/china.js';
没什么不同的, js文件里是帮我们做了注册json

-->




const dataSource = [ //散点数据  value:[纬度,经度,数据]
    { name: '浙江省', value: [122.6953, 30.8936, 10] },
    { name: '江苏省', value: [117.5977, 34.4531, 0.1] },
    { name: '宁夏', value: [106.9629, 38.9795, 0.2] },
    { name: '天津', value: [117.334, 40.1221, 0.5] },
    { name: '湖北', value: [110.5664, 33.2666, 2.3] },
    { name: '辽宁', value: [123.1348, 42.8027, 1.6] },
    { name: "美国", value: [-93.310319, 36.908779, 0.015] },
    { name: "丹麦", value: [9.1577, 56.1388, 0.275] },
    { name: "瑞士", value: [8.6649, 47.5276, 0.0354] }
]

export default class MapChart extends React.Component {
<!--因为写了多个地图,所以我抽取了一个地图生成方法-->
    initChart = (data = {}) => {
        this.createChart({
            title: '使用echarts--world.json世界地图',
            rootDom: document.getElementById('world'),
            data: dataSource,
            geoMap: 'world',
            // geoCenter: [100.4, 35.9],
            // zoom: 6,
            visualMapRange: [//自定义左下角的可视化范围栏(默认的是平分比例,根据业务需求。因为我们这数据集中在前百分之几,所以需要自定义)
                { min: 0.2, max: 100, color: '#abd3ec', label: '0.2%-100%' },
                { min: 0.07, max: 0.2, color: '#9cc8e5', label: '0.07%-0.2%' },
                { min: 0.04, max: 0.07, color: '#78a8cf', label: '0.04%-0.07%' },
                { min: 0.02, max: 0.04, color: '#4f7fad', label: '0.02%-0.04%' },
                { min: 0, max: 0.02, color: '#2a5782', label: '0%-0.02%' }
            ]
        });

        this.createChart({
            title: '使用echarts--china.json中国地图',
            rootDom: document.getElementById('china'),
            data: dataSource,
            geoMap: 'china',
            // zoom: 1,
            visualMapRange: [
                { min: 0.2, max: 100, color: '#abd3ec', label: '0.2%-100%' },
                { min: 0.07, max: 0.2, color: '#9cc8e5', label: '0.07%-0.2%' },
                { min: 0.04, max: 0.07, color: '#78a8cf', label: '0.04%-0.07%' },
                { min: 0.02, max: 0.04, color: '#4f7fad', label: '0.02%-0.04%' },
                { min: 0, max: 0.02, color: '#2a5782', label: '0%-0.02%' }
            ]
        });

        this.createChart({
            title: '自己合并的json数据,世界地图+中国地图',
            rootDom: document.getElementById('chinaAndWorld'),
            data: dataSource,
            geoMap: 'chinaAndWorld',
            geoCenter: [100.4, 35.9],
            zoom: 5,
            visualMapRange: [
                { min: 0.2, max: 100, color: '#abd3ec', label: '0.2%-100%' },
                { min: 0.07, max: 0.2, color: '#9cc8e5', label: '0.07%-0.2%' },
                { min: 0.04, max: 0.07, color: '#78a8cf', label: '0.04%-0.07%' },
                { min: 0.02, max: 0.04, color: '#4f7fad', label: '0.02%-0.04%' },
                { min: 0, max: 0.02, color: '#2a5782', label: '0%-0.02%' }
            ]
        });
 
    }

    createChart = ({ rootDom, data, title, visualMapRange, geoMap, geoCenter, zoom }) => {
        var mapChart = echarts.init(rootDom);
        echarts.registerMap('chinaAndWorld', worldJson);/* 注册world地图 */
        echarts.registerMap('world', world);/* 注册world地图 */
        echarts.registerMap('china', china);/* 注册china地图 */

        mapChart.setOption({
            backgroundColor: '#fff',
            title: {
                text: title,
                left: 'left'
            },
            //地理坐标系组件用于地图的绘制,支持在地理坐标系上绘制散点图,线集。
            //要显示散点图,必须填写这个配置
            geo: {
                show: true,//是否显示地理坐标系组件
                roam: true, //是否允许鼠标滚动放大,缩小
                map: geoMap,//这就是注册的地图文件的那个mapName
                emphasis: { //高亮状态下的多边形和标签样式。
                    label: { //文本
                        // color: '#ADA',
                        show: true
                    },
                    itemStyle: { //区域
                        areaColor: '#ccc'
                    }
                },
                center:geoCenter,//当前视角的中心点,用经纬度表示
                zoom: zoom,//起始缩放比例
                nameMap: nameMap,//世界各国名中英文对应
            },
            tooltip: {
                show: true,
                formatter: function (params) {
                    return `${params.name}  ${params.value[2]}%`
                }
            },
            //是视觉映射组件,用于进行『视觉编码』,也就是将数据映射到视觉元素(视觉通道)。
            visualMap: {
                type: 'piecewise', // 定义为连续型 visualMap
                min: 0, //最小值
                max: 10, //最大值
                calculable: true, //是否显示拖拽用的手柄(手柄能拖拽调整选中范围)。
                inRange: {
                    // 从左到右颜色越来越轻
                    color: visualMapRange.map(item => item.color) //颜色
                },
                textStyle: {
                    color: '#fff'
                },
                pieces: visualMapRange.map(item => ({ min: item.min, max: item.max, label: item.label })),
                textStyle: {
                    color: '#000'
                }
            },
            series: [
                {
                    type: 'effectScatter',//散点地图的type
                    coordinateSystem: 'geo', //该系列使用的坐标系
                    mapType: 'world',
                    data: data,
                    label: {
                        formatter: (params) => {
                            const { data = {} } = params;
                            return `${data.name} \n ${data.value[2]}%`
                        },
                        position: 'insideLeft', //bottom
                        show: true,
                        color: '#000',
                    },
                    //标记的大小,可以设置数组或者函数返回值的形式,也可以用数组分开表示宽和高,例如 [20, 10] 表示标记宽为20,高为10。
                    symbolSize: 10,
                    rippleEffect: { //涟漪特效相关配置。
                        brushType: 'stroke' //波纹的绘制方式
                    },
                    hoverAnimation: true, //鼠标移入放大圆
                }
            ]
        })
    }

    render() {
        return (
            <div>
                <div id="world" style={{ width: '50%', height: 600, display: 'inline-block' }}></div>
                <div id="china" style={{ width: '50%', height: 600, display: 'inline-block' }}></div>
                <div id="chinaAndWorld" style={{ width: '100%', height: 600, display: 'inline-block' }}></div>
            </div>
        )
    }
}

echarts.baidu.com/asset/map/j… 世界地图文件下载 echarts.baidu.com/asset/map/j… datav.aliyun.com/tools/atlas…

这是echarts自带的地图信息

最后效果

然而,这并不是最后

emmm,在这篇文章还没写完的时候,产品提了个要求,地图上的图标有点太乱了,缩小地图的时候,图表消失,像百度地图那样的视觉体验。最终的实现让我把原来的代码结构都调整了

import React from 'react';
import { observer } from 'mobx-react';
import echarts from 'echarts';
import { debounce } from '$config/util';
import nameMap from './nameMap.json';
import worldJson from './world.json';//自定义的地图文件   世界地图+中国各省地图

const data = [ //散点数据  value:[纬度,经度,数据]
    { name: '浙江省', value: [122.6953, 30.8936, 10] },
    { name: '江苏省', value: [117.5977, 34.4531, 0.1] },
    { name: '宁夏', value: [106.9629, 38.9795, 0.2] },
    { name: '天津', value: [117.334, 40.1221, 0.5] },
    { name: '湖北', value: [110.5664, 33.2666, 2.3] },
    { name: '辽宁', value: [123.1348, 42.8027, 1.6] },
    { name: "美国", value: [-93.310319, 36.908779, 0.015] },
    { name: "丹麦", value: [9.1577, 56.1388, 0.275] },
    { name: "瑞士", value: [8.6649, 47.5276, 0.0354] }
]

const symbolSize = {
    small: false,//小号图标
    large: true,//大号图标
    no: false,//没有图标
};

let mapChart = null;

@observer
export default class MapChart extends React.Component {
    constructor(props) {
        super(props);
        // debounce是防抖方法
        this.resizeDebounce = debounce(this.chartsResize, 250);

    }

    componentDidMount() {
        mapChart = this.createMap();
        // 监听浏览器resize
        window.addEventListener('resize', this.resizeDebounce);
    }

    componentWillUnmount() {
        // 别忘了取消监听
        window.removeEventListener('resize', this.resizeDebounce);
    }

    chartsResize = () => {
        if (mapChart) {
            mapChart.resize();//实例 的resize
        }
    }

    getOptions = () => {
    <!--还是那些配置-->
        return {
            backgroundColor: '#fff',
            title: {
                text: '世界+中国地图',
                left: 'left',
                textStyle: {
                    fontSize: 16,
                    fontWeight: 'bold',
                    marginLeft: 10,
                }
            },
            //地理坐标系组件用于地图的绘制,支持在地理坐标系上绘制散点图,线集。
            //要显示散点图,必须填写这个配置
            geo: {
                show: true,//是否显示地理坐标系组件
                roam: true, //是否允许鼠标滚动放大,缩小
                map: 'world',
                emphasis: { //高亮状态下的多边形和标签样式。
                    label: { //文本
                        // color: '#ADA',
                        show: true
                    },
                    itemStyle: { //区域
                        areaColor: '#ccc'
                    }
                },
                center: [100.4, 35.9],//视图中心,展示在中国
                zoom: 6,//起始缩放比例
                nameMap: nameMap,//世界各国名中英文对应
            },
            tooltip: {
                show: true,
                formatter: function (params) {
                    return `${params.name}  ${params.value[2]}%`
                }
            },
            //是视觉映射组件,用于进行『视觉编码』,也就是将数据映射到视觉元素(视觉通道)。
            visualMap: {
                type: 'piecewise', // 定义为连续型 visualMap
                min: 0, //最小值
                max: 10, //最大值
                calculable: true, //是否显示拖拽用的手柄(手柄能拖拽调整选中范围)。
                textStyle: {
                    color: '#fff'
                },
                textStyle: {
                    color: '#000'
                }
            },
            series: [
                {
                    type: 'effectScatter',
                    coordinateSystem: 'geo', //该系列使用的坐标系
                    mapType: 'world',
                    data: data,
                    label: {
                        formatter: (params) => {
                            const { data = {} } = params;
                            return `${data.name} \n ${data.value[2]}%`
                        },
                        position: 'insideLeft', //bottom
                        show: true,
                        color: '#333'
                    },
                    //标记的大小,可以设置数组或者函数返回值的形式,也可以用数组分开表示宽和高,例如 [20, 10] 表示标记宽为20,高为10。
                    symbolSize: 8,
                    rippleEffect: { //涟漪特效相关配置。
                        brushType: 'stroke' //波纹的绘制方式
                    },
                    hoverAnimation: true, //鼠标移入放大圆
                }
            ]
        }
    }

    createMap = (initOption) => {
        const dom =  document.getElementById('chart');
        const mapChart = echarts.init(dom);

        echarts.registerMap('world', worldJson);/* 注册world地图 */
        const options = initOption || this.getOptions()
        mapChart.setOption(options);
        mapChart.on('georoam', () => {// 监听地图缩放事件
            const { center, zoom } = mapChart.getOption().geo[0];

            // 这个缩放范围是自己定的
            if ((zoom > 2 && zoom < 6) || zoom === 2) {
                if (!symbolSize.small) {
                    const option = this.getOptions();
                    // 重新设置图标大小和 当前的缩放比例 视图中心
                    option.series[0].symbolSize = 2;
                    option.series[0].label.fontSize = 8;
                    option.geo.zoom = zoom;
                    option.geo.center = center;
                    this.createMap(option);
                    symbolSize.small = true;
                    symbolSize.large = false;
                    symbolSize.no = false;
                }

            } else if ((zoom > 0 && zoom < 2) || zoom === 0) {
                if (!symbolSize.no) {
                    const option = this.getOptions();
                    option.series[0].symbolSize = 0;
                    option.geo.zoom = zoom;
                    option.geo.center = center;
                    this.createMap(option);
                    symbolSize.small = false;
                    symbolSize.large = false;
                    symbolSize.no = true;
                }

            } else {
                if (!symbolSize.large) {
                    const option = this.getOptions();
                    option.series[0].symbolSize = 10;
                    option.series[0].label.fontSize = 14;
                    option.geo.zoom = zoom;
                    option.geo.center = center;
                    this.createMap( option);
                    symbolSize.small = false;
                    symbolSize.large = true;
                    symbolSize.no = false;
                }
            }
        });
        return mapChart;
    }

    render() {
        return (
            <div id="chart" style={{ width: '50%', height: 600, display: 'inline-block' }}></div>
        )
    }
}