变更记录
序号 | 录入时间 | 录入人 | 备注 |
---|---|---|---|
1 | 2016-02-29 | Alfred Jiang | - |
1 | 2016-03-08 | Alfred Jiang | 更新 OC 调用 |
方案名称
地图 - 使用 Eviltransform 进行火星坐标转换(大陆地区地理坐标偏移问题)
关键字
地图 \ Eviltransform \ 坐标 \ 偏移
需求场景
- 需要解决中国大陆地图坐标偏移问题
参考链接
详细内容
Swift Version : 更新版,支持 OC 调用
//
// 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
#!/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))
效果图
(无)
备注
(无)