地图坐标系

分类

所有坐标体系的原点,都是非洲几内亚湾。

按用途分类:

  1. GIS(尤其WebGIS)常用坐标系: WBS84,GCJ02,BD09
  2. 测绘常用坐标系:西安80,北京54,CGC2000

按测量单位分类:

  1. 经纬度坐标:球面坐标,单位,如(116.38817139.935961)。
  2. 墨卡托坐标:平面坐标,单位,如(215362.00021333335 99526.00034912192), 主要用于后台计算。

墨卡托坐标和经纬度坐标基本一一对应,如 WGS84墨卡托,GCJ02墨卡托,BD09墨卡托,此处按下不表。

按获取途径分:

  1. 设备获取GPS坐标:
    • 若ios的原生定位库,则获得WGS84
    • 若高德sdk,则获取GCJ02
    • 若百度sdk,则可获得百度坐标(bd09)或者火星坐标(GCJ02),默认是bd09
  2. 在线WEB地图应用:遵循各自的坐标系。

WGS84坐标系

WGS-84坐标系(World Geodetic System)是目前国际上统一采用的大地坐标系。坐标原点为地球质心。

GPS广播星历是以WGS-84坐标系为根据的。

世界主流Web地图都基于该坐标系。如:

  • Google Maps
  • OpenStreetMap
  • Bing Map
  • ArcGIS
  • Heremaps

中国因政策要求,在WGS基础上进行经纬度加密,形成加密后的新坐标系(GCJ02、BD09)。

GCJ02坐标系(火星坐标系)

GCJ-02坐标系(Guojia,Cehui,Ju),是由中国国家测绘局制订的地理信息系统的坐标系统。

  • 该坐标系在WGS84坐标系的基础上,加密经纬度数据。
  • 按地形图非线性保密处理算法,将真实坐标加密成虚假坐标。因随机加密,故各地偏移情况有所不同。
  • 使用GCJ-02记录的地点,在GCJ-02地图中会正确显示位置,在WGS-84地图会有100-700米不等的偏移。
  • 网上纠偏实现都是基于某份泄露的WGS到CGJ加偏代码实现。

国内出版的各种地图系统(包括电子形式),必须至少采用GCJ-02对地理位置进行首次加密。

  • 国测局授权提供位置和地图服务的厂商,例如高德地图、NavInfo,都需要特别购买一个“纠偏”算法,将GPS坐标转为和地图一致的坐标系。
  • 由于一国两制,香港、澳门地图不受测绘法限制,没有偏移问题。然而在两地和中国大陆边境附近,网络地图提供的道路形状会因为偏移而互相断开。

中国主流Web地图都是基于GCJ02坐标系,如:

  1. 谷歌中国地图(由高德提供地图数据)
  2. 腾讯地图
  3. 高德地图

BD09坐标系(百度坐标系)

该坐标系在GCJ02坐标系基础上,加密经纬度算法(百度自身的加偏算法)。
目前只有百度地图采用BD09坐标系:

  1. 百度地图

其他坐标系

搜狗坐标: 搜狐搜狗地图API

关系图

互联网地图服务规定,国内互联网地图必须使用国测局加密的GCJ02坐标系。
百度在GCJ02的基础上,又做了一次加密。

graph TD
A((WGS84坐标系)) ==> B>加密]
    B ==> C((GCJ02坐标系))
    C ==> E>加密]
    E ==> F((百度坐标系))

    A --- D(相关地图)
    D --- D1(GoogleMaps)
    D --- D2(BingMap)
    D --- D3(ArcGIS)
    D --- D4(OpenStreetMap)
    D --- D5(Heremaps)

    F --- F1(百度地图)

    C --- G(相关地图)
    G --- G1(高德地图)
    G --- G2(腾讯地图)
    G --- G3(谷歌中国地图)

    G1 -.- H1(阿里)
    G2 -.- H2(腾讯)

    F1 -.- H3(Echarts)

    style C fill:greenyellow
    style F fill:greenyellow

    style F1 fill:orange
    style G1 fill:orange
    style G2 fill:orange
    style G3 fill:orange

