中身をくり抜いた図形をMapKitの地図に描画する~MKPolygonを利用~


はじめに

MapKitでは地図上の好きな位置に図形を描画することができます。
これにより、特定のエリアに色をつけて目立たせたりすることができます。
今回は、エリアの一部をくり抜いた図形を表示したかったのでその方法を調べました。

完成図

環境

Xcode12.2
iPhone12シミュレータにて動作確認

実装

今回は埼玉県あたりを覆う大きな矩形と、その中に包含される大宮あたりの三角形の小さな図形を用意します。

まず、図形を緯度経度で指定しMKPolygonを作成します。
矩形のほうが大きく、埼玉県全体を覆うもので、三角形はその中に包含されます。

最後に、作成したMKPolygonをMKMapViewに追加しています。


let mapView = MKMapView()

let omiyaArea = [
    CLLocationCoordinate2D(latitude: 35.9243073, longitude: 139.4899383),
    CLLocationCoordinate2D(latitude: 35.9061086, longitude: 139.6257497),
    CLLocationCoordinate2D(latitude: 35.9800549, longitude: 139.5930384)
]
let smallPolygon = MKPolygon(coordinates: omiyaArea, count: omiyaArea.count)

let saitamaArea = [
    CLLocationCoordinate2D(latitude: 35.7926364, longitude: 139.9048961),
    CLLocationCoordinate2D(latitude: 36.2841001, longitude: 139.8764339),
    CLLocationCoordinate2D(latitude: 36.2977199, longitude: 138.6847137),
    CLLocationCoordinate2D(latitude: 35.8108062, longitude: 138.6762004)
]
let saitamaAreaPolygon = MKPolygon(coordinates: saitamaArea, count: saitamaArea.count, interiorPolygons: [smallPolygon])
// 地図に追加
mapView.addOverlay(saitamaAreaPolygon)

ポイントはsaitamaAreaPolygonのMKPolygonのinit処理です。interiorPolygonsにsmallPolygonを配列で指定しています。
くり抜きたい図形をinteriorPolygonsに指定することで三角形部分のみ描画が行われないようになりました。
init(coordinates:count:interiorPolygons:)

色の指定

addOverlayのみでは描画は行われないのでデリゲートメソッドを実装する必要があります。

// 別の箇所にて設定しておく
mapView.delegate = self

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
    if overlay is MKPolygon {
        let renderer = MKPolygonRenderer(overlay: overlay)
        renderer.fillColor = .red
        renderer.alpha = 0.5
        return renderer
    }
    return MKOverlayRenderer(overlay: overlay)
}

mapView(_:rendererFor:)

発展

くり抜いた図形の中の一部は色を描画したい場合はどうすればいいでしょうか。試してみました。

結果

三角形でくり抜いた中にさらに三角形が描画できました。

実装

// くり抜いた三角形に包含されるエリア
let area = [
    CLLocationCoordinate2D(latitude: 35.9543861, longitude: 139.5727745),
    CLLocationCoordinate2D(latitude: 35.9562351, longitude: 139.582018),
    CLLocationCoordinate2D(latitude: 35.9492669, longitude: 139.5809517)
]
let areaPolygon = MKPolygon(coordinates: area, count: area.count)

let omiyaArea = [
    CLLocationCoordinate2D(latitude: 35.9243073, longitude: 139.4899383),
    CLLocationCoordinate2D(latitude: 35.9061086, longitude: 139.6257497),
    CLLocationCoordinate2D(latitude: 35.9800549, longitude: 139.5930384)
]
let smallPolygon = MKPolygon(coordinates: omiyaArea, count: omiyaArea.count)

let saitamaArea = [
    CLLocationCoordinate2D(latitude: 35.7926364, longitude: 139.9048961),
    CLLocationCoordinate2D(latitude: 36.2841001, longitude: 139.8764339),
    CLLocationCoordinate2D(latitude: 36.2977199, longitude: 138.6847137),
    CLLocationCoordinate2D(latitude: 35.8108062, longitude: 138.6762004)
]
// interiorPolygonsにareaPolygonも追加
let saitamaAreaPolygon = MKPolygon(coordinates: saitamaArea, count: saitamaArea.count, interiorPolygons: [smallPolygon, areaPolygon])

mapView.addOverlay(saitamaAreaPolygon)

interiorPolygonsに色を描画したい領域を追加することで実現できました。