Adding multiple overlays to MKMapView
ios, mkmapview, mkoverlay, objective-c, xcode
Solution
The `routeLine` and `routeLineView` variables can only point to one overlay and overlay view at a time so you would theoretically need an array of such variables.
However, based on the code shown, you don't need to keep references to these in the first place.
I suggest removing the `routeLine` and `routeLineView` variables.
Then you just create an overlay object locally and in `viewForOverlay` return a view for the requested `overlay` parameter.
In the `loadRoute` method, you can loop through the `routesArray` and for each route, create a local overlay object, and call `addOverlay` on it.
There's also an easier way to construct a map rect that bounds all the routes so you can set the map's region to show them all.
Example:
-(void) loadRoute
{
_routeRect = MKMapRectNull;
for (int raIndex = 0; raIndex < routesArray.count; raIndex++)
{
//So we grab an array that holds all the locations of one route.
NSArray *locations = [[NSArray alloc] initWithArray:
[routesArray objectAtIndex:raIndex]];
//note we are getting object at raIndex (not 0) above
//...no change to existing code here...
//self.routeLine = [MKPolyline polylineWithPoints:pointArr count:[locations count]];
//replace above line with this...
MKPolyline *pl = [MKPolyline polylineWithPoints:pointArr count:[locations count]];
[mapView addOverlay:pl];
//Zoom in to fit route on screen
//_routeRect = MKMapRectMake(southWestPoint.x, southWestPoint.y, northEastPoint.x - southWestPoint.x, northEastPoint.y - southWestPoint.y);
//replace above line with this to create a rect around ALL routes...
if (MKMapRectIsNull(_routeRect))
_routeRect = pl.boundingMapRect;
else
_routeRect = MKMapRectUnion(_routeRect, pl.boundingMapRect);
// clear the memory allocated earlier for the points
free(pointArr);
}
}
In `viewDidLoad`, just call `loadRoute` and don't call `addOverlay`.
The `viewForOverlay` method becomes much simpler:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
MKPolylineView *pv = [[MKPolylineView alloc] initWithPolyline:overlay];
pv.fillColor = [UIColor redColor];
pv.strokeColor = [UIColor redColor];
pv.lineWidth = 3;
return pv;
}
By the way, in `loadRoute`, this line:
MKMapPoint* pointArr = malloc(sizeof(CLLocationCoordinate2D) *[locations count]);
should be:
MKMapPoint* pointArr = malloc(sizeof(MKMapPoint) *[locations count]);
It should use the size of `MKMapPoint` (not `CLLocationCoordinate2D`). They are not the same thing (even though the structs happen to be the same byte size).
Problem
I am having some trouble understanding how to add multiple overlays to MKMapView. I have an array routes tracked. The routes tracked are arrays of CLLocations. So I have an array of arrays. I am able to take one route and draw it on the map view. However, I need to draw ALL the routes onto the map view. So here is how I go about creating the MKPolyline for 1 route: ``` -(void) loadRoute { //So we grab an array that holds all the locations of one route. NSArray *locations = [[NSArray alloc] initWithArray:[routesArray objectAtIndex:0]]; // 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) *[locations count]); for(int idx = 0; idx < [locations count]; idx++) { CLLocation *tempLoc = (CLLocation*)[locations objectAtIndex:idx]; CLLocationDegrees latitude = tempLoc.coordinate.latitude; CLLocationDegrees longitude = tempLoc.coordinate.longitude; // create our coordinate and add it to the correct spot in the array CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(latitude, longitude); MKMapPoint point = MKMapPointForCoordinate(coordinate); // 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; } self.routeLine = [MKPolyline polylineWithPoints:pointArr count:[locations count]]; //Zoom in to fit route on screen _routeRect = MKMapRectMake(southWestPoint.x, southWestPoint.y, northEastPoint.x - southWestPoint.x, northEastPoint.y - southWestPoint.y); // clear the memory allocated earlier for the points free(pointArr); } ``` Here is the MapKit delegate call that returns the overlay: ``` #pragma mark MKMapViewDelegate - (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)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]; self.routeLineView.fillColor = [UIColor redColor]; self.routeLineView.strokeColor = [UIColor redColor]; self.routeLineView.lineWidth = 3; } overlayView = self.routeLineView; } return overlayView; } ``` So all the code given above works fine. I can't figure out how to load more overlays to show the other routes. I thought that maybe in loadRoute method, running through all the routes and creating a MKOverlayView for each one. Storing all those MKOverlayView in an array then doing: [self.mapView addOverlays:array]; But it doesn't work. The problem is that in the delegate method I somehow need to know which overlay to return. So if I could do something along the lines of: ``` - (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay { int tag = overlay.tag; return (MKOverlayView*)[arrayOfViews objectAtIndex:tag]; } ``` it would work fine. But unfortunately it doesn't work like that :( Any help would be very much appreciated! EDIT: I use this in ViewDidLoad to add the overlay: ``` [self loadRoute]; // add the overlay to the map if (nil != self.routeLine) { [self.mapView addOverlay:self.routeLine]; } // zoom in on the route. [self zoomInOnRoute]; ``` This is in the header: ``` // the data representing the route points. MKPolyline* _routeLine; // the view we create for the line on the map MKPolylineView* _routeLineView; // the rect that bounds the loaded points MKMapRect _routeRect; ```