教你从零开始画echarts地图-程序员宅基地

技术标签: echarts  JavaScript  前端  vue.js  

echarts地图制作

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

基础配置

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

<template>
  <div class="content">
    <div ref="charts" style="width: 1000px; height: 800px"></div>
  </div>
</template>


<script>
import * as echarts from "echarts";
import zhongguo from "@/assets/mapJson/data-city.json"
export default {
  created () {
    this.$nextTick(() => {
      this.initCharts();
    })
  },
  methods: {
    initCharts() {
      const charts = echarts.init(this.$refs["charts"]);
      const option = {
        // 背景颜色
        backgroundColor: "#404a59",
        // 提示浮窗样式
        tooltip: {
          show: true,
          trigger: "item",
          alwaysShowContent: false,
          backgroundColor: "#0C121C",
          borderColor: "rgba(0, 0, 0, 0.16);",
          hideDelay: 100,
          triggerOn: "mousemove",
          enterable: true,
          textStyle: {
            color: "#DADADA",
            fontSize: "12",
            width: 20,
            height: 30,
            overflow: "break",
          },
          showDelay: 100
        },
        // 地图配置
        geo: {
          map: "china",
          label: {
            // 通常状态下的样式
            normal: {
              show: true,
              textStyle: {
                color: "#fff",
              },
            },
            // 鼠标放上去的样式
            emphasis: {
              textStyle: {
                color: "#fff",
              },
            },
          },
          // 地图区域的样式设置
          itemStyle: {
            normal: {
              borderColor: "rgba(147, 235, 248, 1)",
              borderWidth: 1,
              areaColor: {
                type: "radial",
                x: 0.5,
                y: 0.5,
                r: 0.8,
                colorStops: [
                  {
                    offset: 0,
                    color: "rgba(147, 235, 248, 0)", // 0% 处的颜色
                  },
                  {
                    offset: 1,
                    color: "rgba(147, 235, 248, .2)", // 100% 处的颜色
                  },
                ],
                globalCoord: false, // 缺省为 false
              },
              shadowColor: "rgba(128, 217, 248, 1)",
              shadowOffsetX: -2,
              shadowOffsetY: 2,
              shadowBlur: 10,
            },
            // 鼠标放上去高亮的样式
            emphasis: {
              areaColor: "#389BB7",
              borderWidth: 0,
            },
          },
        },
      };
      // 地图注册,第一个参数的名字必须和option.geo.map一致
      echarts.registerMap("china",zhongguo)

      charts.setOption(option);
    },
  },
};
</script>

这是最基础的配置,外加了一些我自己写的样式,使地图美观一些,如果你完全的复制,并且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] },
    //   ],
    // },
],

两种渲染方式如下:

请添加图片描述

请添加图片描述

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

const option = {
    
    // ...
    tooltip: {
    
    // ...
        formatter(params) {
    
            return `地区:${
      params.name}</br>数值:${
      params.value[2]}`;
        }
    }
}

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

嵌入文字

使用option.graphic可以实现简单的水印效果

const option = {
    
    // ...
    graphic:{
    
        // 水印类型
        type: 'text',
        // 相对于容器的位置
        left:'10%',
        top: '10%',
        // 样式设置
        style: {
    
            // 文本内容
            text: "create by cRack_cLick",
            // 字体粗细、大小、字体
            font: 'bolder 1.5rem "Microsoft YaHei", sans-serif',
            // 字体颜色
            fill: "#fff"
        }
    }
}

效果如下:

请添加图片描述

利用graphictype=“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 cRack_cLick",
                font: 'bolder 1.5rem "Microsoft YaHei", sans-serif',
              },
            },
          ],
        },

请添加图片描述

地图下钻

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

为了试下点击下钻,我们需要先了解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)
        }
    })
}

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


参数:

调用方式:

chart.setOption(option, notMerge, lazyUpdate);

或者

chart.setOption(option, {
    
    notMerge: ...,
    lazyUpdate: ...,
    silent: ...
});
  • option

    图表的配置项和数据。

  • notMerge

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

  • lazyUpdate

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

  • silent

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


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

