Search code examples
iosobjective-cmkmapviewmapkit

detect if a point is inside a MKPolygon overlay


I want to be able to tell if tap is within a MKPolygon.

I have a MKPolygon:

CLLocationCoordinate2D  points[4];

points[0] = CLLocationCoordinate2DMake(41.000512, -109.050116);
points[1] = CLLocationCoordinate2DMake(41.002371, -102.052066);
points[2] = CLLocationCoordinate2DMake(36.993076, -102.041981);
points[3] = CLLocationCoordinate2DMake(36.99892, -109.045267);

MKPolygon* poly = [MKPolygon polygonWithCoordinates:points count:4];

[self.mapView addOverlay:poly];  

//create UIGestureRecognizer to detect a tap
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(foundTap:)];
tapRecognizer.numberOfTapsRequired = 1;
tapRecognizer.numberOfTouchesRequired = 1;
[self.mapView addGestureRecognizer:tapRecognizer];

its just a basic outline of the state Colorado.

I got the tap to lat/long conversion set up:

-(IBAction)foundTap:(UITapGestureRecognizer *)recognizer
{
    CGPoint point = [recognizer locationInView:self.mapView];

    CLLocationCoordinate2D tapPoint = [self.mapView convertPoint:point toCoordinateFromView:self.view];
}

but i am unsure how to tech if my tap point is within the MKPolygon. there does not seem to be a method to do this check, so i'm guessing i need to convert the MKPolygon to a CGRect and use CGRectContainsPoint.

MKPolygon has a .points property but i can't seem to get them back out.

any suggestions?

EDIT:

Both solutions below work in iOS 6 or lower, but breaks in iOS 7. In iOS 7 the polygon.path property allways returns NULL. Ms Anna was kind enough to provide a solution in another SO question here. It involves creating your own path from the polygon points to pass into CGPathContainsPoint().

image of my polygon:

enter image description here


Solution

  • I created this MKPolygon category in case anyone wants to use it. Seems to work well. You have to account for the interior polygons (i.e. holes in the polygon):

    @interface MKPolygon (PointInPolygon)
      -(BOOL) pointInPolygon:(CLLocationCoordinate2D) point mapView: (MKMapView*) mapView;
    @end
    
    @implementation MKPolygon (PointInPolygon)
    
    -(BOOL) pointInPolygon:(CLLocationCoordinate2D) point mapView: (MKMapView*) mapView {
        MKMapPoint mapPoint = MKMapPointForCoordinate(point);
        MKPolygonView * polygonView = (MKPolygonView*)[mapView viewForOverlay:self];
        CGPoint polygonViewPoint = [polygonView pointForMapPoint:mapPoint];
        return CGPathContainsPoint(polygonView.path, NULL, polygonViewPoint, NO) && 
            ![self pointInInteriorPolygons:point mapView:mapView];
    }
    
    -(BOOL) pointInInteriorPolygons:(CLLocationCoordinate2D) point mapView: (MKMapView*) mapView {
        return [self pointInInteriorPolygonIndex:0 point:point mapView:mapView];
    }
    
    -(BOOL) pointInInteriorPolygonIndex:(int) index point:(CLLocationCoordinate2D) point mapView: (MKMapView*) mapView {
        if(index >= [self.interiorPolygons count])
            return NO;
        return [[self.interiorPolygons objectAtIndex:index] pointInPolygon:point mapView:mapView] || [self pointInInteriorPolygonIndex:(index+1) point:point mapView:mapView];
    }
    
    @end