Google Maps APIで観光地、施設名等から住所を表示する

GoogleMapで地名や施設名から緯度、経度を表示できるようにする方法はいくつか見つけましたが、住所を表示できるようにする方法がなかなか見つからなかったので、自分なりに解決した方法をまとめておきます。

#作成するアプリ

![f2df81a4220d468932c0f0ff70ca5b0d.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555962/fe66c643-cc27-b5fb-22a8-6216a1dadd93.gif)


#実装内容
フォームに入力した情報からGoogleMapを更新して、ピンのある場所の緯度経度、住所を表示します。

#実装する前に
Google Maps APIを利用するためには、APIキーを作成する必要があります。
APIキーをまだ作成していない場合は作成してください。
私が参考にした記事があるので、APIキーを作成していない方は参考にしてください。

参考URL
https://qiita.com/nagaseToya/items/e49977efb686ed05eadb

今回私が利用しているAPIライブラリは
・Geocoding API
・Maps JavaScript API
この2つです。


#作成・編集するファイル

・routes.rb
・maps_controller.rb
・index.html.erb

この3つのファイルを編集します。

##ルーティング
```ruby:routes.rb
Rails.application.routes.draw do
  get 'maps/index'
  root to: 'maps#index'
  resources :maps, only: [:index]
end
```
##コントローラー
```ruby:maps_controller.rb
class MapsController < ApplicationController
  def index
  end
end
```
ルーティング、コントローラーはこれで完成です。

#ビューの作成
ビューは記述する内容が多いので段階を踏んで書きます。
・HTMLの作成
・GoogleMapの表示
・入力した情報によるGoogleMapの表示変更
・緯度経度、住所の表示

##HTMLの作成
まずは土台になるHTMLから編集していきます。

```html:index.html.erb
<h2>Google Map Test</h2>
<input id="address" type="textbox" value="">
<input type="button" value="送信" onclick="codeAddress()">
<div id="latlngDisplay">ここに緯度、経緯が表示される</div>
<div id="addressDisplay">ここに住所が表示される</div>
<div id="map"></div>

<style>
#map{
  height: 600px;
  width: 600px;
}
</style>
```

以下のように表示できたら成功です。
<img width="573" alt="スクリーンショット 2020-04-30 16.22.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555962/c9744d31-f2c2-590c-c8c4-8dc9c1fd0f53.png">


ここでは`<style>`タグを使用してマップの大きさを指定していますが、設定しないとGoogleMapが表示されなくなるので必ず指定するようにしましょう。

確認する場合は、`<style>`タグの中に下の１文を追加したら確認することができます。

```html:
<style>
#map{
  height: 600px;
  width: 600px;
  /* 下の1文を追加 */
  background-color: red;
}
</style>
```

##GoogleMapの表示
次は`map`にGoogleMapが表示されるように編集します。
先ほどのファイルに以下のコードを追加してください。

```html:
<style>
/* 省略 */
</style>
<script>
function initMap(){

  tokyo = {lat: 35.6803997, lng: 139.7690174}

  map = new google.maps.Map(document.getElementById('map'), {
    center: tokyo,
    zoom: 12,
  });
  
  marker = new google.maps.Marker({
    position: tokyo,
    map: map
  });
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap" async defer></script>
```


順に説明していきます。

```js:
  function initMap(){
  //省略
  }
```

`intiMap`という関数はGoogle Maps APIの読み込みが完了した後に呼び出される関数なので、変更しないでください。
変更すると動作しなくなります。

```js:
  //latが緯度、lngが経度を示す
  tokyo = {lat: 35.6803997, lng: 139.7690174}

  map = new google.maps.Map(document.getElementById('map'), {

    //地図の中心になる座標を指定
    center: tokyo,
    //0~20の間の数値で、表示する地図の大きさを指定
    zoom: 12,
  });

  marker = new google.maps.Marker({

    //ピンの表示位置を座標で指定
    position: tokyo,
    //ピンを表示する地図を指定
    map: map
  });
```

