【Swift】Google Map API for iOSを使用してみた

# Google Map API for iOS

iOSに用意されているMapKitが微妙だったので、
Googleが提供するAPIを試してみた。

iOSネイティブなAPIだけでも情報が多かったので整理してみた。

## Maps SDK for iOS

通常のGoogleマップを表示するために使用する。特定の固定位置情報を表現したり、画像を取得するには優れているが
検索機能がデフォルトでないため、ユーザが独自に検索して場所を検索する用途には向かなそう。
（検索は後述のPlacePickerでカバーされている）

### マップオブジェクト

UIViewクラスのサブクラスでGMSMapViewクラスを提供している。
UIViewControllerが持つviewにGMSMapViewのインスタンスを上書きすると、
拡大縮小可能なマップが画面いっぱいに表示される。
カメラのアングルも設定可能。

```swift
import UIKit
import GoogleMaps

class DemoViewController:UIViewController {
  override func loadView() {
    let camera = GMSCameraPosition.camera(withLatitude:1.285, longitude:103.848, zoom:12)
    let mapView = GMSMapView.map(withFrame: .zero, camera: camera)
    self.view = mapView
  }
}
```

マップの種類も「標準」「ハイブリッド」「航空写真」「地形図」から選択できる。

現在の位置情報も取得することが可能。

```swift
mapView.isMyLocationEnabled = true
if let mylocation = mapView.myLocation {
  print("User's location: \(mylocation)")
} else {
  print("User's location is unknown")
}
```

マップのパディングを指定することも可能。

```swift
let mapInsets = UIEdgeInsets(top:100.0, left:0.0, bottom:0.0, right:300.0)
mapView.padding = mapInsets
```

### 商業施設やその他の有名スポット
有名スポットはベースマップにデフォルトで表示される。
タップ操作に応答したい場合は、GMSMapViewDelegateを実装し、mapViewメソッドを追加する。

```swift
import UIKit
import GoogleMaps

class ViewController:UIViewController, GMSMapViewDelegate {

...

  override func loadView() {
    let camera = GMSCameraPosition.camera(withLatitude:47.603,
                                          longitude:-122.331,
                                          zoom:14)
    let mapView = GMSMapView.map(withFrame: .zero, camera: camera)
    mapView.delegate = self
    self.view = mapView
  }

  func mapView(_ mapView:GMSMapView, didTapPOIWithPlaceID placeID:String,
               name:String, location:CLLocationCoordinate2D) {
    print("You tapped \(name): \(placeID), \(location.latitude)/\(location.longitude)")
  }

}
```

### ストリートビュー

ストリートビューの画像は正距円筒図法（Plate Carrée 図法）に準拠し、
360 度の水平ビュー（周囲全体）と 180 度の垂直ビュー（真上から真下）を含む。
ストリートビューのパノラマ画像は、GMSPanoramaView オブジェクトで参照可能。

ストリートビューのパノラマ画像は、2 つのメタデータのどちらかで特定する

#### panoramaID
ストリートビューのパノラマ画像の一意なID。 このpanoramaIDは、将来的に変わる可能性があるため、
長期間にわたって参照する場合やハード コーディングを行う場合には適さない。

#### coordinate
CLLocationCoordinate2D で表現したこの画像の正確な位置。
coordinateは、パノラマの場所を永続的に保存する場合や、マップ上でのユーザーのアクションを
ストリートビュー画像に変換する場合に使用します

```swift
import UIKit
import GoogleMaps

class ViewController:UIViewController, GMSMapViewDelegate {

  override func loadView() {
    let panoView = GMSPanoramaView(frame: .zero)
    self.view = panoView

    panoView.moveNearCoordinate(CLLocationCoordinate2D(latitude: -33.732, longitude:150.312))
  }
}
```

### マーカーのカスタマイズ

#### マーカーの追加
マーカーを追加するには、position と title を含む GMSMarker オブジェクトを作成し、map を設定する。
```swift
let position = CLLocationCoordinate2D(latitude: 10, longitude: 10)
let marker = GMSMarker(position: position)
marker.title = "Hello World"
marker.map = mapView
```

marker.appearAnimation プロパティを kGMSMarkerAnimationPop に設定すると、
新しいマーカーのマップへの追加をアニメーション表示できる。

