GPS / WGS84
GPS 设备、Google Earth 和 OpenStreetMap 通常使用 WGS84,常作为全球经纬度数据的交换基准。天地图使用 CGCS2000;普通网页地图标点可近似看待,专业测绘仍应遵循数据规范。
GPS、高德/腾讯(火星坐标)、百度坐标在线互转,修正坐标放到国内地图上出现偏移的问题。
按照国家相关规定,中国大陆公开发布的地图需要对坐标做加密偏移处理,由此形成 GCJ-02 坐标系,也叫国测局坐标,俗称“火星坐标”。高德、腾讯以及 Apple 地图、Google 地图的中国大陆道路图都使用 GCJ-02;百度地图在 GCJ-02 的基础上又做了一次偏移,形成 BD-09。GPS 设备和手机原始定位给出的是 WGS84 坐标,如果直接放到这些地图上,标记通常会偏离几百米,到了百度地图上可能超过 1 公里。坐标纠偏的做法,就是把坐标转换成目标地图使用的坐标系。
输入 GPS、高德/腾讯或百度地图提供的经纬度,每行一组即可批量处理。保留清晰的数字和分隔符,方便检查解析结果。
按坐标来源选择 GPS、高德/腾讯或百度,再选择要使用结果的地图。选项同时标明 WGS84、GCJ-02、BD-09,方便核对坐标系。
查看目标坐标系的结果,复制单项或全部坐标,也可以打开目标地图核对标记。位置不符时,先检查来源和经纬度顺序。
GPS 设备、Google Earth 和 OpenStreetMap 通常使用 WGS84,常作为全球经纬度数据的交换基准。天地图使用 CGCS2000;普通网页地图标点可近似看待,专业测绘仍应遵循数据规范。
高德、腾讯、Apple 地图(中国大陆)使用 GCJ-02。GPS 坐标直接用于这些地图时,标记可能与道路或建筑错位。请按实际来源和目标地图选择转换方向,不要仅凭数值相近作判断。
百度地图使用 BD-09,与 GCJ-02 仍有差异。GPS 坐标用于百度时,通常经 WGS84、GCJ-02 转为 BD-09;百度坐标转到其他地图时则选择相应的反向转换。
| 地图或数据来源 | 坐标系 |
|---|---|
| GPS 设备、手机原始定位、照片 EXIF | WGS84 |
| Google Earth、OpenStreetMap | WGS84 |
| Google 地图(中国大陆道路图) | GCJ-02(卫星图为 WGS84,两层会错位) |
| Apple 地图(中国大陆) | GCJ-02(地图数据由高德提供) |
| 高德地图、腾讯地图 | GCJ-02(火星坐标) |
| 百度地图 | BD-09(经纬度)/ BD-09MC(墨卡托米制) |
| 天地图 | CGCS2000(网页标点可近似看作 WGS84) |
在中国大陆范围内,Google 地图的道路图使用 GCJ-02,卫星图使用 WGS84,所以切换到卫星图后,道路和建筑会错开几百米。用 Google 地图道路图采集的中国大陆坐标属于 GCJ-02,用于 GPS 设备或 OpenStreetMap 前,应选择 GCJ-02 转 WGS84;在卫星图上采集的点则按 WGS84 处理。
下表用本工具对三个示例点做转换,显示同一位置在三种坐标系下的数值(经度, 纬度),以及与 WGS84 原点的距离。
| 示例点 | WGS84(GPS) | GCJ-02(高德/腾讯) | BD-09(百度) | 相对 WGS84 的偏移 |
|---|---|---|---|---|
| 北京 天安门城楼 | 116.391263, 39.907359 | 116.397504, 39.908760 | 116.403877, 39.915104 | GCJ-02 约 555 米;BD-09 约 1378 米 |
| 上海 东方明珠电视塔 | 121.495260, 31.241946 | 121.499709, 31.239947 | 121.506320, 31.245587 | GCJ-02 约 478 米;BD-09 约 1127 米 |
| 广州 广州塔 | 113.319148, 23.109015 | 113.324558, 23.106404 | 113.331100, 23.112168 | GCJ-02 约 625 米;BD-09 约 1272 米 |
GPS 点放到高德或腾讯地图后与底图错位时,通常需要转为 GCJ-02;请先确认来源是 GPS/WGS84。
百度地图使用 BD-09。选择 WGS84 到 BD-09,工具会处理经过 GCJ-02 的转换链路。
将高德、腾讯地图上采集的点用于 GPS 或全球地图时,可近似反向转为 WGS84,适合日常标点和数据核对。
将百度坐标用于高德或腾讯地图时,选择 BD-09 为来源、GCJ-02 为目标,并打开地图核对标记。
每行输入一组十进制度经纬度,例如天安门城楼的 GPS 坐标 116.391263, 39.907359(经度在前);多组请换行。来源和目标代表坐标系,顺序选项决定经纬度谁在前。高德、腾讯接口常用经度在前,部分 GPS 数据则纬度在前;自动识别后也建议抽查原始值。结果可复制为目标值或全部坐标,并选择逗号、逗号加空格或 JSON 格式,便于粘贴到表格和脚本。
还没有坐标? 用经纬度坐标拾取器在地图上点选
GCJ-02 与 BD-09 主要用于中国地图产品。大陆以外位置通常无需这些偏移,工具会按粗略范围判断并提示;这不是行政区或测绘边界。港澳台和不同地图版本的规则可能不同,重要点位应核对来源设备或接口说明,并在目标地图验证。测绘及 GIS 数据还可能使用 CGCS2000、投影坐标或高程基准;本工具只转换经纬度,不替代专业基准转换。
本工具按常见公式转换,GCJ-02/BD-09 逆转 GPS 使用迭代近似。用上面的示例点做往返测试,GCJ-02 转回 WGS84 的算法误差约 1 厘米,BD-09 转回 WGS84 约 6 厘米。原始数据和地图底图本身也会带来误差;结果适合日常标点和数据检查,不保证与测绘成果重合。请勿用于工程测量、法律边界、安全导航或应急救援。
以下 JavaScript 和 Python 函数与本工具使用相同的公式,参数和返回值均为经度在前。
const PI = Math.PI;
const A = 6378245.0;
const EE = 0.00669342162296594323;
const X_PI = PI * 3000.0 / 180.0;
function outOfChina(lng, lat) {
return lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271;
}
function transformLat(x, y) {
let ret = -100 + 2 * x + 3 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
ret += (20 * Math.sin(6 * x * PI) + 20 * Math.sin(2 * x * PI)) * 2 / 3;
ret += (20 * Math.sin(y * PI) + 40 * Math.sin(y / 3 * PI)) * 2 / 3;
ret += (160 * Math.sin(y / 12 * PI) + 320 * Math.sin(y * PI / 30)) * 2 / 3;
return ret;
}
function transformLng(x, y) {
let ret = 300 + x + 2 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += (20 * Math.sin(6 * x * PI) + 20 * Math.sin(2 * x * PI)) * 2 / 3;
ret += (20 * Math.sin(x * PI) + 40 * Math.sin(x / 3 * PI)) * 2 / 3;
ret += (150 * Math.sin(x / 12 * PI) + 300 * Math.sin(x / 30 * PI)) * 2 / 3;
return ret;
}
// WGS84 (GPS) -> GCJ-02 (Amap / Tencent)
function wgs84ToGcj02(lng, lat) {
if (outOfChina(lng, lat)) return [lng, lat];
const radLat = lat / 180 * PI;
let magic = Math.sin(radLat);
magic = 1 - EE * magic * magic;
const sqrtMagic = Math.sqrt(magic);
const dLat = transformLat(lng - 105, lat - 35) * 180 / ((A * (1 - EE)) / (magic * sqrtMagic) * PI);
const dLng = transformLng(lng - 105, lat - 35) * 180 / (A / sqrtMagic * Math.cos(radLat) * PI);
return [lng + dLng, lat + dLat];
}
// GCJ-02 -> WGS84, iterative approximation
function gcj02ToWgs84(lng, lat) {
if (outOfChina(lng, lat)) return [lng, lat];
let [wLng, wLat] = [lng, lat];
for (let i = 0; i < 10; i++) {
const [gLng, gLat] = wgs84ToGcj02(wLng, wLat);
wLng -= gLng - lng;
wLat -= gLat - lat;
}
return [wLng, wLat];
}
// GCJ-02 -> BD-09 (Baidu)
function gcj02ToBd09(lng, lat) {
const z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * X_PI);
const theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * X_PI);
return [z * Math.cos(theta) + 0.0065, z * Math.sin(theta) + 0.006];
}
// BD-09 -> GCJ-02
function bd09ToGcj02(lng, lat) {
const x = lng - 0.0065;
const y = lat - 0.006;
const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * X_PI);
const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * X_PI);
return [z * Math.cos(theta), z * Math.sin(theta)];
}import math
PI = math.pi
A = 6378245.0
EE = 0.00669342162296594323
X_PI = PI * 3000.0 / 180.0
def out_of_china(lng, lat):
return lng < 72.004 or lng > 137.8347 or lat < 0.8293 or lat > 55.8271
def _transform_lat(x, y):
ret = -100 + 2 * x + 3 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * math.sqrt(abs(x))
ret += (20 * math.sin(6 * x * PI) + 20 * math.sin(2 * x * PI)) * 2 / 3
ret += (20 * math.sin(y * PI) + 40 * math.sin(y / 3 * PI)) * 2 / 3
ret += (160 * math.sin(y / 12 * PI) + 320 * math.sin(y * PI / 30)) * 2 / 3
return ret
def _transform_lng(x, y):
ret = 300 + x + 2 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * math.sqrt(abs(x))
ret += (20 * math.sin(6 * x * PI) + 20 * math.sin(2 * x * PI)) * 2 / 3
ret += (20 * math.sin(x * PI) + 40 * math.sin(x / 3 * PI)) * 2 / 3
ret += (150 * math.sin(x / 12 * PI) + 300 * math.sin(x / 30 * PI)) * 2 / 3
return ret
def wgs84_to_gcj02(lng, lat):
"""WGS84 (GPS) -> GCJ-02 (Amap / Tencent)"""
if out_of_china(lng, lat):
return lng, lat
rad_lat = lat / 180 * PI
magic = 1 - EE * math.sin(rad_lat) ** 2
sqrt_magic = math.sqrt(magic)
d_lat = _transform_lat(lng - 105, lat - 35) * 180 / ((A * (1 - EE)) / (magic * sqrt_magic) * PI)
d_lng = _transform_lng(lng - 105, lat - 35) * 180 / (A / sqrt_magic * math.cos(rad_lat) * PI)
return lng + d_lng, lat + d_lat
def gcj02_to_wgs84(lng, lat):
"""GCJ-02 -> WGS84, iterative approximation"""
if out_of_china(lng, lat):
return lng, lat
w_lng, w_lat = lng, lat
for _ in range(10):
g_lng, g_lat = wgs84_to_gcj02(w_lng, w_lat)
w_lng -= g_lng - lng
w_lat -= g_lat - lat
return w_lng, w_lat
def gcj02_to_bd09(lng, lat):
"""GCJ-02 -> BD-09 (Baidu)"""
z = math.sqrt(lng * lng + lat * lat) + 0.00002 * math.sin(lat * X_PI)
theta = math.atan2(lat, lng) + 0.000003 * math.cos(lng * X_PI)
return z * math.cos(theta) + 0.0065, z * math.sin(theta) + 0.006
def bd09_to_gcj02(lng, lat):
"""BD-09 -> GCJ-02"""
x, y = lng - 0.0065, lat - 0.006
z = math.sqrt(x * x + y * y) - 0.00002 * math.sin(y * X_PI)
theta = math.atan2(y, x) - 0.000003 * math.cos(x * X_PI)
return z * math.cos(theta), z * math.sin(theta)