天天看點

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

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

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] >= ) {
            //調用此方法之前必須在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(, );// 顯示跨度
        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 *[email protected]{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 *[email protected]{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。

繼續閱讀