#### マーカーの色変更
```swift
marker.icon = GMSMarker.markerImage(with: .black)
```

#### カスタムアイコンの設定
```swift
let position = CLLocationCoordinate2D(latitude:51.5, longitude: -0.127)
let london = GMSMarker(position: position)
london.title = "London"
london.icon = UIImage(named: "house")
london.map = mapView
```

#### マーカーの透明度
```swift
marker.opacity = 0.6
```

#### 情報ウィンドウの追加
情報ウィンドウを使用して、ユーザーがマーカーをタップしたときに情報を表示できる。
情報ウィンドウの内容は、title プロパティと snippet プロパティによって定義される。

```swift
let position = CLLocationCoordinate2D(latitude:51.5, longitude: -0.127)
let london = GMSMarker(position: position)
london.title = "London"
london.snippet = "Population: 8,174,100"
london.map = mapView
```

自動更新をオンにしたい場合は以下の設定を追加。

```swift
marker.tracksInfoWindowChanges = true
```

### 図形の追加
マップ上に図形を追加できる。
 * 複数の線分をつなげたポリライン。
 * 閉じた図形であるポリゴン。
 * 円。

以下ポリゴンをマップ上に追加する方法。

```swift
// Create a rectangular path
let rect = GMSMutablePath()
rect.add(CLLocationCoordinate2D(latitude:37.36, longitude: -122.0))
rect.add(CLLocationCoordinate2D(latitude:37.45, longitude: -122.0))
rect.add(CLLocationCoordinate2D(latitude:37.45, longitude: -122.2))
rect.add(CLLocationCoordinate2D(latitude:37.36, longitude: -122.2))

// Create the polygon, and assign it to the map.
let polygon = GMSPolygon(path: rect)
polygon.fillColor = UIColor(red:0.25, green:0, blue:0, alpha:0.05);
polygon.strokeColor = .black
polygon.strokeWidth = 2
polygon.map = mapView
```

## Places API for iOS
現在位置情報を取得したり、AutoCompleteという検索補助機能が備わっている。使いこなせばすごく便利そうだが
チュートリアル通りにコーディングしても期待通りの結果にならない。。。

### Place Picker
地理的住所やローカル ビジネスに一致するプレイスを含むインタラクティブマップや近隣のプレイスの一覧を表示する
UIダイアログが用意されている。
アプリで Place Picker を使用する場合は、位置情報サービスを利用するためのパーミッションを要求する必要がある。
 * NSLocationWhenInUseUsageDescription
 * NSLocationAlwaysUsageDescription


 ```swift
 // The code snippet below shows how to create and display a GMSPlacePickerViewController.
 @IBAction func pickPlace(_ sender: UIButton) {
   let config = GMSPlacePickerConfig(viewport: nil)
   let placePicker = GMSPlacePickerViewController(config: config)

   present(placePicker, animated: true, completion: nil)
 }

 // To receive the results from the place picker 'self' will need to conform to
 // GMSPlacePickerViewControllerDelegate and implement this code.
 func placePicker(_ viewController: GMSPlacePickerViewController, didPick place: GMSPlace) {
   // Dismiss the place picker, as it cannot dismiss itself.
   viewController.dismiss(animated: true, completion: nil)

   print("Place name \(place.name)")
   print("Place address \(place.formattedAddress)")
   print("Place attributions \(place.attributions)")
 }

 func placePickerDidCancel(_ viewController: GMSPlacePickerViewController) {
   // Dismiss the place picker, as it cannot dismiss itself.
   viewController.dismiss(animated: true, completion: nil)

   print("No place selected")
 }
 ```

### AutoComplete

検索ワードを打ち込むと、関連する有名スポット（駅名や店名等）が検索候補として表示される。
AutoComplete機能もPlacePikcerに内包されているので、基本的にはPlacePickerを呼び出してあげれば問題ない。
 注意）AutoComplete機能を使用するときはマイconsole画面から「Place API for iOS」を有効にしないと検索結果が表示されない。

## Directions API

ルート検索をサポート。Googleの特定URLにリクエストを含めて叩くと、最大23箇所の地点を通過するルートを検索できる。

[デモコード](https://github.com/shunp/GoogleMapDemo)