1. 程式人生 > >百度地圖檢索以及路徑規劃

百度地圖檢索以及路徑規劃

通過 發送 ble 申請 poi 當前 for face rect

代碼地址如下:<br>http://www.demodashi.com/demo/11030.html

一、準備工作

  • 需要集成百度地圖SDK
  • 需要申請百度地圖AppKey
  • 本例子實現了POI檢索以及詳情檢索、路線規劃

二、程序實現

首先貼一下項目截圖:
技術分享圖片
(項目截圖1)
技術分享圖片
(項目截圖2)

POI詳情:
第一步:在Appdelegate.m中設置AppKey


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    HouseMapTootsViewController *vc = [[HouseMapTootsViewController alloc] init];
    vc.latitude = 31.976;
    vc.longitude = 118.71;
    UINavigationController *navi = [[UINavigationController alloc] initWithRootViewController:vc];
    //配置百度地圖
    [self configurationBMKMap];
    self.window.rootViewController = navi;
    [self.window makeKeyAndVisible];
    return YES;
}

#pragma mark -- 百度地圖

- (void)configurationBMKMap {

    // 要使用百度地圖,請先啟動BaiduMapManager
    _mapManager = [[BMKMapManager alloc] init];
    BOOL ret = [_mapManager start:@"appkey" generalDelegate:self];
    if (!ret) {
        NSLog(@"manager start failed!");
    }
}

第二步:完成代理

#pragma mark -- BMKGeneralDelegate

- (void)onGetNetworkState:(int)iError {
    if (0 == iError) {
        NSLog(@"聯網成功");
    }else {
        NSLog(@"onGetNetworkState %d",iError);
    }
}

- (void)onGetPermissionState:(int)iError {
    if (0 == iError) {
        NSLog(@"授權成功");
    }else {
        NSLog(@"onGetPermissionState %d",iError);
    }
}

第三步:創建百度地圖,開啟用戶定位(後面路線規劃需要)。並且添加一個大頭針,這個大頭針就是你即將檢索的中心點。

 self.mapView = [[BMKMapView alloc] initWithFrame:CGRectMake(0, 40+64, kScreenWidth, kScreenHeight - 40 - 64)];
    [self.view addSubview:self.mapView];
    [self.view addSubview:self.planView];

    self.locService = [[BMKLocationService alloc] init];
    self.mapView.delegate = self;
    self.locService.delegate = self;
    //定位方向模式  不能使用跟隨,不然地圖中心就不是大頭針了
    [self.mapView setZoomLevel:16];
    self.mapView.showMapScaleBar = YES;
    self.mapView.userTrackingMode = BMKUserTrackingModeNone;
    self.mapView.showsUserLocation = YES;
    [self.locService startUserLocationService];
    CLLocationCoordinate2D coor;
    coor.latitude = self.latitude;
    coor.longitude = self.longitude;

    if (self.pointAnnotation == nil) {
        //自定義大頭針
        self.pointAnnotation = [[YLAnnotationView alloc]init];
        self.pointAnnotation.coordinate = coor;
        self.pointAnnotation.title = @"房源位置";
        self.pointAnnotation.subtitle = @"點擊到這裏去";
        self.pointAnnotation.image = [UIImage imageNamed:@"homelocation"];
    }
    [self.mapView addAnnotation:self.pointAnnotation];
    //設置中心點
    [self.mapView setCenterCoordinate:coor];
    //檢索周邊設施
    self.poiSearch.delegate = self;

    //添加大頭針後添加周邊檢索
    self.option.location = coor;
    self.option.pageIndex = 0;
    self.option.pageCapacity = 20;
    self.option.radius = 1500;

第四步:在點擊事件中初始化檢索對象,Demo中我自己定義了一個topView用來做不同點擊區分。

#pragma mark -- YLSelectorItemViewDelegate

