省市区县乡镇各级 GeoJSON 叠不齐?共享边拓扑对齐的落地做法
做全国省市区县乡镇街道村geojson数据下载的整理时,我最怕的不是单个文件脏,而是两级边界叠上去对不上:省的轮廓和市级边界拼不齐、街道边和村边界之间有缝。单看每一级都没问题,一放到同一张地图上就露馅。这篇讲讲为什么会出现"边界打架",以及怎么用共享边(shared edges)把各级 GeoJSON 对齐成能无缝叠合的图层。
边界为什么会对不齐
你在做 GeoJSON下载 或从多个来源拼全国行政区划数据时,各级边界对不齐,根子通常有三个:
- 各自独立抽稀。省界做过一次 Douglas-Peucker,市界又抽一次,同一个真实边界在两套数据里有不同的顶点,放大就成一条粗带。
- 来源不一致。省市级源自天地图,乡镇村源自带 OSM 修正,两套人对"边界在哪"的理解不一样,差出几十上百米。
- 坐标系混源。有的是 WGS84 经纬度,有的回源后做过投影再转回来,四舍五入也会造成顶点错位。
所以"边界对齐"我的判断标准很简单:两级边界的公共边,几何上必须完全重合(顶点集合一致或共享同一线段),投影到地图上没有缝、没有重叠带。这是纯几何一致性的技术校验,不涉及任何领土范围或主权判定。
用共享边做拓扑对齐
要做共享边,得把每一级边界当成既继承又约束的关系来处理。核心思路不是"画的时候各自为政",而是低级别先定、高级别复用:
1. 先建最细的乡镇/村级边为"基准线"(source line),它们的精度决定整条边的精度。 2. 省、市、区县边界由下级边的公共段合并而来,而不是有独立的几何。这样高级边就像拼图一样严格复用低级边,天然重合。 3. 合并临时产生的悬挂点、0.0000x 度碎缝要清掉,否则共享边比不出来。
下面这段 Node 检测相邻两级边界的公共顶点是否对齐,找出"声称相邻但实际对不上"的边:
function snapKey(lng, lat, d = 6) {
return `${Number(lng.toFixed(d))},${Number(lat.toFixed(d))}`;
}
function findMisalignedEdges(upper, lower, tol = 0.00001) {
const lowerPts = new Set();
for (const f of lower.features) {
for (const ring of flatten(f.geometry.coordinates)) {
for (const p of ring) lowerPts.add(snapKey(p[0], p[1]));
}
}
const bad = [];
for (const f of upper.features) {
for (const ring of flatten(f.geometry.coordinates)) {
for (let i = 0; i < ring.length; i++) {
const k = snapKey(ring[i][0], ring[i][1], 6);
if (!lowerPts.has(k)) bad.push([k, ring[i]]);
}
}
}
return bad; // 返回高级边中"在下级边上找不到对应顶点"的点
}
跑完你会发现,多数错位来自抽稀容差不同和顶点四舍五入到不同位数。对策是统一容差(比如全省统一用 100 米顺差)并统一保留小数位数(6 位经纬度),合并端点在快照时用相同精度,错的点自然就变多了——再从那批点反向修复。
合并与对齐的实操顺序
对齐不是一次 union 就完事,我踩过的顺序是:
1. 统一坐标序与环方向。RFC 7946 经纬度(lng,lat)、外环逆时针,先跑一遍 rewind,否则共享边逐顶点比时时方向相反会全判错。 2. 统一精度再抽稀。先把所有顶点 round 到 6 位,再统一按米容差抽稀,"先修整再瘦身"。 3. 共享段复用。高级边界按"取下级公共段"生成,而不是各自独立维护,从根上消灭不一致。 4. 快照容差合并。对仍差 0.0000x 度的点做顶点吸附(snap),把几乎重合的顶点拉到同一点。
下面这张表是我在不同级别做对齐时的方案选择:
| 场景 | 推荐做法 | 备注 |
|---|---|---|
| 省↔市边界 | 市界 = 省内各区县边 union | 严格复用,天然重合 |
| 市↔区县边界 | 区县边 = 乡镇边 union | 先最细后上卷 |
| 区县↔乡镇边界 | 乡镇边作基准线 | 精度最优先 |
| 边界上碎缝 | 顶点快照吸附 | 0.0000x 度合并 |
| 抽稀不一致 | 统一米容差再抽 | 全省统一 100m |
这套"低级别定线、高级别复用"的做法,前提是同一套坐标系 + 同一次抽稀参数。混源数据务必先把投影统一(本例全 WGS84),否则后续所有对齐都是白做。
验证是否真的对齐
对齐完得能验证,我用两个硬指标兜底:
指标一:公共边顶点重合率。 对上卷出的高级边与低级边,统计"高级边顶点能在下级边中找到快照对应点的比例",趋近 100% 才算对齐。指标二:gap/overlap 面积。 把相邻两级图层 S 与 S' 做差集,统计 S 减 S' 的交叠与缺口面积占总面积比例,应 < 0.5%。比例一高,大概率是快照没生效或某级用了独立几何。
顺带说明地图完整性只做几何校验:我这里只保证"要素不重叠、无缝隙、共享边重合"这些纯数据的完整性指标,作画与展示按审图与技术服务边界执行,脚本本身不涉领土范围或主权判定。
小结
各级 GeoJSON 叠不齐,多半不是数据"坏",而是高级边界没有复用低级边界。用 12 位 adcode 对齐要素归属,把省市区县乡镇各级边界做成"低级别定线、高级别复用共享边"的拓扑,统一坐标序、环方向、6 位精度与米容差,最后用公共边重合率和 gap 面积双指标验证,就能得到一套叠上去严丝合缝的全国省市区县geojson数据下载边界库。这套做法对全国村级geojson数据下载同样适用,村边是整条边链的基准。边界齐了,后续做 choropleth、叠加展示才不会被缝隙和重叠带拖垮。
---
需要这条全国省市区县区划边界数据下载链路里的省市区县乡镇各级边界,欢迎到 geojsoncn.com 的全国省市区县区划边界数据下载专区,本站预览与在线可视化免费,导出原始 GeoJSON 为付费能力(每次下载仅需 ¥1.99),可把各级边界按 adcode 对齐到同一套拓扑上。