xyk blog

最近は iOS 開発の記事が多めです。

マップ上に追加した MKCircle やMKPolygon などの領域内にある座標かどうかを判定する方法

検証環境:
Xcode 12
Swift 5.3

マップ(MKMapView)上にオーバーレイしたMKCircleMKPolygonなどの領域内にある座標かどうかを判定する方法。
座標の緯度経度はCLLocationCoordinate2Dで扱っているとする。

マップ上に MKCircle や MKPolygon の図形をオーバーレイする方法は以下のPostで書いた。

xyk.hatenablog.com

xyk.hatenablog.com

まずMapKitをインポートしておく。

import MapKit

MKCircle 領域内かを判定する

やり方はMKCircleからMKCircleRendererを作成して、renderer.path.containsメソッドで判定できる。

// 対象の座標
let location = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)

// 半径100mの円領域
let center = CLLocationCoordinate2D(latitude: 35.6809591, longitude: 139.7673068)
let circle = MKCircle(center: center, radius: CLLocationDistance(100))
let renderer = MKCircleRenderer(circle: circle)

// 緯度経度(CLLocationCoordinate2D)をマップ上のポイント(MKMapPoint)に変換する
let mapPoint = MKMapPoint(location)
// マップ上のポイントを MKCircleRenderer 領域内のポイントに変換する
let rendererPoint = renderer.point(for: mapPoint)
if renderer.path.contains(rendererPoint) {
    // 含まれる
}

ちなみにMKCircleMKMapViewに実際に addOverlay していない状態でも判定可能。

マップ上でタップした地点が MKCircle 領域内かを判定する例

MKMapView に MKCircle を addOverlay することでマップ上に円図形がオーバーレイされる。

let circle = MKCircle(center: center, radius: radius)
mapView.addOverlay(circle)

タップジェスチャ登録。

let tapGesture = UITapGestureRecognizer(target: self, action: #selector(mapTapped(_:)))
mapView.addGestureRecognizer(tapGesture)

タップジェスチャ時に呼び出すメソッド。

@objc func mapTapped(_ sender: UITapGestureRecognizer) {
    if sender.state == .ended {
        let tapPoint = sender.location(in: mapView)
        let coord = mapView.convert(tapPoint, toCoordinateFrom: mapView)

        for case let circle as MKCircle in mapView.overlays { // 1つの MKCircle が追加されていることを想定
            let renderer = MKCircleRenderer(circle: circle)
            let mapPoint = MKMapPoint(coord)
            let rendererPoint = renderer.point(for: mapPoint)
            if renderer.path.contains(rendererPoint) {
                // 含まれる
            }
        }
    }
}

MKPolygon 領域内かを判定する

MKPolygonからMKPolygonRendererを作成して、renderer.path.containsメソッドで判定できる。

// 対象の座標
let location = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)

// 四角形領域
var coordinates: [CLLocationCoordinate2D] = [coord1, coord2, coord3, coord4]
let polygon = MKPolygon(coordinates: &coordinates, count: coordinates.count)
let renderer = MKPolygonRenderer(polygon: polygon)

let mapPoint = MKMapPoint(location)
let rendererPoint = renderer.point(for: mapPoint)
if renderer.path.contains(rendererPoint) {
    // 含まれる
}

MKCircle、MKPolygon の Extension にする

extension MKCircle {

    func contains(_ coordinate: CLLocationCoordinate2D) -> Bool {
        let renderer = MKCircleRenderer(circle: self)
        let mapPoint = MKMapPoint(coordinate)
        let rendererPoint = renderer.point(for: mapPoint)
        return renderer.path.contains(rendererPoint)
    }
}

extension MKPolygon {

    func contains(_ coordinate: CLLocationCoordinate2D) -> Bool {
        let renderer = MKPolygonRenderer(polygon: self)
        let mapPoint = MKMapPoint(coordinate)
        let rendererPoint = renderer.point(for: mapPoint)
        return renderer.path.contains(rendererPoint)
    }
}