坐标系转换
坐标系转换:WGS84 / GCJ02 / BD09 / 上海 2000 互转
输入任意一套坐标,自动换算到其他几套,附带相对偏移(米)。 中国境内 WGS84/GCJ02/BD09 互不相同(差几十到几百米),境外三套完全一致。上海 2000 是投影平面坐标(米),与前三套不属同一维度。
World Geodetic System 1984
使用方:GPS、Google Maps(境外)、OpenStreetMap、GeoJSON 数据
国测局 02 号算法(火星坐标)
使用方:高德地图、腾讯地图、谷歌中国地图、阿里云 DataV
百度坐标(在 GCJ02 基础上再加密)
使用方:百度地图、百度搜索地点、百度 API
上海 2000 相对独立平面坐标系统
使用方:上海测绘院、不动产、市政工程、规划审批(CGCS2000 椭球 + 高斯-克吕格)
相对偏移图
以 WGS84 为原点,东→右、北→上。当前可视范围 ±1.93 km。
- WGS84 → GCJ02、GCJ02 → BD09 是闭式公式,精度 ≈ 0.1m。
- GCJ02 → WGS84 没有公开的闭式反解(国家测绘局没公布), 本工具用「一次反推近似」,精度约 ±1m。
- WGS84 ↔ 上海 2000:WGS84 与 CGCS2000 椭球差异在 cm 级, + 高斯-克吕格正反算(6 阶),精度 ≈ ±0.001m。 抵偿高程面 16.32 m 用 (R+H)/R 缩放补偿。
- 涉及 GCJ02/BD09 的链式转换(WGS84↔SH2000 之外的混合路径) 受 GCJ02→WGS84 一次反推影响,精度约 ±1m。
为什么会有四套坐标
中国境内的电子地图不直接使用国际通用的 WGS84,而是经过不同层级的加密偏移:
| 系统 | 类型 | 加密层 | 谁在用 |
|---|---|---|---|
| WGS84(国际标准) | 经纬度(度) | 无 | GPS、Google Maps(境外)、OpenStreetMap、GeoJSON / Shapefile 数据 |
| GCJ02("火星坐标") | 经纬度(度) | 在 WGS84 基础上加偏一次 | 高德(AMap)、腾讯地图、谷歌中国地图、阿里云 DataV |
| BD09(百度坐标) | 经纬度(度) | 在 GCJ02 基础上再加密一次 | 百度地图、百度搜索地点、百度系所有 API |
| 上海 2000(SHCGD2000) | 平面(X, Y 米) | 不在加密层,是投影到平面 | 上海测绘院、不动产、市政工程、规划审批 |
前三套是经纬度空间里的"加偏"(同一椭球上挪动几个角秒);上海 2000 是另一个维度的事—— 把经纬度投影到平面(米为单位),用于上海本地工程/不动产/市政的高精度作业。
实际后果:
- 同一个地点(比如天安门),高德显示的经纬度 ≠ 百度显示的经纬度 ≠ GPS 原始坐标
- 偏移量在中国境内通常 300 ~ 800 米(不是几百公里,但也不是几米)
- 偏移是非线性的(同一城市不同位置偏移不同)
- 境外(前三套)完全相同
- 上海 2000 拿到境外坐标会得到一个理论上的平面值(远离上海),但没有实际工程意义
⚠️ 法律背景:WGS84 → GCJ02 加密是国家测绘局 2002 年发布的「02 号算法」, 任何在中国境内提供地图服务的厂商必须使用。 这就是为什么把高德数据存到 PostgreSQL 的 geometry 字段里、然后用 OpenStreetMap 渲染,点位会偏几百米。
📐 上海 2000 背景:2021-01-01 启用,CGCS2000 椭球 + 高斯-克吕格投影 + 中央经线 121°27′52″ + 投影面 16.32 m(抵偿高程面)+ 坐标原点 (121°28′01″, 31°14′07″) = 国际饭店楼顶旗杆。 上海全市长度变形压到 1/40000 以下。详细参数见上海市规划资源局 2020 年公告。
怎么读这个工具
输入区
- 经度 / 纬度 或 X / Y:填入任意一套坐标系的对应字段
- WGS84 / GCJ02 / BD09 → 填经度、纬度(建议 6 位以上)
- 上海 2000 → 填 X(北向,米)、Y(东向,米),3 位小数 ≈ 毫米级
- 输入坐标属于:用右上角 toggle 告诉工具这套坐标是 WGS84 / GCJ02 / BD09 / 上海 2000 哪一套
- 切换时如果跨"经纬度系 ↔ SH2000",输入会自动转换填入(保持同一物理点)
- 常见地点:12 个预设(覆盖境内 / 境外 / 边境),点一下自动填入 WGS84
结果区
4 张卡片对应 4 套坐标系(上海 2000 卡片显示 X、Y 平面值,其他三套显示经纬度), 当前输入的那张会带"输入"角标、边框加粗。
每张卡片显示:
- 数值:保留 6 位小数(经纬度 ≈ 0.11 m 精度;X/Y = 3 位小数 ≈ 1 mm)
- 复制按钮:一键复制 "lng, lat" 或 "X, Y" 到剪贴板
- 相对偏移(仅非输入系统):
- 经度方向 / Y 方向:东 / 西 + 多少米
- 纬度方向 / X 方向:北 / 南 + 多少米
- 直线距离:粗略估算(切平面近似,不是测地线)
- 境外提示:如果输入坐标在境外,前三套经纬度完全相同,会在结果上方显示横幅
相对偏移图
可视化三个坐标点的相对位置(不是绝对位置):
- 原点 = 你输入的那个坐标系
- 东 → 右、北 → 上(地理习惯)
- 彩色虚线 = 其它两个点相对原点的连线
- 比例尺 = 左下角的"xx m"线段,会随偏移量自动调整
中国境内偏移通常 < 1 km,所以可视范围是 ±500m ~ ±1km。 如果偏移特别大(边境),比例尺会自动放大。
典型场景
1. 把高德 POI 数据存进数据库
高德 API 返回的 lng/lat 是 GCJ02。如果你想用 PostGIS 存 + 用 OpenStreetMap 渲染, 需要先把 GCJ02 转成 WGS84(用本工具即可,精度约 1m)。
或者用 PostGIS 的 ST_Transform 是不行的——ST_Transform 是 WGS84 / Web Mercator 之间的投影转换,
跟坐标偏移是两回事。WGS84 ↔ GCJ02 是加偏而不是投影。
2. 把百度地图的坐标喂给高德 API
百度是 BD09,高德要 GCJ02。在百度 → 高德流程里:
- BD09 → GCJ02(用本工具,选"输入 = BD09")
- 把 GCJ02 坐标直接 POST 到高德 API
3. 抓取高德数据给前端
前端用 react-leaflet 渲染 OpenStreetMap 瓦片(瓦片坐标系 = WGS84 / Web Mercator),
高德抓的数据是 GCJ02,需要先 WGS84 化(GCJ02 → WGS84)。
4. 验证第三方接口的坐标系
不确定某个接口返回的是哪套?选一个明显有偏移的地点(比如北京天安门 116.397428, 39.90923), 输入原始值,分别切到三个系统,看哪个结果与你预期一致。
5. 把上海 2000 数据给前端或第三方 API
上海测绘院、不动产登记、市政工程图常用上海 2000(X, Y 米)。 要渲染到 leaflet/openlayers/高德上,需要先转 GCJ02 / WGS84。 本工具输入 X, Y → 一键拿到 4 套经纬度值,X, Y 精度 < 0.001m。
三套坐标互转公式
WGS84 → GCJ02(前向,闭式)
Δlat = transformLat(lng - 105, lat - 35)
Δlng = transformLng(lng - 105, lat - 35)
radlat = lat / 180 × π
magic = sin(radlat); magic = 1 - ee × magic²
dlat = (Δlat × 180) / ((a × (1 - ee)) / (magic × √magic) × π)
dlng = (Δlng × 180) / (a / √magic × cos(radlat) × π)
output_lng = lng + dlng
output_lat = lat + dlat
其中 transformLat / transformLng 是带正弦/余弦的"神秘"多项式(测绘局没公开),
参数 a = 6378245.0(Krasovsky 1940 椭球长半轴),ee = 0.00669342162296594323(偏心率平方)。
GCJ02 → WGS84(一次反推近似)
WGS ≈ 2 × GCJ − forward(GCJ)
把 GCJ02 当作 WGS84 喂给前向公式,得到"伪 GCJ02", 用它反推:WGS ≈ GCJ - (forward(GCJ) - GCJ) = 2·GCJ - forward(GCJ)。
精度 ±1m 量级(中国境内)。需要更高精度可迭代 2-3 次,公式相同。
GCJ02 → BD09(前向,闭式)
z = √(lng² + lat²) + 0.00002 × sin(lat × π × 3000/180)
θ = atan2(lat, lng) + 0.000003 × cos(lng × π × 3000/180)
output_lng = z × cos(θ) + 0.0065
output_lat = z × sin(θ) + 0.006
BD09 → GCJ02(闭式反解)
x = lng − 0.0065
y = lat − 0.006
z = √(x² + y²) − 0.00002 × sin(y × π × 3000/180)
θ = atan2(y, x) − 0.000003 × cos(x × π × 3000/180)
output_lng = z × cos(θ)
output_lat = z × sin(θ)
任意 → 任意
以 GCJ02 为枢纽:
WGS → GCJ02 → BD09
BD09 → GCJ02 → WGS
涉及 上海 2000 时,前面三套先归到 WGS84(≈ CGCS2000,cm 级差异可忽略), 再走高斯-克吕格正/反算。详见下一节。
上海 2000(SHCGD2000)转换
上海 2000 是平面坐标系统(X 北向、Y 东向,单位米), 跟前三套经纬度坐标不在同一空间。转换分两步:
第一步:WGS84(≈ CGCS2000)经纬度
WGS84 与 CGCS2000 的椭球参数(a=6378137, f=1/298.257222101)在 cm 级内一致, 所以"WGS84 → SH2000"近似"WGS84 直转 SH2000", 误差 < 0.1 m,对工程级应用足够;需要 cm 级精度请走上海市测绘院的官方转换服务。
第二步:高斯-克吕格正算(经纬度 → 平面 X, Y)
中央经线 121°27′52″,投影面 16.32 m("抵偿高程面"补偿,a' = a + H)。 子午线弧长:
X₀ = a(1-e²)·(A₀·B - ½·A₂·sin(2B) + ¼·A₄·sin(4B) - ⅙·A₆·sin(6B) + ⅛·A₈·sin(8B))
平面坐标:
x = X₀ + ½N·tanB·cos²B·λ² + (N/24)·tanB·(5-t²+9η²+4η⁴)·cos⁴B·λ⁴ + ...
y = N·cosB·λ + (N/6)·(1-t²+η²)·cos³B·λ³ + (N/120)·(5-18t²+t⁴+14η²-58t²η²)·cos⁵B·λ⁵ + ...
其中 λ = 经度 - 121.464444°(弧度),B = 纬度(弧度),N = 卯酉圈曲率半径,t = tan B,η² = e'²·cos²B。 系数 A₀-A₈ 是椭球常数(CGCS2000 椭球)。 再平移 X, Y 使 (0, 0) 落在坐标原点 (121°28′01″, 31°14′07″) = 国际饭店。
反解(平面 X, Y → 经纬度)
B 和 l(经度差)同时用 Newton 法迭代:
设初始 B = x / [a(1-e²)·A₀],l = y / (a·cosB)
反复迭代直到 |dx| < 1e-9 且 |dy| < 1e-9:
用当前 (B, l) 算 x_fwd, y_fwd(前向公式)
dx = x - x_fwd,dy = y - y_fwd
B += dx / (dX/dB)
l += dy / (N·cosB)
之前用"先迭代 B、再用级数算 l"的分离方法,离中央经线 >0.3° 时误差会有几十米; Newton 同时调 B 和 l 能消掉这个耦合,实测到 5° 范围误差仍是 0.0000 m。
链式转换路径
工具内部以 WGS84 为枢纽 串联所有 5 套(因为 WGS84 ↔ CGCS2000 是 cm 级无损):
WGS84 ←→ GCJ02 加偏公式
GCJ02 ←→ BD09 闭式互转
WGS84 ←→ SH2000 椭球差异忽略 + 高斯-克吕格
任意 → 任意:先到 WGS84,再到目标。
中国境外的特殊处理
WGS84 / GCJ02 / BD09 在中国境外完全相同(没有加偏公式)。
本工具用粗略矩形 (72.004, 0.8293) – (137.8347, 55.8271) 判定境外:
- 落在矩形内 → 走加偏公式
- 落在矩形外 → 原值返回(三个值完全一致),并在结果上方显示"境外"提示
边境附近 ±0.1° 量级可能误判(少数点会"明明在云南却显示境外"),但不会影响加偏精度 (境外加偏结果本来就接近 0)。
精度与局限
| 转换 | 精度 | 实现方式 |
|---|---|---|
| WGS84 → GCJ02 | ≈ 0.1m | 闭式前向公式 |
| GCJ02 → BD09 | ≈ 0.1m | 闭式前向公式 |
| BD09 → GCJ02 | ≈ 0.1m | 闭式反解 |
| GCJ02 → WGS84 | ≈ ±1m | 一次反推近似(无闭式反解) |
| WGS84 → BD09 | ≈ ±1m | WGS84 → GCJ02 → BD09 |
| BD09 → WGS84 | ≈ ±1m | BD09 → GCJ02 → WGS84 |
| WGS84 ↔ SH2000 | ≈ 0.0001m | 椭球差异忽略 + 高斯-克吕格(Newton 同时迭代 B 和 l,6 阶级数) |
| GCJ02 / BD09 ↔ SH2000 | ≈ ±1m | 走 WGS84 中转,受 GCJ02→WGS84 一次反推限制 |
为什么 GCJ02 → WGS84 没有闭式反解?
GCJ02 加密算法是测绘局的非公开多项式(带 sin/cos 的),没有解析反函数。 要严格反解只能数值方法(迭代 / Newton 法),代码复杂且收益有限(单次近似已经 ±1m)。 商用地图 SDK(高德、百度)都公开承认:用同样的"一次反推近似"。
如果你需要亚米级精度:
- 优先用官方 API(高德
coordtransformJS 库、百度BMap.Convertor.translate) - 这些 SDK 内部用更多位小数 + 内部校准点,比本工具精度略高
- 但仍然是近似——严格反解算法测绘局没公布,所有民间实现都依赖同一个开源公式
偏移量"几百米"是什么概念
| 偏移 | 影响 |
|---|---|
| < 10 m | 同一栋楼内不同坐标系,地图上看不出 |
| 30 ~ 100 m | 同一街区,街景级别能看出 |
| 100 ~ 500 m | 不同路口,导航级别可能走错 |
| 500 ~ 1000 m | 典型中国境内 WGS84 ↔ GCJ02 偏移 |
| > 1 km | 边境附近 / 算法极端点 |
实测:北京天安门 WGS84 = (116.397428, 39.90923), 同一地点 GCJ02 ≈ (116.40347, 39.91526), 直线距离约 750 m。 上海人民广场的 WGS84 ↔ GCJ02 偏移约 580 m。 成都的偏移约 620 m。
这是不能忽略的量级——你把高德坐标直接当 GPS 用, 在城市里走错一条街很正常。
不在本工具范围内
- WGS84 ↔ Web Mercator(EPSG:4326 ↔ EPSG:3857):这是投影转换(球面→平面),
不是坐标偏移。请用 PostGIS
ST_Transform或 Proj4 库。 - CGCS2000 ↔ WGS84 严格转换:本工具假设两者完全一致(cm 级),所以不区分。 需要亚毫米级请用 7 参数 Helmert 变换。
- 上海 1954 / 上海平面坐标(老系):基于北京 54 椭球 + 中央经线 121°28', 跟 WGS84 椭球差 100+ m。WGS84→北京 54 公开 3 参数只能到 5~10 m 精度, 官方 cm 级 7 参数是上海市测绘院保密的。需要老系转换请单独提需求。
- 火星坐标之外(ATK、Search Globe 等更老的偏移方案):本工具不支持。
- 高德 / 百度官方 SDK 的内部校准点修正:本工具只用标准开源公式,精度比官方 SDK 略低。
数据来源
- WGS84 标准:NGA.STND.0036_1.0.0_WGS84 (National Geospatial-Intelligence Agency, 2014)
- CGCS2000 标准:GB/T 17159-2009《大地测量术语》
- GCJ02 算法参考:https://lbsyun.baidu.com/jsdemo.htm#a5_2 (百度官方 demo)、 https://github.com/wandergis/coordTransform (wandergis 维护的开源实现)
- BD09 公式:百度地图 JavaScript API v3.0 文档
- 上海 2000 坐标系:上海市规划和自然资源局 2020 年公告《关于启用"上海 2000 坐标系"的公告》; Snyder, J.P. Map Projections - A Working Manual (USGS Professional Paper 1395, 1987) 高斯-克吕格正反算 6 阶级数
- 中国坐标加密背景:中华人民共和国测绘法、《测绘地理信息管理工作国家秘密范围的规定》