Episode #67

Map Overlays

14 minutes
Published on May 16, 2013

This video is only available to subscribers. Get access to this video and 586 others.

In this episode we take the shape file data we parsed in episode 66 and use it to draw outlines around US States. We do this using the MKPolygon overlay in combination with MKPolygonView, which allows us to fill & stroke the provided vertices.

Episode Links

Parse the points into a list of states

We first want to keep an array to hold the states we've parsed:

@property (nonatomic, strong) NSMutableArray *states;

We initialize this right before processing the shapes:

self.states = [NSMutableArray arrayWithCapacity:numEntities];

We can pass each shape off to our WARState class, which will just be a container for a collection of polygons. It's easy to think that each state is 1 connected polygon, but a few states have islands that are not connected.

for (int i=0; i<numEntities; i++) {
    SHPObject *shpObject = SHPReadObject(shp, i);
    WARState *state = [[WARState alloc] initWithShapeObject:shpObject];
    [self.states addObject:state];
}

Converting the shapes into map overlays

Next, we need to loop over each states polygons, creating a CLLocationCoordinate2D array of points. Since we captured ours in an NSArray, they are all boxed as NSValue objects. We'll need to unbox them here.

  for (WARState *state in self.states) {
        for (WARPolygon *polygon in state.polygons) {
            int count = [polygon.coordinates count];
            CLLocationCoordinate2D coords[count];

            // convert each boxed value to a value on the stack, and add it to our array 
            for (int c=0; c<count; c++) {
                NSValue *coordValue = polygon.coordinates[c];
                CLLocationCoordinate2D coord = [coordValue MKCoordinateValue];
                coords[c] = coord;
            }

            // create the overlay            
            MKPolygon *polygon = [MKPolygon polygonWithCoordinates:coords count:count];
            [self.mapView addOverlay:polygon];
        }
    }

Now our map has overlays, but we haven't done any rendering. Similar to MKAnnotation, MKOverlay only provides the requisite data to describe the overlay. Rendering is handled by a companion view. To provide this view, we need to conform to the MKMapViewDelegate protocol and implement the mapView:viewForOverlay: method.

Providing the overlay view

-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay {
    if ([overlay isKindOfClass:[MKPolygon class]]) {
        MKPolygonView *view = [[MKPolygonView alloc] initWithPolygon:overlay];
        view.strokeColor = [UIColor blueColor];
        view.lineWidth = 2;
        view.lineCap = kCGLineCapRound;
        view.fillColor = [[UIColor redColor] colorWithAlphaComponent:0.4];
        return view;
    } else {
        return nil;
    }
}

Now you can build and run the application to see the polygons overlaid on top of the map.