Drawing polyines or routes on a MKMapView in iOS4 – Part 3
July 4th, 2010Get 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