另外在echarts v3.x的版本里,切换地图默认是有过渡动画的,而v4.xv5.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]
}

需要建立两个字典,一个是汉字和拼音的对照映射,一个是拼音和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);
        });
    }
}

效果如下:

请添加图片描述

结合水印制作级联效果

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

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

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

// ...
graphic: [
    {
    
        type: "text",
        left: "10%",
        top: "10%",
        style: {
    
            text: "中国",
            font: 'bolder 1.5rem "Microsoft YaHei", sans-serif',
            fill: "#fff",
        },
    },
],

以数组的形势编写后,思路就明显了,只要在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);
});

点击后效果如下:

请添加图片描述

现在还有问题,就是点击地区名字没有响应,所以我们还要为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);
});

这里会有个坑,在给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",
        },
    }
}

如果你的代码是跟着我从上面一直写下来的,那么此时你应该发现只是定位的图标变了,相应的地图区域并未变色,所以我们还要把地图的数据映射上去,所以在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",
        },
    }
}

如果你的代码是跟着我从上面一直写下来的,那么此时你应该发现只是定位的图标变了,相应的地图区域并未变色,所以我们还要把地图的数据映射上去,所以在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中的相应属性。


很多人找我要源码,博主的本意是希望大家可以自己实验,加深印象。就我个人来说,我拿到源码后就懒得去研究了,总觉得自己已经会了。但介于可能很多人还是刚刚接触,对很多内容并不熟悉,所以在这里贴一下源码,只不过因为时间久远,已经找不到写文章时的代码了,这里贴的是从项目里抽出来的代码,可能会有一些冗余,所以大家尽量参考文章来理解:源码地址

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/SkelleBest/article/details/121204097

智能推荐

稀疏编码的数学基础与理论分析-程序员宅基地

文章浏览阅读290次,点赞8次,收藏10次。1.背景介绍稀疏编码是一种用于处理稀疏数据的编码技术,其主要应用于信息传输、存储和处理等领域。稀疏数据是指数据中大部分元素为零或近似于零的数据,例如文本、图像、音频、视频等。稀疏编码的核心思想是将稀疏数据表示为非零元素和它们对应的位置信息,从而减少存储空间和计算复杂度。稀疏编码的研究起源于1990年代,随着大数据时代的到来,稀疏编码技术的应用范围和影响力不断扩大。目前,稀疏编码已经成为计算...

EasyGBS国标流媒体服务器GB28181国标方案安装使用文档-程序员宅基地

文章浏览阅读217次。EasyGBS - GB28181 国标方案安装使用文档下载安装包下载,正式使用需商业授权, 功能一致在线演示在线API架构图EasySIPCMSSIP 中心信令服务, 单节点, 自带一个 Redis Server, 随 EasySIPCMS 自启动, 不需要手动运行EasySIPSMSSIP 流媒体服务, 根..._easygbs-windows-2.6.0-23042316使用文档

【Web】记录巅峰极客2023 BabyURL题目复现——Jackson原生链_原生jackson 反序列化链子-程序员宅基地

文章浏览阅读1.2k次,点赞27次,收藏7次。2023巅峰极客 BabyURL之前AliyunCTF Bypassit I这题考查了这样一条链子:其实就是Jackson的原生反序列化利用今天复现的这题也是大同小异,一起来整一下。_原生jackson 反序列化链子

一文搞懂SpringCloud,详解干货,做好笔记_spring cloud-程序员宅基地

文章浏览阅读734次,点赞9次,收藏7次。微服务架构简单的说就是将单体应用进一步拆分,拆分成更小的服务,每个服务都是一个可以独立运行的项目。这么多小服务,如何管理他们?(服务治理 注册中心[服务注册 发现 剔除])这么多小服务,他们之间如何通讯?这么多小服务,客户端怎么访问他们?(网关)这么多小服务,一旦出现问题了,应该如何自处理?(容错)这么多小服务,一旦出现问题了,应该如何排错?(链路追踪)对于上面的问题,是任何一个微服务设计者都不能绕过去的,因此大部分的微服务产品都针对每一个问题提供了相应的组件来解决它们。_spring cloud

