美文网首页
高德地图开发相关总结

高德地图开发相关总结

作者: Kevin_wzx | 来源:发表于2022-07-22 11:49 被阅读0次

    目录

    1.坐标初始化方法
    2.打开第三方地图应用App进行导航及坐标转换
    3.地图最简单的定位及气泡图标展示
    4.地图路径规划(起点到终点的距离和时间)
    5.地图路径规划画折线(起终点两点折线)
    6.地图画区域图形多边形遮盖物
    7.地图气泡的聚合功能
    8.App内部地图导航开发(包含更换终点重新导航)
    9.地图根据经纬度进行逆编码
    10.地图SDK导入工程及plist权限添加
    11.集中简单看下关键类以及方法

    高德地图官方文档:https://lbs.amap.com/api/ios-sdk/summary/
    百度地图开发相关总结:https://www.jianshu.com/p/dad28e138dca

    1.坐标初始化方法

    同百度地图开发相关总结

    2.打开第三方地图应用App进行导航及坐标转换

    同百度地图开发相关总结

    3.地图最简单的定位及气泡展示讲解(司机分布例子)

    场景需求:以当前定位为中心,一公里为半径的分布区域内展示周边代驾司机的分布情况,正在忙碌非空闲的用红色图标表示,空闲的用蓝色图标表示。中间的定位图标一直至于地图中心位置,地图可以拖动进行刷新周边司机分布情况。

    举例:司机分布控制器 BBXDriverDistributionViewController.m

    司机分布效果图

    1.基础类以及方法等

    基础类属性等 基本方法1 基本方法2 基本方法3 懒加载1 懒加载2

    2.网络请求方法(获取数据添加点到地图)

    #pragma mark -- 网络请求
    //网络请求,刷新界面
    - (void)refreshEventWithLat:(NSString *)lat Lng:(NSString *)lng {
        
        NSDictionary *params = @{
                                 @"line_id":[BBXDriverInfo shareManager].line_id?:@"",
                                 @"max_distance":@(1000),//1公里范围内
                                 @"min_distance":@(0),
                                 @"location":@{@"lat":lat?:@"",
                                               @"lng":lng?:@""
                                              }
                                 };
        NSString *url = [NSString stringWithFormat:@"%@/position/getDesignatedDriverPosition", kNewDomainBBXTest];//获取代驾司机分布
        [SVProgressHUD show];
        @weakify(self);
        [BBXAPIHTTP yh_requestBBXWithParameter:params adnFinishBlock:^(RequestResultObj *resultObj) {
            @strongify(self);
            [SVProgressHUD dismiss];
            if([resultObj.data isKindOfClass:[NSDictionary class]]) {
                
                NSInteger status = [resultObj.data[@"status"] integerValue];
                NSString * message = resultObj.data[@"message"];
                if(status == 200) {//成功
                    [self.orderArray removeAllObjects];//移除所有数据
                    [self.mapView removeAnnotations:self.mapView.annotations];//移除地图上的所有标注
                    NSArray *dataA = [resultObj.data[@"result"] copy];
                    self.orderArray = [BBXRepDriveDistributeModel mj_objectArrayWithKeyValuesArray:dataA];
                    
                    //重新排序
                    NSMutableArray *kongArr = [[NSMutableArray alloc] init];//空闲
                    NSMutableArray *noKongArr = [[NSMutableArray alloc] init];//非空闲
                    for (BBXRepDriveDistributeModel *disModel in self.orderArray) {
                        if (kStrEqual(disModel.show_status, @"0")) {//0:空闲
                            [kongArr addObject:disModel];
                        } else {
                            [noKongArr addObject:disModel];
                        }
                    }
                    NSArray *kong = [NSArray arrayWithArray:kongArr];//空闲
                    NSArray *noKong = [NSArray arrayWithArray:noKongArr];//非空闲
                    noKong = [noKong arrayByAddingObjectsFromArray:kong];//kong排在noKong后面,后加入描点在最上面
                    self.orderArray = [NSMutableArray arrayWithArray:noKong];
                    
                    for (NSDictionary *dict in self.orderArray) {
                        NSDictionary *poiDict = dict;
                        BBXRepDriveDistributeModel *poi = [BBXRepDriveDistributeModel mj_objectWithKeyValues:poiDict];
                        BBXRepDrivePointAnnotation *annotation = [[BBXRepDrivePointAnnotation alloc] init];
                        CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake([poi.location.lat doubleValue], [poi.location.lng doubleValue]);
                        annotation.coordinate = coordinate;
                        annotation.distributrModel = poi;
                        [self.mapView addAnnotation:annotation];
                    }
                } else {
                    [SVProgressHUD dismiss];
                    [self.view showRequestFailToast:message];
                }
            } else{
                [SVProgressHUD dismiss];
                [self.view showRequestFailToast:nil];
            }
        } andRequestURl:url toCrypt:YES];
    }
    

    3.地图定位代理方法

    #pragma mark -- 高德地图代理方法:定位delegate
    //连续定位回调函数.注意:如果实现了本方法,则定位信息不会通过amapLocationManager:didUpdateLocation:方法回调。
    - (void)amapLocationManager:(AMapLocationManager *)manager didUpdateLocation:(CLLocation *)location reGeocode:(AMapLocationReGeocode *)reGeocode {
        
    }
    
    //设备方向改变时回调函数
    - (void)amapLocationManager:(AMapLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading {
        if (!newHeading) {
            return;
        }
    }
    
    //地图区域改变完成后会调用此接口(位置变更的回调如拖动地图)
    - (void)mapView:(MAMapView *)mapView regionDidChangeAnimated:(BOOL)animated wasUserAction:(BOOL)wasUserAction {
        
        CGPoint touchPoint = self.mapView.center;
        CLLocationCoordinate2D touchMapCoordinate = [self.mapView convertPoint:touchPoint toCoordinateFromView:self.mapView];//移动地图屏幕中心点的经纬度:touchMapCoordinate
        NSLog(@"touching %f,%f",touchMapCoordinate.latitude,touchMapCoordinate.longitude);
        NSString *lat = [NSString stringWithFormat:@"%f",touchMapCoordinate.latitude];
        NSString *lng = [NSString stringWithFormat:@"%f",touchMapCoordinate.longitude];
        self.refreshLat = lat;
        self.refreshLng = lng;
        [self refreshEventWithLat:lat Lng:lng];
    }
    

    4.地图代理方法

    #pragma mark -- 高德地图代理方法:展示点击delegate
    //地图初始化完毕时会调用此接口
    - (void)mapViewDidFinishLoadingMap:(MAMapView *)mapView {
        
        [self.mapView setCenterCoordinate:[BBXLocationManager shareManager].location.coordinate];//设置当前地图中心点
    }
    
    //根据anntation生成对应的View(设置大头针样式)
    - (MAAnnotationView *)mapView:(MAMapView *)mapView viewForAnnotation:(id<MAAnnotation>)annotation {
        
        NSString *IdentifierStr2 = @"bbxAnnotationSend2";
        BBXRepDriveAnnotationView *annotationView2 = (BBXRepDriveAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:IdentifierStr2];
        if (!annotationView2) {
            annotationView2 = [[BBXRepDriveAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:IdentifierStr2];
        }
        BBXRepDrivePointAnnotation *bbx = (BBXRepDrivePointAnnotation *)annotation;
        annotationView2.draggable = NO;//禁止标注在地图上拖动
        annotationView2.annotation = bbx;
        if (self.orderArray.count > 0) {
            annotationView2.distribuModel = bbx.distributrModel;
        }
        annotationView2.canShowCallout = NO;
        annotationView2.centerOffset=CGPointMake(0,0);
        return annotationView2;
    }
    
    //点地图上的气泡时回调
    - (void)mapView:(MAMapView *)mapView didSelectAnnotationView:(MAAnnotationView *)view {
        if ([view isKindOfClass:[BBXShareView class]]) {
            BBXShareView *annotationview = (BBXShareView *)view;
            BBXShare *s = view.annotation;//当前点击拿到的点
        }
    }
    

    5.初始化地图以及定位对象

    //初始化地图
    - (void)initMap {
        self.mapView = [[MAMapView alloc] init];
        self.mapView.delegate = self;
        [self.view addSubview:self.mapView];
        [self.mapView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.edges.equalTo(self.view);
        }];
        self.mapView.showsUserLocation = YES;
        self.mapView.zoomLevel = 18;//地图缩放级别
        self.mapView.zoomEnabled = NO;//不能缩放
        self.mapView.userTrackingMode = MAUserTrackingModeFollow;
        self.mapView.rotateEnabled = NO;//是否支持旋转
        self.mapView.rotateCameraEnabled = NO;
        [self.locationManager startUpdatingLocation];//连续定位回调方法(location定位结果)
        [self.locationManager startUpdatingHeading];//设备朝向回调方法;如果设备支持方向识别,则会通过代理回调方法
        self.represent = [[MAUserLocationRepresentation alloc] init];
        self.represent.showsAccuracyRing = NO;//精度圈是否显示
        self.represent.image = kImage(@"bbx_carLocation");
        [self.mapView updateUserLocationRepresentation:self.represent];
    }
    

    其中上述代码中涉及的相关类:同百度地图开发相关总结

    4.地图路径规划(起点到终点的距离和时间)

    地图路径规划:也叫路线规划,就是规划起点到终点所需要的路程和时间。这个规划的结果可能有多条,根据不同规划策略有不同结果,比如最短距离方案、最快时间方案等,依照自己需求而定。这个就相当于用地图App进行起终点导航的时候会优先规划好路线让你选择一个进行导航。

    App导航路径规划效果图

    高德App导航路径规划效果图 bbx路径规划效果图及选中气泡 bbx路径规划效果图及选中气泡

    1.准备工作

    1.引入文件名以及协议
    #import <AMapNaviKit/AMapNaviKit.h>
    #import <AMapSearchKit/AMapSearchKit.h>
    #import <AMapFoundationKit/AMapFoundationKit.h>
    #import <AMapLocationKit/AMapLocationKit.h>
    协议:<MAMapViewDelegate, AMapSearchDelegate, AMapLocationManagerDelegate>
    
    2.相关属性及视图创建
    @property (nonatomic, strong) MAMapView *mapView;//地图视图
    @property (nonatomic, strong) AMapLocationManager *locationManager;//高德地图负责获取定位相关信息
    @property (nonatomic, strong) MAUserLocation *userLocation;//司机当前位置对象
    @property (nonatomic, strong) MAUserLocationRepresentation *represent;//定位图层图标
    @property (nonatomic, strong) AMapSearchAPI *mapSearch;//路径规划
    @property (nonatomic, strong) NSMutableArray *orderArray;//订单数据数组
    
    //初始化地图
    - (void)initMap {
    
        self.mapView = [[MAMapView alloc] initWithFrame:self.view.bounds];
        [self.view addSubview:self.mapView];
        self.mapView.delegate = self;
        self.mapView.showsUserLocation = YES;
        self.mapView.zoomLevel = 15;//地图缩放级别
        self.mapView.userTrackingMode = MAUserTrackingModeFollow;
        self.mapView.rotateEnabled = NO;//是否支持旋转
        self.mapView.rotateCameraEnabled = NO;
        self.represent = [[MAUserLocationRepresentation alloc] init];
        self.represent.showsAccuracyRing = NO;//精度圈是否显示
        [self.locationManager startUpdatingLocation];//连续定位回调方法(location定位结果)
        [self.locationManager startUpdatingHeading];//设备朝向回调方法;如果设备支持方向识别,则会通过代理回调方法
        self.represent.image = kImage(@"bbx_carLocation");
        [self.mapView updateUserLocationRepresentation:self.represent];
    }
    
    //订单数组
    - (NSMutableArray *)orderArray {
        if (!_orderArray) {
            _orderArray = [[NSMutableArray alloc] init];
        }
        return _orderArray;
    }
    
    //司机当前位置对象
    - (MAUserLocation *)userLocation {
        if (!_userLocation) {
            _userLocation = [[MAUserLocation alloc] init];
        }
        return _userLocation;
    }
    
    //定位管理类
    - (AMapLocationManager *)locationManager {
        if (!_locationManager) {
            _locationManager = [[AMapLocationManager alloc] init];
            _locationManager.delegate = self;
            [_locationManager setPausesLocationUpdatesAutomatically:NO];
            [_locationManager setAllowsBackgroundLocationUpdates:NO];
        }
        return _locationManager;
    }
    
    //路线规划
    - (AMapSearchAPI *)mapSearch {
        if (!_mapSearch) {
            _mapSearch = [[AMapSearchAPI alloc] init];
            _mapSearch.delegate = self;
        }
        return _mapSearch;
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.navigationController.navigationBar.hidden = NO;
        self.title = @"到达出发地";
        [self createMainView];//创建主界面,包含创建地图
        [self refreshEvent:YES];//显示底部弹框
        [self setAddressAnnotation];//网络请求方法中获取到数据也会调用这个方法
    }
    

    2.核心代码

    1.设置地图标注,路径规划初始化
    //设置地图标注视图(即地图中显示“xx公里xx分钟”气泡)
    - (void)setAddressAnnotation {
        
        if (self.orderArray.count == 0 || !self.orderArray) {
            [self.mapView removeAnnotations:[self.mapView annotations]];//移除当前地图View的已经添加的标注数组
            return;
        }
        
        //添加大头针设置完标注坐标后,后调用viewForAnnotation代理方法,可在该代理方法中设置大头针的样式
        for (BBXOrderListModel *orderModel in self.orderArray) {
            [self creatPlan:orderModel];
        }
        
        //设置地图的显示区域
        for (BBXOrderListModel *orderModel in self.orderArray) {
            if (!orderModel.endAddress || orderModel.endAddress == nil || kStrEqual(orderModel.endAddress, @"")) {
                [self.mapView setCenterCoordinate:[BBXLocationManager shareManager].location.coordinate];//设置当前地图中心点
            } else {
                [self showAllAnnotationWithAllPointArray:self.allOrderPointArr];//显示全部
            }
        }
        
        //设置弹窗控件模型
        if (self.currentOrderModel) {
            BBXOrderViewModel * model = [[BBXOrderViewModel alloc] initWithOrderModel:self.currentOrderModel];
            [self.mapPopupView setViewModel:model bbxMapVCType:BBXRepDrivingMapVCTypeWithNetContractPickMap];
        }
    }
    
    //初始化司机路径规划
    - (void)creatPlan:(BBXOrderListModel *)model {
        
        //发起驾车路线规划
        AMapDrivingRouteSearchRequest *navi = [[AMapDrivingRouteSearchRequest alloc] init];
        AMapGeoPoint *endPoint;
        navi.origin = [AMapGeoPoint locationWithLatitude:BBXLocationManager.shareManager.location.coordinate.latitude longitude:BBXLocationManager.shareManager.location.coordinate.longitude];//起点(司机位置)
        CLLocation *listLocation = [[CLLocation alloc] initWithLatitude:model.end_lat longitude:model.end_lng];
        CLLocationDegrees lat = listLocation.coordinate.latitude;
        CLLocationDegrees lng = listLocation.coordinate.longitude;
        endPoint = [AMapGeoPoint locationWithLatitude:lat longitude:lng];
        navi.destination = endPoint;//终点(叫代驾或叫车的人的位置)
        navi.strategy = 2;//距离优先(传一个想要的规划策略给高德)
        [self.mapSearch AMapDrivingRouteSearch:navi];//发起驾车路径规划查询
    }
    
    2.#para mark -- 路径规划结果回调方法
    //默认是驾车路径,需要哪种路径规划可以通过判断request设置,比如骑行 AMapRidingRouteSearchRequest,驾车 AMapDrivingRouteSearchRequest
    //路线规划搜索结果(默认驾车)
    - (void)onRouteSearchDone:(AMapRouteSearchBaseRequest *)request response:(AMapRouteSearchResponse *)response {
        if (response.route == nil) {//路线规划出错(太近检索失败)
            return;
        }
        AMapPath *path = [response.route.paths firstObject];
        NSLog(@"start:%f---%f,end:%f---%f", response.route.origin.latitude, response.route.origin.longitude, response.route.destination.latitude, response.route.destination.longitude);
        NSLog(@"distance: %ld", (long)path.distance);
        [self.receiveMapVM updateClusters:response];
    }
    
    //路径规划处理详情
    - (void)updateClusters:(AMapRouteSearchResponse *)response {
        if (!response) {
            return;
        }
        
        AMapRoute *route = response.route;
        AMapPath *path = [route.paths firstObject];
        NSInteger currentDis = path.distance;
        NSInteger driverTimeHours = path.duration/3600;
        NSInteger driverTimeMinutes = path.duration/60;
        NSInteger minDis = NSIntegerMax;
        if (currentDis < minDis) {
            minDis = currentDis;
        }
        for (BBXOrderListModel *model in self.orderArray) {
            if (model.order_type == OrderType_Bus &&
                !([model.locations.start.area_level isEqualToString:@"1"] || [model.locations.start.area_level isEqualToString:@"3"])) {// 快线-并且站点名称无详细地址(后台返回经纬度只有小数点5位)
                NSDictionary *dataDict = [NSDictionary dictionaryWithJsonString:model.locations.start.station_location];
                if([dataDict isKindOfClass:[NSDictionary class]]) {
                    CLLocationDegrees lat = [dataDict[@"lat"] doubleValue];
                    CLLocationDegrees lng = [dataDict[@"lng"]doubleValue];
                    CLLocationCoordinate2D startPt = (CLLocationCoordinate2D){lat, lng};
                    // 判断当前路线是属于哪个订单,当然如果多个订单在同一个位置,或者附近就直接一个气泡显示
                    if (route.origin.latitude - startPt.latitude > -0.0001
                        && route.origin.latitude - startPt.latitude < 0.0019
                        && route.origin.longitude- startPt.longitude > -0.0001
                        && route.origin.longitude- startPt.longitude < 0.0019) {
                        
                        model.dis = minDis?minDis:99999999;
                        
                        if (driverTimeHours>0 && driverTimeMinutes>0) {//计算路径规划的时间
                            model.driverTime = [NSString stringWithFormat:@"约%.ld小时%.ld分钟",(long)driverTimeHours,(long)driverTimeMinutes];
                        } else if (driverTimeHours>0 && driverTimeMinutes<=0) {
                            model.driverTime = [NSString stringWithFormat:@"约%.ld小时",(long)driverTimeHours];
                        } else if (driverTimeHours<=0 && driverTimeMinutes>0) {
                            model.driverTime = [NSString stringWithFormat:@"约%.ld分钟",(long)driverTimeMinutes];
                        } else if (driverTimeHours<=0 && driverTimeMinutes<=0) {
                            model.driverTime = @"约1分钟";
                        }
                    }
                }
            } else {
                // 其他线路
                if (route.destination.latitude - model.start_lat > -0.000009
                    && route.destination.latitude - model.start_lat < 0.000009
                    && route.destination.longitude- model.start_lng > -0.000009
                    && route.destination.longitude- model.start_lng < 0.000009) {
                    model.dis = minDis?minDis:99999999;
                    if (driverTimeHours>0 && driverTimeMinutes>0) {//计算路径规划的时间
                        model.driverTime = [NSString stringWithFormat:@"约%.ld小时%.ld分钟",(long)driverTimeHours,(long)driverTimeMinutes];
                    } else if (driverTimeHours>0 && driverTimeMinutes<=0) {
                        model.driverTime = [NSString stringWithFormat:@"约%.ld小时",(long)driverTimeHours];
                    } else if (driverTimeHours<=0 && driverTimeMinutes>0) {
                        model.driverTime = [NSString stringWithFormat:@"约%.ld分钟",(long)driverTimeMinutes];
                    } else if (driverTimeHours<=0 && driverTimeMinutes<=0) {
                        model.driverTime = @"约1分钟";
                    }
                } else {
                    NSLog(@"");
                }
            }
        }
        [self addAnnotationsToMapView:self.mapView];//聚合详情代码,此处省略,包含标注点添加到地图
    }
    

    3.其他相关方法

    //根据anntation生成对应的View(自定义标注点View)
    - (MAAnnotationView *)mapView:(MAMapView *)mapView viewForAnnotation:(id<MAAnnotation>)annotation {
        
        if ([annotation isKindOfClass:[BBXShare class]]) {
            
            //根据指定标识查找一个可被复用的标注View
            NSString *IdentifierStr = @"bbxAnnotation";
            BBXShareView *annotationView = (BBXShareView *)[mapView dequeueReusableAnnotationViewWithIdentifier:IdentifierStr];
            if (!annotationView) {//创建标识视图
                annotationView = [[BBXShareView alloc] initWithAnnotation:annotation reuseIdentifier:IdentifierStr];
            }
            BBXShare *bbxAn = (BBXShare *)annotation;
            annotationView.annotation = bbxAn;
            annotationView.tag = bbxAn.tag;//地图大头针的tag
            bbxAn.model.isMap = YES;
            annotationView.model = bbxAn.model;
            annotationView.canShowCallout = NO;//当为YES时,view被选中时会弹出气泡,annotation必须实现了title这个方法
            annotationView.model.order_typeCarpool = bbxAn.order_typeCarpool;
            annotationView.model.order_typeGoods = bbxAn.order_typeGoods;
            annotationView.model.order_typeFastLine = bbxAn.order_typeFastLine;
            annotationView.model.order_typeCity = bbxAn.order_typeCity;
            annotationView.model.order_typeCharter = bbxAn.order_typeCharter;
            annotationView.isSendMap = NO;
            annotationView.size = bbxAn.size;
            
            //接客地图
            if ([bbxAn.model.order_id isEqualToString:self.currentOrderModel.order_id] && self.hasPickUpOrder) {//当前选中的标注点
                
                annotationView.leftImg.image = [UIImage imageNamed:@"map-driving"];//代驾
                if (annotationView.rightLb.text.length>=14 && annotationView.rightLb.text.length<=16) {
                    annotationView.pinBgView.image = kImage(@"bbx_bgBlueLong");
                    annotationView.rightLb.textColor = BBXBlack;
                } else if (annotationView.rightLb.text.length>16) {
                    annotationView.pinBgView.image = kImage(@"bbx_bgBlueLong2");
                    annotationView.rightLb.textColor = BBXBlack;
                } else {
                    annotationView.pinBgView.image = kImage(@"bbx_bgBlue");
                    annotationView.rightLb.textColor = BBXWhite;
                }
                annotationView.centerOffset = CGPointMake(0, -(annotationView.pinBgView.size.height/2));
            } else {//未选中的标注点
                
                //代驾订单这边气泡一直都选中且只有一个,所以背景跟上面选中设置一样
                if (bbxAn.model.order_status == Order_state_sended) {//已派单
                    annotationView.leftImg.image = [UIImage imageNamed:@"map-driving"];//代驾
                    if (annotationView.rightLb.text.length>=14 && annotationView.rightLb.text.length<=16) {
                        annotationView.pinBgView.image = kImage(@"bbx_bgBlueLong");
                        annotationView.rightLb.textColor = BBXBlack;
                    } else if (annotationView.rightLb.text.length>16) {
                        annotationView.pinBgView.image = kImage(@"bbx_bgBlueLong2");
                        annotationView.rightLb.textColor = BBXBlack;
                    } else {
                        annotationView.pinBgView.image = kImage(@"bbx_bgBlue");
                        annotationView.rightLb.textColor = BBXWhite;
                    }
                    annotationView.centerOffset = CGPointMake(0, -(annotationView.pinBgView.size.height/2));
                }
            }
            return annotationView;
        }
        return nil;
    }
    
    //地图初始化完毕时会调用此接口
    - (void)mapViewDidFinishLoadingMap:(MAMapView *)mapView {
       //[self showAllAnnotationWithAllPointArray:self.allOrderPointArr];//显示全部
    }
    
    网络请求数据获取订单数组:self.orderArray 为数据源,此处详情省略

    5.地图路径规划画折线(起终点两点折线)

    地图路径规划画折线:即将起点到终点按要求进行的路线规划连成一条线在地图上呈现。折线途经点设置可以理解起点到终点过程中还有好几个点需要连接起来,按照起点与途经点以及终点这些综合的实际情况规划一条最优的路径顺序串联起来。

    需求场景举例1:以代驾司机当前位置为起点,下单代驾的人的起点为终点,画折线进行骑行路劲规划。

    需求场景举例2:以司机当前位置为起点,有n个订单乘客需要接,最后一个接的乘客当做终点,剩下乘客当做途径点进行路线规划串联起来。此处高德地图未讲解,参考百度地图途经点。

    其他说明:下面用的画折线方法为:polylineWithCoordinate,还有一种通过经纬度方法为:polylineWithPoints

    相关链接:
    https://lbs.amap.com/api/ios-sdk/guide/draw-on-map/draw-polyline
    https://www.jianshu.com/p/79b455c74679

    折线效果图

    画折线-最简单2个点即起终点折线设置 画折线-接送客地图途经点设置效果图

    1.准备工作

    对于一些准备工作比如地图头文件和协议引用、地图及定位、路线规划等属性的创建、界面基础创建以及数据源接口请求获取原理同上面,此处省略,这部分功能重点看下面核心代码。

    2.核心代码
    2.1最简单的2个点画折线(起终点)

    //1.发起路线规划(获取数据源接口成功的地方调用这个)
    - (void)updateViewByShowingOrderListModel:(BBXOrderListModel *)orderListModel {
    
        //发起骑行路线规划
        AMapRidingRouteSearchRequest *navi = [[AMapRidingRouteSearchRequest alloc] init];
        AMapGeoPoint *endPoint;
        navi.origin = [AMapGeoPoint locationWithLatitude:BBXLocationManager.shareManager.location.coordinate.latitude longitude:BBXLocationManager.shareManager.location.coordinate.longitude];//起点
        CLLocation *listLocation = [[CLLocation alloc] initWithLatitude:self.orderListModel.start_lat longitude:self.orderListModel.start_lng];
        CLLocationDegrees lat = listLocation.coordinate.latitude;
        CLLocationDegrees lng = listLocation.coordinate.longitude;
        endPoint = [AMapGeoPoint locationWithLatitude:lat longitude:lng];
        navi.destination = endPoint;//终点(叫代驾或叫车的人的位置)
        navi.type = 1;//推荐路线
        [self.mapSearch AMapRidingRouteSearch:navi];//发起骑行路径规划查询
    }
    
    #pragma mark -- 高德路线规划
    //2.骑行路线规划结果
    - (void)onRouteSearchDone:(AMapRouteSearchBaseRequest *)request response:(AMapRouteSearchResponse *)response {
        
        NSArray *array = [NSArray arrayWithArray:self.mapView.annotations];
        [self.mapView removeAnnotations:array];
        array = [NSArray arrayWithArray:self.mapView.overlays];
        [self.mapView removeOverlays:array];//移除遮盖物
        
        if (response.route == nil) {//路线规划出错(太近检索失败)
    
            AMapRoute *route = response.route;
            AMapPath *path = [route.paths firstObject];
    
            //添加起点标注
            BBXPassAnnotation *startAnnotation = [[BBXPassAnnotation alloc] init];
            startAnnotation.type = @"start";
            CLLocation * driverLocation = [BBXLocationManager shareManager].location;
            startAnnotation.coordinate = driverLocation.coordinate;
            [self.mapView addAnnotation:startAnnotation];
    
            //添加终点标注
            BBXPassAnnotation  *endAnnotation = [[BBXPassAnnotation alloc] init];
            endAnnotation.type = @"end";
            CLLocation *listLocation = [[CLLocation alloc] initWithLatitude:self.orderListModel.start_lat longitude:self.orderListModel.start_lng];
            endAnnotation.coordinate = listLocation.coordinate;
            [self.mapView addAnnotation:endAnnotation];
    
            //泡泡弹出的地点(叫代驾的人所在的点)
            BBXPassAnnotation *passAn = [[BBXPassAnnotation alloc] init];
            passAn.type = @"Pop";
            passAn.title = @"距离车主1.0米";
            CLLocation *palocation = [[CLLocation alloc] initWithLatitude:self.orderListModel.start_lat longitude:self.orderListModel.start_lng];
            passAn.coordinate = palocation.coordinate;
            [self.mapView addAnnotation:passAn];
            
            //画路线规划的折线
            MAPolyline *polyline = [self polylinesForPath:path];//轨迹点
            [self.mapView addOverlay:polyline];//画路线规划的折线
            [self mapViewFitPolyLine:polyline];//根据polyline设置地图范围
            return;
        } else {
            
            AMapPath *path = [response.route.paths firstObject];
            NSInteger mile = path.distance;
           //float distance= MKMetersBetweenMapPoints(startPoint,endPoint);//计算指定两点之间的距离
            if (mile>=0 && mile<=100.0) {//100米认为比较近
                [BBXProgressHUD showInfo:@"距离太近,无法规划路线"];
                return;
            }
            //路线规划成功
            if ([request isKindOfClass:[AMapRidingRouteSearchRequest class]]) {//骑行路径规划
                
                AMapRoute *route = response.route;
                AMapPath *path = [route.paths firstObject];
                NSInteger size = [path.steps count];
               
                for (int i = 0; i < size; i++) {
                                    
                    if (i == 0) {
                        //添加起点标注
                        BBXPassAnnotation *startAnnotation = [[BBXPassAnnotation alloc] init];
                        startAnnotation.type = @"start";
                        CLLocation *startLocat = [[CLLocation alloc] initWithLatitude:route.origin.latitude longitude:route.origin.longitude];
                        startAnnotation.coordinate = startLocat.coordinate;
                        [self.mapView addAnnotation:startAnnotation];
                    }
    
                    if(i == size-1) {
                        //添加终点标注
                        BBXPassAnnotation *endAnnotation = [[BBXPassAnnotation alloc] init];
                        endAnnotation.type = @"end";
                        CLLocation *endLocat = [[CLLocation alloc] initWithLatitude:route.destination.latitude longitude:route.destination.longitude];
                        endAnnotation.coordinate = endLocat.coordinate;
                        [self.mapView addAnnotation:endAnnotation];
                    }
                }
    
                //泡泡弹出的地点(叫代驾的人所在的点)
                BBXPassAnnotation *passAn = [[BBXPassAnnotation alloc] init];
                passAn.type = @"Pop";
                CLLocation *listLocation = [[CLLocation alloc] initWithLatitude:self.orderListModel.start_lat longitude:self.orderListModel.start_lng];
                passAn.coordinate = listLocation.coordinate;
                float distance = path.distance;
                if (distance >= 100.0) {
                    NSString *kmString = [BBXMileageConvert meterTurnKilometreOneDecimalWith:distance];//米转公里
                    passAn.title = [NSString stringWithFormat:@"距离车主%@公里", kmString];
                } else {
                    if (distance<=10.0) {
                        distance = 1.0;
                        passAn.title = [NSString stringWithFormat:@"距离车主%.1f米", distance];
                    } else {
                        passAn.title = [NSString stringWithFormat:@"距离车主%.1f米", distance];
                    }
                }
                [self.mapView addAnnotation:passAn];
    
                //画路线规划的折线
                MAPolyline *polyline = [self polylinesForPath:path];//轨迹点
                [self.mapView addOverlay:polyline];//画路线规划的折线
                [self mapViewFitPolyLine:polyline];//根据polyline设置地图范围
            }
        }
    }
    
    //3.路径规划检索失败
    - (void)AMapSearchRequest:(id)request didFailWithError:(NSError *)error {
        NSLog(@"Error: %@", error);
    }
    
    //4.路线解析-轨迹点
    - (MAPolyline *)polylinesForPath:(AMapPath *)path {
        if (path == nil || path.steps.count == 0){
            return nil;
        }
        NSMutableString *polylineMutableString = [@"" mutableCopy];
        for (AMapStep *step in path.steps) {
            [polylineMutableString appendFormat:@"%@;",step.polyline];
        }
        NSUInteger count = 0;
        CLLocationCoordinate2D *coordinates = [self coordinatesForString:polylineMutableString
                                                         coordinateCount:&count
                                                              parseToken:@";"];
        MAPolyline *polyline = [MAPolyline polylineWithCoordinates:coordinates count:count];
        free(coordinates), coordinates = NULL;
        return polyline;
    }
    
    //5.解析经纬度
    - (CLLocationCoordinate2D *)coordinatesForString:(NSString *)string
                                     coordinateCount:(NSUInteger *)coordinateCount
                                          parseToken:(NSString *)token{
        if (string == nil){
            return NULL;
        }
        
        if (token == nil){
            token = @",";
        }
        
        NSString *str = @"";
        if (![token isEqualToString:@","]) {
            str = [string stringByReplacingOccurrencesOfString:token withString:@","];
        } else {
            str = [NSString stringWithString:string];
        }
        
        NSArray *components = [str componentsSeparatedByString:@","];
        NSUInteger count = [components count] / 2;
        if (coordinateCount != NULL){
            *coordinateCount = count;
        }
        CLLocationCoordinate2D *coordinates = (CLLocationCoordinate2D*)malloc(count * sizeof(CLLocationCoordinate2D));
        
        for (int i = 0; i < count; i++){
            coordinates[i].longitude = [[components objectAtIndex:2 * i]     doubleValue];
            coordinates[i].latitude  = [[components objectAtIndex:2 * i + 1] doubleValue];
        }
        return coordinates;
    }
    
    //6.根据polyline设置地图范围
    - (void)mapViewFitPolyLine:(MAPolyline *) polyLine {
        CGFloat leftTopX, leftTopY, rightBottomX, rightBottomY;
        if (polyLine.pointCount < 1) {
            return;
        }
        MAMapPoint pt = polyLine.points[0];//BMKMapPoint
        // 左上角顶点
        leftTopX = pt.x;
        leftTopY = pt.y;
        // 右下角顶点
        rightBottomX = pt.x;
        rightBottomY = pt.y;
        for (int i = 1; i < polyLine.pointCount; i++) {
            MAMapPoint pt = polyLine.points[i];
            leftTopX = pt.x < leftTopX ? pt.x : leftTopX;
            leftTopY = pt.y < leftTopY ? pt.y : leftTopY;
            rightBottomX = pt.x > rightBottomX ? pt.x : rightBottomX;
            rightBottomY = pt.y > rightBottomY ? pt.y : rightBottomY;
        }
    
        MAMapRect rect;//BMKMapRect
        rect.origin = MAMapPointMake(leftTopX, leftTopY);//BMKMapPointMake
        rect.size = MAMapSizeMake(rightBottomX - leftTopX, rightBottomY - leftTopY);//BMKMapSizeMake
        UIEdgeInsets padding = UIEdgeInsetsMake(175, 175, 175, 175);//这个自己根据需求调整
        MAMapRect fitRect = [self.mapView mapRectThatFits:rect edgePadding:padding];
        [self.mapView setVisibleMapRect:fitRect];
    }
    
    //7.设置折线的样式
    - (MAOverlayRenderer *)mapView:(MAMapView *)mapView rendererForOverlay:(id<MAOverlay>)overlay {
        if ([overlay isKindOfClass:[MAPolyline class]]){
            MAPolylineRenderer *polylineView = [[MAPolylineRenderer alloc] initWithOverlay:overlay];//BMKPolylineView
            polylineView.strokeColor = [UIColor colorWithRed:51/255.0 green:149/255.0 blue:255/255.0 alpha:1.0];//折线颜色
            polylineView.lineWidth = 6.0; //折线宽度
            polylineView.lineJoinType = kMALineJoinRound;//连接类型
            return polylineView;//BMKOverlayView
        }
        return nil;
    }
    
    //8.根据数据生成地图标注视图
    - (MAAnnotationView *)mapView:(MAMapView *)mapView viewForAnnotation:(id<MAAnnotation>)annotation {
    
        if ([annotation isKindOfClass:[BBXPassAnnotation class]]){
            MAAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:@"passenger"];//BMKAnnotationView
    
            if (!annotationView){
                annotationView = [[MAAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"passenger"];
            }
            annotationView.canShowCallout = NO;
            BBXPassAnnotation * ann = (BBXPassAnnotation *)annotation;
            if ([ann.type isEqualToString:@"start"]) { //起点(司机所在地)
                annotationView.image = [UIImage imageNamed:@"icon_map_designatedDriver"];//代驾
                annotationView.centerOffset = CGPointMake(0, -13);
            } else if ([ann.type isEqualToString:@"Pop"]){ //终点(叫代驾人所在地),弹出泡泡
                annotationView.image = [UIImage imageNamed:@"grab_pop"];
                self.distancelabel = [[UILabel alloc] init];
                [annotationView addSubview:self.distancelabel];
                self.distancelabel.frame = CGRectMake(0, 0, annotationView.bounds.size.width, annotationView.bounds.size.height - 5);
                self.distancelabel.textAlignment = NSTextAlignmentCenter;
                self.distancelabel.text = ann.title;
                self.distancelabel.font  = [UIFont boldSystemFontOfSize:15];
                self.distancelabel.textColor = [UIColor whiteColor];
                annotationView.centerOffset = CGPointMake(0, -annotationView.bounds.size.height/2 - 30);
            } else if ([ann.type isEqualToString:@"end"]){ //终点(叫代驾人所在地)
                annotationView.image = [UIImage imageNamed:@"grab_start"];
                annotationView.centerOffset = CGPointMake(0, -13);
            }
            return annotationView;
        }
        return nil;
    }
    
    //9.用户位置更新后,会调用此函数(userLocation 新的用户位置)
    - (void)compositeManager:(AMapNaviCompositeManager *_Nonnull)compositeManager updateNaviLocation:(AMapNaviLocation *_Nullable)naviLocation {
        CLLocation *listLocation = [[CLLocation alloc] initWithLatitude:naviLocation.coordinate.latitude longitude:naviLocation.coordinate.longitude];
        self.mapView.centerCoordinate = listLocation.coordinate;//更新地图中心点,不然偶尔会跑到北京天安门去
    }
    

    2.2设置途经点画折线

    详情参考百度地图开发相关总结

    #para mark -- 途经点相关
    //途经点 AMapGeoPoint 数组,目前最多支持6个途经点
    @property (nonatomic, copy) NSArray<AMapGeoPoint *> *waypoints;
    

    2.3其他备注

    轨迹点:在调试过程中,对比用同样的方法 polylineWithPoints 画折线的过程中,轨迹点高德地图返回结果为经纬度,百度地图返回结果好像为坐标,再考虑换地图开发的时候注意一下。

    高德地图返回结果:经纬度 百度地图返回结果:好像坐标? 百度地图返回结果:好像坐标?

    6.地图画区域图形多边形遮盖物

    场景:根据需求确定某个区域为上下车区域,超出这个区域即提醒用户超出范围,如下图移动地图定位在蓝色区域内可以上车,上方气泡展示该点的地址或者提示语,这个蓝色区域就是根据接口返回的几个点生成的自定义多边形。

    其他例子参考:
    https://www.jianshu.com/p/fb1177cac1ec?open_source=weibo_search&ivk_sa=1024320u
    https://blog.csdn.net/xuanweihong_ios/article/details/90573787

    区域多边形效果图

    区域多边形效果图g

    7.地图气泡的聚合功能

    点聚合功能:可通过缩小地图层级,将定义范围内的多个标注点聚合显示成一个标注点,解决加载大量点要素到地图上产生覆盖现象的问题并提高性能。 多个气泡标注点可以聚合成一个气泡,点击聚合的气泡可以分解成多个具体的气泡,也可以通过缩放地图进行气泡聚合和分开。

    场景需求描述:如下图刚进入接客地图的时候,需要根据司机当前位置和所有点的位置进行布局展示,要求计算出一个合适的范围能合理展示所有气泡和司机位置,气泡点如果是同一个位置或者两个点相差在50米及以内则将点聚合在一起展示。在最初的地图展示之后,点击单个气泡则气泡变蓝色且弹出该气泡的底部面板展示订单信息和操作(如导航按钮);如果点击聚合气泡则分解在地图上以合适的比例尺展示出所有分解的点,缩放地图的时候,如果气泡缩小很近很近融合在一起展示新的聚合气泡,再缩放放大地图,则聚合气泡随着放大的比例尺也会慢慢分解开展示具体的气泡。

    具体讲解:https://www.jianshu.com/p/29794c293e7e

    聚合效果图

    地图官方效果图 刚进入接客地图展示所有点 点击3单聚合气泡分解成3个具体气泡 缩放地图气泡进行融合成大气泡

    8.App内部地图导航开发(包含更换终点重新导航)

    App内部导航就是在App中集成地图导航SDK,按导航开发文档接入配置好相关配置比如Key,在需要导航的地方再进行相关代码API进行调用地图导航相关的类进行导航,其中需要用到起终点的相关坐标。需要注意的是进入导航中的这个导航页,高德地图导航页是个控制器,而百度地图是地图的一个view不是控制器,在做页面跳转啥的时候需要注意考虑这个。

    更换终点重新导航:后续再总结

    效果图:

    接客地图导航按钮入口 方法1:进入导航中 方法1:进入导航中-缩放效果 方法1:进入导航中-导航页3D图:是个控制器 方法2:进入导航选择页 方法2:开始导航进入导航页.PNG 方法2:进入导航选择页VC-导航页3D图

    相关代码:
    1.AppDelegate.m 中进行导航相关配置

    地图相关配置

    2.BBXRepDrivePickMapViewController.m 代驾接客地图导航按钮入口

    BBXRepDrivePickMapViewController.m 代驾接客地图导航按钮回调方法 BBXMapDriverGuiderManager中跳转高德导航页面,2种方式

    3.开始导航
    方法1:自定义导航控制器 BBXMapDriverGuideViewController

    1.BBXMapDriverGuideViewController.h
    #import <UIKit/UIKit.h>
    #import "BBXOrderViewModel.h"
    #import "BBXBusPassengerOrderModel.h"
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface BBXMapDriverGuideViewController : BBXBaseViewController
    
    @property (nonatomic, strong) BBXOrderViewModel *orderViewModel;
    @property (nonatomic, copy) void(^carTakeUPBlock)();//接客地图导航
    @property (nonatomic, copy) void(^carOffBlock)();
    @property (nonatomic, copy) void(^updatePhoneCallback)(void);
    @property (nonatomic, assign) BOOL isOrderListJump;//地图导航是否是从任务列表跳过来
    @property (nonatomic, assign) NSInteger viewTag;
    
    @end
    NS_ASSUME_NONNULL_END
    
    2.BBXMapDriverGuideViewController.m(重点看核心代码)
    #import "BBXMapDriverGuideViewController.h"
    #import <AMapNaviKit/AMapNaviKit.h>
    #import "BBXLocationManager.h"
    #import "BBXMapNavigationPopupView.h"
    #import "BBXOrderInfo.h"
    #import "BBXCallUtil.h"
    #import "BBXAudioManager.h"
    #import "AppDelegate+TCP.h"
    #import "BBXPassengerOrderDetail.h"
    #import "BBXMapDriverGuideManager.h"
    #import "BBXMapGuideViewModel.h"
    #import <CoreLocation/CLLocationManager.h>
    
    @interface BBXMapDriverGuideViewController ()<AMapNaviDriveManagerDelegate,AMapNaviDriveViewDelegate,AMapNaviDriveDataRepresentable,BBBXMapNavigationPopupViewDelegate, AMapNaviCompositeManagerDelegate>
    
    @property (nonatomic, strong) AMapNaviDriveView *driveView;
    @property (nonatomic, strong) AMapNaviCompositeManager *compositeManager;
    @property (nonatomic, strong) BBXMapNavigationPopupView *mapNavigationPopupView;//导航信息弹窗
    @property (nonatomic, strong) UIButton *exitMapButton;//退出导航按钮
    @property (nonatomic, strong) UILabel *routeInfoLab;
    @property (nonatomic, strong) AMapNaviPoint *endPoint;
    @property (nonatomic, assign) NSInteger messageType; //接收到的后台通知类型
    @property (nonatomic, strong) CLLocationManager *clManager;
    @property (nonatomic, strong) BBXMapGuideViewModel *mapGuideVM;
    @property (nonatomic, assign) BOOL isLoadAMap;//是否加载了高德地图
    
    @end
    
    @implementation BBXMapDriverGuideViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.messageType = -1;//初始化为无效值
        [self initDriveView];//创建导航地图页面
        [self initDriveManager];//导航相关类
    }
    
    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
        [self.navigationController.navigationBar setHidden:YES];
    }
    
    - (void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        [self.navigationController.navigationBar setHidden:YES];
        if (self.mapNavigationPopupView.superview == nil) {
            if(self.orderViewModel) {
                [self.view addSubview:self.mapNavigationPopupView];
                self.mapNavigationPopupView.orderViewModel = self.orderViewModel;
            }
        }
    }
    
    - (void)viewWillDisappear:(BOOL)animated {
        [super viewWillDisappear:animated];
        [self.navigationController.navigationBar setHidden:NO];
    }
    
    - (void)viewDidDisappear:(BOOL)animated {
        [super viewDidDisappear:animated];
        [[AMapNaviDriveManager sharedInstance] stopNavi];
        [[AMapNaviDriveManager sharedInstance] removeDataRepresentative:self.driveView];
        [[AMapNaviDriveManager sharedInstance] setDelegate:nil];
        BOOL success = [AMapNaviDriveManager destroyInstance];
        NSLog(@"单例是否销毁成功 : %d",success);
        if (self.mapNavigationPopupView) {
            [self.mapNavigationPopupView removeFromSuperview];
            self.mapNavigationPopupView = nil;
        }
    }
    
    #pragma mark -- 初始化页面、数据 (核心代码)
    //创建导航地图页面
    - (void)initDriveView {
        
        if (self.driveView == nil) {
            self.driveView = [[AMapNaviDriveView alloc] initWithFrame:self.view.bounds];
            self.driveView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
            self.driveView.showUIElements = YES;
            self.driveView.showBrowseRouteButton = NO;
            self.driveView.showGreyAfterPass = YES;
            self.driveView.showMode = AMapNaviDriveViewShowModeCarPositionLocked;
            self.driveView.trackingMode = AMapNaviViewTrackingModeCarNorth;
            self.driveView.autoSwitchShowModeToCarPositionLocked = YES;
            self.driveView.showMoreButton = YES;
            UIView *mapTopView = [self.driveView valueForKey:@"topInfoContainerView"];
            CGFloat y = mapTopView?CGRectGetMaxY(mapTopView.frame):145;
            CGFloat width = 240;
            self.routeInfoLab = [[UILabel alloc] initWithFrame:CGRectMake((SCREEN_WIDTH-width)*0.5, y, width, 40)];
            self.routeInfoLab.backgroundColor = HEXACOLOR(0x1F2B3C, 0.94);
            self.routeInfoLab.textColor = [UIColor whiteColor];
            self.routeInfoLab.font = [UIFont systemFontOfSize:16];
            self.routeInfoLab.layer.cornerRadius = 6.0;
            self.routeInfoLab.layer.masksToBounds = YES;
            self.routeInfoLab.textAlignment = NSTextAlignmentCenter;
            self.routeInfoLab.hidden = YES;
            [mapTopView.superview insertSubview:self.routeInfoLab atIndex:0];
            [self.driveView setDelegate:self];
            [[AMapNaviDriveManager sharedInstance] addDataRepresentative:self];
            [self.view addSubview:self.driveView];
            if(self.orderViewModel) {
                [self.view addSubview:self.mapNavigationPopupView];//添加地图导航弹窗
                self.routeInfoLab.hidden = NO;
                self.mapNavigationPopupView.orderViewModel = self.orderViewModel;
            }
        }
    }
    
    //导航相关类
    - (void)initDriveManager {
        
        [[AMapNaviDriveManager sharedInstance] setDelegate:self];
        [[AMapNaviDriveManager sharedInstance] setMultipleRouteNaviMode:YES];//多路线
        [AMapNaviDriveManager sharedInstance].isUseInternalTTS = YES;
        //将driveView添加为导航数据的Representative,使其可以接收到导航诱导数据
        [[AMapNaviDriveManager sharedInstance] addDataRepresentative:self.driveView];
        
        //导航起点终点经纬度
        CLLocationDegrees dest_lat = 0;//纬度
        CLLocationDegrees dest_lng = 0;//经度
        BBXOrderListModel *orderListModel = self.orderViewModel.orderModel;
        dest_lat = orderListModel.start_lat;
        dest_lng = orderListModel.start_lng;
        CLLocationCoordinate2D startCoor = [BBXLocationManager shareManager].location.coordinate;//导航起点
        CLLocationCoordinate2D endCoor = CLLocationCoordinate2DMake(dest_lat, dest_lng);//导航终点
        //将经纬度坐标转换为投影后的直角地理坐标
        MKMapPoint startPoint = MKMapPointForCoordinate(startCoor);
        MKMapPoint endPoint = MKMapPointForCoordinate(endCoor);
        self.endPoint = [AMapNaviPoint locationWithLatitude:dest_lat longitude:dest_lng];
        //计算指定两点之间的距离
        float distance= MKMetersBetweenMapPoints(startPoint,endPoint);
        if (distance < 100) {
            NSString *spekStr = @"距离太近,不能导航";
            [BBXAudioManager sharedAudioManager].spekType = @"0";
            [[AppDelegate shareAppDelegate] judgeVoicePlayWithModel:nil Speking:spekStr spekType:[BBXAudioManager sharedAudioManager].spekType];
            [BBXProgressHUD showInfo:spekStr];
            [BBXProgressHUD dismiss];
            [self.navigationController popViewControllerAnimated:YES];
            return;
        }
        CLLocationCoordinate2D gaodeStart = startCoor;
        CLLocationCoordinate2D gaodeEnd = endCoor;
        AMapNaviPOIInfo *startInfo = [[AMapNaviPOIInfo alloc] init];
        startInfo.locPoint = [AMapNaviPoint locationWithLatitude:gaodeStart.latitude longitude:gaodeStart.longitude];
        AMapNaviPOIInfo *endInfo = [[AMapNaviPOIInfo alloc] init];
        endInfo.locPoint = [AMapNaviPoint locationWithLatitude:gaodeEnd.latitude longitude:gaodeEnd.longitude];
        //根据高德POIInfo进行驾车路径规划发起算路
        if (distance >= 100*1000) {
            [[AMapNaviDriveManager sharedInstance] calculateDriveRouteWithStartPOIInfo:startInfo endPOIInfo:endInfo wayPOIInfos:nil drivingStrategy:AMapNaviDrivingStrategyMultipleShortestTimeDistance];
        } else {
            [[AMapNaviDriveManager sharedInstance] calculateDriveRouteWithStartPOIInfo:startInfo endPOIInfo:endInfo wayPOIInfos:nil drivingStrategy:AMapNaviDrivingStrategyMultipleDefault];
        }
    }
    
    #pragma mark -- 驾车导航管理类 AMapNaviDriveManagerDelegate (核心代码)
    //驾车路径规划成功后的回调函数
    - (void)driveManager:(AMapNaviDriveManager *)driveManager onCalculateRouteSuccessWithType:(AMapNaviRoutePlanType)type {
        
        //算路成功后开始GPS实时导航
        [[AMapNaviDriveManager sharedInstance] startGPSNavi];//注意:必须在路径规划成功的情况下,才能够开始实时导航
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            self.driveView.showUIElements = YES;
            UIView *laneInfoView = [self.driveView valueForKey:@"laneInfoView"];
            laneInfoView.y += 50;
            UIView *mapTopView = [self.driveView valueForKey:@"topInfoContainerView"];
            CGFloat y = mapTopView?CGRectGetMaxY(mapTopView.frame)+10:145;
            self.routeInfoLab.y = y;
            [self.view addSubview:self.exitMapButton];
        });
    }
    
    //驾车路径规划失败后的回调函数
    - (void)driveManager:(AMapNaviDriveManager *)driveManager onCalculateRouteFailure:(NSError *)error {
        if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [self initDriveManager];
            });
        } else {
            [BBXProgressHUD showErrorWithStatus:@"路径规划失败,请重新规划"];
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [self.navigationController popViewControllerAnimated:YES];
            });
        }
    }
    
    //导航信息更新回调
    - (void)driveManager:(AMapNaviDriveManager *)driveManager updateNaviInfo:(nullable AMapNaviInfo *)naviInfo{
        NSLog(@"%@", naviInfo);
        UIView *bottomInfoView = [self.driveView valueForKey:@"bottomInfoView"];
        UILabel *remainInfoLab = [bottomInfoView valueForKey:@"remainInfoLabelPor"];
        if (remainInfoLab) {
            NSString *text = remainInfoLab.text;
            NSInteger routeTrafficLights = naviInfo.routeRemainTrafficLightCount;
            NSArray *array = [text componentsSeparatedByString:@"\n"];
            if (array.count >= 1) {
                text = [array firstObject];
            }
            text = [text stringByReplacingOccurrencesOfString:@"剩余" withString:@""];
            text = [text stringByAppendingString:@"   "];
            NSMutableAttributedString *attri = [[NSMutableAttributedString alloc] init];
            NSTextAttachment *attch = [[NSTextAttachment alloc] init];
            attch.image = kImage(@"bbx_roundtraffic");
            NSAttributedString *imageText = [NSAttributedString attributedStringWithAttachment:attch];
            NSAttributedString *frontText = [[NSAttributedString alloc] initWithString:text];
            NSAttributedString *backText = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%ld", routeTrafficLights]];
            [attri appendAttributedString:frontText];
            [attri appendAttributedString:imageText];
            [attri appendAttributedString:backText];
            //text = [text stringByAppendingString:[NSString stringWithFormat:@"        🚥%ld个", routeTrafficLights]];
            //text = [text stringByReplacingOccurrencesOfString:@"\n" withString:[NSString stringWithFormat:@" 🚥%ld         ", (long)routeTrafficLights]];
            self.routeInfoLab.attributedText = attri;
        }
    }
    
    //导航界面更多按钮点击时的回调函数
    - (void)driveViewMoreButtonClicked:(AMapNaviDriveView *)driveView {
        AMapNaviCompositeUserConfig *userConfig = [[AMapNaviCompositeUserConfig alloc] init];
        BOOL ret = [userConfig setRoutePlanPOIType:AMapNaviRoutePlanPOITypeEnd location:self.endPoint name:@"北京" POIId:nil];
        [userConfig setStartNaviDirectly:YES];
        [userConfig setShowVoiceAssistEnable:YES];
        [self.compositeManager presentRoutePlanViewControllerWithOptions:userConfig];
    }
    
    //发生错误时,会调用此方法
    - (void)compositeManager:(AMapNaviCompositeManager *_Nonnull)compositeManager error:(NSError *_Nonnull)error {
        NSLog(@"error:%@", error);
    }
    
    #pragma mark AMapNaviDriveViewDelegate
    //导航界面关闭按钮点击时的回调函数
    - (void)driveViewCloseButtonClicked:(AMapNaviDriveView *)driveView{
        [self.navigationController popViewControllerAnimated:YES];
    }
    
    #pragma mark -- 底部订单面板信息view代理方法
    //右滑控件值变化的代理方法;orderModel:当前订单model
    - (void)mapNavigationPopupViewSliderValueChangingWith:(BBXOrderListModel *)orderModel {
        
        if (orderModel.order_status == Order_state_sended) {//已派单 -- 司机对指定订单执行上车操作
            if (self.isOrderListJump == YES) {//订单列表跳进来滑动底部按钮
                WeakSelf
                [self.mapGuideVM passengerGetOnWithOrderModel:orderModel completeBlock:^(NSDictionary * _Nonnull responseDict) {
                    StrongSelf
                    //公式计价并且乘客已上车,则刷新价格
                    if (orderModel.showInGongshijija) {
                        [BBXOrderInfo shareManager].priceBlock = ^(NSDictionary *priceDict) {
                            if (!self) return;
                            dispatch_async(dispatch_get_main_queue(), ^{
                                [self.orderViewModel refreshPrice];
                                self.mapNavigationPopupView.orderViewModel = weakSelf.orderViewModel;//更新数据
                            });
                        };
                    }
                    //改变终点(司机接到乘客后,导航终点变成乘客要去的那个点),重新导航
                    [self.navigationController popViewControllerAnimated:YES];
                }];
                /*
                __weak typeof(self)weakSelf = self;
                [[NetworkClient sharedInstance] passengerGetOnWithOrderModel:orderModel completeBlock:^(NSDictionary *responseDictionary) {
                    NSInteger status = [responseDictionary[@"status"] integerValue];
                    //网络请求失败
                    if (status != 0) {
                        NSString *message = responseDictionary[@"message"];
                        dispatch_async(dispatch_get_main_queue(), ^{
                            [SKToast showWithText:message];
                        });
                        return;
                    }
                    
                    //公式计价并且乘客已上车,则刷新价格
                    if (orderModel.showInGongshijija) {
                        [OrderInfo shareManager].priceBlock = ^(NSDictionary *priceDict) {
                            if (!weakSelf) return;
                            dispatch_async(dispatch_get_main_queue(), ^{
                                [weakSelf.orderViewModel refreshPrice];
                                weakSelf.mapNavigationPopupView.orderViewModel = weakSelf.orderViewModel;//更新数据
                            });
                        };
                    }
                    //改变终点(司机接到乘客后,导航终点变成乘客要去的那个点),重新导航
                    [self.navigationController popViewControllerAnimated:YES];
                }];*/
            } else {
                //接客地图-进入导航页里面滑动接客时候的回调
                [self.navigationController popViewControllerAnimated:YES];
                if (self.carTakeUPBlock) {
                    self.carTakeUPBlock();
                }
            }
        } else if (orderModel.order_status == Order_state_get_on) {//已上车 -- 退出导航
            [self.navigationController popViewControllerAnimated:YES];
            if (self.carOffBlock) {
                self.carOffBlock();
            }
        }
    }
    
    #pragma mark -- 按钮响应
    //退出导航
    - (void)exitMap {
        if (self.navigationController) {
            [self.navigationController popViewControllerAnimated:YES];
        } else {
            [self dismissViewControllerAnimated:YES completion:nil];
        }
    }
    
    #pragma mark -- 懒加载
    //导航组件类
    - (AMapNaviCompositeManager *)compositeManager {
        if (!_compositeManager) {
            _compositeManager = [[AMapNaviCompositeManager alloc] init];
            _compositeManager.delegate = self;//如果需要使用AMapNaviCompositeManagerDelegate的相关回调(如自定义语音、获取实时位置等),需要设置delegate
        }
        return _compositeManager;
    }
    
    //导航底部信息弹窗
    - (BBXMapNavigationPopupView *)mapNavigationPopupView {
        if (!_mapNavigationPopupView) {
            _mapNavigationPopupView = [[BBXMapNavigationPopupView alloc] initWithFrame:CGRectMake(0, ([UIScreen mainScreen].bounds.size.height - 190), [UIScreen mainScreen].bounds.size.width, 205)];
            _mapNavigationPopupView.delegate = self;
        }
        return _mapNavigationPopupView;
    }
    
    //退出导航按钮
    - (UIButton *)exitMapButton {
        if (!_exitMapButton) {
            UIImage * image = kImage(@"BBXMapNaviManager_exit");
            //获取状态栏的高度
            CGFloat statusHeight = [[UIApplication sharedApplication] statusBarFrame].size.height;
            CGFloat x = [UIScreen mainScreen].bounds.size.width - 15 - 50;
            CGFloat y = 30 + statusHeight;
            _exitMapButton = [UIButton buttonWithType:UIButtonTypeCustom];
            _exitMapButton.frame = CGRectMake(x, y, 50, 50);
            [_exitMapButton setImage:image forState:UIControlStateNormal];
            [_exitMapButton addTarget:self action:@selector(exitMap) forControlEvents:UIControlEventTouchUpInside];
        }
        return _exitMapButton;
    }
    
    //处理订单业务逻辑刷新等
    - (BBXMapGuideViewModel *)mapGuideVM {
        if (!_mapGuideVM) {
            _mapGuideVM = [[BBXMapGuideViewModel alloc]init];
            _mapGuideVM.orderRefresnCB = ^(BBXOrderListModel *newOrderModel) {
                self.orderViewModel = [[BBXOrderViewModel alloc] initWithOrderModel:newOrderModel];
                dispatch_async(dispatch_get_main_queue(), ^{
                    self.mapNavigationPopupView.orderViewModel = self.orderViewModel;//根据更新后的当前订单model信息,更新右滑控、地址信息、订单类型等界面信息
                });
            };
        }
        return _mapGuideVM;
    }
    
    - (void)dealloc {
        NSLog(@"%s", __FUNCTION__);
    //    [[AMapNaviDriveManager sharedInstance] stopNavi];
    //    [[AMapNaviDriveManager sharedInstance] removeDataRepresentative:self.driveView];
    //    [[AMapNaviDriveManager sharedInstance] setDelegate:nil];
    //    BOOL success = [AMapNaviDriveManager destroyInstance];
    //    NSLog(@"单例是否销毁成功 : %d",success);
    }
    
    @end
    

    方法2:先选择导航方案:自定义导航控制器 BBXMapCompositeViewController

    1.BBXMapCompositeViewController.h
    #import <Foundation/Foundation.h>
    #import "BBXOrderViewModel.h"
    #import "BBXMapOtherInfoModel.h"
    
    NS_ASSUME_NONNULL_BEGIN
    
    @class BBXBusPassengerOrderModel;
    
    @interface BBXMapCompositeViewController : NSObject
    
    @property (nonatomic, strong) BBXOrderViewModel *orderViewModel;
    @property (nonatomic, strong) BBXBusPassengerOrderModel *busflightModel;
    @property (nonatomic, strong) BBXMapOtherInfoModel *otherInfo;
    @property (nonatomic, assign) BOOL isOffStation; // 是否下车点位
    - (void)showGaodeCompositeVC;
    - (void)showGaodeCompositeFlightVC;
    
    @end
    NS_ASSUME_NONNULL_END
    
    2.BBXMapCompositeViewController.m
    #import "BBXMapCompositeViewController.h"
    #import <AMapNaviKit/AMapNaviKit.h>
    #import <ReactiveObjC/ReactiveObjC.h>
    #import "UIViewController+BBX.h"
    
    static AMapNaviCompositeManager *s_compositeManager;
    @interface BBXMapCompositeViewController ()<AMapNaviCompositeManagerDelegate>
    
    @property (nonatomic, strong) AMapNaviCompositeManager *compositeManager;
    
    @end
    
    @implementation BBXMapCompositeViewController
    
    - (void)showGaodeCompositeVC {
        if (!self.orderViewModel) {
            return;
        }
        BBXOrderListModel *orderListModel = self.orderViewModel.orderModel;
        AMapNaviPoint *endPoint = nil;
        NSString *destAddress = nil;
        if (orderListModel.order_status == Order_state_get_on) {
            CLLocationCoordinate2D endCoor = {orderListModel.end_lat, orderListModel.end_lng};
            endPoint = [AMapNaviPoint locationWithLatitude:endCoor.latitude longitude:endCoor.longitude];
            destAddress = orderListModel.endAddress;
        } else {
            CLLocationCoordinate2D endCoor = {orderListModel.start_lat, orderListModel.start_lng};
            endPoint = [AMapNaviPoint locationWithLatitude:endCoor.latitude longitude:endCoor.longitude];
            destAddress = orderListModel.startAddress;
        }
        AMapNaviCompositeUserConfig *userConfig = [[AMapNaviCompositeUserConfig alloc] init];
        //设置起点、终点、途径点(最多支持三个途径点)来调起路线规划页面,自动完成算路. 如果不设置起点,默认使用的是“我的位置”
        [userConfig setRoutePlanPOIType:AMapNaviRoutePlanPOITypeEnd location:endPoint name:destAddress POIId:nil];
        [userConfig setShowVoiceAssistEnable:YES];
        [self.compositeManager presentRoutePlanViewControllerWithOptions:userConfig];//导航组件页面
    }
    
    - (AMapNaviCompositeManager *)compositeManager {
        if (!_compositeManager) {
            _compositeManager = [[AMapNaviCompositeManager alloc] init];
            _compositeManager.delegate = self;//如果需要使用AMapNaviCompositeManagerDelegate的相关回调(如自定义语音、获取实时位置等),需要设置delegate
    //        s_compositeManager = _compositeManager;
        }
        return _compositeManager;
    }
    
    //退出导航(退到上一个页面即选择导航方案页面)
    - (void)compositeManager:(AMapNaviCompositeManager *_Nonnull)compositeManager didBackwardAction:(AMapNaviCompositeVCBackwardActionType)backwardActionType {
        if (self.otherInfo.backEventCB) {
            self.otherInfo.backEventCB();
        }
    }
    
    @end
    

    9.地图根据经纬度进行逆编码

    逆编码:即逆地理编码也叫反编码,指的是将经纬度转换成地理位置信息,如地名、所在的省份或城市等,地图提供了相应的API可以方便调用。
    相关链接:https://blog.csdn.net/chongran9230/article/details/100804174

    正地理编码:也直接叫地理编码,指的是将地理位置名称转换成经纬度。

    10.地图SDK导入工程及plist权限添加

    高德地图相关SDK:地图、导航、定位、鹰眼 高德地图使用相关权限

    判断手机App地图定位权限是否有打开:

    举例:如果App定位权限不是“始终”,则提醒打开
    - (void)jugdeIsOpenAlwaysLocation {
        
        //如果用户选择的定位权限不是“始终”,则弹窗让用户修改权限
        if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedAlways) {
            UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"定位权限请选择“始终”,否则服务无法使用!" preferredStyle:UIAlertControllerStyleAlert];
            UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                [self.navigationController popToRootViewControllerAnimated:YES];
            }];
            UIAlertAction * okAction = [UIAlertAction actionWithTitle:@"前往开启" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
            }];
            [alertController addAction:cancelAction];
            [alertController addAction:okAction];
            [self presentViewController:alertController animated:YES completion:nil];
            return;
        }
    }
    

    11.集中简单看下关键类以及方法

    相关链接:https://www.jianshu.com/p/9102ca8e72ca

    相关文章

      网友评论

          本文标题:高德地图开发相关总结

          本文链接:https://www.haomeiwen.com/subject/cayeirtx.html