由于最近业务原因,需要对经纬度进行一些操作,而不同的厂商使用的坐标系又不一样,所以在这里总结一下几种坐标系的区别。
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类似,区别只是偏率不同,且偏率差距非常小,大多数情况可以忽略不计。