- (void)didSelectedItems:(NSInteger)item {

    CLLocationCoordinate2D coor;
    coor.latitude = self.latitude;
    coor.longitude = self.longitude;
    self.seletItem = item;
    self.isFirst = YES;

    if (item == 1) {
        self.option.keyword = @"美食";
        BOOL flag = [self.poiSearch poiSearchNearBy:self.option];
        if(flag) {
            NSLog(@"周邊檢索發送成功");
        }else {
            NSLog(@"周邊檢索發送失敗");
        }
    }else if (item == 2) {
        self.option.keyword = @"超市";
        BOOL flag1 = [self.poiSearch poiSearchNearBy:self.option];
        if(flag1) {
            NSLog(@"周邊檢索發送成功");
        }else {

        }
    }else if (item == 3) {
        self.option.keyword = @"ATM";
        BOOL flag2 = [self.poiSearch poiSearchNearBy:self.option];
        if(flag2) {
            NSLog(@"周邊檢索發送成功");
        }else {
            NSLog(@"周邊檢索發送失敗");
        }
    }else if (item == 4) {
        self.option.keyword = @"購物";
        BOOL flag3 = [self.poiSearch poiSearchNearBy:self.option];
        if(flag3) {
            NSLog(@"周邊檢索發送成功");
        }else {
            NSLog(@"周邊檢索發送失敗");
        }
    }

}

第五步:點擊後會進入下面這個代理,首先刪除屏幕上的大頭針,由於我這裏還是需要顯示這個房源大頭針,這裏我做了一個處理保存下來,在for循環中拿到了所有的list中的對象,這些對象就是我們要的周邊信息,但是並不是詳情,詳情是需要拿到這個目標對象UID再次去檢索(這裏普通檢索和詳情檢索被百度強行分開了,可能處於流量或者模塊化的考慮吧)。那麽就必須再次創建檢索對象了,這次for循環每次都會出現一個詳情檢索,於是我們可以到詳情代理中做事情了。

//實現PoiSearchDeleage處理回調結果
- (void)onGetPoiResult:(BMKPoiSearch *)searcher result:(BMKPoiResult *)poiResultList errorCode:(BMKSearchErrorCode)error {

    // 清楚屏幕中除卻房源外的所有的annotation
    NSMutableArray *array = [NSMutableArray arrayWithArray:self.mapView.annotations];
    //把房源的保存下載
    [array removeObjectAtIndex:0];
    [self.mapView removeAnnotations:array];
    array = [[NSArray arrayWithArray:self.mapView.overlays] mutableCopy];
    [self.mapView removeOverlays:array];
    if (error == BMK_SEARCH_NO_ERROR) {
        for (int i = 0; i < poiResultList.poiInfoList.count; i++) {
            BMKPoiInfo *poi = [poiResultList.poiInfoList objectAtIndex:i];
            //自定義大頭針
            BMKPoiDetailSearchOption *option = [[BMKPoiDetailSearchOption alloc] init];
            option.poiUid = poi.uid;
            BMKPoiSearch *se = [[BMKPoiSearch alloc] init];
            se.delegate = self;
            //把所有的POI存入數組,用於最終的釋放,避免循環引用;
            [self.poiDetails addObject:se];
            [se poiDetailSearch:option];
        }
    } else if (error == BMK_SEARCH_AMBIGUOUS_KEYWORD){
        NSLog(@"搜索詞有歧義");
    } else {
        // 各種情況的判斷。。。
    }
}

詳情代理,這裏我需要不同的大頭針圖片,做了一個處理

//周邊搜索的詳情代理
- (void)onGetPoiDetailResult:(BMKPoiSearch *)searcher result:(BMKPoiDetailResult *)poiDetailResult errorCode:(BMKSearchErrorCode)errorCode {
    CLLocationCoordinate2D houseCoor;
    houseCoor.latitude = self.latitude;
    houseCoor.longitude = self.longitude;

    if (errorCode == BMK_SEARCH_NO_ERROR) {
        YLAnnotationView *item = [[YLAnnotationView alloc] init];
        item.coordinate = poiDetailResult.pt;

        switch (self.seletItem) {
            case 1:
                item.image = [UIImage imageNamed:@"food"];
                item.subtitle = [NSString stringWithFormat:@"均價:%.2f",poiDetailResult.price];
                break;
            case 2:
                item.image = [UIImage imageNamed:@"supermarket"];
                item.subtitle = poiDetailResult.address;
                break;
            case 3:
                item.image = [UIImage imageNamed:@"ATM"];
                item.subtitle = poiDetailResult.address;
                break;
            case 4:
                item.image = [UIImage imageNamed:@"shoping"];
                item.subtitle = poiDetailResult.address;
                break;
            default:
                break;
        }
        item.title = poiDetailResult.name;
        //加個判斷,第一次進來的時候設置中心點,不能每次都移動中心,不然POI期間會拖不動地圖。
        if (self.isFirst) {
            [self.mapView setCenterCoordinate:houseCoor animated:YES];
        }
        self.isFirst = NO;
        [self.mapView addAnnotation:item];

    }else if (errorCode == BMK_SEARCH_AMBIGUOUS_KEYWORD) {

        NSLog(@"搜索詞有歧義");

    }else {

    }
}

