Detecting a point in a MKPolygon broke with iOS7 (CGPathContainsPoint)
Asked Answered
C

4

12

In a SO question I asked earlier this year, I got this chunk of code:

MKPolygonView *polygonView = (MKPolygonView *)[self.mapView viewForOverlay:polygon];
MKMapPoint mapPoint = MKMapPointForCoordinate(tapCoord);
CGPoint polygonViewPoint = [polygonView pointForMapPoint:mapPoint];

if (CGPathContainsPoint(polygonView.path, NULL, polygonViewPoint, FALSE)) {
    // do stuff
}

This works great up until iOS7. It now always returns false and will not detect a point with the path.

I'm trying to find any documentation stating that the method change, but can't find any.

Any ideas why it broke? Or a new solution?

Cultivated answered 25/9, 2013 at 20:48 Comment(0)
R
22

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.

Ritchey answered 26/9, 2013 at 3:16 Comment(8)
Just a note: it looks like if the MKPolygon is not visible, the polygon.path property will return NULL in iOS 6. This solution works like a charm, thank you very much Ms Anna!Cultivated
this worked great. one note: the line "CGPoint mapPointAsCGP = CGPointMake(mapPoint.x, mapPoint.y);" is critical; my code previously had done this instead "CGPoint polygonViewPoint = [overlayView pointForMapPoint:mapPoint];" which is also broken in iOS7 it seems.Eldenelder
@sethpollack: The mapPointAsCGP thing is only to avoid a possible compiler warning that you are passing a MKMapPoint where a CGPoint is expected (it does not change the values passed -- it just puts them inside a CGPoint struct). The reason pointForMapPoint would not work here is that the path is built using MKMapPoint units so the point we're testing must also be in MKMapPoint units (not actual screen CGPoint units).Ritchey
@Anna - I'm trying to test existing map annotations to see if they are inside of the polygon. Any tips on converting the annotation to CGPoint for this evaluation? Previous reference uses the [polygon pointForMapPoint:point]. thanks in advance!Laurin
@CocoaEv: As mentioned in the code example: //mapPoint above is the MKMapPoint of the coordinate we are testing. So you take the annotation's coordinate and convert it to an MKMapPoint using the MKMapPointForCoordinate function. All the "CG" stuff is only because we are using Core Graphics functions (not actual CGPoints).Ritchey
@Anna- Thank you. wearing dunce cap here. How do I cast MKMapPoint to CGPoint to make CGPathContainsPoint test happy - compiler complains?Laurin
@CocoaEv: That's what the code example is avoiding by then creating mapPointAsCGP. If still an issue, please post a question with the code you're trying.Ritchey
@Anna - ty,ty,ty. Got it! re-read your solution. MKMapPoint annoPoint = MKMapPointForCoordinate(anOrder.coordinate); CGPoint mapPointAsCGP = CGPointMake(annoPoin.x, annoPoint.y);Laurin
D
2

The issue with your code is that the method

- (MKOverlayView *)viewForOverlay:(id < MKOverlay >)overlay

has been deprecated in iOS 7 (See the doc), you should use this instead:

- (MKOverlayRenderer *)rendererForOverlay:(id < MKOverlay >)overlay

So, in order to make your code work properly in iOS 7 you have to replace:

MKPolygonView *polygonView = (MKPolygonView *)[self.mapView viewForOverlay:polygon];

with

MKPolygonRenderer *polygonView = (MKPolygonRenderer *)[self.mapView rendererForOverlay:polygon];

The problem with this is that - rendererForOverlay: is new in iOS 7, so this change make that your application do not run in previous versions. You can implement two version of the method and make call one or the other depending on the iOS version.

I haven't profile the performance of this in comparison with the @Anna solution

Define answered 5/12, 2013 at 16:1 Comment(2)
The issue is that the path property of MKPolygonView/MKPolygonRenderer returns null in iOS 7 (even with rendererForOverlay). In your experience, does path have a value in the MKPolygonRenderer after calling rendererForOverlay?Ritchey
yes, in my case using rendererForOverlay path return a non-null value, unless the polygon sent as parameter hasn't been added to the mapView beforeDefine
W
2

I was having the same issue. Solved it by calling invalidatePath on the MKPolygonRenderer before access the Path.

Watt answered 29/12, 2013 at 17:52 Comment(0)
H
1

It's working straight on 7.1 but not on 7.0.

jdehlin response fixed the problem for me with 7.0, ie. calling [view invalidatePath] before each call to

CGPathContainsPoint(polygonView.path, NULL, polygonViewPoint, FALSE)
Hardiman answered 7/5, 2014 at 14:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.