• 教你从零开始画echarts地图


    echarts地图制作

    基于VUE编写,其他框架请自行转换,大同小异

    基础配置

    先让地图内容出来,npm安装步骤省略,请参考官方文档,创建的div必须设置宽度和高度,关于图表的宽高自适应,参考我的另一篇文章

    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102

    这是最基础的配置,外加了一些我自己写的样式,使地图美观一些,如果你完全的复制,并且china.json文件也引入了,那么你会看到如下的内容

    请添加图片描述

    这其中比较有意思的是,如果你注册地图时,还有option.geo.map的名字用的是china,南海诸岛会如上图以缩略图展示,但是以此之外来命名地图,则不会展示缩略图。

    再次声明,如果二者名字不一致,将会导致异常,致使地图无法显示

    数据渲染

    实际开发中,往往需要将后台的数据渲染到地图里,我们在option里添加series属性,以下是我的两个示例,仅做参考:

    series: [
        {
            type: "scatter",
            coordinateSystem: "geo",
            symbol: "pin",
            legendHoverLink: true,
            symbolSize: [60, 60],
            // 这里渲染标志里的内容以及样式
            label: {
                show: true,
                formatter(value) {
                    return value.data.value[2];
                },
                color: "#fff",
            },
            // 标志的样式
            itemStyle: {
                normal: {
                    color: "rgba(255,0,0,.7)",
                    shadowBlur: 2,
                    shadowColor: "D8BC37",
                },
            },
            // 数据格式,其中name,value是必要的,value的前两个值是数据点的经纬度,其他的数据格式可以自定义
            // 至于如何展示,完全是靠上面的formatter来自己定义的
            data: [
                { name: "西藏", value: [91.23, 29.5, 2333] },
                { name: "黑龙江", value: [128.03, 47.01, 1007] },
            ],
            showEffectOn: "render",
            rippleEffect: {
                brushType: "stroke",
            },
            hoverAnimation: true,
            zlevel: 1,
        },
        // {
        //   type: "effectScatter",
        //   coordinateSystem: "geo",
        //   effectType: "ripple",
        //   showEffectOn: "render",
        //   rippleEffect: {
        //     period: 10,
        //     scale: 10,
        //     brushType: "fill",
        //   },
    
        //   hoverAnimation: true,
        //   itemStyle: {
        //     normal: {
        //       color: "rgba(255, 235, 59, .7)",
        //       shadowBlur: 10,
        //       shadowColor: "#333",
        //     },
        //   },
        //   zlevel: 1,
        //   data: [
        //     { name: "西藏", value: [91.23, 29.5, 2333] },
        //     { name: "黑龙江", value: [128.03, 47.01, 1007] },
        //   ],
        // },
    ],
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    两种渲染方式如下:

    请添加图片描述

    请添加图片描述

    使用备注的部分时,需要在option.tooltip里添加formatter属性,我写的如下:

    const option = {
        // ...
        tooltip: {
        // ...
            formatter(params) {
                return `地区:${params.name}
    数值:${params.value[2]}`; } } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    更多的方式还要自己多试验,这是个费时且需要耐心的活,你甚至可以将柱状图放上去。有更花里胡哨的效果,也请分享给我。

    嵌入文字

    使用[这里是代码011]可以实现简单的水印效果

    const option = {
        // ...
        graphic:{
            // 水印类型
            type: 'text',
            // 相对于容器的位置
            left:'10%',
            top: '10%',
            // 样式设置
            style: {
                // 文本内容
                text: "create by ",
                // 字体粗细、大小、字体
                font: 'bolder 1.5rem "Microsoft YaHei", sans-serif',
                // 字体颜色
                fill: "#fff"
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    效果如下:

    请添加图片描述

    利用graphic的type=“group”,还可以组合出一些有意思的效果(抄官方文档的效果):

            graphic: {
              type: "group",
              rotation: Math.PI / 4,
              bounding: "raw",
              left: 110,
              top: 110,
              z: 100,
              children: [
                {
                  type: "rect",
                  left: "center",
                  top: "center",
                  z: 100,
                  shape: {
                    width: 400,
                    height: 50,
                  },
                  style: {
                    fill: "rgba(0,0,0,0.3)",
                  },
                },
                {
                  type: "text",
                  left: "center",
                  top: "center",
                  z: 100,
                  style: {
                    fill: "#ddd",
                    text: "create by ",
                    font: 'bolder 1.5rem "Microsoft YaHei", sans-serif',
                  },
                },
              ],
            },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    请添加图片描述

    地图下钻

    往往还有一种需求,在我们点击一个省的时候,需要切换到这个省的详细地图,甚至还可以下钻到市、县等等。

    为了试下点击下钻,我们需要先了解echarts中的点击事件,文档参考

    以目前的功能来说,我们暂时不需要加入其它的业务逻辑以及省级的数据渲染,仅仅只做地图的切换,所以点击事件里我们需要实现获取点击的省份名称,然后根据省份名称,来选择地图的JSON文件,最后重新渲染echarts图表,下面是我的简单示例:

    // 新增加北京的地图JSON文件
    import beijing from "@/assets/mapJson/data-beijing.json";
    // ...
    
    initCharts(){
        const charts = echarts.init(this.$refs["charts"]);
    // ...
    // 注意这里是echarts的实例对象,而不是echarts组件本身。
        charts.on('click', ({name}) => {
            if (name === "北京") {
                // 修改option的配置,可以继续自定义
                option.geo.zoom = 0.8
                // 就像上面提到的,这里必须要和注册地图时的名字一致
                option.geo.map = "beijing"
                // 注册地图
                echarts.registerMap("beijing", beijing)
                // 重新渲染
                charts.setOption(option, true)
            }
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    需要注意的是,在重新setOption的时候,我们加入了第二个参数,按照官方文档的说法:


    参数:

    调用方式:

    chart.setOption(option, notMerge, lazyUpdate);
    
    • 1

    或者

    chart.setOption(option, {
        notMerge: ...,
        lazyUpdate: ...,
        silent: ...
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • option

      图表的配置项和数据。

    • notMerge

      可选,是否不跟之前设置的 option 进行合并,默认为 false,即合并。

    • lazyUpdate

      可选,在设置完 option 后是否不立即更新图表,默认为 false,即立即更新。

    • silent

      可选,阻止调用 setOption 时抛出事件,默认为 false,即抛出事件。


    第二个从参数设置为true来让图表重新渲染,而不合并配置,当然,这一点具体需要看你显示开发的需求,我在这里仅是为了演示。绝不是偷懒

    另外在echarts v3.x的版本里,切换地图默认是有过渡动画的,而v4.x和v5.x的版本里则没有过渡动画,如果知道怎么加上的,可以私信我。

    上面虽然可以实现地图切换,但很显然开发中这么写要被打死。下钻三十多个地图要写三十多个if,显然是一种不理智的开发方式。一种方式我们可以通过axios或者ajax异步请求,但是这样需要你在生产环境和运维协商好,否则会导致请求不到JSON文件。

    下面是我在前端写的一个简单的工具方法,仅供参考:

    import zhongguo from "@/assets/mapJson/data-city.json";
    import neimenggu from "@/assets/mapJson/data-neimenggu.json";
    import beijing from "@/assets/mapJson/data-beijing.json";
    // ...
    
    const mapDict = {
      "北京": "beijing",
      "内蒙古": "neimenggu",
      // ...
    }
    
    const mapData = {
      beijing,
      neimenggu,
      // ...
    }
    
    export function getMap(mapName) {
      const cityName = mapDict[mapName]
      if(cityName){
        return [cityName, mapData[cityName]]
      }
      return ['china', zhongguo]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    需要建立两个字典,一个是汉字和拼音的对照映射,一个是拼音和JSON文件的映射,这个可灵活配置,并非唯一。

    优化一下上面的的代码:

    // 删除地图json文件的引用,修改为上面的工具方法
    import { getMap } from "./maputil";
    
    
    methods: {
        initCharts() {
            const charts = echarts.init(this.$refs["charts"]);
            const option = {
                // ...
            };
            // 不传name默认会返回中国地图
            const [mapName, mapJson] = getMap();
            option.geo.map = mapName;
            // 地图注册,第一个参数的名字必须和option.geo.map一致
            echarts.registerMap(mapName, mapJson);
    
            charts.setOption(option);
    
            charts.on("click", ({ name }) => {
                // 这里和上面一样,其实还可以再优化一下。为了方便阅读,这里不再封装。
                const [mapName, mapJson] = getMap(name);
                option.geo.zoom = 0.8;
                option.geo.map = mapName;
                echarts.registerMap(mapName, mapJson);
                charts.setOption(option, true);
            });
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    效果如下:

    请添加图片描述

    结合水印制作级联效果

    现在的地图可以下钻了,但是似乎操作起来还有些别扭。

    我们现在想要的效果是:我们需要每下钻一层,水印部分就会加上当前地区的名称。点击水印地区的名称,就会跳转到当前地区的地图,我们要来改造一下echarts示例的click事件。

    首先option.graphic的默认值修改为中国地图,这里为了方便阅读,仅使用text格式演示:

    // ...
    graphic: [
        {
            type: "text",
            left: "10%",
            top: "10%",
            style: {
                text: "中国",
                font: 'bolder 1.5rem "Microsoft YaHei", sans-serif',
                fill: "#fff",
            },
        },
    ],
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    以数组的形势编写后,思路就明显了,只要在click事件的时候,将下钻地图的信息push进来,并且为了防止重合,稍微移动一下定位即可,我的示例如下:

    charts.on("click", ({ name }) => {
        const [mapName, mapJson] = getMap(name);
        option.geo.zoom = 0.8;
        option.geo.map = mapName;
        // 为了重新定位,这里使用了length
        const idx = option.graphic.length + 1;
        option.graphic.push({
            type: "text",
            left: `${idx * 10}%`,
            top: "10%",
            style: {
                text: name,
                font: 'bolder 1.5rem "Microsoft YaHei", sans-serif',
                fill: "#fff",
            },
        });
        echarts.registerMap(mapName, mapJson);
        charts.setOption(option, true);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    点击后效果如下:

    请添加图片描述

    现在还有问题,就是点击地区名字没有响应,所以我们还要为option.graphic子元素加上click事件

    这个click事件功能也类似,获取地图名称,获取地图数据,重新渲染。但是这个click事件需要注意,比如我点击了北京,那么在数组里是需要将密云区的元素删除掉的,同理,点击中国,则后面的元素都要删除。在这里我就不把相同的部分抽离出来了:

    // 防止graph里频繁添加click事件,在添加click事件之前先全部清空掉。
    charts.off()
    charts.on("click", ({name}) => {
        // 如果option.graphic里已经有了城市名称,则不进行任何操作,防止频繁点击
        const index = option.graphic.findIndex(i => i.style.text === name);
        if (!name || index !== -1) return
        const [mapName, mapJson] = getMap(name);
        option.geo.zoom = 0.8;
        option.geo.map = mapName;
        // 为了重新定位,这里使用了length
        const idx = option.graphic.length + 1;
        option.graphic.push({
            type: "text",
            left: `${idx * 10}%`,
            top: "10%",
            style: {
                text: name,
                font: 'bolder 1.5rem "Microsoft YaHei", sans-serif',
                fill: "#fff",
            },
            onclick: () => {
                // 利用函数的作用域,可以直接拿上面的name来用
                const [grahpName, graphJson] = getMap(name);
                const index = option.graphic.findIndex(i => i.style.text === name);
                // 点击元素之后的所有元素全部删除
                option.graphic.splice(index + 1);
                // 很多操作重复了,你可以将公共部分抽离出来
                option.geo.map = mapName;
                echarts.registerMap(grahpName, graphJson);
                charts.setOption(option, true);
            },
        });
        echarts.registerMap(mapName, mapJson);
        charts.setOption(option, true);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    这里会有个坑,在给graph添加click事件后,点击时会同时触发我们上面charts.on的click事件,想了很久也没有找到好一点的方式来解决这个事件冲突,最后只好判断了一下name是否为空来暂时解决。如果有更好的办法,也请留言。最终效果如下:

    请添加图片描述

    至此绘制地图已经完毕,更多是依靠自己的业务需求来进行更灵活的配置和渲染,它的API没有什么太复杂的,只是我们缺少了一点耐心去实验。

    visualMap

    首先来看效果

    请添加图片描述

    增加visualMap来让地图的数据渲染更有层次感,实现起来也很简单,只需要在option里增加visualMap配置即可:

    const option = {
        // ...
        visualMap: {
            // 是否展示左下角,即是是false,也仅是不显示,不影响数据的映射
            show: true,
            // 上下端文字
            text: ["高", "低"],
            // 最小值和最大值,必须指定
            min: 0,
            max: 6000,
            // 位置
            left: "10%",
            bottom: "10%",
            // 是否展示滑块
            calculable: true,
            // 指定映射的数据,对应的是option.series,这里根据自己的实际需要进行配置
            seriesIndex: [0],
            // 从下到上的颜色
            inRange: {
                color: ['#00467F', '#A5CC82'],
            },
            //字体颜色
            textStyle: {
                color: "#fff",
                map: "china",
            },
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    如果你的代码是跟着我从上面一直写下来的,那么此时你应该发现只是定位的图标变了,相应的地图区域并未变色,所以我们还要把地图的数据映射上去,所以在option.series里再加一个元素,使其type=“map”,内容与geo一致即可,但是要多加data属性,渲染的数据和定位图标一致。并将seriesIndex的索引做好映射,即可实现。

    const option = {
        // ...
        visualMap: {
            // 是否展示左下角,即是是false,也仅是不显示,不影响数据的映射
            show: true,
            // 上下端文字
            text: ["高", "低"],
            // 最小值和最大值,必须指定
            min: 0,
            max: 6000,
            // 位置
            left: "10%",
            bottom: "10%",
            // 是否展示滑块
            calculable: true,
            // 指定映射的数据,对应的是option.series,这里根据自己的实际需要进行配置
            seriesIndex: [0],
            // 从下到上的颜色
            inRange: {
                color: ['#00467F', '#A5CC82'],
            },
            //字体颜色
            textStyle: {
                color: "#fff",
                map: "china",
            },
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    如果你的代码是跟着我从上面一直写下来的,那么此时你应该发现只是定位的图标变了,相应的地图区域并未变色,所以我们还要把地图的数据映射上去,所以在option.series里再加一个元素,使其type=“map”,内容与geo一致即可,但是要多加data属性,渲染的数据和定位图标一致。并将seriesIndex的索引做好映射,即可实现。

    如果出现了缩放重影,说明生成了两个地图组件,需要在新的series里加上geoIndex属性,值是geo里的索引,这样就只会共享一个组件,不会出现缩放重影的问题了

    以下为源文档

    默认情况下,map series 会自己生成内部专用的 geo 组件。但是也可以用这个 geoIndex 指定一个 geo组件。这样的话,map 和 其他 series(例如散点图)就可以共享一个 geo组件了。并且,geo组件的颜色也可以被这个 map series 控制,从而用 visualMap来更改。

    当设定了 geoIndex 后,series-map.map属性,以及 series-map.itemStyle等样式配置不再起作用,而是采用 geo中的相应属性。

    先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦

  • 相关阅读:
    风控模型效能提升的秘籍,这三种相关度的处理方法用好就对了
    【好书推荐】《Python编程:从入门到实践(第2版)》
    谈一谈冷门的C语言爬虫
    顺序表的基本操作
    记一次 .NET某机械臂上位系统 卡死分析
    信息化发展74
    期货开户公司受到证监会的监管
    【数据结构】测试1 绪论
    java命令中的options
    设计模式学习笔记(十)装饰器模式及其应用
  • 原文地址:https://blog.csdn.net/m0_67401660/article/details/126081326