Detecting a Point in a Mkpolygon Broke with iOS7 (Cgpathcontainspoint)

Detecting a point in a MKPolygon broke with iOS7 (CGPathContainsPoint)

For some reason (possibly a bug), the path property returns NULL in the current release of iOS 7.

A workaround is to construct your own CGPathRef from the points of the polygon.

With this method, you don't need a reference to the MKPolygonView or the MKPolygonRenderer.

For example:

CGMutablePathRef mpr = CGPathCreateMutable();

MKMapPoint *polygonPoints = myPolygon.points;
//myPolygon is the MKPolygon

for (int p=0; p < myPolygon.pointCount; p++)
{
MKMapPoint mp = polygonPoints[p];
if (p == 0)
CGPathMoveToPoint(mpr, NULL, mp.x, mp.y);
else
CGPathAddLineToPoint(mpr, NULL, mp.x, mp.y);
}

CGPoint mapPointAsCGP = CGPointMake(mapPoint.x, mapPoint.y);
//mapPoint above is the MKMapPoint of the coordinate we are testing.
//Putting it in a CGPoint because that's what CGPathContainsPoint wants.

BOOL pointIsInPolygon = CGPathContainsPoint(mpr, NULL, mapPointAsCGP, FALSE);

CGPathRelease(mpr);

This should work on iOS 6 as well.

However, you may want to do this manual construction only if the overlay view's path property returns NULL.

detect if a point is inside a MKPolygon overlay

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

Detecting touches on MKOverlay in iOS7 (MKOverlayRenderer)

I've done it.

Thanks to incanus and Anna!

Basically I add a TapGestureRecognizer to the MapView, convert the point tapped to map coordinates, go through my overlays and check with CGPathContainsPoint.

Adding TapGestureRecognizer. I did that trick of adding a second double tap gesture, so that the single tap gesture isn't fired when doing a double tap to zoom on map. If anyone knows a better way, I'm glad to hear!

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleMapTap:)];
tap.cancelsTouchesInView = NO;
tap.numberOfTapsRequired = 1;

UITapGestureRecognizer *tap2 = [[UITapGestureRecognizer alloc] init];
tap2.cancelsTouchesInView = NO;
tap2.numberOfTapsRequired = 2;

[self.mapView addGestureRecognizer:tap2];
[self.mapView addGestureRecognizer:tap];
[tap requireGestureRecognizerToFail:tap2]; // Ignore single tap if the user actually double taps

Then, on the tap handler:

-(void)handleMapTap:(UIGestureRecognizer*)tap{
CGPoint tapPoint = [tap locationInView:self.mapView];

CLLocationCoordinate2D tapCoord = [self.mapView convertPoint:tapPoint toCoordinateFromView:self.mapView];
MKMapPoint mapPoint = MKMapPointForCoordinate(tapCoord);
CGPoint mapPointAsCGP = CGPointMake(mapPoint.x, mapPoint.y);

for (id overlay in self.mapView.overlays) {
if([overlay isKindOfClass:[MKPolygon class]]){
MKPolygon *polygon = (MKPolygon*) overlay;

CGMutablePathRef mpr = CGPathCreateMutable();

MKMapPoint *polygonPoints = polygon.points;

for (int p=0; p < polygon.pointCount; p++){
MKMapPoint mp = polygonPoints[p];
if (p == 0)
CGPathMoveToPoint(mpr, NULL, mp.x, mp.y);
else
CGPathAddLineToPoint(mpr, NULL, mp.x, mp.y);
}

if(CGPathContainsPoint(mpr , NULL, mapPointAsCGP, FALSE)){
// ... found it!
}

CGPathRelease(mpr);
}
}
}

I could ask for the MKPolygonRenderer which already has the "path" property and use it, but for some reason it is always nil. I did read someone saying that I could call invalidatePath on the renderer and it does fill the path property but it just seems wrong as the point is never found inside any of the polygons. That is why I rebuild the path from the points. This way I don't even need the renderer and just make use of the MKPolygon object.

