八十六、车轮上的LBS(iOS端解决方案)

“吃穿住行”是生活的基本构成。拿“行”来说,从最早的人力车、马车到现今的高铁、飞机等,地上/下行驶着奔腾不息的公交、出租车、地铁乃至私家车。随着移动互联网不断渗透,涌现出一大批出行类软件。
出行类软件大概有以下几种:
打车类:滴滴打车、快的打车
专车类:滴滴专车、一号专车、神州专车
拼车类:嘀嗒拼车、51用车、天天用车
出行类用车乘客端的LBS部分,我们已经做好了实现方式。
我们的目标是:让天下没有难做的地图开发!
同时,源码已经开放,工程地址详见:https://github.com/hadesh/tripdemo

Step 1:定位+逆地理编码+Annotation,搞定前端展示

image
屏幕中间的小蓝点 显示当前位置用到了定位功能;位于顶部的输入框的起点信息 是根据定位位置进行逆地理编码查询,所返回的第一条POI的名称;地图上蓝色车标 是将附近的车主以Annotation展示。

定位

image
一句代码轻松搞定定位,只需开启定位即可(将MAMapView的showsUserLocation属性设置为YES)。 在ViewController中的viewDidAppear方法中开启定位的代码如下:

1
2
3
4
5
- (void)viewDidAppear:(BOOL)animated
{
    _mapView.delegate = self;
    _mapView.showsUserLocation = YES;//YES开启定位,NO关闭定位
}

为了获取定位点的经纬度坐标,需要在开启定位之前必须设置MAMapView的代理(_mapView.delegate),这里放到了viewDidAppear方法中的原因是:当3D地图复用的时候,可能代理会设置成其他的,所以要在显示的时候再设置回来。
定位回调中获取经纬度进行逆地理编码查询,定位回调代码如下:

1
2
3
4
- (void)mapView:(MAMapView *)mapView didUpdateUserLocation:(MAUserLocation *)userLocation updatingLocation:(BOOL)updatingLocation
{
    ……
}

 

逆地理编码

Demo中所有的搜索功能(如:逆地理编码、POI查询、路径规划等等,请参考:http://lbs.amap.com/api/ios-sdk/guide/amapsearch/)用block方式封装在了DDSearchManager中,这样可以避免在多处设置代理。
第一次定位和点击定位按钮,会进行逆地理编码查询,将查询出来的第一条POI作为起点。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//逆地理编码查询
- (void)searchReGeocodeWithCoordinate:(CLLocationCoordinate2D)coordinate
{
    //构造AMapReGeocodeSearchRequest对象,设置参数
    AMapReGeocodeSearchRequest *regeo = [[AMapReGeocodeSearchRequest alloc] init];
     
    regeo.location = [AMapGeoPoint locationWithLatitude:coordinate.latitude longitude:coordinate.longitude];
    regeo.requireExtension = YES;
     
    __weak __typeof(&*self) weakSelf = self;
    [[DDSearchManager sharedInstance] searchForRequest:regeo completionBlock:^(id request, id response, NSError *error) {
        if (error)
        {
            NSLog(@"error :%@", error);
        }
        else
        {
            //逆地理编码回调结果
            AMapReGeocodeSearchResponse * regeoResponse = response;
            if (regeoResponse.regeocode != nil)
            {
                if (regeoResponse.regeocode.pois.count > 0)
                {
                    AMapPOI *poi = regeoResponse.regeocode.pois[0];
                     
                    weakSelf.currentLocation.coordinate = CLLocationCoordinate2DMake(poi.location.latitude, poi.location.longitude);
                    weakSelf.currentLocation.name = poi.name;
                     
                    weakSelf.currentLocation.address = poi.address;
                }
                ……
            }
        }
    }];
}

 

Annotation(显示附近车主)

Demo中在DDDriverManager中模拟了主车数据,您也可以自己传入自己的数据。 以Annotation显示附近车主的步骤如下:
a) 构造MAPointAnnotation,并MAMapview的addAnnoations方法添加到地图上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
- (void)searchDoneInMapRect:(MAMapRect)mapRect withDriversResult:(NSArray *)drivers timestamp:(NSTimeInterval)timestamp
{
    [_mapView removeAnnotations:_drivers];
     
    NSMutableArray * currDrivers = [NSMutableArray arrayWithCapacity:[drivers count]];
    [drivers enumerateObjectsUsingBlock:^(DDDriver * obj, NSUInteger idx, BOOL *stop) {
        MAPointAnnotation * driver = [[MAPointAnnotation alloc] init];
        driver.coordinate = obj.coordinate;
        driver.title = obj.idInfo;
        [currDrivers addObject:driver];
    }];
     
    [_mapView addAnnotations:currDrivers];
     
    _drivers = currDrivers;
}
b)  实现MAMapViewDelegate中viewForAnnotation回调,以 图标显示车主位置。代码如下:
- (MAAnnotationView *)mapView:(MAMapView *)mapView viewForAnnotation:(id<maannotation>)annotation
{
    if ([annotation isKindOfClass:[MAPointAnnotation class]])
    {
        static NSString *pointReuseIndetifier = @"driver";
         
        MovingAnnotationView *annotationView = (MovingAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:pointReuseIndetifier];
        if (annotationView == nil)
        {
            annotationView = [[MovingAnnotationView alloc] initWithAnnotation:annotation
                                                              reuseIdentifier:pointReuseIndetifier];
        }
         
        UIImage *image = [UIImage imageNamed:@"icon_taxi"];
         
        annotationView.image = image; //车主图标
        annotationView.centerOffset = CGPointMake(0, -image.size.height / 2.0);
        annotationView.canShowCallout = YES;//可弹出气泡
         
        return annotationView;
    }
    return nil;
}
</maannotation>

