一、坐标系概述

  1. WGS-84(World Geodetic System 1984)

    • 国际通用标准坐标系
    • 应用场景:GPS模块、Google Earth、国际航空导航
    • 特点:未经任何偏移的原始GPS坐标
  2. GCJ-02(中国国家测绘局坐标系)

    • 中国官方规定的坐标偏移标准
    • 应用场景:Google Map(中国版)、高德地图、腾讯地图等
    • 特点:对WGS-84坐标进行加密偏移的"火星坐标系"
  3. BD-09(百度坐标系)

    • 百度自研的二次坐标偏移标准
    • 应用场景:百度地图(Baidu Map)
    • 特点:在GCJ-02基础上进行二次加密偏移

二、坐标转换方法

  1. 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)
        }

代码优化说明:

  1. 结构优化

    • 将常量提取为类/模块级常量
    • 使用静态方法保持代码复用性
    • 统一输入输出格式(对象/数组/字典)
  2. 性能优化

    • 使用hypot替代手动计算平方根
    • 减少重复计算(如X_PI的预计算)
    • 参数类型标注(Python/Java)
  3. 精度优化

    • 使用更精确的数学库函数(如Python的math模块)
    • 保留原始算法精度(误差在厘米级)
  4. 可维护性提升

    • 添加清晰的函数命名
    • 使用类型提示(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}

注意事项:

  1. 转换结果存在约10米的误差范围
  2. 不适用于高精度测绘场景
  3. 需要确保输入坐标在合理范围内(中国境内坐标)
  4. 百度坐标系(BD-09)的逆向工程算法存在法律争议,建议用于学习参考

标签: 坐标转换, 地图开发

添加新评论