1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Swift] MapViewで山手線の各駅にピンを立てて線で結んでみた

Last updated at Posted at 2023-05-04

MapViewでピンを立てて線で結ぶにはどうするの?

散歩した軌跡をマップに表示したいと思い、MapViewでどう実現するのかを調べた。
そこで得た成果として『山手線の各駅にピンを立てて線で結ぶ』コードを書いたてみた。

XcodeのPlaygroundで動かしました。

ピンの立て方

アノテーションが正しい呼び方。(PinAnnotationはDeprecated)

let annotation = MKPointAnnotation()
let (latitude, longitude) = ピンを立てる座標経度緯度
annotation.title = title
annotation.coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
mapView.addAnnotation(annotation)

座標データMKPointAnnotationを用意して、mapView.addAnnotation()呼ぶだけ。
今回は使っていないが、複数の座標にまとめてピンを立てるなら、座標データを配列にしてmapView.addAnnotations()を呼ぶ。

let annotations: [MKPointAnnotation] = [
 座標データを配列にする
]
mapView.addAnnotations(annotations)

ピンの色などは後ほど説明するデリゲートメソッドで指定する。

座標間の結び方

線を引いていく順に座標を配列にしてpolylineを作成し、mapView.addOverlay()を呼ぶ。

let coordinates: [CLLocationCoordinate2D] = [
    線で結びたい座標データをCLLocationCoordinate2Dの配列にする
]
let polyline = MKPolyline(coordinates: coordinates, count: coordinates.count)
mapView.addOverlay(polyline, level: .aboveRoads)

線の色や太さなどは、次に説明するデリゲートメソッドで指定する。

MapViewデリゲートメソッド

class MapDelegate: NSObject, MKMapViewDelegate {
    // ピンの色やイメージを指定する
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        let identifier = "annotation"
        if let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) {
            annotationView.annotation = annotation
            return annotationView
        } else {
            let annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
            annotationView.markerTintColor = .blue
         // annotationView.glyphImage = 独自バルーンのイメージ
            return annotationView
        }
    }

    //線の色や太さを指定する
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        let renderer = MKPolylineRenderer(overlay: overlay)
        renderer.strokeColor = .orange
        renderer.lineWidth = 3.0
        return renderer
    }
}

縮尺の指定

これを指定しないと、冒頭動画の初めのように日本列島全体が見渡せるかなり高所から見たマップが表示されます。
方法は、見せたい複数の座標点をカバーする範囲をMKCoordinateRegionで指定して、mapView.setRegion()を呼びます。
線で結ぶときに作ったPolylineからboundingMapRectプロパティで得られる領域を引数とします。

  let coordinates: [CLLocationCoordinate2D] = [
      線で結びたい座標データをCLLocationCoordinate2Dの配列にする
  ]
  let polyline = MKPolyline(coordinates: coordinates, count: coordinates.count)
  mapView.addOverlay(polyline, level: .aboveRoads)

+ var region = MKCoordinateRegion(polyline.boundingMapRect)
+ region.span.latitudeDelta *= 1.2
+ region.span.longitudeDelta *= 1.2
+ mapView.setRegion(region, animated: true)

マップの上下左右に余裕ができるように1.2倍しています。

『山手線の各駅にピンを立てて線で結ぶ』コード

最後に、冒頭の動画で使ったコード全体を載せておきます。
タイマーを使って、3秒ごとに駅にピンを立て線で結びます。
縮尺(MKCoordinateRegion)は、駅にピンを立てるたびに領域にunionで付け加えています。
各駅のデータは、コードの末尾に配列で定義しています。

import PlaygroundSupport
import MapKit

let mapView = MKMapView()
mapView.frame = CGRect(x: 0, y: 0, width: 600, height: 600)
PlaygroundPage.current.liveView = mapView
PlaygroundPage.current.needsIndefiniteExecution = true

