iOS百度地圖鷹眼軌跡,繪制軌跡點(diǎn),繪制路線,規(guī)劃路線,地理編碼,坐標(biāo)距離計(jì)算,地理檢索,根據(jù)坐標(biāo)更改車輛角度等最詳細(xì)功能

嗨,我已經(jīng)很久沒更新文章啦,最近比較忙。然后我們之前做個(gè)一個(gè)項(xiàng)目里的功能包含了百度地圖里大部分功能啦,下面我放出大部分的功能。

先說鷹眼軌跡,個(gè)人認(rèn)為鷹眼軌跡是最難的,因?yàn)殛P(guān)系的東西比較多,我不知道你們的鷹眼軌跡是哪種,一般是車載硬件將地理位置發(fā)送給百度地圖的,你要根據(jù)EntityName來請百度地圖返回給你的數(shù)據(jù),然后將返回給你的數(shù)據(jù)拿到繪制軌跡點(diǎn)。

而這里的軌跡點(diǎn)分為兩種:歷史軌跡+實(shí)施軌跡

歷史軌跡:只有1種:
1.獲取所有數(shù)據(jù),一次性展示出來。這里屬于歷史軌跡點(diǎn)。

實(shí)時(shí)軌跡:分為2種:
1.一種定時(shí)器反復(fù)請求歷史軌跡,每次都將重新繪制軌跡,個(gè)人感覺無縫連接。
2.一種是實(shí)時(shí)請求軌跡,每次都將最新的軌跡點(diǎn)存入到數(shù)組里,當(dāng)然還是需要定時(shí)器的,但是方法不同。(這里需要注意的是:網(wǎng)絡(luò)不好或者沒有拿到新的數(shù)據(jù)的話,你的軌跡點(diǎn)繪制會(huì)很丑,你需要將定時(shí)器速度加快)。

但經(jīng)過我的反復(fù)測試,除非你一直請求, 否則第2條你注定會(huì)出現(xiàn)偏差,比如車開出路線,在道路外面跑,軌跡線連接有縫隙等等一堆問題。

GYHistoryTrackParam.h

#import <Foundation/Foundation.h>

@interface GYHistoryTrackParam : NSObject

@property (nonatomic, copy) NSString *entityName;
@property (nonatomic, assign) NSUInteger startTime;
@property (nonatomic, assign) NSUInteger endTime;
@property (nonatomic, assign) BOOL isProcessed;
@property (nonatomic, strong) BTKQueryTrackProcessOption *processOption;
@property (nonatomic, assign) BTKTrackProcessOptionSupplementMode supplementMode;

@end
GYHistoryTrackParam.m

#import "GYHistoryTrackParam.h"

@implementation GYHistoryTrackParam

@end
#import <Foundation/Foundation.h>
#import "GYHistoryTrackParam.h"

typedef void (^HistoryQueryCompletionHandler) (NSArray *points);

@interface GYHistoryViewModel : NSObject <BTKTrackDelegate>

@property (nonatomic, copy) HistoryQueryCompletionHandler completionHandler;

- (void)queryHistoryWithParam:(GYHistoryTrackParam *)param;

@end
#import <Foundation/Foundation.h>

@interface GYHistoryTrackPoint : NSObject

@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomic, assign) NSUInteger loctime;
@property (nonatomic, assign) NSUInteger direction;
@property (nonatomic, assign) double speed;
#import "GYHistoryTrackPoint.h"

@implementation GYHistoryTrackPoint

@end

#import "GYHistoryViewModel.h"
#import "GYHistoryTrackPoint.h"

@interface GYHistoryViewModel ()

@property (nonatomic, strong) NSMutableArray *points;
@property (nonatomic, strong) dispatch_group_t historyDispatchGroup;
@property (nonatomic, assign) NSUInteger firstResponseSize;
@property (nonatomic, assign) NSUInteger total;
@property (nonatomic, strong) GYHistoryTrackParam *param;

@end

// 軌跡查詢每頁的上限為5000條,但是若請求糾偏甚至綁路后的軌跡,比較耗時(shí)。綜合考慮,我們選取每頁1000條
static NSUInteger const kHistoryTrackPageSize = 1000;

@implementation GYHistoryViewModel

-(instancetype)init {
    self = [super init];
    if (self) {
        _points = [NSMutableArray array];
        _historyDispatchGroup = dispatch_group_create();
    }
    return self;
}

- (void)queryHistoryWithParam:(GYHistoryTrackParam *)param {
    // 清空已有數(shù)據(jù)
    [self.points removeAllObjects];
    
    self.param = param;
    // 發(fā)送第一次請求,確定size和total,以決定后續(xù)是否還需要發(fā)請求,以及發(fā)送請求的pageSize和pageIndex
    dispatch_async(GLOBAL_QUEUE, ^{
        BTKQueryHistoryTrackRequest *request = [[BTKQueryHistoryTrackRequest alloc] initWithEntityName:param.entityName startTime:param.startTime endTime:param.endTime isProcessed:param.isProcessed processOption:param.processOption supplementMode:param.supplementMode outputCoordType:BTK_COORDTYPE_BD09LL sortType:BTK_TRACK_SORT_TYPE_ASC pageIndex:1 pageSize:kHistoryTrackPageSize serviceID:serviceID tag:1];
        [[BTKTrackAction sharedInstance] queryHistoryTrackWith:request delegate:self];
    });
}

