#実現したい事
Railsで登録した住所を元にgoogle map上に位置を表示するまでの実装方法をまとめました。
以下のデモのように入力フォームに登録した住所を用いて詳細ページでGoogle Map上にその位置を表示させます。
#実装までの流れ
実装の流れとしては以下の通りです。
1.Google Mapの機能を使うためにAPIキーを取得する
2.住所(address)、緯度(latitude)、経度(longitude)を登録するためのカラムを用意する
3.住所を登録するためのフォームを作成
4.gemのgeocoderを用意する→これによって住所の情報を元に緯度、経度を割り出してくれます
5.viewにGoogle Mapを表示させるための記述をする
--ここまでで住所を元にGoogle Mapの表示はひとまず出来ます。
以下の6,7は追加で自分がつまずいた事、気をつける事をまとめました。
6.住所を登録できてるのにMapにきちんと表示されない時
7.Github等で管理する場合、取得したAPIキーをpushしないために
それでは、流れに沿って進めていきます!
#1.Google Mapの機能を使うためにAPIキーを取得する
こちらに関してはこの記事内では省略します。以下の方の記事がとても分かりやすかったです。
手順通りに進めれば問題なくできると思います。
https://qiita.com/tiara/items/4a1c98418917a0e74cbb
#2.住所、緯度、経度を登録するためのカラムを用意
住所(address)、緯度(latitude)、経度(longitude)のカラムを追加します。
db/migrate/...
マイグレーションファイルへの記述でカラムを追加します。
class AddMapInfoToShops < ActiveRecord::Migration[5.2]
def change
add_column :shops, :address, :string //すでにaddressカラムが登録されている場合は必要ありません
add_column :shops, :latitude, :float
add_column :shops, :longitude, :float
end
end
緯度、経度はfloat型にします。floatとは浮動小数点数型です。
難しい名前ですがざっくり言えば小数を扱える型です。
記述したらrails db:migrate
してください。
#3.住所を登録するためのフォームを作成
Mapに表示させたい住所(:address
)を登録できるようにします。
カラムの:address
に住所が登録できればどんな形での実装でも大丈夫です。
以下は一例です。
<%= form_for(@shop) do |f| %>
...
<h4>住所</h4>
<%= f.text_field :address, placeholder: "住所" %>
...
<%= f.submit "登録する" %>
<% end %>
#4.gemのgeocoderを用意する
Map表示する際に、Google Mapでは緯度、経度から位置を取得します。
しかし、今の段階ではaddress
に住所は入っているものの緯度、経度が入力されていません。
住所は簡単に調べられても緯度、経度までユーザー側で登録するのは面倒です。そこでgemのgeocoder
を使用します。
geocoder
を使用すると住所の情報を元に緯度、経度を割り出してくれます。
gem "geocoder"
記述したらbundle install
してください。
次に、geocoderを使うために適用するモデルに以下の記述をします。
今回は店舗の位置情報を表示したいので/app/models/shop.rb
に記述しました。
class Shop < ApplicationRecord
geocoded_by :address
after_validation :geocode, if: :address_changed?
end
これで:address
を登録した際にgeocoder
が緯度、経度のカラムにも自動的に値を入れてくれるようになります。
この記述がないとaddress
を登録してもlatitude
(緯度),longitude
(経度)が登録されなくなってしまう(nillになる)ので注意です。
#5.viewにGoogle Mapを表示させる記述をする
では、実際にMapを表示させたいviewファイルにMapを表示させるための記述をしていきます。
(JS,CSSどちらもhtmlファイル内でまとめて記述していますが、本来はそれぞれJavaScriptはjsファイル、styleの記述はcssファイルに記述した方がコードとしては望ましいです。)
...
#shopとなっている部分の記述は自身の実装に合わせて変えてください。
<script type="text/javascript">
function initMap() {
#latitude,longitudeから位置を特定
var test ={lat: <%= shop.latitude %>, lng: <%= shop.longitude %>};
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 15,
center: test
});
var transitLayer = new google.maps.TransitLayer();
transitLayer.setMap(map);
var contentString = '住所:<%= shop.address %>';
var infowindow = new google.maps.InfoWindow({
content: contentString
});
#Map上の指定した位置にピンを挿して表示する
var marker = new google.maps.Marker({
position:test,
map: map,
title: contentString
});
marker.addListener('click', function() {
infowindow.open(map, marker);
});
}
</script>
#以下の記述の中にあるYOUR_API_KEYには取得したご自身のAPIキーを記述してください
<script async defer
src="https://maps.googleapis.com/maps/api/js?v=3.exp&key=YOUR_API_KEY&callback=initMap">
</script>
#表示するmapのcssです。ご自身でカスタマイズしてください。高さが設定されていないと表示されないことがあります。
<style type="text/css">
#map { height: 200px;
width: 70%;}
</style>
#mapの表示
<div id="map"></div>
...
実際の表示画面↓ (上記コードの記述は以下の画面のMap表示部分のみのコードです)
これで登録した住所を元にGoogle Mapへの表示ができるようになりました。
ただし、上の画面で指定した住所は東京都渋谷区とざっくりしています。これが東京都渋谷区◯-◯◯-◯◯◯というように詳細な地点の表示となると上手くいかない場合があります。
自分はこれで詰まりました。どうやらデフォルトのgeocoder
のままではより詳細な住所から経度、緯度を持ってくることが出来ないようです。
以下では、この問題を含めた表示がうまくいかない場合に確認すべきことをまとめました。
#6.住所を登録できてるのにMapが表示されない時
まず手順5の最後に触れた自分も詰まった東京都渋谷区◯-◯◯-◯◯◯のような細かい位置の指定での検索方法です。
なぜこれで指定できないのかというと、geocoder
の検索精度が原因です。
中にはデフォルト状態のgeocoder
でも細かく住所を指定して検索できる地域もあるのですが、場所によっては検索できなくなります。
ここでいうgeocoder
の検索精度とは、住所から緯度、経度の二つを検索する精度という意味です。つまり住所によってデフォルトのgeocoder
では緯度、経度が検索しきれない場所が出てくるということです。
ではどうすればいいか、それはより地図情報が豊富な情報を元に緯度、経度を検索できるようにします。
地図情報を豊富に持ってるものとは何か、それはGoogleです!
そのためgeocoder
でもGoogle Map APIの情報源を使えるように設定すれば解決します。
config
フォルダ内にgeocoder.rb
ファイルを作成します。
$ bin/rails g geocoder:config
ターミナルで上のコマンドを行うことによってconfig/initializers/geocoder.rb
ファイルができます。
作成されたファイルの中身を変更してgeocoder
でgoogle mapのAPIを使って緯度、経度を検索できるようにします。
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)
#YOUR_API_KEYにはご自身のAPIキーを記述してください。
api_key: YOUR_API_KEY, # 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
の精度をあげることができます。より詳細な場所の指定ができるようになりました。
それでも上手くいかない場合は
・必要なカラムがきちんと追加できているか?
→スペルミス、緯度、経度がfloat型になっていない、rails db:migrate
していない等を確認。
・geocoder
はきちんと機能しているか?
→手順の4番をきちんと実行できているか、bundle install
しているか?
・そもそもaddress
に値が入力、取得できているか?
→コントローラーで:address
カラムに値を入力、取得はきちんとできているか?、スペルミス等がないか?
#7.Github等でのAPIキーの管理
地図表示の実装が上手くいった。よしとりあえずここまでGithubにpushしよう!と考える時に、気をつけなければならないのが取得したAPIキーの管理です。
上手く地図表示できたそのコードをそのままpushしてしまうとコード内に記述した自分のAPIキーまでpushしてしまい、悪用されてしまうかもしれません。その為、キーを管理するために一工夫が必要となります。
その方法はgemのdotenv-rails
を用いてGithubにpushするコマンドを打ってもpushされないファイルを作り、その中でAPIキーを管理するようにします。
まずは、gem dotenv-rails
を導入します。
gem "dotenv-rails"
記述したら忘れずbundle install
してください。
次にpushされないファイルを作成します。
実装したいアプリケーションの直下に.env
ファイルを作成します。app
等と同じステージに作成してください。
作成したら.env
ファイルに隠したいAPIキーの中身を記述します。
#YOUR_API_KEYには自身の取得したAPIキーを入力してください
GOOGLE_MAP_API_KEY=YOUR_API_KEY
次にこの作成した.env
ファイルをGithubにpushした時、pushされないファイルとして扱うようにしていきます。
そのためにgitignore
というファイルの中に記述をします。
gitignore
ファイルの最後の行に以下の記述をしてください。
これでpushした時に.env
ファイルは文字通り無視(ignore)されてpushされるようになります。
# Ignore master key for decrypting credentials and more.
/.env
これでpushしてもAPIキーはpushせずに済むようになりました。
最後に今までAPIキーを直接書き込んでいた全てのファイルでAPIキーの記述を.env
ファイル内で定義したGOOGLE_MAP_API_KEY
に書き換えて完了です。
例:この記事のshow.html.erb
、geocoder.rb
ファイルそれぞれ書き換えた場合↓
#以下のコードの YOUR_API_KEY → <%= ENV['GOOGLE_MAP_API_KEY'] %> に変更されています。
<script async defer
src="https://maps.googleapis.com/maps/api/js?v=3.exp&key=<%= ENV['GOOGLE_MAP_API_KEY'] %>&callback=initMap">
</script>
Geocoder.configure(
...
api_key: ENV['GOOGLE_MAP_API_KEY'],
...
)
###余談:APIキーを記述したファイルをうっかりGithubにpushしてしまったら …
実は自分はこの.env
ファイルでキーを管理する前にAPIキーを記述したファイルをすっかり忘れてGithubへpushしてしまいました。
その後、すぐにgoogleからメールが届きました。さらにGithubからもメールが。内容はどちらも同じで「あなたのgoogle map APIキーがGithub等で公開されてしまっているよ」というものでした。
どうやらAPIキーのような情報を察知するプログラムが企業、個人レベルであちこち働いているようです。
こうなった場合、焦らず対応しましょう。以下の2つを行えば大丈夫です。
1.Googleから届いたメールが対応策をいくつか示してくれるので、それに従います。
自分はこの対応策にしたがって公開してしまったAPIキーを破棄し、新しいAPIキーを発行するという方法を取りました。
2.上の1番の対応だけでも解決しますが念のためGithub上のAPIキーを上げてしまったコミットを取り消します。
Githubへpushしたコミットの取り消し方は検索すれば様々出てくるのでここでは割愛します。
#参考にさせていただいた記事
Google Map API 登録、キーの取得
https://qiita.com/tiara/items/4a1c98418917a0e74cbb
mapを表示するためのviewの記述
https://qiita.com/enzen/items/9a919a75ebf0a34e7b91
geocoderの導入、準備
https://remonote.jp/rails-googlemap
geocoder 緯度、経度の位置を詳細に検索するために
https://qiita.com/roark/items/2fedc1ebac763e72d70b
APIキーの管理、Githubにキーをあげずに管理する
https://qiita.com/ryosuketter/items/ceb592dc6b23a20e51b5
今回、上記の記事を参考に実装しました。
本当に助かりました。ありがとうございました。
まだまだ理解の足りない部分もあるので何かありましたらご指摘いただけますと幸いです。
ここまでお読みいただきありがとうございます。少しでも参考になれたら嬉しいです。