How to determine if an annotation is inside of MKPolygonView (iOS)

The following converts the coordinate to a CGPoint in the polygon view and uses CGPathContainsPoint to test if that point is in the path (which may be non-rectangular):

CLLocationCoordinate2D mapCoordinate = ...; //user location or annot coord

MKMapPoint mapPoint = MKMapPointForCoordinate(mapCoordinate);

MKPolygonView *polygonView =
(MKPolygonView *)[mapView viewForOverlay:polygonOverlay];

CGPoint polygonViewPoint = [polygonView pointForMapPoint:mapPoint];

BOOL mapCoordinateIsInPolygon =
CGPathContainsPoint(polygonView.path, NULL, polygonViewPoint, NO);

This should work with any overlay view that is a subclass of MKOverlayPathView. You can actually replace MKPolygonView with MKOverlayPathView in the example.

Detect if a point is inside of MKCircle in iPhone sdk ios 7

This apporach should work too, using MKCircleRenderer :

    MKCircleRenderer *circleRenderer = (MKCircleRenderer *)[mapview rendererForOverlay:circleOverlay];
[circleRenderer invalidatePath];

MKMapPoint mapPoint = MKMapPointForCoordinate(tapPoint);
CGPoint circlePoint = [circleRenderer pointForMapPoint:mapPoint];
BOOL mapCoordinateIsInCircle = CGPathContainsPoint(circleRenderer.path, NULL, circlePoint, NO);

if ( mapCoordinateIsInCircle )

{
//do something
}

How to do an action if location hits custom area

First, the compiler error "incompatible type MKMapPoint" on this line:

MKMapPoint *mapPoint = MKMapPointForCoordinate(...);

is because MKMapPointForCoordinate returns a plain MKMapPoint struct but you've declared mapPoint as a pointer to an MKMapPoint struct so it's "incompatible".

Don't declare mapPoint as a pointer (remove the asterisk). So change that line to:

MKMapPoint mapPoint = MKMapPointForCoordinate(...);




Next, your other question was "How does the CGPathContainsPoint code check multiple polygons?"

One way is to store all the CGMutablePathRef paths for all the polygons in a C array just like you're storing the individual polygon coordinates in a C array.

Then, you can loop through the paths array and call CGPathContainsPoint with each one.


Following is an example to test multiple polygon overlays that have been added to the map. This example assumes you've already added MKPolygon overlays to the map using addOverlay or addOverlays.

Wherever you want to test whether some coordinate is in any of the polygons added to the map, put this:

CLLocationCoordinate2D coordinateToTest = 
CLLocationCoordinate2DMake(52.366243, 4.891097);

MKMapPoint mapPointToTest = MKMapPointForCoordinate(coordinateToTest);

//Loop through MKPolygon overlays...
for (id overlay in myMapView.overlays)
{
if ([overlay isKindOfClass:[MKPolygon class]])
{
MKPolygon *polygon = (MKPolygon *)overlay;


CGMutablePathRef mpr = CGPathCreateMutable();

MKMapPoint *polygonPoints = polygon.points;

for (int p=0; p < polygon.pointCount; p++)
{
MKMapPoint mp = polygonPoints[p];
if (p == 0)
CGPathMoveToPoint(mpr, NULL, mp.x, mp.y);
else
CGPathAddLineToPoint(mpr, NULL, mp.x, mp.y);
}

CGPoint mapPointAsCGP = CGPointMake(mapPointToTest.x, mapPointToTest.y);

BOOL pointIsInPolygon = CGPathContainsPoint(mpr, NULL, mapPointAsCGP, FALSE);
NSLog(@"Coordinate %f,%f is in polygon %@: %@",
coordinateToTest.latitude, coordinateToTest.longitude,
polygon.title,
(pointIsInPolygon ? @"Yes" : @"No"));

CGPathRelease(mpr);
}
}


Related Topics



Leave a reply



Submit