代码地址如下:<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
二:路径规划
点击搜索,传过来一种路线方式,并且传来开始地与目的地。我本想直接写出需要注意的地方,但是发现在代码中不少都已经注释了,请大家注意,例如 //每次必须是一个新的对象,不然pt
和name
会混乱
下面代码有很多逻辑上的处理,为了一体性,我没有删去。
#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大师代发,拒绝转载,转载需要作者授权