iOS

If you have made it here but haven’t read the MapKit Showcase entry then please do so, since this entry builds off of that post. Thanks!

One of the things that we wanted to add, that the customer didn’t specifically request, would be to hide/show any annotations that were specific to a polygon of interest, depending on how far the user was zoomed in or out. Maybe even the other way around  – always show the annotation but only show the polygon associated with that annotation if you were zoomed in far enough. The idea here, is that, if you zoom out far enough, at some point, you aren’t going to be able to see the polygons anymore. When this happens, it would be useful to show the annotation (created when adding a polygon to the map but by default, hidden). Then you could tap on the annotation pin to get any info that you might need or perhaps add an accessory button to find out more information.

I  implemented both ways, but will show you here the first approach of hiding the annotation pin. I accomplished this by calculating the area that each polygon covered on the map at creation time and whenever the area of the map view rectangle changed (using the delegate methods regionDidChangeAnimated, regionWillChangeAnimated), I would loop through all the polygons and check to see if the ratio of their areas divided by the current map area was less than a certain threshold. If that was true, then I would show that polygons annotation pin.

How do I know which annotation to show? After all, there are multiple polygons on the map. Well, when creating the polygon, I set its title to whatever was typed into the text field (see the screenshot in the original entry). I also add an annotation to the map right after I add the overlay of the polygon to the map.

[map addOverlay:myPolygon];
[map addAnnotation:myPolygon];

Pretty easy, actually. It just so happens that MKPolygon is actually an annotation object (MKPolygon conforms to MKAnnotation and MKOverlay), which means the annotation that I add to the map also receives all of the polygon properties as well (title, coordinate). In addition, if you look deeper you will see that MKPolygonView, which provides the visual representation of this polygon, inherits from the generic MKOverlayPathView generic class, which gives you a CGPath property – which will be VERY useful (more on that in a minute).

Here are different screenshots of me manually zooming out from the three polygons that I drew earlier (again see the original blog entry for screenshots). You will notice that the smaller polygons annotations are shown first, because they cover less area on the map than the biggest polygon(Polygon 1). After the annotation is in view you can click on the annotation pin to see the title and the accessory button that currently is just a placeholder.

Screenshot 2013.05.19 00.26.18Screenshot 2013.05.19 00.26.42Screenshot 2013.05.19 00.28.19Now what if you were to zoom in far enough that the annotation is hidden but you still want to be able to find out information about the polygon. Well, it makes sense that you might want to select any of the polygons on the map and have something like a custom view appear, that would give you that information. To do this you will have to figure out which polygon the user has selected on the map, if at all.

How are we going to do this? Well, MKPolygon really can’t help you here. As mentioned earlier, however, the MKPolygonView is the one that provides you with the visual representation of the polygon object – i.e. it has a property of type MKPolygon. If you look a bit deeper you will see that MKPolygonView inherits from the MKOverlayPathView, which draws its contents using the CGPathRef data type. If you scroll down just a bit you will notice that there is this CGPathRef property available to you called path, which is a pointer to a CGPath class (YES, I went down quite a few rabbit holes to get to this point).

Let’s look at the methods available to us from the CGPath class. A lot of the methods available look very promising and useful but there is one that catches my eye.

CGPathContainsPoint – Checks whether a point is contained in a graphics path.

This is what we need! It requires that you send it a CGPathRef and a CGPoint so that it can do its magic. We know that we can get a CGPathRef from our MKPolygonView. All we need now is the CGPoint. No problem!

When the user touches anywhere in the map view you can easily get the CLCoordinate2D information from that. Then use the method below:

MKMapPoint MKMapPointForCoordinate(CLLocationCoordinate2D coordinate);

The you loop through all of your overlays and use introspection to see which ones are  MKPolygons. If it is, then you can get the MKPolygonView from that.

then you can do this below:

CGPoint polygonPoint = [MKPolygonView pointForMapPoint:MKMapPoint];

Now you have your CGPoint!. Now lets use the CGPath class method that we found earlier. this would be somewhere in your loop:

if(CGPathContainsPoint(currentPolygonView.path, NULL, polygonPoint, NO)){

//do whatever it is you want to do if this is true. – i.e. draw your custom view that will describe stuff about the polygon that you just selected.

}

I have been adding some additional features, after integrating this code into the project that I don’t have time to talk about now, but I hope that this was useful and informative!

Leave a comment