到這裏主要代碼就結束了。文末我會附上Demo
二:路徑規劃
點擊搜索,傳過來一種路線方式,並且傳來開始地與目的地。我本想直接寫出需要註意的地方,但是發現在代碼中不少都已經註釋了,請大家註意,例如 //每次必須是一個新的對象,不然ptname會混亂
下面代碼有很多邏輯上的處理,為了一體性,我沒有刪去。

#pragma mark -- RoutePlanViewDelegate
//搜路線
- (void)didSelectorItems:(NSInteger)item withStartName:(NSString *)startName andEndName:(NSString *)endName {
    CLLocationCoordinate2D houseCoor;
    houseCoor.latitude = self.latitude;
    houseCoor.longitude = self.longitude;

    //每次必須是一個新的對象,不然pt和name會混亂
    self.startNode = [[BMKPlanNode alloc] init];
    self.endNode = [[BMKPlanNode alloc] init];
    self.startNode.cityName = @"南京";
    self.endNode.cityName = @"南京";
    //設置出發點與終點
    if ([startName isEqualToString:@""] || [endName isEqualToString:@""]) {
        NSLog(@"搜索字段有歧義");
        return;
    }
    if ([startName isEqualToString:@"當前位置"] && [endName isEqualToString:@"房源位置"]) {
        self.startNode.pt = self.userLocation.location.coordinate;
        self.endNode.pt = houseCoor;
    }else if ([endName isEqualToString:@"當前位置"] && [startName isEqualToString:@"房源位置"]) {
        self.startNode.pt = houseCoor;
        self.endNode.pt = self.userLocation.location.coordinate;
    }else if (![startName isEqualToString:@"當前位置"] && [endName isEqualToString:@"房源位置"]) {
        self.startNode.name = startName;
        self.endNode.pt = houseCoor;
    }else if([startName isEqualToString:@"當前位置"] && ![endName isEqualToString:@"房源位置"]) {
        self.startNode.pt = self.userLocation.location.coordinate;
        self.endNode.name = endName;
    }else if(![startName isEqualToString:@"當前位置"] && [endName isEqualToString:@"當前位置"]) {
        self.startNode.name = startName;
        self.endNode.pt = self.userLocation.location.coordinate;
    }else if([startName isEqualToString:@"房源位置"] && ![endName isEqualToString:@"當前位置"]) {
        self.startNode.pt = houseCoor;
        self.endNode.name = endName;
    }else {
        self.startNode.name = startName;
        self.endNode.name = endName;
    }
    NSLog(@"%@ ---- %@",self.startNode.name,self.endNode.name);

    if (item == 1) {

        //駕車
        BMKDrivingRoutePlanOption *driveRouteSearchOption =[[BMKDrivingRoutePlanOption alloc]init];

        driveRouteSearchOption.from = self.startNode;

        driveRouteSearchOption.to = self.endNode;

        BOOL flag = [self.routeSearch drivingSearch:driveRouteSearchOption];
        if (flag) {

        }else {

        }

    }else if (item == 2) {
        //公交
        BMKMassTransitRoutePlanOption *option = [[BMKMassTransitRoutePlanOption alloc]init];
        option.from = self.startNode;
        option.to = self.endNode;
        BOOL flag = [self.routeSearch massTransitSearch:option];

        if(flag) {
            NSLog(@"公交交通檢索(支持垮城)發送成功");
        } else {
            NSLog(@"公交交通檢索(支持垮城)發送失敗");
        }

    } else if (item == 3) {
        //步行
        BMKWalkingRoutePlanOption *walkingRouteSearchOption = [[BMKWalkingRoutePlanOption alloc] init];
        walkingRouteSearchOption.from = self.startNode;
        walkingRouteSearchOption.to = self.endNode;
        BOOL flag = [self.routeSearch walkingSearch:walkingRouteSearchOption];
        if(flag) {
            NSLog(@"walk檢索發送成功");
        }else {
            NSLog(@"walk檢索發送失敗");
        }

    }else {
        //騎車
        BMKRidingRoutePlanOption *option = [[BMKRidingRoutePlanOption alloc]init];
        option.from = self.startNode;
        option.to = self.endNode;
        BOOL flag = [self.routeSearch ridingSearch:option];
        if (flag) {
            NSLog(@"騎行規劃檢索發送成功");
        }else {
            NSLog(@"騎行規劃檢索發送失敗");
        }

    }
}

