【Rails】Google Maps APIを利用して登録した住所をMap表示する

#目標
アプリケーションで登録した住所を元にgoogle mapを表示させる機能実装についてまとめます。
入力フォームに入力した住所から、詳細ページにてマップを表示、ピンを立てて位置表示させるまで実装します。
![1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/771476/28af47f2-ceda-fd2b-7022-0d9b573004da.gif)

#開発環境
* Ruby: 2.6.4
* Rails: 5.2.4
* OS: macOS Catalina

#前提条件
* **既に、住所を登録するためのテーブルおよびカラムは作成済みであること**<br>
私は、音楽スタジオの口コミレビューサイトを作成しました。デモでは、スタジオを新規作成し、その詳細ページにマップ表示されるといったものです。<br>
本記事で登場する変数では下記を使用します。それぞれご自身の環境に合わせて書き換えてください。<br>
テーブル名：studios<br>
カラム名：address

#Google Mapの機能を使うための準備
こちらに関してはこの記事内では省略します。以下の三点が準備出来ている前提で進めていきます。
参考にさせていただいた記事のリンクも貼っておきます。
1. APIキーを取得する
2. Maps JavaScript APIの有効化
　↪https://qiita.com/matsubishi5/items/196fa1941da2152b6d5d
3. Geocoding APIの有効化
　↪https://qiita.com/matsubishi5/items/1b784dbbe5f1c336ac70

#緯度、経度を登録するためのカラムを用意
緯度(latitude)、経度(longitude)のカラムを追加します。
db/migrate/...マイグレーションファイルへの記述でカラムを追加します。

```ruby
class AddDetailsToStudios < ActiveRecord::Migration[5.2]
  def change
    #住所（address）カラムは登録済み
    add_column :studios, :latitude, :float
    add_column :studios, :longitude, :float
  end
end
```
緯度、経度はfloat型にします。floatとは浮動小数点数型です。
難しい名前ですがざっくり言えば小数を扱える型です。
緯度、経度は小数点以下を含む数値によって表されるので、こちらの型にします。

記述したら`rails db:migrate`してください。

#Gemを導入する
```ruby:Gemfile
gem 'gon'
gem 'geocoder'
```
`gem 'gon'`
　↪コントローラーで定義したインスタンス変数を、viewのjavascript内で使用できるようにする
`gem 'geocoder'`
 ↪住所から緯度、経度を算出する

マップは、緯度と経度の情報を元に表示します。
geocoderは、登録した住所から緯度、経度を自動で算出してくれるいいヤツです。

記述したら`bundle install`してください。

#モデルの編集

次に、geocoderを使うために適用するモデルに以下の記述をします。
今回はスタジオの位置情報を表示したいので`/app/models/studio.rb`に記述しました。

```ruby:studio.rb
class Studio < ApplicationRecord
  geocoded_by :address #追記
  after_validation :geocode, if: :address_changed? #追記
```
これで`address`を登録した際に`geocoder`が緯度、経度のカラムにも自動的に値を入れてくれるようになります。

#`geocorder`の設定ファイルを作成し、編集
今の状態でも、登録した住所から緯度、経度を算出してくれますが、
`東京都渋谷区`といった、大まかな住所までしか算出してくれません。

`東京都渋谷区◯-◯◯-◯◯◯`のような細かい位置まで算出するためにはどうすればよいでしょうか？

ここで活躍するのが、`Geocoding API`です。
`geocoder`でもGoogle Map APIの情報源を使えるように設定すれば解決します。

`config`フォルダ内に`geocoder.rb`ファイルを作成します。

```
$ bin/rails g geocoder:config
```
ターミナルで上のコマンドを行うことによって`config/initializers/geocoder.rb`ファイルができます。

作成されたファイルの中身を変更して`geocoder`でgoogle mapのAPIを使って緯度、経度を検索できるようにします。

```ruby:geocoder.rb
Geocoder.configure(
  # Geocoding options
  # timeout: 3,                 # geocoding service timeout (secs)
   lookup: :google,         # name of geocoding service (symbol)
  # ip_lookup: :ipinfo_io,      # name of IP address geocoding service (symbol)
  # language: :en,              # ISO-639 language code
   use_https: true,           # use HTTPS for lookup requests? (if supported)
  # http_proxy: nil,            # HTTP proxy server (user:pass@host:port)
  # https_proxy: nil,           # HTTPS proxy server (user:pass@host:port)
   api_key: ENV['GMAP_API'],               # API key for geocoding service
  # cache: nil,                 # cache object (must respond to #[], #[]=, and #del)
  # cache_prefix: 'geocoder:',  # prefix (string) to use for all cache keys

  # Exceptions that should not be rescued by default
  # (if you want to implement custom error handling);
  # supports SocketError and Timeout::Error
  # always_raise: [],

  # Calculation options
  # units: :mi,                 # :km for kilometers or :mi for miles
  # distances: :linear          # :spherical or :linear
)
```
これでGoogle Map APIを用いてgeocoderの精度をあげることができます。
より詳細な場所の指定ができるようになりました。

