
简介这套ECharts中国地图JSON资源包为需要做地理数据可视化的开发者与数据分析人员提供全国、省、市边界数据解决地图绘制和地区编码匹配的问题。压缩包共424个JSON文件大小7.96MB覆盖全国总图及省市级地理边界每个文件包含行政区划编码、边界坐标和子区域信息清晰规范可配合echarts.registerMap直接使用。已有3364人浏览学习在同类资源中有较高参考价值。借助按地区编码命名的文件结构能快速检索并关联业务数据配合ECharts配置项可实现区域着色、悬浮提示、点击联动等交互效果也支持将GDP、人口、销量等指标映射到地图上。资源保留了完整的省、地市级层级关系适合下钻与联动展示能降低开发调试成本。1. 中国地图 JSON 文件为什么一张静态 JSON 会让地图项目集体翻车一张中国地图 JSON 文件看起来就是几千行静态数据却能在一夜之间让整个数据大屏白屏、报错、区域错位。实际接手这类需求时最常撞见的情况是文件找了一下午引入后控制台直接报Map not exists地图好不容易渲染出来了业务数据却一团白一个省份都不上色省名要么对不上、要么显示乱码。这篇文章我按自己真实的排查路径来讲——先看懂 GeoJSON 的结构再把它注册进 ECharts最后处理数据绑定和那些绕不开的坑。适合刚接手地图可视化需求的前端开发者也适合数据分析师自己验证数据。目标是让你拿到任意一份中国地图 GeoJSON 之后能在十分钟内跑出第一张图并且能判断问题到底出在文件、配置还是数据上。2. GeoJSON 结构先看清ECharts 拿到文件后到底读取了什么2.1 地图 JSON 与接口 JSON 的根本区别几何对象与属性字段后端接口返回的 JSON 通常是业务数据比如订单列表、用户信息字段随业务定义。而一张中国地图 JSON 是遵循 GeoJSON 标准的地理数据文件它的结构是固定的最外层是一个 FeatureCollection内部包含一个 features 数组每个 feature 代表一个地理区域。一个极简示例大致长这样。{ type: FeatureCollection, features: [ { type: Feature, properties: { name: 北京市, adcode: 110000 }, geometry: { type: MultiPolygon, coordinates: [ [ [ [116.41, 39.92], [117.11, 40.71], [116.91, 40.21] ] ] ] } } ] }这里的properties.name是给这个区域命名的字段ECharts 后续全靠它来匹配你的业务数据。geometry里存的是边界坐标Polygon是单个多边形MultiPolygon意味着这个区域由多个不连续的多边形组成——很多省份有岛屿或飞地所以实际文件里大量使用 MultiPolygon。坐标的嵌套层级很关键Polygon 的 coordinates 是三维数组MultiPolygon 是四维写脚本处理坐标时一定要先判断类型否则下标访问直接越界。初看这份 JSON 时不需要理解每一个坐标点但必须理解一件事ECharts 渲染地图时读的是geometry的坐标匹配业务数据时读的是properties.name。这两块各管各的前者画轮廓后者绑数据不要混在一起排查。我通常拿到文件后第一件事就是打印所有 feature 的 name 列表先确认文件里到底是什么区域粒度——是全国省级边界、还是只有南方几个省份这决定了后面所有配置方向。2.2 ECharts 对地图 JSON 的四个核心诉求坐标系、features、properties 与编码先明确一个前提ECharts 从 5.x 开始不再内置任何地图数据必须由使用者提供 GeoJSON 并主动调用registerMap注册。网上很多旧教程直接写map: china就能出图那是 4.x 时代的效果照搬到 5.x 会白屏。地图 JSON 文件本身的质量直接决定你后续是顺利还是踩坑主要有四个诉求。第一个是坐标系。GeoJSON 标准的坐标顺序是经度在前、纬度在后也就是[lng, lat]。有些 GIS 工具导出的数据是[lat, lng]或者坐标经过了火星坐标偏移这类文件注册后地图会整体旋转或严重变形。判断方法很简单看北京的坐标是不是大约在[116.4, 39.9]附近。如果看到[39.9, 116.4]这种顺序说明经纬度写反了。第二个是 features 完整性。一份正常的全国地图文件应该有 34 个省级行政区域要素加上南海诸岛等附属要素一般在 50 个以内。如果只有十几个 feature大概率拿到的是区域裁剪版。第三个是 properties.name 字段的规范。字段名必须是name不是NAME也不是省值要带完整的行政区划后缀比如广东省而不是广东。大小写不一致、缺省字、带空格或括号都会导致数据匹配不上。第四个是文件编码。地图文件必须保存为 UTF-8 编码如果用 GBK 或 ANSI 保存现代浏览器加载后中文 name 会显示成乱码进而导致所有区域都无法匹配业务数据。用编辑器打开文件右下角通常能看到当前编码批量下载的文件尤其要留意这一项。3. 从白屏到第一张中国地图注册、引入与最小配置3.1 引入地图文件的三种方式全局变量、ESM import 与 fetch拿到一份 china.json 之后怎么把它交给 ECharts 是第一个分岔路口。常见做法有三种分别对应不同的项目形态。第一种是直接把 GeoJSON 赋值给一个全局变量比如把文件内容改写成window.chinaJson {...}然后通过script src./china.js/script引入。这种方式在纯 HTML 页面里最省事没有异步加载问题缺点是手动改文件格式后续更新不方便。第二种是在 Vite 或 Webpack 工程里用import chinaJson from ./map/china.json直接导入。打包工具会把 JSON 解析成对象这种方式最干净但需要确认项目配置允许导入 JSONVite 默认支持旧版 Webpack 需要加json-loader。第三种是运行时用fetch(./china.json)拉取。这种方式最灵活但有两个前置条件页面必须运行在 HTTP 服务下而不是本地file://协议以及服务器需要正确返回application/json的 MIME 类型。我自己的选择标准是单页演示用全局变量工程化项目用 import 或 fetch 二选一。// 方式一全局变量需要把 china.json 手工改成 china.js // window.chinaJson { type: FeatureCollection, features: [] } // 方式二工程内 importVite / Webpack import chinaJson from ./map/china.json // 方式三运行时 fetch页面必须跑在 HTTP 服务下 const res await fetch(./map/china.json) const chinaJson await res.json()fetch 方式在本地调试时最容易翻车很多人直接在文件管理器里双击 HTML 打开页面然后发现地图怎么都渲染不出来打开控制台才发现fetch请求被 CORS 拦截。这不是代码问题是协议限制。本地调试用python -m http.server 8000起一个临时服务即可后面避坑章还会展开说。3.2 用 registerMap 完成地图注册顺序错了地图就白屏ECharts 的registerMap是把 GeoJSON 对象和自定义地图名绑定在一起的过程。绑定完成之后配置项里的map: china才能被识别。这个方法接收两个参数第一个是地图名随便起但必须和后续配置项里series.map的值完全一致第二个是 GeoJSON 对象也就是上一步加载到的数据。import * as echarts from echarts // 第一个参数 china 是注册名第二个参数是 GeoJSON 对象 echarts.registerMap(china, chinaJson) const chart echarts.init(document.getElementById(map-container)) chart.setOption({ series: [ { type: map, map: china, // 这里必须和 registerMap 的第一个参数一致 roam: true } ] })这里最容易踩的坑是注册时机。registerMap必须发生在setOption之前而且必须发生在 ECharts 实例化之后任意时间都行只要保证 setOption 执行时地图已经注册进去。如果用了 fetch 异步加载文件一定要在.then回调里先注册再 setOption很多人把 setOption 写在 fetch 外面文件还没加载完配置就执行了自然白屏。注册名的风格建议统一用小写英文或自定义业务名避免混用导致排查困难。3.3 最小示例一个 HTML 文件跑通全国地图渲染把前面的步骤串起来一个最小可运行示例需要四个文件echarts.min.js、china.json、index.html以及一个本地 HTTP 服务。不要直接在浏览器双击打开 HTML必须用服务方式访问。下面这份代码可以直接复制为一个完整的 HTML 文件。!DOCTYPE html html langzh-CN head meta charsetUTF-8 / title中国地图最小示例/title style #map-container { width: 800px; height: 600px; } /style /head body div idmap-container/div script src./lib/echarts.min.js/script script // fetch 加载同目录下的 china.json fetch(./china.json) .then((res) { if (!res.ok) throw new Error(HTTP res.status) return res.json() }) .then((geoJson) { // 关键步骤注册地图china 是自定义注册名 echarts.registerMap(china, geoJson) const chart echarts.init(document.getElementById(map-container)) chart.setOption({ series: [ { type: map, map: china, roam: true, label: { show: true, fontSize: 10 } } ] }) }) .catch((err) { // 加载失败时把错误打印出来排查路径或跨域问题 console.error(地图文件加载失败, err) }) /script /body /html这段代码里roam: true允许用户鼠标拖拽和缩放地图label.show控制是否显示区域名称。首次调试时这两个配置建议都打开因为如果地图位置偏移你可以通过手动拖拽确认是文件问题还是 viewport 配置问题。如果页面正常渲染出全国地图轮廓说明文件、注册、配置这条链路已经通了接下来才是数据对接。3.4 渲染前的快速自检先看控制台再改配置地图不显示时很多人习惯先调配置项一顿乱改不如先在控制台做三件事。第一件打印已加载 GeoJSON 的基础信息确认文件不是空的。第二件打印所有区域的 name 列表确认数据粒度。第三件确认 ECharts 是否认识这个地图名可以在控制台执行echarts.getMap(china)如果返回对象说明注册成功返回空则说明注册没执行或名称不一致。下面这段代码可以贴到浏览器控制台里直接跑。// 在控制台里检查 GeoJSON 是否有效 fetch(./china.json) .then((res) res.json()) .then((geoJson) { console.log(type:, geoJson.type) console.log(features 数量:, geoJson.features.length) console.log(区域名称列表:, geoJson.features.map((f) f.properties.name)) }) // 检查 ECharts 是否已注册地图 console.log(已注册地图:, echarts.getMap(china))这三步能定位 90% 的白屏问题。如果 features 数量为 0说明文件内容不对或者解析失败如果 name 列表里全是乱码说明编码问题如果echarts.getMap(china)返回空说明注册没生效。控制台给出的信息比任何配置排查都直接。另一个我常用的检查是看 Network 面板里china.json的请求状态码404 说明路径写错200 但地图不显示则多半是注册或配置的问题。4. 业务数据绑定地图name 匹配、visualMap 与 tooltip 联动4.1 数据对接的唯一准绳GeoJSON 里的 properties.name地图轮廓渲染出来之后下一步就是把业务数据绑定到对应的省份上。ECharts 的匹配逻辑很直接series 里data数组的每一项通过name字段去和 GeoJSON 的properties.name做精确匹配。匹配上这个区域就显示对应的值匹配不上这个区域就静默跳过不报错、不提示因此数据丢失非常隐蔽。// 业务数据示例销售业绩 const salesData [ { name: 广东省, value: 11032 }, { name: 江苏省, value: 10245 }, { name: 浙江省, value: 8830 }, { name: 北京市, value: 3150 }, { name: 上海市, value: 2890 } ]常见翻车现场是业务系统里省份存的是简称广东而 GeoJSON 里是广东省一字之差全部匹配失败。另一个高发问题是历史数据里的省份名有空格或全角括号比如新疆维吾尔自治区 尾部带空格。最保险的做法是写一个动态映射函数同时兼容多种叫法而不是手工改数据源。我通常的做法是先用控制台把 GeoJSON 的 name 列表导出来全量比对一遍业务数据把差异项单独维护一个映射表而不是轻信文档或接口文档里写的字段。// 兼容别名映射优先匹配全称再匹配别名 const nameMap { 广东: 广东省, 江苏: 江苏省, 浙江: 浙江省, 北京: 北京市, 新疆: 新疆维吾尔自治区 } function normalizeName(raw) { const trimmed raw.trim() return nameMap[trimmed] || trimmed } const normalizedData rawData.map((item) ({ name: normalizeName(item.region), // region 是接口返回的省份字段 value: item.amount }))这段代码的核心价值在于把不可控的接口数据转换成和 GeoJSON 对齐的标准化数据。注意normalizeName里的trim()消掉了首尾空格这是最常见的隐性坑。如果业务数据里有港澳台或自治区一定要确认 GeoJSON 里对应区域的完整名称。少数地图文件会把区域名写成香港特别行政区而不是香港这类差异在映射表里加一行就能解决。4.2 做一个带 visualMap 分级着色的省份数据地图数据匹配只是第一步展示上通常需要做分级着色把数值映射到不同颜色深度。ECharts 里的visualMap组件就是干这个的。它会把数值范围分成若干段每段对应一个颜色直观呈现各省份之间的数据差异。下面是一份完整的配置示例。const option { tooltip: { trigger: item, formatter: (params) { // params.name 是省份名params.value 是对应的数值 const value params.value null ? 暂无数据 : params.value return params.name value } }, visualMap: { min: 0, max: 12000, left: 20, bottom: 20, text: [高, 低], inRange: { color: [#e8f0fe, #a6c8f6, #4a90d9, #1f5fa8] }, itemHeight: 120 }, series: [ { type: map, map: china, roam: true, label: { show: true, fontSize: 10, color: #333 }, data: normalizedData // 上一步标准化后的数据 } ] } chart.setOption(option)这段配置里visualMap.min和max控制色阶的数值上下限。上限设得太小数据超过上限的省份会全部显示最深色无法区分差异上限设得太大浅色区域过多视觉上没有对比。我习惯的调法是先不设置max让 ECharts 自动取数据的最大值看一下实际分布再手动收窄。inRange.color数组的顺序是从小到大对应浅色到深色数组顺序反了颜色深浅就完全反着来这个细节经常被忽略。4.3 geo 组件与 series-map 的取舍叠加散点还是直接填色搞清楚了 map series另一个绕不开的问题是 ECharts 里还有geo组件两者都能显示地图容易让人混淆。直接说结论series-map适合做区域数据填色因为它直接把 data 绑定到每个区域geo组件适合做底层地图背景配合散点图、涟漪图、航线图来用。最典型的场景是geo 显示中国地图轮廓然后在此之上叠加城市散点、飞线、涟漪效果。const option { geo: { map: china, roam: true, itemStyle: { areaColor: #f5f5f5, borderColor: #aaa } }, series: [ { type: effectScatter, coordinateSystem: geo, // 关键使用 geo 坐标系而不是地图 series data: [ { name: 北京, value: [116.41, 39.92, 100] }, { name: 上海, value: [121.47, 31.23, 80] } ] } ] }一个常见误区是 geo 和 series-map 同时使用且 map 相同容易造成区域双重渲染或事件冲突。如果只需要区域填色就不要叠加 geo如果需要散点叠加在填色地图上可以只用 series-map 并设置散点系列的coordinateSystem: geo但 geo 指向同一份地图。更简单的方式是用 series-map 自己的 label 和 tooltip 完成展示尽量少引入 geo。下表是一个快速选型参考。需求场景推荐方式原因全国各省数据分级着色series-map数据与区域直接绑定配置简单地图加城市散点/飞线geo effectScattergeo 提供坐标系散点按经纬度定位同时填色和飞线series-map geo 同源需要额外处理双层渲染谨慎使用5. 中国地图 JSON 使用避坑五个高频问题的现象与修复5.1 报错 Map china not exists先查注册时机再查配置现象页面一片空白控制台报错Map china not exists。原因基本是两类要么echarts.registerMap没有执行要么执行了但配置项里的地图名不一致。常见诱因是用了 ECharts 5.x 的老教程代码以为地图是内置的完全不写注册代码。另一个诱因是 fetch 异步加载还没完成就执行了setOption注册名还没注册进去。解决方法是先确认代码里存在echarts.registerMap(china, geoJson)并且series.map的值和注册名完全一致。异步加载场景下把注册和setOption一起放进.then回调里。这个坑是出现频率最高的也是最容易修的重点是别在配置上浪费时间。5.2 区域不上色、tooltip 无数据省名不一致和编码乱码是重灾区现象地图轮廓能显示但只有部分省份有颜色没上色的区域鼠标悬停时 tooltip 里只有省份名没有数值。原因大概率是数据里的省份名与 GeoJSON 的properties.name不一致。差异形式有四种简称与全称、尾部空格、全角括号或空格、以及文件本身用 GBK 编码导致中文乱码。解决方法是把 GeoJSON 的 name 列表打印出来和业务数据逐项比对。我处理过一个案例业务数据里写的是内蒙GeoJSON 里是内蒙古自治区差两个字导致整个区域数据丢失。先跑一个比对脚本把两端 name 的差集打出来看到差异后维护一个映射表即可。另一种情况是文件被某些编辑器保存成了 GBK加载后 name 字段变成乱码这种问题在控制台打印 name 列表时一眼就能看出来。5.3 南海诸岛显示错位和消失文件本身与 viewport 的双重嫌疑现象全国地图渲染出来了但地图区域集中在可视区的一角或者地图下方的附属岛屿组跑到地图外面。这里要分清两种不同的问题。如果只是整体地图显示不全调整layoutCenter和layoutSize可以把地图重新居中。如果是南海诸岛跑到奇怪位置多半是文件本身自带的附属区域坐标范围太大导致整张图被压缩。很多地图 GeoJSON 会在左下角附带南海诸岛区域但不同文件处理方式不同有的把南海诸岛合并在主图边界里有的独立成一个 feature。解决方法是先检查 features 列表里有没有独立的南海诸岛要素如果有结合layoutCenter定位。这块没有统一配置需要根据实际文件微调视角范围。5.4 只有一部分省份渲染先确认文件范围再排查 viewport现象地图能显示出来但只有山东、江苏等几个省份其余区域全是空白。这个问题的优先级是先确认文件本身是不是全国版。用控制台打印features.length和 name 列表如果文件里就只有十个省份那再怎么调配置都白搭。确认文件是全国版之后再看 viewport 配置。layoutCenter和layoutSize配合使用可以把地图缩放到指定区域但配置不当会导致大部分省份画在可视区之外。还有一个隐蔽原因是 GeoJSON 的某个 feature 坐标异常比如某个点的经度写成了 999渲染时整个坐标范围被拉爆其他正常区域被挤到屏幕外。这种问题可以在控制台遍历所有坐标检查经纬度是否在合法范围内。5.5 双击 HTML 打开跨域报错fetch 在 file:// 协议下不可用现象本地调试时直接双击 HTML 文件地图区域空白控制台报跨域错误或 fetch failed。原因是浏览器安全策略限制file://协议下发起的 fetch 请求不同浏览器表现不一致Chrome 直接拦截Firefox 某些版本可能放行但会有警告。解决方式有三种。第一种是本地起 HTTP 服务python -m http.server 8000然后访问http://localhost:8000这是最常见的调试姿势。第二种是把 GeoJSON 转成 JS 文件用全局变量方式引入绕开 fetch。第三种是配置打包工具的开发服务器代理但本地调试没必要搞这么复杂。这不是 ECharts 的问题也不是地图文件的问题是 Web 开发的基础知识但确实让很多新手卡了很久。6. 更进一步裁剪、压缩与上线前的验证脚本6.1 区域裁剪与坐标压缩一个脚本同时解决体积和渲染范围生产环境里通常不需要整张全国地图。如果业务只覆盖华东几个省份把无关区域从 GeoJSON 里过滤掉可以明显减少渲染开销和加载体积。同时GeoJSON 里坐标点的精度往往到了十多位小数实际地图缩放展示根本用不到这么高的精度截断坐标小数位能在几乎不影响视觉效果的情况下大幅压缩文件。下面这个脚本同时做两件事按省份白名单裁剪以及按精度截断坐标。// prepare-geojson.js // 用法node prepare-geojson.js china.json output.json const fs require(fs) const [,, inputPath, outputPath] process.argv const geoJson JSON.parse(fs.readFileSync(inputPath, utf8)) // 白名单只保留业务涉及的区域 const keepNames new Set([广东省, 江苏省, 浙江省, 福建省, 山东省]) // 截断坐标精度digits4 时误差约 11 米足够地图展示 function roundCoords(coords, digits) { if (typeof coords[0] number) { return coords.map((v) Number(v.toFixed(digits))) } return coords.map((item) roundCoords(item, digits)) } geoJson.features geoJson.features.filter((feature) { return keepNames.has(feature.properties.name) }) const digits Number(process.env.DIGITS || 4) geoJson.features.forEach((feature) { if (feature.geometry) { feature.geometry.coordinates roundCoords(feature.geometry.coordinates, digits) } }) fs.writeFileSync(outputPath, JSON.stringify(geoJson)) console.log(保留区域数:, geoJson.features.length) console.log(输出文件:, outputPath)这里的roundCoords用了递归写法因为 Polygon 和 MultiPolygon 的坐标层级不同统一递归到最内层的一维数组再处理。digits通过环境变量控制默认保留 4 位小数经纬度 4 位小数对应的精度大约是 11 米在地图上肉眼完全看不出差别。如果文件原始体积特别大可以开到 3 位但超过这个精度就可能有明显的边界锯齿。裁剪时注意白名单里的省份名必须和 GeoJSON 的 name 字段完全一致建议先用上一章的打印方法确认列表再写白名单。6.2 上线前验证脚本类型、name 与坐标范围一次查清发布前花两分钟跑一个验证脚本比上线后被业务方发现在地图上找不着自己省份再回来改要省心得多。验证脚本覆盖三类典型问题文件结构是否完整、区域名称是否规范、坐标是否越界。任何一个检查项报警都需要在发布前处理掉。// validate-geojson.js // 用法node validate-geojson.js china.json const fs require(fs) const [,, inputPath] process.argv const geoJson JSON.parse(fs.readFileSync(inputPath, utf8)) // 1. 顶层结构检查 if (geoJson.type ! FeatureCollection) { console.error(错误type 不是 FeatureCollection) process.exit(1) } // 2. 统计要素数量 console.log(features 数量:, geoJson.features.length) // 3. 检查每个 feature 的 name 与几何类型 geoJson.features.forEach((feature, index) { const name feature.properties feature.properties.name const geom feature.geometry feature.geometry.type if (!name) console.warn(第 ${index} 个要素缺少 name) if (geom ! Polygon geom ! MultiPolygon) { console.warn(要素 [${name}] 几何类型异常:, geom) } }) // 4. 检查坐标是否越界 function validateCoords(coords) { if (typeof coords[0] number) { const [lng, lat] coords if (lng -180 || lng 180 || lat -90 || lat 90) { console.warn(坐标越界: lng${lng}, lat${lat}) } return } coords.forEach(validateCoords) } geoJson.features.forEach((feature) { validateCoords(feature.geometry.coordinates) }) console.log(校验完成无致命错误)坐标越界检查能抓到很多隐蔽的文件损坏问题比如个别坐标点被错误写成 999 或 9999这种问题不检查的话地图渲染时整个 viewport 会被异常坐标撑爆。需要说明的是这是一份通用校验脚本它不校验边界是否与真实国界一致那需要更专业的地理数据工具。业务上使用地图边界文件时建议从可靠来源获取并保留来源信息不要随手改边界数据。我现在的习惯是每接手一个新的地图可视化需求先去控制台打印 name 列表和 features 数量再写白名单和校验脚本最后才碰业务数据。这几个步骤走下来地图这个环节的返工率能降一大半。中国地图 JSON 文件本身不复杂复杂的是它和业务数据之间那层隐形的匹配关系。把文件结构看清楚把匹配规则定清楚地图渲染这件事就能从玄学变成可预期的工程步骤。希望帮到你。本文还有配套的精品资源点击获取