CLLocationManager定位坐标不准确问题以及WGS_84转GCJ_02坐标位置纠错的方法

作者 john 日期 2016-11-29 阅读量
CLLocationManager定位坐标不准确问题以及WGS_84转GCJ_02坐标位置纠错的方法

背景:

最近用高德的一个基于web的URI地图路径规划及导航模块,以实现根据起始坐标 实现路径规划,见此处

起点是当前位置,由于没有集成高德API,所以用系统的CLLocationManager实现定位。

问题:

但是实际上,CLLocationManager定位的坐标,在高德地图上标注的位置与实际地点有偏差,并且较大!以前知道不同的地图坐标不能直接通用,但是如我所知,Apple的地图也是基于高德的,为什么CLLocationManager定位出的不准确,而MKMapView的定位却是准确的呢?

原因:

是这样的,按照国家统一的保密要求,任何一个地图产品都不允许使用GPS坐标,国内地图使用的坐标系统是GCJ-02GCJ-02,国测局02年发布的坐标体系。又称“火星坐标”。在中国,必须至少使用GCJ-02的坐标体系。比如谷歌腾讯高德都在用这个坐标体系。
国内其他坐标体系。一般都是由GCJ-02进过偏移算法得到的。这种体系就根据每个公司的不同,坐标体系都不一样了。比如,百度和搜狗就使用自己的坐标体系,与其他坐标体系不兼容。百度(BD_09坐标)
到这里大家可能都猜出了CLLocationManager有偏差,是因为采用的是WGS-84,也就是GPS原始坐标;
Apple的MKMapView框架准确,是因为 老乔想进中国,就得按规矩来咯,所以iOS中的地图理所应当将WGS-84坐标转成了国内GCJ-02坐标;

解决:

思路一:

使用现有API转换,将CLLocationManager的WGS_84坐标转成GCJ_02坐标,调用apple的私有模块类MKLocationManager中得方法来对经纬度做一个偏移修正

问题:但是如此使用会审核被拒,而且iOS 5之后,这个方法已经不再使用。

思路二:

既然MKMapView地图中的定位是准确的,那么用[self.mapView setShowsUserLocation:YES]得了

1
2
3
4
-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
CLLocationCoordinate2D coord = [userLocation coordinate];
NSLog(@"经度:%f,纬度:%f",coord.latitude,coord.longitude);
}

不足:需要将mapView加到父视图上,不然不会调用上面的代理方法,当然了,可以设置为hidden=YES,也是个办法

思路三:

使用算法,对得到的WGS_84坐标处理,得到GCJ_02坐标

听闻是为了国家安全搞的加密措施,使用的是非线性的偏移值,想得到真实的数据,得同GCJ申请,交钱,才能得到解密方法。不过高手在民间啊,被人破解了,具体算法分析可以看这篇文章

这个算法网上是有的,具体出自谁不清楚,还是要感谢并参考一下,下面贴出位置纠错算法的OC代码:新建一个继承NSObject的类


WGS84ConvertToGCJ02.h 文件:
1
2
3
4
5
6
7
8
9
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
@interface WGS84ConvertToGCJ02ForAMapView : NSObject
//判断是否已经超出中国范围
+(BOOL)isLocationOutOfChina:(CLLocationCoordinate2D)location;
//转GCJ-02
+(CLLocationCoordinate2D)transformFromWGSToGCJ:(CLLocationCoordinate2D)wgsLoc;
@end


WGS84ConvertToGCJ02.m文件:
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
#import "WGS84ConvertToGCJ02ForAMapView.h"
const double a = 6378245.0;
const double ee = 0.00669342162296594323;
@implementation WGS84ConvertToGCJ02ForAMapView
+(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 * M_PI;
double magic = sin(radLat);
magic = 1 - ee * magic * magic;
double sqrtMagic = sqrt(magic);
adjustLat = (adjustLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * M_PI);
adjustLon = (adjustLon * 180.0) / (a / sqrtMagic * cos(radLat) * M_PI);
adjustLoc.latitude = wgsLoc.latitude + adjustLat - 0.00039900; // 减去这个数字 完全是凑数,准确性有待验证
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(fabs(x));
lat += (20.0 * sin(6.0 * x * M_PI) + 20.0 *sin(2.0 * x * M_PI)) * 2.0 / 3.0;
lat += (20.0 * sin(y * M_PI) + 40.0 * sin(y / 3.0 * M_PI)) * 2.0 / 3.0;
lat += (160.0 * sin(y / 12.0 * M_PI) + 320 * sin(y * M_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(fabs(x));
lon += (20.0 * sin(6.0 * x * M_PI) + 20.0 * sin(2.0 * x * M_PI)) * 2.0 / 3.0;
lon += (20.0 * sin(x * M_PI) + 40.0 * sin(x / 3.0 * M_PI)) * 2.0 / 3.0;
lon += (150.0 * sin(x / 12.0 * M_PI) + 300.0 * sin(x / 30.0 * M_PI)) * 2.0 / 3.0;
return lon;
}
@end


以我的项目为例,在CLLocationManager的定位代理方法中这样使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#pragma mark -
#pragma mark CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations
{
CLLocation *newLocation = [locations firstObject];
//判断是不是属于国内范围
if (![WGS84ConvertToGCJ02ForAMapView isLocationOutOfChina:[newLocation coordinate]]) {
//转换后的coord
CLLocationCoordinate2D coord = [WGS84ConvertToGCJ02ForAMapView transformFromWGSToGCJ:[newLocation coordinate]];
newLocation = [[CLLocation alloc] initWithLatitude:coord.latitude longitude:coord.longitude];
}
if (_location) {
if (newLocation.horizontalAccuracy < _location.horizontalAccuracy) {
_location = newLocation;
[self postLocationNotificationOnMainThreadWithName:GPSManagerUpdateLocationNotification];
}
}
else{
_location = newLocation;
[self postLocationNotificationOnMainThreadWithName:GPSManagerUpdateLocationNotification];
}
}

不足:毕竟不是正统解密算法,还是会有偏差,大概10+m,但是精度已经很高了。

位置纠错算法的OC代码源文件

下载地址