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を使用しました。
他にいい手段があればご指摘ください。
gem 'gon'
# Railsのコントローラー側で生成したインスタンス変数を、Javaのファイルの中で使用することができる。
3. controllerの設定
コントローラー側では、showアクションでgooglemapを表示したいので、gon.postに@postインスタンスを代入する。
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タグの中に以下を記述。
: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の記述をまとめましたので、ここでローディング。
入力フォームに現在位置を自動入力して、消えるボタンを設置します。
<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 %>
投稿詳細では地図が実際に表示されるように、ボタンを設置。
<%= @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の箱のサイズを設定しておきます。
# sample {
width: 700px;
height: 400px;
}
5. 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に使用すると、現在位置情報が取得できる。
これで投稿時に緯度と経度が入力できました。
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をもっと勉強すれば、より理解度が深まりそうな気がします。