xyk blog

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

表示中のマップ(MKMapView)領域内に含まれている座標かどうかを判定する

検証環境:
Xcode 12
Swift 5.3

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

import MapKit

MKMapRect を使う

ある座標が表示中のマップMKMapView領域内に含まれているかで判定する方法。
座標の緯度経度はCLLocationCoordinate2Dで扱っているとする。

  1. 表示中のマップ領域の矩形MKMapRectmapView.visibleMapRectで取得する。
  2. 緯度経度CLLocationCoordinate2Dをマップ座標系MKMapPointに変換する。
  3. MKMapRect#contains(_ point: MKMapPoint)メソッドで領域内の点であるかを判定する。
let mapRect = mapView.visibleMapRect
let mapPoint = MKMapPoint(coordinate)
if mapRect.contains(mapPoint) {
    // 領域内に含まれる
}

使用例: 表示中のマップ領域内に含まれるアノテーションのみ抽出する

let mapRect: MKMapRect = mapView.visibleMapRect
for annotation in mapView.annotations {
    let mapPoint = MKMapPoint(annotation.coordinate)
    if mapRect.contains(mapPoint) {
        // 領域内に含まれる
    }
}

MKCoordinateRegion を使う

ある座標がMKCoordinateRegion領域内に含まれているかで判定する方法。

func standardAngle(_ angle: CLLocationDegrees) -> CLLocationDegrees {
    let angle = angle.truncatingRemainder(dividingBy: 360)
    return angle < -180 ? -360 - angle : angle > 180 ? 360 - angle : angle
}

func regionContains(region: MKCoordinateRegion, location: CLLocationCoordinate2D) -> Bool {
    let deltaLat = abs(standardAngle(region.center.latitude - location.latitude))
    let deltaLon = abs(standardAngle(region.center.longitude - location.longitude))
    return region.span.latitudeDelta / 2 >= deltaLat && region.span.longitudeDelta / 2 >= deltaLon
}

使用例: 表示中のマップ領域内に含まれるアノテーションのみ抽出する

let region: MKCoordinateRegion = mapView.region
for annotation in mapView.annotations {
    if regionContains(region: region, location: annotation.coordinate) {
        // 領域内に含まれる
    }
}

MKCoordinateRegion の Extension にした場合。

extension MKCoordinateRegion {
    
    func contains(location: CLLocationCoordinate2D) -> Bool {
        let deltaLat = abs(standardAngle(center.latitude - location.latitude))
        let deltaLon = abs(standardAngle(center.longitude - location.longitude))
        return span.latitudeDelta / 2 >= deltaLat && span.longitudeDelta / 2 >= deltaLon
    }
    
    private func standardAngle(_ angle: CLLocationDegrees) -> CLLocationDegrees {
        let angle = angle.truncatingRemainder(dividingBy: 360)
        return angle < -180 ? -360 - angle : angle > 180 ? 360 - angle : angle
    }
}
if mapView.region.contains(location: coordinate) {
    // 領域内に含まれる
}