I have the swift code below that draws a polygon and drops an annotation on MKMapView. I am trying to figure out how i could identify if the annotation\'s coordinate is with
- (BOOL)isPoint:(MKMapPoint)point insidePolygon:(MKPolygon *)poly {
MKMapPoint *a = poly.points;
BOOL isInsidePolygon = NO;
double testx = point.x;
double testy = point.y;
NSUInteger i = 0, j = 0, nvert = [poly pointCount];
for (i = 0, j = nvert - 1; i < nvert; j = i++) {
if (((a[i].y >= testy) != (a[j].y >= testy)) &&
(testx <= (a[j].x - a[i].x) * (testy - a[i].y) / (a[j].y - a[i].y) + a[i].x)) {
isInsidePolygon = !isInsidePolygon;
}
}
return isInsidePolygon;
}
Because the points x y are in Mercator projection coordinates, this makes sense and you don't need to convert to spherical coordinates for the calculation.
Why this is a correct calculation here.
EDIT: updated so that the calculation also considers a point on the line as inside the poly.
You can create a polygon renderer object and check if it's path contains the point based on the location in the map:
func checkIf(_ location: CLLocationCoordinate2D, areInside polygon: MKPolygon) -> Bool {
let polygonRenderer = MKPolygonRenderer(polygon: polygon)
let mapPoint = MKMapPointForCoordinate(location)
let polygonPoint = polygonRenderer.point(for: mapPoint)
return polygonRenderer.path.contains(polygonPoint)
}
Method to be attached to a gesture recognizer:
@objc
func mapTapped(_ tap: UILongPressGestureRecognizer) {
if tap.state == .recognized {
let touchPoint = tap.location(in: mapView)
let coord = mapView.convert(touchPoint, toCoordinateFrom: mapView)
for overlay: MKOverlay in mapView.overlays {
if let polygon = overlay as? MKPolygon {
let renderer = MKPolygonRenderer(polygon: polygon)
let mapPoint = MKMapPoint(coord)
let rendererPoint = renderer.point(for: mapPoint)
if renderer.path.contains(rendererPoint) {
// here comes your code
}
}
}
}
}
I created a Swift version of the answer by @StefanS and made it easier to read by porting the code from this answer
func isPoint(point: MKMapPoint, insidePolygon poly: MKPolygon) -> Bool {
let polygonVerticies = poly.points()
var isInsidePolygon = false
for i in 0..<poly.pointCount {
let vertex = polygonVerticies[i]
let nextVertex = polygonVerticies[(i + 1) % poly.pointCount]
// The vertices of the edge we are checking.
let xp0 = vertex.x
let yp0 = vertex.y
let xp1 = nextVertex.x
let yp1 = nextVertex.y
if ((yp0 <= point.y) && (yp1 > point.y) || (yp1 <= point.y) && (yp0 > point.y))
{
// If so, get the point where it crosses that line. This is a simple solution
// to a linear equation. Note that we can't get a division by zero here -
// if yp1 == yp0 then the above if be false.
let cross = (xp1 - xp0) * (point.y - yp0) / (yp1 - yp0) + xp0
// Finally check if it crosses to the left of our test point. You could equally
// do right and it should give the same result.
if cross < point.x {
isInsidePolygon = !isInsidePolygon
}
}
}
return isInsidePolygon
}
How about using MkPolygon.intersect()? This requires converting the point to a tiny MKMapRect, but it is simpler and may get some acceleration from the underlying API:
func pointIsInside(point: MKMapPoint, polygon: MKPolygon) -> Bool {
let mapRect = MKMapRectMake(point.x, point.y, 0.0001, 0.0001)
return polygon.intersects(mapRect)
}