4

I'm looking for some guidance in testing if a MKPolygon intersects an MKCircle. Currently I'm using:

if ([circle intersectsMapRect:[poly boundingMapRect]]) {
                    //they do intersect
   }

I'm finding this returns inaccurate results simply b/c it draws a rectangle around my circle, thus giving me intersections that shouldn't otherwise be.

Searching the topic has lead me to Chad Saxon's polygon-polygon intersection project. This could be useful if I could somehow convert my MKCircle to a multi-sided polygon - which could be possible but ultimately I believe this is the round-about way to solve this.

I'm ultimately wondering if there is a simple solution I've overlooked before delving into porting my own custom geometry-ray-testing-algorithm implementation.

capikaw
  • 12,232
  • 2
  • 43
  • 46

2 Answers2

6

A couple of thoughts:

  1. If you use that polygon intersection project, be aware that it has a few leaks in it. I issued a pull request that fixes a few of them (and a few other random observations). I would be wary of adopting any of that view controller code (as it has other issues), too, but the category seems ok if you're ok with the various limitations it entails (notably, the clockwise limitation, which is not really an issue if you're only determining if they intersected).

  2. Rather than converting the circle to a series of polygons and then using that polygon intersection class, I might consider an alternative approach leveraging that you can detect the intersection with a circle by leveraging the fact that you can look at the distance between the relevant points in the polygon and the radius of a circle. It seems that there are three aspects of the problem:

    • If the distance between any of the polygon's vertices and the center of the circle is less than the radius of the circle, then the polygon and circle intersect.

      vertex inside circle

    • Does the polygon encompass the circle (this is that special case where the distance from all of the sides of the polygon would be greater than the circle's radius, but the circle and polygon still obviously intersect). This is easily achieved by checking to see if the CGPath of the polygon's view encompasses the center of the circle using CGPathContainsPoint.

      enter image description here

    • The only complicated part is to check to see if any side of the polygon intersects the circle, namely that the minimum distance between the sides of the polygon and the center of the circle less than the radius of the circle;

      side inside circle

    To calculate the distance of each side from the center of the circle, I might therefore iterate through each side of the polygon and for those sides facing the circle's center (i.e. for which the circle's center is perpendicular to the segment, meaning that an imaginary line perpendicular to the polygon's side that goes through the center of the circle actually crosses the line segment), you could:

    • Calculate the constants a, b, and c for this side of the polygon for the equation ax + by + c = 0 for a line segment going between vertices of the polygon (x1, y1) and (x2, y2):

    • a = (y1 – y2)

    • b = (x2 – x1)

    • c = (x1y2 – x2y1)

    • Calculate the distance from a point to a line, using (x0, y0) as the center of the circle:

      abs(ax0+by0+c)/sqrt(a^2+b^2)

    • If that distance is less than the radius of the circle, then you know that the polygon intersects the circle.

I put a sample project employing this technique on github.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • thats what he has.. the polygon is no rectangle. – Daij-Djan Sep 07 '13 at 07:54
  • 1
    @Daij-Djan My use of rectangles was just to illustrate the three scenarios of the algorithm, but nothing in this answer is contingent upon the polygon being a rectangle. – Rob Sep 07 '13 at 12:35
  • Hi Rob - what an excellent answer. Thank you. I had since figured out the the distance to polygon-vertice test. I loop each point of the polygon and then basically check it's distance to the center of the circle: if it's less than the radius, break and return yes. I'm guessing calculating less "point to SIDE"'s vs many "point to POINT"'s is simply faster? I'll test the performance of both this week and post the results here. – capikaw Sep 07 '13 at 14:12
  • 1
    @capikaw No, I'm not doing the center-to-polygon-side logic for performance reasons. I do it because of my third scenario, where they intersect even though none of the corners of the polygon are inside the circle. Maybe that's a scenario you're not worried about, in which case it's much simpler. But I think a precise "polygon and circle intersect" logic probably requires it. – Rob Sep 07 '13 at 15:37
  • Of course, that makes complete sense now - thanks again @Rob! – capikaw Sep 08 '13 at 13:39
1

Just for those to get a bit of substance on the solution, here is a useful MKCircle extension I wrote that checks if a point (in this case a polygon point) is inside the circle or not. Enjoy!

//MKCircle+PointInCircle.h

#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>

@interface MKCircle (PointInCircle)

-(BOOL)coordInCircle:(CLLocationCoordinate2D)coord;

@end

//MKCircle+PointInCircle.m

#import "MKCircle+PointInCircle.h"

@implementation MKCircle (PointInCircle)

-(BOOL)coordInCircle:(CLLocationCoordinate2D)coord {

    CLLocation *locFrom = [[CLLocation alloc] initWithLatitude:self.coordinate.latitude longitude:self.coordinate.longitude];
    CLLocation *locTo = [[CLLocation alloc] initWithLatitude:coord.latitude longitude:coord.longitude];

    double distance = [locFrom distanceFromLocation:locTo];
    BOOL isInside = (distance <= self.radius);

    return isInside;
}

@end
capikaw
  • 12,232
  • 2
  • 43
  • 46