為什麼要糾偏?
GCJ-02是由中國國家測繪局制訂的地理信息系統的坐標系統。
它是一種對經緯度數據的加密算法,即加入隨機的偏差。
國內出版的各種地圖系統(包括電子形式),必須至少采用GCJ-02對地理位置進行首次加密。
簡而言之就是WGS-84坐標系是GPS定位獲得的真實經緯度,GCJ-02坐標系是中國因國防需要在WGS-84基礎上進行過加密的坐標系,兩者存在隨機偏差;由GPS獲得的經緯度並不能適用於國內的地圖。
糾偏方式?
其實就是利用iOS自帶的地圖定位獲得GCJ-02坐標系。測試過網上流傳的坐標轉換算法,結果該怎麼偏還是怎麼偏,換了個方位偏而已。
上代碼
首先要在info.plist文件中添加定位問詢
Key:
//允許App運行期間定位 Privacy - Location When In Use Usage Description //允許一直定位 Privacy - Location Always Usage Description
Value:
如果要適配iOS10,Value必須非空,否則會crash;只適配iOS8-9允許為空
地圖和定位要用到的庫
#import #import
設置代理
@interface LocationViewController ()
{
MKMapView *_mapView;//全局地圖對象
}
//定位服務的入口點,設置成屬性
@property(nonatomic,strong)CLLocationManager *locationManager;
@property(nonatomic,copy)NSString *latitude;//緯度
@property(nonatomic,copy)NSString *longitude;//經度- (void)viewDidLoad {
[super viewDidLoad];
//判斷是否開啟定位服務,GPS傳感器是否可用
if ([CLLocationManager locationServicesEnabled]) {//是否開啟定位服務
if ([CLLocationManager headingAvailable]) {//傳感器是否可用
self.locationManager=[[CLLocationManager alloc] init];
self.locationManager.delegate=self;
self.locationManager.desiredAccuracy=kCLLocationAccuracyBest;//最高精度
self.locationManager.headingFilter=kCLHeadingFilterNone;//設置濾波器不工作(過濾器用於過濾更新信號,默認為1,這裡我們使其不工作,即接受所有更新信號,達到最精准模式)
//iOS8以後加入了問詢用戶是否開啟定位權限,這裡需要判斷操作系統版本,如果要兼容iOS7及以下,需要自己判斷版本
if ([self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
[self.locationManager requestWhenInUseAuthorization];
}
else {
NSLog(@"版本不匹配");
}
}
else {
NSLog(@"傳感器不可用");
}
}
else {
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"未開啟定位服務"
message:@"請至設置-隱私-定位服務中開啟定位服務"
delegate:self
cancelButtonTitle:@"確定"
otherButtonTitles:nil, nil];
[alert setAlertViewStyle:UIAlertViewStyleDefault];
[alert show];
}
//初始化地圖,如果只想獲得坐標不要地圖,可以將frame設置在視圖外,初始化後隱藏
_mapView=[[MKMapView alloc] init];
_mapView.frame=CGRectMake(0, 0, self.view.frame.size.width, height/3);
_mapView.delegate=self;
_mapView.mapType=MKMapTypeStandard;//地圖標准模式
_mapView.showsUserLocation=YES;//顯示當前位置
_mapView.userTrackingMode=MKUserTrackingModeFollow;//跟隨
[self.view addSubview:_mapView];
// _mapView.hidden=YES;
}開始定位需要用調用startUpdatingLocation,結束定位需要調用stopUpdatingLocation,可以自己選擇何時開始/結束定位。
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated]; //開始定位
[self.locationManager startUpdatingLocation];
}
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated]; //停止定位
[self.locationManager stopUpdatingLocation];
}//默認點擊地圖當前位置的藍點只會顯示"當前位置"四個字,如果要顯示當前地址需要設置藍點的title,不想顯示地圖的可以不用實現這個代理方法
#pragma mark MKMapViewDelegate
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation{
//當前的坐標,反編碼
CLGeocoder *geo = [[CLGeocoder alloc] init];
[geo reverseGeocodeLocation:userLocation.location completionHandler:^(NSArray *placemarks, NSError *error) {
//取出標記
CLPlacemark *pm = [placemarks lastObject];
//賦值
userLocation.title = pm.name;
}];
}#pragma mark - CLLocationManagerDelegate
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
//獲取WGS-84坐標的對象,兩者選其一
CLLocation *location=[locations lastObject];
CLLocationCoordinate2D coords=location.coordinate;
//獲取GCJ-02坐標的對象,兩者選其一
CLLocationCoordinate2D coords=_mapView.userLocation.location.coordinate;
//獲得經緯度
NSLog(@"緯度%f,經度%f",coords.latitude,coords.longitude);
//經緯方向
NSString *latitudeDirection=nil;
NSString *longitudeDirection=nil;
if (coords.latitude>=0) {
latitudeDirection=@"N";
}
else if (coords.latitude=0) {
longitudeDirection=@"E";
}
else if (coords.longitude<0) {
longitudeDirection=@"W";
}
//經緯度拼接方向
self.latitude=[NSString stringWithFormat:@"%f%@",coords.latitude,latitudeDirection];//緯度
self.longitude=[NSString stringWithFormat:@"%f%@",coords.longitude,longitudeDirection];//經度
//這個代理方法會每秒執行一次,如果你只想定位成功一次就結束定位,需要判斷定位成功後在此處調用stopUpdatingLocation結束定位。
}
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
if (error.code==kCLErrorDenied) {
//提示出錯原因
}
}附:經緯度十進制(小數)轉六十進制(度分秒)的方法
#pragma mark - 經緯度單位轉換
- (NSString *)stringWithCoordinateString:(NSString *)coordinateString{
//示例:118.815033
/** 將經度或緯度整數部分提取出來 */
int latNumber = [coordinateString intValue];//118
/** 取出小數點後面兩位(為轉化成'分'做准備) */
NSArray *array = [coordinateString componentsSeparatedByString:@"."];
/** 小數點後面部分 */
NSString *minuteCompnetString = [array lastObject];
/** 拼接字字符串(將字符串轉化為0.xxxx形式) */
NSString *str1 = [NSString stringWithFormat:@"0.%@", minuteCompnetString];
/** 將字符串轉換成float類型以便計算 */
float minuteNum = [str1 floatValue]; //0.815033
/** 將小數點後數字轉化為'分'(minuteNum * 60) */
float minuteNum1 = minuteNum * 60; //0.815033*60=48.90198
/** 將轉化後的float類型轉化為字符串類型 */
NSString *latStr = [NSString stringWithFormat:@"%f", minuteNum1];
/** 取整數部分即為緯度或經度'分' */
int latMinute = [latStr intValue]; //48
//取秒
/** 取出小數點後面兩位(為轉化成'秒'做准備) */
NSArray *secondArr = [latStr componentsSeparatedByString:@"."];
/** 小數點後面部分 */
NSString *lastCompnetString = [secondArr lastObject];
/** 拼接字字符串(將字符串轉化為0.xxxx形式) */
NSString *str2 = [NSString stringWithFormat:@"0.%@", lastCompnetString];
/** 將字符串轉換成float類型以便計算 */
float secondNum = [str2 floatValue]; //0.90198
/** 將小數點後數字轉化為'分'(minuteNum * 60) */
float secondNum1 = secondNum * 60; //0.90198*60=54.1188
/** 將轉化後的float類型轉化為字符串類型 */
NSString *latStr2 = [NSString stringWithFormat:@"%f", secondNum1];
/** 取整數部分即為緯度或經度'分' */
int latSecond = [latStr2 intValue]; //54
/** 將經度或緯度字符串合並為(xx°xx')形式 */
NSString *string = [NSString stringWithFormat:@"%d°%d'%d''", latNumber, latMinute, latSecond];
return string;
}
文章轉自 傅邑的簡書