`new google.maps.Map`はGoogleMapを表示するために、`new google.maps.Marker`はGoogleMap上にピンを表示するために必要になります。

それぞれの第2引数に指定されているオプション`center`、`zoom`、`position`、`map`は必ず設定が必要なので忘れずに設定しましょう。

今回は東京の座標を設定していますが、別の座標でも構いません。

```js:
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap" async defer></script>
```

最後の行の`YOUR_API_KEY`には自分で取得したAPIキーを入れてください。

ここまで実装したら以下のように表示できるか確認しましょう。
表示できたら成功です。

<img width="605" alt="スクリーンショット 2020-04-30 16.15.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555962/094fd105-2640-a9bb-55fc-ec597b242e6e.png">

##入力した情報によるGoogleMapの表示変更
ここからは実際にフォームに地名、施設名を入力して、GoogleMapに入力した場所を表示変更できるようにします。

以下のコードを追加します。

```js:

funciton initMap(){
//省略
  });
}

function codeAddress(){
  geocoder = new google.maps.Geocoder();

  inputAddress = document.getElementById('address').value;

  geocoder.geocode({ 'address': inputAddress}, function(results, status){
    if (status == 'OK') {
      map.setCenter(results[0].geometry.location);
      marker = new google.maps.Marker({
        position: results[0].geometry.location,
        map: map,
      });
    } else {
      alert("該当する結果がありませんでした：" + status);
    }
  });
}
```
順に説明していきます。

```js:
  geocoder = new google.maps.Geocoder();
```

`geocoder = new google.maps.Geocoder();`はGeocoding APIを利用するために必要なコードです。
Geocoding APIを利用する場合は必ず書きましょう。

「そもそもGeocoding APIって何をするためのもの？」という人のために簡単に説明しますと、住所を緯度経度に変換したり、緯度経度を住所に変換するために必要なAPIです。
今回のアプリでは住所を緯度経度に変換するパターンと緯度経度を住所に変換するパターンの両方を利用します。

```js:
  inputAddress = document.getElementById('address').value;
```

これはフォームに入力した文字列を変数に代入するためのコードです。
`value`にはフォームで入力した文字列が入り、その文字列をgeocodeで利用するために変数に代入しています。

```js:
  geocoder.geocode({ 'address': inputAddress}, function(results, status){
    if (status == 'OK') {
      //省略
      } else {
      alert("該当する結果がありませんでした：" + status);
    }
  });
```

`geocoder.geocode();`はGeocodingにリクエストを送ってレスポンスを受け取るために必要なコードです。

第1引数の`{ 'address': inputAddress}`でフォームに入力した文字列を`address`の形でリクエストします。
`address`は地名、施設名、住所などから緯度経度を取得するために必要なオプションです。

第2引数の`function(results, status)`はレスポンスで受け取った結果を処理するために必要なコールバック関数です。
`results`はGeocodingからのレスポンス結果、内容を受け取ります。
`status`はGeocodingからのレスポンスステータスコードを受け取ります。

`if (status == 'OK') {} else {}`はコールバック関数で受け取った`status`がOKだった場合に処理し、それ以外ならelseを処理するというコードです。

```js:
  map.setCenter(results[0].geometry.location);
  marker = new google.maps.Marker({
    position: results[0].geometry.location,
    map: map,
  });
```

そしてここからはコールバック関数で受け取った`results`を利用して`map`、`marker`を設定していきます。

ここではmapの`setCenter`というメソッドを利用してmapの座標を設定します。
`setCenter`を利用するためには緯度経度の値が必要になります。
`results`の中にある緯度経度の値は`results[0].geometry.location`というコードで取得できるので取得して座標を設定します。

markerを設定する方法はGoogleMapの表示のときに説明しているので割愛します。

それではどのようになっているか確認しましょう。
以下のように表示できたら成功です。