點擊後,會進入下面這個代理


#pragma mark -- BMKRouteSearchDelegate

//駕車
- (void)onGetDrivingRouteResult:(BMKRouteSearch *)searcher result:(BMKDrivingRouteResult *)result errorCode:(BMKSearchErrorCode)error {

    NSMutableArray *array = [NSMutableArray arrayWithArray:self.mapView.annotations];
    [array removeObjectAtIndex:0];
    [self.mapView removeAnnotations:array];
    array = [[NSArray arrayWithArray:self.mapView.overlays] mutableCopy];
    [self.mapView removeOverlays:array];
    if (error == BMK_SEARCH_NO_ERROR) {
        //表示一條駕車路線
        BMKDrivingRouteLine *plan = (BMKDrivingRouteLine *)[result.routes objectAtIndex:0];
        // 計算路線方案中的路段數目
        int size = (int)[plan.steps count];
        int planPointCounts = 0;
        for (int i = 0; i < size; i++) {
            //表示駕車路線中的一個路段
            BMKDrivingStep *transitStep = [plan.steps objectAtIndex:i];
            if(i==0){
                RouteAnnotation *item = [[RouteAnnotation alloc]init];
                item.coordinate = plan.starting.location;
                item.title = @"起點";
                item.type = 0;
                [self.mapView addAnnotation:item]; // 添加起點標註

            }else if(i==size-1){
                RouteAnnotation *item = [[RouteAnnotation alloc]init];
                item.coordinate = plan.terminal.location;
                item.title = @"終點";
                item.type = 1;
                [self.mapView addAnnotation:item]; // 添加終點標註
            }
            //添加annotation節點
            RouteAnnotation *item = [[RouteAnnotation alloc]init];
            item.coordinate = transitStep.entrace.location;
            item.title = transitStep.entraceInstruction;
            item.degree = transitStep.direction  *30;
            item.type = 4;
            [self.mapView addAnnotation:item];

            //軌跡點總數累計
            planPointCounts += transitStep.pointsCount;
        }
        // 添加途經點
        if (plan.wayPoints) {
            for (BMKPlanNode *tempNode in plan.wayPoints) {
                RouteAnnotation *item = [[RouteAnnotation alloc]init];
                item.coordinate = tempNode.pt;
                item.type = 5;
                item.title = tempNode.name;
                [self.mapView addAnnotation:item];
            }
        }
        //軌跡點
        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構建BMKPolyline
        BMKPolyline *polyLine = [BMKPolyline polylineWithPoints:temppoints count:planPointCounts];
        [self.mapView addOverlay:polyLine]; // 添加路線overlay
        delete []temppoints;
        [self mapViewFitPolyLine:polyLine];
    }
}

上面我僅僅放了一個駕車的代理,還有步行等沒有放上去,太長了,文末為了不想下載代碼的同學觀看,我會放上整頁代碼提供參考。
言歸正傳,你們發現我有自定義了一個RouteAnnotation類。這個路線需要字段比較多,我不想改動之前大頭針類了,就直接重寫了一個。如下

