The next viewDidLoad will (1) set two locations, (2) delete all previous annotations, and (3) call custom helper functions (to get waypoints and draw a route).
-(void)viewDidLoad { [super viewDidLoad]; // Origin Location. CLLocationCoordinate2D loc1; loc1.latitude = 29.0167; loc1.longitude = 77.3833; Annotation *origin = [[Annotation alloc] initWithTitle:@"loc1" subTitle:@"Home1" andCoordinate:loc1]; [objMapView addAnnotation:origin]; // Destination Location. CLLocationCoordinate2D loc2; loc2.latitude = 19.076000; loc2.longitude = 72.877670; Annotation *destination = [[Annotation alloc] initWithTitle:@"loc2" subTitle:@"Home2" andCoordinate:loc2]; [objMapView addAnnotation:destination]; if(arrRoutePoints) // Remove all annotations [objMapView removeAnnotations:[objMapView annotations]]; arrRoutePoints = [self getRoutePointFrom:origin to:destination]; [self drawRoute]; [self centerMap]; }
Below is the MKMapViewDelegate method, which imposes an overlay (iOS 4.0 and later).
- (MKOverlayView*)mapView:(MKMapView*)theMapView viewForOverlay:(id <MKOverlay>)overlay { MKPolylineView *view = [[MKPolylineView alloc] initWithPolyline:objPolyline]; view.fillColor = [UIColor blackColor]; view.strokeColor = [UIColor blackColor]; view.lineWidth = 4; return view; }
The following function will get both places and prepare the URL to get all the waypoints. And of course, will call stringWithURL.
- (NSArray*)getRoutePointFrom:(Annotation *)origin to:(Annotation *)destination { NSString* saddr = [NSString stringWithFormat:@"%f,%f", origin.coordinate.latitude, origin.coordinate.longitude]; NSString* daddr = [NSString stringWithFormat:@"%f,%f", destination.coordinate.latitude, destination.coordinate.longitude]; NSString* apiUrlStr = [NSString stringWithFormat:@"http://maps.google.com/maps?output=dragdir&saddr=%@&daddr=%@", saddr, daddr]; NSURL* apiUrl = [NSURL URLWithString:apiUrlStr]; NSError *error; NSString *apiResponse = [NSString stringWithContentsOfURL:apiUrl encoding:NSUTF8StringEncoding error:&error]; NSString* encodedPoints = [apiResponse stringByMatching:@"points:\\\"([^\\\"]*)\\\"" capture:1L]; return [self decodePolyLine:[encodedPoints mutableCopy]]; }
The following code is real magic (decoder for the response we received from the API). I would not change this code if I do not know what I am doing :)
- (NSMutableArray *)decodePolyLine:(NSMutableString *)encodedString { [encodedString replaceOccurrencesOfString:@"\\\\" withString:@"\\" options:NSLiteralSearch range:NSMakeRange(0, [encodedString length])]; NSInteger len = [encodedString length]; NSInteger index = 0; NSMutableArray *array = [[NSMutableArray alloc] init]; NSInteger lat=0; NSInteger lng=0; while (index < len) { NSInteger b; NSInteger shift = 0; NSInteger result = 0; do { b = [encodedString characterAtIndex:index++] - 63; result |= (b & 0x1f) << shift; shift += 5; } while (b >= 0x20); NSInteger dlat = ((result & 1) ? ~(result >> 1) : (result >> 1)); lat += dlat; shift = 0; result = 0; do { b = [encodedString characterAtIndex:index++] - 63; result |= (b & 0x1f) << shift; shift += 5; } while (b >= 0x20); NSInteger dlng = ((result & 1) ? ~(result >> 1) : (result >> 1)); lng += dlng; NSNumber *latitude = [[NSNumber alloc] initWithFloat:lat * 1e-5]; NSNumber *longitude = [[NSNumber alloc] initWithFloat:lng * 1e-5]; printf("\n[%f,", [latitude doubleValue]); printf("%f]", [longitude doubleValue]); CLLocation *loc = [[CLLocation alloc] initWithLatitude:[latitude floatValue] longitude:[longitude floatValue]]; [array addObject:loc]; } return array; }
This function will draw a route and add an overlay.
- (void)drawRoute { int numPoints = [arrRoutePoints count]; if (numPoints > 1) { CLLocationCoordinate2D* coords = malloc(numPoints * sizeof(CLLocationCoordinate2D)); for (int i = 0; i < numPoints; i++) { CLLocation* current = [arrRoutePoints objectAtIndex:i]; coords[i] = current.coordinate; } self.objPolyline = [MKPolyline polylineWithCoordinates:coords count:numPoints]; free(coords); [objMapView addOverlay:objPolyline]; [objMapView setNeedsDisplay]; } }
The following code centers the alignment of the map.
- (void)centerMap { MKCoordinateRegion region; CLLocationDegrees maxLat = -90; CLLocationDegrees maxLon = -180; CLLocationDegrees minLat = 90; CLLocationDegrees minLon = 180; for(int idx = 0; idx < arrRoutePoints.count; idx++) { CLLocation* currentLocation = [arrRoutePoints objectAtIndex:idx]; if(currentLocation.coordinate.latitude > maxLat) maxLat = currentLocation.coordinate.latitude; if(currentLocation.coordinate.latitude < minLat) minLat = currentLocation.coordinate.latitude; if(currentLocation.coordinate.longitude > maxLon) maxLon = currentLocation.coordinate.longitude; if(currentLocation.coordinate.longitude < minLon) minLon = currentLocation.coordinate.longitude; } region.center.latitude = (maxLat + minLat) / 2; region.center.longitude = (maxLon + minLon) / 2; region.span.latitudeDelta = maxLat - minLat; region.span.longitudeDelta = maxLon - minLon; [objMapView setRegion:region animated:YES]; }
Hope this helps someone.