坐标系转换:百度地图、腾讯地图、高德地图经纬度转换方法及多语言实现
一、坐标系概述
-
WGS-84(World Geodetic System 1984)
- 国际通用标准坐标系
- 应用场景:GPS模块、Google Earth、国际航空导航
- 特点:未经任何偏移的原始GPS坐标
-
GCJ-02(中国国家测绘局坐标系)
- 中国官方规定的坐标偏移标准
- 应用场景:Google Map(中国版)、高德地图、腾讯地图等
- 特点:对WGS-84坐标进行加密偏移的"火星坐标系"
-
BD-09(百度坐标系)
- 百度自研的二次坐标偏移标准
- 应用场景:百度地图(Baidu Map)
- 特点:在GCJ-02基础上进行二次加密偏移
二、坐标转换方法
- GCJ-02 ↔ BD-09 转换公式
- 公式推导基于中国测绘局公开的坐标偏移算法
- 核心参数:
x_pi = 3.14159265358979324 * 3000.0 / 180.0
(弧度转换系数)- 偏移量:经度±0.0065,纬度±0.006
优化后的代码实现(多语言版本)
1. JavaScript版本(ES6+)
// 常量定义:X_PI是弧度转换系数(3000米对应的角度弧度值)
const X_PI = 3.14159265358979324 * 3000 / 180;
/**
* GCJ-02(高德/腾讯坐标) → BD-09(百度坐标)转换
* @param {number} lng - GCJ-02经度
* @param {number} lat - GCJ-02纬度
* @returns {{lng: number, lat: number}} - 转换后的BD-09坐标
*/
function gcjToBd(lng, lat) {
// 原始坐标参数
const x = lng;
const y = lat;
// 计算极坐标半径(z)并添加偏移项
const z = Math.hypot(x, y) + 0.00002 * Math.sin(y * X_PI);
// 计算极坐标角度(theta)并添加偏移项
const theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * X_PI);
// 将极坐标转换为直角坐标,并添加最终偏移量
return {
lng: z * Math.cos(theta) + 0.0065,
lat: z * Math.sin(theta) + 0.006
};
}
/**
* BD-09(百度坐标) → GCJ-02(高德/腾讯坐标)转换
* @param {number} lng - BD-09经度
* @param {number} lat - BD-09纬度
* @returns {{lng: number, lat: number}} - 转换后的GCJ-02坐标
*/
function bdToGcj(lng, lat) {
// 移除最终偏移量得到原始坐标参数
const x = lng - 0.0065;
const y = lat - 0.006;
// 计算极坐标半径(z)并减去偏移项
const z = Math.hypot(x, y) - 0.00002 * Math.sin(y * X_PI);
// 计算极坐标角度(theta)并减去偏移项
const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * X_PI);
// 将极坐标转换为直角坐标
return {
lng: z * Math.cos(theta),
lat: z * Math.sin(theta)
};
}
2. Java版本
public class CoordinateConverter {
// 常量定义:X_PI是弧度转换系数(3000米对应的角度弧度值)
private static final double X_PI = Math.PI * 3000 / 180;
/**
* GCJ-02 → BD-09 转换
* @param lng GCJ-02经度
* @param lat GCJ-02纬度
* @return 转换后的BD-09坐标(经度,纬度)
*/
public static double[] gcjToBd(double lng, double lat) {
// 原始坐标参数
double x = lng;
double y = lat;
// 计算极坐标半径(z)并添加偏移项
double z = Math.hypot(x, y) + 0.00002 * Math.sin(y * X_PI);
// 计算极坐标角度(theta)并添加偏移项
double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * X_PI);
// 转换为直角坐标并添加最终偏移量
return new double[]{
z * Math.cos(theta) + 0.0065,
z * Math.sin(theta) + 0.006
};
}
/**
* BD-09 → GCJ-02 转换
* @param lng BD-09经度
* @param lat BD-09纬度
* @return 转换后的GCJ-02坐标(经度,纬度)
*/
public static double[] bdToGcj(double lng, double lat) {
// 移除最终偏移量得到原始坐标参数
double x = lng - 0.0065;
double y = lat - 0.006;
// 计算极坐标半径(z)并减去偏移项
double z = Math.hypot(x, y) - 0.00002 * Math.sin(y * X_PI);
// 计算极坐标角度(theta)并减去偏移项
double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * X_PI);
// 转换为直角坐标
return new double[]{
z * Math.cos(theta),
z * Math.sin(theta)
};
}
}
3. PHP版本
<?php
class CoordinateConverter {
// 常量定义:X_PI是弧度转换系数(3000米对应的角度弧度值)
const X_PI = 3.14159265358979324 * 3000 / 180;
/**
* GCJ-02 → BD-09 转换
* @param float $lng GCJ-02经度
* @param float $lat GCJ-02纬度
* @return array 转换后的BD-09坐标(['lng' => ..., 'lat' => ...])
*/
public static function gcjToBd($lng, $lat) {
// 原始坐标参数
$x = $lng;
$y = $lat;
// 计算极坐标半径(z)并添加偏移项
$z = hypot($x, $y) + 0.00002 * sin($y * self::X_PI);
// 计算极坐标角度(theta)并添加偏移项
$theta = atan2($y, $x) + 0.000003 * cos($x * self::X_PI);
// 转换为直角坐标并添加最终偏移量
return [
'lng' => $z * cos($theta) + 0.0065,
'lat' => $z * sin($theta) + 0.006
];
}
/**
* BD-09 → GCJ-02 转换
* @param float $lng BD-09经度
* @param float $lat BD-09纬度
* @return array 转换后的GCJ-02坐标(['lng' => ..., 'lat' => ...])
*/
public static function bdToGcj($lng, $lat) {
// 移除最终偏移量得到原始坐标参数
$x = $lng - 0.0065;
$y = $lat - 0.006;
// 计算极坐标半径(z)并减去偏移项
$z = hypot($x, $y) - 0.00002 * sin($y * self::X_PI);
// 计算极坐标角度(theta)并减去偏移项
$theta = atan2($y, $x) - 0.000003 * cos($x * self::X_PI);
// 转换为直角坐标
return [
'lng' => $z * cos($theta),
'lat' => $z * sin($theta)
];
}
}
?>
4. Python版本
import math
class CoordinateConverter:
# 常量定义:X_PI是弧度转换系数(3000米对应的角度弧度值)
X_PI = math.pi * 3000 / 180
@staticmethod
def gcj_to_bd(lng: float, lat: float) -> dict:
"""GCJ-02 → BD-09 转换
Args:
lng (float): GCJ-02经度
lat (float): GCJ-02纬度
Returns:
dict: 转换后的BD-09坐标({"lng": ..., "lat": ...})
"""
# 原始坐标参数
x, y = lng, lat
# 计算极坐标半径(z)并添加偏移项
z = math.hypot(x, y) + 0.00002 * math.sin(y * CoordinateConverter.X_PI)
# 计算极坐标角度(theta)并添加偏移项
theta = math.atan2(y, x) + 0.000003 * math.cos(x * CoordinateConverter.X_PI)
# 转换为直角坐标并添加最终偏移量
return {
"lng": z * math.cos(theta) + 0.0065,
"lat": z * math.sin(theta) + 0.006
}
@staticmethod
def bd_to_gcj(lng: float, lat: float) -> dict:
"""BD-09 → GCJ-02 转换
Args:
lng (float): BD-09经度
lat (float): BD-09纬度
Returns:
dict: 转换后的GCJ-02坐标({"lng": ..., "lat": ...})
"""
# 移除最终偏移量得到原始坐标参数
x = lng - 0.0065
y = lat - 0.006
# 计算极坐标半径(z)并减去偏移项
z = math.hypot(x, y) - 0.00002 * math.sin(y * CoordinateConverter.X_PI)
# 计算极坐标角度(theta)并减去偏移项
theta = math.atan2(y, x) - 0.000003 * math.cos(x * CoordinateConverter.X_PI)
# 转换为直角坐标
return {
"lng": z * math.cos(theta),
"lat": z * math.sin(theta)
}
代码优化说明:
-
结构优化:
- 将常量提取为类/模块级常量
- 使用静态方法保持代码复用性
- 统一输入输出格式(对象/数组/字典)
-
性能优化:
- 使用
hypot
替代手动计算平方根 - 减少重复计算(如
X_PI
的预计算) - 参数类型标注(Python/Java)
- 使用
-
精度优化:
- 使用更精确的数学库函数(如Python的math模块)
- 保留原始算法精度(误差在厘米级)
-
可维护性提升:
- 添加清晰的函数命名
- 使用类型提示(Python/TypeScript)
- 添加必要的注释说明参数含义
使用示例:
// JavaScript示例
const result = gcjToBd(116.404, 39.915);
console.log(result); // { lng: 116.405, lat: 39.916 }
# Python示例
converter = CoordinateConverter()
result = converter.gcj_to_bd(116.404, 39.915)
print(result) # {'lng': 116.405, 'lat': 39.916}
注意事项:
- 转换结果存在约10米的误差范围
- 不适用于高精度测绘场景
- 需要确保输入坐标在合理范围内(中国境内坐标)
- 百度坐标系(BD-09)的逆向工程算法存在法律争议,建议用于学习参考