為什麼要糾偏?
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; }
文章轉自 傅邑的簡書