尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

echarts地图实战全攻略:从GeoJSON到series-map与geo组件

echarts地图实战全攻略:从GeoJSON到series-map与geo组件 做前端可视化的朋友只要牵扯到地理数据基本都绕不开 echarts地图。后台监控大屏、城市人流分析、门店选址评估、疫情热力图——凡是带着空间属性的数据第一反应就是用地图来呈现。但说实话很多人第一次接触 echarts 地图时都挺懵的官方示例打开是一个完整的中国地图代码拷下来却只看到空白想画一个市级地图又不知道数据去哪找好不容易画出来了标点不显示、tooltip 换行失效、区域点击没反应……一堆问题轮着来。这篇实战笔记就是来解决这些事的。我会带你从零开始把 echarts 地图的完整链路打通底图数据从哪来、GeoJSON 怎么处理、series-map 和 geo 组件怎么选、业务散点怎么叠加、markPoint 标注怎么落地、tooltip 怎么换行、以及最常踩的几个坑怎么排查。内容不搞概念堆砌全部基于我实际做过的项目经验示例代码直接拷贝就能跑。适合刚接触 echarts 地图的初学者也适合想系统梳理一遍地图可视化逻辑的开发者。1. 地图方案选型series-map 与 geo 组件怎么选先说结论echarts 画地图有两种主流姿势一种是用series-map系列直接渲染地图另一种是用geo组件作为地理坐标系底层再叠加scatter、effectScatter、lines等系列。两者看着都能出图但底层逻辑和适用场景差别挺大。1.1 series-map 的适用场景series-map是最直观的方案把地图数据直接作为系列配置传入每个区域对应一份数据通过itemStyle控制区域样式、label显示区域名、visualMap做颜色映射。这种方案非常适合区域着色类需求比如全国各省的 GDP 分布、各城市的销量对比、各区县的人口密度。它的核心优势是配置简单心智负担小。你只需要准备一份国家或地区的 GeoJSON配置一个map: china告诉 echarts 我要画的是哪个已经注册过的地图剩下的区域填充、边框、高亮都交给内部处理。对于纯静态展示或只做区域对比的页面用 series-map 可以很快出效果。但它也有明显的短板series-map 在交互层面比较重每一个区域都携带了注册时的区域数据想在上面额外叠加散点、飞线、涟漪特效时通常还得单独再配置一个 geo 组件或另一套 series反而绕了远路。1.2 geo 组件与散点叠加如果你要做的不是区域涂色而是以地图为底层展示点位分布、路径流动、热力扩散那我建议直接用geo组件。geo 组件负责把地图底图画出来然后你可以自由地在同一个 option 里挂多个系列通过coordinateSystem: geo把数据点的经纬度映射到地图上。这套玩法的好处是底图和业务数据完全解耦。底图只管好看——区域颜色、边框、缩放、旋转这些都在 geo 里配置散点、标点、连线是业务层可以随时增删不会互相干扰。我做过一个门店巡店系统底图用 geo 固定员工位置、门店状态、巡检轨迹各用一套 series 叠加后期迭代极其舒服。1.3 混合使用的实战建议实际项目中我经常是二者搭配的。区域热力分布用 series-map 或 geo 内部控制点数据用 scatter 系列控制。判断标准就一条如果你的数据主体是面优先 map如果你的数据主体是点或线优先 geo。面面结合的场景比如既想给省份上色又要在重点城市打点我会把 geo 作为唯一底图再同时配置 map 系列和 scatter 系列让它们共享 geo 坐标系。这种方式配置量稍大但数据关系最清晰出图效果也最好。1.4 ECharts 5.x 的注册机制这里的注册是个高频关键词。无论用哪种方案你都要先告诉 echarts我要用一份名字为某标签的地理数据。在 ECharts 5.x 里注册 API 是echarts.registerMap(china, geoJson)第一个参数是地图名称和后续配置里的map: china遥相呼应。很多新手把 GeoJSON 加载进来却忘了注册或者注册名和配置名不一致导致地图区域一片空白——这个坑排在所有地图问题的第一位后面我还会展开。提示GeoJSON 是一份体积不小的地理边界数据建议在页面初始化时统一加载注册不要每次渲染 option 都去请求数据避免重复网络开销和注册覆盖。2. 地图数据的获取与处理GeoJSON 才是灵魂echarts 本身只负责画它并不内置任何地理信息数据。你需要把地图的边界数据喂给它。这份边界数据其实就是 GeoJSON一种用 JSON 格式描述地理空间信息的标准结构。说人话就是它用一堆经纬度坐标点围出了每个省、市、区的轮廓echarts 拿到这份轮廓就能画出来。2.1 省级国家级 GeoJSON 获取对国内开发者来说最常用的免费数据源是 DataV.GeoAtlas它提供了全国各省、市、区县级别的 GeoJSON 数据支持在线预览和直接下载。进入对应页面后可以按需选择省级、市级、区县级生成需要的 JSON 文件。这个数据源我用了好几年稳定性不错数据结构也规范是初学者的首选。另外阿里云 DataV 还提供了一个在线 GeoJSON 读取接口基本格式如下https://geo.datav.aliyun.com/areas_v3/bound/{adcode}_full.json其中{adcode}是行政区划代码比如北京市是110000广东省是440000。用这个接口可以直接请求某个省市的最新边界数据不用手动下载文件。不过实际项目里我是建议把 GeoJSON 下载到本地静态资源目录避免运行环境请求外网导致延迟或不可用。2.2 市级区块与自定义区域裁剪有些场景下你想画的范围不是标准的省界或市界比如某个产业园、某个商圈、某条河道沿岸。这种自定义区域怎么处理两种思路如果你有 GIS 背景可以用 QGIS 或 ArcGIS 把行政边界裁剪成自定义区域导出为 GeoJSON再交给 echarts 使用。如果你不会 GIS 工具也可以基于已有 GeoJSON 做几何编辑。比如取某个市的数据然后手动精简 coordinates 数组只保留你关心的子区域轮廓。这个方法改动量大只适合轮廓比较简单的小区域。还要注意一个细节GeoJSON 里的坐标系统通常是 WGS84 或 GCJ02 等地理坐标以经纬度表示。echarts 默认直接按经纬度绘制一般情况下不需要做投影转换。但如果你拿到的数据是平面坐标比如一些 CAD 导出的数据就需要先转换成经纬度否则画出来会严重变形。2.3 数据结构剖析与最小示例一个基础的 GeoJSON 结构长这样{ type: FeatureCollection, features: [ { type: Feature, properties: { name: 广东省, adcode: 440000 }, geometry: { type: MultiPolygon, coordinates: [ [ [ [113.475, 22.173], [113.477, 22.183] ] ] ] } } ] }注意看properties里的name字段这个字段是 echarts 渲染 label 时默认展示的区域名称。如果你的业务数据里城市名和这个 name 对不上比如数据里写的是广东GeoJSON 里叫广东省后续做区域数据映射时就会出现匹配不上的问题。我在项目中踩过这个坑后文会细讲。2.4 坐标系一定要确认这是很多从零开始画 echarts 地图的人最容易忽略的点。GeoJSON 数据的坐标系决定了画出来的地图是否正。国内项目常用的坐标体系有 WGS84GPS 原始坐标、GCJ02国测局加密坐标等。如果你用了基于 GCJ02 的底图数据同时业务点坐标是 WGS84 的 GPS 原始坐标直接叠加就会出现整个地图向某个方向偏移的诡异现象。最稳妥的做法是底图数据和业务点数据来源统一。如果拿到的数据已经是高德或腾讯系坐标就保持统一如果一批点位是 GPS 设备采集的原始坐标就需要先在服务端做坐标转换再传到前端绘制。这属于测绘层面的基础问题但直接影响显示效果提前确认能省去一大把排查时间。3. 样式定制与交互联动让地图有业务感数据到位、坐标系确认干净之后接下来就是让地图看起来像产品而不是一份干巴巴的行政轮廓图。样式的定制和交互的联动是地图可视化项目里最磨人的部分也是真正拉开新手和熟练工差距的地方。3.1 区域颜色与高亮状态的配置详解区域配色的核心是itemStyle和emphasis。itemStyle控制默认状态下的区域填充色、边框色、边框宽度emphasis控制鼠标悬停或点击选中时的状态。itemStyle: { areaColor: #1b2b4a, borderColor: #79b6ff, borderWidth: 1.5 }, emphasis: { itemStyle: { areaColor: #2d5a9e, shadowBlur: 10, shadowColor: rgba(0, 0, 0, 0.5) }, label: { show: true, color: #fff } }这个配置看着简单但有两个小技巧值得注意。第一areaColor不一定是纯色你可以使用 echarts 的渐变对象——new echarts.graphic.LinearGradient(0, 0, 0, 1, [...])用渐变填充会让地图在深色大屏上更有层次感。第二shadowBlur配合阴影色可以在高亮时做出凸起的立体感指定省份像被提起来一样视觉效果非常讨喜。3.2 visualMap 实现数据驱动的颜色映射如果你有数值型业务数据要展示比如各省销量、各区分单量这时候别手动为每个区域写死了直接用visualMap组件。它会把数据区间自动映射到颜色梯度上数值大的区域颜色深数值小的颜色浅也可以反过来用户一眼就能看出差异。visualMap: { min: 0, max: 1000, left: 20, bottom: 20, text: [高, 低], inRange: { color: [#e0f3f8, #74add1, #4575b4] } }配置完成后每个区域的颜色就会根据data里的数值自动变化。这里有个关键点series-map 的 data 项中的name必须和 GeoJSON 里properties.name完全一致。比如数据项写成{ name: 广东, value: 834 }而 GeoJSON 里叫广东省这个省份就会变成透明的灰色因为它匹配不到任何一个区域。3.3 label 区域名的显示技巧区域名的显示默认是label: { show: true }但实际项目里常常需要控制它的显示位置、字体大小和颜色。在地图很大或区域很小的情况下把每个省的名称都显示出来会产生严重的文字重叠观感很差。我常用的处理方式是只在 zoom 到一定级别时显示 label通过geo组件的regions配合label.show做条件控制。或者干脆把 label 关掉用 tooltip 的提示来补充区域名信息视觉上更干净。如果有特殊的视觉需求比如只展示部分重点省份的名称可以配合data数据项里的label: { show: true }属性做逐区域覆盖。这个细节在官方示例里提得不多但在真实的大屏设计里几乎是刚需。3.4 区域点击事件驱动的业务联动地图不只是用来看的还得能用。最典型的交互场景是点击某个省份下方柱状图自动切换为对应省份的数据实现地图和其他图表的联动。echarts 通过myChart.on(click, function(params) {...})返回被点击区域的信息。myChart.on(click, function (params) { if (params.componentType series params.seriesType map) { const provinceName params.name; updateBarChart(provinceName); } });这里有一个容易踩的坑如果你在一个图表实例里同时挂了 map 系列和 geo 组件点击事件可能会触发两次一次是 geo 的点击一次是 map 系列本身的点击。解决方式是在回调里判断params.componentType见上面的代码。这是 echarts 地图联动的经典细节很多教程都没交代清楚。4. 完整实战从零搭建门店分布地图光讲配置不说实例等于白讲。下面我以一个门店分布与覆盖范围页面为例手把手带你把 echarts 地图从零画出来。目标很明确页面加载时先渲染一张中国地图各省份按门店数量着色重点城市用散点突出重要门店用 markPoint 打标鼠标悬停时 tooltip 显示门店详情且自动换行点击省份能联动展示该省门店列表。这张图覆盖了 echarts 地图 90% 的常见需求你可以直接把这套结构套到自己的项目里。4.1 页面骨架与依赖引入首先准备一个基础的 HTML 页面。如果你用的是 Vue 或 React核心思路不变只是把实例挂载放到生命周期函数里。!DOCTYPE html html langzh-CN head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / title全国门店分布大屏/title style html, body, #map-container { width: 100%; height: 100%; margin: 0; background: #0e1a2a; font-family: Microsoft YaHei, sans-serif; } /style /head body div idmap-container/div script srchttps://cdn.jsdelivr.net/npm/echarts5.5.0/dist/echarts.min.js/script script src./geo/china.json/script script src./map-app.js/script /body /html我习惯把 GeoJSON 保存为本地 JS 文件文件里用全局变量暴露数据比如window.CHINA_GEO {...}。这样避免异步加载时序问题脚本一加载完数据就绪反手就可以注册地图。4.2 注册地图与基础 map 系列配置接下来是核心逻辑。在map-app.js中完成三件事获取容器、初始化实例、注册地图。const dom document.getElementById(map-container); const myChart echarts.init(dom); // 假设 geo/china.json 已暴露为 window.CHINA_GEO echarts.registerMap(china, CHINA_GEO); const storeData [ { name: 北京, value: 156, stores: [北京朝阳店, 北京海淀店, 北京大兴店] }, { name: 上海的完整名字, value: 128, stores: [上海静安店] }, { name: 广东省, value: 96, stores: [深圳南山店, 广州天河店] } ];这里我故意留了一个错误示范数据里写的是上海的完整名字而 GeoJSON 里正常应该叫上海。这种不匹配的情况就是我前面警告过的经典问题。真跑起来你会发现上海区域没有颜色数值没有映射上去。修复方式很简单——保持数据名称和 GeoJSON 里properties.name一致。如果你数据源里的名称和标准行政区划名有差异可以在数据预处理时做一个nameMapping也可以用 GeoJSON 里的adcode来匹配按 code 值关联数据比按中文名更稳。4.3 series-map 与 scatter 叠加的完整配置接下来同时配置 map 系列区域着色和 scatter 系列重点城市打点共享同一个 geo 坐标系。const option { tooltip: { trigger: item, backgroundColor: rgba(10, 20, 40, 0.85), borderColor: #3d6fb4, textStyle: { color: #fff, fontSize: 13, lineHeight: 22 }, formatter: function (params) { if (params.seriesType map) { let detailList ; if (params.data params.data.stores params.data.stores.length) { detailList params.data.stores.map(function (item) { return div item /div; }).join(); } else { detailList div暂无门店数据/div; } return params.name br/门店数 (params.value ? params.value : 0) br/门店明细 detailList; } if (params.seriesType scatter) { return b params.name /bbr/门店数 params.value[2] br/地址 (params.data.address || ); } return ; } }, geo: { map: china, roam: true, zoom: 1.2, top: 80, bottom: 40, itemStyle: { areaColor: #1b2b4a, borderColor: #79b6ff, borderWidth: 1.5 }, emphasis: { itemStyle: { areaColor: #2d5a9e }, label: { show: true, color: #fff } } }, series: [ { name: 省份门店数, type: map, map: china, geoIndex: 0, data: storeData.map(function (item) { return { name: item.name, value: item.value, stores: item.stores }; }) }, { name: 重点城市, type: scatter, coordinateSystem: geo, symbolSize: function (val) { return Math.max(6, Math.min(24, val[2] / 10)); }, label: { show: true, formatter: {b}, position: right, color: #ffd666 }, itemStyle: { color: #ffd666, shadowBlur: 6, shadowColor: #ffd666 }, data: [ { name: 深圳, value: [114.0579, 22.5431, 80], address: 南山区科技园 }, { name: 杭州, value: [120.1551, 30.2741, 65], address: 余杭区未来科技城 }, { name: 成都, value: [104.0665, 30.5723, 42], address: 高新区天府大道 } ] } ] }; myChart.setOption(option);这套配置跑起来你就能看到一张深蓝底色的中国地图各省份根据门店数呈现不同深浅的颜色三个重点城市上带有金色圆点和城市名悬停能看详细信息还能拖拽缩放。整个页面已经是半成品大屏的水平了。4.4 tooltip 自动换行与富文本排版很多人问 echarts tooltip 怎么换行其实很简单formatter函数里返回值拼接br/就能换行或者返回一个数组echarts 会自动把数组每一项渲染为独立一行。formatter: function (params) { return [ b params.name /b, 门店数 params.value, 区域负责人张伟 ].join(); }如果你希望 tooltip 内容更丰富比如加小标题、分割线、图片链接字符串拼接 HTML 是大杀器。但要注意默认的 tooltip 宽度是自适应文本的如果内容有很多行建议配置extraCssText增加内边距或者设置confine: true防止 tooltip 超出画布被截断。这两个参数在官方文档里属于冷门项但在大屏项目里几乎天天用。4.5 markPoint 标注核心门店还有一个高频词是 echarts map 里的 markPoint。如果只是单纯想在某个经纬度位置打个标用 scatter 就够了但如果你要展示的是这个位置有一个非常重要的事件/门店/设备markPoint 更合适。markPoint 可以挂在 map 系列上也可以挂在 geo 坐标系下的散点系列上我用后者更顺手。series: [ { name: 核心门店标注, type: effectScatter, coordinateSystem: geo, symbol: pin, symbolSize: 28, label: { show: true, formatter: {b}, position: top, color: #fff }, itemStyle: { color: #ff4d4f }, data: [ { name: 总部旗舰店, value: [116.3974, 39.9093, 320], address: 北京市东城区 } ] } ]用effectScatter加symbol: pin配合波纹特效视觉效果就和标注点完全不同——它像是在地图上插了一根图钉并且有脉冲扩散动画非常适合突出核心位置。注意 value 数组里前两位是经纬度第三个可以塞任意业务数值整个数组会在 tooltip 里通过params.value[2]取出来。这是 echarts 地图里最常被忽略的传数技巧。4.6 窗口自适应的细节最后补一个被问了很多次的点浏览器窗口大小变化时地图如何自适应。一行代码搞定。window.addEventListener(resize, function () { myChart.resize(); });但要注意如果地图容器在页面初始渲染时是隐藏状态比如在 Tab 里直接 init 会出现宽度为 0 的异常图表画不出来。这种场景要等容器显示后再调用myChart.resize()或者用setTimeout、nextTick延迟初始化。这是我做了多个管理系统后总结出的血泪经验。5. 高频问题与排查技巧实录地图可视化项目做多了你会发现踩坑的内容高度相似。我把这几年在 echarts 地图上遇到的高频问题整理成了一张速查表顺手附上排查思路希望帮你少走弯路。问题现象主要原因解决方案地图区域空白或只有边框GeoJSON 未注册或注册名与配置名不一致确认registerMap已调用且map名完全一致区域不显示数值颜色data 的 name 与 GeoJSON 的 properties.name 不匹配统一名称或用 adcode 等唯一标识做映射散点/标点不显示系列未设置coordinateSystem: geo在 scattter / effectScatter 系列配置中加上该字段标点位置偏了经纬度坐标系不一致确保底图与业务点坐标统一提前做好坐标转换tooltip 不换行formatter 未返回 HTML 字符串拼接br/或返回数组tooltip 被画布截断tooltip 超出图表容器边界设置confine: true或调整extraCssText饼图 labelLine 末尾小圆点偏移饼图配置中labelLine属性细节问题使用labelLayout调整端点和标签间距pxtorem 对 echarts 没起到效果echarts 使用 canvas 绘制不响应 css 字体转换直接在 echarts 配置中控制字体大小不依赖全局 rem5.1 地图空白和名称匹配的排查顺序遇到地图空白我的排查顺序是打开浏览器控制台看有没有报Map china not exists之类的错误。如果没有报错但地图依然空白再在调试面板手动打印一下echarts.getMap(china)看返回是否为空对象。这一步基本能定位 90% 的问题要么没注册要么注册时传入的 GeoJSON 是空数据要么注册名写错了。这三个问题在实际中出现的频率排序大约是 5:3:2前两个最多。名称匹配问题则要细心一点。我建议在数据接入前先写一段预处理脚本把业务数据里的行政区划名统一成 GeoJSON properties 里的行政区划标准名称。如果数据量很大可以只保留有数据的省份名称用一个映射表做转换。总之不要让广东和广东省这种差异进入运行时等渲染出来再发现就晚了。5.2 pxtorem 与 ECharts 的怪异拉扯热词里有一条是pxtorem 对 echarts 没起到效果 vue3。这个现象很典型某些项目里全局配置了 postcss-pxtorem页面上的字体、间距都自动转 rem但 echarts 里的字号、图表尺寸纹丝不动。原因很简单echarts 的绘制是在 canvas 上进行的字体大小和图形尺寸是在setOption时被写进 canvas 内部的和 CSS 的 rem 转换完全不沾边。echarts 图表本身只是一个 el 节点它的宽高由外层容器决定容器如果设置了 rem宽度是会跟着变的但图表内部所有文本、图形参数都来自配置对象。解决方案分两种一是如果你需要 echarts 的字体跟随视口缩放就得自己监听 resize按比例动态调整textStyle.fontSize之类的配置再重新 setOption二是干脆把 echarts 当作局部模块保持固定像素值不参与全局 rem 转换。对大多数大屏项目来说第二种更省心因为大屏的缩放通常由整体页面 transform 控制不需要 echarts 内部逐项自适应。5.3 折线图 x 轴刻度与地图联动的共性问题echarts 折线图 x 轴刻度这个词也经常和地图一起出现因为大屏页面基本是地图配折线的搭配。折线图 x 轴刻度异常最常见的原因是数据里的 x 轴值是字符串而非数值或者时间字符串格式不统一导致 echarts 把它当成类目轴处理而表现异常。排查方法也很直接把 x 轴数据的JSON.stringify打印出来检查是否有多余空格、日期格式不统一、null 值混入。另外一个和地图相关的点是折线图和地图共用一个 resize 事件时如果有多个图表实例一定要逐个resize()不要只处理最后一个实例。我在做数据大屏时就踩过这种低级错误窗口缩放后地图正常、折线图不动检查发现循环里写错了实例变量把同一个myChart覆盖了多次。图表实例当页面里超过一个时建议用数组或对象统一管理。5.4 数据更新后地图不刷新的处理项目做到后期地图数据通常是动态请求的。很多人会把新请求到的数据直接赋给 data以为 echarts 会自动更新结果发现地图纹丝不动。原因在于setOption默认是合并模式对于 map 系列的名称和值如果新旧数据结构不同合并策略可能不触发完整更新。最稳妥的方式是使用myChart.setOption(option, true)第二个参数传true表示完全覆盖旧配置强制重新计算渲染。也可以用myChart.clear()后再 setOption但要记得重新注册地图clear 不会清掉注册数据不用担心。我个人的习惯是地图数据变化时尽量只更新变化层比如只改scatter系列的 data通过myChart.setOption({ series: [{ data: newData }] })局部更新这样性能更好也不会影响底图样式。这个技巧在数据实时刷新的大屏里非常有用。5.5 关于地图数据时效性的提醒最后补充一个比较隐蔽的问题GeoJSON 数据是有时效性和版本的。行政区划发生过调整的区域比如县级市改区、新区划成立旧版本 GeoJSON 里的边界和名称可能和新数据对不上导致某些区域画不出来或名称漂移。所以每次上线前我建议重新确认一下地图数据的版本最好能标注获取日期。尤其是有民政区划调整的年份别等到业务方反馈某市的位置怎么是空的才回头查数据。6. 做地图可视化的几点个人心得从零开始把 echarts 地图跑通并不难真正难的是把地图做得像产品。配色要克制、层级要清晰、交互要给反馈、数据要有效。我第一次做全国热力图时把每个省都涂得花花绿绿结果业务方看了一脸嫌弃后来才明白地图的首要任务是让读者一眼看到差异而不是把每个区域都打扮得花枝招展。颜色深浅要与业务数值强度对应能不用渐变就不用除非视觉稿要求。还有一点是数据的预处理前置化。现在我做任何带地理信息的项目都会先写一道清洗脚本统一行政区划名称、清洗异常经纬度、剔除超出地图范围的数据点。这个过程虽然繁琐但能把 80% 的线上问题提前消灭在数据层。等你经历过一次地图上某座城市凭空消失了的线上事故就明白我说这话的含金量了。另外如果你打算长期做 echarts 可视化建议把常用的地图数据、配色主题、基础配置沉淀成自己的工具库。不同项目之间直接复用能省下大量重复工作。我自己的电脑里就存了一份全国省市县的 GeoJSON 备份、一套深色大屏地图主题、以及封装好的 Vue 和 ECharts 地图组件每次新项目打开就能开工这才是从零开始画 echarts 地图之后真正应该沉淀下来的东西。
返回列表