Google Mapをウェブサイトに埋め込む方法

Twitterのように、投稿する情報に自分がいる位置を付け加えて、投稿詳細画面を開くと、埋め込んだGoogle Mapがでてくるようにしました。

#1. データベース設計

今回は投稿する情報に位置情報を乗せてあげたかったので、postsテーブルにlatitude(緯度), longitude(経度)を設計しました。integerで設定してしまうと、小数点以下が切り捨てられてしまうため、データ型はfloat(浮動小数点型)にしています。
また、位置情報を乗せたくない人もいるでしょうから、validationは切っておきました。

| column_name      |       type |    option    |
|:----------------:|:-----------------:|:------------------:|
| text            |              string|       -       |
| user_id           |            integer |      -      |
| latitude          |              float |       -        |
| longitude          |              float |       -       |
| created_at        |              datetime |       null: false      |
| updated_at          |              datetime |       null: false    |

#2. 今回使ったGem
どうしても、データベースからデータを引っ張って、Javaに渡す方法がわからなかったので、以下のGemを使用しました。
他にいい手段があればご指摘ください。

```ruby:Gemfile
gem 'gon'
#Railsのコントローラー側で生成したインスタンス変数を、Javaのファイルの中で使用することができる。
```

#3. controllerの設定
コントローラー側では、showアクションでgooglemapを表示したいので、gon.postに@postインスタンスを代入する。

```ruby:posts_controller.rb
class PostsController < ApplicationController

  def show
    @post = Post.find(params[:id])
    gon.post = @post #gonを頭に持ってくることで、postインスタンスをJavaScriptsのファイル上でgon.postとして使用可能になる
  end

  def update
    @post = Post.find(params[:id])
    @post.update(post_params)
    redirect_to root_path
  end

  private
  
  def post_params
    params.require(:post).permit(:text, :user_id, :latitude, :longitude).merge(user_id: current_user.id)
  end

end
```

# 4. htmlとcssファイル設定
application.html.erbファイルのheadタグの中に以下を記述。

```html
:application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>MapApp</title>
    #省略
    <%= include_gon %>
    <script async defer src="https://maps.googleapis.com/maps/api/js?key=ここにGoogleAPIの認証キーを記述&callback=initMap" type="text/javascript"></script>
    <%= javascript_include_tag "map.js" %>
  </head>
  <body>
  #省略
  </body>
</html>
```

```
<%= include_gon %>
gemで設定した、gonを使えるようにする。

<script async defer src="https://maps.googleapis　以下略>
Google Mapを埋め込み利用するために、GoogleAPIの鍵を取得して、key=以下にコピペしてください。
注意：GitHubにアップロードするときは、公開用の鍵を設定しないと悪用される可能性があります。

<%= javascript_include_tag "map.js" %>
map.jsファイルに今回のJavaScriptsの記述をまとめましたので、ここでローディング。
```

入力フォームに現在位置を自動入力して、消えるボタンを設置します。

```html:post.hemlt.erb
<button id="button" onclick="getPosition()">位置情報を有効にする</button><br>
#"位置情報を有効にする"ボタンをクリックすると、map.jsのgetPosition()メソッドが動き、現在位置の緯度と経度をしたのlatitude, longitudeフォームに自動入力してくれるようにしました。
<p id="getPosition"></p>
<%= form_for(@post, url: "/posts/#{@post.id if @post}") do |f| %>
  <%= f.label "Text" %>
  <%= f.text_field :text, placeholder: "input text" %><br>
  
  <%= f.label "latitude" %>
  <%= f.text_field :latitude, placeholder: "input latitude", id: "latitude" %><br>
  
  <%= f.label "longitude" %>
  <%= f.text_field :longitude, placeholder: "input longitude", id: "longitude" %><br>
  #緯度と経度の入力フォームは、上記のボタンを押せば自動で記入してくれますが、ここではほかの場所のmapも取得したかったので、あえてフォームを表示しました。また、ボタンを一度押したら、消えるようにしています。
  <% if @post.text %>
    <%= f.submit "Update", method: :patch %>    
  <% else %>
    <%= f.submit "Submit", method: :post %>
  <% end %>
<% end %>
```

投稿詳細では地図が実際に表示されるように、ボタンを設置。

```html:show.html.erb
<%= @post.user.name %><br>
<%= @post.text %><br>
<%= @post.latitude %><br>
<%= @post.longitude %><br>
<%= @post.created_at %><br>
<% if @post.latitude && @post.longitude  %>
  <button id="button" onclick="showGoogleMap()">投稿した位置を表示する</button><br>
  #"投稿した位置を表示するボタンをクリックすると、map.jsのshowGoogleMap()メソッドが動き、下の<div id="sample">の中にgoogle mapが表示されるようになります。また、このボタンが消えるようにしました。
    <% if user_signed_in? && @post.user_id == current_user.id %>
      <div>
        <%= link_to "EDIT", "/posts/#{@post.id}/edit" %>
        <%= link_to "DELETE", "/posts/#{@post.id}", method: :delete %> 
      </div>
    <% end %>
  <div id="sample"></div>
  #ここに地図を表示
<% end %>
```

google mapを埋め込むにあたりcssでサイズを設定しなければ、表示されないので、divの箱のサイズを設定しておきます。

```sass:posts.scss
#sample {
  width: 700px;
    height: 400px;
 }
```


#5.　map.jsの記述
最初に現在位置を取得する、プログラムを記述

```map.js
function getPosition() {
  var x = document.getElementById("getPosition"); #ブラウザが対応しているか結果表示用
  var b = document.getElementById("button"); #ボタンを消す用
  var lat = document.getElementById("latitude"); #緯度をlatitude_formに記入する
  var lon = document.getElementById("longitude");　#緯度をlongitude_formに記入する
  #htmlの中からそれぞれのidを持つ要素を取得

  function getLocation() {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(showPosition);
      #geolocationAPIがサポートする現在位置を取得するメソッド、引数にコールバック関数として、後述のshowPosition関数を記述して、そこにpositionオブジェクトを返す
  } else {
      x.innerHTML = "Geolocation is not supported by this browser.";
      #ブラウザが対応してなければサポートしていないと返す
    }
  }
  getLocation() #実行
  function showPosition(position) {
    lat.value = position.coords.latitude; #positionオブジェクトから緯度を取得
    lon.value = position.coords.longitude; #positionオブジェクトから経度を取得
  }
  b.style.display = "none"; #ボタンを消す
}
#getLocation()の返り値にpositionが返され、それをそのままshowPositionに使用すると、現在位置情報が取得できる。
```

これで投稿時に緯度と経度が入力できました。

```javascript:map.js
function showGoogleMap(){
  var map; #google map表示用
  var marker; #google mapに現在位置のピンを留める
  var b = document.getElementById("button"); #ボタンを消すため
  var center = {
    lat: gon.post.latitude,
    lng: gon.post.longitude
  };
  #google mapのセンターを現在位置にする。showコントローラーを呼び出すため、ここでgon.postを使って、投稿した時の現在位置を呼び出す。

  function initgoogleMap(){
    map = new google.maps.Map(document.getElementById('sample'),{
      center: center,
      zoom: 17
    });
    marker = new google.maps.Marker({
      position: center,
      map: map
    });
  }
  initgoogleMap()
  b.style.display = "none";
}
```

以上で、最低限Google Mapを表示することができるようになりました。Geolocationをもっと勉強すれば、より理解度が深まりそうな気がします。