iOS自带地图纠偏问题

…………纠偏 篇…………..

1. 涉及接口:<CoreLocation/CoreLocation.h>

2. 核心代码解读:

if ([CLLocationManager locationServicesEnabled]) {

[self.locationManager setDelegate:self];

[self.locationManager setDistanceFilter:200];

[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];

[self.locationManager startUpdatingLocation];

} // 开始定位

-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation;

//此代理方法可以实现新位置更新,即一定距离(200) 则更新一次当前位置,保持经纬度始终为最新

self.coordinate = [newLocation coordinate];

self.coordinate.longitude,self.coordinate.latitude  // 更新后的经纬度

TIPS:虽然上面方法中获取到了目前的经纬度,但是这个经纬度并不是真实的经纬度,而是火星经纬度,理所当然,下面就是考虑如何纠偏了,因为直接用这个经纬度去获取周边信息,基本不算定位,属于分身之术。

经过又一轮的查询相关资料和询问相关人士,居然小小纠偏却蕴藏很大商机,很多公司都有自己的一套纠偏方案,或者是购买第三方纠偏数据库,没有办法,只能再找,最后发现,我们的国产互联网巨头百度老大居然提供一个纠偏接口,真是感动啊,开动纠偏程序:

————————

NSString *rectificationURL=[NSString stringWithFormat:@"http://api.map.baidu.com/ag/coord/convert?from=0&to=2&x=%f&y=%f",self.coordinate.longitude,self.coordinate.latitude]; // 带上经纬度开始纠偏,对API有疑问,请自觉百度一下

纠偏结果如下:

{“error”:0,

“x”:”MTE2LjQzNTM1NDU0NjQ0″,

“y”:”MzkuOTEzNzAwOTAwNjA4″

} // 0 即为正确纠偏,后面的太不厚道了,居然用GTMBase64 加密,还好,费一番功夫下载Google 的”GTMBase64.h” 代码顺利解密,汗…

结果如下:

{

error: 0,

x: “MTE2LjQzNTM1NDU0NjQ0″,  116.43535454644

y: “MzkuOTEzNzAwOTAwNjA4″  39.913700900608

}

再次感谢百度,接下来当然是带上经度,带上纬度,开始取周边地理信息,关键代码如下:

NSString *url = [NSString stringWithFormat:@"https://maps.googleapis.com/maps/api/geocode/json?latlng=%f,%f&language=%@&sensor=false", aCoordinate.latitude, aCoordinate.longitude,NSLocalizedStringFromTable(@"signLanguage", @"sign", nil)]; //获取用户当前具体地理位置

