如何将Google Directions API折线字段解码为适用于iPhone的objective-C中的lat长点?

Ala*_*ore 45 iphone json objective-c google-directions-api google-polyline

我想在地图上绘制与我通过Google Directions API获得的路线JSON相对应的路线:http://code.google.com/apis/maps/documentation/directions/

我已经想出如何从阶段字段中提取纬度和经度,但这并不能很好地遵循弯曲的道路.我认为我需要的是解码折线信息,我找到了关于如何编码折线的Googles说明:http://code.google.com/apis/maps/documentation/utilities/polylinealgorithm.html

我确实在这里找到了一些代码用于Android以及解码折线的Javascript,例如:

使用Google Directions API地图查看绘制路线 - 解码折线

android获取并解析Google Directions

但是对于Objective-C iPhone代码我找不到相同的内容,任何人都可以帮我这个吗?我敢肯定,如果必须的话,我可以自己做,但如果它已经可以在某个地方使用,那肯定能节省我一些时间...

编辑:这里的关键是能够逐个字符地解码base64编码.更具体地说,我在谷歌的JSON中得到了类似的东西,它使用base64编码进行编码:

...   "overview_polyline" : {
        "points" : "ydelDz~vpN_@NO@QEKWIYIIO?YCS@WFGBEBICCAE?G@y@RKBEBEBAD?HTpB@LALALCNEJEFSP_@LyDv@aB\\GBMB"
       },
...
Run Code Online (Sandbox Code Playgroud)

注意:我应该提一下,这个问题是指Google Maps API v1,使用GMSPolyLine polyLineWithPath在v2中更容易实现,因为下面的许多答案都会告诉你(感谢@cdescours).

Sed*_*ien 85

如果它与问题相关,我希望链接到我自己的博客文章并不违反规则,但我过去已经解决了这个问题.链接帖子的独立答案:

@implementation MKPolyline (MKPolyline_EncodedString)

+ (MKPolyline *)polylineWithEncodedString:(NSString *)encodedString {
    const char *bytes = [encodedString UTF8String];
    NSUInteger length = [encodedString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
    NSUInteger idx = 0;

    NSUInteger count = length / 4;
    CLLocationCoordinate2D *coords = calloc(count, sizeof(CLLocationCoordinate2D));
    NSUInteger coordIdx = 0;

    float latitude = 0;
    float longitude = 0;
    while (idx < length) {
        char byte = 0;
        int res = 0;
        char shift = 0;

        do {
            byte = bytes[idx++] - 63;
            res |= (byte & 0x1F) << shift;
            shift += 5;
        } while (byte >= 0x20);

        float deltaLat = ((res & 1) ? ~(res >> 1) : (res >> 1));
        latitude += deltaLat;

        shift = 0;
        res = 0;

        do {
            byte = bytes[idx++] - 0x3F;
            res |= (byte & 0x1F) << shift;
            shift += 5;
        } while (byte >= 0x20);

        float deltaLon = ((res & 1) ? ~(res >> 1) : (res >> 1));
        longitude += deltaLon;

        float finalLat = latitude * 1E-5;
        float finalLon = longitude * 1E-5;

        CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(finalLat, finalLon);
        coords[coordIdx++] = coord;

        if (coordIdx == count) {
            NSUInteger newCount = count + 10;
            coords = realloc(coords, newCount * sizeof(CLLocationCoordinate2D));
            count = newCount;
        }
    }

    MKPolyline *polyline = [MKPolyline polylineWithCoordinates:coords count:coordIdx];
    free(coords);

    return polyline;
}

@end
Run Code Online (Sandbox Code Playgroud)

  • 这不是违反我的规则,谢谢你的链接.这看起来正是我需要的.我给你复选标记 - 假设它对我有效:) (2认同)
  • 干得好,也应该给予应有的好评,看起来像http://jeffreysambells.com/2010/05/27/decoding-polylines-from-google-maps-direction-api-with-java. (2认同)
  • 有关更新的答案,请参阅下面的@cdescours答案 (2认同)

cde*_*urs 31

最好和最轻的答案应该是使用Google在框架中提供的方法:

[GMSPolyline polylineWithPath:[GMSPath pathFromEncodedPath:encodedPath]]

  • 谢谢你,先生.很高兴我向下滚动. (7认同)

Roo*_*ode 10

如果您正在iOS上使用谷歌地图并希望绘制包含折线的路线,谷歌本身提供了一种更简单的方法来从折线获取GMSPath,