-(void)onQueryHistoryTrack:(NSData *)response {
    
    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:response options:NSJSONReadingAllowFragments error:nil];
    
    
    NSLog(@"%@",dict);
    
    if (nil == dict) {
        NSLog(@"HISTORY TRACK查詢格式轉(zhuǎn)換出錯(cuò)");
        return;
    }
    if (0 != [dict[@"status"] intValue]) {
        NSLog(@"HISTORY TRACK查詢返回錯(cuò)誤");
        return;
    }
    
    // 每次回調(diào)得到的歷史軌跡點(diǎn),都加到數(shù)組中
    for (NSDictionary *point in dict[@"points"]) {
        
        
        
        GYHistoryTrackPoint *p = [[GYHistoryTrackPoint alloc] init];
        p.coordinate = CLLocationCoordinate2DMake([point[@"latitude"] doubleValue], [point[@"longitude"] doubleValue]);
        p.loctime = [point[@"loc_time"] unsignedIntValue];
        p.direction = [point[@"direction"] unsignedIntValue];
        p.speed = [point[@"speed"] doubleValue];
        

        
        [self.points addObject:p];
        
    }
    
    // 對于第一次回調(diào)結(jié)果,根據(jù)total和size,確認(rèn)還需要發(fā)送幾次請求,并行發(fā)送這些請求
    // 使用dispatch_group同步多次請求,當(dāng)所有請求均收到響應(yīng)后,points屬性中存儲的就是所有的軌跡點(diǎn)了
    // 根據(jù)請求的是否是糾偏后的結(jié)果,處理方式稍有不同:
    // 若是糾偏后的軌跡,直接使用direction字段即可,該方向是準(zhǔn)確的;
    // 若是原始的軌跡,direction字段可能不準(zhǔn),我們自己根據(jù)相鄰點(diǎn)計(jì)算出方向。
    // 最好都是請求糾偏后的軌跡,簡化客戶端處理步驟
    if ([dict[@"tag"] unsignedIntValue] == 1) {
        self.firstResponseSize = [dict[@"size"] unsignedIntValue];
        self.total = [dict[@"total"] unsignedIntValue];
        for (size_t i = 0; i < self.total / self.firstResponseSize; i++) {
            dispatch_group_enter(self.historyDispatchGroup);
            
            BTKQueryHistoryTrackRequest *request = [[BTKQueryHistoryTrackRequest alloc] initWithEntityName:self.param.entityName startTime:self.param.startTime endTime:self.param.endTime isProcessed:self.param.isProcessed processOption:self.param.processOption supplementMode:self.param.supplementMode outputCoordType:BTK_COORDTYPE_BD09LL sortType:BTK_TRACK_SORT_TYPE_ASC pageIndex:(2 + i) pageSize:kHistoryTrackPageSize serviceID:serviceID tag:(2 + i)];
            [[BTKTrackAction sharedInstance] queryHistoryTrackWith:request delegate:self];
        }
        dispatch_group_notify(self.historyDispatchGroup, GLOBAL_QUEUE, ^{
            // 將所有查詢到的軌跡點(diǎn),按照loc_time升序排列,注意是穩(wěn)定排序。
            // 因?yàn)榻壜窌r(shí)會(huì)補(bǔ)充道路形狀點(diǎn),其loc_time與原始軌跡點(diǎn)一樣,相同的loc_time在排序后必須保持原始的順序,否則direction不準(zhǔn)。
            [self.points sortWithOptions:NSSortStable usingComparator:^NSComparisonResult(GYHistoryTrackPoint *  _Nonnull obj1, GYHistoryTrackPoint *  _Nonnull obj2) {
                if (obj1.loctime < obj2.loctime) {
                    return NSOrderedAscending;
                } else if (obj1.loctime > obj2.loctime) {
                    return NSOrderedDescending;
                } else {
                    return NSOrderedSame;
                }
            }];
            // 如果我們請求的是原始軌跡,最好自己計(jì)算每個(gè)軌跡點(diǎn)的方向,因?yàn)榇藭r(shí)返回的direction字段可能不準(zhǔn)確。
            if (FALSE == self.param.isProcessed) {
                // 根據(jù)相鄰兩點(diǎn)之間的坐標(biāo),計(jì)算方向
                for (size_t i = 0; i < self.points.count - 1; i++) {
                    GYHistoryTrackPoint *point1 = (GYHistoryTrackPoint *)self.points[i];
                    GYHistoryTrackPoint *point2 = (GYHistoryTrackPoint *)self.points[i + 1];
                    double lat1 = [self getRadianFromDegree:point1.coordinate.latitude];
                    double lon1 = [self getRadianFromDegree:point1.coordinate.longitude];
                    double lat2 = [self getRadianFromDegree:point2.coordinate.latitude];
                    double lon2 = [self getRadianFromDegree:point2.coordinate.longitude];
                    double deltaOfLon = lon2 - lon1;
                    double y = sin(deltaOfLon) * cos(lat2);
                    double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(deltaOfLon);
                    double radianDirection = atan2(y, x);
                    double degreeDirection = [self getDegreeFromRadian:radianDirection];
                    ((GYHistoryTrackPoint *)self.points[i]).direction = [self mapGeometryDirectionToGPSDirection:degreeDirection];
                }
            }
            if (self.completionHandler) {
                self.completionHandler(self.points);
            }
        });
    } else {
        dispatch_group_leave(self.historyDispatchGroup);
    }
}

- (double)getRadianFromDegree:(double)degree {
    return degree * M_PI / 180.0;
}

- (double)getDegreeFromRadian:(double)radian {
    return radian * 180.0 / M_PI;
}

