村界geojson坐标写反怎么救:三招实测
技术专栏 · 数据处理 · 2026-10-24
把村界geojson导入地图后整个掉进海里、或者点位于全跑到了国外,八成不是数据坏了,而是经纬度顺序写反了。这件事阴险在两处:RFC 7946 规定 GeoJSON 坐标是 [经度, 纬度],而 CSV 表格和一批前端库按「纬度在前」走;更要命的是写反之后 JSON 依然合法、多边形依然闭合,程序一个警告都不给。本文拿站内两份真实村界包做互换实验,把「大声报错」和「无声漂移」两副面孔拆开,再给三招检测和一套自动修复。
两副面孔:崇明当场爆炸,喀什漂到北冰洋
实验对象选得刻意:崇明区(489 个村级面)在东经 121°,喀什地区(3,145 个面)在东经 74–80°。把两份数据的所有坐标整体互换([lon, lat] 变 [lat, lon]),结果天差地别:
| 数据包 | 读法 | 范围违规率 | 落中国范围 | 实际位置 |
|---|---|---|---|---|
| 崇明区 · 489 面 | 正确 | 0% | 100% | 东经 121.1–122.1°,长江口 |
| 写反 | 100% | 0% | 纬度 121.1–122.1° 超出 90°,当场报错 | |
| 喀什地区 · 3,145 面 | 正确 | 0% | 100% | 东经 74.5–79.9°,新疆西南 |
| 写反 | 0% | 0% | 东经 35.5–40.3°、北纬 74.5–79.9°,巴伦支海域 |
差别全在经度数值上。东部沿海的经度超过 90,互换后变成纬度直接爆掉范围检查,任何渲染器都会大声拒绝;西部经度不足 90,互换后纬度落在合法区间,检查全部通过——数据被完整地搬到了北纬 74.5° 以北的巴伦支海域,质心漂移 4,488 公里。喀什的村界在那里一片荒海,但你的程序看不出任何问题。
第一招:范围硬检,一行循环拦下报错的那一半
纬度合法区间 [-90, 90]、经度 [-180, 180],越界即写反(或更罕见的坐标系错乱)。深度遍历所有坐标对,几行代码:
function rangeCheck(coords, bad = { n: 0, total: 0 }) {
if (typeof coords[0] === 'number') {
bad.total++;
if (Math.abs(coords[0]) > 180 || Math.abs(coords[1]) > 90) bad.n++;
} else coords.forEach(c => rangeCheck(c, bad));
return bad;
}
// 崇明写反数据:{ n: 489 面全部顶点, total: 同 } → 违规率 100%
// 喀什写反数据:违规率 0% —— 这招对西部数据失明
这招的价值是把「当场爆炸」的那一半拦在渲染之前。中国东部沿海、中原、江南的经度都在 90° 以东,人口和经济活动密集区恰好全覆盖,所以日常拿到的数据大多是崇明模式,一行范围检查足够挡住。但新疆、西藏西部(经度 73–90°)写反后完全合法,得靠下面两招。
第二招:落点率,专治静默漂移
顺序写反的数学特征是坐标整体镜像到另一片区域——只要知道数据「应该在」的大致范围,统计顶点落中率就能识破。给中国范围(东经 73.4–135.2°、北纬 3.7–53.6°)写一个包含性判断,逐顶点统计:
const CN = { lonMin: 73.4, lonMax: 135.2, latMin: 3.7, latMax: 53.6 };
function hitRate(feats) {
let hit = 0, total = 0;
const walk = c => typeof c[0] === 'number'
? (total++, hit += (c[0] >= CN.lonMin && c[0] <= CN.lonMax &&
c[1] >= CN.latMin && c[1] <= CN.latMax) ? 1 : 0)
: c.forEach(walk);
feats.forEach(f => walk(f.geometry.coordinates));
return hit / total;
}
// 喀什正确读法:100% 写反读法:0% —— 一眼定案
落点率的靶子不必是中国——做城市级项目就换成市域范围,做全球业务就用「陆地多边形」当靶子。它比范围检查强在能发现「合法但离谱」的位置,弱点是需要一个先验范围。连先验都没有时,用顶点集聚性兜底:正确数据的顶点云紧凑、方差小,写反后顶点云会散到另一块大陆,两片顶点云的重叠率趋近于零。
第三招:双读法互证,顺手完成自动修复
前两招是判断题,这一招直接给修复。对整份数据同时按「原样」和「整体互换」两种读法算落点率,比例高的就是正确读法——确认后把坐标互换即完成修复:
function autoOrient(feats, hitFn) {
const asIs = hitFn(feats);
const flipped = hitFn(swapAll(feats)); // swapAll:所有 [a,b] → [b,a]
if (flipped > asIs) return { order: 'lat,lon', fix: swapAll(feats), asIs, flipped };
if (asIs >= 0.99) return { order: 'lon,lat', fix: feats, asIs, flipped };
throw new Error('两种读法落点率都异常(' + asIs.toFixed(2) + ' / ' + flipped.toFixed(2) + '),问题不在顺序');
}
// 喀什实测:asIs 100% / flipped 0% → 判定「lon,lat」,零修复成本
// 喀什写反版:asIs 0% / flipped 100% → 判定「lat,lon」,整体互换即修复
实测判定毫无歧义:正确数据两种读法的落点率是 100% 对 0%,写反数据反过来。最后那个 throw 是故意留的——两种读法都异常说明问题更深(坐标系错乱、单位是度分秒、或是投影坐标),顺序修复救不了,别硬修。整份数据互换在 Python 里是 [[y, x] for x, y in ring] 一行,在 JS 里就是文中的深度遍历,成本都在秒级。
377 个市包体检:站内数据全部合规
把这三招用回自己身上。对站内全部 377 个市包做首坐标抽样扫描(每包取第一个顶点做范围与落点双检),结果:范围违规 0、落中国范围外 0——站内交付的村界包全部按 [经度, 纬度] 规范存储,拿到的数据可以先默认合规,再在拼接外部数据时跑一遍本文检测。这和《边界数据拓扑检查:一段脚本查出缝隙与重叠》是同一套体检思路:拓扑查的是面与面的关系,顺序查的是整包的语义,两次检查各管一摊。
从外部拿数据时(各种转换器、同事的 CSV、其他平台导出),建议固定一个 30 秒仪式:打开文件看第一对坐标——中国数据正确读法第一个数应在 73–136 之间、第二个数在 3–54 之间;如果第一个数是个位数两位数、第二个数是三位数,直接跑双读法互证确认后互换修复。至于叠加高德、百度底图时那种几百米的整体偏移,那不是顺序问题,是 GCJ-02 加密偏移,原理与转换见《中国大陆地图坐标系入门》。
本文的检测与修复代码可直接套用到任何边界数据上;需要干净的、顺序合规的村界geojson做对照基准,「村界数据 · 按市」377 个市包每包都通过了这轮扫描。
常见问题
- GeoJSON 的经纬度顺序到底是什么?
- RFC 7946 规定 GeoJSON 坐标写作 [lon, lat],经度在前、纬度在后,和数学里的 (x, y) 习惯一致。但 CSV 表格、部分前端库(如 Leaflet 的 L.latLng)和不少转换工具按「纬度在前」走,两头一对接就写反了。
- 经纬度写反了为什么程序不报错?
- 写反只是语义错误,不是结构错误:JSON 依然合法、多边形依然闭合、拓扑依然干净,解析器和渲染器都不会抱怨。实测把喀什地区的村界包整体互换后,3,145 个面的范围违规率是 0%——数据被完整地画到了北纬 74.5° 以北的巴伦支海域,质心漂移 4,488 公里。
- 哪些环节最容易把顺序写反?
- 三个高频点:一是 CSV 与 Shapefile/GeoJSON 互转时列序照抄(Excel 里经度列常排在纬度后面);二是前端库的习惯差异,Leaflet、高德、百度的点位 API 多为 (lat, lng) 或 (y, x),直接把返回值塞进 GeoJSON 就反了;三是 PostGIS 的 ST_MakePoint(x, y) 与 ST_SetSRID 组合时 x 写成了纬度。
- 怎么快速判断手上的数据有没有写反?
- 打开文件看第一对坐标:中国范围内的数据,正确读法经度应在 73°–136°、纬度在 3°–54° 之间。第一个数是个位数或两位数(比如 31.6)、第二个数是三位数(比如 121.6),基本就是写反了。东部数据写反会直接纬度超 90 报错,西部数据(经度小于 90)则静默通过——本文的三招检测代码可以直接复用。
- 写反的数据能自动修复吗?
- 能。对整份数据按「原样」和「整体互换」两种读法分别统计落点率,落在目标区域比例高的就是正确读法;确认后把全部坐标互换即可。实测喀什包写反读法落点率 0%、正确读法 100%,判定毫无歧义。个别数据两种读法落点率都异常,说明问题不在顺序,要单独排查。
- 叠加高德或百度底图时整体偏移几百米,是坐标写反吗?
- 不是。几百米的整体偏移是高德、百度对底图做了 GCJ-02、BD-09 加密偏移,与坐标顺序无关,顺序写反的偏移量级是几十到几千公里。两者的处理方法也不同,坐标系的原理与转换见站内《中国大陆地图坐标系入门》。