[iOS 利用MapKit和CoreLocation框架打造精簡的定位和導航]


運行效果:

               

一.利用<CoreLocation/CoreLocation.h>定位

創建變量 CLLocationManager *locationManager ,並加入<CLLocationManagerDelegate>協議

以下是Viewdidload里需要初始化的參數:

    self.locationManager = [[CLLocationManager alloc]init];
    [self.locationManager setDelegate:self];
    [self.locationManager requestAlwaysAuthorization];
    [self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
    [self.locationManager setDistanceFilter:kCLDistanceFilterNone];
    if ([locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
        [locationManager requestWhenInUseAuthorization];
    }
    [self.locationManager startUpdatingLocation];

開始定位后,結果會用過這個函數回調,locations數組含有一切定位信息:時間,經緯度等

// Location Manager Delegate Methods
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    NSLog(@"%@", [locations lastObject]);
}

需要注意的是:在iOS8之后,使用地圖需要請求權限,需要在info.plist文件中加入2個字段:

NSLocationWhenInUseDescription

NSLocationAlwaysUsageDescription

並且在使用定位前需要運行函數:

 [self.locationManager requestAlwaysAuthorization];
或 [locationManager requestWhenInUseAuthorization];

二、 利用Mapkit使用地圖服務

創建變量 MKMapView *regionMapView ,並加入<MKMapViewDelegate>協議

 

初始化參數:

 

self.regionMapView.delegate = self;
self.regionMapView.showsUserLocation = YES;

 

關於地圖,有個大坑是目前各大地圖的坐標系都不一樣。

國際標准是WGS-84,比如谷歌、蘋果地圖提供的API等都是基於這個坐標的。

天朝為了“照顧大家安全”,立了個新標准GCJ-02,比如高德地圖就是基於它的。也就是所謂的火星坐標系。另外百度為了"更好地保護大家隱私",也有自己的二次加密后的標准BD-09. 

如果直接使用國外那些不安全的地圖提供的經緯度來插到我們天朝地圖上,很可能就存在很大偏移。所以作為地圖開發者我們首先要熟練運用6種坐標互相轉換算法。

國外《-》高德  高德《-》百度    百度《-》國外

所以上面利用CoreLocation定位出的經緯度顯示是有偏差的(定位出的是WGC-84,國內目前是高德地圖),要想正確顯示就要用算法轉換(WGC-84轉GCJ-02)。

或者有另外一個方法,利用MapKit的回調函數,得到的結果是經過轉換的。可見MapKit內部應該是已經調用了定位的。

#pragma mark MKMapViewDelegate -user location定位變化
-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation{
    _userlocation=userLocation;
    self.nowCoords = [userLocation coordinate];
    NSLog(@"定位到當前位置");
    _updateInt++;
    //放大地圖到自身的經緯度位置。
    self.userRegion = MKCoordinateRegionMakeWithDistance(self.nowCoords, 200, 200);
    if(_updateInt==1||_iffollowed==YES){
        [self.regionMapView setRegion:self.userRegion animated:NO];
    }
    //僅在打開地圖后,第一次更新地理信息時,確定使用者的大致地理位置
    if (_updateInt<=1) {
        //CLGeocoder 是谷歌接口通過經緯度查詢大致地址
        NSLog(@"通過經緯度查詢地理信息");
        CLGeocoder *geocoder = [[CLGeocoder alloc] init];
        [geocoder reverseGeocodeLocation:[userLocation location] completionHandler:^(NSArray *array, NSError *error) {
            if (array.count > 0) {
                CLPlacemark *placemark = [array objectAtIndex:0];
                _myregion=[placemark region];
                NSString *region = [placemark.addressDictionary objectForKey:@"SubLocality"];
                NSString *address = [placemark.addressDictionary objectForKey:@"Name"];
                self.regionStr = region;
                self.addressStr = address;
                self.city = placemark.locality;
                NSLog(@"當前使用者所在:地點名:%@,地址:%@,城市:%@",self.regionStr,self.addressStr,self.city);
            }else{
                self.regionStr = @"";
                self.addressStr = @"";
                self.city = @"";
                NSLog(@"未查詢到有效地址");
            }
        }];
    }
    //判斷是否是否要根據運動路線繪圖
    if (![[NSString stringWithFormat:@"%0.8f",[[userLocation location] coordinate].latitude] isEqualToString:[NSString stringWithFormat:@"%0.8f",self.centerCoordinate.latitude]] ) {
        
        //做點什么
        return;
    }
}

 

使用地圖主要有2個基本:(1)地理編碼:地址轉經緯度。

             (2)地理反編碼:經緯度轉成地址。

(1)地址轉經緯度的方法(具體功能為:輸入查找地點,然后顯示在地圖上

    方法有2個:一個是采用MK框架自帶的接口 CLGeocoder,該接口還有其他的用法,按住CMD+點擊CLGeocoder就會出現該類的接口。

CLGeocoder *geocoder = [[CLGeocoder alloc] init];
    [geocoder geocodeAddressString:@"屏峰" completionHandler:^(NSArray *placemarks, NSError *error) {
        if ([placemarks count] > 0 && error == nil){
            NSLog(@"Found %lu placemark(s).", (unsigned long)[placemarks count]);
            CLPlacemark *firstPlacemark = [placemarks objectAtIndex:0];
            NSLog(@"Longitude = %f", firstPlacemark.location.coordinate.longitude);
            NSLog(@"Latitude = %f", firstPlacemark.location.coordinate.latitude);
        }
        else if ([placemarks count] == 0 && error == nil){
            NSLog(@"Found no placemarks.");
        }
        else if (error != nil){
            NSLog(@"An error occurred = %@", error);
        }
    }];

  另一種就是使用百度或者其他地圖的接口,比如我的Demo中使用的就是百度接口,不方便的是,百度接口獲得的經緯度需要轉換才能正確的“插”在高德上。

      百度接口為:http://api.map.baidu.com/place/search?&query=西湖&region=杭州&output=json&key=bqApldE1oh6oBb98VYyIfy9S

      主要是4個參數。效果如下:

      

 

 (2)地理反編碼:經緯度轉成地址。

          方法同上,反過來也有接口。

    第一種是Mapkit接口:

CLGeocoder *geocoder = [[CLGeocoder alloc] init];
        [geocoder reverseGeocodeLocation:[userLocation location] completionHandler:^(NSArray *array, NSError *error) {
            if (array.count > 0) {
                CLPlacemark *placemark = [array objectAtIndex:0];
                _myregion=[placemark region];
                NSString *region = [placemark.addressDictionary objectForKey:@"SubLocality"];
                NSString *address = [placemark.addressDictionary objectForKey:@"Name"];
                self.regionStr = region;
                self.addressStr = address;
                self.city = placemark.locality;
                NSLog(@"當前使用者所在:地點名:%@,地址:%@,城市:%@",self.regionStr,self.addressStr,self.city);
            }else{
                self.regionStr = @"";
                self.addressStr = @"";
                self.city = @"";
                NSLog(@"未查詢到有效地址");
            }
        }];

   第二種是其他接口,同樣傳入參數是經緯度,返回地理信息。

   比如百度:http://api.map.baidu.com/geocoder/v2/?ak=E4805d16520de693a3fe707cdc962045&callback=renderReverse&location=39.983424,116.322987&output=json&pois=1

 效果:

 

三、地圖操作:

1.地圖依據經緯度插點:

主要利用Mapkit接口:(如果要發現某個類有哪些好玩的接口可以按住CMD+該類名進去看看有哪些函數,然后去搜搜這些函數的用法或看看官方文檔)

    [map addAnnotation:annotation];

后者,也就是那個棒棒糖屬於MKAnnotation類,這個類有很多自定義UI的例子,通過在上面加label,加button,可以擴展獲得很多功能。(比如顯示該地點的具體圖片文字信息等)

 

2.地圖畫線進行導航:

這些奇葩的功能都是由你所使用的地圖API提供的,我這里使用的Mapkit,所以我使用的就是Mapkit的畫線接口。

另外神奇的是,你可以通過[UIApplication sharedApplication]使用其他地圖來構造導航路線。

(1)把導航的繁重任務交給其他專業的地圖吧,這里只是大概代碼(具體參見我的DEMO):

-(void)doAcSheet{
    NSArray *appListArr = [CheckInstalledMapAPP checkHasOwnApp];
    NSString *sheetTitle = [NSString stringWithFormat:@"導航到 %@",[self.navDic objectForKey:@"address"]];
    UIActionSheet *sheet;
    if ([appListArr count] == 2) {
        sheet = [[UIActionSheet alloc] initWithTitle:sheetTitle delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:appListArr[0],appListArr[1], nil];
    }else if ([appListArr count] == 3){
        sheet = [[UIActionSheet alloc] initWithTitle:sheetTitle delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:appListArr[0],appListArr[1],appListArr[2], nil];
    }else if ([appListArr count] == 4){
        sheet = [[UIActionSheet alloc] initWithTitle:sheetTitle delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:appListArr[0],appListArr[1],appListArr[2],appListArr[3], nil];
    }else if ([appListArr count] == 5){
        sheet = [[UIActionSheet alloc] initWithTitle:sheetTitle delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:appListArr[0],appListArr[1],appListArr[2],appListArr[3],appListArr[4], nil];
    }
    sheet.actionSheetStyle = UIActionSheetStyleBlackOpaque;
    [sheet showInView:self.view];
}

-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
    NSString *btnTitle = [actionSheet buttonTitleAtIndex:buttonIndex];
    if (buttonIndex == 0) {
            CLLocationCoordinate2D to;
            to.latitude = _naviCoordsGd.latitude;
            to.longitude = _naviCoordsGd.longitude;
            MKMapItem *currentLocation = [MKMapItem mapItemForCurrentLocation];
            MKMapItem *toLocation = [[MKMapItem alloc] initWithPlacemark:[[MKPlacemark alloc] initWithCoordinate:to addressDictionary:nil]];
            
            toLocation.name = _addressStr;
            [MKMapItem openMapsWithItems:[NSArray arrayWithObjects:currentLocation, toLocation, nil] launchOptions:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:MKLaunchOptionsDirectionsModeDriving, [NSNumber numberWithBool:YES], nil] forKeys:[NSArray arrayWithObjects:MKLaunchOptionsDirectionsModeKey, MKLaunchOptionsShowsTrafficKey, nil]]];
    }
    if ([btnTitle isEqualToString:@"google地圖"]) {
        NSString *urlStr = [NSString stringWithFormat:@"comgooglemaps://?saddr=%.8f,%.8f&daddr=%.8f,%.8f&directionsmode=transit",self.nowCoords.latitude,self.nowCoords.longitude,self.naviCoordsGd.latitude,self.naviCoordsGd.longitude];
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlStr]];
    }else if ([btnTitle isEqualToString:@"高德地圖"]){
        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"iosamap://navi?sourceApplication=broker&backScheme=openbroker2&poiname=%@&poiid=BGVIS&lat=%.8f&lon=%.8f&dev=1&style=2",self.addressStr,self.naviCoordsGd.latitude,self.naviCoordsGd.longitude]];
        [[UIApplication sharedApplication] openURL:url];
        
    }else if ([btnTitle isEqualToString:@"百度地圖"]){
        double bdNowLat,bdNowLon;
        bd_encrypt(self.nowCoords.latitude, self.nowCoords.longitude, &bdNowLat, &bdNowLon);
        
        NSString *stringURL = [NSString stringWithFormat:@"baidumap://map/direction?origin=%.8f,%.8f&destination=%.8f,%.8f&&mode=driving",bdNowLat,bdNowLon,self.naviCoordsBd.latitude,self.naviCoordsBd.longitude];
        NSURL *url = [NSURL URLWithString:stringURL];
        [[UIApplication sharedApplication] openURL:url];
    }else if ([btnTitle isEqualToString:@"顯示路線"]){
        [self drawRout];
    }
}