NSString *url = [NSString stringWithFormat:@"https://maps.googleapis.com/maps/api/place/search/json?location=%f,%f&radius=%@&types=%@&sensor=true&key=%@&language=%@", aCoordinate.latitude, aCoordinate.longitude, [NSString stringWithFormat:@"%i", RADIUS], @”", kGOOGLE_API_KEY,NSLocalizedStringFromTable(@”signLanguage”, @”sign”, nil)]; //获取用户周边地理位置

Tips:若对上述API 有疑问,请自觉前往:https://developers.google.com/places/documentation/#Authentication  其中涉及的具体参数都有说明,请自动取舍,切记先申请 kGOOGLE_API_KEY.

 

注:此外,还有一些导入的bug在这里说明一下:

1.首先是下载了GTMBase64.h(GTMBase64.h下载地址),因为这个GTMBase64是MRC的,所以要设置一下,可以在Build Phases中的Compile Sources中需要设置兼容的文件后面加入编译标记-fno-objc-arc,这样就不会报MRC和ARC兼容的错了

2.这个下载的GTMBase64文件在设置兼容mrc后还是会报错:Implicit declaration of function 'CC_MD5' is invalid in C99所以还要设置一下,在GTMBase64.m文件import一个头文件:#import <CommonCrypto/CommonDigest.h>,至此这些坑都填好了

 

---------------------华丽的分割线--------------------------

 

 

其原理是这样的:保密局开发了一个系统,能将实际的坐标转换成虚拟的坐标。所有在中国销售的数字地图必须使用这个系统进行坐标转换之后方可上市。这是生产环节,这种电子地图被称为火星地图。在使用环节,GPS终端设备必须集成保密局提供的加密算法(集成工作由保密局完成),把从GPS卫星那里得到的坐标转换成虚拟坐标,然后再去火星地图上查找,这样就在火星坐标系上完成了地图的匹配。 所以大家所用的百度,高德等地图定位准是偏差几百米

名词总结:

地球坐标:指WGS84坐标系统

火星坐标:指使用国家保密插件人为偏移后的坐标
地球地图:指与地球坐标对应的客观真实的地图
火星地图:指经过加密偏移后的,与火星坐标对应的地图

坐标系转换算法

1.GCJ-02(火星坐标系)和BD-09转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// GCJ-02 坐标转换成 BD-09 坐标
+ (CLLocationCoordinate2D)MarsGS2BaiduGS:(CLLocationCoordinate2D)coordinate
{
    double x_pi = PI * 3000.0 / 180.0;
    double x = coordinate.longitude, y = coordinate.latitude;
    double z = sqrt(x * x + y * y) + 0.00002 * sin(y * x_pi);
    double theta = atan2(y, x) + 0.000003 * cos(x * x_pi);
    double bd_lon = z * cos(theta) + 0.0065;
    double bd_lat = z * sin(theta) + 0.006;
    return CLLocationCoordinate2DMake(bd_lat, bd_lon);
}
 
// BD-09 坐标转换成 GCJ-02 坐标
+ (CLLocationCoordinate2D)BaiduGS2MarsGS:(CLLocationCoordinate2D)coordinate
{
    double x_pi = PI * 3000.0 / 180.0;
    double x = coordinate.longitude - 0.0065, y = coordinate.latitude - 0.006;
    double z = sqrt(x * x + y * y) - 0.00002 * sin(y * x_pi);
    double theta = atan2(y, x) - 0.000003 * cos(x * x_pi);
    double gg_lon = z * cos(theta);
    double gg_lat = z * sin(theta);
    return CLLocationCoordinate2DMake(gg_lat, gg_lon);
}

2WGS-84(地球坐标系)和BD-09(百度坐标)转换

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// WGS-84 坐标转换成 BD-09 坐标
+ (CLLocationCoordinate2D)WorldGS2BaiduGS:(CLLocationCoordinate2D)coordinate
{
    CLLocationCoordinate2D mars = [ALDGeocoder WorldGS2MarsGS:coordinate];
    CLLocationCoordinate2D baidu = [ALDGeocoder MarsGS2BaiduGS:mars];
    return baidu;
}
 
// BD-09 坐标转换成 WGS-84 坐标
+ (CLLocationCoordinate2D)BaiduGS2WorldGS:(CLLocationCoordinate2D)coordinate
{
    CLLocationCoordinate2D mars = [ALDGeocoder BaiduGS2MarsGS:coordinate];
    CLLocationCoordinate2D world = [ALDGeocoder MarsGS2WorldGS:mars];
    return world;
}

3.WGS-84和sogou坐标转换

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
// WGS-84 坐标转换成 Sogou 坐标
+ (CLLocationCoordinate2D)WorldGS2SogouGS:(CLLocationCoordinate2D)coordinate
{
    const double ee = 0.082271854224939184;
    double lon = coordinate.longitude;
    double lat = coordinate.latitude;
    double dlon = [ALDGeocoder rad:CLIP(lon, -360, 360)];
    double dlat = [ALDGeocoder rad:CLIP(lat, -90, 90)];
    dlon = 6378206.4 * dlon;
    double sinphi = sin(dlat);
    double temp1, temp2;
    if((temp1 = 1.0 + sinphi) == 0.0){
        dlat = -1000000000;
    }else if((temp2 = 1.0 - sinphi) == 0.0){
        dlat = 1000000000;
    }else{
        double esinphi = ee * sinphi;
        dlat = 3189103.2000000002 * log((temp1 / temp2) * pow((1.0 - esinphi) / (1.0 + esinphi), ee));
    }
    return CLLocationCoordinate2DMake(dlat, dlon);
}
 
// Sogou 坐标转换成 WGS-84 坐标
+ (CLLocationCoordinate2D)SogouGS2WorldGS:(CLLocationCoordinate2D)coordinate
{
    const double ee = 1.5707963267948966;
    const double aa = 0.0033938814110493522;
    double lon = coordinate.longitude;
    double lat = coordinate.latitude;
    double dlon = lon / 6378206.4;
    double temp = -lat / 6378206.4;
    double chi;
    if(temp < -307){
        chi = ee;
    }else if(temp > 308){
        chi = -ee;
    }else{
        chi = ee - 2 * atan(exp(temp));
    }
    double chi2 = 2 * chi;
    double coschi2 = cos(chi2);
    double dlat = chi + sin(chi2) * (aa + coschi2 * (1.3437644537757259E-005 + coschi2 * (7.2964865099246009E-008 + coschi2 * 4.4551470401894685E-010)));
    double rlon = CLIP([ALDGeocoder deg:dlon], -360, 360);
    double rlat = CLIP([ALDGeocoder deg:dlat], -90, 90);
    return CLLocationCoordinate2DMake(rlat, rlon);
}

4火星坐标和地球坐标转换

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
// World Geodetic System ==> Mars Geodetic System
+ (CLLocationCoordinate2D)WorldGS2MarsGS:(CLLocationCoordinate2D)coordinate
{
    // a = 6378245.0, 1/f = 298.3
    // b = a * (1 - f)
    // ee = (a^2 - b^2) / a^2;
    const double a = 6378245.0;
    const double ee = 0.00669342162296594323;
     
    if (outOfChina(coordinate.latitude, coordinate.longitude))
    {
        return coordinate;
    }
    double wgLat = coordinate.latitude;
    double wgLon = coordinate.longitude;
    double dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
    double dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
    double radLat = wgLat / 180.0 * PI;
    double magic = sin(radLat);
    magic = 1 - ee * magic * magic;
    double sqrtMagic = sqrt(magic);
    dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI);
    dLon = (dLon * 180.0) / (a / sqrtMagic * cos(radLat) * PI);
     
    return CLLocationCoordinate2DMake(wgLat + dLat, wgLon + dLon);
}
 