- (double)mapGeometryDirectionToGPSDirection:(double)geometryDirection {
    double gpsDirection = geometryDirection;
    if (geometryDirection < 0) {
        gpsDirection = geometryDirection + 360.0;
    }
    return gpsDirection;
}
viewController.m
#import "viewController..h"


-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    self.navigationController.navigationBarHidden =YES;
    
    [self.mapView viewWillAppear];
    self.mapView.delegate = self;

}

-(void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    self.navigationController.navigationBarHidden =NO;

    [self.mapView viewWillDisappear];
    self.mapView.delegate = nil;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.mapView];
    
    
    
    GYHistoryTrackParam * paramInfo =[[GYHistoryTrackParam alloc]init];
    [paramInfo setStartTime:self.startTime];
    [paramInfo setEndTime:self.endTime];
    [paramInfo setEntityName:self.entityName];
    [paramInfo setIsProcessed:FALSE];
    BTKQueryTrackProcessOption *processOption = [[BTKQueryTrackProcessOption alloc] init];
    processOption.radiusThreshold = 0;
    processOption.transportMode = BTK_TRACK_PROCESS_OPTION_TRANSPORT_MODE_DRIVING;
    paramInfo.processOption = processOption;
    paramInfo.supplementMode = BTK_TRACK_PROCESS_OPTION_NO_SUPPLEMENT;
    
    //下面可以單獨(dú)提出來,用作定時(shí)器訪問
    
    GYHistoryViewModel *vm = [[GYHistoryViewModel alloc] init];
    vm.completionHandler = ^(NSArray *points) {
        self.historyPoints = points;
        [self drawHistoryTrackWithPoints:points];
    };
    [vm queryHistoryWithParam:paramInfo];
}
-(BMKMapView *)mapView {
   
    if (!_mapView) {
        
        _mapView = [[BMKMapView alloc] init];

        if (WZIsPhoneX)
        {
            _mapView.frame =CGRectMake(0, 0, ScreenWidth, ScreenHeight-20);

        }else
        {
            _mapView.frame =CGRectMake(0, 0, ScreenWidth, ScreenHeight);
        }
        _mapView.zoomLevel = 19;
        _mapView.gesturesEnabled= YES;
        _mapView.delegate= self;
        [_mapView setMapType:BMKMapTypeStandard];
    }
    return _mapView;
}

到這就可以開始寫代理方法了,來接收返回的數(shù)據(jù)并繪制軌跡點(diǎn)。

