MKPolygon - How to determine if a CLLocationCoordinate2D is in a CLLocationCoordinate2D polygon?

前端 未结 5 831
春和景丽
春和景丽 2021-01-15 06:06

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

相关标签:
5条回答
  • 2021-01-15 06:32
    - (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.

    0 讨论(0)
  • 2021-01-15 06:38

    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)
    }
    
    0 讨论(0)
  • 2021-01-15 06:40

    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
                    }
                }
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-15 06:44

    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
        }
    
    0 讨论(0)
  • 2021-01-15 06:53

    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)
    }
    
    0 讨论(0)
提交回复
热议问题