地球坐标转高德地图坐标

我写的客户端获取到的经纬度是底层蓝牙传上来的,其实是地球坐标,也就是GPS卫星测量到的实实在在的原始坐标。而高德地图采用的坐标是火星坐标,是经天朝测绘局加密后的坐标。我直接拿原始坐标去定位,自然会出现很大的偏差。

但是对于国内地图而言,使用LocationManager定位所获得经纬度,是有一段较大距离的偏移的。
原来国内地图使用的坐标系统是GCJ-02而iOS SDK中所用到的是国际标准的坐标系统WGS-84。
因为国内使用的是加密后的坐标系GCJ-02就是网络上叫的火星坐标。

LocationManager就是因为得到的是火星坐标偏移后的经纬度,所以导致在MapView上有很大的偏差,而在MKMapView上通过定位自己位置所获得的经纬度有是准确,因为Apple已经对国内地图做了偏移优化。

SDK 坐标系 名称
iOS SDK WGS-84 地球坐标
高德 SDK BD-09 火星坐标
百度 SDK GCJ-02 百度坐标

WGS84TOGCJ02.h

#import 
#import 

@interface WGS84TOGCJ02 : NSObject

// 判断是否已经超出中国范围
+ (BOOL)isLocationOutOfChina:(CLLocationCoordinate2D)location;

// 将WGS-84转为GCJ-02(火星坐标)
// 转GCJ-02
+ (CLLocationCoordinate2D)transformFromWGSToGCJ:(CLLocationCoordinate2D)wgsLoc;

@end

WGS84TOGCJ02.m

#import "WGS84TOGCJ02.h"

const double a = 6378245.0;
const double ee = 0.00669342162296594323;
const double pi = 3.14159265358979324;

@implementation WGS84TOGCJ02

// 我们通过判断isLocationOutOfChina 然后调用transformLatWithX方法就能获取转换后的
+ (CLLocationCoordinate2D)transformFromWGSToGCJ:(CLLocationCoordinate2D)wgsLoc
{
    CLLocationCoordinate2D adjustLoc;
    if([self isLocationOutOfChina:wgsLoc]){
        adjustLoc = wgsLoc;
    }
    else{
        double adjustLat = [self transformLatWithX:wgsLoc.longitude - 105.0 withY:wgsLoc.latitude - 35.0];
        double adjustLon = [self transformLonWithX:wgsLoc.longitude - 105.0 withY:wgsLoc.latitude - 35.0];
        double radLat = wgsLoc.latitude / 180.0 * pi;
        double magic = sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = sqrt(magic);
        adjustLat = (adjustLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
        adjustLon = (adjustLon * 180.0) / (a / sqrtMagic * cos(radLat) * pi);
        adjustLoc.latitude = wgsLoc.latitude + adjustLat;
        adjustLoc.longitude = wgsLoc.longitude + adjustLon;
    }
    return adjustLoc;
    
}
 + (BOOL)isLocationOutOfChina:(CLLocationCoordinate2D)location
{
    if (location.longitude < 72.004 || location.longitude > 137.8347 || location.latitude < 0.8293 || location.latitude > 55.8271)
        return YES;
    return NO;
}

+ (double)transformLatWithX:(double)x withY:(double)y
{
    double lat = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(abs(x));
    lat += (20.0 * sin(6.0 * x * pi) + 20.0 *sin(2.0 * x * pi)) * 2.0 / 3.0;
    lat += (20.0 * sin(y * pi) + 40.0 * sin(y / 3.0 * pi)) * 2.0 / 3.0;
    // 320 写成 3320了
//    lat += (160.0 * sin(y / 12.0 * pi) + 3320 * sin(y * pi / 30.0)) * 2.0 / 3.0;
    lat += (160.0 * sin(y / 12.0 * pi) + 320 * sin(y * pi / 30.0)) * 2.0 / 3.0;
    return lat;
}

+ (double)transformLonWithX:(double)x withY:(double)y
{
    double lon = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(abs(x));
    lon += (20.0 * sin(6.0 * x * pi) + 20.0 * sin(2.0 * x * pi)) * 2.0 / 3.0;
    lon += (20.0 * sin(x * pi) + 40.0 * sin(x / 3.0 * pi)) * 2.0 / 3.0;
    lon += (150.0 * sin(x / 12.0 * pi) + 300.0 * sin(x / 30.0 * pi)) * 2.0 / 3.0;
    return lon;
}


@end

测试结果:

latitude longitude address
25.048367 102.701019 中国云南省昆明市五华区华山街道中和巷1号
25.063974 102.701012 中国云南省昆明市五华区莲华街道民院路
25.063915 102.701027 中国云南省昆明市五华区莲华街道民院路
25.063770 102.701141 中国云南省昆明市五华区莲华街道民院路13号
25.064100 102.701020 云南省昆明市五华区莲华街道教场中路社区
中国云南省昆明市五华区教场东路9号(在教场中路社区附近)

| 25.064100| 102.701020 | 云南省昆明市五华区莲华街道教场中路社区 |

推荐的解决方案:

  • 既然是在国内,存储一律用火星坐标,这样在使用国内地图显示时最方便(用百度地图显示时可以一次转换取得)
  • CLLocationManager 拿到的 CLLocation 转为火星坐标,MKMapView 不用处理
  • 使用地图 API 进行 地址解析/逆地址解析(Geocoding) 时注意相应使用相应地图商的坐标系
  • 部分地图商支持多个坐标系输入,如高德支持地球、火星坐标(这个一直有变动,具体只能参考厂商最新文档了)

你可能感兴趣的:(地球坐标转高德地图坐标)