https://www.huangdf.xyz/categories/study-notes
南风
南风
发布于 2025-10-13 / 3 阅读
0
0

关于几种在线坐标系

由于最近业务原因,需要对经纬度进行一些操作,而不同的厂商使用的坐标系又不一样,所以在这里总结一下几种坐标系的区别。

1、目前几种坐标系

1.1、大地坐标系

CGCS2000,全称China Geodetic Coordinate System 2000,即大地坐标系。WGS84(World Geodetic System)是一样的,两者只在扁率上有微小差异,所以基本上可以近似认为两者没有差距。

使用代表:天地图。

1.2、国家测绘局02坐标系

俗称火星坐标系、GCJ-02坐标系。是在WGS84坐标系上经过偏移(GCJ-02加密算法)得到的坐标系,广泛用于导航等,但仅适用于中国大陆地区。在港澳台等地区偏移很小或者不偏移,在境外不偏移。

使用代表:高德地图、谷歌地图(大陆部分)、腾讯地图、搜狗地图。

1.3、BD-09

百度地图是在火星坐标系的基础上进行加密得到的,具体加密算法未知,得到的坐标系专用于百度地图等系列服务。是百度地图的独家坐标系标准。

使用代表:百度地图。

1.4、北斗坐标系

北斗坐标系和WGS84坐标系类似,但是没有那么多框架点,框架点只有几个,测量精度低,但更新快。而CGCS2000和WGS84由于框架点多达2000+,所以更新慢,周期能达到几十年,但是精度高。

使用代表:北斗。

2、几种坐标系转换

2.1、WGS84转GCJ02

由于官方算法保密,社区通过逆向的方式实现了近似的算法,如下:

import math

def wgs84_to_gcj02(lng, lat):
    """
    WGS-84转GCJ-02(火星坐标系)
    基于公开的近似算法
    """
    a = 6378245.0  # 长半轴
    ee = 0.00669342162296594323  # 扁率
    
    if out_of_china(lng, lat):
        return lng, lat
    
    dlat = transform_lat(lng - 105.0, lat - 35.0)
    dlng = transform_lng(lng - 105.0, lat - 35.0)
    
    radlat = lat / 180.0 * math.pi
    magic = math.sin(radlat)
    magic = 1 - ee * magic * magic
    sqrtmagic = math.sqrt(magic)
    
    dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * math.pi)
    dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * math.pi)
    
    mglat = lat + dlat
    mglng = lng + dlng
    
    return mglng, mglat

def out_of_china(lng, lat):
    """判断是否在中国境外"""
    if lng < 72.004 or lng > 137.8347:
        return True
    if lat < 0.8293 or lat > 55.8271:
        return True
    return False

def transform_lat(x, y):
    """纬度转换"""
    ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * math.sqrt(abs(x))
    ret += (20.0 * math.sin(6.0 * x * math.pi) + 20.0 * math.sin(2.0 * x * math.pi)) * 2.0 / 3.0
    ret += (20.0 * math.sin(y * math.pi) + 40.0 * math.sin(y / 3.0 * math.pi)) * 2.0 / 3.0
    ret += (160.0 * math.sin(y / 12.0 * math.pi) + 320 * math.sin(y * math.pi / 30.0)) * 2.0 / 3.0
    return ret

def transform_lng(x, y):
    """经度转换"""
    ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * math.sqrt(abs(x))
    ret += (20.0 * math.sin(6.0 * x * math.pi) + 20.0 * math.sin(2.0 * x * math.pi)) * 2.0 / 3.0
    ret += (20.0 * math.sin(x * math.pi) + 40.0 * math.sin(x / 3.0 * math.pi)) * 2.0 / 3.0
    ret += (150.0 * math.sin(x / 12.0 * math.pi) + 300.0 * math.sin(x / 30.0 * math.pi)) * 2.0 / 3.0
    return ret

2.2、GCJ-02转WGS84

def gcj02_to_wgs84(lng, lat):
    """
    GCJ-02转WGS-84
    使用迭代法逼近真实坐标
    """
    # 先用GCJ-02坐标作为初始值
    wgs84_lng, wgs84_lat = lng, lat
    
    # 迭代计算,直到误差足够小
    for i in range(10):
        gcj_lng, gcj_lat = wgs84_to_gcj02(wgs84_lng, wgs84_lat)
        delta_lng = gcj_lng - lng
        delta_lat = gcj_lat - lat
        
        wgs84_lng -= delta_lng
        wgs84_lat -= delta_lat
        
        if abs(delta_lng) < 1e-7 and abs(delta_lat) < 1e-7:
            break
    
    return wgs84_lng, wgs84_lat

2.3、GCJ-02转BD-09

import math

def gcj02_to_bd09(lng, lat):
    """
    将GCJ-02坐标转换为BD-09坐标
    """
    x_pi = 3.14159265358979324 * 3000.0 / 180.0
    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)
    bd_lng = z * math.cos(theta) + 0.0065
    bd_lat = z * math.sin(theta) + 0.006
    return bd_lng, bd_lat

2.4、BD-09转GCJ-02

def bd09_to_gcj02(bd_lng, bd_lat):
    """
    将BD-09坐标转换为GCJ-02坐标
    """
    x_pi = 3.14159265358979324 * 3000.0 / 180.0
    x = bd_lng - 0.0065
    y = bd_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)
    gg_lng = z * math.cos(theta)
    gg_lat = z * math.sin(theta)
    return gg_lng, gg_lat

2.5、其余转换

其余的类似WGS84转BD09也就是WGS84->GCJ02->BD09,反之亦然。而CGCS2000和WGS84类似,区别只是偏率不同,且偏率差距非常小,大多数情况可以忽略不计。


评论