- (void)drawHistoryTrackWithPoints:(NSArray *)points {
    // line代表軌跡
    
    NSLog(@"%@",points);
    
    CLLocationCoordinate2D coors[points.count];
    NSInteger count = 0;
    for (size_t i = 0; i < points.count; i++) {
        CLLocationCoordinate2D p = ((GYHistoryTrackPoint *)points[i]).coordinate;
        if (fabs(p.latitude) < EPSILON || fabs(p.longitude) < EPSILON) {
            continue;
        }
        count++;
        coors[i] = ((GYHistoryTrackPoint *)points[i]).coordinate;
    }
    BMKPolyline *line = [BMKPolyline polylineWithCoordinates:coors count:count];
    // 起點(diǎn)annotation
    BMKPointAnnotation *startAnnotation = [[BMKPointAnnotation alloc] init];
    startAnnotation.coordinate = coors[0];
    startAnnotation.title = kStartPositionTitle;
    // 終點(diǎn)annotation
    BMKPointAnnotation *endAnnotation = [[BMKPointAnnotation alloc] init];
    endAnnotation.coordinate = coors[count - 1];
    endAnnotation.title = kEndPositionTitle;
    
    dispatch_async(MAIN_QUEUE, ^{
        [self.mapView removeOverlays:self.mapView.overlays];
        [self.mapView removeAnnotations:self.mapView.annotations];
        [self mapViewFitForCoordinates:points];
        [self.mapView addOverlay:line];
        [self.mapView addAnnotation:startAnnotation];
        [self.mapView addAnnotation:endAnnotation];
    });
}
//設(shè)置起點(diǎn)圖片,和最后一個(gè)點(diǎn)的位置
-(BMKAnnotationView *)mapView:(BMKMapView *)mapView viewForAnnotation:(id<BMKAnnotation>)annotation {
    BMKAnnotationView *view = nil;
    if ([annotation.title isEqualToString:kStartPositionTitle]) {
        static NSString *historyTrackStartPositionAnnotationViewID = @"historyTrackStartPositionAnnotationViewID";
        view = [mapView dequeueReusableAnnotationViewWithIdentifier:historyTrackStartPositionAnnotationViewID];
        if (view == nil) {
            view = [[BMKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:historyTrackStartPositionAnnotationViewID];
            view.image = [UIImage imageNamed:@"icon_start"];
        }
    } else if ([annotation.title isEqualToString:kEndPositionTitle]) {
        static NSString *historyTrackEndPositionAnnotationViewID = @"historyTrackEndPositionAnnotationViewID";
        view = [mapView dequeueReusableAnnotationViewWithIdentifier:historyTrackEndPositionAnnotationViewID];
        if (view == nil) {
            view = [[BMKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:historyTrackEndPositionAnnotationViewID];
            view.image = [UIImage imageNamed:@"icon_end"];
        }
    } else if ([annotation.title isEqualToString:kArrowTitle]) {
        static NSString *historyTrackArrorAnnotationViewID = @"historyTrackArrorAnnotationViewID";
        view = [mapView dequeueReusableAnnotationViewWithIdentifier:historyTrackArrorAnnotationViewID];
        if (view == nil) {
            self.arrowAnnotationView = [[GYArrowAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:historyTrackArrorAnnotationViewID];
            self.arrowAnnotationView.imageView.transform = CGAffineTransformMakeRotation(((GYHistoryTrackPoint *)[self.historyPoints firstObject]).direction);
            view = self.arrowAnnotationView;
        }
    }
    return view;
}

//下面就是軌跡線的粗細(xì)顏色
-(BMKOverlayView *)mapView:(BMKMapView *)mapView viewForOverlay:(id<BMKOverlay>)overlay {
    
    
    if ([overlay isKindOfClass:[BMKPolyline class]]) {
        BMKPolylineView* polylineView = [[BMKPolylineView alloc] initWithOverlay:overlay];
        polylineView.strokeColor = BlueColor;
        polylineView.lineWidth = 2.0;
        return polylineView;
    }
    return nil;
}
-(void)mapViewFitForCoordinates:(NSArray *)points {
    double minLat = 90.0;
    double maxLat = -90.0;
    double minLon = 180.0;
    double maxLon = -180.0;
    for (size_t i = 0; i < points.count; i++) {
        minLat = fmin(minLat, ((GYHistoryTrackPoint *)points[i]).coordinate.latitude);
        maxLat = fmax(maxLat, ((GYHistoryTrackPoint *)points[i]).coordinate.latitude);
        minLon = fmin(minLon, ((GYHistoryTrackPoint *)points[i]).coordinate.longitude);
        maxLon = fmax(maxLon, ((GYHistoryTrackPoint *)points[i]).coordinate.longitude);
    }
    CLLocationCoordinate2D center = CLLocationCoordinate2DMake((minLat + maxLat) * 0.5, (minLon + maxLon) * 0.5);
    BMKCoordinateSpan span;
    span.latitudeDelta = 1.2*((maxLat - minLat) + 0.01);
    span.longitudeDelta = 1.2 *((maxLon - minLon) + 0.01);
    BMKCoordinateRegion region;
    region.center = center;
    region.span = span;
    [self.mapView setRegion:region animated:YES];
}


以上就是歷史軌跡的方法繪制出來的如下圖:


WechatIMG1.jpeg

接下來是角度的問題,如何根據(jù)坐標(biāo)來改變車輛的角度,讓車一直動(dòng)起來。我的思路是使用歷史軌跡,每隔5秒請求上面的歷史軌跡,雖然這樣會(huì)慢于開車的人,但是還是很準(zhǔn)的,你也可以更快點(diǎn),只要百度有用戶的位置那就能加載出來。 請求到的最后一個(gè)和倒數(shù)第二個(gè)坐標(biāo)就是車應(yīng)該變的角度。

//這個(gè)就是計(jì)算2個(gè)坐標(biāo)之間的角度

-(double)getBearingWithLat1:(double)lat1 whitLng1:(double)lng1 whitLat2:(double)lat2 whitLng2:(double)lng2
{
    
    double d = 0;
    double radLat1 = [self radian:lat1];
    double radLat2 = [self radian:lat2];
    double radLng1 = [self radian:lng1];
    double radLng2 = [self radian:lng2];
    
    d = sin(radLat1)*sin(radLat2)+cos(radLat1)*cos(radLat2)*cos(radLng2-radLng1);
    d = sqrt(1-d*d);
    d = cos(radLat2)*sin(radLng2-radLng1)/d;
    d =  [self angle:asin(d)];
    
    return d;
    
}
-(double)radian:(double)d{
    
    return d * M_PI/180.0;
}
-(double)angle:(double)r{
    
    return r * 180/M_PI;

}

那么怎么用呢,剛才上面也用到了下面這個(gè)方法

-(BMKAnnotationView *)mapView:(BMKMapView *)mapView viewForAnnotation:(id<BMKAnnotation>)annotation {

這個(gè)方法我們需要調(diào)整一下,上面一開始的開始和結(jié)束點(diǎn)就是固定,那就是固定的歷史軌跡,如果你想要車能動(dòng),那就需要更新 AnnotationView

-(BMKAnnotationView *)mapView:(BMKMapView *)mapView viewForAnnotation:(id<BMKAnnotation>)annotation {
    
    
    if ([annotation.title isEqualToString:kStartPositionTitle]) {
        static NSString *historyTrackStartPositionAnnotationViewID = @"historyTrackStartPositionAnnotationViewID";
        BMKAnnotationView * startAnnotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:historyTrackStartPositionAnnotationViewID];
        if (startAnnotationView == nil) {
            startAnnotationView = [[BMKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:historyTrackStartPositionAnnotationViewID];

            startAnnotationView.image = [UIImage imageNamed:@"icon_start"];
        }
        return startAnnotationView;
        
    }
    else if ([annotation.title isEqualToString:kEndPositionTitle])
    {
        if ([annotation isKindOfClass:[RouteAnnotation class]]) {
            return [self getRouteAnnotationView:mapView viewForAnnotation:(RouteAnnotation *)annotation];
        }
        
        static NSString *historyTrackStartPositionAnnotationViewID = @"endID";
        BMKAnnotationView * startAnnotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:historyTrackStartPositionAnnotationViewID];
        if (startAnnotationView == nil) {
            startAnnotationView = [[BMKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:historyTrackStartPositionAnnotationViewID];
        }
//        startAnnotationView.image = [UIImage imageNamed:@"MapCar_1"];

        UIImage *image = [UIImage imageNamed:@"MapCar"];
        
//is_travel這里是指判斷車當(dāng)前是停止還是在開
        if ([[_dataDic objectForKey:@"is_travel"]integerValue]==1)
        {
            
            startAnnotationView.image = image;
            
            double ag = [self getBearingWithLat1:_daoshudier.latitude whitLng1:_daoshudier.longitude whitLat2:_daoshudiyi.latitude whitLng2:_daoshudiyi.longitude];
            
            double ag2 = [self getAngleSPt:_daoshudier endPt:_daoshudiyi];
                        
            NSLog(@"ag------%f----%f",ag, ag2);
            
//這里就是車子角度變化的代碼
            startAnnotationView.image = [image imageRotatedByDegrees:ag-180];

            
        }
        else
        {
//如果停止就換一個(gè)灰色的小汽車圖片
            startAnnotationView.image =[UIImage imageNamed:@"MapCar_1"];

        }
        
        
        startAnnotationView.annotation = annotation;
        

        return startAnnotationView;
        
    }else 
    {
        if ([annotation isKindOfClass:[RouteAnnotation class]]) {
            return [self getRouteAnnotationView:mapView viewForAnnotation:(RouteAnnotation *)annotation];
        }
        
        NSString *AnnotationViewID = @"datouzhen";
        
        // 檢查是否有重用的緩存
        BMKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationViewID];
        
        // 緩存沒有命中,自己構(gòu)造一個(gè),一般首次添加annotation代碼會(huì)運(yùn)行到此處
        if (annotationView == nil) {
            annotationView = [[BMKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationViewID];
            ((BMKPinAnnotationView*)annotationView).animatesDrop = NO;
        }
        // 設(shè)置位置
        annotationView.centerOffset = CGPointMake(0, -(annotationView.frame.size.height  *0.5));
        annotationView.annotation = annotation;
        // 單擊彈出泡泡,彈出泡泡前提annotation必須實(shí)現(xiàn)title屬性
        annotationView.canShowCallout = YES;
        // 設(shè)置是否可以拖拽
        annotationView.draggable = NO;
        
    }
    
    return nil;
    
}
#pragma mark -- 獲取路線的標(biāo)注,顯示到地圖(自定義的一個(gè)大頭針類實(shí)例方法)
- (BMKAnnotationView *)getRouteAnnotationView:(BMKMapView *)mapview viewForAnnotation:(RouteAnnotation *)routeAnnotation {
    BMKAnnotationView *view = nil;
    //根據(jù)大頭針類型判斷是什么圖標(biāo)
    switch (routeAnnotation.type) {
        case 0:
        {   //開始點(diǎn)
            view = [mapview dequeueReusableAnnotationViewWithIdentifier:@"start_node"];
            if (view == nil) {
                view = [[BMKAnnotationView alloc] initWithAnnotation:routeAnnotation reuseIdentifier:@"start_node"];
                //從百度地圖資源文件中拿到需要的圖片
                view.image = [UIImage imageNamed:@"icon_start"];
                view.centerOffset = CGPointMake(0, -(view.frame.size.height * 0.5));
                view.canShowCallout = true;
            }
            view.annotation = routeAnnotation;
        }
            break;
        case 1:
        {   //結(jié)束點(diǎn)
            view = [mapview dequeueReusableAnnotationViewWithIdentifier:@"car_node"];
            if (view == nil)
            {
                view = [[BMKAnnotationView alloc]initWithAnnotation:routeAnnotation reuseIdentifier:@"car_node"];
            }
            else
            {
                [view setNeedsDisplay];
            }
            
            if ([[_dataDic objectForKey:@"is_travel"]integerValue]==1)
            {
                UIImage *image = [UIImage imageNamed:@"MapCar"];
                view.image = image;
            }else
            {
                UIImage *image = [UIImage imageNamed:@"MapCar_1"];
                view.image = image;

            }
            
            
            
            view.annotation = routeAnnotation;
            
            return  view;

        }
            break;
            
        case 2:
        {   //路線點(diǎn)
            view = [mapview dequeueReusableAnnotationViewWithIdentifier:@"drive_node"];
            if (view == nil) {
                view = [[BMKAnnotationView alloc] initWithAnnotation:routeAnnotation reuseIdentifier:@"drive_node"];
                view.canShowCallout = true;
            } else {
                [view setNeedsDisplay];
            }
            UIImage *image = [UIImage imageWithContentsOfFile:[self getMyBundlePath1:@"images/icon_direction"]];
            view.image = image;
            //旋轉(zhuǎn)圖片
            view.image = [image imageRotatedByDegrees:routeAnnotation.degree];
            view.annotation = routeAnnotation;
        }
            break;
        default:
            break;
    }
    return view;
}

下面就是實(shí)現(xiàn)后的樣式


WechatIMG2.jpeg

接下來是路徑規(guī)劃

首先我們需要拿到定位,然后和需要去的位置計(jì)算。

// start2D 傳定位位置或者起始點(diǎn)位置
- (void)routePlanningWithStartLocation:(CLLocationCoordinate2D )start2D
{
    _routeSearch = [[BMKRouteSearch alloc] init];
    //設(shè)置delegate,用于接收檢索結(jié)果
    _routeSearch.delegate = self;
    //構(gòu)造步行查詢基礎(chǔ)信息類
    
    BMKPlanNode* start = [[BMKPlanNode alloc] init];
    BMKPlanNode* end = [[BMKPlanNode alloc] init];
    
    
    
    NSDictionary *  vehicle =[_vpDict objectForKey:@"vehicle"];
    
// 這里是車輛位置,不是定位位置

    double vLatitude =[[vehicle objectForKey:@"latitude"]doubleValue];
    double vLongitude =[[vehicle objectForKey:@"longitude"]doubleValue];
    
    
    CLLocationCoordinate2D end2D = CLLocationCoordinate2DMake(vLatitude, vLongitude);
    
    start.pt = start2D;
    
    end.pt =end2D;
    
    double distance =[[_vpDict objectForKey:@"distance"]doubleValue];
    
// 如果距離小于1000米
    if (distance < 1)
    {
        _walkingRouteSearchOption = [[BMKWalkingRoutePlanOption alloc] init];
        
        _walkingRouteSearchOption.from = start;
        
        _walkingRouteSearchOption.to = end;
        
        BOOL flag = [_routeSearch walkingSearch:_walkingRouteSearchOption];
        if (flag) {
            
            NSLog(@"walk檢索發(fā)送成功");
        } else{
            NSLog(@"walk檢索發(fā)送失敗");
            [MBProgressHUD showHUDMsg:@"無法為您規(guī)劃步行路線"];
        }
        
    }else
    {
        BMKDrivingRoutePlanOption *driveRouteSearchOption =[[BMKDrivingRoutePlanOption alloc]init];
        
        driveRouteSearchOption.from = start;
        
        driveRouteSearchOption.to = end;
        
        BOOL flag = [_routeSearch drivingSearch:driveRouteSearchOption];
        if (flag) {
            NSLog(@"駕車檢索發(fā)送成功");
            
        }else {
            
            NSLog(@"駕車檢索發(fā)送失敗");
            [MBProgressHUD showHUDMsg:@"無法為您規(guī)劃駕車路線"];
        }
        
    }
    
}
-(void)routePlanDidUserCanceled:(NSDictionary*)userInfo
{
    NSLog(@"算路取消");
}
//超過5kM 以內(nèi)按照駕車規(guī)劃路線
- (void)onGetDrivingRouteResult:(BMKRouteSearch *)searcher result:(BMKDrivingRouteResult *)result errorCode:(BMKSearchErrorCode)error {
    
    
    _carAndMyLoactionDistance = 0;

    if (_isChooseDistance==YES)
    {
        [_mapView removeOverlays:_mapView.overlays];
        [_mapView removeAnnotations:_mapView.annotations];
        
        
        
        if (error == BMK_SEARCH_NO_ERROR) {
            //表示一條駕車路線
            BMKDrivingRouteLine *plan = (BMKDrivingRouteLine *)[result.routes objectAtIndex:0];
            // 計(jì)算路線方案中的路段數(shù)目
            int size = (int)[plan.steps count];
            int planPointCounts = 0;
            for (int i = 0; i < size; i++) {
                //表示駕車路線中的一個(gè)路段
                BMKDrivingStep *transitStep = [plan.steps objectAtIndex:i];
                if(i==0){
                    RouteAnnotation *item = [[RouteAnnotation alloc]init];
                    item.coordinate = plan.starting.location;
                    item.title = @"起點(diǎn)";
                    item.type = 0;
                    [self.mapView addAnnotation:item]; // 添加起點(diǎn)標(biāo)注
                    
                }else if(i==size-1){
                    RouteAnnotation *item = [[RouteAnnotation alloc]init];
                    item.coordinate = plan.terminal.location;
                    item.title = kEndPositionTitle;
                    item.type = 1;
                    [self.mapView addAnnotation:item]; // 添加終點(diǎn)標(biāo)注
                    
                }
                //添加annotation節(jié)點(diǎn)
                RouteAnnotation *item = [[RouteAnnotation alloc]init];
                item.coordinate = transitStep.entrace.location;
                item.title = transitStep.entraceInstruction;
                item.degree = transitStep.direction  *30;
                item.type = 2;
                [self.mapView addAnnotation:item];
                
                //軌跡點(diǎn)總數(shù)累計(jì)
                planPointCounts += transitStep.pointsCount;
            }
            //軌跡點(diǎn)
            BMKMapPoint *temppoints = new BMKMapPoint[planPointCounts];
            
            int i = 0;
            for (int j = 0; j < size; j++) {
                BMKDrivingStep *transitStep = [plan.steps objectAtIndex:j];
                
                int k=0;
                for(k=0;k<transitStep.pointsCount;k++) {
                    temppoints[i].x = transitStep.points[k].x;
                    temppoints[i].y = transitStep.points[k].y;
                    i++;
                }
                
            }
            
            
            // 通過points構(gòu)建BMKPolyline
            BMKPolyline *polyLine = [BMKPolyline polylineWithPoints:temppoints count:planPointCounts];
            
            
            [self.mapView addOverlay:polyLine]; // 添加路線overlay
            delete []temppoints;
            [self mapViewFitPolyLine:polyLine];
            

        }

    }else
    {
        
        if (error == BMK_SEARCH_NO_ERROR) {
            //表示一條駕車路線
            BMKDrivingRouteLine *plan = (BMKDrivingRouteLine *)[result.routes objectAtIndex:0];
            // 計(jì)算路線方案中的路段數(shù)目
            int size = (int)[plan.steps count];

            for (int i = 0; i < size; i++) {
                //表示駕車路線中的一個(gè)路段
                BMKDrivingStep *transitStep = [plan.steps objectAtIndex:i];
                
                BMKMapPoint point1 = BMKMapPointForCoordinate(CLLocationCoordinate2DMake(_carAndMyLoactionCoors.latitude,_carAndMyLoactionCoors.longitude));
                
                BMKMapPoint point2 = BMKMapPointForCoordinate(CLLocationCoordinate2DMake(transitStep.entrace.location.latitude,transitStep.entrace.location.longitude));
                
                if (i!=0)
                {
                    CLLocationDistance distance = BMKMetersBetweenMapPoints(point1,point2);
                    _carAndMyLoactionDistance += distance;
                    _footerView.distance.text =[NSString stringWithFormat:@"您與車相聚%0.2f公里",_carAndMyLoactionDistance/1000];
                }
                
                _carAndMyLoactionCoors = CLLocationCoordinate2DMake(transitStep.entrace.location.latitude,transitStep.entrace.location.longitude);
            }
        }

        
        
        
        
        
        
    }
}
// 低于5km 按照步行規(guī)劃路線
- (void)onGetWalkingRouteResult:(BMKRouteSearch *)searcher result:(BMKWalkingRouteResult *)result errorCode:(BMKSearchErrorCode)error {
    
    _carAndMyLoactionDistance = 0;

    if (_isChooseDistance==YES)
    {
        [_mapView removeOverlays:_mapView.overlays];
        [_mapView removeAnnotations:_mapView.annotations];
        
        if (error == BMK_SEARCH_NO_ERROR) {
            BMKWalkingRouteLine *plan = (BMKWalkingRouteLine *)[result.routes objectAtIndex:0];
           
            NSInteger size = [plan.steps count];
            
            int planPointCounts = 0;
            for (int i = 0; i < size; i++) {
                BMKWalkingStep *transitStep = [plan.steps objectAtIndex:i];
                if(i==0){
                    RouteAnnotation *item = [[RouteAnnotation alloc]init];
                    item.coordinate = plan.starting.location;
                    item.title = @"起點(diǎn)";
                    item.type = 0;
                    [self.mapView addAnnotation:item]; // 添加起點(diǎn)標(biāo)注
                    
                }else if(i==size-1){
                    RouteAnnotation *item = [[RouteAnnotation alloc]init];
                    item.coordinate = plan.terminal.location;
                    item.title = kEndPositionTitle;
                    item.type = 1;
                    [self.mapView addAnnotation:item]; // 添加終點(diǎn)標(biāo)注
                }
                //添加annotation節(jié)點(diǎn)
                RouteAnnotation *item = [[RouteAnnotation alloc]init];
                item.coordinate = transitStep.entrace.location;
                item.title = transitStep.entraceInstruction;
                item.degree = transitStep.direction  *30;
                item.type = 2;
                [self.mapView addAnnotation:item];
                
                //軌跡點(diǎn)總數(shù)累計(jì)
                planPointCounts += transitStep.pointsCount;
            }
            
            //軌跡點(diǎn)
            BMKMapPoint *temppoints = new BMKMapPoint[planPointCounts];
            int i = 0;
            for (int j = 0; j < size; j++) {
                BMKWalkingStep *transitStep = [plan.steps objectAtIndex:j];
                int k=0;
                for(k=0;k<transitStep.pointsCount;k++) {
                    temppoints[i].x = transitStep.points[k].x;
                    temppoints[i].y = transitStep.points[k].y;
                    i++;
                }
                
            }
            // 通過points構(gòu)建BMKPolyline
            BMKPolyline *polyLine = [BMKPolyline polylineWithPoints:temppoints count:planPointCounts];
            [self.mapView addOverlay:polyLine]; // 添加路線overlay
            delete []temppoints;
            [self mapViewFitPolyLine:polyLine];
        }
    }else
    {
        
        
        if (error == BMK_SEARCH_NO_ERROR) {
            BMKWalkingRouteLine *plan = (BMKWalkingRouteLine *)[result.routes objectAtIndex:0];
            NSInteger size = [plan.steps count];

            for (int i = 0; i < size; i++) {
                BMKWalkingStep *transitStep = [plan.steps objectAtIndex:i];

                BMKMapPoint point1 = BMKMapPointForCoordinate(CLLocationCoordinate2DMake(_carAndMyLoactionCoors.latitude,_carAndMyLoactionCoors.longitude));
                
                BMKMapPoint point2 = BMKMapPointForCoordinate(CLLocationCoordinate2DMake(transitStep.entrace.location.latitude,transitStep.entrace.location.longitude));
                
                if (i!=0)
                {
                    CLLocationDistance distance = BMKMetersBetweenMapPoints(point1,point2);
                    _carAndMyLoactionDistance += distance;
                    _footerView.distance.text =[NSString stringWithFormat:@"您與車相聚%0.2f公里",_carAndMyLoactionDistance/1000];
                }
                
                _carAndMyLoactionCoors = CLLocationCoordinate2DMake(transitStep.entrace.location.latitude,transitStep.entrace.location.longitude);

            }
            
        }

    }
}
- (void)mapViewFitPolyLine:(BMKPolyline *) polyLine {
    CGFloat ltX, ltY, rbX, rbY;
    
    if (polyLine.pointCount < 1) return;
    BMKMapPoint pt = polyLine.points[0];
    ltX = pt.x, ltY = pt.y;
    rbX = pt.x, rbY = pt.y;
    
    for (int i = 0; i < polyLine.pointCount; i++) {
        BMKMapPoint pt = polyLine.points[i];
        if (pt.x < ltX) {
            ltX = pt.x;
        }
        if (pt.x > rbX) {
            rbX = pt.x;
        }
        if (pt.y > ltY) {
            ltY = pt.y;
        }
        if (pt.y < rbY) {
            rbY = pt.y;
        }
    }
    BMKMapRect rect;
    BMKMapPoint point;
    point.x = ltX;
    point.y = ltY;
    rect.origin = point;
    BMKMapSize size;
    size.width = rbX - ltX;
    size.height = rbY - ltY;
    rect.size = size;
    [self.mapView setVisibleMapRect:rect];
    self.mapView.zoomLevel = self.mapView.zoomLevel - 0.3;
}
//這里方法和上面一樣,只不過多了一個(gè)BMKGroundOverlay
-(BMKOverlayView *)mapView:(BMKMapView *)mapView viewForOverlay:(id<BMKOverlay>)overlay {
    
    
    if ([overlay isKindOfClass:[BMKPolyline class]]) {
        BMKPolylineView* polylineView = [[BMKPolylineView alloc] initWithOverlay:overlay];
        polylineView.strokeColor = BlueColor;
        polylineView.lineWidth = 3.0;
        return polylineView;
    }
    else if ([overlay isKindOfClass:[BMKGroundOverlay class]]){
        BMKGroundOverlayView* groundView = [[BMKGroundOverlayView alloc] initWithOverlay:overlay];
        return groundView;
    }
    return nil;
}


上面完成后的樣式:(每條道路需要轉(zhuǎn)彎的時(shí)候我都加了圖標(biāo))


da

放大以后是這樣的:


WechatIMG3.jpeg

接下來是最容易被用到的地方:定位~


    _locationManager = [[BMKLocationManager alloc] init];
    //設(shè)置delegate
    _locationManager.delegate = self;
    //設(shè)置返回位置的坐標(biāo)系類型
    _locationManager.coordinateType = BMKLocationCoordinateTypeBMK09LL;
    //設(shè)置距離過濾參數(shù)
    _locationManager.distanceFilter = kCLDistanceFilterNone;
    //設(shè)置預(yù)期精度參數(shù)
    _locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    //設(shè)置應(yīng)用位置類型
    _locationManager.activityType = CLActivityTypeAutomotiveNavigation;
    //設(shè)置是否自動(dòng)停止位置更新
    _locationManager.pausesLocationUpdatesAutomatically = NO;
    //設(shè)置是否允許后臺定位
    //_locationManager.allowsBackgroundLocationUpdates = YES;
    //設(shè)置位置獲取超時(shí)時(shí)間
    _locationManager.locationTimeout = 10;
    //設(shè)置獲取地址信息超時(shí)時(shí)間
    _locationManager.reGeocodeTimeout = 10;
    
    [_locationManager requestLocationWithReGeocode:YES withNetworkState:NO completionBlock:^(BMKLocation * _Nullable location, BMKLocationNetworkState state, NSError * _Nullable error) {
        
        if (error)
        {
            NSLog(@"locError:{%ld - %@};", (long)error.code, error.localizedDescription);
        }
        if (location) {//得到定位信息,添加annotation
            
            if (location.location) {
                
                CLLocationCoordinate2D  startCoors  = location.location.coordinate;
                
            }
            if (location.rgcData) {
                NSLog(@"rgc = %@",[location.rgcData description]);
            }
        }
        //        NSLog(@"BMKLocation = %@",location);
        
    }];

地理編碼,獲取文字地理位置 如下:

            BMKGeoCodeSearch *geoCodeSearch = [[BMKGeoCodeSearch alloc]init];
            //設(shè)置反地理編碼檢索的代理
            geoCodeSearch.delegate = self;
            //初始化請求參數(shù)類BMKReverseGeoCodeOption的實(shí)例
            BMKReverseGeoCodeSearchOption *reverseGeoCodeOption = [[BMKReverseGeoCodeSearchOption alloc] init];
            // 待解析的經(jīng)緯度坐標(biāo)(必選)
            reverseGeoCodeOption.location = _daoshudiyi;
            //是否訪問最新版行政區(qū)劃數(shù)據(jù)(僅對中國數(shù)據(jù)生效)
            reverseGeoCodeOption.isLatestAdmin = YES;
            /**
             根據(jù)地理坐標(biāo)獲取地址信息:異步方法,返回結(jié)果在BMKGeoCodeSearchDelegate的
             onGetAddrResult里
             
             reverseGeoCodeOption 反geo檢索信息類
             成功返回YES,否則返回NO
             */
            BOOL flag = [geoCodeSearch reverseGeoCode:reverseGeoCodeOption];
            if (flag) {
                NSLog(@"反地理編碼檢索成功");
            } else {
                NSLog(@"反地理編碼檢索失敗");
            }

- (void)onGetReverseGeoCodeResult:(BMKGeoCodeSearch *)searcher result:(BMKReverseGeoCodeSearchResult *)result errorCode:(BMKSearchErrorCode)error {
//    NSString *message = [NSString stringWithFormat:@"街道號碼:%@\n街道名稱:%@\n區(qū)縣名稱:%@\n城市名稱:%@\n省份名稱:%@\n國家:%@\n 國家代碼:%@\n行政區(qū)域編碼:%@\n地址名稱:%@\n商圈名稱:%@\n結(jié)合當(dāng)前位置POI的語義化結(jié)果描述:%@\n城市編碼:%@\n緯度:%f\n經(jīng)度:%f\n", result.addressDetail.streetNumber, result.addressDetail.district, result.addressDetail.city, result.addressDetail.province, result.addressDetail.country, result.addressDetail.countryCode, result.addressDetail.adCode, result.addressDetail.streetName,result.address, result.businessCircle, result.sematicDescription, result.cityCode, result.location.latitude, result.location.longitude];
    
    _footerView.address.text = [NSString stringWithFormat:@"%@",result.address];
    
    
//    NSLog(@"%@",message);
//    [self alertMessage:message];
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,853評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內(nèi)容