2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Rails】住所から地図と最寄り駅を表示する

Last updated at Posted at 2023-06-11

はじめに

  • エンジニア転職用のRailsポートフォリオに、住所から緯度経度情報を取得して地図とその地点の最寄り駅を表示する機能を導入した手順の記録です。
  • プログラミング学習3か月目の初心者です。誤りなどありましたらお教えいただけると幸いです。

環境

  • Ruby 3.1.2
  • Rails 6.1.7.3
  • IDE: Cloud9

完成イメージ

ezgif-4-2c32ac29a4.gif
実装した機能の動作デモです

流れ

  • ①gem geocorderを使用して住所から緯度経度を取得
  • ②取得した緯度経度からgoogle mapAPIで地図を表示し、ピンを立てる。
  • ③取得した緯度経度からHeartrailsAPIで最寄り駅・路線情報を取得し表示する。

参考URLなど

実装

①モデルの準備

テーブルに住所、緯度、経度、最寄り駅、最寄り駅路線のカラムが必要になるため追加します。
今回はJobモデルに追加します。

bash
$ rails g migration AddAddressesToJobs address:string latitude:double longitude:double near_station:string near_station_line:string

②Geocoderの準備

こちらこちら の記事を参考に、gem「geocoder」を使えるようにしていきます。
APIkeyの取得などは参考記事をご確認ください。
今回はGeocodingAPIとMaps JavaScript APIを有効化しておきます。


取得したAPIkeyは環境変数化しておきます。

.env
MAP_API_KEY = "取得したAPIキー"

続いてgem

gemfile
gem 'geocoder'
bash
$ bundle install

続いてそのほかの記述をしていきます

app/models/job.rb
geocoded_by :address
after_validation :geocode, if: :address_changed? 
# 住所が保存・更新されたら緯度経度を自動取得してlatitude,longitudeに入れてくれる
bash
$ rails g geocoder:config

※上のコマンドでconfig/initialize/geocorder.rbが生成されるので、以下の通り書き換えます。

config/initialize/geocorder.rb
# frozen_string_literal: true

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["MAP_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: :mi,                 # :km for kilometers or :mi for miles
  # distances: :linear          # :spherical or :linear

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

以上でGeocoderの準備は完了です。

③GoogleMapAPIで地図表示

次に、erbファイルに以下の記述を追加します。可読性を考慮して今回はパーシャル化しています。
スタイルや倍率、中心位置などカスタマイズ可能です。

app/views/jobs/_map.html.erb
<div id="map"></div>

<style>
/*マップのスタイル*/
#map{
  height: 300px;
  width:100%;
}
</style>

<script>
// マップ表示スクリプト
function initMap(){

  //マップの設定
  let map = new google.maps.Map(document.getElementById('map'), {
  center: {lat: <%=job.latitude%>, lng: <%= job.longitude %>},       //地図表示の中心位置を指定
  zoom: 15                                                           //倍率
  });

  //マーカーの設定
  let marker= new google.maps.Marker({
     position: { lat: <%=job.latitude%>,lng:  <%= job.longitude %>}, //マーカーの位置を指定
     map: map
  });
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['MAP_API_KEY'] %>&callback=initMap" async defer></script>

以上で、入力された住所に対応したマーカー付地図を表示できました。

④Heartrails APIで最寄り駅表示

続いてGeocoderで取得した緯度経度から、HeartrailsAPIを叩いて最寄り駅・最寄り駅路線の情報を取得していきます。特にキーの取得などは必要ありません。
jobsコントローラのcreate,updateアクションで動作させます。緯度経度を入れてAPIのURLにアクセスするとレスポンスが返ってくるので、緯度経度保存→最寄り情報取得→再度保存、の流れを踏みます。例:(https://express.heartrails.com/api/json?method=getStations&x=139.6986798793053&y=35.6596004)

app/controllers/jobs_controller.rb
def create
    job=Job.new(job_params)
    job.near_station_line = ""
    job.near_station = "" # 一度保存しないとgeocoderが動かないため最寄りは空欄でいったん保存
    if job.save
        # 最寄り駅情報を取得。
        uri = URI.parse("http://express.heartrails.com/api/json?method=getStations&x=#{job.longitude}&y=#{job.latitude}")
        response = Net::HTTP.get_response(uri)
        result = JSON.parse(response.body)
        
        #カラムにそれぞれ代入する。最寄り駅が存在しない座標の場合にエラーになるためunless以下も必須
        job.near_station = result["response"]["station"][0]["name"] unless result["response"]["station"].blank?
        job.near_station_line =  result["response"]["station"][0]["line"] unless result["response"]["station"].blank?

        # 再度保存する
        job.save
        redirect_to job_path(job),flash: {notice: "募集を公開しました"}
    else
        @job = job
        flash.now[:error] = "作成に失敗しました"
        render :new
    end
end


def update
    job=Job.find(params[:id])
    if job.update(job_params)
        # 最寄り駅情報を取得。
        uri = URI.parse("http://express.heartrails.com/api/json?method=getStations&x=#{job.longitude}&y=#{job.latitude}")
        response = Net::HTTP.get_response(uri)
        result = JSON.parse(response.body)
        
        #カラムにそれぞれ代入する。最寄り駅が存在しない座標の場合にエラーになるためunless以下も必須
        job.near_station = result["response"]["station"][0]["name"] unless result["response"]["station"].blank?
        job.near_station_line =  result["response"]["station"][0]["line"] unless result["response"]["station"].blank?

        # 再度保存する
        job.save          
        redirect_to job_path(job),flash: {notice: "募集を公開しました"}
    else
      @job=job
      flash.now[:error] = "公開できませんでした"
      render :edit
    end
end

これで動作しますが、ごちゃついてしまうので最寄り取得部分をapp/models/job.rbに切り出して整理します。

app/models/job.rb
 # 最寄り駅を取得して保存するメソッド
  def addStation
    # 最寄り駅情報を取得。
    uri = URI.parse("http://express.heartrails.com/api/json?method=getStations&x=#{self.longitude}&y=#{self.latitude}")
    response = Net::HTTP.get_response(uri)
    result = JSON.parse(response.body)

    #カラムにそれぞれ代入する。最寄り駅が存在しない座標の場合にエラーになるためunless以下も必須
    self.near_station = result["response"]["station"][0]["name"] unless result["response"]["station"].blank?
    self.near_station_line =  result["response"]["station"][0]["line"] unless result["response"]["station"].blank?

    # 再度保存する
    self.save
  end

コントローラは以下のように修正。

app/controllers/jobs_controller.rb
 def create
    job=Job.new(job_params)
    job.near_station_line = ""
    job.near_station = "" # 一度保存しないとgeocoderが動かないため最寄り駅空欄でいったん保存
    if job.save
        # 最寄り駅情報を取得して再保存
        job.addStation
        redirect_to job_path(job),flash: {notice: "募集を公開しました"}
    else
        @job = job
        flash.now[:error] = "作成に失敗しました"
        render :new
    end
    
end

def update
    job=Job.find(params[:id])
    if job.update(job_params)
        # 最寄り駅情報を取得して再保存
        job.addStation
        redirect_to job_path(job),flash: {notice: "募集を公開しました"}
    else
        @job=job
        flash.now[:error] = "公開できませんでした"
        render :edit
    end

end

以上で最寄り駅・路線を取得してテーブルに保存することが出来ました。ビューは適宜実装してください。

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?