变更记录

序号 录入时间 录入人 备注
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 调用
//
//  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))

效果图

(无)

备注

(无)

results matching ""

    No results matching ""