坐标转换

各类别坐标可相互转换(位置纠偏),但请首先确认采集数据的坐标系类别。

  1. 坐标系类别
  2. 是否经纬度反序,如谷歌地图(lat, lng),高德地图(lng, lat)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
public class GPSUtil {
public static double pi = 3.1415926535897932384626;
public static double x_pi = 3.14159265358979324 * 3000.0 / 180.0;
public static double a = 6378245.0;
public static double ee = 0.00669342162296594323;

public static double transformLat(double x, double y) {
double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
return ret;
}

public static double transformLon(double x, double y) {
double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
return ret;
}
public static double[] transform(double lat, double lon) {
if (outOfChina(lat, lon)) {
return new double[]{lat,lon};
}
double dLat = transformLat(lon - 105.0, lat - 35.0);
double dLon = transformLon(lon - 105.0, lat - 35.0);
double radLat = lat / 180.0 * pi;
double magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
double sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
double mgLat = lat + dLat;
double mgLon = lon + dLon;
return new double[]{mgLat,mgLon};
}
public static boolean outOfChina(double lat, double lon) {
if (lon < 72.004 || lon > 137.8347)
return true;
if (lat < 0.8293 || lat > 55.8271)
return true;
return false;
}
/**
* WGS84 to GCJ-02
* @param lat
* @param lon
* @return
*/
public static double[] gps84_To_Gcj02(double lat, double lon) {
if (outOfChina(lat, lon)) {
return new double[]{lat,lon};
}
double dLat = transformLat(lon - 105.0, lat - 35.0);
double dLon = transformLon(lon - 105.0, lat - 35.0);
double radLat = lat / 180.0 * pi;
double magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
double sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
double mgLat = lat + dLat;
double mgLon = lon + dLon;
return new double[]{mgLat, mgLon};
}

/**
* GCJ-02 to WGS84
* @param lon
* @param lat
* @return
* */
public static double[] gcj02_To_Gps84(double lat, double lon) {
double[] gps = transform(lat, lon);
double lontitude = lon * 2 - gps[1];
double latitude = lat * 2 - gps[0];
return new double[]{latitude, lontitude};
}
/**
* GCJ-02 to BD-09
* @param lat
* @param lon
*/
public static double[] gcj02_To_Bd09(double lat, double lon) {
double x = lon, y = lat;
double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
double tempLon = z * Math.cos(theta) + 0.0065;
double tempLat = z * Math.sin(theta) + 0.006;
double[] gps = {tempLat,tempLon};
return gps;
}

/**
* BD-09 to GCJ-02
* @param bd_lat
* @param bd_lon
* @return
*/
public static double[] bd09_To_Gcj02(double lat, double lon) {
double x = lon - 0.0065, y = lat - 0.006;
double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
double tempLon = z * Math.cos(theta);
double tempLat = z * Math.sin(theta);
double[] gps = {tempLat,tempLon};
return gps;
}

/**WGS84to BD-09
* @param lat
* @param lon
* @return
*/
public static double[] gps84_To_bd09(double lat,double lon){
double[] gcj02 = gps84_To_Gcj02(lat,lon);
double[] bd09 = gcj02_To_Bd09(gcj02[0],gcj02[1]);
return bd09;
}
public static double[] bd09_To_gps84(double lat,double lon){
double[] gcj02 = bd09_To_Gcj02(lat, lon);
double[] gps84 = gcj02_To_Gps84(gcj02[0], gcj02[1]);
//保留小数点后六位
gps84[0] = retain6(gps84[0]);
gps84[1] = retain6(gps84[1]);
return gps84;
}

/**保留小数点后六位
* @param num
* @return
*/
private static double retain6(double num){
String result = String .format("%.6f", num);
return Double.valueOf(result);
}
}
-------------Keep It Simple Stupid-------------
0%