RailsでGoogleMapを表示させる(gem 'gmaps4rails'の使い方)

##0. はじめに

この記事は、RailsでGoogle Mapを表示する方法の説明です。gem 'gmaps4rails'の使い方を書いてきます。
https://github.com/apneadiving/Google-Maps-for-Rails 

## 1. gmaps4railsの導入

### 1. 準備

新しいプロジェクトを作ります。

```bash
$ rails new gmap
$ cd gmap
```
今回は、地名から緯度経度に変換できるgem 'geocoder'は使わずに進めてきます。

```bash
$ rails g scaffold place name:string description:string latitude:float longitude:float
$ rake db:migrate
```
Google Mapに表示するPlaceデータがまだない場合はrails consoleで、作っておいて試してみてもいいかと思います。

```
$ rails c
Loading development environment (Rails 4.2.5)
>>Place.create([
  { name: '東京駅', description: '東京駅東京駅東京駅', latitude: '35.681298', longitude: '139.7640529' },
  { name: 'スカイツリー', description: '634m', latitude: '35.7100627', longitude: '139.8085117' },
])         
```

### 2.Gemfileの編集とインストール
Gemfileにgmaps4railsを追加する。


```rb:Gemfile
source 'https://rubygems.org'

(省略)...
gem 'gmaps4rails'
```

gemをインストール。

```
bundle install
```

### 3.Google Maps JavaScript API のロードする

・Google Maps APIのキーを取得する
以下にキーの取得の仕方は書いてあります。
https://developers.google.com/maps/documentation/javascript/get-api-key

```application.html.slim
script src="//maps.google.com/maps/api/js?v=3.23&key=[your API key]"
```
application.html.slimに追加。
[your API key]部分に取得したAPIキーを書いてください。

```ruby:application.html.slim
doctype html
html
  head
    title
      | Gmap
    = stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true
    = javascript_include_tag 'application', 'data-turbolinks-track' => true
    = csrf_meta_tags
    script src="//maps.google.com/maps/api/js?v=3.23&key=[your API key]"
    script src="//cdn.rawgit.com/mahnunchik/markerclustererplus/master/dist/markerclusterer.min.js"
    script src='//cdn.rawgit.com/printercu/google-maps-utility-library-v3-read-only/master/infobox/src/infobox_packed.js' type='text/javascript'
  body
    = yield
```

・underscore、gmaps/googleをapplicaton.jsに追加します。