/**
  * 路線的標註  自定義一個大頭針類   為了便捷,就直接放這裏了
 */
@interface RouteAnnotation : BMKPointAnnotation {
    int _type; ///<0:起點 1:終點 2:公交 3:地鐵 4:駕乘 5:途經點
    int _degree;//旋轉的角度
}

@property (nonatomic) int type;
@property (nonatomic) int degree;
@end

@implementation RouteAnnotation

@synthesize type = _type;
@synthesize degree = _degree;
@end

如果你也這樣做,那麽就像我一樣在大頭針重用方法中做以下判斷,並且實現這個方法,如下:


 if ([annotation isKindOfClass:[RouteAnnotation class]]) {
        return [self getRouteAnnotationView:view viewForAnnotation:(RouteAnnotation *)annotation];
    }
#pragma mark -- 獲取路線的標註,顯示到地圖(自定義的一個大頭針類實例方法)我只貼到case 0;其他的在文末查找,需要註意的地方我已寫註釋
- (BMKAnnotationView *)getRouteAnnotationView:(BMKMapView *)mapview viewForAnnotation:(RouteAnnotation *)routeAnnotation {
    BMKAnnotationView *view = nil;
    //根據大頭針類型判斷是什麽圖標
    switch (routeAnnotation.type) {
        case 0:
        {   //開始點
            view = [mapview dequeueReusableAnnotationViewWithIdentifier:@"start_node"];
            if (view == nil) {
                view = [[BMKAnnotationView alloc] initWithAnnotation:routeAnnotation reuseIdentifier:@"start_node"];
                //從百度地圖資源文件中拿到需要的圖片
                view.image = [UIImage imageWithContentsOfFile:[self getMyBundlePath1:@"images/icon_nav_start"]];
                view.centerOffset = CGPointMake(0, -(view.frame.size.height * 0.5));
                view.canShowCallout = true;
            }
            view.annotation = routeAnnotation;
        }

在駕車路線的代理最後我們添加了一條線,就是如下代碼

  // 通過points構建BMKPolyline
        BMKPolyline *polyLine = [BMKPolyline polylineWithPoints:temppoints count:planPointCounts];
        [self.mapView addOverlay:polyLine]; // 添加路線overlay
        delete []temppoints;
        [self mapViewFitPolyLine:polyLine];

這樣我們還需要實現一個劃線的代理,這個是必須實現的。還有一個地圖路線的範圍計算,文末的所有代碼中的最後一段,這些都是從百度地圖官方代碼拿來的。

#pragma mark -- 路線線條繪制代理

- (BMKOverlayView *)mapView:(BMKMapView *)map viewForOverlay:(id<BMKOverlay>)overlay {
    if ([overlay isKindOfClass:[BMKPolyline class]]) {
        BMKPolylineView *polylineView = [[BMKPolylineView alloc] initWithOverlay:overlay];
        //設置線條顏色
        polylineView.fillColor = [[UIColor alloc] initWithRed:0 green:1 blue:1 alpha:1];
        polylineView.strokeColor = [[UIColor alloc] initWithRed:0 green:0 blue:0.8 alpha:0.7];
        polylineView.lineWidth = 3.0;
        return polylineView;
    }
    return nil;
}

雖然上面大多都是復制粘貼把,但是 Demo 代碼都是我用心準備的,這裏也主要是說個流程。

三、運行效果

技術分享圖片
技術分享圖片

四、其他補充

百度地圖的集成很簡單,按照開發文檔幾分鐘就搞定了,我就不抄寫了,但是記錄幾個可能會出問題的地方吧。

  • Privacy - Location Always Usage Description plist.info請求使用GPS
  • LSApplicationQueriesSchemes 如果你需要調起百度地圖客戶端
  • Bundle display name plist.info中需要加入,而且是必要的
  • 這個文件用到了c++代碼,請務必把文件後綴名改為.mm
    百度地圖檢索以及路徑規劃

代碼地址如下:<br>http://www.demodashi.com/demo/11030.html

註:本文著作權歸作者,由demo大師代發,拒絕轉載,轉載需要作者授權

百度地圖檢索以及路徑規劃