[Swift] GoogleMapAPI ~現在地付近の施設情報を取得し、任意の場所でピン立て~

# はじめに

これは、前回書いた記事[[Swift] GoogleMapAPI　〜Map表示＆現在値にピン立て編〜](https://qiita.com/Ritsuya/items/2a628d89d0004a2a4488)
と関連した部分があります。

なので、GoogleMapAPIの導入などは省きますので、導入部分を読みたい方は前回記事をご参考ください。

### 対象者
- GoogleMapをアプリに組み込もうとしている方
- PlacesAPIを検討している方

### バージョン
- Xcode....Version 11.1
- Swift....5.0

# 完成物
GoogleMapで現在地を表示したあと、現在地付近の情報をtableViewに表示し、cellをタップするとその地名にピンを立てる仕様になっております。

![IMB_Iuupat.GIF](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373532/5b5b5577-bbfc-5da3-3b40-8fab6ce5e2b5.gif)



#概要
前回記事では、現在位置をGoogleMapに表示する際に、Corelocationというデフォルトで存在するフレームワークとMaps SDK for iosの両方を用いて行っていましたが、今回はPlaces SDK for iosを用いて行いました。

#実装
それでは実装に移ります。

基本的に、公式ドキュメントから読み取ったものなので、これを見た後に公式ドキュメントを見ると、理解が深まってよいかもしれません。

### MapViewControllerに書くコード
まず、変数宣言していきます。今回宣言したのは以下の通り。

```swift:MapVIewController.swift
  @IBOutlet var showMapView: UIView!
    
  var locationManager = CLLocationManager()
  var mapView: GMSMapView!
  var placesClient: GMSPlacesClient!
  var zoomLevel: Float = 15.0
```
ここでは、表示するViewの名前と、その他初期設定をしました。また、以下も宣言しました。

```swift:MapVIewController.swift
  // 近くにある建造物の情報を格納
  var likelyPlaces: [GMSPlace] = []

  // PlacesViewControllerのtableViewで選択されたときの建造物についての情報
  var selectedPlace: GMSPlace?
```
意味合いとしてはコメントアウトした通りです。

次に、`CLLocationManagerDelegate`のDelegateメソッドをMapViewControllerに追記します。

```swift:MapViewController.swift
// Delegates to handle events for the location manager.
extension MapViewController: CLLocationManagerDelegate {

  // Handle incoming location events.
  func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    let location: CLLocation = locations.last!
    print("Location: \(location)")

    let camera = GMSCameraPosition.camera(withLatitude: location.coordinate.latitude,
                                          longitude: location.coordinate.longitude,
                                          zoom: zoomLevel)

    if mapView.isHidden {
      mapView.isHidden = false
      mapView.camera = camera
    } else {
      mapView.animate(to: camera)
    }

  }

  // Handle authorization for the location manager.
  func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
    switch status {
    case .restricted:
      print("Location access was restricted")
    case .denied:
      print("User denied access to location.")
      // Display the map using the default location.
      mapView.isHidden = false
    case .notDetermined:
      print("Location status not determined.")
    case .authorizedAlways: fallthrough
    case .authorizedWhenInUse:
      print("Location status is OK.")
    @unknown default:
      fatalError()
    }
  }

  // Handle location manager errors.
  func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
    locationManager.stopUpdatingLocation()
    print("Error: \(error)")
  }
}
```
上記のlocationManager(_: didUpdateLocations:)デリゲートメソッドは、位置情報を取得・更新するたびに呼ばれます。また、locationManager(_: didChangeAuthorization)デリゲートメソッドは、ユーザーのプリバシー情報が変わるたびに呼ばれます。

次に、Viewdidloadに以下のコードを書きます。

```swift:MapViewController.swift

override func viewDidLoad() {
  super.viewDidLoad()
        
  //  location managerの初期化
  locationManager = CLLocationManager()
  locationManager.desiredAccuracy = kCLLocationAccuracyBest
  locationManager.requestAlwaysAuthorization()
  locationManager.distanceFilter = 50
  locationManager.startUpdatingLocation()
  locationManager.delegate = self

   placesClient = GMSPlacesClient.shared()

   // Create a map.
   let camera = GMSCameraPosition.camera(withLatitude: defaultLocation.coordinate.latitude,
                                               longitude: defaultLocation.coordinate.longitude,
                                               zoom: zoomLevel)
  mapView = GMSMapView.map(withFrame: showMapView.bounds, camera: camera)
//
  mapView.settings.myLocationButton = true
  mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  mapView.isMyLocationEnabled = true

  // Add the map to the view, hide it until we&#39;ve got a location update.
  showMapView.addSubview(mapView)
  mapView.isHidden = true 
}
```

上記は、ViewDidloadにて、location managerの初期化とMapの表示を行います。

次に、以下のメソッドを追加します。これは、近くにある施設の情報を、likelyPlacesの配列に追加するためのものです。

```swift:MapViewController.swift

//可能性のある場所を配列に入力.
    func listLikelyPlaces() {

      placesClient.currentPlace(callback: { (placeLikelihoods, error) -> Void in
        if let error = error {
          // TODO: Handle the error.
          print("Current Place error: \(error.localizedDescription)")
          return
        }

        // likelyPlacesに値を挿入
        if let likelihoodList = placeLikelihoods {
          for likelihood in likelihoodList.likelihoods {
            let place = likelihood.place
            print("Current Place name \(String(describing: place.name)) at likelihood \(likelihood.likelihood)")
            print("Current PlaceID \(String(describing: place.placeID))")
            self.likelyPlaces.append(place)
             
          }
        }
      })
    }
```

次に、ボタンを押したときに、次の画面に`likelyPlaces`配列の値を受け渡すために、`prepare(for segue:)`を追加で書きます。

```swift:MapViewController.swift

// Prepare the segue.
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
      if segue.identifier == "segueToSelect" {
        if let nextViewController = segue.destination as? PlacesViewController {
          nextViewController.likelyPlaces = likelyPlaces
        }
      }
    }
```

ボタンを押したときのアクションは以下の通り。

```swift:MapViewController.swift

    @IBAction func unwindToMain(segue: UIStoryboardSegue) {
        // Clear the map.
        mapView.clear()

        listLikelyPlaces()
       
        self.performSegue(withIdentifier: "segueToSelect", sender: nil)
       
      }

```

これで、ひとまずMapViewControllerのコードは落ち着きました。ビルドして、Mapが表示されること・likelyPlacesを出力してみて値が入っているかなどを確認してください。


### PlacesViewControllerに書くコード

次に、周辺施設の情報をtableViewにてまとめている、についてふれます。

これは、tableViewの知識でいけるので、コードのみ記載します。

```swift:PlacesViewController.swift

//可能性のある場所を配列に入力.
import UIKit
import GoogleMaps
import GooglePlaces

class PlacesViewController: UIViewController,UITableViewDataSource, UITableViewDelegate {
 
    @IBOutlet weak var tableView: UITableView!

    var likelyPlaces: [GMSPlace] = []
    var selectedPlace: GMSPlace?

    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.dataSource = self
        tableView.delegate = self
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        likelyPlaces.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
        cell?.textLabel?.text = likelyPlaces[indexPath.row].name
        return cell!
    }

    // Pass the selected place to the new view controller.
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
      if segue.identifier == "unwindToMain" {
        if let nextViewController = segue.destination as? MapViewController {
          nextViewController.selectedPlace = selectedPlace
        }
      }
    }
    
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        selectedPlace = likelyPlaces[indexPath.row]
        performSegue(withIdentifier: "unwindToMain", sender: self)
    }
    
}


```
最後のtableViewのdelegateメソッドと`prepare(forsegue:)`で、タップされたcellの情報を次のviewControllerに受け渡しています。


### MapViewControllerに付け足し

値が受け渡された先のViewControllerも、最初に作ったMapViewControllerとします。ただ、cellの値の部分にピン立てをするコードを付け足します。

viewDidLoad()にて、以下のコードを付け足してください。

```swift:MapViewController

  override func viewDidLoad() {
        super.viewDidLoad()
        
        
        // mapにmarkerを加える
        if selectedPlace != nil {
          let marker = GMSMarker(position: (self.selectedPlace?.coordinate)!)
          marker.title = selectedPlace?.name
          marker.snippet = selectedPlace?.formattedAddress
          marker.map = mapView
        }

    }


```

これで、完成版のように表示することができたかと思います！

# おわりに
Google Maps Platformが提供しているAPIは多岐に渡ります。

知る分アイディアの幅も拡がると思うので、精進していきます。