知识指南 · 坐标系

WGS84、GCJ02、BD09 到底有什么区别?

同一个地点,在 GPS 设备、高德地图、百度地图里读出的经纬度可以相差几百米。 这不是设备不准,而是三套坐标系的基准不同。搞清它们的来龙去脉,是做位置业务的第一课。

先说结论

中国大陆范围内公开的地图坐标不是单一体系,至少存在三层:

坐标系谁在用与上一层的关系
WGS-84GPS、北斗民用输出、境外 GIS、OpenStreetMap 类数据地心坐标系,国际通用基准
GCJ-02高德、腾讯等国内主流地图服务在 WGS-84 上做非线性加密偏移
BD-09百度地图在 GCJ-02 之上再做一次极坐标偏移

因此,转换关系是一条链:WGS-84 ⇄ GCJ-02 ⇄ BD-09。 想从 BD-09 回到 WGS-84,必须先退回 GCJ-02,再反解 WGS-84,不能直接一步跳。

GCJ-02 为什么要偏移

GCJ-02 常被称作「火星坐标系」。它对经纬度加入了一组与位置相关的非线性扰动, 目的是让公开的地图底图与实测 GPS 坐标不能直接套合,从而满足测绘数据安全管理要求。 这套偏移有几个重要特性:

  • 与位置相关。不同城市、不同经纬度上的偏移量不同,不能用一个固定常数加减来「修正」。
  • 境外不生效。超出中国大陆大致范围的坐标,算法直接原样返回,这也是判断数据是否被处理过的简易方法。
  • 偏移量约几百米。以北京中心城区为例,WGS-84 与 GCJ-02 的差值通常在 300–600 米量级,足以让一个 POI 落到马路对面。
  • 正向有解析式,反向没有。WGS-84 → GCJ-02 可以直接算;反过来的精确解需要迭代求逆,网上流传的「减一个固定值」写法只是粗略近似。

BD-09 又多做了什么

百度地图在 GCJ-02 基础上,把直角坐标转成极坐标、加上微小扰动后再转回来,形成 BD-09。 这一步的偏移比 GCJ-02 小,但同样不可忽略。实践中最常见的错误,是把百度返回的坐标直接喂给高德 SDK, 或者反过来——两者相差通常在几十到上百米。

可直接用的转换代码

本站坐标系转换工具使用的就是下面这段逻辑(简化版,省略了三角扰动函数):

// GCJ-02 → WGS-84:迭代求逆,6 次即可收敛到 1e-9 度
function gcj02ToWgs84(lng, lat) {
  var wl = lng, wt = lat;
  for (var i = 0; i < 6; i++) {
    var g = wgs84ToGcj02(wl, wt);
    wl -= g[0] - lng;
    wt -= g[1] - lat;
  }
  return [wl, wt];
}

// BD-09 → GCJ-02
function bd09ToGcj02(lng, lat) {
  var x = lng - 0.0065, y = lat - 0.006;
  var z = Math.sqrt(x*x + y*y) - 0.00002 * Math.sin(y * Math.PI * 3000 / 180);
  var t = Math.atan2(y, x) - 0.000003 * Math.cos(x * Math.PI * 3000 / 180);
  return [z * Math.cos(t), z * Math.sin(t)];
}

完整的三角扰动实现可以在本站 assets/js/geo.js 中找到; 如果有成百上千个点要处理,用批量转换工具粘贴进表格即可。

四个高频坑

  1. 把坐标系写进字段名却不做校验。数据库里叫 lng/lat 的列,可能混着三种来源的数据。入库前用格式校验工具清洗,并把坐标系作为独立字段存下来。
  2. 来回反复转换。每次转换都引入浮点误差,来回几次后坐标会漂移。始终保留原始值,只在使用时按需转换。
  3. 把城市代表点当 POI。城市经纬度是便于检索的参考点,不能用于导航、围栏或距离考核。本站每个城市页都明确标注了这一点。
  4. 境外数据也做偏移。对境外坐标调用 GCJ-02 转换算法应原样返回,若代码里没做范围判断,可能产生错误结果。

合规提醒

坐标系问题不只是技术细节。境内地图服务应使用具备资质的服务商,公开地图的国界与行政区划标注需符合国家标准, 前端不应明文暴露第三方地图密钥,个人位置信息更要遵循最小必要原则。完整清单见国内地图数据与坐标系合规要点。

相关阅读

配套工具