地圖與定位(四)導航劃線

本文我們介紹一下如何實現導航劃線。

1. 自定義導航劃線

地圖開發中,常常需要我們爲用途提供行進路線,在MapKit框架中提供了MKDirectionRequest對象用於計算路線,提供了MKDirections用於計算方向,這樣一來只需要調用MKMapView的addOverlay等方法添加覆蓋物即可實現類似的效果,下面我們來試一下。

下面是添加導航路線的具體步驟,當然在計算路線之前,我們需要對地理名稱做地理編碼獲取地標,用於初始化MKPlacemark:

  • 對地理名稱做地理編碼獲取地標
  • 初始化方向的請求對象
  • 設置方向的起點 –> 初始化地標對象MKPlacemark(類似於CLPlacemark,只是它在MapKit框架中,可以根據CLPlacemark創建MKPlacemark)。
  • 設置方向請求的終點
  • 通過方向的請求對象獲得導航方向
  • 計算路徑
  • 添加路線覆蓋,必須實現代理方法

示例代碼:

#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#import "Annotation.h"

@interface ViewController ()<CLLocationManagerDelegate,MKMapViewDelegate>

@property (nonatomic, strong)CLLocationManager *locationManager;// 定位管家
@property (nonatomic, strong)CLGeocoder *geocoder;// 地理編碼器
@property (nonatomic, strong)MKMapView *mapView;

@end

@implementation ViewController

- (CLLocationManager *)locationManager {

    if (!_locationManager) {

        _locationManager = [[CLLocationManager alloc] init];
        _locationManager.delegate = self;
    }
    return _locationManager;
}
- (CLGeocoder *)geocoder {

    if (!_geocoder) {
        _geocoder = [[CLGeocoder alloc] init];
    }
    return _geocoder;
}
- (MKMapView *)mapView {

    if (!_mapView) {

        _mapView = [[MKMapView alloc] initWithFrame:self.view.bounds];
        _mapView.delegate = self;
        _mapView.mapType =  MKMapTypeStandard;
        _mapView.userTrackingMode = MKUserTrackingModeFollow;
    }
    return _mapView;
}
- (void)viewDidLoad {
    [super viewDidLoad];

    [self.view addSubview:self.mapView];

    //1.判斷手機定位服務是否打開
    if (![CLLocationManager locationServicesEnabled]) {
        NSLog(@"手機定位服務沒有打開");
        return;
    }

    //2.iOS8.0以上的用戶需要授權
    if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
        if ([[[UIDevice currentDevice]  systemVersion] floatValue] >= 8.0) {
            //調用此方法之前必須在plist文件中添加NSLocationWhenInUseUsageDescription --string-- 後面跟的文字就是提示信息
            [self.locationManager requestWhenInUseAuthorization];
        }
    }

    [self getSoucePlaceMark:@"北京" andDestinationMark:@"濟南"];
}

// 獲取地理位置在地圖上的地標
- (void)getSoucePlaceMark:(NSString *)soucePlace andDestinationMark:(NSString *)destinationPlace {

    // 由地理位置通過地理編碼獲取地標
    [self.geocoder geocodeAddressString:soucePlace completionHandler:^(NSArray *placemarks, NSError *error) {
        if (error) {
            return;
        }
        // 1.獲取起點的地標
        CLPlacemark *source = [placemarks firstObject];

        // 由於每次只能對一個地理位置做地理編碼,所以更多的地理編碼任務要嵌套在block中執行
        [self.geocoder geocodeAddressString:destinationPlace completionHandler:^(NSArray *placemarks, NSError *error) {

            if (error) {
                return;
            }
            // 2.獲取終點的地標
            CLPlacemark *destination = [placemarks firstObject];

            // 3.獲取起點和終點的地標後開始計算路線並做導航畫線
            [self addLineFromSource:source toDestination:destination];
        }];

        // 3.設置地圖顯示的區域
        CLLocationCoordinate2D center = source.location.coordinate;// 獲取中心點位置
        MKCoordinateSpan span = MKCoordinateSpanMake(4.193344, 3.069712);// 顯示跨度
        MKCoordinateRegion region = MKCoordinateRegionMake(center,  span);// 區域
        [self.mapView setRegion:region];
    }];
}

/**
 *  在sourcePm 和 desPm 之間添加線
 *
 *  @param source 起始位置
 *  @param destination    終點位置
 */
