Rails|アプリにGoogleMap APIを導入する
目標
日本語学校の住所を登録したら、学校情報詳細画面でGoogle Mapが表示されるようになる。
開発環境
ruby 3.1.2p20
Rails 6.1.7.4
Cloud9
前提
Schoolモデルは作成済み。
Schoolモデルには、prefectureカラム、addressカラム、buildingカラムを作成済み。
API Keyを取得
1️⃣ Google Map Platformにアクセスし、プロジェクトを生成する。
2️⃣ 請求情報を登録する
3️⃣ 「Maps JavaScript API」と「Geocoding API」を有効化する
4️⃣ 「認証情報」より、APIキーを作成する。
5️⃣ 「認証情報」より、キーの制限を設定する。
詳細はこちらの記事を参照
緯度・経度カラムを追加する
Schoolモデルに緯度・経度カラムを追加する。
マイグレーションファイルの作成。
$ rails g migration add_column_to_schools
latitude(緯度)、longitude(経度)カラムを追加する。
class AddColumnToSchool < ActiveRecord::Migration[6.1]
def change
add_column :schools, :latitude, :float
add_column :schools, :longitude, :float
end
end
DBへ反映。
$ rails db:migrate
住所登録用ビューの作成
<%= form_with model: School.new, url: admin_schools_path, method: :post do |f| %>
...
<%= f.label :都道府県 %><span class="text-danger">*</span>
<%= f.select :prefecture, School.prefectures.keys.map { |key| [I18n.t("enums.school.prefecture.#{key}"), key] }, {} %>
<%= f.label :都道府県以降の住所 %><span class="text-danger">*</span>
<%= f.text_area :address, class: "form-control", rows: "1", placeholder: "渋谷区1-2-3" %>
<%= f.label :建物名 %>
<%= f.text_field :building, class: "form-control", placeholder: "東京ビル1F" %>
...
<% end %>
今回、都道府県はプルダウンで選択、都道府県以降の住所は手入力にしていたので、上記のようなフォームになった。
また、住所欄に建物名まで入れると緯度経度が正確に計測できないようなので、建物名は別カラムに保存した。
gemの追加・設定
以下のライブラリをGemfileに追加。
gem 'geocoder'
gem 'gmaps4rails'
geocoder
住所情報を緯度経度の数値に変換するgem。
gmaps4rails
緯度経度の情報からマップ上にピンを指すgem。
$ bundle install
Schoolモデルに設定を追記。
class School < ApplicationRecord
geocoded_by :full_address
after_validation :geocode
def full_address
[prefecture, address].compact.join(' ')
end
end
geocoded_by: full_address
geocoder
がfull_address
の住所を参照して、緯度経度を計算するように設定。
after_validation :geocode
Schoolモデルのバリデーションチェックが成功した後に、緯度経度を計算するように設定。
[prefecture, address].compact.join(' ')
Schoolモデルでは都道府県とそれ以降の住所を分けて保存しているので、それらを結合させて、full_addressとして取得できるようにしている。
.compact
配列からnil
を取り除くメソッド。
Map表示部分のビューの作成
<div class="map">
<div id="map" data-latitude="<%= school.latitude %>" data-longitude="<%= school.longitude %>">
</div>
</div>
<script type="text/javascript">
function initMap() {
var lat = parseFloat(document.getElementById('map').dataset.latitude);
var lng = parseFloat(document.getElementById('map').dataset.longitude);
let map = new google.maps.Map(document.getElementById('map'), {
center: { lat: lat, lng: lng },
zoom: 15
});
var marker = new google.maps.Marker({
position: {lat: lat, lng: lng},
map: map
});
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['API_KEY'] %>&callback=initMap" async defer></script>
<div id="map" data-latitude="<%= school.latitude %>" data-longitude="<%= school.longitude %>">
実際にMapが表示される部分。
function initMap() {
Google Mapを初期化するための関数。
var lat = parseFloat(document.getElementById('map').dataset.latitude);
Idがmap
のHTML要素を取得し、そこで埋め込まれているdata-latitude
の値を取得している。また、取得した文字列を数値に変換する。
let map = new google.maps.Map(document.getElementById('map')...
新しいGoogle Mapを生成。また、Mapの中心位置とズームレベルを設定している。
var marker = new google.maps.Marker({...
マップ上にピンを立てている。ピンの位置は学校の緯度と経度で指定されている。
<script src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['API_KEY'] %>&callback=initMap" async defer></script>
Google Map PIAを読み込んでいる。
以上で、基本的な設定は完了。
以上の設定で、「千葉県千葉市」等のおおまかな住所であれば地図が生成できるが、
詳しい住所を入力すると、地図が生成されない場合がある。
そのような場合は、以下の手順を踏む。
詳しい住所を表示させる
geocoderの設定ファイルを作成する。
$ bin/rails g geocoder:config
以下の通り編集する。
Geocoder.configure(
# Geocoding options
# timeout: 3, # geocoding service timeout (secs)
lookup: :google,
# lookup: :nominatim, # name of geocoding service (symbol)
# ip_lookup: :ipinfo_io, # name of IP address geocoding service (symbol)
# language: :en, # ISO-639 language code
# use_https: false, # 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['API_KEY'],
# api_key: nil, # API key for geocoding service
# cache: nil, # cache object (must respond to #[], #[]=, and #del)
# 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
# Cache configuration
# cache_options: {
# expiration: 2.days,
# prefix: 'geocoder:'
# }
)
lookup: :google
Geocoderが位置情報を取得するために使用するサービスを Googleに指定する。
これで、詳細な住所を指定しても、きちんと地図が生成されるようになりました!
参考にさせていただいた記事
Discussion