投稿された住所をマップに反映させるRailsアプリを作る


Railsで、ユーザが投稿した住所をマップ上に表示させるアプリを作りました。
ごくごく簡単なものになりますのでササッと。

アプリケーションの動き

  1. ユーザーがフォームから住所を投稿
  2. 住所を緯度経度に変換
  3. 変換した移動軽度から、画面上のマップにマーカーを反映させる。

な感じです。

準備

駆け足で書いていくため、最初の手順は省きます。
取り敢えず、
rails new がされている状態から始めます。

Gemfileに追記

Gemfileに以下を加え、bundle installを実行します。

# map表示を簡易化する
gem 'leaflet-rails'
# map上のマーカー周りをゴニョゴニョする
gem 'leaflet-markercluster-rails'
# 住所を緯度経度に変換する
gem 'geocoder'

設定ファイルを追加

config/initializersに以下のファイルを追加

Leaflet.tile_layer = "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
Leaflet.attribution = 'Map data &copy; <a href="http://openstreetmap.org">OpenStreetMap</a>'
Leaflet.max_zoom = 18

特に難しい設定ではなくて、見てのとおりですね
tile_layerを書き換えれば、OpenStreetMapだけでなくgoogle mapやらも使うことが出来ます。

scaffoldでササッと投稿アプリを制作

Railsの魔法ことScaffoldで、簡単にユーザー投稿アプリを作ります。

rails g scaffold maps name address comment:text lonlat

それぞれ

  • name:住所を投稿したユーザの名前(なくていい)
  • address:投稿された住所
  • comment:なにかしらのコメント
  • lonlat:投稿された住所を(内部的に)緯度経度変換し保存する

です。

緯度経度(lonlat)はユーザーに入力させたくないので、_form.html.erbから該当箇所を削除しておきます。

最後にrails db:migrateも実行。

leafletでマップを表示する

今回は『Open Street Map』を使用します。

leafletを使うための準備

まずleafletを使用するため、application.jsとapplication.cssに以下を追加します。

application.js

//= require leaflet
//= require leaflet.markercluster

application.css

 *= require leaflet
 *= require leaflet.markercluster
 *= require leaflet.markercluster.default
 */

適当なマーカー用画像を用意

今回は、取り敢えず"marker.png"という画像を/Publicに追加したということで進めます。

leafletを使ってマップを表示

maps/indexに以下のコードを追記します。


(省略)
<div id="map" style="height: 500px; width: 500px;"></div>
<%=
  map(:center => {
      :latlng => [36, 140],
      :zoom => 4,
  })
%>
<script>
  // rubyから渡された住所配列をjsの変数に格納
  var maps = <%= @maps.to_json.html_safe %>;

  for(var hash_count = 0; hash_count < maps.length; hash_count++){
    // 緯度経度とコメントを取り出しマーカー化
    L.marker(maps[hash_count].lonlat.split(','), {icon: L.icon({iconUrl: "/marker.png"})},)
      .bindPopup(maps[hash_count].comment)
      .addTo(map);
  }
</script>

scriptタグの中はこちらの書き方の方がスマートでいいですね


  <% @maps.each do |map| %>
    L.marker("<%= map.lonlat %>".split(','), {icon: L.icon({iconUrl: "/cycle24.png"})},)
    .bindPopup("<%= map.comment %>")
    .addTo(map);
  <% end %>

投稿された住所を緯度経度に変換する

ユーザーが住所を入力し保存(あるいは更新)する際に、住所を緯度経度に変換しlonlatカラムに保存するための仕組みを作ります。

class Map < ApplicationRecord
    before_save :update_lonlat

    def update_lonlat
        lonlat = address_to_lonlat(read_attribute(:address))
        write_attribute(:lonlat, lonlat)
    end

    # 住所を保存用経緯度に変換
    def address_to_lonlat(address)
      result = Geocoder.search(address)
      lonlat = result.first.coordinates
      return lonlat.join(',')
    end
end

これだけで、住所(address)カラムを保存、更新した際に緯度経度(lonlat)カラムも更新してくれます。

完成

上記の手順でおそらく完成するはずです。

寝る前にササッと書いているのでかなり駆け足でしたが、お読み頂きありがとうございました。