How to decode the Google Directions API polylines field into lat long points in Objective-C for iPhone?

后端 未结 13 976
孤街浪徒
孤街浪徒 2020-11-30 19:05

I want to draw routes on a map corresponding to directions JSON which I am getting through the Google Directions API: https://developers.google.com/maps/documentation/direct

相关标签:
13条回答
  • 2020-11-30 19:44

    The other answers here seem to be about using Apple Maps, for using Google Maps I found I had to make some modifications to @SedateAlien's great category.

    MODIFIED CATEGORY

    + (GMSPolyline *)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;
            }
        }
    
        GMSMutablePath *path = [[GMSMutablePath alloc] init];
    
        int i;
        for (i = 0; i < coordIdx; i++)
        {
            [path addCoordinate:coords[i]];
        }
    
        GMSPolyline *polyline = [GMSPolyline polylineWithPath:path];
        free(coords);
    
        return polyline;
    }
    

    USAGE

    // Here I make the call to the Google Maps API to get the routes between two points...
    
    ....
    
    // Get the encoded array of points.
    NSString *points = routes[@"routes"][0][@"overview_polyline"][@"points"];
    
    // Use the modified category to get a polyline from the points.
    GMSPolyline *polyline = [GMSPolyline polylineWithEncodedString:points];
    
    // Add the polyline to the map.
    polyline.strokeColor = [UIColor redColor];
    polyline.strokeWidth = 10.f;
    polyline.map = theMapView;
    }
    
    0 讨论(0)
  • 2020-11-30 19:45

    Here's how I do it in my directions app. keyPlace is your destination object

    - (void)getDirections {
    
      CLLocation *newLocation;// = currentUserLocation;
      MKPointAnnotation *annotation = [[[MKPointAnnotation alloc] init] autorelease];
      annotation.coordinate = CLLocationCoordinate2DMake(newLocation.coordinate.latitude, newLocation.coordinate.longitude);
      annotation.title = @"You";
      [mapView addAnnotation:annotation];
    
      CLLocationCoordinate2D endCoordinate;
    
      NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"https://maps.googleapis.com/maps/api/directions/json?origin=%f,%f&destination=%f,%f&sensor=false&mode=walking", newLocation.coordinate.latitude, newLocation.coordinate.longitude, keyPlace.lat, keyPlace.lon]];
      ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
      [request startSynchronous];
    
      if ([[request.responseString.JSONValue valueForKey:@"status"] isEqualToString:@"ZERO_RESULTS"]) {
        [[[[UIAlertView alloc] initWithTitle:@"Error"
                                     message:@"Could not route path from your current location"
                                    delegate:nil
                           cancelButtonTitle:@"Close"
                           otherButtonTitles:nil, nil] autorelease] show];
        self.navigationController.navigationBar.userInteractionEnabled = YES;
        return; 
      }
    
      int points_count = 0;
      if ([[request.responseString.JSONValue objectForKey:@"routes"] count])
        points_count = [[[[[[request.responseString.JSONValue objectForKey:@"routes"] objectAtIndex:0] objectForKey:@"legs"] objectAtIndex:0] objectForKey:@"steps"] count];
    
      if (!points_count) {
        [[[[UIAlertView alloc] initWithTitle:@"Error"
                                     message:@"Could not route path from your current location"
                                    delegate:nil
                           cancelButtonTitle:@"Close"
                           otherButtonTitles:nil, nil] autorelease] show];
        self.navigationController.navigationBar.userInteractionEnabled = YES;
        return;     
      }
      CLLocationCoordinate2D points[points_count * 2];
    
      int j = 0;
      NSArray *steps = nil;
      if (points_count && [[[[request.responseString.JSONValue objectForKey:@"routes"] objectAtIndex:0] objectForKey:@"legs"] count])
        steps = [[[[[request.responseString.JSONValue objectForKey:@"routes"] objectAtIndex:0] objectForKey:@"legs"] objectAtIndex:0] objectForKey:@"steps"];
      for (int i = 0; i < points_count; i++) {
    
        double st_lat = [[[[steps objectAtIndex:i] objectForKey:@"start_location"] valueForKey:@"lat"] doubleValue];
        double st_lon = [[[[steps objectAtIndex:i] objectForKey:@"start_location"] valueForKey:@"lng"] doubleValue];
        //NSLog(@"lat lon: %f %f", st_lat, st_lon);
        if (st_lat > 0.0f && st_lon > 0.0f) {
          points[j] = CLLocationCoordinate2DMake(st_lat, st_lon);
          j++;
        }
        double end_lat = [[[[steps objectAtIndex:i] objectForKey:@"end_location"] valueForKey:@"lat"] doubleValue];
        double end_lon = [[[[steps objectAtIndex:i] objectForKey:@"end_location"] valueForKey:@"lng"] doubleValue];
    
        if (end_lat > 0.0f && end_lon > 0.0f) {
          points[j] = CLLocationCoordinate2DMake(end_lat, end_lon);
          endCoordinate = CLLocationCoordinate2DMake(end_lat, end_lon);
          j++;
        }
      }
    
      MKPolyline *polyline = [MKPolyline polylineWithCoordinates:points count:points_count * 2];
      [mapView addOverlay:polyline];
    
    
    }
    
    #pragma mark - MapKit
    - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
      MKPinAnnotationView *annView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"currentloc"] autorelease];
      annView.canShowCallout = YES;
      annView.animatesDrop = YES;
      return annView;
    }
    
    - (MKOverlayView *)mapView:(MKMapView *)mapView
                viewForOverlay:(id<MKOverlay>)overlay {
      MKPolylineView *overlayView = [[[MKPolylineView alloc] initWithOverlay:overlay] autorelease];
      overlayView.lineWidth = 5;
      overlayView.strokeColor = [UIColor purpleColor];
      overlayView.fillColor = [[UIColor purpleColor] colorWithAlphaComponent:0.5f];
      return overlayView;
    }
    
    0 讨论(0)
  • 2020-11-30 19:45

    In case anyone would need the decoding-code in VBA, here is a (working) port:

        Function decodeGeopoints(encoded)
      decodeGeopoints = ""
      ' This code is a port to VBA from code published here:
      ' http://blog.synyx.de/2010/06/routing-driving-directions-on-android-part-1-get-the-route/
    
      '//decoding
      'List poly = new ArrayList();
    
      '// replace two backslashes by one (some error from the transmission)
      'encoded = encoded.replace("\\", "\");
      encoded = Replace(encoded, "\\", "\")
    
      'int index = 0, len = encoded.length();
      Dim index As Long
      index = 0
      Dim leng As Long
      leng = Len(encoded)
    
      'int lat = 0, lng = 0;
      Dim lat As Long
      lat = 0
      Dim lng As Long
      lng = 0
    
      'while (index < len) {
      While (index < leng)
         'int b, shift = 0, result = 0;
         Dim b, shift, result As Long
         b = 0
         shift = 0
         result = 0
    
         'do {
         Do
            'b = encoded.charAt(index++) - 63;
            index = index + 1
            b = Asc(Mid(encoded, index, 1)) - 63
            'result |= (b & 0x1f) << shift;
            result = result Or ((b And 31) * (2 ^ shift))
    
            'shift += 5;
            shift = shift + 5
         '} while (b >= 0x20);
         Loop While (b >= 32)
         'int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
         Dim dlat As Long
         If (result And 1) <> 0 Then
          dlat = Not Int(result / 2)
         Else
          dlat = Int(result / 2)
         End If
    
         'lat += dlat;
         lat = lat + dlat
    
         'shift = 0;
         shift = 0
         'result = 0;
         result = 0
         'do {
         Do
           'b = encoded.charAt(index++) - 63;
           index = index + 1
           b = Asc(Mid(encoded, index, 1)) - 63
           'result |= (b & 0x1f) << shift;
            result = result Or ((b And 31) * (2 ^ shift))
           'shift += 5;
            shift = shift + 5
         '} while (b >= 0x20);
         Loop While (b >= 32)
         'int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
         Dim dlng As Long
         If (result And 1) <> 0 Then
          dlng = Not Int(result / 2)
         Else
          dlng = Int(result / 2)
         End If
    
         'lng += dlng;
         lng = lng + dlng
    
         'GeoPoint p = new GeoPoint((int) (((double) lat / 1E5) * 1E6), (int) (((double) lng / 1E5) * 1E6));
         Dim myLat, myLng As Double
         myLat = (lat / 100000)
         'myLat = myLat * 1000000
         myLng = (lng / 100000)
         'myLng = myLng * 1000000
    
         'poly.add(p);
         decodeGeopoints = decodeGeopoints & Comma2Dot(myLng) & "," & Comma2Dot(myLat) & ",0 "
      '}
      Wend
    
    End Function
    
    0 讨论(0)
  • 2020-11-30 19:46

    The best and lightest answer should be to use the method provided by Google in the framework :

    [GMSPolyline polylineWithPath:[GMSPath pathFromEncodedPath:encodedPath]]

    0 讨论(0)
  • 2020-11-30 19:47

    Swift 4.2 / Swift 5

    let gmsPolyline = GMSPolyline(path: GMSPath(fromEncodedPath: encodedPolyline))
    gmsPolyline.map = map
    
    0 讨论(0)
  • 2020-11-30 19:52

    Swift 3.0

    let polyline = GMSPolyline(path: GMSPath.init(fromEncodedPath: encodedPolyline))
    
    0 讨论(0)
提交回复
热议问题