```app/assets/javascripts/application.js
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require underscore
//= require gmaps/google
//= require_tree .
```
・underscore.jsも読み込みます。（Gmaps4railsはunderscore.jsを使っているため。）underscore.jsを[こちら](http://underscorejs.org/underscore-min.js)からコピーしてきます。underscore.jsとunderscore-min.jsがありますが、underscore-min.jsで大丈夫です。

```app/assets/javascripts/underscore.js
// underscore-min.jsの中身を貼る
(省略)...
```

####【補足】Google MapのAPIキーが必須になりました。
2016年6月22日から新規のサイトでGoogle MapのAPIを使用する際には、keyが必須なったと[公式サイト](https://developers.google.com/maps/pricing-and-plans/standard-plan-2016-update)で発表されています。これまではキーがなくても地図の表示は行えましたが、[こちら](https://developers.google.com/maps/documentation/javascript/get-api-key)に書いてある方法で、APIキーを取得してください。
ですので、gmaps4railsの使い方に関する6月から更新がないQiitaの記事で、以下のscriptを読み込むと書いていると思いますが、今はそれではマップは表示されません。

```
<script src="//maps.google.com/maps/api/js?v=3.23&sensor=false&client=&key=&libraries=geometry&language=&hl=&region=">
```

上記のscriotsを使うと、consoleログに以下の警告が出ます。

```
Google Maps API warning: NoApiKeys https://developers.google.com/maps/documentation/javascript/error-messages#no-api-keys
```

[Google-Maps-for-Railsの公式GitHubページ](https://github.com/apneadiving/Google-Maps-for-Rails/commit/e4d665e56e815c87d7c8c000a80181c3b27e8eb1)でも2016年6月に修正されています。

###4.View
Google Mapを表示する箇所に以下のコード挿入します。

```app/views/places/index.html.slim
#map style="width: 800px; height: 400px;"

javascript:
  handler = Gmaps.build('Google');
  handler.buildMap({ provider: {}, internal: {id: 'map'}}, function(){
    markers = handler.addMarkers(#{raw @hash.to_json});
    handler.bounds.extendWith(markers);
    handler.fitMapToBounds();
  });
```

###5.Controller
```ruby:app/controllers/places_controller.rb
def index
  @places = Place.all
  @hash = Gmaps4rails.build_markers(@places) do |place, marker|
    marker.lat place.latitude
    marker.lng place.longitude
    marker.infowindow place.name
  end
end
```
###6.動作確認

[http://localhost:3000/places](http://localhost:3000/places)
起動して、アクセスすると、表示されます！

![52a27c6f3edcf6044d6b68624bbda6cd.png](https://qiita-image-store.s3.amazonaws.com/0/89910/d064fe19-5cda-c50c-2a27-478f06564ff4.png)

## 2.追加
### 1.infowindowに関して

> [InfoWindow](https://developers.google.com/maps/documentation/javascript/infowindows) は、マップ上の指定の場所にポップアップ ウィンドウでコンテンツ（通常はテキストまたは画像）を表示します。情報ウィンドウには、コンテンツ領域と先細の突起部分があります。突起部分の先端が、マップ上の指定された場所に付着しています。

1.導入でやったように```marker.infowindow place.name```を追加しておくと、markerをクリックした時に、infowindowが開くのですが、いい感じに変更したい時に以下の方法で出来ます。また、`marker.lat,marker.lng`があるので、infowindowの中に個々のplaceのGoggle Mapへのリンクを追加しておくといいと思います。

```ruby:app/controllers/places_controller.rb
def index
  @places = Place.all
  @hash = Gmaps4rails.build_markers(@places) do |place, marker|
    marker.lat place.latitude
    marker.lng place.longitude
    marker.infowindow render_to_string(partial: "places/infowindow", locals: { place: place })
  end
end
```

```app/views/places/_infowindow.html.slim
h2 = link_to place.name, place_path(place)
= place.description
= link_to "マップを開く" ,"https://maps.google.co.jp/maps?q=loc:#{place.latitude},#{place.longitude}&iwloc=J",target: "_blank"
```
![ed21b080f39cf6b2dd19bd649c6fb6a7.png](https://qiita-image-store.s3.amazonaws.com/0/89910/3cfc71dd-c73d-9956-8579-fe4562a50e4b.png)




### 2.Mapのzoomとcenterに関して

`handler.fitMapToBounds();`がすべてのスポットが、枠に収まるようにzoomを自動的に調整してくれています。
逆に、すべてのスポットを表示することによって、以下の問題が起きます。
・Google Mapが広範囲を表示しすぎでそれぞれの場所分かりにくい。
・スポットが一つの時に、拡大しすぎてしまう。

![47a66d4dfcb9b5f5cef74c53cbf2e761.png](https://qiita-image-store.s3.amazonaws.com/0/89910/73ecdf87-2ad0-c767-8ccc-4e6e1547e95f.png)
![da2a67eaa35f885c5aa5285b99ae4b5d.png](https://qiita-image-store.s3.amazonaws.com/0/89910/f9be8d06-c823-9888-dd59-2da6c5bab605.png)

解決法として、`handler.getMap().setZoom(yourValue);`を追加すると、zoomを指定することができます。
ただ上の図では、```handler.getMap().setZoom(12);```だけを追加すると、海が表示される(centerがそこだから)だけになってしますので、`handler.getMap().setCenter(latlng);`も追加し、どの位置を中心として、`handler.getMap().setZoom(12);`にするかを決めるようにします。

```app/views/places/index.html.slim
#map style="width: 800px; height: 400px;"

javascript:
  handler = Gmaps.build('Google');
  handler.buildMap({ provider: {}, internal: {id: 'map'}}, function(){
    markers = handler.addMarkers(#{raw @hash.to_json});
    handler.bounds.extendWith(markers);
    handler.fitMapToBounds();
    handler.getMap().setCenter(new google.maps.LatLng(35.681298, 139.7640582));
    handler.getMap().setZoom(12);
  });
```
・他にも,maxZoomやminZoomは指定して、対応することもできます。


（ちなみに、こういうAPIのオプションは、providerの部分に書くのが普通ですが、`handler.fitMapToBounds();`が自動的に枠に収まるようにするので、`handler.fitMapToBounds();`を使う場合、providerにzoomとcenterは書かない方がいいと思います。僕はこれで、ずっと詰まってました。。)

```js:良くない例
handler.buildMap({
  internal: {id: 'map'},
  provider: {
    zoom: 12,
    center: new google.maps.LatLng(35.681298, 140.7640529),
  }
},
```
https://developers.google.com/maps/documentation/javascript/reference
これが、そこらへんに関するリファレンスです。

##最後に
僕も、公式ページに書いてある通りにしているのに、マップが表示されないと困っていたので、うまく表示されなかったらコメント等してもらえたらと思います。