资讯详情 ECharts 5 中国地图完整实现:从 GeoJSON 获取到省市县下钻实战
📅 2026/10/13 1:02:52
1. 先说明白echarts 中国地图的核心卡点在哪做数据可视化的人应该都有过这种时刻大屏架子搭好了指标算完了结果卡在怎么把中国地图画出来这一步。尤其是用 echarts 的时候5.x 版本开始官方不再内置地图数据想象中的map: china直接变成一张空白画布。更难受的是网上随手搜出来的全国省市json大多是几年前的老文件行政区划早就调整过好几轮下钻到区县时缺边界、错名字成了家常便饭。这个需求我帮人排查过很多次核心其实就三件事第一去哪里拿一份新的、标准的 GeoJSON 数据第二怎么在 echarts 里正确注册并渲染第三真到了生产环境会遇到哪些坑。这篇就把这三件事一次讲透全程基于我实际跑通过的方案不需要注册什么平台账号也不需要申请什么密钥直接抄作业就行。1.1 为什么会卡在地图数据处理很多人一开始以为画中国地图是 echarts 的一个内置功能填个配置就行。这个认知在 4.x 时代确实成立但 5.x 之后官方把地图数据从包里拆掉了理由很简单行政区划数据需要跟随主管部门的调整持续更新塞在框架包里既跟不上节奏又让框架体积白白变大。于是地图数据和图表框架被彻底解耦开发者自己负责搞数据、注册数据。这个设计的本意是更好的但对很多没跟上变化的人来说就成了第一道门槛。更隐蔽的问题在数据本身。行政区划代码和边界数据每年都可能微调网上流传的很多 json 文件还停留在几年前有的县级单位已经合并撤销了。还有些仓库为了压缩体积把部分附属岛屿要素砍掉了这种数据在对外展示的项目里是绝对不能用的。所以数据从哪来比代码怎么写更值得花心思。1.2 这篇文章能帮你解决什么我准备把整条链路完整走一遍先对比几种获取地图数据的方案告诉你哪个亲测可用、为什么可用然后从零到一演示 echarts 里注册地图、渲染底图、上色、做省市区县下钻的完整过程最后把我在真实项目里踩过的坑整理成一份速查表你照着排查基本都能解决。适合看这篇的人前端开发、数据可视化工程师、做大屏看板的独立开发者以及准备在作品集里加一个中国地图下钻场景的同学。看完你可以拿到一套完整可复用的代码逻辑以及一个能即时下载全国省市区县边界数据的公开渠道。2. 全国省市区县 GeoJSON 数据怎么拿三条路对比与亲测方案地图数据获取这件事看着简单实际坑很多。我把网上能找到的主流方案都试过一遍下面直接说结论。2.1 开源仓库、厂商平台、在线数据接口的真实体验第一条路从开源仓库或 npm 包下载现成的china.json。这条路最快但坑也最多。很多仓库最后一次提交还在好几年前期间行政区划已经变过好几轮你拿到的文件跟如今的地图对不上。还有一部分仓库为了省体积把部分边界要素精简掉了这种数据在需要精确展示的场景里完全不能用。如果你只是做个一次性 Demo这条路可以做正经项目不建议赌运气。第二条路用地图厂商的开放平台。问题在于绝大多数平台提供的是 JS SDK 和在线渲染服务你要的是一份静态 GeoJSON 文件这跟他们的产品形态对不上。有的平台即使开放了数据接口也需要申请 Key、配置白名单甚至只允许在线调用不允许下载到本地。对做大屏项目来说运行时还要跨域请求别人的服务器风险和延迟都很不划算。第三条路也是我最终推荐的路子使用一个公开的行政区划数据在线服务。它本质上是一个行政区划选择器页面可以按全国、省、市、区县四级提供标准的 GeoJSON 文件URL 完全规律化不要 Key直接 HTTP 访问就能拿到。这个服务我用了很长时间亲测可以稳定拉取也是社区里做地图下钻时普遍在用的数据源。三条路放一起对比差异很明显方案获取速度数据新旧是否要Key格式标准适合场景开源仓库打包下载快普遍偏旧否参差不齐快速Demo、不追求准确地图厂商开放平台中较新多数要需自行转换在线SDK项目行政区划在线数据服务快随调整更新否标准GeoJSON生产项目、下钻场景2.2 亲测可用的即时获取入口与 URL 规律这个服务的入口业内一般称为 GeoAtlas 行政区划选择器。打开之后页面上就是一张全国地图左边有省级列表你点什么区域页面右侧就会给出对应的 GeoJSON 下载地址。它的数据文件地址非常有规律核心是行政区划代码也就是 adcode。比如全国https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json四川省https://geo.datav.aliyun.com/areas_v3/bound/510000_full.json成都市https://geo.datav.aliyun.com/areas_v3/bound/510100_full.json也就是说你只要知道某个区域的 adcode就能直接拼出它的完整下载地址。adcode 就是统计部门公布的行政区划代码一共 6 位全国是 100000省一级是省代码 四个零市一级再往后接两位。你在选择器页面上点选区域的时候地址栏里也会直接出现对应文件的 URL复制下来就能用不用死记。这里有个高频细节值得单独说URL 里的_full后缀。带_full的文件表示包含下一级行政区的完整边界。比如100000_full是全国带省级边界的版本510000_full是四川带市级边界的版本510100_full是成都带区县级边界的版本。如果你只需要本级轮廓、不想要下一级边界把_full去掉同样能访问体积更小。做下钻交互时用_full做单层展示时用普通版按需取用。还有一点要注意不设区的地级市比如东莞、中山这类直筒子市下钻到市级就到头了没有区县一级文件别再去拼下一层 adcode白费功夫。判断有没有下一级可以看 GeoJSON 里properties.childrenNum这个字段大于 0 就说明还有下级。2.3 数据下载后的完整性检查与本地化文件拿到手后我建议第一时间做三件事。第一打开 JSON 确认是合法的FeatureCollection结构有features数组每个元素里有geometry和properties。第二检查properties里有没有adcode、name、childrenNum这些字段后面写交互逻辑要依赖它们。第三把文件下载到项目静态目录存成本地资源不要每次启动页面都去请求远程地址。这样部署之后加载速度稳定也避免源站万一调整导致线上地图突然白屏。生产环境还要格外注意底图的完整性。对外展示的可视化大屏地图底图必须包含完整的国界线要素和南海诸岛等附属要素。这个在线服务返回的 full 版本默认是带上的但你在网上随便下的第三方精简 json 就未必。所以我养成了一个习惯任何来源的地图数据上线前都做一次完整性检查打开文件搜一下关键要素是否存在不要等到客户在大屏上发现缺了东西才后悔。坐标系也要确认。这个服务的 GeoJSON 用的是国际通用的经纬度坐标与 echarts 的默认处理方式一致不需要额外纠偏。但如果你从其他地理数据库导出的数据一定要先确认坐标系否则后续叠加散点、飞线时点位会和区域边界错位。3. echarts 加载中国地图的完整落地流程数据搞定之后剩下的就是 echarts 的标准操作了。我把从零到可交互的完整流程拆成四步每一步都给出可以直接复制的代码。3.1 版本选择与最小工程搭建先明确一个原则哪怕项目还在用 4.x我也建议尽早按 5.x 的方式组织代码。原因很简单4.x 把地图数据内置在包里看起来省事实际上数据和框架耦合在一起数据更新极不方便5.x 把获取数据和注册地图拆开后续更新地图数据只需要换 json 文件业务代码一行都不用动。这种解耦在维护期能省下大量时间。安装不复杂npm install echarts5如果用 CDN 方式引入直接用 echarts 5 对应的压缩文件即可。注意 5.x 没有任何内置的中国地图注册方法这是很多人一上来就懵的根本原因——不是写错了是少了一步注册。3.2 注册地图与基础渲染一个能跑的最小示例假设你已经把100000_full.json下载下来放到了项目的static/map/china.json。下面的代码是最小可运行版本!DOCTYPE html html langzh-CN head meta charsetUTF-8 / titleecharts 中国地图最小示例/title script src./echarts.min.js/script /head body div idmap stylewidth: 100%; height: 600px/div script fetch(./static/map/china.json) .then((res) res.json()) .then((geoJson) { // 关键步骤把 GeoJSON 注册成 echarts 可识别的地图 echarts.registerMap(china, geoJson); const chart echarts.init(document.getElementById(map)); chart.setOption({ tooltip: { trigger: item }, geo: { map: china, roam: true, itemStyle: { areaColor: #e8f0fb, borderColor: #606c84, borderWidth: 1 }, label: { show: true, fontSize: 10 } } }); }); /script /body /html有两个关键点。第一echarts.registerMap(china, geoJson)里的china是注册名它决定了后面geo.map和series.map里要填的名字两者必须完全一致。很多人白屏就是因为这里写成了China、china-map之类跟注册名对不上。第二geo组件的map属性引用注册名作用是画一个纯底图。如果只需要展示地图轮廓不叠加业务数据用geo就够了配置简单性能也好。在真实项目里geo更常见的用途是当底图在上面叠加散点图、飞线图、涟漪特效这些系列。在打包工程里也可以直接用 import 引入 jsonimport geoJson from ./static/map/china.json; echarts.registerMap(china, geoJson);3.3 数值着色与 visualMap 的正确姿势光有底图没意义大多数场景是要把省份的数值映射成颜色深浅。这种需求要用series里的map系列。它的原理是echarts 根据data数组里每一项的name去 GeoJSON 的 feature properties 里找到同名区域然后按数值大小结合 visualMap 映射到颜色。所以重点来了地图能不能正确上色靠的是 name 的严格匹配。const chart echarts.init(document.getElementById(map)); fetch(./static/map/china.json) .then((res) res.json()) .then((geoJson) { echarts.registerMap(china, geoJson); chart.setOption({ tooltip: { trigger: item, formatter: (params) params.name params.value 万元 }, visualMap: { min: 0, max: 100, left: 20, bottom: 20, text: [高, 低], calculable: true, inRange: { color: [#e8f3ff, #a8c8e8, #3e6fa3] } }, series: [ { type: map, map: china, roam: true, label: { show: true, fontSize: 10 }, emphasis: { label: { show: true, fontWeight: bold } }, data: [ { name: 四川, value: 86 }, { name: 广东, value: 92 }, { name: 北京, value: 75 } ] } ] }); });常见问题是某个省没上色十有八九是data里的name和 GeoJSON 里的name不一致。比如 GeoJSON 里可能写的是内蒙古自治区你数组里写的是内蒙古哪怕肉眼觉得是同一个地方echarts 也只认字符串完全相等。解决办法是给数据做标准化映射或者更简单——直接遍历 GeoJSON 的features把properties.name收集成一份标准名单业务数据在名单基础上填充值从源头杜绝 mismatch。我强烈推荐后者一劳永逸。visualMap 的min和max也要注意。如果写死成 0 到 100而你的真实数据范围只有 60 到 80地图会全是一个颜色看不出差异。正确做法是用数据实际极值const values data.map((item) item.value); const max Math.max(...values); const min Math.min(...values);如果业务上想分档展示比如 0-50 是一档、50-80 是一档用 visualMap 的pieces配置会更直观比连续渐变色更容易读懂。3.4 省到市到区县的下钻联动实现三级下钻是这个需求里最有含金量的部分。实现逻辑其实不复杂监听地图点击事件从 GeoJSON 的 feature properties 里拿到点击区域的 adcode然后拼出下一级 json 地址registerMap注册成新地图再setOption更新视图。// 建议初始化时从全国 geojson 的 features 里动态生成映射不要手写 const adcodeMap { 四川: 510000, 广东: 440000, 北京: 110000 }; chart.on(click, (params) { const adcode adcodeMap[params.name]; if (!adcode) return; fetch(https://geo.datav.aliyun.com/areas_v3/bound/${adcode}_full.json) .then((res) res.json()) .then((geoJson) { // 每次用 adcode 生成唯一注册名避免覆盖旧地图 const newMapName level adcode; echarts.registerMap(newMapName, geoJson); chart.setOption({ series: [ { type: map, map: newMapName, roam: true, label: { show: true }, data: generateCityData(params.name) } ] }); }); });有几个细节必须说明。第一注册名不要反复用china因为同一个名字重复registerMap会覆盖注册表而旧 option 里可能还引用着旧名字容易出现白屏或者地图混乱。用level adcode这种命名方式天然不会冲突。第二adcode 映射表不要手工维护那是灾难。正确姿势是初始化时遍历全国 GeoJSON 的 features每个 feature 的properties里都有adcode和name循环一遍就自动生成了。第三下钻之后把地图中心移到对应区域体验会好很多。GeoJSON 的properties.center字段就是区域中心经纬度chart.setOption({ geo: { center: feature.properties.center, zoom: 6 } });返回上一级也简单用一个数组保存下钻历史点击返回按钮时 pop 出上一级地图信息重新注册并 setOption。如果你不需要交互只是展示静态地图可以直接按需下载对应层级文件绕开下钻逻辑代码量更少。4. 地图高频问题排查与上线避坑实录代码写完只是第一步我见过太多项目在看着能跑和真正能上线之间翻了车。这一节把高频问题和生产细节一起说清楚。4.1 白屏、不上色、错位等 6 类问题速查先把最常踩的 6 个问题整理成速查表现象可能原因解决办法整个区域白屏没调用 registerMap或 map 名字不匹配检查注册名与 geo/series 的 map 值完全一致有背景但没有任何边界GeoJSON 文件损坏或 features 为空用 JSON.parse 校验检查文件完整性部分区域不上色data 的 name 与 geojson 的 name 不一致从 features 动态生成标准 name 名单地图和散点位置错位坐标系不一致统一使用国际通用经纬度坐标不要混用加密坐标标签密集重叠默认 label 全开区县级关闭 label或用 formatter 选择性显示切换下钻后地图空白重复注册同名 map或旧实例未清除每次用 adcode 生成唯一注册名销毁旧实例白屏要分两种排查。第一种是 canvas 有网格线但没地图轮廓说明 geoJson 没读进来或者解析失败优先检查 fetch 路径、服务器有没有正确返回 json 文件第二种是 canvas 完全空白连 tooltip 都触发不了优先检查 echarts 是否引入成功、init容器是否存在。打开控制台看有没有报Component series.map not exists只要看到这句话就是注册环节的问题。4.2 代码之外容易被忽略的 4 个生产细节第一本地调试时如果你直接双击 index.html 用 file 协议打开fetch 会被浏览器跨域策略拦住地图必然加载不出来。解决办法是在项目目录起一个本地静态服务这是前端开发的常规操作但经常有人忽略以为是自己代码写错了。第二大屏项目里一个页面往往有多个图表实例如果同一个容器重复initcanvas 会叠加内存也会一直涨。在 React 或 Vue 工程里组件卸载时务必调用chart.dispose()。切换数据时最好也先 dispose 再重新 init。第三自适应问题。大屏通常要跟随窗口缩放地图也需要跟着动。常规做法是监听 resize 事件调chart.resize()。但注意 resize 只重算画布尺寸不会重算你手动设置过的zoom和center所以大屏适配时可能需要按比例重新设置否则地图会看起来被拉变形。第四数据更新节奏。行政区划代码不是一成不变的每年都可能调整。我习惯在项目里留一份地图数据更新记录把每次更新的时间、涉及区域写清楚。这个习惯看似多余真能帮后续维护的人省下大把排查时间。5. 走通之后批量资源库、大屏组合玩法与心得基础流程通了后面就是效率问题和玩法扩展了。5.1 用脚本批量建立本地地图资源库全国 34 个省级行政区加上几百个地级市、几千个区县如果一个个去页面里点、手动下载浪费时间不说还容易漏。我后来的做法是写一个 Node 脚本利用 adcode 的编号规律批量拉取先获取全国 JSON解析每个省份的 adcode循环拼接 URL 下载省级文件每个省级文件里再解析市级 adcode继续循环。脚本跑完本地就有一套完整的地图资源库之后任何项目要用地图直接复制目录过去从本地读取速度快、稳定性高。脚本的思路用伪代码说就是读一级文件 - 遍历 features 收集下一级 adcode - 并发下载 - 存成map/{adcode}.json。实际操作要注意控制并发数不然几十个请求同时打过去容易触发对方限流同时做好失败重试和断点记录下载到一半断了可以从上次的位置接着跑。5.2 大屏场景的组合玩法与实操心得底图就位后玩法很开放。你可以用geo底图叠加散点系列表示城市热力叠加航线系列做客流或物流飞线叠加涟漪特效标记重大事件点。这些系列的坐标也是经纬度只要保证坐标系和底图一致后端直接返回[lng, lat]就能对上位置。再配合前面的下钻逻辑一个大屏从全国到省、到市的联动框架就完整了。最后说一点个人体会地图类的需求最容易出问题的从来不是 echarts 本身而是数据源。数据源可靠、格式规范、更新及时地图渲染只是几行代码的事。所以我的习惯永远是把地图数据当作一等公民单独管理、单独更新、单独测试不跟业务代码耦合在一起。这套方案我在很多项目里验证过从大型数据平台到单页 Demo 都好用。如果你照着流程走一遍还有卡住的地方优先检查三处注册名、json 路径、name 匹配这三个位置基本能覆盖九成以上的问题。