let mapDelegate = MapDelegate()
mapView.delegate = mapDelegate

var stations = 0
var boundingMapRect = MKMapRect()
let timer = Timer.scheduledTimer(withTimeInterval: 3.0, repeats: true) { timer in
    if stations > YamanoteLineCoordinates.count {
        timer.invalidate()
    } else {
        let annotation = MKPointAnnotation()
        let (title, latitude, longitude) = YamanoteLineCoordinates[stations % YamanoteLineCoordinates.count]
        annotation.title = title
        annotation.coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
        if stations < YamanoteLineCoordinates.count {
            mapView.addAnnotation(annotation)
        }
        
        if stations > 0 {
            let (_, latitude, longitude) = YamanoteLineCoordinates[stations - 1]
            let coordinate2 = [
                CLLocationCoordinate2D(latitude: latitude, longitude: longitude),
                annotation.coordinate
            ]
            let polyline = MKPolyline(coordinates: coordinate2, count: coordinate2.count)
            mapView.addOverlay(polyline, level: .aboveRoads)
            boundingMapRect.isEmpty
            boundingMapRect = boundingMapRect.isEmpty ? polyline.boundingMapRect : boundingMapRect.union(polyline.boundingMapRect)

            var region = MKCoordinateRegion(boundingMapRect)
            region.span.latitudeDelta = region.span.latitudeDelta * 1.2
            region.span.longitudeDelta = region.span.longitudeDelta * 1.2
            mapView.setRegion(region, animated: true)
        }
    }
    stations += 1
}

class MapDelegate: NSObject, MKMapViewDelegate {
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        let renderer = MKPolylineRenderer(overlay: overlay)
        renderer.strokeColor = .orange
        renderer.lineWidth = 3.0
        return renderer
    }
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        let identifier = "annotation"
        if let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) {
            annotationView.annotation = annotation
            return annotationView
        } else {
            let annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
            annotationView.markerTintColor = .blue
            return annotationView
        }
    }
}

var YamanoteLineCoordinates: [(String, Double, Double)] {
[ //(駅名, 緯度, 経度),
    ("東京", 35.681382, 139.766084),
    ("有楽町", 35.675069, 139.763328),
    ("新橋", 35.665498, 139.75964),
    ("浜松町", 35.655646, 139.756749),
    ("田町", 35.645736, 139.747575),
    ("高輪ゲートウェイ", 35.6355, 139.7407),
    ("品川", 35.630152, 139.74044),
    ("大崎", 35.6197, 139.728553),
    ("五反田", 35.626446, 139.723444),
    ("目黒", 35.633998, 139.715828),
    ("恵比寿", 35.64669, 139.710106),
    ("渋谷", 35.658517, 139.701334),
    ("原宿", 35.670168, 139.702687),
    ("代々木", 35.683061, 139.702042),
    ("新宿", 35.690921, 139.700258),
    ("新大久保", 35.701306, 139.700044),
    ("高田馬場", 35.712285, 139.703782),
    ("目白", 35.721204, 139.706587),
    ("池袋", 35.728926, 139.71038),
    ("大塚", 35.731401, 139.728662),
    ("巣鴨", 35.733492, 139.739345),
    ("駒込", 35.736489, 139.746875),
    ("田端", 35.738062, 139.76086),
    ("西日暮里", 35.732135, 139.766787),
    ("日暮里", 35.727772, 139.770987),
    ("鶯谷", 35.720495, 139.778837),
    ("上野", 35.713768, 139.777254),
    ("御徒町", 35.707438, 139.774632),
    ("秋葉原", 35.698683, 139.774219),
    ("神田", 35.69169, 139.770883),
] }
  • Xcode 14.3
  • Swift 5.8
  • macOS 13.3.1(a)

おしまい

点の集合でも軌跡は表現できるが、やはり線で結びたいと思い調べて実装しました。(ピンを立てずに線を引くだけで軌跡を表現)

以上です

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?