GMSPath *pathFromPolyline = [GMSPath pathFromEncodedPath:polyLinePoints];
Run Code Online (Sandbox Code Playgroud)

这是完整的代码:

+ (void)callGoogleServiceToGetRouteDataFromSource:(CLLocation *)sourceLocation toDestination:(CLLocation *)destinationLocation onMap:(GMSMapView *)mapView_{
    NSString *baseUrl = [NSString stringWithFormat:@"http://maps.googleapis.com/maps/api/directions/json?origin=%f,%f&destination=%f,%f&sensor=false", sourceLocation.coordinate.latitude,  sourceLocation.coordinate.longitude, destinationLocation.coordinate.latitude,  destinationLocation.coordinate.longitude];

    NSURL *url = [NSURL URLWithString:[baseUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];

    NSLog(@"Url: %@", url);

    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {

        GMSMutablePath *path = [GMSMutablePath path];

        NSError *error = nil;
        NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];

        NSArray *routes = [result objectForKey:@"routes"];

        NSDictionary *firstRoute = [routes objectAtIndex:0];

        NSDictionary *leg =  [[firstRoute objectForKey:@"legs"] objectAtIndex:0];

        NSArray *steps = [leg objectForKey:@"steps"];

        int stepIndex = 0;

        CLLocationCoordinate2D stepCoordinates[1  + [steps count] + 1];

        for (NSDictionary *step in steps) {

            NSDictionary *start_location = [step objectForKey:@"start_location"];
            stepCoordinates[++stepIndex] = [self coordinateWithLocation:start_location];
            [path addCoordinate:[self coordinateWithLocation:start_location]];

            NSString *polyLinePoints = [[step objectForKey:@"polyline"] objectForKey:@"points"];
            GMSPath *polyLinePath = [GMSPath pathFromEncodedPath:polyLinePoints];
            for (int p=0; p<polyLinePath.count; p++) {
                [path addCoordinate:[polyLinePath coordinateAtIndex:p]];
            }


            if ([steps count] == stepIndex){
                NSDictionary *end_location = [step objectForKey:@"end_location"];
                stepCoordinates[++stepIndex] = [self coordinateWithLocation:end_location];
                [path addCoordinate:[self coordinateWithLocation:end_location]];
            }
        }

        GMSPolyline *polyline = nil;
        polyline = [GMSPolyline polylineWithPath:path];
        polyline.strokeColor = [UIColor grayColor];
        polyline.strokeWidth = 3.f;
        polyline.map = mapView_;
    }];
}

+ (CLLocationCoordinate2D)coordinateWithLocation:(NSDictionary*)location
{
    double latitude = [[location objectForKey:@"lat"] doubleValue];
    double longitude = [[location objectForKey:@"lng"] doubleValue];

    return CLLocationCoordinate2DMake(latitude, longitude);
}
Run Code Online (Sandbox Code Playgroud)


api*_*nho 6

斯威夫特 3.0

let polyline = GMSPolyline(path: GMSPath.init(fromEncodedPath: encodedPolyline))
Run Code Online (Sandbox Code Playgroud)


Mik*_*tes 5

Python实现

这不是 Objective-C 中的内容,但如果您想从 Google 地图中解码折线字符串,那么 Google 会将您带到这个线程。如果其他人需要它(就像我一样),这里有一个用于解码折线字符串的 Python 实现。这是从 Mapbox JavaScript 版本移植的;在我的回购页面上找到更多信息。

def decode_polyline(polyline_str):
    index, lat, lng = 0, 0, 0
    coordinates = []
    changes = {'latitude': 0, 'longitude': 0}

    # Coordinates have variable length when encoded, so just keep
    # track of whether we've hit the end of the string. In each
    # while loop iteration, a single coordinate is decoded.
    while index < len(polyline_str):
        # Gather lat/lon changes, store them in a dictionary to apply them later
        for unit in ['latitude', 'longitude']: 
            shift, result = 0, 0

            while True:
                byte = ord(polyline_str[index]) - 63
                index+=1
                result |= (byte & 0x1f) << shift
                shift += 5
                if not byte >= 0x20:
                    break

            if (result & 1):
                changes[unit] = ~(result >> 1)
            else:
                changes[unit] = (result >> 1)

        lat += changes['latitude']
        lng += changes['longitude']

        coordinates.append((lat / 100000.0, lng / 100000.0))

    return coordinates
Run Code Online (Sandbox Code Playgroud)


kal*_*esh 5

