Swift MkMapViewで地図アプリ作成してみた（16）- iOSから転送された地点から現在位置の距離をwatchOSで計算して表示する

#記事一覧
[Swift MkMapViewで地図アプリ作成してみた（記事一覧）](https://qiita.com/rain_00_00_09/items/3151b3faf2303fe78312)

#iOSのアプリから取得した緯度経度を保持する
###iOSのアプリでロングタップした位置をwatchOSに送信する
sendMessageでwatchOSにロングタップした位置を送信する方法は以下の記事を参照ください。

* [Swift MkMapViewで地図アプリ作成してみた（14）- iOSから送信したデータをwatchOSで受信する](https://qiita.com/rain_00_00_09/items/59ff413b71caf4189656)
* [Swift MkMapViewで地図アプリ作成してみた（15）- watchOSからiOSにデータ送信を要求する](https://qiita.com/rain_00_00_09/items/cb4aefc6a027c36904db)

###受信した緯度経度を保持する

クラスのメンバ変数に緯度経度を追加し、iOSからsendMessageで送信された緯度経度を変数に設定する。

```swift:InterfaceController.swift：緯度経度の変数を追加
class InterfaceController:  WKInterfaceController,
                            WCSessionDelegate { // sasaki
    var dlon:Double! = 0
    var dlat:Double! = 0
```

```swift:InterfaceController.swift：iOSからsendMessageで送信された緯度経度を変数に設定
    // iPhoneからMessage受信
    public func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Swift.Void){
        print("receiveMessage::\(message)")
        
        guard let lon = message["lon"] as? Double else {
            return
        }

        guard let lat = message["lat"] as? Double else {
            return
        }
        
        // iOSからsendMessageで送信された緯度経度を変数に設定
        dlon = lon
        dlat = lat
    }
```

#CLLocationManagerで現在位置を取得する
###CLLocationManagerのdelegateを登録する

CoreLocationをimportし、CLLocationManagerDelegateを継承する。
現在位置取得に必要なCLLocationManagerの変数を定義する。

```swift:InterfaceController.swift
import CoreLocation

class InterfaceController:  WKInterfaceController,
                            WCSessionDelegate,
                            CLLocationManagerDelegate {
    var dlon:Double! = 0
    var dlat:Double! = 0
    var locationManager = CLLocationManager()
```

Initializerメソッドで、CLLocationManagerのdelegateを登録する。

```swift:InterfaceController.swift
    override init() {
        super.init()
        
        // CLLocationManagerのdelegateを登録
        locationManager.delegate = self
    }
```

###CLLocationManagerで現在位置の更新を受信する

以下の条件を満たしている場合に、startUpdatingLocationメソッドで現在位置の更新を開始できる。

* locationServicesEnabledメソッドで端末の位置情報サービスが有効である
* authorizationStatusメソッドでユーザから位置情報利用の許可を一度得ている

```swift:InterfaceController.swift
    override init() {
        super.init()
        
        // CLLocationManagerのdelegateを登録
        locationManager.delegate = self

        // 端末の位置情報サービスが有効である
        if CLLocationManager.locationServicesEnabled() {
            switch CLLocationManager.authorizationStatus() {
            // ユーザから位置情報利用の許可を一度得ている
            case .authorizedAlways, .authorizedWhenInUse:
                // 現在位置の更新を開始
                locationManager.startUpdatingLocation()
            case .notDetermined:
                print("notDetermined")
            case .restricted:
                print("restricted")
            case .denied:
                print("denied")
            }
        }
    }
```

locationManagerメソッドを定義すると、現在位置の更新を受信できる。

```swift:InterfaceController.swift
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

        print(Date(), locations)
    }
```

#現在位置更新を受信した時にiOSから受信した位置との距離を表示する
###2点間の距離(m)を算出するメソッドを作成する

引数に現在位置を指定して、クラスのメンバ変数に設定したiOSから受信した緯度経度との距離(m)を算出するメソッドを作成する。

```swift:InterfaceController.swift
    func calcDistance(_ a: CLLocationCoordinate2D) -> CLLocationDistance {
        // CLLocationオブジェクトを生成
        let aLoc: CLLocation = CLLocation(latitude: a.latitude, longitude: a.longitude)
        let cloclon:CLLocationDegrees = CLLocationDegrees(exactly: dlon)!
        let cloclat:CLLocationDegrees = CLLocationDegrees(exactly: dlat)!
        let bLoc: CLLocation = CLLocation(latitude: cloclat, longitude: cloclon)
        // CLLocationオブジェクトのdistanceで2点間の距離(m)を算出
        let dist = bLoc.distance(from: aLoc)
        return dist
    }
```

###現在位置の更新を受信した時に距離を算出してwatchOSのアプリに表示する

locationManagerメソッドで現在位置の更新を受信した時に、iOSから受信した緯度経度との距離(m)を算出して表示する。

```swift:InterfaceController.swift
    // CLLocationManagerのdelegate：現在位置更新
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

        print(Date(), locations)

        // iOSから緯度経度を受信している場合
        if dlon != 0 && dlat != 0 {
            // 現在位置とiOSから受信した緯度経度との距離(m)を算出する
            let distance = calcDistance((locations.last?.coordinate)!)
            // mをyardに変換する
            let yardStr = Int(distance * 1.09361)
            // 距離をラベルのテキストに設定する
            let fontSize = UIFont.systemFont(ofSize: 45)
            let text = String(Int(distance).description + "m" + "\n" + yardStr.description + "y")
            let attrStr = NSAttributedString(string: text, attributes:[NSAttributedString.Key.font:fontSize])
            label.setAttributedText(attrStr)
        }
    }
```

#iOSから緯度経度を受信した時にも現在位置との距離を表示する
###現在位置をクラスのメンバ変数に保持する

メンバ変数をCLLocationCoordinate2D型で定義する。

```swift:InterfaceController.swift
class InterfaceController:  WKInterfaceController,
                            WCSessionDelegate,
                            CLLocationManagerDelegate {
    var locCord2D:CLLocationCoordinate2D?
```

現在位置をクラスのメンバ変数に保持する。

```swift:InterfaceController.swift
    // CLLocationManagerのdelegate：現在位置更新
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        print(Date(), locations)

        // 現在位置をクラスのメンバ変数に保持する
        locCord2D = (locations.last?.coordinate)!

        // iOSから緯度経度を受信している場合
        if dlon != 0 && dlat != 0 {
```

###iOSから緯度経度を受信した時にも現在位置との距離を表示する

sessionメソッドに距離を表示する処理を追加する。

```swift:InterfaceController.swift
    // iPhoneからMessage受信
    public func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Swift.Void){
        print("receiveMessage::\(message)")
        
        guard let lon = message["lon"] as? Double else {
            return
        }

        guard let lat = message["lat"] as? Double else {
            return
        }

        // iOSからsendMessageで送信された緯度経度を変数に設定
        dlon = lon
        dlat = lat

        // iOSから緯度経度を受信した時にも現在位置との距離を表示する
        if nil != locCord2D {
            // 現在位置とiOSから受信した緯度経度との距離(m)を算出する
            let distance = calcDistance(locCord2D!)
            // mをyardに変換する
            let yardStr = Int(distance * 1.09361)
            // 距離をラベルのテキストに設定する
            let fontSize = UIFont.systemFont(ofSize: 45)
            let text = String(Int(distance).description + "m" + "\n" + yardStr.description + "y")
            let attrStr = NSAttributedString(string: text, attributes:[NSAttributedString.Key.font:fontSize])
            label.setAttributedText(attrStr)
        }
    }
```

<img width="750" alt="image1.png" src="https://qiita-image-store.s3.amazonaws.com/0/362635/cf7f165f-01a6-6124-4edf-f15e7eabe195.png">


#記事一覧
[Swift MkMapViewで地図アプリ作成してみた（記事一覧）](https://qiita.com/rain_00_00_09/items/3151b3faf2303fe78312)