GPS坐标系转换(标准坐标系WGS84转GCJ-02火星坐标系)
坐标系简介
通常用经纬度来表示一个地理位置,但是由于一些原因,我们从不同渠道得到的经纬度信息可能并不是在同一个坐标系下。
高德地图、腾讯地图以及谷歌中国区地图使用的是GCJ-02坐标系
百度地图使用的是BD-09坐标系
底层接口(HTML5 Geolocation或ios、安卓API)通过GPS设备获取的坐标使用的是WGS-84坐标系
不同的坐标系之间可能有几十到几百米的偏移,所以在开发基于地图的产品,或者做地理数据可视化时,我们需要修正不同坐标系之间的偏差。
WGS-84 标准坐标系
WGS-84(World Geodetic System, WGS)是使用最广泛的坐标系,也是世界通用的坐标系,GPS设备得到的经纬度就是在WGS84坐标系下的经纬度。通常通过底层接口得到的定位信息都是WGS84坐标系。
GCJ-02 - 国测局坐标
GCJ-02(G-Guojia国家,C-Cehui测绘,J-Ju局),又被称为火星坐标系,是一种基于WGS-84制定的大地测量系统,由中国国测局制定。此坐标系所采用的混淆算法会在经纬度中加入随机的偏移。
国家规定,中国大陆所有公开地理数据都需要至少用GCJ-02进行加密,也就是说我们从国内公司的产品中得到的数据,一定是经过了加密的。绝大部分国内互联网地图提供商都是使用GCJ-02坐标系,包括高德地图,谷歌地图中国区等。
BD-09 - 百度坐标系
BD-09(Baidu, BD)是百度地图使用的地理坐标系,其在GCJ-02上多增加了一次变换,用来保护用户隐私。从百度产品中得到的坐标都是BD-09坐标系。
干货前的说明
其实可以这么理解,火星坐标系是在标准坐标系的基础上进行了一次加密,加密也就是加了一次位置的偏移量,而百度坐标系是进行了两次加密
WGS84转GCJ-02代码
#include <stdio.h>
#include <math.h>
double lng_r,lat_r,tran_lng,tran_lat;
double dlat,dlng,radlat,magic,sqrtmagic;
double mglat,mglng,lngtitude,lattitude;
double lng_w = 120.13372381819; //标准坐标系
double lat_w = 30.181981289109;
double PI = 3.1415926535897932384626;
double ee = 0.00669342162296594323;
int a = 6378245;
//标准坐标系WGS84转GCJ-02火星坐标系
void WG_to_GCJ(float lng,float lat)
{
lng_r = lng - 105.0;
lat_r = lat - 35.0;
//经度转换
tran_lng= 300.0 + lng_r + 2.0 * lat_r + 0.1 * lng_r * lng_r + 0.1 * lng_r * lat_r + 0.1 * sqrt(abs(lng_r));
tran_lng = tran_lng + (20.0 * sin(6.0 * lng_r * PI) + 20.0 * sin(2.0 * lng_r * PI)) * 2.0 / 3.0;
tran_lng = tran_lng + (20.0 * sin(lng_r * PI) + 40.0 * sin(lng_r / 3.0 * PI)) * 2.0 / 3.0;
tran_lng = tran_lng + (150.0 * sin(lng_r / 12.0 * PI) + 300.0 * sin(lng_r / 30.0 * PI)) * 2.0 / 3.0;
//转换纬度
tran_lat = -100.0 + 2.0 * lng_r + 3.0 * lat_r + 0.2 * lat_r * lat_r + 0.1 * lng_r * lat_r + 0.2 * sqrt(abs(lng_r));
tran_lat = tran_lat+(20.0 * sin(6.0 * lng_r * PI) + 20.0 * sin(2.0 * lng_r * PI)) * 2.0 / 3.0;
tran_lat = tran_lat+(20.0 * sin(lat_r * PI) + 40.0 * sin(lat_r / 3.0 * PI)) * 2.0 / 3.0;
tran_lat = tran_lat+(160.0 * sin(lat_r / 12.0 * PI) + 320 * sin(lat_r * PI / 30.0)) * 2.0 / 3.0;
dlat = tran_lat;
dlng = tran_lng;
radlat = lat/180.0 * PI;
magic = sin(radlat);
magic = 1- ee * magic * magic;
sqrtmagic = sqrt(magic);
dlat = (dlat * 180.0)/((a * (1-ee))/(magic * sqrtmagic)*PI);
dlng = (dlng * 180.0)/(a / sqrtmagic * cos(radlat) * PI);
mglat = lat + dlat;
mglng = lng + dlng;
printf("%lf,%lf\r\n",mglat,mglng);
}
int main(void)
{
WG_to_GCJ(lng_w,lat_w);
return 0;
}
GCJ-02转WGS84代码
double lng_r,lat_r,tran_lng,tran_lat;
double dlat,dlng,radlat,magic,sqrtmagic;
double mglat,mglng,lngtitude,lattitude;
double lng_G = 120.13845564; //火星坐标系
double lat_G = 30.17967337889;
double PI = 3.1415926535897932384626;
double ee = 0.00669342162296594323;
int a = 6378245;
void GCJ_to_WG(float lng,float lat)
{
lng_r = lng - 105.0;
lat_r = lat - 35.0;
//转换经度
tran_lng= 300.0 + lng_r + 2.0 * lat_r + 0.1 * lng_r * lng_r + 0.1 * lng_r * lat_r + 0.1 * sqrt(abs(lng_r));
tran_lng = tran_lng + (20.0 * sin(6.0 * lng_r * PI) + 20.0 * sin(2.0 * lng_r * PI)) * 2.0 / 3.0;
tran_lng = tran_lng + (20.0 * sin(lng_r * PI) + 40.0 * sin(lng_r / 3.0 * PI)) * 2.0 / 3.0;
tran_lng = tran_lng + (150.0 * sin(lng_r / 12.0 * PI) + 300.0 * sin(lng_r / 30.0 * PI)) * 2.0 / 3.0;
//转换纬度
tran_lat = -100.0 + 2.0 * lng_r + 3.0 * lat_r + 0.2 * lat_r * lat_r + 0.1 * lng_r * lat_r + 0.2 * sqrt(abs(lng_r));
tran_lat = tran_lat+(20.0 * sin(6.0 * lng_r * PI) + 20.0 * sin(2.0 * lng_r * PI)) * 2.0 / 3.0;
tran_lat = tran_lat+(20.0 * sin(lat_r * PI) + 40.0 * sin(lat_r / 3.0 * PI)) * 2.0 / 3.0;
tran_lat = tran_lat+(160.0 * sin(lat_r / 12.0 * PI) + 320 * sin(lat_r * PI / 30.0)) * 2.0 / 3.0;
dlat = tran_lat;
dlng = tran_lng;
radlat = lat/180.0 * PI;
magic = sin(radlat);
magic = 1- ee * magic * magic;
sqrtmagic = sqrt(magic);
dlat = (dlat * 180.0)/((a * (1-ee))/(magic * sqrtmagic)*PI);
dlng = (dlng * 180.0)/(a / sqrtmagic * cos(radlat) * PI);
mglat = lat + dlat;
mglng = lng + dlng;
lngtitude = lng * 2 - mglng;
lattitude = lat * 2 - mglat;
printf("%f,%f\r\n",lattitude,lngtitude);
}
int main(void)
{
GCJ_to_WG(lng_G,lat_G);
return 0;
}