前言
- 最近在研究腾讯地图SDK的检索功能,使用公交路线规划功能实现了这个小Demo,暂时还没有做同一路线不同公交线路切换功能(以后有时间可能会完善这个Demo)。
使用场景:
- 公交路线规划
准备:
- 腾讯地图iOS SDK
公交路线规划检索结果的数据说明
检索结果:QMSBusingRouteSearchResult中的
NSArray *routes
属性包含了检索到的结果,每个结果都是一个独立的公交出行方案QMSBusingRoutePlan。-
公交出行方案:QMSBusingRoutePlan,表示从起点到终点的完整出行规划
属性 说明 CGFloat distance 方案的总距离 CGFloat duration 方案的预估时间 NSString *bounds 方案的东北、西南坐标
用于调整地图视角显示路线NSArray *steps 总路线的分段信息(包含步行、公交,其中公交可能包含多个方案) -
出行方案的分段路线规划
属性 说明 NSString *mode "WALKING":步行
"TRANSIT":公交
标记该分段的出行方式CGFloat distance 分段的距离 CGFloat duration 分段的预估时间 CGFloat price 如果是公交或者地铁,需要花费的金额,元 NSString *direction 方向描述,如果为步行,表达为向哪个方向走 NSArray *polyline 分段途径坐标点(这里必须说一下,腾讯地图SDK使用的是CLLocationCoordinate2D的encode类型,因此还需要decode才能使用) NSArray *lines 如果是公交,并且有多个线路可以乘坐的话,该数组就会包含多个路线:QMSBusingRouteTransitLine -
公交线路规划:QMSBusingRouteTransitLine,到这里才是最麻烦的地方,因为是一个线路的不同方案
属性 说明 NSString *vehicle 交通工具:公交、地铁 NSString *id_ 应该是个标记,没发现作用 CGFloat distance 距离 NSTimeInterval duration 预计乘坐时间 NSString *title 车名:如333路、软件园通勤车等 NSArray *polyline 途径坐标点 NSInteger station_count 途径站的数目 NSArray *stations 途径站的站名 QMSStationEntrance *destination 目的地的地址,也就是属于哪个街道 QMSBusStation *geton 上车车站 QMSBusStation *getoff 下车车站
公交路线规划的详细说明
-
公交路线规划的起点和终点坐标的检索(第一个麻烦的地方,不支持地名检索)
-
腾讯SDK的检索参数并不支持地名检索,只有坐标检索,因此要使用检索功能就必须先通过POI检索功能来获取起点和终点的坐标位置:
- (IBAction)searchButtonClicked:(UIButton *)sender { _startPoiData = nil; _endPoiData = nil; // 起点:腾讯北京总部 _startPoiOption = [[QMSPoiSearchOption alloc] init]; _startPoiOption.keyword = _startPointTextField.text; [_startPoiOption setBoundaryByRegionWithCityName:@"北京" autoExtend:NO]; [self.mapSearcher searchWithPoiSearchOption:_startPoiOption]; // 终点:西二旗地忒站 _endPoiOption = [[QMSPoiSearchOption alloc] init]; _endPoiOption.keyword = _endPointTextField.text; [_endPoiOption setBoundaryByRegionWithCityName:@"北京" autoExtend:NO]; [self.mapSearcher searchWithPoiSearchOption:_endPoiOption]; } - (void)searchWithPoiSearchOption:(QMSPoiSearchOption *)poiSearchOption didReceiveResult:(QMSPoiSearchResult *)poiSearchResult { if (poiSearchOption == _startPoiOption) { _startPoiData = poiSearchResult.dataArray.firstObject; } else { _endPoiData = poiSearchResult.dataArray.firstObject; } // 添加起点终点前,先消除之前的大头针 [self.mapView removeAnnotations:self.mapView.annotations]; // 设置起点和终点 QPointAnnotation *startPointAnnotation = [[QPointAnnotation alloc] init]; startPointAnnotation.coordinate = _startPoiData.location; [self.mapView addAnnotation:startPointAnnotation]; QPointAnnotation *endPointAnnotation = [[QPointAnnotation alloc] init]; endPointAnnotation.coordinate = _endPoiData.location; [self.mapView addAnnotation:endPointAnnotation]; // 发起公交路线规划检索 [self searchBusRoute]; }
-
通过起点和终点POI数据来发起公交路线规划检索
- (void)searchBusRoute { if (_startPoiData != nil && _endPoiData != nil) { QMSBusingRouteSearchOption *option = [[QMSBusingRouteSearchOption alloc] init]; [option setFromCoordinate:_startPoiData.location]; [option setToCoordinate:_endPoiData.location]; [self.mapSearcher searchWithBusingRouteSearchOption:option]; } }
-
-
显示所有路线的基本信息
-
获取方案的数量:planCount
NSInteger planCount = busingRouteSearchResult.routes.count;
-
创建路线方案模型数组,在这里我在保存了路线的同事,提出了几样数据:
// 路线方案 @property (nonatomic, strong) QMSBusingRoutePlan *routePlan; // 路线预估时间 @property (nonatomic, assign) NSInteger duration; // 路线的步行距离 @property (nonatomic, assign) CGFloat distance; // 途径的公交车站数 @property (nonatomic, assign) NSInteger stationCount; // 上车站名称 @property (nonatomic, strong) NSString *startStationName;
-
获取路线的步行距离:需要遍历方案中的所有分段信息,判断是否为步行,然后累加distance:
// 遍历方案中的步骤 for (int i = 0; i < plan.steps.count; i++) { QMSBusingSegmentRoutePlan *segmentRoutePlan = plan.steps[i]; // 判断步骤为步行,累计距离 if ([self routeIsWalkingPlan:segmentRoutePlan]) { walkingDistance += segmentRoutePlan.distance; } }
-
获取途径的公交站总数和上车站名称:由于同一分段可能有多个公交出行方案,在此只演示了默认第一条方案:
// 判断步骤为公交 if ([self routeIsTransitPlan:segmentRoutePlan]) { // 遍历经过的公交 for (int j = 0; j < segmentRoutePlan.lines.count; j++) { QMSBusingRouteTransitLine *line = segmentRoutePlan.lines[j]; // 公交站数 stationCount += line.station_count; // 第一站名称 if (j == 0) { QMSBusStation *station = line.stations.firstObject; startStationName = station.title; } } }
-
通过TableView来展示所有的方案:
-
-
选择方案,展示路线图
-
先获取该方案总共的分段数:
NSInteger stepCount = routePlan.steps.count;
-
通过判断分段方案的类型,来区分虚线和蚯蚓线:
if ([self routeIsWalkingPlan:plan]) { // 步行虚线 CLLocationCoordinate2D coords[plan.polyline.count]; for (int i = 0; i < plan.polyline.count; i++) { NSValue *value = plan.polyline[i]; CLLocationCoordinate2D coord = [value coordinateValue]; coords[i] = coord; } QPolyline *walkingPolyline = [[QPolyline alloc] initWithCoordinates:coords count:plan.polyline.count]; // QPolyline中的userData属性可以用来添加自定义的内容去判断数据 walkingPolyline.userData = @"WALKING"; [self.mapView addOverlay:walkingPolyline]; } else { // 驾车蚯蚓线 QMSBusingRouteTransitLine *line = plan.lines[0]; CLLocationCoordinate2D coords[line.polyline.count]; for (int i = 0; i < line.polyline.count; i++) { NSValue *value = line.polyline[i]; CLLocationCoordinate2D coord = [value coordinateValue]; coords[i] = coord; } QPolyline *busPolyline = [[QPolyline alloc] initWithCoordinates:coords count:line.polyline.count]; busPolyline.userData = @"TRANSIT"; [self.mapView addOverlay:busPolyline]; }
-
最后,实现代理方法去绘制线路:
- (QOverlayView *)mapView:(QMapView *)mapView viewForOverlay:(id
)overlay { if ([overlay isKindOfClass:[QPolyline class]]) { QPolyline *polyline = (QPolyline *)overlay; NSString *userData = (NSString *)polyline.userData; if ([userData isEqualToString:@"WALKING"]) { // 步行虚线 QPolylineView *polylineView = [[QPolylineView alloc] initWithPolyline:polyline]; polylineView.lineWidth = 8; polylineView.lineDashPattern = @[@15, @10]; polylineView.strokeColor = [UIColor colorWithRed:0 green:0 blue:1 alpha:0.5]; return polylineView; } else { // 驾车蚯蚓线 QTexturePolylineView *polylineView = [[QTexturePolylineView alloc] initWithPolyline:polyline]; polylineView.drawType = QTextureLineDrawType_ColorLine; // 路线箭头 polylineView.drawSymbol = YES; polylineView.lineWidth = 8; polylineView.strokeColor = [UIColor colorWithRed:0 green:0 blue:1 alpha:0.8]; return polylineView; } } return nil; } -
补充:当添加完毕之后,让地图的视野自动适配路线
-
路线方案中的
NSString *bounds
属性标记了西南、东北两个方向的坐标,但是这个两个坐标组合成了一个字符串,还需要我们自己去解析:- (QCoordinateBounds)boundsFromString:(NSString *)boundsString { NSArray *array = [boundsString componentsSeparatedByString:@","]; NSString *southWestLatString = array[0]; NSString *southWestLongString = array[1]; NSString *northEastLatString = array[2]; NSString *northEastLongString = array[3]; CLLocationCoordinate2D northEast = CLLocationCoordinate2DMake(northEastLatString.floatValue, northEastLongString.floatValue); CLLocationCoordinate2D southWest = CLLocationCoordinate2DMake(southWestLatString.floatValue, southWestLongString.floatValue); QCoordinateBounds bounds; bounds.northEast = northEast; bounds.southWest = southWest; return bounds; }
-
最后调用地图接口,调整地图显示范围:
[self.mapView setVisibleMapRect:QMapRectForCoordinateBounds(bounds) edgePadding:UIEdgeInsetsMake(10, 10, 10, 10) animated:YES];
-
-
最终显示效果:
-
吐槽
- 官方文档还是有很多需要补充的地方,至少这些嵌套的路线方案(对象属性)就需要理解一阵。
- 坐标给的并不是很直接,有decode的NSValue类型,还有NSString类型需要去切割字符串然后再转化。
- 同一分段路线的不同公交方案,这里的处理需要细心处理。
- 为什么检索不支持文字的起点终点呢?
Demo下载
链接: https://pan.baidu.com/s/1vHbpPW5_1E0UmAlx5GBVQg 密码: q1tt