临时收藏iOS 开发每天分享优质文章

[IOS] 高德地图 不同类型 导航 线路规划及展示

2017-06-12  本文已影响440人  科文Calvin

(文章末尾可下载源码)

目前在试着做一个旅游类app项目,自己想加入一个功能:定位当前用户位置,并能规划出到旅游目的地的不同类型的线路及展示,界面参照拉钩网app工作地址详情页,采用高德地图的接口,琢磨下高德地图的demo,实现起来挺容易,直接上图:

2017-06-11 22.51.20.gif

1.集成高德地图

首先要集成高德地图,需要在高德地图官网申请高德地图的开发者账号,然后导入其第三方库等步骤,可以参考http://www.jianshu.com/p/bc9462f9c1e9

2.显示用户位置及目的地展示

通过第一步,地图应该能显示出来了(高兴),接下来就是在地图上显示出我们需要的信息,首先是目的地展示(这里我采用大头针来显示目的地),以及当前定位当前用户,使用高德地图的第三方库也都几句代码的事,官方文档也有说明这里也不多说直接上代码:
地图初始化完成后的方法中

_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];

然后 实现 <MAMapViewDelegate> 协议中的 mapView:viewForAnnotation:回调函数,设置标注样式

- (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;       //设置气泡可以弹出,默认为NO
        annotationView.animatesDrop = YES;        //设置标注动画显示,默认为NO
        annotationView.draggable = YES;        //设置标注可以拖动,默认为NO
        annotationView.pinColor = MAPinAnnotationColorPurple;
        return annotationView;
    }
    return nil;
}

3.不同类型路线规划

关于路线规划,主要参照了官方demo,使用AMapSearchAPI类,发起路线规划请求,驾车和步行的路线规划区别不大,公共交通出行路线倒是有点不同。

3.1驾车路线规划发起

在这里我自定义了一个路线类型属性,1为驾车,2为公共交通,3为步行。这样做的用意,大家后面就知道了。

-(void)driveNav{
   
   routeType=1;//自定义的路线类型,1为驾车,2为公共交通,3为步行
   AMapDrivingRouteSearchRequest *navi = [[AMapDrivingRouteSearchRequest alloc] init];
   navi.requireExtension = YES;//是否返回扩展信息,默认为 NO
   navi.strategy = 5;// 驾车导航策略([default = 0]) 0-速度优先(时间);1-费用优先(不走收费路段的最快道路);2-距离优先;3-不走快速路;4-结合实时交通(躲避拥堵);5-多策略(同时使用速度优先、费用优先、距离优先三个策略);6-不走高速;7-不走高速且避免收费;8-躲避收费和拥堵;9-不走高速且躲避收费和拥堵
   /* 出发点. */
   navi.origin = [AMapGeoPoint locationWithLatitude:_mapView.userLocation.location.coordinate.latitude
                                          longitude:_mapView.userLocation.location.coordinate.longitude];
   /* 目的地. */
   navi.destination =self.ampDistancePoint;
   [self.search AMapDrivingRouteSearch:navi];//驾车路线规划
}

3.2公共交通路线规划发起

公共交通不一样的点主要是多了一个city属性,必须要让高德地图知道是哪个城市的公共交通路线。

-(void)busNav{
    routeType=2;
    AMapTransitRouteSearchRequest *navi = [[AMapTransitRouteSearchRequest alloc] init];
    navi.requireExtension = YES;
    navi.strategy = 4;//公交换乘策略([default = 0])0-最快捷模式;1-最经济模式;2-最少换乘模式;3-最少步行模式;4-最舒适模式;5-不乘地铁模式
    navi.city =@"chongqing";
    /* 出发点. */
    navi.origin = [AMapGeoPoint locationWithLatitude:_mapView.userLocation.location.coordinate.latitude
                                           longitude:_mapView.userLocation.location.coordinate.longitude];
    /* 目的地. */
    navi.destination =self.ampDistancePoint;
    [self.search AMapTransitRouteSearch:navi];//公共交通路线规
}

3.3步行路线规划发起

步行路线和驾车大致相同

-(void)WalkNav{
    routeType=3;
    AMapWalkingRouteSearchRequest *navi = [[AMapWalkingRouteSearchRequest alloc] init];
    /* 出发点. */
    navi.origin = [AMapGeoPoint locationWithLatitude:_mapView.userLocation.location.coordinate.latitude
                                           longitude:_mapView.userLocation.location.coordinate.longitude];
    /* 目的地. */
    navi.destination = self.ampDistancePoint;
    [self.search AMapWalkingRouteSearch:navi];
}

