变更记录

序号 录入时间 录入人 备注
1 2016-02-29 Alfred Jiang -
1 2016-03-08 Alfred Jiang 更新 OC 调用

方案名称

地图 - 使用 Eviltransform 进行火星坐标转换(大陆地区地理坐标偏移问题)

关键字

地图 \ Eviltransform \ 坐标 \ 偏移

需求场景

  1. 需要解决中国大陆地图坐标偏移问题

参考链接

  1. Why You Can’t Trust GPS in China
  2. GitHub - Eviltransform

详细内容

Swift Version : 更新版,支持 OC 调用
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
//
// LocationTransform.swift
// PSWalker
//
// Created by viktyz on 16/3/8.
// Copyright © 2016年 Alfred Jiang. All rights reserved.
//

import Foundation

@objc public class LocationTransform : NSObject {

static let π = M_PI, latKey = "lat", lonKey = "lon"

static func isOutOfChina(lat lat: Double, lon: Double) -> Bool {
if lon < 72.004 || lon > 137.8347 {
return true
}
if lat < 0.8293 || lat > 55.8271 {
return true
}
return false
}

static func transformLat(x x: Double, y: Double) -> Double {
var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y
ret += 0.1 * x * y + 0.2 * sqrt(abs(x))
ret += (20.0 * sin(6.0 * x * π) + 20.0 * sin(2.0 * x * π)) * 2.0 / 3.0
ret += (20.0 * sin(y * π) + 40.0 * sin(y / 3.0 * π)) * 2.0 / 3.0
ret += (160.0 * sin(y / 12.0 * π) + 320 * sin(y * π / 30.0)) * 2.0 / 3.0
return ret
}

static func transformLon(x x: Double, y: Double) -> Double {
var ret = 300.0 + x + 2.0 * y + 0.1 * x * x
ret += 0.1 * x * y + 0.1 * sqrt(abs(x))
ret += (20.0 * sin(6.0 * x * π) + 20.0 * sin(2.0 * x * π)) * 2.0 / 3.0
ret += (20.0 * sin(x * π) + 40.0 * sin(x / 3.0 * π)) * 2.0 / 3.0
ret += (150.0 * sin(x / 12.0 * π) + 300.0 * sin(x / 30.0 * π)) * 2.0 / 3.0
return ret
}

static func delta(lat lat: Double, lon: Double) -> (Double, Double) {
let r = 6378245.0
let ee = 0.00669342162296594323
let radLat = lat / 180.0 * π
var magic = sin(radLat)
magic = 1 - ee * magic * magic
let sqrtMagic = sqrt(magic)
var dLat = transformLat(x: lon - 105.0, y: lat - 35.0)
var dLon = transformLon(x: lon - 105.0, y: lat - 35.0)
dLat = (dLat * 180.0) / ((r * (1 - ee)) / (magic * sqrtMagic) * π)
dLon = (dLon * 180.0) / (r / sqrtMagic * cos(radLat) * π)
return (dLat, dLon)
}

//GCJ-02坐标用在谷歌地图,高德地图等中国地图服务。(百度地图要在GCJ-02基础上再加转换)

//输入WGS-84地球坐标(wgsLat, wgsLng),转换为GCJ-02火星坐标(gcjLat, gcjLng)
static func wgs2gcj(wgsLat: Double, wgsLon: Double) -> [String: Double] {
if isOutOfChina(lat: wgsLat, lon: wgsLon) {
return [latKey: wgsLat, lonKey: wgsLon]
}
let (dLat, dLon) = delta(lat: wgsLat, lon: wgsLon)
return [latKey: wgsLat + dLat, lonKey: wgsLon + dLon]
}

//输入GCJ-02火星坐标(gcjLat, gcjLng),转换为WGS-84地球坐标(wgsLat, wgsLng),输出的WGS-84坐标精度为1米到2米之间
static func gcj2wgs(gcjLat: Double, gcjLon: Double) -> [String: Double] {
if isOutOfChina(lat: gcjLat, lon: gcjLon) {
return [latKey: gcjLat, lonKey: gcjLon]
}
let (dLat, dLon) = delta(lat: gcjLat, lon: gcjLon)
return [latKey: gcjLat - dLat, lonKey: gcjLon - dLon]
}

//输入GCJ-02火星坐标(gcjLat, gcjLng),转换为WGS-84地球坐标(wgsLat, wgsLng),输出的WGS-84坐标精度0.5米内
static func gcj2wgs_exact(gcjLat: Double, gcjLon: Double) -> [String: Double] {
let initDelta = 0.01, threshold = 0.000001
var dLat = initDelta
var dLon = initDelta
var mLat = gcjLat - dLat
var mLon = gcjLon - dLon
var pLat = gcjLat + dLat
var pLon = gcjLon + dLon
var wgsLat = gcjLat, wgsLon = gcjLon
for (var i = 0; i < 30; i++) {
wgsLat = (mLat + pLat) / 2
wgsLon = (mLon + pLon) / 2
var tmp = wgs2gcj(wgsLat, wgsLon: wgsLon) as [String: Double]
dLat = tmp[latKey]! - gcjLat
dLon = tmp[lonKey]! - gcjLon
if (abs(dLat) < threshold) && (abs(dLon) < threshold) {
return [latKey: wgsLat, lonKey: wgsLon]
}
if dLat > 0 {
pLat = wgsLat
} else {
mLat = wgsLat
}
if dLon > 0 {
pLon = wgsLon
} else {
mLon = wgsLon
}
}
return [latKey: wgsLat, lonKey: wgsLon]
}

//GCJ-02坐标 转为 百度地图坐标
static func gcj2bd(gcjLat: Double, gcjLon: Double) -> [String: Double] {
if isOutOfChina(lat: gcjLat, lon: gcjLon) {
return [latKey: gcjLat, lonKey: gcjLon]
}
let x = gcjLon, y = gcjLat
let z = sqrt(x * x + y * y) + 0.00002 * sin(y * π)
let theta = atan2(y, x) + 0.000003 * cos(x * π)
let bdLon = z * cos(theta) + 0.0065
let bdLat = z * sin(theta) + 0.006
return [latKey: bdLat, lonKey: bdLon]
}

//百度地图坐标 转为 GCJ-02坐标
static func bd2gcj(bdLat: Double, bdLon: Double) -> [String: Double] {
if isOutOfChina(lat: bdLat, lon: bdLon) {
return [latKey: bdLat, lonKey: bdLon]
}
let x = bdLon - 0.0065, y = bdLat - 0.006
let z = sqrt(x * x + y + y) - 0.00002 * sin(y * π)
let theta = atan2(y, x) - 0.000003 * cos(x * π)
let gcjLon = z * cos(theta)
let gcjLat = z * sin(theta)
return [latKey: gcjLat, lonKey: gcjLon]
}

//WGS-84坐标 转为 百度地图坐标
static func wgs2bd(wgsLat: Double, wgsLon: Double) -> [String: Double] {
let gcj = wgs2gcj(wgsLat, wgsLon: wgsLon) as [String: Double]
return gcj2bd(gcj[latKey]!, gcjLon: gcj[lonKey]!)
}

//百度地图坐标 转为 WGS-84坐标
static func bd2wgs(bdLat: Double, bdLon: Double) -> [String: Double] {
let gcj = bd2gcj(bdLat, bdLon: bdLon) as [String: Double]
return gcj2wgs(gcj[latKey]!, gcjLon: gcj[lonKey]!)
}

}
Python Version
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
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import math