Js实现图片点击切换与轮播-程序员宅基地

文章浏览阅读5.9k次,点赞6次,收藏20次。Js实现图片点击切换与轮播图片点击切换<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title></title> <script type="text/ja..._点击图片进行轮播图切换

tensorflow-gpu版本安装教程(过程详细)_tensorflow gpu版本安装-程序员宅基地

文章浏览阅读10w+次,点赞245次,收藏1.5k次。在开始安装前,如果你的电脑装过tensorflow,请先把他们卸载干净,包括依赖的包(tensorflow-estimator、tensorboard、tensorflow、keras-applications、keras-preprocessing),不然后续安装了tensorflow-gpu可能会出现找不到cuda的问题。cuda、cudnn。..._tensorflow gpu版本安装

随便推点

物联网时代 权限滥用漏洞的攻击及防御-程序员宅基地

文章浏览阅读243次。0x00 简介权限滥用漏洞一般归类于逻辑问题,是指服务端功能开放过多或权限限制不严格,导致攻击者可以通过直接或间接调用的方式达到攻击效果。随着物联网时代的到来,这种漏洞已经屡见不鲜,各种漏洞组合利用也是千奇百怪、五花八门,这里总结漏洞是为了更好地应对和预防,如有不妥之处还请业内人士多多指教。0x01 背景2014年4月,在比特币飞涨的时代某网站曾经..._使用物联网漏洞的使用者

Visual Odometry and Depth Calculation--Epipolar Geometry--Direct Method--PnP_normalized plane coordinates-程序员宅基地

文章浏览阅读786次。A. Epipolar geometry and triangulationThe epipolar geometry mainly adopts the feature point method, such as SIFT, SURF and ORB, etc. to obtain the feature points corresponding to two frames of images. As shown in Figure 1, let the first image be ​ and th_normalized plane coordinates

开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先抽取关系)_语义角色增强的关系抽取-程序员宅基地

文章浏览阅读708次,点赞2次,收藏3次。开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先关系再实体)一.第二代开放信息抽取系统背景​ 第一代开放信息抽取系统(Open Information Extraction, OIE, learning-based, 自学习, 先抽取实体)通常抽取大量冗余信息,为了消除这些冗余信息,诞生了第二代开放信息抽取系统。二.第二代开放信息抽取系统历史第二代开放信息抽取系统着眼于解决第一代系统的三大问题: 大量非信息性提取(即省略关键信息的提取)、_语义角色增强的关系抽取

10个顶尖响应式HTML5网页_html欢迎页面-程序员宅基地

文章浏览阅读1.1w次,点赞6次,收藏51次。快速完成网页设计,10个顶尖响应式HTML5网页模板助你一臂之力为了寻找一个优质的网页模板,网页设计师和开发者往往可能会花上大半天的时间。不过幸运的是,现在的网页设计师和开发人员已经开始共享HTML5,Bootstrap和CSS3中的免费网页模板资源。鉴于网站模板的灵活性和强大的功能,现在广大设计师和开发者对html5网站的实际需求日益增长。为了造福大众,Mockplus的小伙伴整理了2018年最..._html欢迎页面

计算机二级 考试科目,2018全国计算机等级考试调整,一、二级都增加了考试科目...-程序员宅基地

文章浏览阅读282次。原标题:2018全国计算机等级考试调整,一、二级都增加了考试科目全国计算机等级考试将于9月15-17日举行。在备考的最后冲刺阶段,小编为大家整理了今年新公布的全国计算机等级考试调整方案,希望对备考的小伙伴有所帮助,快随小编往下看吧!从2018年3月开始,全国计算机等级考试实施2018版考试大纲,并按新体系开考各个考试级别。具体调整内容如下:一、考试级别及科目1.一级新增“网络安全素质教育”科目(代..._计算机二级增报科目什么意思

conan简单使用_apt install conan-程序员宅基地

文章浏览阅读240次。conan简单使用。_apt install conan