资讯动态

省市区县GeoJSON数据包:从DataV接入到adcode关联的完整实践

发布时间:2026/10/13 17:45:25 来源:尧图企业网站定制
简介这是一份基于阿里云DataV GeoAtlas服务的全国省市区县GeoJSON数据包面向需要中国行政区划边界数据的地图可视化、数据分析及Web GIS开发者。数据以标准GeoJSON格式存储覆盖全国省级、地级和区县级边界适合直接接入DataV、ECharts、Leaflet等工具绘制地图大屏或做区域关联分析。压缩包内共13012个文件其中13010个为json地理边界文件按行政区代码命名另含2个Python爬取脚本可用于后续自助更新数据。整体大小约80.87MB目录结构整理清晰便于按需提取特定区域。数据更新至2020年8月包含“子域、不含子域”两套字段组织方式满足不同精细度绘图需求。目前已有3171人学习下载。无论用于行政区划统计、区域高亮展示还是省市县联动选择这份数据都能显著节省前期数据采集与清洗时间尤其适合已在或准备使用阿里云DataV搭建可视化项目的开发者。1. 省市区县GeoJSON数据包一份DataV专用、解压即用的地图数据做可视化大屏最烦的往往不是图表怎么配而是手头没有一份干净的地图边界数据。之前每次要用国内省市区县的GeoJSON都要临时从DataV.GeoAtlas在线接口拉数据再手动合并成能直接用的json遇到文件名乱码、adcode对不上能折腾一上午。这份阿里专用DataV.GeoAtlas全国GeoJSON省市区县数据包把省级、市级、区县级地图边界一次性打包并区分了子域版本和不含子域版本目录分好、解压就能用。它可以接DataV地图组件做区域热力层也能喂给ECharts这类前端地图库适合做大屏可视化、数据地图、本地地图资产接口的开发者尤其适合不想再写爬虫整理数据的场景。2. 先看懂数据再动手GCJ-02基准、adcode与子域/不含子域的文件差异2.1 为什么这份GeoJSON能直接被DataV识别GeoJSON是一个开放标准但不代表所有符合规范的GeoJSON都能被DataV直接消化。DataV.GeoAtlas返回的数据是一个FeatureCollectionfeatures数组里每一项都是一个完整的行政区要素properties里带name、adcode、center等信息geometry是Polygon或MultiPolygon。DataV的区域热力层和基础地图组件在设计上就是按这种结构解析的所以这份数据包解压之后不需要二次转换直接作为数据源填入即可。如果你自己上网找的GeoJSON是单个Feature或者geometry类型不标准DataV解析器就会报错或干脆不渲染。更关键的是坐标系。DataV.GeoAtlas的边界点基于国内民用坐标系业内一般叫GCJ-02和DataV底图是一套基准所以行政区边界能严丝合缝地贴在底图上。如果你从其他开源仓库拿到的地图边界是WGS-84GPS原始坐标叠加到DataV底图上会整体偏移一小段距离项目里看起来就是“边界和道路对不上”。这一点在后面避坑章节还会细说但请先记住地图数据的坐标基准必须和你的底图一致这比格式是否正确更容易被忽略。还有一点容易被新手忽略DataV渲染时并不只是一个画图工具它内部还会拿feature.properties里的name去做下钻、联动和提示框展示。所以拿到数据包后不要为了省空间把name改掉或删掉否则tooltip会显示不出区域名下钻联动也会断掉。adcode同样不要改动它是行政区划编码DataV的地图服务在很多时候会按这个字段识别区域身份。2.2 子域与不含子域版本按渲染层级来选择数据包里标注的“子域、不含子域”指的是文件里是否包含下级行政区的要素。以“某省级、不含子域”文件为例json里只放该省自身的外边界而“子域版本”会把该省边界和下辖地市的边界全部放进同一个json甚至还会带上更下一级。这样做的好处是一次请求就能拿到完整的父子结构坏处是体积变大、要素增多地图组件在加载时会同时画出多个层级的边界。我一般这样选地图只需要展示当前区域轮廓时用不含子域版本文件小、加载快视觉上也干净。要做省→市→区县逐级下钻联动时用子域版本但要注意每次只加载当前层级的数据而不是把整个子域文件一次性塞进渲染器否则所有层级的边界同时画出来视觉上会糊成一片。下面这个表是我平时做选型时的基本判断对比项子域版本不含子域版本包含内容当前区域下级行政区边界仅当前区域边界文件体积整体更大明显更小渲染效果多层级同时绘制时容易重叠干净清晰适用场景下钻联动、一次性获取父子结构单层静态地图、快速加载解压后你会看到文件大体按行政层级分组命名常见的是“adcode.json”这种方式比如省级文件末尾四个0市级文件末尾两个0区县就是完整的6位adcode。这是一个直观的层级判断方法但不是绝对规则判断一个json对应哪个层级时最可靠的是看文件内properties.adcode而不是文件名。如果文件名是中文我一般会先对照adcode重命名一遍因为中文路径在部分服务器和旧版Windows环境里容易出乱码还会让接口拼URL时多一次encode操作。2.3 先用脚本体检一遍可解析性、重复adcode与非法geometry拿到数据包的第一件事不是急着把json填进DataV而是跑一遍体检。这个体检脚本不需要多复杂但能拦住大多数地图组件的“玄学报错”const fs require(fs); const path require(path); function inspectGeoJSON(filePath) { const raw fs.readFileSync(filePath, utf-8); // JSON.parse同时验证两件事文件能否被解析、是否携带BOM导致首字符异常 const data JSON.parse(raw); const features data.features || []; const invalid features.filter(f !f.geometry || (f.geometry.type ! Polygon f.geometry.type ! MultiPolygon) ); const adcodes features.map(f f.properties f.properties.adcode); const duplicate features.length - new Set(adcodes.map(String)).size; console.log({ file: path.basename(filePath), topType: data.type, featureCount: features.length, duplicateAdcodeCount: duplicate, invalidGeometryCount: invalid.length }); } // 用法示例node inspect.js ./某省级不含子域.json这段脚本里JSON.parse失败时说明文件在解压或保存过程中被改动过常见原因是编辑器以带BOM的UTF-8保存或文件里有不可见字符。topType必须等于FeatureCollectionDataV解析器只认这种顶层类型。duplicateAdcodeCount大于0说明合并文件时可能把两个层级的要素混到了一个文件里页面会出现边界重复。invalidGeometryCount大于0说明geometry类型不是DataV支持的多边形类型基本可以判定文件损坏或来源不对。如果你体检的是全国级别的子域版大文件几十MB的JSON一次性JSON.parse会很吃内存。我看到node报“heap out of memory”时常规做法是加大内存参数或者改成逐条读取features数组做流式校验。但本地体检阶段直接加内存参数更省事node --max-old-space-size4096 inspect.js ./全国子域版.json体检不只是验文件有没有坏更重要的是给你一个基线这一版的adcode范围、feature数量、文件体积是多少方便后续发布时和线上版本做对比。我每次替换数据包版本前都强制跑一遍这个脚本输出存成一个txt半年后再回看就能知道哪些数据更换过、有没有引入重复编码。3. 把它接进DataV地图组件三种加载方式与参数配置清单3.1 静态数据粘贴适合小范围单层展示DataV项目里添加“区域热力层”这类地图子组件后地理数据源常见做法是可以切换到“静态数据”模式直接把GeoJSON粘贴到编辑框。这种方式最简单适合省级范围内的小文件比如单个省市县不含子域的json。我一般只在要素数量不多、数据文件不超过几百KB时用这种方式。超过这个量级编辑器在保存配置时可能会卡住预览时也容易出现长时间转圈。数据如果是复制粘贴进来的还可能引入不可见字符建议粘贴前先在本地的JSON.parse里校验通过再提交。区域热力层里有两个字段容易填错一是“区域名称”字段DataV默认按properties.name去识别名称你如果改成了别的字段名组件就不知道该拿谁当下钻和提示的名称二是“数值字段”它对应的是你业务数据里的value如果业务侧没做关联注入这个值会一直是空。还有一个小习惯静态粘贴适合快速出demo不适合真正的线上发布。因为DataV的配置文本会随着项目保存几MB的JSON会让配置拉取变慢每次打开编辑器都明显卡顿。正式环境还是要走下面这种URL加载方式。3.2 对象存储或公网URL加载正式项目的主流方案更稳妥的做法是把数据包里的json传到对象存储或自己的CDN上生成一个公开HTTP地址然后在DataV的地理数据源里填写这个URL。运行阶段DataV会去这个地址拉取GeoJSON本地不需要再次嵌入大字符串。这种方式下区域热力层的常用参数配置大概是这样配置项推荐值说明地理数据源https接口或CDN地址必须是公网可访问的URL区域名称字段name对应feature.properties.name数值字段value对应业务数据或注入后的properties.value填充色按视觉映射设置用于区分数值高低边界线宽0.51太细则边界不明显太粗会盖住邻区这里藏着一个高频坑这个URL必须允许跨域访问否则DataV控制台预览阶段就能看到浏览器报跨域错误地图组件显示空白。如果你的对象存储默认不允许跨域需要到控制台给bucket配置CORS规则允许来源填“*”或你的DataV域名。上线后如果页面地图空白第一时间先打开浏览器开发者工具看网络请求确认这个GeoJSON是否真的被拉下来、状态码是否是200而不是一上来就怀疑数据有问题。在你自己的后端接口返回GeoJSON时我还会加上缓存协商头。GeoJSON文件一般不会频繁变动设置合适的ETag或Last-ModifiedDataV预览和线上运行时可以少传很多重复流量。如果使用反向代理还要注意代理服务有没有对超大JSON做截断我踩过一次代理默认只放行2MB响应体的坑边界数据被切了一半地图下半部分直接消失。3.3 自建前端项目本地文件直接加载不是所有场景都在DataV控制台里做。如果你打算用这份数据包搭自己的前端项目常规做法是把json放到项目的静态资源目录运行时用fetch加载再交给地图库。ECharts场景下大概是这样的先用fetch拿到json文本JSON.parse后调用echarts.registerMap(区域名, json)注册地图然后在option里用map类型配置地图组件。这里要检查的还是坐标系如果前端底图是WGS-84服务而json是GCJ-02边界就会偏移需要纠偏或统一底图。在Vue/React这类框架里我建议把GeoJSON的加载放到应用初始化阶段而不是组件每次渲染时都请求一次。可以封装一个模块级缓存第一次fetch成功后存进内存后续组件直接读取避免地图组件重新挂载时出现一段空白期。如果你做的是单页应用路由切走再切回来时地图会重新初始化这时候缓存的价值就很明显。在自建服务端时我通常不把全部json暴露给前端一次性下载而是做一个按adcode取数的接口/geojson/{adcode}后端从数据包中读取对应文件并返回前端在下钻切换时只请求当前层级。这样既控制了请求体积也方便在服务端做缓存和权限控制。接口设计时可以顺便做一层校验adcode存在性判断走索引表不存在的编码直接返回404而不是去文件系统里碰运气。4. 用adcode把业务表挂到地图上数据关联、字段注入与下钻结构4.1 adcode是唯一可靠的关联主键做地图可视化时地理边界数据只是底子真正要展示的业务值来自另一张表。把两张表关联起来最稳的键是adcode而不是行政区名称。原因是这种GeoJSON数据包里properties.name常常是“某省”“某市”而业务表里可能写的是简称、带“省”或“市”后缀的变体甚至同一个名字在业务里对应多个区段。名称对不上就关联失败最终地图上大面积空值。我遇到过最典型的名称坑是“市辖区”这个词。在业务表里有些地市下辖的区都被统一写成“市辖区”但GeoJSON数据包是按真实区划拆开的你拿“市辖区”去匹配所有区级要素要么匹配不到要么匹配出来一整个区域没有对应值。这时靠名称关联基本无解只有adminCode能精确区分。还有“某新区”这种业务侧自造的区域名在行政区划数据里根本不存在必须在业务入表环节就做编码规范。所以拿到数据包后我习惯的第一步就是把所有feature的adcode抽成一个Set和业务表里的行政区划编码做差异对比找出业务表里哪些编码在数据包中不存在。如果差异不大可能就是数据包版本比业务表旧更换数据包版本即可如果差异很大说明业务侧用了自造编码需要在接口层做转换而不是在地图前端硬关联。4.2 把业务指标注入Feature的properties如果你用的是DataV区域热力层建议直接把业务指标写进feature.properties.value这样一次数据请求就能完成“边界数值”的加载减少前端关联操作。下面是一段常见做法const fs require(fs); const csv require(csv-parse/sync); // 读取业务表假设是CSV格式至少包含adcode和value两列 const rows csv.parse(fs.readFileSync(./biz.csv, utf-8), { columns: true, skip_empty_lines: true }); // 注意adcode统一转成字符串避免和JSON里的字段类型不一致 const bizMap new Map(rows.map(r [String(r.adcode), Number(r.value) || 0])); const geo JSON.parse(fs.readFileSync(./某省级.json, utf-8)); geo.features.forEach(f { const adcode String(f.properties.adcode); f.properties.value bizMap.has(adcode) ? bizMap.get(adcode) : null; }); fs.writeFileSync(./某省级_with_value.json, JSON.stringify(geo));这段代码逻辑很简单先读业务表以adcode为键做索引然后遍历GeoJSON的features把value写进properties。最后输出的新文件既保留了边界又携带了指标值DataV那边把“数值字段”配置成value即可直接渲染。参数上需要注意三个地方一是adcode必须统一转字符串JSON里的adcode在不同来源中可能是数字也可能是字符串不转换就匹配不上二是value用Number()做一次转换防止CSV里带千分位逗号或空格三是匹配不到的feature保留null而不是删掉这样可以在地图上明显看出哪些区域没有业务数据。如果你的业务数据是数据库查询结果不一定要先生成CSV再从文件读。常见做法是在后端查询时直接按adcode分组然后service层读取数据包的GeoJSON把相同的合并逻辑用内存里的Map完成最后把结果JSON返回给前端。本质上是一样的流程只是把CSV这个中间文件省掉避免多一步文件流转。4.3 省、市、区县三级下钻父子结构怎么生成下钻的地图并不需要一次性加载全部层级的GeoJSON。常见做法是点击某市后前端向服务端请求该市的json替换当前地图数据源。服务端要能快速知道“某市的adcode属于哪个省”这个父子关系可以来自业务侧的行政区划表也可以按adcode前缀推导省级adcode通常末尾四个0市级adcode末尾两个0区县级就是完整6位。用这个规则生成children树结构类似这样{ adcode: xxxx00, name: 某省, children: [ { adcode: xxxxxx, name: 某市, children: [] } ] }生成这个树的目的不是渲染而是用它来维护下钻菜单或面包屑。真正绘制地图时还是按层单独加载json文件不要试图把整棵树直接交给地图组件。下钻交互上我一般还会在请求URL里加版本号参数类似/geojson/{adcode}?v1防止浏览器缓存了旧的边界文件导致数据包更新后地图边界还是老样子。退回到上一层时前端可以缓存已加载的GeoJSON直接恢复上一层的数据源不用再请求一次。如果你用的子域文件已经包含父子边界但仍建议单独做一层索引。因为子域文件体积大每次为了拿下一层边界而把整包数据传下来网络耗时和解析耗时都不划算。业务上只传当前层需要的小文件反而更快。5. 避坑排查坐标偏移、格式报错、子域误用等5个高频问题地图数据看起来简单实际上线时容易翻车的点很集中。下面5条是这份数据包使用中比较常见的故障按“现象、原因、解决”拆开写你可以直接对照排障。5.1 现象边界和底图道路整体偏离越放大越明显原因GeoAtlas数据使用国内民用坐标系GCJ-02而你叠加的业务轨迹或底图是WGS-84坐标系两套基准存在固定偏移这个偏移量在不同经纬度上方向还不完全一致。解决不要让偏移数据同时叠在一张画布上。要么把整个项目统一成GCJ-02基准要么对WGS-84原始数据做坐标纠偏后再接入。不要手动平移边界去“对上”这个偏移不是线性平移手动对齐解决不了全局问题。排查时可以先取地图上一个已知地标道路交叉口对比边界点位和底图道路位置偏差方向一致基本就是坐标系问题。5.2 现象DataV地图组件提示JSON格式无效或解析失败原因常见有三种。一是文件在保存时带了BOM头二是json被编辑过、出现了多余逗号或不可见字符三是你把单个Feature而不是FeatureCollection直接粘贴进了数据源。解决先用第二章的体检脚本JSON.parse一遍确认顶层type是FeatureCollection、features存在。用编辑器另存为“UTF-8无BOM”格式。如果来源文件本身损坏从原压缩包里重新解压一份。这里我还想说一个判断技巧报错位置如果总在文件开头附近大概率是BOM或首字符异常报错位置在文件中间多半是截图时被截断或手动编辑时删掉了大括号。5.3 现象用子域文件做单层展示边界线条又多又乱原因子域版本里同一个json包含了当前区域及所有下级行政区的边界渲染器把所有线都画了出来视觉上自然重叠颜色映射也会因为数据密度大变得看不出层级。解决单层静态展示一律用不含子域版本。如果你需要在下钻时才出现子级边界就在前端联动里根据当前层级动态替换数据源而不是一开始就整包塞入。调试时可以单独检查features的长度如果这个文件你预期只有一个省但features数量是几十上百这就是子域版。5.4 现象把几MB的GeoJSON直接粘进DataV静态数据编辑器卡住或保存失败原因DataV的静态数据编辑框对超长文本处理不友好几MB的JSON会让页面失去响应甚至导致配置保存超时折腾半天等于白干。解决改成URL加载方式把json放到对象存储或CDN在数据源里填公网地址。如果必须在本地编辑只处理单个省市级别的较小文件全国级数据不要尝试粘贴。还有一个折中方案先粘一个精简版小pdf测试链路确认逻辑通了再替换成完整数据源定位问题会快很多。5.5 现象预览或上线后地图空白F12里显示跨域错误原因DataV运行在浏览器环境去拉取GeoJSON地址时服务器响应头里没有允许跨域的配置浏览器拦截了请求。解决为对象存储配置CORS规则允许来源填“*”或你的DataV域名允许方法GET。如果地址是由自己的后端接口返回要在服务端设置正确的Access-Control-Allow-Origin响应头。排查时先确认URL在浏览器里直接访问能拿到正确JSON再确认请求头里跨域配置生效。如果数据接口在HTTP环境而DataV控制台是HTTPS还会有混合内容拦截同样会造成地图空白需要让接口统一走HTTPS。6. 进阶技巧瘦身JSON并生成adcode索引让大屏地图秒开6.1 只保留渲染需要的属性字段GeoAtlas的数据包里每个feature的properties除了name和adcode还会带center、centroid等附加信息。这些字段在打点和标签定位时有用但如果你只做区域热力图它们属于冗余数据。我有一个固定脚本每次使用前会把properties裁剪成只保留name、adcode和可能用到的centergeometry一字不动。这是因为DataV渲染区域边界只认geometry属性字段精简可以让json体积明显下降又不会破坏地图形状。const fs require(fs); const keep [name, adcode, center]; const geo JSON.parse(fs.readFileSync(./input.json, utf-8)); geo.features.forEach(f { const p {}; keep.forEach(k { if (f.properties[k] ! undefined) p[k] f.properties[k]; }); f.properties p; }); fs.writeFileSync(./output.json, JSON.stringify(geo));这段代码的边界很明确它不简化边界点的数量也不会压缩geometry。如果你对边界点做抽稀一定要在抽稀后重新校验多边形是否闭合、是否出现自相交否则地图会出现破洞。绝大多数情况下属性瘦身已经能带来可感知的体量下降没必要冒险动坐标点。6.2 启动时生成adcode索引避免运行时逐个试探上线前我还会跑一遍索引脚本遍历整个数据包目录把每个json的adcode、文件路径、feature数量、包围盒bounds生成一张索引表。项目启动时加载这张表前端请求某个adcode时服务端直接从索引里定位文件路径返回不再依赖目录名称。这个动作本质上把不确定的文件组织方式收敛成一个可校验的查询表也方便在代码里对adcode做存在性判断减少404请求。某次帮某公司做大屏我图省事把全国子域版整个粘到了静态数据里预览时页面转圈十几秒保存配置还超时一次最后老实按上面的流程重做数据瘦身、上传对象存储、按adcode建立索引接口。从那以后我每次拿到地图数据都强制走一遍体检、瘦身、生成索引这三步再决定用静态粘贴还是URL加载。希望这份省市区县GeoJSON数据包能帮你也少踩几个地图数据的坑。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