(文章末尾可下載源碼)
目前在試著做一個旅游類app項目,自己想加入一個功能:定位當前用戶位置务热,并能規(guī)劃出到旅游目的地的不同類型的線路及展示,界面參照拉鉤網(wǎng)app工作地址詳情頁己儒,采用高德地圖的接口崎岂,琢磨下高德地圖的demo,實現(xiàn)起來挺容易闪湾,直接上圖:
1.集成高德地圖
首先要集成高德地圖冲甘,需要在高德地圖官網(wǎng)申請高德地圖的開發(fā)者賬號,然后導(dǎo)入其第三方庫等步驟途样,可以參考http://www.reibang.com/p/bc9462f9c1e9江醇。
2.顯示用戶位置及目的地展示
通過第一步,地圖應(yīng)該能顯示出來了(高興)何暇,接下來就是在地圖上顯示出我們需要的信息陶夜,首先是目的地展示(這里我采用大頭針來顯示目的地),以及當前定位當前用戶裆站,使用高德地圖的第三方庫也都幾句代碼的事条辟,官方文檔也有說明這里也不多說直接上代碼:
地圖初始化完成后的方法中
_mapView.centerCoordinate=CLLocationCoordinate2DMake(self.ampDistancePoint.latitude,self.ampDistancePoint.longitude);//定位中心點
_mapView.zoomLevel=16.5;//地圖縮放級別
_mapView.showsUserLocation = YES; //顯示定位藍點
在這里要說一下大頭針,大頭針也可以參考高德地圖中的點標記繪制來進行自定義宏胯,在viewDidAppear中
MAPointAnnotation *pointAnnotation = [[MAPointAnnotation alloc] init];
pointAnnotation.coordinate = CLLocationCoordinate2DMake(self.ampDistancePoint.latitude,self.ampDistancePoint.longitude);
pointAnnotation.title = self.model.name; //大頭針
[_mapView addAnnotation:pointAnnotation];
然后 實現(xiàn) <MAMapViewDelegate> 協(xié)議中的 mapView:viewForAnnotation:回調(diào)函數(shù)羽嫡,設(shè)置標注樣式
- (MAAnnotationView *)mapView:(MAMapView *)mapView viewForAnnotation:(id <MAAnnotation>)annotation
{
if ([annotation isKindOfClass:[MAPointAnnotation class]])
{
static NSString *pointReuseIndentifier = @"pointReuseIndentifier";
MAPinAnnotationView*annotationView = (MAPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:pointReuseIndentifier];
if (annotationView == nil)
{
annotationView = [[MAPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pointReuseIndentifier];
}
annotationView.canShowCallout= YES; //設(shè)置氣泡可以彈出,默認為NO
annotationView.animatesDrop = YES; //設(shè)置標注動畫顯示肩袍,默認為NO
annotationView.draggable = YES; //設(shè)置標注可以拖動厂僧,默認為NO
annotationView.pinColor = MAPinAnnotationColorPurple;
return annotationView;
}
return nil;
}
3.不同類型路線規(guī)劃
關(guān)于路線規(guī)劃,主要參照了官方demo了牛,使用AMapSearchAPI類,發(fā)起路線規(guī)劃請求颜屠,駕車和步行的路線規(guī)劃區(qū)別不大,公共交通出行路線倒是有點不同鹰祸。
3.1駕車路線規(guī)劃發(fā)起
在這里我自定義了一個路線類型屬性甫窟,1為駕車,2為公共交通蛙婴,3為步行粗井。這樣做的用意,大家后面就知道了。
-(void)driveNav{
routeType=1;//自定義的路線類型浇衬,1為駕車懒构,2為公共交通,3為步行
AMapDrivingRouteSearchRequest *navi = [[AMapDrivingRouteSearchRequest alloc] init];
navi.requireExtension = YES;//是否返回擴展信息耘擂,默認為 NO
navi.strategy = 5;// 駕車導(dǎo)航策略([default = 0]) 0-速度優(yōu)先(時間)胆剧;1-費用優(yōu)先(不走收費路段的最快道路);2-距離優(yōu)先醉冤;3-不走快速路秩霍;4-結(jié)合實時交通(躲避擁堵);5-多策略(同時使用速度優(yōu)先蚁阳、費用優(yōu)先铃绒、距離優(yōu)先三個策略);6-不走高速螺捐;7-不走高速且避免收費颠悬;8-躲避收費和擁堵;9-不走高速且躲避收費和擁堵
/* 出發(fā)點. */
navi.origin = [AMapGeoPoint locationWithLatitude:_mapView.userLocation.location.coordinate.latitude
longitude:_mapView.userLocation.location.coordinate.longitude];
/* 目的地. */
navi.destination =self.ampDistancePoint;
[self.search AMapDrivingRouteSearch:navi];//駕車路線規(guī)劃
}
3.2公共交通路線規(guī)劃發(fā)起
公共交通不一樣的點主要是多了一個city屬性定血,必須要讓高德地圖知道是哪個城市的公共交通路線椿疗。
-(void)busNav{
routeType=2;
AMapTransitRouteSearchRequest *navi = [[AMapTransitRouteSearchRequest alloc] init];
navi.requireExtension = YES;
navi.strategy = 4;//公交換乘策略([default = 0])0-最快捷模式;1-最經(jīng)濟模式糠悼;2-最少換乘模式;3-最少步行模式浅乔;4-最舒適模式倔喂;5-不乘地鐵模式
navi.city =@"chongqing";
/* 出發(fā)點. */
navi.origin = [AMapGeoPoint locationWithLatitude:_mapView.userLocation.location.coordinate.latitude
longitude:_mapView.userLocation.location.coordinate.longitude];
/* 目的地. */
navi.destination =self.ampDistancePoint;
[self.search AMapTransitRouteSearch:navi];//公共交通路線規(guī)
}
3.3步行路線規(guī)劃發(fā)起
步行路線和駕車大致相同
-(void)WalkNav{
routeType=3;
AMapWalkingRouteSearchRequest *navi = [[AMapWalkingRouteSearchRequest alloc] init];
/* 出發(fā)點. */
navi.origin = [AMapGeoPoint locationWithLatitude:_mapView.userLocation.location.coordinate.latitude
longitude:_mapView.userLocation.location.coordinate.longitude];
/* 目的地. */
navi.destination = self.ampDistancePoint;
[self.search AMapWalkingRouteSearch:navi];
}
4路徑規(guī)劃搜索回調(diào)
通過AMapSearchAPI類向服務(wù)器發(fā)起路線規(guī)劃請求后,如規(guī)劃成功則跳到onRouteSearchDone方法,response靖苇,包含類路線的所有信息耗時席噩,距離,換乘等等贤壁,否則跳轉(zhuǎn)到- (void)AMapSearchRequest:(id)request didFailWithError:(NSError *)error方法中悼枢,并附帶出錯原因。
在這里我使用了預(yù)加載的模式脾拆,在用戶跳轉(zhuǎn)到本界面時馒索,就自動發(fā)起多有類型路線的規(guī)劃,等所有路線類型都規(guī)劃完畢后名船,用戶才能查看各種類型的導(dǎo)航路線绰上。預(yù)加載可以讓用戶對三種路線規(guī)劃耗時一目了然,增強用戶體驗渠驼。
在預(yù)加載這里有一個問題蜈块,如果同時發(fā)起三種類型的路線規(guī)劃請求,將會收到三次回調(diào),但是由于異步的關(guān)系百揭,就不能知道哪次回調(diào)對應(yīng)著的是哪種類型的路線規(guī)劃請求爽哎。
所以在這里我用了一個笨方法,先發(fā)起步行路線規(guī)劃器一,等收到回調(diào)后再發(fā)起公共交通路線規(guī)劃课锌,用routeType屬性可以知道當前是哪種類型請求的回調(diào),再發(fā)起駕車路線規(guī)劃盹舞,等駕車路線規(guī)劃收到回調(diào)产镐,那么所有路線加載完畢,則隱藏小菊花踢步,用戶也就能查看到各種類型的導(dǎo)航路線了癣亚。
/* 路徑規(guī)劃搜索回調(diào). */
- (void) onRouteSearchDone:(AMapRouteSearchBaseRequest *)request response:(AMapRouteSearchResponse *)response
{
if (response.route == nil)
{
return;
}
/* 預(yù)加載*/
if (routeType==1) {
self.driveRoute=response.route;
self.driveDration.text=[self timeFomart: response.route.paths[0].duration];//預(yù)計用時
[MBProgressHUD hideHUDForView:self.navView animated:YES];//隱藏菊花
self.distance.text =[self disFormat:self.driveRoute.paths[0].distance];//顯示距離
[self isHideNavView:false];//顯示控件
}
if (routeType==2) {
self.busRoute=response.route;
if (response.route.transits!=nil && response.route.transits.count!=0) {
if(response.route.transits.lastObject!=nil){
self.busDration.text=[self timeFomart:response.route.transits[0].duration];
}
}else{
self.busDration.text=@"暫無";
self.busBtn.enabled=false;
}
[self driveNav];
}
if (routeType==3) {
self.walkRoute=response.route;
if (response.route.paths!=nil) {
self.walkDration.text=[self timeFomart: response.route.paths[0].duration];
}
else{
self.walkDration.text=@"暫無";
self.walkBtn.enabled=false;
}
[self busNav];
}
self.route = response.route;
self.currentCourse = 0;
}
如若出錯,應(yīng)該就是網(wǎng)絡(luò)不好訪問超時获印,要么就是超過距離述雾,如超過了市區(qū)的公交線路導(dǎo)航,或其他原因系統(tǒng)無法規(guī)劃出路線兼丰,那么則在出錯處理中根據(jù)不同的類型進行錯誤的處理玻孟,并在此發(fā)起下一個類型的線路規(guī)劃請求,在這里為了方便鳍征,出錯的類型直接顯示暫無= =黍翎。
//出錯處理
- (void)AMapSearchRequest:(id)request didFailWithError:(NSError *)error
{
if (routeType==1) {
self.driveDration.text=@"暫無";
self.carBtn.enabled=false;
}
if (routeType==2) {
self.busDration.text=@"暫無";
self.busBtn.enabled=false;
[self driveNav];
}
if (routeType==3) {
self.walkDration.text=@"暫無";
self.walkBtn.enabled=false;
[self busNav];
}
5解析路線及路線的繪制
路線的解析以及路線的繪制,如用戶查看駕車路線時艳丛,直接解析已預(yù)加載好的路線規(guī)劃匣掸,然后再進行路線的繪制即可,在這里我直接是用的官方demo里的代碼氮双,僅僅根據(jù)自己的需求及界面的美觀小小的修改了一下碰酝。
路線的解析,注意有些類是demo中封裝好的戴差,非官方庫中的送爸。
注意:要先清楚地圖上的所有繪制
//清理繪制路線
- (void)clear
{
[self.naviRoute removeFromMapView];
}
點擊駕車路線規(guī)劃圖標:
- (IBAction)drivingBtn:(id)sender {
[self clear];
routeType=1;
self.distance.text =[self disFormat:self.driveRoute.paths[0].distance];
[self presentCurrentCourse];//路線解析
}
//根據(jù)不同導(dǎo)航類型解析不同路線
- (void)presentCurrentCourse
{
MANaviAnnotationType type = MANaviAnnotationTypeDrive;
if (routeType==1) {
type = MANaviAnnotationTypeDrive;
self.naviRoute = [MANaviRoute naviRouteForPath:self.driveRoute.paths[self.currentCourse] withNaviType:type showTraffic:YES startPoint:[AMapGeoPoint locationWithLatitude:_mapView.userLocation.location.coordinate.latitude longitude:_mapView.userLocation.location.coordinate.longitude]
endPoint:self.ampDistancePoint];
[self.naviRoute addToMapView:self.mapView];
}
if (routeType==2) {
self.naviRoute = [MANaviRoute naviRouteForTransit:self.busRoute.transits[self.currentCourse] startPoint:[AMapGeoPoint locationWithLatitude:_mapView.userLocation.location.coordinate.latitude longitude:_mapView.userLocation.location.coordinate.longitude]
endPoint:self.ampDistancePoint];
[self.naviRoute addToMapView:self.mapView];
}
if (routeType==3) {
type = MANaviAnnotationTypeWalking;
self.naviRoute = [MANaviRoute naviRouteForPath:self.walkRoute.paths[self.currentCourse] withNaviType:type showTraffic:YES startPoint:[AMapGeoPoint locationWithLatitude:_mapView.userLocation.location.coordinate.latitude longitude:_mapView.userLocation.location.coordinate.longitude]
endPoint:self.ampDistancePoint];
[self.naviRoute addToMapView:self.mapView];
}
/* 縮放地圖使其適應(yīng)polylines的展示. */
self.naviRoute.anntationVisible=YES;
[self.mapView showOverlays:self.naviRoute.routePolylines edgePadding:UIEdgeInsetsMake(RoutePlanningPaddingEdge, RoutePlanningPaddingEdge, RoutePlanningPaddingEdge, RoutePlanningPaddingEdge) animated:YES];
}
路線的繪制直接照搬官方demo中的(手動斜眼)
注意:地圖的繪制分兩個部分,路線的繪制以及路線節(jié)點(Annotation)的繪制
路線的繪制
//根據(jù)不同導(dǎo)航類型繪制路線
- (MAOverlayRenderer *)mapView:(MAMapView *)mapView rendererForOverlay:(id <MAOverlay>)overlay// 任何遵循此協(xié)議的對象
{
if ([overlay isKindOfClass:[LineDashPolyline class]])
{
MAPolylineRenderer *polylineRenderer = [[MAPolylineRenderer alloc] initWithPolyline:((LineDashPolyline *)overlay).polyline];
polylineRenderer.lineWidth = 8;
polylineRenderer.lineDashPattern = @[@10, @15];
polylineRenderer.strokeColor = [UIColor redColor];
return polylineRenderer;
}
if ([overlay isKindOfClass:[MANaviPolyline class]])
{
MANaviPolyline *naviPolyline = (MANaviPolyline *)overlay;
MAPolylineRenderer *polylineRenderer = [[MAPolylineRenderer alloc] initWithPolyline:naviPolyline.polyline];
polylineRenderer.lineWidth = 8;
if (naviPolyline.type == MANaviAnnotationTypeWalking)
{
polylineRenderer.strokeColor = self.naviRoute.walkingColor;
}
else if (naviPolyline.type == MANaviAnnotationTypeRailway)
{
polylineRenderer.strokeColor = self.naviRoute.railwayColor;
}
else
{
polylineRenderer.strokeColor = self.naviRoute.routeColor;
}
return polylineRenderer;
}
if ([overlay isKindOfClass:[MAMultiPolyline class]])
{
MAMultiColoredPolylineRenderer * polylineRenderer = [[MAMultiColoredPolylineRenderer alloc] initWithMultiPolyline:(MAMultiPolyline *)overlay];
polylineRenderer.lineWidth = 8;
polylineRenderer.strokeColors = [self.naviRoute.multiPolylineColors copy];
polylineRenderer.gradient = YES;
return polylineRenderer;
}
return nil;
}
路線節(jié)點的繪制
//根據(jù)導(dǎo)航類型繪制覆蓋物
- (MAAnnotationView *)mapView:(MAMapView *)mapView viewForAnnotation:(id <MAAnnotation>)annotation
{
if ([annotation isKindOfClass:[MAPointAnnotation class]])
{
static NSString *routePlanningCellIdentifier = @"RoutePlanningCellIdentifier";
MAAnnotationView *poiAnnotationView = (MAAnnotationView*)[self.mapView dequeueReusableAnnotationViewWithIdentifier:routePlanningCellIdentifier];
if (poiAnnotationView == nil)
{
poiAnnotationView = [[MAAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:routePlanningCellIdentifier];
}
poiAnnotationView.canShowCallout = YES;
poiAnnotationView.image = nil;
if ([annotation isKindOfClass:[MANaviAnnotation class]])
{
switch (((MANaviAnnotation*)annotation).type)
{
case MANaviAnnotationTypeRailway:
poiAnnotationView.image = [UIImage imageNamed:@"railway_station"];
break;
case MANaviAnnotationTypeBus:
poiAnnotationView.image = [UIImage imageNamed:@"bus"];
break;
case MANaviAnnotationTypeDrive:
poiAnnotationView.image = [UIImage imageNamed:@"car"];
break;
case MANaviAnnotationTypeWalking:
poiAnnotationView.image = [UIImage imageNamed:@"man"];
break;
default:
break;
}
}
else
{
/* 起點. */ //繪制起點的紅色大頭針在這里哦
if ([[annotation title] isEqualToString:self.model.name])
{
static NSString *pointReuseIndentifier = @"pointReuseIndentifier";
MAPinAnnotationView *annotationView = (MAPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:pointReuseIndentifier];
if (annotationView == nil)
{
annotationView = [[MAPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pointReuseIndentifier];
}
annotationView.canShowCallout= YES; //設(shè)置氣泡可以彈出暖释,默認為NO
annotationView.animatesDrop = YES; //設(shè)置標注動畫顯示袭厂,默認為NO
annotationView.draggable = YES; //設(shè)置標注可以拖動,默認為NO
annotationView.pinColor = MAPinAnnotationColorRed;
return annotationView;
}
}
return poiAnnotationView;
}
return nil;
}
6其他
還有些邊角代碼:如時間格式轉(zhuǎn)換球匕,距離格式轉(zhuǎn)換嵌器,string類型的經(jīng)緯度轉(zhuǎn)換等也一并獻上吧,
-(AMapGeoPoint *)pointFormat:(NSString *)point{
//經(jīng)緯度格式轉(zhuǎn)換
NSArray *arry= [point componentsSeparatedByString:@","];
double p2=((NSString *)arry[0]).doubleValue;
double p1=((NSString *)arry[1]).doubleValue;
return [AMapGeoPoint locationWithLatitude:CLLocationCoordinate2DMake(p1,p2).latitude
longitude:CLLocationCoordinate2DMake(p1,p2).longitude];
}
//距離格式轉(zhuǎn)換
-(NSString *)disFormat:(double)meters {
double intDistance=(int)round(meters);
return [NSString stringWithFormat:@"距離:%0.2fKM",intDistance/1000 ];
}
//時間格式轉(zhuǎn)換
-(NSString *)timeFomart:(double)duration{
return [NSString stringWithFormat:@"%0.0f分鐘",duration/60];
}
//笨方法谐丢,隱藏加載中的控件
-(void)isHideNavView:(BOOL) ishide{
self.busBtn.hidden=ishide;
self.busDration.hidden=ishide;
self.driveDration.hidden=ishide;
self.carBtn.hidden=ishide;
self.walkDration.hidden=ishide;
self.walkBtn.hidden=ishide;
}
項目demo開源在github歡迎Star(害羞#)
注:需手動導(dǎo)入AmpFrameworks
https://github.com/calvinWen/AmpDemo