4路径规划搜索回调

通过AMapSearchAPI类向服务器发起路线规划请求后,如规划成功则跳到onRouteSearchDone方法,response,包含类路线的所有信息耗时,距离,换乘等等,否则跳转到- (void)AMapSearchRequest:(id)request didFailWithError:(NSError *)error方法中,并附带出错原因。

在这里我使用了预加载的模式,在用户跳转到本界面时,就自动发起多有类型路线的规划,等所有路线类型都规划完毕后,用户才能查看各种类型的导航路线。预加载可以让用户对三种路线规划耗时一目了然,增强用户体验。
在预加载这里有一个问题,如果同时发起三种类型的路线规划请求,将会收到三次回调,但是由于异步的关系,就不能知道哪次回调对应着的是哪种类型的路线规划请求。

所以在这里我用了一个笨方法,先发起步行路线规划,等收到回调后再发起公共交通路线规划,用routeType属性可以知道当前是哪种类型请求的回调,再发起驾车路线规划,等驾车路线规划收到回调,那么所有路线加载完毕,则隐藏小菊花,用户也就能查看到各种类型的导航路线了。

/* 路径规划搜索回调. */
- (void) onRouteSearchDone:(AMapRouteSearchBaseRequest *)request response:(AMapRouteSearchResponse *)response
{
    if (response.route == nil)
    {
        return;
    }
    /* 预加载*/
    if (routeType==1) {
        self.driveRoute=response.route;
        self.driveDration.text=[self timeFomart: response.route.paths[0].duration];//预计用时
        [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;
}

如若出错,应该就是网络不好访问超时,要么就是超过距离,如超过了市区的公交线路导航,或其他原因系统无法规划出路线,那么则在出错处理中根据不同的类型进行错误的处理,并在此发起下一个类型的线路规划请求,在这里为了方便,出错的类型直接显示暂无= =。
//出错处理

- (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解析路线及路线的绘制

路线的解析以及路线的绘制,如用户查看驾车路线时,直接解析已预加载好的路线规划,然后再进行路线的绘制即可,在这里我直接是用的官方demo里的代码,仅仅根据自己的需求及界面的美观小小的修改了一下。
路线的解析,注意有些类是demo中封装好的,非官方库中的。
注意:要先清楚地图上的所有绘制

//清理绘制路线
- (void)clear
{
    [self.naviRoute removeFromMapView];
}

点击驾车路线规划图标:

- (IBAction)drivingBtn:(id)sender {
    [self clear];
    routeType=1;
    self.distance.text =[self disFormat:self.driveRoute.paths[0].distance];
    [self presentCurrentCourse];//路线解析
}
//根据不同导航类型解析不同路线
- (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];

    }    
    /* 缩放地图使其适应polylines的展示. */
    self.naviRoute.anntationVisible=YES;
    [self.mapView showOverlays:self.naviRoute.routePolylines edgePadding:UIEdgeInsetsMake(RoutePlanningPaddingEdge, RoutePlanningPaddingEdge, RoutePlanningPaddingEdge, RoutePlanningPaddingEdge) animated:YES];    
}

路线的绘制直接照搬官方demo中的(手动斜眼)

注意:地图的绘制分两个部分,路线的绘制以及路线节点(Annotation)的绘制

路线的绘制

//根据不同导航类型绘制路线
- (MAOverlayRenderer *)mapView:(MAMapView *)mapView rendererForOverlay:(id <MAOverlay>)overlay// 任何遵循此协议的对象
{
    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;
}  

路线节点的绘制

//根据导航类型绘制覆盖物
- (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;       //设置气泡可以弹出,默认为NO
                    annotationView.animatesDrop = YES;        //设置标注动画显示,默认为NO
                    annotationView.draggable = YES;        //设置标注可以拖动,默认为NO
                    annotationView.pinColor = MAPinAnnotationColorRed;
                    return annotationView;
            }
        }
        return poiAnnotationView;
    }
    return nil;
}

6其他

还有些边角代码:如时间格式转换,距离格式转换,string类型的经纬度转换等也一并献上吧,

-(AMapGeoPoint *)pointFormat:(NSString *)point{
    //经纬度格式转换
    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];
}
//距离格式转换
-(NSString *)disFormat:(double)meters {
    double intDistance=(int)round(meters);
    return [NSString stringWithFormat:@"距离:%0.2fKM",intDistance/1000 ];
}
//时间格式转换
-(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(害羞#)

注:需手动导入AmpFrameworks
https://github.com/calvinWen/AmpDemo

上一篇下一篇

猜你喜欢

热点阅读