(2)還是試試自己畫線好了O(∩_∩)O:(主要是提供兩個點的參數)

-(void)drawRout{
    MKPlacemark *fromPlacemark = [[MKPlacemark alloc] initWithCoordinate:_nowCoords addressDictionary:nil];
    MKPlacemark *toPlacemark   = [[MKPlacemark alloc] initWithCoordinate:_naviCoordsGd addressDictionary:nil];
    MKMapItem *fromItem = [[MKMapItem alloc] initWithPlacemark:fromPlacemark];
    MKMapItem *toItem   = [[MKMapItem alloc] initWithPlacemark:toPlacemark];
    
    [self.regionMapView removeOverlays:self.regionMapView.overlays];
    [self findDirectionsFrom:fromItem to:toItem];
    
}
#pragma mark - ios7路線繪制方法
-(void)findDirectionsFrom:(MKMapItem *)from to:(MKMapItem *)to{
    MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
    request.source = from;
    request.destination = to;
    request.transportType = MKDirectionsTransportTypeWalking;
    if (ISIOS7) {
        request.requestsAlternateRoutes = YES;
    }
    
    MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
    //ios7獲取繪制路線的路徑方法
    [directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
        if (error) {
            NSLog(@"error:%@", error);
        }
        else {
            MKRoute *route = response.routes[0];
            [self.regionMapView addOverlay:route.polyline];
        }
    }];
}
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView
            rendererForOverlay:(id<MKOverlay>)overlay{
    MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
    renderer.lineWidth = 5.0;
    renderer.strokeColor = [UIColor redColor];
    return renderer;
}

 

GithubDemo:https://github.com/rayshen/ShenMapViewDemo

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM