Drawing polyines or routes on a MKMapView in iOS4 – Part 3
Get right to it: here’s the sample code
With the release of iOS 4, Apple has drastically decreased the complexity involved in rendering shapes, including routes, on a map. In fact, they’ve added an entire set of classes to MapKit specifically designed to help you add shapes to a map. These new classes and protocols are:
MKCircle
MKCircleView
MKMultiPoint
MKOverlayPathView
MKPolygon
MKPolygonView
MKPolyline
MKPolylineView
MKShape
MKOverlay
These classes make it incredibly easy to do what was once complex (And slightly buggy). Before when you would scroll or zoom in on a map with a route rendered in an MKAnnotationView, the route would have to temporarily be hidden because it was not scaled or scrolled along with with map. the MapKit framework now handles all this automatically.
So how do you add a route to a map using the updated MKMapView in iOS 4?
- Create a MKPolyline
- Add the polyline (as a MKOverlay) to the map
- Implement mapView:viewForOverlay: in your MKMapViewDelegate
- Initialize, set values on, and return a MKPolylineView from your updated map view delegate
Here’s some sample code that shows how you might create your MKPolyline:
-(void) loadRoute
{
NSString* filePath = [[NSBundle mainBundle] pathForResource:@”route” ofType:@”csv”];
NSString* fileContents = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
NSArray* pointStrings = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];// while we create the route points, we will also be calculating the bounding box of our route
// so we can easily zoom in on it.
MKMapPoint northEastPoint;
MKMapPoint southWestPoint;// create a c array of points.
MKMapPoint* pointArr = malloc(sizeof(CLLocationCoordinate2D) * pointStrings.count);for(int idx = 0; idx < pointStrings.count; idx++)
{
// break the string down even further to latitude and longitude fields.
NSString* currentPointString = [pointStrings objectAtIndex:idx];
NSArray* latLonArr = [currentPointString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@","]];CLLocationDegrees latitude = [[latLonArr objectAtIndex:0] doubleValue];
CLLocationDegrees longitude = [[latLonArr objectAtIndex:1] doubleValue];// create our coordinate and add it to the correct spot in the array
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(latitude, longitude);MKMapPoint point = MKMapPointForCoordinate(coordinate);
//
// adjust the bounding box
//// if it is the first point, just use them, since we have nothing to compare to yet.
if (idx == 0) {
northEastPoint = point;
southWestPoint = point;
}
else
{
if (point.x > northEastPoint.x)
northEastPoint.x = point.x;
if(point.y > northEastPoint.y)
northEastPoint.y = point.y;
if (point.x < southWestPoint.x)
southWestPoint.x = point.x;
if (point.y < southWestPoint.y)
southWestPoint.y = point.y;
}pointArr[idx] = point;
}
// create the polyline based on the array of points.
self.routeLine = [MKPolyline polylineWithPoints:pointArr count:pointStrings.count];_routeRect = MKMapRectMake(southWestPoint.x, southWestPoint.y, northEastPoint.x - southWestPoint.x, northEastPoint.y - southWestPoint.y);
// clear the memory allocated earlier for the points
free(pointArr);}
Now that you’ve got your route’s polyline created, you need to let the map view display it by adding it as an overlay:
[self.mapView addOverlay:self.routeLine];
Adding the overlay alone will not render anything on the map. Your MKMapViewDelegate implementation must return an overlay for this route you’ve just added. Luckily, the new iOS 4 MapKit framework provides a default implementation of an MKOverlayView that is capable of rendering a line on a map. This class is called MKPolylineView, and this is how you might use it:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id
)overlay
{
MKOverlayView* overlayView = nil;if(overlay == self.routeLine)
{
//if we have not yet created an overlay view for this overlay, create it now.
if(nil == self.routeLineView)
{
self.routeLineView = [[[MKPolylineView alloc] initWithPolyline:self.routeLine] autorelease];
self.routeLineView.fillColor = [UIColor redColor];
self.routeLineView.strokeColor = [UIColor redColor];
self.routeLineView.lineWidth = 3;
}overlayView = self.routeLineView;
}
return overlayView;
}
I hope this helps you use the new MapKit classes. Apple really has simplified rendering lines, routes and other shapes on the map, and these new classes and protocols really deprecate my previous articles on the topic, so long as you are targeting iOS 4 and above.
Here a sample project that uses the above code:
os4Maps
July 13th, 2010 at 12:48 am
A working solution can be found here: http://github.com/mobilemelting/nvpolyline
This solution is especially targeted at iPhone OS versions prior to v.4.0
Although it can also be used in v.4.0
July 16th, 2010 at 6:04 am
That’s very cool solutions but in real life app, how would you get the route location points between start and destination?
July 16th, 2010 at 7:50 am
Marcin,
The points I used in this demo were obtained by using the core location manager. You could do something similar and then generate your list of points from the incoming data from the core location manager.
-Craig
July 24th, 2010 at 12:40 am
Hello, this is great example!!
I’ve run build and analyze found leaks…
July 30th, 2010 at 3:10 pm
thank you I was freaking out when i updated my xcode to 4.0