// Mars Geodetic System ==> World Geodetic System
+ (CLLocationCoordinate2D)MarsGS2WorldGS:(CLLocationCoordinate2D)coordinate
{
    double gLat = coordinate.latitude;
    double gLon = coordinate.longitude;
    CLLocationCoordinate2D marsCoor = [ALDGeocoder WorldGS2MarsGS:coordinate];
    double dLat = marsCoor.latitude - gLat;
    double dLon = marsCoor.longitude - gLon;
    return CLLocationCoordinate2DMake(gLat - dLat, gLon - dLon);
}

 

5WGS-84 和 墨卡托 坐标转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//WGS-84 坐标转换成 墨卡托 坐标
+ (CLLocationCoordinate2D)WorldGS2Mercator:(CLLocationCoordinate2D)coordinate
{
    double lon = coordinate.longitude*20037508.34/180;
    double lat = log(tan((90+coordinate.latitude)*M_PI/360))/(M_PI/180);
    lat = lat*20037508.34/180;
    return CLLocationCoordinate2DMake(lat, lon);
}
 
//墨卡托 坐标转换成 WGS-84 坐标
+ (CLLocationCoordinate2D)Mercator2WorldGS:(CLLocationCoordinate2D)mercator
{
    double lon = mercator.longitude/20037508.34*180;
    double lat = mercator.latitude/20037508.34*180;
    lat = 180/M_PI*(2*atan(exp(lat*M_PI/180))-M_PI/2);
    return CLLocationCoordinate2DMake(lat, lon);
}

 

开发时所面临的现状

获取经纬度(GPS)

  • 火星坐标

    • MKMapView

  • 地球坐标

    • CLLocationManager

显示经纬度(地图)

  • 火星坐标

    • iOS 地图

    • Gogole地图

    • 搜搜、阿里云、高德地图

  • 地球坐标

    • Google 卫星地图(国外地图应该都是……)

  • 百度坐标

    • 百度地图

推荐的解决方案:

    • 既然是在国内,存储一律用火星坐标,这样在使用国内地图显示时最方便(用百度地图显示时可以一次转换取得)

    • CLLocationManager 拿到的 CLLocation 转为火星坐标,MKMapView 不用处理

    • 使用地图 API 进行 地址解析/逆地址解析(Geocoding) 时注意相应使用相应地图商的坐标系

    • 部分地图商支持多个坐标系输入,如高德支持地球、火星坐标(这个一直有变动,具体只能参考厂商最新文档了

 

引用地址:iOS地图定位纠偏1

     iOS地图定位纠偏2

     iOS地图火星坐标转换

     iOS 火星坐标相关整理及解决方案汇总

              WGS84、Web墨卡托、火星坐标、百度坐标互转

              WGS84坐标转火星坐标(iOS篇)

              WGS84坐标转火星坐标(iOS篇)(原文)

              iOS开发系列--地图与定位

              iOS 高德地图(继承即可使用定位\反编码)

              iOS开发中的火星坐标系及各种坐标系转换算法

              iOS开发拓展篇—CoreLocation地理编码

posted on 2016-03-02 11:54  On1Key  阅读(1808)  评论(0编辑  收藏  举报

导航