🔴

【Ruby】RailsでGoogle Maps API(Geocording)を導入しよう

2024/05/02に公開

はじめに

使い勝手のよいwebアプリケーションを作成する上では、APIを理解しなければならない。今回は、APIやその他関連知識をまとめ、応用編として「Google maps API」を用いてアプリケーション作成できるようまとめている。

APIとは

APIの種類[1]

名称 提供者 具体例
WebAPI webサービスベンダー GoogleAPIなど
ネイティブAPI オペレーティングシステム(OS) WindowsAPIなど
ライブラリAPI フレームワーク 〇〇Frameworkなど
ランタイムAPI ランタイム環境 Ruby on RailsAPIなど
データベースAPI データベースサービスベンダー OCLAPIなど

APIの提供方式

名称 利用者
パブリックAPI 登録すれば誰でも利用できる
ビジネスAPI 課金契約で利用
メンバーAPI コミュニティメンバーで利用
パートナーAPI パートナー企業内で利用
クローズドAPI 社内・組織の内部で利用
プライベートAPI 特定のユーザーのみで利用

APIの仕組み

APIはユーザーが必要とするたびに利用(リクエスト)され、その応答(レスポンス)を得ることで利用される。

例)
ECサイトでユーザーがクレジットカードを用いて決済する機能でAPIを用いた場合、ECサイト側でクレジットカードの決済機能を開発する必要はなく、クレジットカード運営会社が用意しているAPIを通じてカード番号や氏名などの情報を含めリクエストすると、決済の可否を表すレスポンスが返ってくるようになっている。

SDKとは

Google Maps APIの全体を把握しよう

Goolge Maps APIの種類

API名 内容
JavaScript API Google Maps をウェブサイトに表示
Static Maps API 地図画像を高解像度で表示
Street View Image API ストリートビュー画像を高解像度で表示
Directions API 最適化された経路検索を自動で行うことが可能
Distance Matrix API 社内・複数の始点・終点の組み合わせルートの「距離」「時間」を求められる
Roads API 道路に沿ったスムーズなパスを取得します、また制限速度情報を返す
Geolocation API GPS 機能が無い環境でユーザーの位置情報を特定可能
Places API 施設検索の API、一億件以上の詳細情報を検索・表示可能
Geocording API 住所を緯度・経度(または緯度経度を住所)に変換可能
Elevation API ある地点の標高(高さ)を取得可能
Time Zone API ある地点のタイムゾーン(時間帯)を取得可能

https://console.cloud.google.com/google/maps-apis/build?project=weighty-diagram-421813

今回は「java script API」と「Geocording API」と「Places API」を実装していく。

JavaScript APIとGeocording APIとPlaces APIを活用してみよう。

1.Google Cloud Platformの登録する。


GCP登録画面

2.メニューバーからAPIとサービスを選択する。

3.ダッシュボードからプロジェクトを作成する。

4.認証情報から「認証情報を作成」を押し「APIキー」を選択する。

5.APIキーが作成されたことを確認する。

6.可能であれば作成されたAPIキーに制限をかける。

  • ウェブサイトの場合はウェブサイトを選択し、ADDからURLを追加する。
  • IPアドレスの場合はターミナルからipconfigコマンドを実行し、IPアドレスを確認する。

7.検索フォームに「Maps Javascript API」と入力し、そのAPIを有効にする。同様のやり方で「geocoding API」「places API」を有効にする。

------------- ここからは、VScode(エディタ)の方  -----------

8.環境変数を管理するために[dotenv]をインストールする。

