railsでGoogle Mapの表示の仕方
APIという便利な機能を実装してみたいと思い、ちょっと調べてみたらGoogle MapもAPIだと言うことを知ったので、実装してみたいと思います。
私の環境
rails : 6.1.4
ruby : 2.7.4
docker
既に、イベントテーブルがあり、ユーザーがイベントを作成し投稿できるという機能が実装済みです。今回はここに、イベントの開催場所をGoogle Map で表示したいと思います!
流れ
Googleのアカウントを作成 ※なければ
APIキーを取得
DBにカラムの追加
Gemの追加
モデルの編集
コントローラーの編集
ビューの編集
Googleアカウントの作成
これは、ほとんどの方は終わっているので、大丈夫かと思います。
APIキーを取得
下記のサイトにアクセスして作成します。
※クレジットカードの情報を入力する必要があるので、準備しておきましょう!
※詳しい登録の仕方などは他の方々が画像などを用いて、説明して下さっているので、そちらを見ながらやるとできました!
お世話になった方々のページ↓
この2つを見ながらやりました!
私の場合は画面が少し違っていて少し混乱しましたが...笑
DBにカラムの追加
すでに存在するテーブルに追加していますが、なければ作る時にtitleなどと一緒に記述して方がいいですね。
$ rails g migration AddMapInfoToEvent
私の場合はeventテーブルに住所の情報を持たせたかったので、eventテーブルですが、なんでも大丈夫です。
マイグレーションファイル
class AddMapInfoToEvent < ActiveRecord::Migration[6.1]
def change
add_column :events, :address, :string
add_column :events, :latitude, :float
add_column :events, :longitude, :float
end
end
マイグレーションファイルには、以上のように書きます。
それぞれのカラムの役割は、
address => 文字列で住所を保存(東京都渋谷区〇〇など)
latitude => 小数点を含む数字で緯度を保存(35.658...など)
longitude => 小数点を含む数字で緯度を保存(139.698...など)
です。
ここで、忘れずmigrateしておきましょう!
$ rails db:migrate
Gemの追加
ここで、Gemも追加しちゃいます。
今回使うのは2つ
Gemfile
gem 'dotenv-rails' 👈 環境変数を.envファイルで使う為のGem
gem 'geocoder' 👈 住所から緯度と経度を変換するGem
を足します。
モデルの編集
event.rb
geocoded_by :address
after_validation :geocode, if: :address_changed?
これらを追記します。
geocoded_by :address の記述で、addressの文字列から自動で緯度と経度のカラムに値を代入してくれるようになります。
after_validation :geocode, if: :address_changed? の記述で、addressカラムが値が代入される時(createとupdate)に、緯度と経度を住所から変換してくれるようになります。
コントローラーの編集
ストロングパラメーターを編集します。
events_controller.rb
def event_params
params.require(:event).permit(:title, :content, :address, :latitude, :longitude)
end
:address, :latitude, :longitudeを追記します。
Gemのgeocoderのおかげで、ビューのフォームにはaddressのフォームのみの記述だけでよくなります!
もし、latitudeとlongitudeが抜けていると、dbには登録してくれません。
ビューの編集
フォームを追加する
<%= form_with(model: @event) do |f| %>
<div class="field my-3">
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div class="field my-3">
<%= f.label :content %>
<%= f.text_area :content %>
</div>
<div class="field my-3">
<%= f.label :address %>
<%= f.text_field :address %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
元々あったフォームにaddressのフォームを追加しました。
Google Mapを表示するビューを追加する
ここは長いです。
あと、動くことを優先にしている為、html.erbにstyleやscriptタグが出てきます。
show.html.erb
<script type="text/javascript">
function initMap() {
var test = {lat: <%= @event.latitude %>, lng: <%= @event.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 = '住所:<%= @event.address %>';
var infowindow = new google.maps.InfoWindow({
content: contentString
});
var marker = new google.maps.Marker({
position:test,
map: map,
title: contentString
});
marker.addListener('click', function() {
infowindow.open(map, marker);
});
}
</script>
<script async defer
src="https://maps.googleapis.com/maps/api/js?v=3.exp&key=<%= ENV['GOOGLE_MAP_API_KEY'] %>&callback=initMap">
</script>
<style type="text/css">
#map { height: 200px;
width: 70%;}
</style>
<div id="map"></div>
長いですね...笑
※ @eventなどはコントローラーで持ってくる変数によって表記を変わります。
最後の方にある、ENV['GOOGLE_MAP_API_KEY']は環境変数です。
これはGemのdotenv-railsでAPIキーを管理しています。
APIキーを直接ファイルに記述してしまうと、githubにあげたときに悪用されたりしてしまうので、環境変数を用いています。
なので、<%= ENV['GOOGLE_MAP_API_KEY'] %>のところは、APIキーを直接記入しても動きます。
.envファイル
GOOGLE_MAP_API_KEY = ここにgoogleのAPIキーを貼り付ける
dotenv-railsを使う場合は、上記のように.envファイルにAPIキーを貼り付けましょう。(ここで、dotenv-railsを初めて入れた人は、gitignoreファイルに、.envファイルをgit管理下から外すように記述しましょう。)
住所を登録してみるときに気をつけること
ここまで来れば住所の登録とMAPの表示はある程度できます。
ですが、実際にやるとざっくりした住所しか登録ができません。
例えば、東京都渋谷区でaddressを登録すれば、addressカラムの情報からgeocoderが緯度と経度を自動で入力してくれますが、渋谷区以降の細かい住所を記述した場合は、geocoderは変換をしてくれません。
なので、これを解決するためにgeocoderの設定を変更します。
$ rails g geocoder:config
そして、作られたファイルの中から3つを変更します
geocoder.rb
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)
api_key: ENV['GOOGLE_MAP_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
)
3つのコメントアウトを外して、元の記述を上記のように変更します。
変更する理由としては、geocoderはGoogle APIとは違うAPIを用いてaddressの情報を変換しているのですが、そうすると住所によっては変更できないものも出てきます。
その為、addressの変更に用いるAPIもGoogleにすればより詳しい住所まで変更できるようになるということです!
まとめ
以上の設定で、Google Mapを入力された住所から、表示することができるはずです。以下には私が個人的にやらかして詰まったところを備忘録として残しておきます。
詰まったところ①(原因:APIの認証をしていなかった)
GoogleのAPIキーの取得までは上手くいったのですが、APIの制限がMaps JavaScript APIだけにしていました。
これが原因で、geocoder.rbを編集しても、addressカラムの詳細な住所を変換してくれませんでした。
解決策としては、GoogleのAPIキーを管理する画面で、Geocoding APIも有効にして、キーの制限の中に追加することで、上手くできました。
ちなみに、この詰まったときに自分のアプリが詳細な住所を変換できるか確かめるために住所をフォームで何回も登録するのが面倒でした。笑
なので、$ rails c をして、下記のコマンドで詳細な住所の変更可否を確認していました。
[1] pry(main)> Geocoder.coordinates("東京都渋谷区道玄坂1-12-5渋谷マークシティ")
このコマンドを打って、nilが帰ってくる場合は、
・APIキーの認証が上手くいっていない
・geocoder.rbにタイポ(うちミス)がある
可能性があります。
もし、ちゃんと詳細な住所が変換されているときはnilではなく、
[1] pry(main)> Geocoder.coordinates("東京都渋谷区道玄坂1-12-5渋谷マークシティ")
=> [35.658046, 139.6980562]
こんな感じで、数字が返ってきます。
詰まったところ②(原因不明)
詰まったところ①を解決したので、登録フォームから詳細な住所を登録することができると思い、今までにテストで作ったイベントの住所を下記のように編集してみました。
(東京都渋谷区 => 東京都渋谷区道玄坂1-12-5渋谷マークシティ)
そうすると、addressは変更できていましたが、緯度と経度、地図の表示は変更されていませんでした。
ですが一旦、あたらしく別のイベントを作成すると、詳細な住所から、緯度と経度、地図も登録表示できました。
また、この新しく作ったイベントは編集でも詳細な住所から変換ができていました。
これに関しては、原因不明でした。
とりあえず、今までのイベントは削除という解決策?を取りました。
あと、詰まったというほどでもないですが、たまに追記や編集したコードが反映されていないなんてこともありました。
私の環境構築(docker)がよくない可能性もありますが、dockerを停止して再起動すれば大体は改善されました。
以上、最後までお付き合いいただきありがとうございました!😂