MKPolygon — как определить, находится ли CLLocationCoordinate2D в полигоне CLLocationCoordinate2D?

У меня есть приведенный ниже быстрый код, который рисует многоугольник и добавляет аннотацию в MKMapView. Я пытаюсь понять, как я могу определить, находится ли координата аннотации в пределах многоугольника?

import UIKit
import MapKit

class ViewController: UIViewController {

    @IBOutlet weak var mapView: MKMapView!

    override func viewDidLoad() {
        super.viewDidLoad()
        let initialLocation = CLLocation(latitude: 49.140838, longitude: -123.127886)
        centerMapOnLocation(initialLocation)
        addBoundry()

        var annotation = MKPointAnnotation()
        annotation.coordinate = point1
        annotation.title = "Roatan"
        annotation.subtitle = "Honduras"

        mapView.addAnnotation(annotation)
    }

    var points = [CLLocationCoordinate2DMake(49.142677,  -123.135139),
                  CLLocationCoordinate2DMake(49.142730, -123.125794),
                  CLLocationCoordinate2DMake(49.140874, -123.125805),
                  CLLocationCoordinate2DMake(49.140885, -123.135214)]

    var point1 = CLLocationCoordinate2DMake(49.141821, -123.131577)

    func addBoundry() {
        let polygon = MKPolygon(coordinates: &points, count: points.count)

        mapView.addOverlay(polygon)
    }

    let regionRadius: CLLocationDistance = 1000

    func centerMapOnLocation(location: CLLocation) {
        let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate, regionRadius * 2.0, regionRadius * 2.0)

        mapView.setRegion(coordinateRegion, animated: true)
    }

    func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
        if overlay is MKPolygon {
            let polygonView = MKPolygonRenderer(overlay: overlay)
            polygonView.strokeColor = UIColor.magentaColor()

            return polygonView
        }

        return nil
    }
}

person Sicong Liu    schedule 17.07.2015    source источник


Ответы (5)


Я создал версию ответа @StefanS для Swift и упростил чтение, перенеся код из этого ответа.

    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
    }
person Moshe Gutman    schedule 21.02.2019

- (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;
}

Поскольку точки x y находятся в координатах проекции Меркатора, это имеет смысл, и вам не нужно преобразовывать их в сферические координаты для вычислений.

Почему это правильный расчет здесь.

РЕДАКТИРОВАТЬ: обновлено, чтобы при расчете точка на линии также учитывалась внутри полигона.

person StefanS    schedule 29.03.2016

Как насчет использования MkPolygon.intersect()? Это требует преобразования точки в крошечный MKMapRect, но это проще и может получить некоторое ускорение от базового API:

func pointIsInside(point: MKMapPoint, polygon: MKPolygon) -> Bool {
    let mapRect = MKMapRectMake(point.x, point.y, 0.0001, 0.0001)
    return polygon.intersects(mapRect)
}
person janu    schedule 20.09.2016
comment
Это должно быть перезаписано, потому что в противном случае это будет просто сравниваться с ограничивающим прямоугольником, а не с правильным MKPolygon. - person Sn0wfreeze; 06.12.2016

Вы можете создать объект рендеринга многоугольника и проверить, содержит ли его путь точку на основе местоположения на карте:

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)
}
person Flavio Bittencourt    schedule 29.06.2018

Способ подключения к распознавателю жестов:

@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
                }
            }
        }
    }
}
person Vadim Zhuk    schedule 02.11.2020