はじめに
こんにちは!現在、アウトプットの一環として個人開発を行っているんですが、エラーでボコボコにされてる今日この頃です!
という話は置いといて、本記事ではレビューの投稿機能にGoogleMapを表示させてみた話をしたいと思います!
実装したいこと
- 投稿ページ(new)にて住所(または地名)を入力してもらう。
- 投稿ページのGoogleMapにマーカーを落とす。
- 詳細ページ(show)のGoogleMapを表示。投稿時に指定した場所にマーカーがある。
開発条件
Ruby 2.5.1
Rails 5.2.3
Haml・Sass記法
データベース
reviewテーブル
Column | Type | Options |
---|---|---|
title | string | null: false |
description | text | null: false |
Association
- has_one :spot
spotテーブル
Column | Type | Options |
---|---|---|
address | string | null: false |
latitude | float | null: false |
longitude | float | null: false |
review_id | references | foreign_key: true, null: false |
Association
- belongs_to :review
実装手順
それでは、実装です。実装手順は参考記事に則ってます。
APIの利用
まず、GoogleMapのAPIを取得する必要があります。そのため、下記のリンクにアクセスしてAPIのkeyを取得してください。発行されたkeyは後々使用します。
今回、利用するAPIは次の2つです。
- Maps JavaScript API
- Geocoding API
この2つを有効に設定しておいてください。
gemのインストール
必要なgemをインストールします。
gem "gmaps4rails"
gem "geocoder"
gem "gon"
gem "dotenv-rails"
記入できたらbundle install
をしてください。
それぞれの役割をしては
- GoogleMapを簡単に作成できるgem "gmaps4rails"
- 地名から緯度経度に変換できるgem "geocoder"
- JSでcontrollerの変数を使えるようにするgem "gon"
- GoogleMapAPIのkeyを隠すためのgem "dotenv-rails"
になります。gem無しでも実装はできるみたいですが、結構複雑みたいなのでgemを使用しました。
JSを導入
application.html.hamlを編集
%head
《中略》
= include_gon
= javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
%body
= yield
%script{src: "https://maps.googleapis.com/maps/api/js?key=#{ENV["GOOGLE_MAP_KEY"]}&callback=initMap"}
%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"}
%head
にはgem "gon"
を使えるようにするための記述をします。
また、%body
にはJSを使うための記述をしています。はじめは%script
も%head
に記述していましたが、GoogleMapが表示されないというエラーが起きたので%body
の最後に記述しました。おそらくは読み込みの順番の問題かと思います。
ENV["GOOGLE_MAP_KEY"]
には.env
ファイルに隠したAPIkeyを入れています。
GOOGLE_MAP_KEY = "あなたが取得したkeyを記述してください"
underscore.jsを作成
その後、app/assets/javascripts下にunderscore.jsを作成してください。作成したファイルに次のリンク先のコードをコピペして貼り付けるらしい。
application.jsで読み込み設定
application.jsに次の記述を追記します。
//= require underscore
//= require gmaps/google
modelを編集
modelを次のように編集していきます。
class Review < ApplicationRecord
has_one :spot, dependent: :destroy
accepts_nested_attributes_for :spot
end
class Spot < ApplicationRecord
belongs_to :review
geocoded_by :address
after_validation :geocode
end
viewを編集
投稿ページを作成します。GoogleMapを表示させる記述以外は省略してます。
= form_with(model: @review, local: true, multipart: true) do |f|
.spot
= f.fields_for :spot do |s|
= s.label :address, "レビュー場所(Google Mapで検索)", class: 'spot__title'
= s.text_field :address, placeholder: "スポットを入力", id: "address", class: 'spot__text'
%input{onclick: "codeAddress()", type: "button", value: "検索する"}
.map{id: "map", style: "height: 320px; width: 640px;"}
次に、投稿したレビューを表示させるページを作成します。
.show
.show__address
= @review.spot.address
.show__maps{id: "show_map", style: "height: 320px; width: 400px;"}
controllerを編集
controllerも編集しておきます。
def new
@review = Review.new
@review.spot.build
end
def create
@review = Review.new(review_params)
if @review.save
redirect_to root_path
else
redirect_to new_review_path
end
end
def show
@review = Review.find(params[:id])
@lat = @review.spot.latitude
@lng = @review.spot.longitude
gon.lat = @lat
gon.lng = @lng
end
private
def review_params
params.require(:review).permit(
:title,
:description,
spot_attributes: [:address]
)
end
showアクションで記述している
@lat = @review.spot.latitude
@lng = @review.spot.longitude
gon.lat = @lat
gon.lng = @lng
では、controllerで定義した@lat
と@lng
の変数をJavaScriptでも扱えるように、それぞれgon.lat
とgon.lng
に代入しています。
JavaScriptの編集
いよいよJavaScriptでGoogleMapを表示させていきます。
let map //変数の定義
let geocoder //変数の定義
function initMap(){ //コールバック関数
geocoder = new google.maps.Geocoder() //GoogleMapsAPIジオコーディングサービスにアクセス
if(document.getElementById('map')){ //'map'というidを取得できたら実行
map = new google.maps.Map(document.getElementById('map'), { //'map'というidを取得してマップを表示
center: {lat: 35.6594666, lng: 139.7005536}, //最初に表示する場所(今回は「渋谷スクランブル交差点」が初期値)
zoom: 15, //拡大率(1〜21まで設定可能)
});
}else{ //'map'というidが無かった場合
map = new google.maps.Map(document.getElementById('show_map'), { //'show_map'というidを取得してマップを表示
center: {lat: gon.lat, lng: gon.lng}, //controllerで定義した変数を緯度・経度の値とする(値はDBに入っている)
zoom: 15, //拡大率(1〜21まで設定可能)
});
marker = new google.maps.Marker({ //GoogleMapにマーカーを落とす
position: {lat: gon.lat, lng: gon.lng}, //マーカーを落とす位置を決める(値はDBに入っている)
map: map //マーカーを落とすマップを指定
});
}
}
function codeAddress(){ //コールバック関数
let inputAddress = document.getElementById('address').value; //'address'というidの値(value)を取得
geocoder.geocode( { 'address': inputAddress}, function(results, status) { //ジオコードしたい住所を引数として渡す
if (status == 'OK') {
let lat = results[0].geometry.location.lat(); //ジオコードした結果の緯度
let lng = results[0].geometry.location.lng(); //ジオコードした結果の経度
let mark = {
lat: lat, //緯度
lng: lng //経度
};
map.setCenter(results[0].geometry.location); //最も近い、判読可能な住所を取得したい場所の緯度・経度
let marker = new google.maps.Marker({
map: map, //マーカーを落とすマップを指定
position: results[0].geometry.location //マーカーを落とす位置を決める
});
} else {
alert('該当する結果がありませんでした');
}
});
}
今回は、地図を表示させる場所が2か所あり、それぞれ別の場所をマップの中心にしたかったので、initMap関数内で「指定のid
の有無」による条件分岐を設けることで対応しています。
JavaScriptについては知識が浅く、コメントアウトで解説した部分で解釈の違いがあるかもしれません。その際にはご指摘頂けると有り難いです。
おわりに
GoogleMapは多くのサイトで利用されているので、どのように実装すればいいかを多少なり知ることができて良かったです!GoogleMapAPIにはまだまだ使ってない機能が沢山あるので、時間があれば色々チャレンジしていきたいと思います!!
参考記事
[gonを使ったRailsとJavascriptの連携について]
(https://qiita.com/s_nakamura/items/5d153f7d9db1b1190296)
[ジオコーディング備忘録]
(https://qiita.com/SignHack/items/d6df5a5abf72e6feffe9)
RailsでGoogleMapを表示させる(gem 'gmaps4rails'の使い方)
[Rails Google Mapを表示させる方法]
(https://matto.work/rails-google-map%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%95%E3%81%9B%E3%82%8B%E6%96%B9%E6%B3%95-230.html)