iOS百度地图POI详情检索与路径规划(附Demo)

POI(Point of Interest)中文可以翻译为“兴趣点”。在地理信息系统中,一个POI可以是一栋房子、一个商铺、一个邮筒、一个公交站等。

写在前面:最近老是有朋友来问我这个检索怎么不行了,我今天看了下,果然,出了问题,似乎是百度地图的一个Bug。POI检索后调POI详情检索,但是详情检索出来的经纬度全部是0,这样自然是不能够成功添加大头针的。奇怪的是在POI检索中经纬度是有的,但是呢,详情中经纬度竟然丢失了。这个只能等百度那边修复了,当然我这里提供一个临时解决这个办法的方法。在文末我上一个截图,有兴趣的看下。

百度地图iOS SDK为开发者提供了公交 驾车 骑行 步行 4种类型的线路规划方案,同时根据不同的方案还可以选择时间最短 距离最短 等策略来完成最终的线路规划。开发者可根据自己实际的业务需求来自由使用。
我想在看此博客之前你应该去浏览下百度地图开发者文档,前面两段都是废话,但既然是博客的功能点,还是写出来。
请下载Demo的朋友尽量更换百度appKey和项目的boundID为了方便部分朋友,我就不删去了,项目可直接运行。

无图无真相!

POI检索

Untitled,,.gif

路线规划

Untitle,.gif

UI是我上架项目中的,为了方便博客和写Demo我就直接拖进去了。

百度地图的集成很简单,按照开发文档几分钟就搞定了,我就不抄写了,但是记录几个可能会出问题的地方吧。

  • Privacy - Location Always Usage Description plist.info请求使用GPS
  • LSApplicationQueriesSchemes 如果你需要调起百度地图客户端
  • Bundle display name plist.info中需要加入,而且是必要的
  • 这个文件用到了c++代码,请务必把文件后缀名改为.mm

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 代码都是我用心准备的,这里也主要是说个流程。 Demo传送门 整个打包,比较大,我也懒得放git,不想下载的看下面代码 iOS技术交流群: 511860085 欢迎加入!

POI不能检索问题临时解决办法 不明白的加群来问。

首先创建一个类

55F75204-98A3-4B68-BBD8-0AC519CCAF18.png

然后仔细看POI两个代理的方法处理 数组自己声明,我就不多截图了

3B08BB92-38A3-430F-B358-798546AB883F.png

CFAD4F73-D921-422F-B5AB-9C932A651D9C.png

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏WeTest质量开放平台团队的专栏

5天2亿活跃用户,2017QQ“LBS+AR”天降红包活动后台揭密

作者王家彬,腾讯后台开发工程师,参与“LBS+AR”天降红包项目,其所在“2016春节红包联合项目团队”获得2016公司级业务突破奖。

1263
来自专栏大神带我来搬砖

站在道德的制高点来写程序——免费获取IntelliJ IDEA license

1464
来自专栏GIS讲堂

扩展graphiclayer实现多城市天气情况的展示

在上一节,实现了点击展示城市天气的效果,在本节,讲述通过扩展graphiclayer实现同时显示多个城市天气的展示。

572
来自专栏程序员互动联盟

同样的技术,为何别人总是能挖到漏洞 ?

菜鸟和高手的区别,不完全在于你学了多少,更看你能否清晰认知到目前所处阶段,正确迸发出对下一阶段知识的渴望。

562
来自专栏DannyHoo的专栏

iOS开发中支付宝支付的集成(其实很简单)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

522
来自专栏Data Analysis & Viz

(送福利)BDP绘制微博转发动态热力图

先把效果图放上来,酷炫压场。看完本文,你就能轻松实现这个动态效果,全程只需几分钟!

852
来自专栏程序员的知识天地

程序员电脑桌面是什么样的? 网友: IE浏览器必删, 不能留!

近日,有网友提问道:作为一个程序员,一直使用的都是默认Windows7桌面,最近被妹子吐槽太丑,打算换一个,不知道各位程序员的桌面都长什么样子?

652
来自专栏编程一生

IO和socket编程

1053
来自专栏phodal

我是如何手绘文章中的流程图?

我们常说,「文不如表,表不如图」。而要做出一张适合文章的图,也不是一件容易的事。 图比表和文章更容易理解,但是其所花费的时间也更长。在构建得差不多的时候,写一篇...

4337
来自专栏杨建荣的学习笔记

关于ORA-00020问题的反思(r2笔记第3天)

今天在生产环境中查看alert日志,发现了如下的一段错误。这个错误确实没有太多需要解释的。很明显就是因为session leak的经典问题。 ORA-000...

3435

扫码关注云+社区