简介:资源提供全国各省市地图边界坐标数据包,面向需要在地图上绘制行政区划边界的前端与数据可视化开发者,可直接用于常用地图组件,免去手工整理坐标点的繁琐流程。包内共36个文件,其中35个JSON格式坐标文件分别对应各省、市、自治区及港澳台地区的边界数据,另含1个JS文件便于页面加载与配置;压缩包整体仅415KB,轻量易集成,结构简洁、命名清晰。每个JSON采用FeatureCollection结构,包含区域编号、名称、中心点及多边形边界点序列,例如北京市密云县的边界数据可直接渲染出完整区域轮廓,开发者无需再做数据清洗。目前已有2479人学习下载,适合快速搭建行政区划地图、制作地理数据可视化大屏或开展地图类前端练习,能明显缩短底图数据准备时间,也适用于教学演示与项目原型开发。 做可视化开发的人,迟早会遇到一个绕不开的需求:画中国地图、省份地图、城市地图。但地图边界这种东西,总不能靠手绘一条条线去描。所谓"中国各省市地图边界坐标 json 文件",本质上就是把每个省、市的地理边界用经纬度坐标点串起来,存成结构化的 json 数据,最常见的载体就是 GeoJSON。这类文件支撑起了 ECharts 注册地图、Leaflet 绘制图层、各种大屏项目的省份高亮、区域下钻、散点聚合等功能,是前端可视化和数据分析链路里最基础也最关键的一环。
这篇文章不是丢给你一个网盘链接就完事,而是把"找数据→清洗→转换→集成→排错"整条链路完整过一遍。我会从数据结构讲起,给出可直接用的代码示例和处理工具,再把我实际踩过的坑整理成排查清单。适合刚开始接触地图可视化的前端开发者、数据分析师,也适合已经被地图数据反复折腾过的同学查漏补缺。
1. 先想清楚:边界坐标 JSON 到底解决什么问题
1.1 为什么不能直接拿地图图片凑合
很多人第一反应是,直接切一张中国地图的 PNG 图片放进页面不就完了?确实,静态展示完全没问题,但一旦涉及交互就露馅了:你要点击某个省份高亮、要在地图上叠加业务数据、要根据数据大小动态调整区域颜色、要放大后边界依然清晰——位图全部做不到。图片放大就糊、区域无法单独响应事件、数据变化就得重新切图,这种维护成本根本没有上限。
矢量边界数据则是另一套玩法。每个省、市的边界是由一串经纬度坐标点连成的闭合多边形,前端拿到这些坐标后实时渲染成图形。用户在图上点击,你得到的是"用户点到了哪个省"这个结构化信息,而不是"用户点到了图片的哪个像素"。数据侧想给某个省标一个数值,只需要把数值和省的名称或者编码对齐。所有交互、联动、缩放都变成纯前端逻辑,这才是地图可视化该有的能力边界。
1.2 GeoJSON 的数据结构到底长什么样
市面上各省市边界 json 文件,绝大多数遵循 GeoJSON 规范。这个格式本身不复杂,核心就是一个嵌套的 json 数组结构,我拆开讲。
最外层通常是 FeatureCollection(要素集合),里面有一个 features 数组,数组里每个元素对应一个省或一个市。每个 feature 由两部分组成:properties 里存属性信息,比如省名、城市名、行政区划代码;geometry 里存实际的边界坐标。
{ "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": { "name": "广东省", "adcode": 440000 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [113.5, 23.5], [114.2, 23.8], [114.8, 24.1] ] ] ] } } ] }这里有两个新手最容易卡住的点。第一,坐标顺序是经度在前、纬度在后,也就是 [longitude, latitude],写成 [经度, 纬度],和日常口语里"北纬23.5度、东经113.5度"的顺序正好相反,很多人在这里掉过坑。第二,geometry 的 type 可能是 Polygon 也可能是 MultiPolygon,如果一个行政区域存在飞地、岛屿,或者边界被切割成多块,就会用 MultiPolygon。coordinates 的嵌套层级也因此不同,Polygon 是四层数组,MultiPolygon 是五层。搞清楚这个层级,你在写代码遍历坐标做简化或者转格式时才不会乱。
2. 数据获取:现成文件哪里来,又该怎么筛
2.1 常见数据源对比
市面上的省市级边界数据来源不少,但质量和更新频率差异很大。我整理了几个常用的渠道,按推荐程度排一下。
| 数据源 | 获取难度 | 数据特点 | 适用场景 |
|---|---|---|---|
| ECharts 官方地图数据 | 低 | 边界经过简化,层级到市级,体积小 | 常规可视化项目、大屏展示 |
| DataV.GeoAtlas(阿里云) | 低 | 按行政区划编码提供 json,更新及时 | 需要单独省份或城市数据时 |
| 高德、天地图 API | 中 | 数据准确度高,但需申请 key 且有调用限制 | 对边界精度要求高的 GIS 项目 |
| 开源仓库(GitHub 维护) | 中 | 数据质量参差不齐,需人工验证 | 二次加工,追求可定制 |
我个人的习惯是:做标准可视化首选 ECharts 和 DataV 的数据,因为它们本身就是为了前端渲染做过简化处理的,坐标点密度适中,直接拿来用不会让页面卡死。如果做 GIS 分析,那宁可花时间申请高德或者天地图的接口拿原始数据,虽然加工成本高一点,但精度有保障。
2.2 下载后第一件事:检查数据质量
拿到文件别急着往项目里塞,先做三件事检查。第一,看 properties 字段是否齐全,重点是 name 和 adcode(行政区划代码)。name 用于和你的业务数据做匹配,adcode 用于做下钻关联,缺了任何一个后面都会很被动。第二,检查坐标范围是否合理。中国范围内的经纬度,经度大致在 73 到 135 之间,纬度在 18 到 54 之间。如果你发现坐标里有几百上千的值,十有八九是数据源坐标顺序反了或者经过了错误投影。第三,确认边界是闭合的,即每个多边形首尾坐标相同,否则渲染时会出现裂缝。
这些检查不需要写复杂脚本,用 Node 跑个小循环,或者直接在浏览器控制台 fetch 下来遍历一遍 coordinates 数组就行。花十分钟做检查,能省掉后面一晚上的排查时间。
3. 处理流程:从原始数据到可上线的 JSON
3.1 坐标点简化,控制文件体积
原始行政边界数据往往非常精细,一个省级单位的边界可能包含上万甚至十几万个坐标点。直接放进前端,json 文件轻松超过几兆,浏览器解析和 Canvas 渲染都会明显卡顿。所以拿到数据后第一件事通常是简化,也就是在不影响边界基本形状的前提下,删掉多余的坐标点。
常用的工具有 Mapshaper,一个开源的地图数据处理工具,支持命令行和网页版。它的核心命令是simplify,跑一遍就能按比例减少坐标点数量。
mapshaper china.json -simplify 1% -o china_simplify.json1%表示保留约百分之一的坐标点,这个比例对省级地图来说通常能保持边界形状可辨认,同时把文件体积缩小几十倍。实际使用中我会根据用途调比例:只做省级填色,1% 足够;要做市级下钻,建议保留 3% 到 5%,因为市级边界更复杂,缩得太多会变形。简化后务必人工扫一眼关键区域,比如沿海线、省界交汇处,确认没有出现明显的边界割裂或尖角。
3.2 格式转换:GeoJSON 不是终点
GeoJSON 是最通用的格式,但不是唯一的选择。不同渲染库对数据格式有各自的偏好,常见的转换需求有三类。
一类是 ECharts 支持的 GeoJSON 直接注册,这个不用转,后面会细讲。第二类是转成 TopoJSON,它通过共享边界的拓扑关系来去重,文件体积比 GeoJSON 更小,适合大型地图数据,但需要在渲染端额外引入topojson-client来解码。第三类是把 json 多边形坐标转成 SVG 的 path 路径,用于静态矢量图输出。之前有读者问"地图 json 转 svg 地图"怎么做,其实就是先读取 GeoJSON 的多边形顶点,用墨卡托或等距投影把经纬度映射到平面坐标,再拼成 SVG 的M、L、Z路径指令。
命令行工具ogr2ogr可以完成多个格式之间的互转,它支持 GeoJSON、Shapefile、TopoJSON 等几十种格式,功能非常强大。不过对纯前端项目来说,我更推荐在网络条件允许时直接用在线转换工具处理零散需求,批量处理再考虑命令行。
3.3 属性字段整理,为下钻和联动铺路
边界坐标只是骨架,真正让地图"活"起来的是 properties 里的属性字段。我建议在数据清洗阶段就把字段统一整理好,至少包含三级信息:name(名称,用于展示和匹配)、adcode(行政区划代码,用于树形下钻和接口关联)、level(省/市/区等级,用于控制交互层级)。有些数据源还带有childrenNum这样的字段,表示下辖子区域数量,做逐级下钻时非常有用。
整理字段时要注意编码一致性问题。比如你的业务数据里写的是"广东",而地图数据里是"广东省",直接匹配必然失败。我的习惯是统一在数据预处理阶段把名称规范化,或者干脆不用名称匹配,全流程用 adcode 作为唯一关联键,名称只用来显示。这个方法在市级下钻场景里尤其省心,因为不同数据源对城市名称的写法五花八门。
4. 在项目里真正跑起来
4.1 ECharts 注册地图的完整示例
ECharts 是前端做地图可视化最常用的库,它本身不内置地图数据,需要先用registerMap把 GeoJSON 注册进去。我给出一个完整的可运行示例。
import * as echarts from 'echarts'; // 1. 加载 json 文件 // Vite 环境可以直接用 ?url 引入,或者用 fetch + import const res = await fetch('/maps/china.json'); const geoJson = await res.json(); // 2. 注册地图,第二个参数就是 GeoJSON 对象 echarts.registerMap('china', geoJson); // 3. 初始化图表并配置 const chart = echarts.init(document.getElementById('map')); chart.setOption({ geo: { map: 'china', roam: true, // 允许缩放拖拽 itemStyle: { areaColor: '#e8e8e8', borderColor: '#a0a0a0' } }, series: [{ type: 'map', map: 'china', roam: true, // data 里的 name 需要和 GeoJSON properties.name 一致 data: [ { name: '广东省', value: 120 }, { name: '浙江省', value: 98 } ], label: { show: true } }] });关键点有两个。一是注册名'china'是自己起的,后面配置里的map字段必须和它一致。二是 series 的 data 中每一项的name要和 GeoJSON 的properties.name完全匹配,否则对应的数据不会渲染到地图上,而且 ECharts 不会报错,只在控制台打一条警告,很容易被忽略。
4.2 省市级下钻的联动实现
下钻的本质是"切换注册的地图数据并重绘"。用户点击某个省份,我们加载该省的市级边界 json,重新注册地图,刷新图表。实现上需要额外监听点击事件。
// 省级地图点击事件 chart.on('click', (params) => { const adcode = params.value?.adcode || getAdcodeByName(params.name); // 按需加载市级 json,adcode 是省份编码的前两位 const cityGeoJson = await loadMapDataByAdcode(adcode); echarts.registerMap('city', cityGeoJson); chart.setOption({ geo: { map: 'city' }, series: [{ type: 'map', map: 'city', data: cityData }] }); });这里推荐按需加载而不是一次性加载全国所有市级地图。一个省动辄几十个市,全部加载可能多出好几兆数据,首屏体验很差。更合理的做法是维护一个映射表,点击时根据省份 adcode 动态 fetch 对应的市级 json,并用 Redis 或浏览器缓存做二次加载加速。返回上一级则重新注册省级地图即可,逻辑对称,不易出错。
4.3 渲染性能优化
地图卡顿的原因通常有三个:坐标点太多、Canvas 重绘太频繁、多个图表实例同时存在。对应的方案也很明确。一是在数据侧做简化,前面提到的 Mapshaper 就是干这个的,我建议把简化写进数据处理的固定流程里,而不是每次手动操作。二是关闭不必要的动画,animation: false在某些高频交互场景下能明显提升帧率,尤其是区域 hover 响应。三是复用图表实例,不要每次数据变更都dispose再init,那样会不断创建新 Canvas,内存占用飙升。
还有一个容易被忽视的点:如果地图区域不需要点击事件,就把series的silent设为true,只保留geo组件的交互,能减少事件绑定带来的开销。实测下来,这些优化叠加起来,对一个包含几百个区县的地图,首屏渲染时间能压缩一半以上。
5. 实战中踩过的坑
5.1 地图区域不显示
这是我在社区里被问到最多的问题。大部分情况是 json 没有成功注册。有人用fetch拿到了数据但忘记await,有人把 json 文件放进了public目录却在src里 import,还有人把geoJson对象传错给了registerMap。排查思路很简单:在registerMap前后分别打印一下数据,确认拿到的是对象而不是字符串。如果 json 是以字符串形式存在,必须先JSON.parse再注册,直接传字符串会导致渲染区域空白。
5.2 区域错位、边界扭曲
表现是地图整体位置不对,或者某个省飞到了海里。多半是坐标顺序写反了,GeoJSON 要求 [经度, 纬度],但有的数据源在生成时写成了 [纬度, 经度]。我处理过一份数据,整体经度纬度数值调换后地图才正常显示。另一个常见原因是坐标系不统一,GeoJSON 通常使用 WGS84 经纬度坐标,但某些来源给的是 GCJ-02(火星坐标系)或偏转了若干米的坐标,直接拿 WGS84 的地图底图去叠必然错位。遇到这种情况,没有别的办法,只能通过坐标纠偏接口或者专业工具把数据统一到同一套坐标系。
5.3 JSON 解析失败与编码问题
json 文件加载后解析失败,常见原因有三个。一是文件里混入了 BOM 头,JSON.parse会直接抛错,处理办法是用文本编辑器另存为 UTF-8 无 BOM 格式。二是文件本身带了注释或者尾逗号,这在某些手写的 json 里很常见,但标准 JSON 解析器不认。三是文件过大导致fetch超时或内存溢出,这时需要回到第 3 节做简化。另外,有读者问过浏览器加载本地 json 文件被拦的问题,这是因为浏览器的安全策略默认禁止页面跨域读取本地文件,开发阶段可以起一个本地静态服务解决,而不是直接双击打开 HTML 文件。
我整理一个排查速查表,方便大家对照。
| 现象 | 直接原因 | 快速验证方法 | 解决办法 |
|---|---|---|---|
| 整块地图空白 | json 未正确注册 | 打印 registerMap 前后数据 | 确认数据是对象,先 JSON.parse |
| 数据没渲染上 | name 与 properties 不匹配 | 控制台查看 series 名称匹配警告 | 统一用 adcode 关联或对齐名称 |
| 边界错位 | 坐标顺序/坐标系不对 | 抽查一个坐标值范围 | 交换经纬度或做坐标纠偏 |
| 页面卡顿 | 坐标点过多 | 查看 json 文件体积 | Mapshaper 简化到 1%~5% |
| 解析报错 | BOM/注释/尾逗号 | 用格式化工具重新保存 | 另存为 UTF-8 无 BOM |
6. 关于数据维护的一些个人建议
地图数据不是一次下载就能管一辈子的事情。行政区划调整时有发生,比如某些地区升级、合并、更名,如果不定期更新数据,地图上的区域和你的业务数据会慢慢对不上。我现在的习惯是,在项目里保留一份"数据来源登记表",记录每个 json 文件的获取时间、来源地址、坐标版本,并在代码里通过 adcode 版本号做接口协商。这样即使某天数据源变更,也能快速定位影响范围,不至于上线后才发现某个区域的统计数字挂到了错误的位置上。
另外一个心得是,不要把地图数据硬编码进业务代码里,尤其是市级、区县级这种会频繁更新的数据。把地图 json 独立成静态资源,按adcode.json的方式命名,由构建流程统一拷贝到静态目录,既方便版本管理,也方便后续单独更新,不需要动业务代码重新发版。
最后说一个很多人会忽略的细节:做非浏览器端项目(比如跨平台桌面应用)时,json 文件的读取路径不能用浏览器域的写法,需要用文件系统 API 读取后转成字符串再解析。这两种环境下fetch的表现完全不一样,我在迁移项目时被这个问题卡过两次,提前封装一个统一的数据加载函数,能省掉不少重复排错的时间。
本文还有配套的精品资源,点击获取