Gemfile
gem 'dotenv-rails'
ターミナル
bundle install
ターミナル
yarn add dotenv
出力結果
出力結果
$ yarn add dotenv
yarn add v1.22.22
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
warning " > @babel/plugin-proposal-private-methods@7.18.6" has unmet peer dependency "@babel/core@^7.0.0-0".
warning "@babel/plugin-proposal-private-methods > @babel/helper-create-class-features-plugin@7.24.1" has unmet peer dependency "@babel/core@^7.0.0".
warning "@babel/plugin-proposal-private-methods > @babel/helper-create-class-features-plugin > @babel/helper-replace-supers@7.24.1" has unmet peer dependency "@babel/core@^7.0.0".
warning " > @babel/plugin-proposal-private-property-in-object@7.21.11" has unmet peer dependency "@babel/core@^7.0.0-0".
warning "@babel/plugin-proposal-private-property-in-object > @babel/helper-create-class-features-plugin@7.24.4" has unmet peer dependency "@babel/core@^7.0.0".
warning "@babel/plugin-proposal-private-property-in-object > @babel/plugin-syntax-private-property-in-object@7.14.5" has unmet peer dependency "@babel/core@^7.0.0-0".
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ dotenv@16.4.5
info All dependencies
└─ dotenv@16.4.5
Done in 15.02s.

9.[.env]ファイルを作成する。

ターミナル
touch .env

10.[.gitignore]ファイルに[.env]を追加する。

.gitignore
/.env

11.[.env]ファイル内に作成したAPIキー(Maps Javascript API/geocoding API)を記載し、読み込めるようにする。

.env
GOOGLE_MAPS_API_KEY="AAAAAABBBBBBCCCCCDDDDD"
GEOCODING_API_KEY="AAAAAABBBBBBCCCCCDDDDD"
PLACES_API_KEY="AAAAAABBBBBBCCCCCDDDDD"

この記載を行うことでRails(index.html.erb)ファイル内で、ENV["GOOGLE_MAPS_API_KEY"]ENV["GEOCODING_API_KEY"]ENV["PLACES_API_KEY"]と記載することで、環境変数を表現することができるようになる。

12.javascriptでAPIキーを読み込めるようにする。

config/webpack/environment.js
require("dotenv").config(); // 追加

const { environment } = require('@rails/webpacker')

module.exports = environment

Google Maps APIは基本的にjava scriptで動作する。そのため、APIキーはjava scriptのコード内で必要となってくるため記載する必要がある。上記ファイル内に記載を追加することで、Railsとjava scriptの両方で環境変数を読み込むことができるようになる。

13.「geocoder Gem」と「google_places Gem」のインストールする。

Gemfile
gem 'geocoder'
gem 'google_places'
ターミナル
bundle install

14.geocoderの設定ファイルを作成し、Geocoding APIを連携させる。

ターミナル(geocoderの設定ファイルを作成する)
rails g geocoder:config
実際の画面

lookup:,user_https,api_key,unitsの項目を変更する。

config/initializers/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["Geocoding_API_Key"],# 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: :km,                 # :km for kilometers or :mi for miles
  # distances: :linear          # :spherical or :linear

  # Cache configuration
  # cache_options: {
  #   expiration: 2.days,
  #   prefix: 'geocoder:'
  # }
)

15.Geocodingの機能を使用するためにマイグレーションファイルを作成する。

ターミナル
rails g migration AddGeocodingColumnToPosts address:string latitude:float longitude:float

作成されたマイグレーションファイルを編集する。

YYYYMMMMDDDD_add_geocoding_column_to_post
class AddGeocodingColumnToPosts < ActiveRecord::Migration[6.1]
  def change
    add_column :posts, :address, :string, null: false, default: ""
    add_column :posts, :latitude, :float, null: false, default: 0
    add_column :posts, :longitude, :float, null: false, default: 0
  end
end
ターミナル
rails db:migrate

16.Postモデルでgeocodingを使えるようにする。

class Post < ApplicationRecord
  :
 :
  validates :address, presence: true
  geocoded_by :address
  after_validation :geocode
 :
 :
end
コードの意味

geocoded_by :addressで、addressカラムの内容を緯度・経度に変換する
after_validation :geocodeで、バリデーションの実行後に変換処理を実行して、latitudeカラム・longitudeカラムに緯度・経度の値が入力される。

17.places apiの設定ファイルを作成し、下記コードをファイル内に追加し、places APIを連携させる。


config/initializers/google_places.rb

config/initializers/google_places.rb
GooglePlaces.configuration do |config|
  config.api_key = ENV['PLACES_API_KEY']