__all__ = ['wgs2gcj', 'gcj2wgs', 'gcj2wgs_exact',
'distance', 'gcj2bd', 'bd2gcj', 'wgs2bd', 'bd2wgs']


def outOfChina(lat, lng):
return not (72.004 <= lng <= 137.8347 and 0.8293 <= lat <= 55.8271)


def transformLat(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 transformLon(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


def delta(lat, lng):
a = 6378245.0
ee = 0.00669342162296594323
dLat = transformLat(lng - 105.0, lat - 35.0)
dLng = transformLon(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)
return dLat, dLng


def wgs2gcj(wgsLat, wgsLng):
if outOfChina(wgsLat, wgsLng):
return wgsLat, wgsLng
else:
dlat, dlng = delta(wgsLat, wgsLng)
return wgsLat + dlat, wgsLng + dlng


def gcj2wgs(gcjLat, gcjLng):
if outOfChina(gcjLat, gcjLng):
return gcjLat, gcjLng
else:
dlat, dlng = delta(gcjLat, gcjLng)
return gcjLat - dlat, gcjLng - dlng


def gcj2wgs_exact(gcjLat, gcjLng):
initDelta = 0.01
threshold = 0.000001
dLat = dLng = initDelta
mLat = gcjLat - dLat
mLng = gcjLng - dLng
pLat = gcjLat + dLat
pLng = gcjLng + dLng
for i in range(30):
wgsLat = (mLat + pLat) / 2
wgsLng = (mLng + pLng) / 2
tmplat, tmplng = wgs2gcj(wgsLat, wgsLng)
dLat = tmplat - gcjLat
dLng = tmplng - gcjLng
if abs(dLat) < threshold and abs(dLng) < threshold:
return wgsLat, wgsLng
if dLat > 0:
pLat = wgsLat
else:
mLat = wgsLat
if dLng > 0:
pLng = wgsLng
else:
mLng = wgsLng
return wgsLat, wgsLng


def distance(latA, lngA, latB, lngB):
earthR = 6371000
x = (math.cos(latA * math.pi / 180) * math.cos(latB * math.pi / 180) *
math.cos((lngA - lngB) * math.pi / 180))
y = math.sin(latA * math.pi / 180) * math.sin(latB * math.pi / 180)
s = x + y
if s > 1:
s = 1
if s < -1:
s = -1
alpha = math.acos(s)
distance = alpha * earthR
return distance


def gcj2bd(gcjLat, gcjLng):
if outOfChina(gcjLat, gcjLng):
return gcjLat, gcjLng

x = gcjLng
y = gcjLat
z = math.hypot(x, y) + 0.00002 * math.sin(y * math.pi)
theta = math.atan2(y, x) + 0.000003 * math.cos(x * math.pi)
bdLng = z * math.cos(theta) + 0.0065
bdLat = z * math.sin(theta) + 0.006
return bdLat, bdLng


def bd2gcj(bdLat, bdLng):
if outOfChina(bdLat, bdLng):
return bdLat, bdLng

x = bdLng - 0.0065
y = bdLat - 0.006
z = math.hypot(x, y) - 0.00002 * math.sin(y * math.pi)
theta = math.atan2(y, x) - 0.000003 * math.cos(x * math.pi)
gcjLng = z * math.cos(theta)
gcjLat = z * math.sin(theta)
return gcjLat, gcjLng


def wgs2bd(wgsLat, wgsLng):
return gcj2bd(*wgs2gcj(wgsLat, wgsLng))


def bd2wgs(bdLat, bdLng):
return gcj2wgs(*bd2gcj(bdLat, bdLng))

效果图

(无)

备注

(无)