07GIS/坐标偏移问题排查

结论

经度偏移大到离谱:

  • 考虑本地坐标系

经度偏移 > 0.006:

经度偏移 > 0.0001 考虑:

处理坐标偏移的工具

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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
var GPS = {
PI: 3.14159265358979324,
x_pi: (3.14159265358979324 * 3000.0) / 180.0,
delta: function(lat, lon) {
// Krasovsky 1940
//
// a = 6378245.0, 1/f = 298.3
// b = a * (1 - f)
// ee = (a^2 - b^2) / a^2;
var a = 6378245.0; // a: 卫星椭球坐标投影到平面地图坐标系的投影因子。
var ee = 0.00669342162296594323; // ee: 椭球的偏心率。
var dLat = this.transformLat(lon - 105.0, lat - 35.0);
var dLon = this.transformLon(lon - 105.0, lat - 35.0);
var radLat = (lat / 180.0) * this.PI;
var magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
var sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / (((a * (1 - ee)) / (magic * sqrtMagic)) * this.PI);
dLon = (dLon * 180.0) / ((a / sqrtMagic) * Math.cos(radLat) * this.PI);
return { lat: dLat, lon: dLon };
},

//WGS-84 to GCJ-02
gcj_encrypt: function(wgsLat, wgsLon) {
if (this.outOfChina(wgsLat, wgsLon)) return { lat: wgsLat, lon: wgsLon };

var d = this.delta(wgsLat, wgsLon);
return { lat: wgsLat + d.lat, lon: wgsLon + d.lon };
},
//GCJ-02 to WGS-84
gcj_decrypt: function(gcjLat, gcjLon) {
if (this.outOfChina(gcjLat, gcjLon)) return { lat: gcjLat, lon: gcjLon };

var d = this.delta(gcjLat, gcjLon);
return { lat: gcjLat - d.lat, lon: gcjLon - d.lon };
},
//GCJ-02 to WGS-84 exactly
gcj_decrypt_exact: function(gcjLat, gcjLon) {
var initDelta = 0.01;
var threshold = 0.000000001;
var dLat = initDelta,
dLon = initDelta;
var mLat = gcjLat - dLat,
mLon = gcjLon - dLon;
var pLat = gcjLat + dLat,
pLon = gcjLon + dLon;
var wgsLat,
wgsLon,
i = 0;
while (1) {
wgsLat = (mLat + pLat) / 2;
wgsLon = (mLon + pLon) / 2;
var tmp = this.gcj_encrypt(wgsLat, wgsLon);
dLat = tmp.lat - gcjLat;
dLon = tmp.lon - gcjLon;
if (Math.abs(dLat) < threshold && Math.abs(dLon) < threshold) break;

if (dLat > 0) pLat = wgsLat;
else mLat = wgsLat;
if (dLon > 0) pLon = wgsLon;
else mLon = wgsLon;

if (++i > 10000) break;
}
//console.log(i);
return { lat: wgsLat, lon: wgsLon };
},
//GCJ-02 to BD-09
bd_encrypt: function(gcjLat, gcjLon) {
var x = gcjLon,
y = gcjLat;
var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * this.x_pi);
var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * this.x_pi);
bdLon = z * Math.cos(theta) + 0.0065;
bdLat = z * Math.sin(theta) + 0.006;
return { lat: bdLat, lon: bdLon };
},
//BD-09 to GCJ-02
bd_decrypt: function(bdLat, bdLon) {
var x = bdLon - 0.0065,
y = bdLat - 0.006;
var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * this.x_pi);
var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * this.x_pi);
var gcjLon = z * Math.cos(theta);
var gcjLat = z * Math.sin(theta);
return { lat: gcjLat, lon: gcjLon };
},
//WGS-84 to Web mercator
//mercatorLat -> y mercatorLon -> x
mercator_encrypt: function(wgsLat, wgsLon) {
var x = (wgsLon * 20037508.34) / 180;
var y =
Math.log(Math.tan(((90 + wgsLat) * this.PI) / 360)) / (this.PI / 180);
y = (y * 20037508.34) / 180;
return { lat: y, lon: x };
/*
if ((Math.abs(wgsLon) > 180 || Math.abs(wgsLat) > 90))
return null;
var x = 6378137.0 * wgsLon * 0.017453292519943295;
var a = wgsLat * 0.017453292519943295;
var y = 3189068.5 * Math.log((1.0 + Math.sin(a)) / (1.0 - Math.sin(a)));
return {'lat' : y, 'lon' : x};
//*/
},
// Web mercator to WGS-84
// mercatorLat -> y mercatorLon -> x
mercator_decrypt: function(mercatorLat, mercatorLon) {
var x = (mercatorLon / 20037508.34) * 180;
var y = (mercatorLat / 20037508.34) * 180;
y =
(180 / this.PI) *
(2 * Math.atan(Math.exp((y * this.PI) / 180)) - this.PI / 2);
return { lat: y, lon: x };
/*
if (Math.abs(mercatorLon) < 180 && Math.abs(mercatorLat) < 90)
return null;
if ((Math.abs(mercatorLon) > 20037508.3427892) || (Math.abs(mercatorLat) > 20037508.3427892))
return null;
var a = mercatorLon / 6378137.0 * 57.295779513082323;
var x = a - (Math.floor(((a + 180.0) / 360.0)) * 360.0);
var y = (1.5707963267948966 - (2.0 * Math.atan(Math.exp((-1.0 * mercatorLat) / 6378137.0)))) * 57.295779513082323;
return {'lat' : y, 'lon' : x};
//*/
},
// two point's distance
distance: function(latA, lonA, latB, lonB) {
var earthR = 6371000;
var x =
Math.cos((latA * this.PI) / 180) *
Math.cos((latB * this.PI) / 180) *
Math.cos(((lonA - lonB) * this.PI) / 180);
var y = Math.sin((latA * this.PI) / 180) * Math.sin((latB * this.PI) / 180);
var s = x + y;
if (s > 1) s = 1;
if (s < -1) s = -1;
var alpha = Math.acos(s);
var distance = alpha * earthR;
return distance;
},
outOfChina: function(lat, lon) {
if (lon < 72.004 || lon > 137.8347) return true;
if (lat < 0.8293 || lat > 55.8271) return true;
return false;
},
transformLat: function(x, y) {
var 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 * this.PI) +
20.0 * Math.sin(2.0 * x * this.PI)) *
2.0) /
3.0;
ret +=
((20.0 * Math.sin(y * this.PI) + 40.0 * Math.sin((y / 3.0) * this.PI)) *
2.0) /
3.0;
ret +=
((160.0 * Math.sin((y / 12.0) * this.PI) +
320 * Math.sin((y * this.PI) / 30.0)) *
2.0) /
3.0;
return ret;
},
transformLon: function(x, y) {
var 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 * this.PI) +
20.0 * Math.sin(2.0 * x * this.PI)) *
2.0) /
3.0;
ret +=
((20.0 * Math.sin(x * this.PI) + 40.0 * Math.sin((x / 3.0) * this.PI)) *
2.0) /
3.0;
ret +=
((150.0 * Math.sin((x / 12.0) * this.PI) +
300.0 * Math.sin((x / 30.0) * this.PI)) *
2.0) /
3.0;
return ret;
}
};

TLDR

WGS-84、北京 54、西安 80 坐标差别:

  • GPS 的测量结果与我国的 54 系或 80 系坐标相差几十米至一百多米,随区域不同,差别也不同,经粗落统计,我国西部相差 70 米左右,东北部 140 米左右,南部 75 米左右,中部 45 米左右。

地理数据加密:

  • 中国大陆所有公开地理数据都需要至少用 GCJ-02 进行加密。

几种坐标:

  • GCJ-02: 高德地图、谷歌地图、腾讯地图
  • BD-09: 百度地图
  • 搜狗坐标:搜狗地图
  • WGS-84:Google Earth(度分秒形式)等

参见