嗨,我已經(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];
}
以上就是歷史軌跡的方法繪制出來的如下圖:
接下來是角度的問題,如何根據(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)后的樣式
接下來是路徑規(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))
放大以后是這樣的:
接下來是最容易被用到的地方:定位~
_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];
}