![2c89d8c173218b0f755034b68ba7ab65.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555962/11efc697-a1ba-e77f-f22f-c3730a0674ee.gif)

##緯度経度、住所の表示

最後に緯度経度、住所を表示できるようにしていきます。
以下のコードを追加します。

```js:
//ここから追加
const latlngDis = document.getElementById('latlngDisplay')
const addressDis = document.getElementById('addressDisplay')
//ここまで追加

function initMap(){

//省略

function codeAddress(){
      //省略
      marker = new google.maps.Marker({
        map: map,
        position: results[0].geometry.location,
      });
      //ここから追加
      latlngDis.innerHTML = results[0].geometry.location;

      geocoder.geocode({ 'location': marker.getPosition()}, function(results, status) {
        if (status == "OK"){
          addressDis.innerHTML = results[0].formatted_address;
        } else {
          alert("Geocode 取得に失敗しました：" + status);
        }
      });
      //ここまで追加
    } else {
      alert("該当する結果がありませんでした：" + status);
    }
  });
}
```

順に説明していきます。

```js:
const latlngDis = document.getElementById('latlngDisplay')
const addressDis = document.getElementById('addressDisplay')
```

まずは、それぞれ緯度経度、住所を表示したい場所のid名を変数で定義します。
`latlngDis`が緯度経度を表示する場所、`addressDis`が住所を表示する場所です。

```js:
  latlngDis.innerHTML = results[0].geometry.location;
```

`innerHTML`というプロパティを利用してHTMLの`id="latlngDisplay"`に緯度経度の値を持っている`results[0].geometry.location`を表示させます。
これで緯度経度の値を表示することができました。

```js:
  geocoder.geocode({ 'location': marker.getPosition()}, function(results, status)
```

先ほどgeocodeを利用した時は`address`を使いましたが、今回は`location`を使います。
`address`は地名、施設名、住所などからリクエストを送っていましたが、`location`は緯度経度からリクエストを送るためのオプションです。
前回は文字列でリクエストを送りましたが、今回は緯度経度でリクエストを送る必要があります。
なので新しく設定したmarkerの緯度経度の値を`getPosition()`というメソッドで取得します。
これで緯度経度の値からリクエストを送ることができます。

```js:
  addressDis.innerHTML = results[0].formatted_address;
```

`results[0].formatted_address`は`results`の中から住所の値を取得するために必要なコードになります。
取得しましたら、先ほどと同じように`innerHTML`でHTMLに`results[0].formatted_address`の値を表示させましょう。

それでは動作確認してみましょう。
以下のように表示できたら完成です。
お疲れ様でした。

![211baaf0c41c2dd54917ce93c3ab7961.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555962/39e9ff2b-32f3-e496-1d89-fc36bd7682b0.gif)

#追記（2020/05/03）
Geocodeingを利用した住所の表示方法についてですが、

```js:
  geocoder.geocode({ 'location': marker.getPosition()}, function(results, status) {
    if (status == "OK"){
      addressDis.innerHTML = results[0].formatted_address;
    } else {
      alert("Geocode 取得に失敗しました：" + status);
        }
  });
```
とコードを書いていましたが、この部分を変更して

```js:
  addressDis.innerHTML = results[0].formatted_address;
```

と書いても表示することができました。

私はGeocodingにリクエストを送る方法として

```js:
  geocoder.geocode({ 'address': inputAddress}, function(results, status)
```

と書いていましたが、どうやら`address`でリクエストを送って、レスポンスされた`results`の中に、`formatted_address`が取得されているので、`location`でリクエストを送る必要はないということが判明しました。

今までは緯度経度と住所の表示にラグがありましたが、これは一つのリクエストで完結しているので、表示にラグがなくなりました。

リクエストを２度送る必要がないので、処理的にもこちらにした方がいいと思います。

#参考にした記事、サイト
https://www.webdesignleaves.com/pr/plugins/googlemap_01.html

https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/

https://qiita.com/nagaseToya/items/e49977efb686ed05eadb