Step 2 POI搜索,完成目的地选择(起点)

通过POI搜索,检索出目的地。同时,也可更改起点地址。
image
通过键盘输入一个字进行一次POI查询,并将返回的结果AMapPOI对象的name和address显示到TableView中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
- (void)searchTipsWithKey:(NSString *)key
{
    if (key.length == 0)
    {
        return;
    }
    //构造AMapPlaceSearchRequest对象,配置POI查询参数
    AMapPlaceSearchRequest *request = [[AMapPlaceSearchRequest alloc] init];
    request.requireExtension = YES;
    request.searchType = AMapSearchType_PlaceKeyword;
    request.keywords = key;
     
    if (self.city.length > 0)
    {
        request.city = @[self.city];
    }
     
    __weak __typeof(&*self) weakSelf = self;
    [[DDSearchManager sharedInstance] searchForRequest:request completionBlock:^(id request, id response, NSError *error) {
        if (error)
        {
            NSLog(@"error :%@", error);
        }
        else
        {
            [weakSelf.locations removeAllObjects];
            //POI查询回调结果
            AMapPlaceSearchResponse *aResponse = (AMapPlaceSearchResponse *)response;
            [aResponse.pois enumerateObjectsUsingBlock:^(AMapPOI *obj, NSUInteger idx, BOOL *stop)
             {
                 DDLocation *location = [[DDLocation alloc] init];
                 location.name = obj.name;
                 location.coordinate = CLLocationCoordinate2DMake(obj.location.latitude, obj.location.longitude);
                 location.address = obj.address;
                 location.cityCode = obj.citycode;
                 [weakSelf.locations addObject:location];
             }];
             
            [weakSelf.displayController.searchResultsTableView reloadData];
        }
    }];
}

Step 3 路径规划,搞定费用估算

根据起点和终点,进行驾车路径规划,将返回的出租车费用作为估算费用。
image
ViewController中requestPathInfo方法中进行驾车路径规划查询代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
- (void)requestPathInfo
{
    //检索所需费用(发起驾车路径规划)
    AMapNavigationSearchRequest *navi = [[AMapNavigationSearchRequest alloc] init];
    navi.searchType       = AMapSearchType_NaviDrive;
    navi.requireExtension = YES;
     
    /* 出发点. */
    navi.origin = [AMapGeoPoint locationWithLatitude:_currentLocation.coordinate.latitude
                                           longitude:_currentLocation.coordinate.longitude];
    /* 目的地. */
    navi.destination = [AMapGeoPoint locationWithLatitude:_destinationLocation.coordinate.latitude
                                                longitude:_destinationLocation.coordinate.longitude];
     
    __weak __typeof(&*self) weakSelf = self;
    [[DDSearchManager sharedInstance] searchForRequest:navi completionBlock:^(id request, id response, NSError *error) {
        //路径规划回调结果
        AMapNavigationSearchResponse * naviResponse = response;
         
        if (naviResponse.route == nil)
        {
            [weakSelf.locationView setInfo:@"获取路径失败"];
            return;
        }
         
        AMapPath * path = [naviResponse.route.paths firstObject];
        [weakSelf.locationView setInfo:[NSString stringWithFormat:@"预估费用%.2f元  距离%.1f km  时间%.1f分钟", naviResponse.route.taxiCost, path.distance / 1000.f, path.duration / 60.f, nil]];
    }];
}

Step4 行驶距离测量,搞定通知最优车主

乘客发出用车请求后,需要对司机进行叫车信息派送,通过多点到单点的行驶距离测量 REST API接口实现最优车主通知。该部分一般在服务端实现。行驶距离测量尚未对外开放,开发者可邮件api@autonavi.com发送申请,申请时请写明 产品、应用场景,待审批后即可开放权限使用。
接口使用场景说明:
行驶距离测量接口可用于出行类产品服务器端分单应用场景,提供从多个起点(司机)到一个终点(乘客)的路径规划计算,按照传入数据顺序返回每个起点到终点的行驶距离及行驶时间,开发者可根据自有业务数据同当前行驶距离/行驶时间相结合,进行业务派单及类似的需求实现。
调用代码(JAVA):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
       * 行驶距离测量
       *@param key      用户key
       *@param origins  起始点 例如116.493381,39.978437,270
       *@param key      终点   例如116.493441,39.978887
       * */
       public static void distance (String key , String origins , String destination){
              String url = "http://restapi.amap.com/v3/distance?output=json&key=";
              url += key;
              url += "&origins=" + origins;
              url += "&destination=" + destination;
               
              HttpRequester request = new HttpRequester();    
              HttpRespons hr = null;
              try {
                     hr = request.sendGet(url);  //建立HTTP请求
              } catch (IOException e) {
                     e.printStackTrace();
              }     
              String content =hr.getContent();  //获取请求数据
               
              if (null!=content) {
                     JSONObject jsonData = JSONObject.fromObject(content); //数据解析
                     JSONArray resultArr = jsonData.optJSONArray("results");
                     if (resultArr != null) {
                            int size = resultArr.size();
                            for (int i = 0; i < size; i++) {
                                   JSONObject result = resultArr.getJSONObject(i);
                                   String distance = result.optString("distance");  //行驶距离
                                   String duration = result.optString("duration");  //行驶时间
                                   System.out.println("distance="+distance+"   duration="+duration); //开发者验证数据
                            }
                     }
              }
       }

posted on 2015-05-25 16:04  PerfectBody  阅读(283)  评论(0)    收藏  举报

导航