- (void)addLineFromSource:(CLPlacemark *)source toDestination:(CLPlacemark *)destination {

    // 1.初始化方向的請求對象
    MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];

    // 2.設置方向的起點 --> 初始化地標對象MKPlacemark
    // 根據地理座標初始化地圖座標MKPlacemark -->傳入地標CLPlacemark(地理編碼獲得)
    MKPlacemark *sourcePM = [[MKPlacemark alloc] initWithPlacemark:source];
    request.source = [[MKMapItem alloc] initWithPlacemark:sourcePM];

    // 3.設置方向請求的終點
    MKPlacemark *destinationPM = [[MKPlacemark alloc] initWithPlacemark:destination];
    request.destination = [[MKMapItem alloc] initWithPlacemark:destinationPM];

    // 4.通過方向的請求對象獲得導航方向
    MKDirections *directions = [[MKDirections alloc] initWithRequest:request];

    // 5.計算路徑
    [directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {

        NSLog(@"可能的路線條數:%ld",response.routes.count);
        for (MKRoute *route  in response.routes) {

            // 6.添加路線覆蓋,必須實現代理方法
            [self.mapView addOverlay:route.polyline];
        }
    }];

    // 添加兩個大頭針標記起點和重點
    Annotation *fromAnno = [[Annotation alloc] init];
    fromAnno.coordinate = source.location.coordinate;
    fromAnno.title = source.name;
    [self.mapView addAnnotation:fromAnno];

    Annotation *toAnno = [[Annotation alloc] init];
    toAnno.coordinate = destination.location.coordinate;
    toAnno.title = destination.name;
    [self.mapView addAnnotation:toAnno];

}

#pragma mapViewDelegate
/**
 *  方法說明:添加導航路徑時調用,必須複寫該方法後才能完成地圖畫線
 *
 *  @param overlay:路線
 *
 *  @return MKOverlayRenderer:路線視圖
 */
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
    // MKPolylineRenderer
    //->MKOverlayPathRenderer
    //->MKOverlayRenderer
    //初始化 導航線
    MKPolylineRenderer *render = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
    render.strokeColor = [UIColor greenColor];

    return render;
}
// 添加導航路線的時候調用
- (void)mapView:(MKMapView *)mapView didAddOverlayRenderers:(NSArray *)renderers {

    NSLog(@"+++++");
}

@end

2. 系統地圖應用

除了可以使用MapKit框架進行地圖開發,對地圖有精確的控制和自定義之外,如果對於應用沒有特殊要求的話選用蘋果自帶的地圖應用也是一個不錯的選擇。使用蘋果自帶的應用時需要用到MapKit中的MKMapItem類,這個類有一個openInMapsWithLaunchOptions:動態方法和一個openMapsWithItems: launchOptions:靜態方法用於打開蘋果地圖應用。第一個方法用於在地圖上標註一個位置,第二個方法除了可以標註多個位置外還可以進行多個位置之間的駕駛導航,使用起來也是相當方便。在熟悉這兩個方法使用之前有必要對兩個方法中的options參數做一下簡單說明:

常量 說明
MKLaunchOptionsDirectionsModeKey 路線模式,常量 MKLaunchOptionsDirectionsModeDriving 駕車模式MKLaunchOptionsDirectionsModeWalking 步行模式
MKLaunchOptionsMapTypeKey 地圖類型,枚舉 MKMapTypeStandard :標準模式MKMapTypeSatellite :衛星模式MKMapTypeHybrid :混合模式
MKLaunchOptionsMapCenterKey 中心點座標,CLLocationCoordinate2D類型
MKLaunchOptionsMapSpanKey 地圖顯示跨度,MKCoordinateSpan 類型
MKLaunchOptionsShowsTrafficKey 是否 顯示交通狀況,布爾型
MKLaunchOptionsCameraKey 3D地圖效果,MKMapCamera類型注意:此屬性從iOS7及以後可用,前面的屬性從iOS6開始可用

下面我們來演示一下如何在蘋果自帶地圖應用上標記一個位置,首先根據反地理編碼獲得一個CLPlacemark位置對象,然後將其轉換爲MKPlacemark對象用於MKMapItem初始化,最後調用其openInMapsWithLaunchOptions:打開地圖應用並標記:

示例代碼:

#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>

@interface ViewController ()

@property (nonatomic, strong)CLGeocoder *geocoder;

@end

@implementation ViewController

- (CLGeocoder *)geocoder {

    if (!_geocoder) {
        _geocoder = [[CLGeocoder alloc] init];
    }
    return _geocoder;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    [self locationAddress:@"濟南"];
}