end

18.フォームから住所を入力できるようにする。(places APIによりフォームの補完が効く)

app/views/posts/new.html.erb
<h1>Hello</h1>

<%= form_with model: @post, url: posts_path, method: :post, id: 'post-form' do |f| %>
  <div>
    <%= f.label :title, "タイトル" %><br>
    <%= f.text_field :title %>
  </div>

  <div>
    <%= f.label :body, "本文" %><br>
    <%= f.text_area :body %>
  </div>

  <div>
    <%= f.label :images, "画像" %><br>
    <%= f.file_field :image, accept: "image/*" %>
  </div>
 <!-- 補完機能の付いた住所フォームが作成できる -->
  <div>
    <%= f.label :address, "住所" %><br>
    <%= f.text_field :address, id: 'address-input' %>
  </div>

  <div>
    <%= f.submit "投稿" %>
  </div>
<% end %>

<!-- places APIを読み込む -->
<script src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['PLACES_API_KEY'] %>&libraries=places"></script>

<!-- 上にある住所のフォーム内にテキストが入力された際の動的な処理 -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    var addressInput = document.getElementById('address-input');
    var autocomplete = new google.maps.places.Autocomplete(addressInput);

    // 選択された住所をフォームの住所フィールドにセット
    google.maps.event.addListener(autocomplete, 'place_changed', function() {
      var place = autocomplete.getPlace();
      addressInput.value = place.formatted_address;
    });
  });
</script>

19.コントローラ(posts_controller.rb)の記述をする。

app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def index
    @posts = Post.all
  end

  def create
    @post = current_user.posts.build(post_params)

    result = Geocoder.search(@post.address).first
    if result
      @post.latitude = result.latitude
      @post.longitude = result.longitude
    end

    if  @post.save
      redirect_to posts_path
    else
      render :new
    end
  end

  private
    def post_params
      params.require(:post).permit(:title, :body, :image, :address,)
    end
end
コードの解説
result = Geocoder.search(@post.address).first

Geocoder.seachメソッドを用いて、ストロングパラメータの中にあるaddressの中身の緯度と経度を探す。

if result
  @post.latitude = result.latitude
  @post.longitude = result.longitude
end

②その投稿(post)のlatitude=緯度とlongitude=経度をそれぞれ代入し、表示に用いる。

20.地図を表示するためにビューファイルにjava scriptのコードを記述する。

app/views/posts/show.html.erb

<div id="map" style="height: 600px;"></div>

<script>
  function initMap() {
    // 地図要素を取得する(マーカーを表示させるために必要)
    const mapElement = document.getElementById('map');

    const mapOptions = {
      center: {lat: <%= @post.latitude %>, lng: <%= @post.longitude %>},
      zoom: 15
    };

    const map = new google.maps.Map(mapElement, mapOptions);

    // マーカーを追加(Postの情報からマーカーを追加する)
    const maker = new google.maps.Marker({
        position: {lat: <%= @post.latitude %>, lng: <%= @post.longitude %>},
        map: map,
        title: '<%= j @post.title %>'
      });
  }
</script>

<script src="https://maps.googleapis.com/maps/api/js?key=<%= ENV["GOOGLE_MAPS_API_KEY"] %>&callback=initMap"></script>

21.http://localhost:3000/posts/newにアクセスし、投稿を行う。

22.http://localhost:3000/posts/4投稿した内容を確認する。(post/の後は投稿のid)

お疲れ様でした。

終わりに

APIを用いるにはAPIキーが必要であり、それは機密情報として扱われるため、アプリケーション内の.envファイル内に保管され、web上にアップされないようgitignoreファイルに記載する必要があることがわかった。Google maps APIを用いたが、世の中には数えきれないほど便利なAPIが多く存在する。これを機に様々なAPIを用いてwebアプリケーション開発にチャレンジしていきたい。

参考

https://qiita.com/tiara/items/573fe5f1a84ca57dabcd

脚注
  1. https://www.sbbit.jp/article/cont1/62752 ↩︎

Discussion