こんにちは。
今回は、RailsでGoogle mapの位置情報を扱うモデルを作成する方法をご紹介します。
Google mapとは
Google mapは、Googleが提供している地図サービスです。
このような地図をアプリ内に埋め込み、マーカーを表示するまでを今回はご紹介します。
Google mapのAPIキーを取得する
Google mapを使うには、APIキーが必要になります。
Google map APIキーの取得方法の公式ドキュメント
GCPのプロジェクトを作成し、上記の公式ドキュメントを参考にAPIキーを取得します。
develop環境で試す分には特にAPIキーの制限は必要ないと思いますが、本番環境で使う場合は、制限を設けることをおすすめします。
developで使用しているキーは流出した場合、課金が発生する可能性があるためこまめにAPIキーを変更することをおすすめします。
RailsのcredentialsにAPIキーを設定する
RailsのcredentialsにAPIキーを設定します。
EDITOR="code --wait" bin/rails credentials:edit
gcp:
maps_platform_api_key: XXXXXXXXXXX
上記の設定で、以下のようにAPIキーを取得することができます。
key = Rails.application.credentials.dig(:gcp, :maps_platform_api_key)
Railsサーバーの再起動が必要です。
位置情報を扱うモデルを作成する
位置情報を扱うモデルとして、今回はAddressモデルを作成します。
Addressモデルのスキーマは以下のようになります。
create_table :addresses, force: :cascade do |t|
t.references :addressable, polymorphic: true
t.string :postal_code, null: false
t.string :text, null: false
t.string :building
t.string :access, null: false
t.float :latitude, null: false
t.float :longitude, null: false
t.string :place_id, null: false
t.string :prefecture, null: false
t.timestamps null: false
end
Addressableは、Addressモデルを関連付けるためのカラムです。
ポリモーフィック関連付けを使っています。
Addressモデルの内部は以下のようになります。
※ 必要な部分のみに絞っています。
次回、途中の文字列(郵便番号など)を入力すると、自動で住所を補完する機能に必要なコードもご紹介しますね。
class Address < ApplicationRecord # rubocop:disable Metrics/ClassLength
# アソシエーション
belongs_to :addressable, polymorphic: true
# バリデーション
validates :postal_code, presence: true
validates :text, presence: true
validates :access, presence: true
validates :longitude, presence: true
validates :latitude, presence: true
validates :place_id, presence: true
validates :prefecture, presence: true
# エラークラスの定義
class ZeroResultError < StandardError; end
class InvalidAddressError < StandardError; end
def self.initialize_with_full_text(text:)
body = fetch_address(text:)
new(formatted_params_for_full_text(body))
end
class << self
private
def fetch_address(text:)
api_key = Rails.application.credentials.dig(:gcp, :maps_platform_api_key)
encoded_text = URI.encode_www_form_component(text)
uri = URI.parse("https://maps.googleapis.com/maps/api/geocode/json?address=#{encoded_text}&language=ja&components=country:JP&key=#{api_key}")
res = Net::HTTP.get_response(uri)
raise ActiveRecord::RecordNotFound if res.code.to_i == 404
raise StandardError unless res.code.to_i == 200
raise ZeroResultError if JSON.parse(res.body, symbolize_names: true)[:status] == 'ZERO_RESULTS'
JSON.parse(res.body, symbolize_names: true)
end
def formatted_params_for_full_text(body)
text = body.dig(:results, 0, :formatted_address).split[1].tr('0-9', '0-9')
address_components = body.dig(:results, 0, :address_components)
postal_code = address_components.find do |component|
component[:types].include?('postal_code')
end.try(:dig, :long_name).try(:tr, '0-9', '0-9')
location = body.dig(:results, 0, :geometry, :location)
latitude = location[:lat]
longitude = location[:lng]
place_id = body.dig(:results, 0, :place_id)
prefecture = address_components.find do |component|
component[:types].include?('administrative_area_level_1')
end.try(:dig, :long_name)
{
postal_code:,
text:,
latitude:,
longitude:,
place_id:,
prefecture:
}
rescue StandardError
raise InvalidAddressError
end
end
end
Address.initialize_with_full_text(text:)
を呼び出すと、Google mapのAPIを叩いて、それに紐づくAddressモデルのインスタンスを返します。
fetch_address(text:)
は、Google mapのAPIを叩いて、その結果を返すメソッドになっています。
formatted_params_for_full_text(body)
は、Google mapのAPIの結果を元に、Addressモデルのインスタンスを作成するためのパラメータを返すメソッドになっています。
これで、フォームから住所のテキスト情報を受け取ったら、Address.initialize_with_full_text(text:)
を呼び出すことで、住所の情報を取得することができます。
Google mapを表示する
入った住所情報をもとに、Google mapを表示します。
Google mapの表示方法には、JavaScriptを使用する方法(= Maps JavaScript API)とHTMLのみで表示する方法(= Maps Embed API)があります。
単純に、一つのマーカーを表示するだけであれば、Maps Embed APIを使うのが簡単ですので今回はこちらを使用します。
またMaps JavaScript APIについても、次回の記事でご紹介します。
Maps Embed APIを使う
Maps Embed APIを使うには、以下のように<iframe>
タグを使用します。
%iframe.w-full.h-80{:src => map_url, :allowfullscreen => "", :loading => "lazy", :referrerpolicy => "no-referrer-when-downgrade"}
map_url
は、以下のようにコンポーネント内のメソッドとして作成します。
class Addresses::Map::Component < ViewComponent::Base
def initialize(place_id:)
@place_id = place_id
end
private
def map_url
key = Rails.application.credentials.dig(:gcp, :maps_platform_api_key)
"https://www.google.com/maps/embed/v1/place?key=#{key}&q=place_id:#{@place_id}"
end
end
view内で、以下のように呼び出します。
= render Addresses::Map::Component.new(place_id: @address.place_id)
これで、Google mapが表示されます。
まとめ
今回は、Google mapのAPIを使って、住所の情報を取得する方法をご紹介しました。
次回、郵便番号から住所を取得する方法をご紹介します。