※`api_key: ENV['GMAP_API'],`
APIキーは、他人に知られてしまうと悪用される危険性があります。
そこで、`dotenv-rails`というGemを使用して、`geocoder.rb`に直接APIキーを記入しないことでこの危険性を回避できます。

今回は、`GMAP_API`(私が命名しただけ)に、実際のAPIキーを格納しています。
geocoder.rb内に`ENV['GMAP_API']`と記入することで、APIキーを渡すことができます。

詳細は、こちらの記事を参考にしました。記事の途中に出てきます。
https://qiita.com/matsubishi5/items/196fa1941da2152b6d5d#%E5%AE%9F%E8%A3%85

#コントローラーの編集
`studios_controller.rb`を編集

```ruby:studios_controller.rb
  def show
    @studio = Studio.find(params[:id])
    gon.studio = @studio #追記
  end
```
#ビューを編集

`application.html.erb`を編集

```ruby:application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>StudioDig</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= include_gon %> #追記
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.9.0/css/all.css">
    <%= stylesheet_link_tag    'application', media: 'all' %>
    <%= javascript_include_tag 'application' %>
```
`gon`を読み込みます。
CSSとJavaScriptより先に読み込んでいる事に注意して下さい。

`show.html.erb`スタジオ詳細ページを編集

```cpp:studios/show.html.erb
<div id="map"></div>


<script>
  let map;

  function initMap() {
    // geocoderを初期化
    geocoder = new google.maps.Geocoder()

    map = new google.maps.Map(document.getElementById('map'), {
      // コントローラーで定義した変数から緯度経度を呼び出し、マップの中心に表示
      center: {
        lat: gon.studio.latitude,
        lng: gon.studio.longitude
      },
      // マップの倍率はお好みで
      zoom: 17,
    });

    marker = new google.maps.Marker({
      // コントローラーで定義した変数から緯度経度を呼び出し、マーカーを立てる
      position: {
        lat: gon.studio.latitude,
        lng: gon.studio.longitude
      },
      map: map
    });
  }
</script>

<script async defer
      src="https://maps.googleapis.com/maps/api/js?v=3.exp&key=<%= ENV['GMAP_API'] %>&callback=initMap">
</script>
```
`<script async defer....</script>`
これを記述しないと、マップが表示されません。忘れずに記述してください。

`<%= ENV['GMAP_API'] %>`
環境変数化したAPIキーです。
ビュー内で使う場合は、上記のように書きます。

CSS等で、高さの設定をしていないとマップが表示されません。忘れずに設定しておいてください。

```ruby:application.scss
...
#map {
  height: 500px;
  width: 100%;
}
...
```

#ハマったエラー
`CLOUD9`で開発していたために起こったエラーです。

上記のように、間違いなく実装しているのになぜか、`Geocoding API`を利用すると緯度、経度を算出しなくなるエラーにハマりました。

`Geocoding API`を利用しない設定（gemのgeocoderのみで算出する設定）にすると、緯度、経度が算出できます。（かなり大まかな住所しか特定しませんが..）

これは、Google Maps API自体の問題だろうと調べてみたところ、
`HTTPリファラー`の設定が問題だったようです。 

HTTPリファラーとは、自分が設定したURL以外からのアクセスが出来ないようにする設定です。セキュリティ対策ですね。
例えば、`http://localhost:3000/`のアクセスを許可する場合、リファラーに`localhost`と登録する。

ただ、CLOUD９はサーバーを再起動するごとに、IPアドレスが変わってしまうので
下手にリファラーを登録すると、Google Maps APIにアクセスができなくなります。

![スクリーンショット 2021-01-27 22.08.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/771476/e9b9d98c-7a0e-eeb1-0bbb-2753ca038257.png)

こんな感じで、設定していましたが

![スクリーンショット 2021-01-27 21.58.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/771476/97674276-0391-b984-ff0c-4a6a4fd32598.png)

制限なしに変更しました。

すると、登録した住所から緯度、経度を算出できるようになりました。
セキュリティ的には、設定したほうがいいんでしょうけどね
今回は、動いたので良しとしました笑

同じ様なエラーにハマっている方の手助けになればと思います。

#参考記事
[【Rails】Google Mapの表示方法](https://qiita.com/matsubishi5/items/196fa1941da2152b6d5d#%E5%AE%9F%E8%A3%85)
[【Rails】Geocoding APIを用いて高精度で緯度経度を算出し、Google Mapに表示する方法](https://qiita.com/matsubishi5/items/1b784dbbe5f1c336ac70)
[Rails 登録した住所をGoogle Mapで表示させる](https://qiita.com/93pMoB8Pi0aFXMG/items/629a7f78e118e3bac1bc)
[Rails5でGoogleMapを表示してみるまで](https://qiita.com/tiara/items/4a1c98418917a0e74cbb)