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
Outlets Cannot Be Connected to Repeating Content Ios
Cordova App Hanging During Startup on iOS 10
Iphone Get a List of All Ssids Without Private Library
Sorting Array Alphabetically With Number
How to Correctly Linearize Depth in Opengl Es in Ios
Make Uinavigationbar Transparent
How to Wrap Text in a Uitableviewcell Without a Custom Cell
Changing Tint/Background Color of Uitabbar
React Native: How to Select the Next Textinput After Pressing the "Next" Keyboard Button
Nsurlsession With Nsblockoperation and Queues
How to Pass Data Using Notificationcenter in Swift 3.0 and Nsnotificationcenter in Swift 2.0
Iphone Smooth Sketch Drawing Algorithm
iOS 7 Tableview Like in Settings App on iPad