Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing a route in MapKit in iOS

Tags:

ios

iphone

mapkit

I want to draw a route between two locations on the map. Something like a tour guide. When the tourist clicks another location, I want to be able to draw a route; as well as, inform about the distance from the current location.

I am aware of sites on the Internet which tell how to draw polylines on map. But, most of the examples had a preloaded .csv file with various coordinates.

Is there an alternative way to get the coordinates from Google or any other provider, as the location is selected dynamically.

If NO, how do I get the information for intermediate coordinates?

Does iOS 6 provide any direct way for this problem?

like image 284
PRN Avatar asked May 14 '10 13:05

PRN


People also ask

How do I create a route in MapKit?

Open Xcode and create a new Single View Application. For product name, use IOS9DrawRouteMapKitTutorial and then fill out the Organization Name and Organization Identifier with your customary values. Enter Swift as Language and make sure only iPhone is selected in Devices.

How do I draw a path in Apple maps?

In the Maps app on your Mac, click a location on the map, such as an intersection, landmark, or business. In the place card, do one of the following: Click Create Route, then enter the destination in the To field (or click the Swap Directions button , then enter the starting point in the From field).

What is MapKit in iOS?

Overview. Use MapKit to give your app a sense of place with maps and location information. You can use the MapKit framework to: Embed maps directly into your app's windows and views. Add annotations and overlays to a map to call out points of interest.


2 Answers

This is a tricky one. There is no way to do that with MapKit: it's easy enough to draw lines when you know the coordinates, but MapKit won't give you access to the roads or other routing information. I'd say you need to call an external API to get your data.

I've been playing with cloudmade.com API. The vector stream server should return what you need, and then you can draw that over your map. However, discrepancies between the Google maps and the OSM maps used by cloudmade may make you want to use cloudmade maps all the way: they have an equivalent to MapKit.

P.S.: Other mapping providers - Google, Bing, etc. may also provide equivalent data feeds. I've just been looking at OSM/Cloudmade recently.

P.P.S.: None of this is trivial newbie stuff! Best of luck!

like image 22
Andiih Avatar answered Sep 22 '22 00:09

Andiih


The following viewDidLoad will (1) set two locations, (2) remove all the previous annotations, and (3) call user defined helper functions (to get route points and draw the 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]; } 

The following is the MKMapViewDelegate method, which draws overlay (iOS 4.0 and later).

/* MKMapViewDelegate Meth0d -- for viewForOverlay*/ - (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 the locations and prepare URL to get all the route points. And of course, will call stringWithURL.

/* This will get the route coordinates from the Google API. */ - (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 the real magic (decoder for the response we got from the API). I would not modify that code unless I 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 will 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 will center align 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]; } 

I hope this would help someone.

like image 158
viral Avatar answered Sep 21 '22 00:09

viral