How to Determine If an Annotation Is Inside of Mkpolygonview (Ios)

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.

Showing annotations only inside the overlays

Overall, that looks fine but there is a problem in the filterAnnotations method with how the annotations are being removed from the annotationsToFilter array.

What will happen is that some annotations will be skipped and never go through the check.

For example:

  • Say there are 5 annotations (0=A, 1=B, 2=C, 3=D, 4=E)
  • The for-loop starts at index 0 and "A" meets the condition for removal
  • "A" is removed from the array and now the other annotations are moved down one index so after the removal the array is: (0=B, 1=C, 2=D, 3=E)
  • Now the for-loop goes to the next index which is 1 (so annotation C is checked)
  • So annotation B is skipped and is never checked

One way to fix this is to collect the annotations you want to keep in another array "annotationsToAdd" instead of removing them from the original and pass annotationsToAdd to the addAnnotations method.

Below is the suggested modification. I'd also recommend moving the viewForOverlay calls outside the for-loop since those references won't change during the loop so there's no need to call them repeatedly.

-(void)filterAnnotations:(NSMutableArray *)annotationsToFilter 
{
NSMutableArray *annotationsToAdd = [NSMutableArray array];

//Get a reference to the overlay views OUTSIDE the for-loop since
//they will remain constant so there's no need to keep calling
//viewForOverlay repeatedly...
MKPolygonView *polygonView = (MKPolygonView *)[mapView viewForOverlay:polygonOverlay];

MKCircleView *circleView = (MKCircleView *)[mapView viewForOverlay:circleOverlay];

for (int i=0; i < [annotationsToFilter count]; i++)
{
//get a handy reference to the annotation at the current index...
id currentAnnotation = [annotationsToFilter objectAtIndex:i];

CLLocationCoordinate2D mapCoordinate = [currentAnnotation coordinate];

MKMapPoint mapPoint = MKMapPointForCoordinate(mapCoordinate);

CGPoint polygonViewPoint = [polygonView pointForMapPoint:mapPoint];
CGPoint circleViewPoint = [circleView pointForMapPoint:mapPoint];

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

BOOL mapCoordinateIsInCircle = CGPathContainsPoint(circleView.path, NULL, circleViewPoint, NO);

if ( !mapCoordinateIsInPolygon && !mapCoordinateIsInCircle )
//Note the reversed if-condition because now
//we are finding annotations we want to KEEP
{
[annotationsToAdd addObject:currentAnnotation];
}
}

[mapView addAnnotations:annotationsToAdd];
}

Also, I noticed that in the showKmlData method you are using the variable mapview but in filterAnnotations it's mapView (uppercase V). Hopefully, the compiler will give you a warning about that.


Additional Info:

Based on your comments and the viewForOverlay code you added to your question...

First, the compiler warning class 'MKPolygonView' does not implement the 'MKOverlay' protocol that you are getting implies that the variables polygonOverlay and circleOverlay are declared as MKPolygonView and MKCircleView instead of MKPolygon and MKCircle.

Second, the code in the viewForOverlay delegate method is wrong. It tries to create both a circle and polygon view for whatever overlay is passed in and then checks what kind of class the overlay is. It also seems to be saving a reference to the overlay view but the rest of the code assumes that we are keeping a reference to the overlay (the MKOverlay object--not the MKOverlayView).

Try the following changes...

//polygonOverlay and circleOverlay should be declared as MKOverlay objects...
@property (nonatomic, retain) MKPolygon *polygonOverlay;
@property (nonatomic, retain) MKCircle *circleOverlay;

//save a reference to them when you call addOverlay...
self.polygonOverlay = [MKPolygon polygonWithCoordinates:polyCoords count:coordsCount];
[mapView addOverlay:polygonOverlay];
self.circleOverlay = [MKCircle circleWithCenterCoordinate:cirleCenter radius:circleRadius];
[mapView addOverlay:circleOverlay];

//the viewForOverlay delegate method...
-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id)overlay
{
if ([overlay isKindOfClass:[MKCircle class]])
{
MKCircleView* circleView = [[[MKCircleView alloc] initWithOverlay:overlay] autorelease];
circleView.fillColor = [UIColor blueColor];
circleView.strokeColor = [UIColor blueColor];
circleView.lineWidth = 5.0;
circleView.alpha = 0.20;
return circleView;
}
else
if ([overlay isKindOfClass:[MKPolygon class]])
{
MKPolygonView *polygonView = [[[MKPolygonView alloc] initWithOverlay:overlay] autorelease];
polygonView.fillColor = [UIColor blueColor];
polygonView.strokeColor = [UIColor blueColor];
polygonView.lineWidth = 5.0;
polygonView.alpha = 0.20;
return polygonView;
}

return nil;
}

You also mention in your edit that "I see the overlays, circles and polygons". That sounds like you are creating more than one circle and/or polygon overlay. In that case, having just one polygonOverlay and circleOverlay variable won't work.

If you really do have multiple overlays of each type, then don't store references to the overlays. Instead, in the filterAnnotations method, for each annotation, loop through the mapView.overlays array and execute the viewForOverlay and point-in-polygon test in the nested loop.

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.

Check if a point on map lies within an overlay

Well, I figured it out finally. The code above should work as is, except that there is a missing break statement in the loop. The code as is returns only the last checked point of the poly. Inserting a test to mapCoordinateIsInPolygon and then a break statement in the inner loop leaves the loop as soon as the first test is positive, thus giving the correct result. ;-)

How to determine if an GMSMarker is inside of GMSPolygon (iOS Google Map SDK)

I got the answer.

There is an inline function defined in GMSGeometryUtils.h file in Google SDK:

//Returns whether |point|,CLLocationCoordinate2D lies inside of path, GMSPath
BOOL GMSGeometryContainsLocation(CLLocationCoordinate2D point, GMSPath *path,
BOOL geodesic);

It contains a function called GMSGeometryContainsLocation, which determines whether location point lies inside of polygon path.

if (GMSGeometryContainsLocation(locationPoint, polygonPath, YES)) {
NSLog(@"locationPoint is in polygonPath.");
} else {
NSLog(@"locationPoint is NOT in polygonPath.");
}

Source: GMSGeometryContainsLocation

How to retrieve Map Annotation coordinates?

The simplest way is to store a reference to the annotation when you add it.

For example, you could declare a retain property called pinToRemember of type myAnnotation and after adding the annotation, do this to save it for later:

[mapview addAnnotation:pinLoc];
self.pinToRemember = pinLoc;

Then in findPin:, you would do:

if (pinToRemember == nil)
return;

region.center = pinToRemember.coordinate;



Alternatively, you could search through the map view's annotations array property and look for the annotation you are interested in by looking for its title or some other custom property. This could be done by using a simple for-loop.



You say you have the "way to tell if the pin is within a polygon" working but this answer may also be useful:

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



Related Topics



Leave a reply



Submit