- (MKPolyline *)polylineWithEncodedString:(NSString *)encodedString {
    const char *bytes = [encodedString UTF8String];
    NSUInteger length = [encodedString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
    NSUInteger idx = 0;
    NSUInteger count = length / 4;
    CLLocationCoordinate2D *coords = calloc(count, sizeof(CLLocationCoordinate2D));
    NSUInteger coordIdx = 0;
    float latitude = 0;
    float longitude = 0;
    while (idx < length) {
        char byte = 0;
        int res = 0;
        char shift = 0;
        do {
            byte = bytes[idx++] - 63;
            res |= (byte & 0x1F) << shift;
            shift += 5;
        } while (byte >= 0x20);

        float deltaLat = ((res & 1) ? ~(res >> 1) : (res >> 1));
        latitude += deltaLat;

        shift = 0;
        res = 0;

        do {
            byte = bytes[idx++] - 0x3F;
            res |= (byte & 0x1F) << shift;
            shift += 5;
        } while (byte >= 0x20);

        float deltaLon = ((res & 1) ? ~(res >> 1) : (res >> 1));
        longitude += deltaLon;

        float finalLat = latitude * 1E-5;
        float finalLon = longitude * 1E-5;

        CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(finalLat, finalLon);
        coords[coordIdx++] = coord;

        if (coordIdx == count) {
            NSUInteger newCount = count + 10;
            coords = realloc(coords, newCount * sizeof(CLLocationCoordinate2D));
            count = newCount;
        }
    }

    MKPolyline *polyline = [MKPolyline polylineWithCoordinates:coords count:coordIdx];
    free(coords);
    return polyline;
}
- (MKPolygonRenderer *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
  //  MKPolygonRenderer *polylineView = [[MKPolygonRenderer alloc] initWithOverlay:overlay];
    MKPolylineView *polylineView = [[MKPolylineView alloc] initWithPolyline:overlay];
    polylineView.strokeColor = [UIColor redColor];
    polylineView.lineWidth = 4.0;
    [self zoomToPolyLine:mapview polyline:overlay animated:YES];
    return polylineView;
}
-(void)zoomToPolyLine: (MKMapView*)map polyline: (MKPolyline*)polyline animated: (BOOL)animated
{
    [map setVisibleMapRect:[polyline boundingMapRect] edgePadding:UIEdgeInsetsMake(25.0, 25.0, 25.0, 25.0) animated:animated];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
   // NSLog(@"didUpdateToLocation: %@", newLocation);
    CLLocation *currentLocation = newLocation;
    if (currentLocation != nil) {
      currlong  =  [NSString stringWithFormat:@"%.8f", currentLocation.coordinate.longitude];
       currlt = [NSString stringWithFormat:@"%.8f", currentLocation.coordinate.latitude];
    }
    NSString *origin = [NSString stringWithFormat:@"%@%@%@",currlt,@",",currlong];

    //I have just mention static location
    NSString *drivein = @"23.0472963,72.52757040000006";
    NSString *apikey = [NSString stringWithFormat:@"https://maps.googleapis.com/maps/api/directions/json?origin=%@&destination=%@",origin,drivein];

    NSURL *url = [NSURL URLWithString:apikey];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLResponse *response;
    NSError *error;
    NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];

    if(!error)
    {
        NSData *data = [responseString dataUsingEncoding:NSUTF8StringEncoding];
        NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data
                                                                     options:kNilOptions
                                                                       error:&error];
        NSArray *routesArray = [jsonResponse objectForKey:@"routes"];
        NSLog(@"route array %@",routesArray);
        if ([routesArray count] > 0)
        {
            NSDictionary *routeDict = [routesArray objectAtIndex:0];
            NSDictionary *routeOverviewPolyline = [routeDict objectForKey:@"overview_polyline"];
            NSString *points = [routeOverviewPolyline objectForKey:@"points"];
            MKPolyline *line = [self polylineWithEncodedString:points];
            [mapview addOverlay:line];
        }
    }
    MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(currentLocation.coordinate, 500, 500);
    MKCoordinateRegion adjustedRegion = [mapview regionThatFits:viewRegion];
    [mapview setRegion:adjustedRegion animated:YES];
    mapview.showsUserLocation = YES;

    MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
    point.coordinate = currentLocation.coordinate;
    point.title = @"Your current Locations";
    point.subtitle = @"You are here!";
    [mapview addAnnotation:point];
    [locationmanger stopUpdatingLocation];
}
Run Code Online (Sandbox Code Playgroud)