- (void)locationAddress:(NSString *)address {

    [self.geocoder geocodeAddressString:address completionHandler:^(NSArray *placemarks, NSError *error) {
        if (error) return;

        // 1.取出地理編碼的目的地的地標CLPlacemark
        CLPlacemark *placemark = [placemarks firstObject];

        // 2.將定位地標轉化爲地圖的地標
        MKMapItem *toLocation = [[MKMapItem alloc] initWithPlacemark:[[MKPlacemark alloc] initWithPlacemark:placemark]];

        // 3.設置系統地圖應用的顯示模式
        NSMutableDictionary *options = [NSMutableDictionary dictionary];
        // 地圖樣式
        options[MKLaunchOptionsMapTypeKey] = @(MKMapTypeStandard);

        // 打開蘋果自帶地圖應用
        [toLocation openInMapsWithLaunchOptions:options];
    }];
}
@end

       如果要標記多個位置需要調用MKMapItem的靜態方法,下面的代碼演示中需要注意,使用CLGeocoder進行定位時一次只能定位到一個位置,所以第二個位置定位放到了第一個位置獲取成功之後。
- (void)locationAddressList {

    [self.geocoder geocodeAddressString:@"濟南" completionHandler:^(NSArray *placemarks, NSError *error) {
        if (error) return;

        CLPlacemark *clPlacemark = [placemarks firstObject];//獲取第一個地標
        // 將地理座標轉化成地圖座標
        MKPlacemark *mkPlacemark = [[MKPlacemark alloc]initWithPlacemark:clPlacemark];

        // 注意地理編碼一次只能定位到一個位置,不能同時定位,所在放到第一個位置定位完成回調函數中再次定位

        // 根據“北京市”進行地理編碼
        [self.geocoder geocodeAddressString:@"北京市" completionHandler:^(NSArray *placemarks, NSError *error) {
            if (error) return;

            CLPlacemark *clPlacemark1 = [placemarks firstObject];//獲取第一個地標
            MKPlacemark *mkPlacemark1 = [[MKPlacemark alloc]initWithPlacemark:clPlacemark1];

            // 根據“天津市”進行地理編碼
            [_geocoder geocodeAddressString:@"天津市" completionHandler:^(NSArray *placemarks, NSError *error) {

                if (error) return;

                CLPlacemark *clPlacemark2 = [placemarks firstObject];//獲取第一個地標
                MKPlacemark *mkPlacemark2 = [[MKPlacemark alloc]initWithPlacemark:clPlacemark2];

                // 設置地圖顯示屬性
                NSDictionary *options=@{MKLaunchOptionsMapTypeKey:@(MKMapTypeStandard)};

                // 獲取標記位置
                MKMapItem *currentItem = [MKMapItem mapItemForCurrentLocation];//當前位置
                MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:mkPlacemark];
                MKMapItem *mapItem1 = [[MKMapItem alloc]initWithPlacemark:mkPlacemark1];
                MKMapItem *mapItem2 = [[MKMapItem alloc]initWithPlacemark:mkPlacemark2];

                // 打開地圖並標記多個位置
                [MKMapItem openMapsWithItems:@[currentItem,mapItem,mapItem1,mapItem2] launchOptions:options];
            }];
        }];


    }];
}
       要使用地圖導航功能在自帶地圖應用中相當簡單,只要設置參數配置導航模式即可,例如在上面代碼基礎上設置駕駛模式,則地圖應用會啓動駕駛模式計算兩點之間的距離同時對路線進行規劃。
- (void)navagationToAddress:(NSString *)address {

    // 根據“天津市”進行地理編碼
    [self.geocoder geocodeAddressString:address completionHandler:^(NSArray *placemarks, NSError *error) {

        if (error) return;

        CLPlacemark *clPlacemark = [placemarks firstObject];//獲取第一個地標
        MKPlacemark *mkPlacemark = [[MKPlacemark alloc]initWithPlacemark:clPlacemark];

        // 設置地圖顯示屬性
        NSDictionary *options=@{MKLaunchOptionsMapTypeKey:@(MKMapTypeStandard),MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving};

        // 獲取標記位置
        MKMapItem *currentItem = [MKMapItem mapItemForCurrentLocation];// 當前位置
        MKMapItem *mapItem = [[MKMapItem alloc]initWithPlacemark:mkPlacemark];// 目標位置

        // 打開地圖並標記多個位置
        [MKMapItem openMapsWithItems:@[currentItem,mapItem] launchOptions:options];
    }];

}

總結:

由於定位和地圖框架中用到了諸多類,有些初學者容易混淆,下面簡單對比一下。

  • CLLocation:用於表示位置信息,包含地理座標、海拔等信息,包含在CoreLoaction框架中。
  • MKUserLocation:一個特殊的大頭針,表示用戶當前位置。
  • CLPlacemark:定位框架中地標類,封裝了詳細的地理信息。
  • MKPlacemark:類似於CLPlacemark,只是它在MapKit框架中,可以根據CLPlacemark創建MKPlacemark。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章