#273 Geocoder
- Download:
- source codeProject Files in Zip (195 KB)
- mp4Full Size H.264 Video (18.6 MB)
- m4vSmaller H.264 Video (9.43 MB)
- webmFull Size VP8 Video (9.95 MB)
- ogvFull Size Theora Video (22.6 MB)
Railsアプリケーションで地理データを扱う場合、Geocoder gemが作業をとても簡単にしてくれます。地名から地理座標への変換あるいはその逆、またIPアドレスを住所に変換することもできます。近くの観光スポットを探して距離と方位を提示したり、その他にも役に立つ機能を多く持っています。
今回のエピソードではユーザが観光スポットを推薦するSiteseerというアプリケーションを作成します。アプリケーションを新規作成したら、次はLocation
のscaffoldをaddress
、latitude
、 longitude
フィールドを指定して作成します。
$ rails g nifty:scaffold location address:string latitude:float longitude:float
latitude
(緯度)とlongitude
(経度)のフィールド名は、Geocoderが観光スポットの地理座標を保存するために使用するので重要です。ただしこのデフォルトのフィールド名は変更することもできます。どちらのフィールドもfloat
(浮動小数)にする必要があります。データベースのマイグレーションを行って、新しくlocationsテーブルを作成します。
$ rake db:migrate
scaffoldの設定ができたので新しい観光スポットを作成できるのですが、このままでは緯度と経度を手入力しなくてはいけません。そこでGeocoderが自動的にこれらのフィールドを埋めるようにアプリケーションを改良していきます。
Geocoderのインストールと使用
Geocoderのインストール方法は通常どおりです。まずGemfile
でgemへの参照情報を追加して、bundle
コマンドを実行してインストールします。
source 'http://rubygems.org' gem 'rails', '3.0.9' gem 'sqlite3' gem 'nifty-generators' gem 'geocoder'
次にLocation
モデルにgeocoded_by
の呼び出しを追加してGeocoderに変換させたい属性(今回の場合はaddress
(住所))を指定します。
class Location < ActiveRecord::Base attr_accessible :address, :latitude, :longitude geocoded_by :address end
もし住所が複数のフィールドに分かれて保存されている場合は代わりにメソッドを指定して、完全な住所を返すメソッドを作成します。
観光スポットが作成あるいは更新されたらgeocode
メソッドを呼び出します。通常はこれをafter_validation
コールバックを使って行います。
class Location < ActiveRecord::Base attr_accessible :address, :latitude, :longitude geocoded_by :address after_validation :geocode end
geocodeメソッドはリクエストを外部API(デフォルトではGoogle Maps API)に送ります。外部サービスを呼び出す場合は、この呼び出しをバックグラウンドプロセスに移した方がいいでしょう。ここではそれは行いませんが、その設定方法を知るためにはResqueについての最近のエピソード[動画を見る, 読む]を参照してください。
それではアプリケーションを起動してGeocoderを試してみましょう。新しい観光スポット(例えばSt Pancras Station, London)を緯度や経度なしで入力すると、Geocoderが外部APIからデータを取得して観光スポットに追加します。
外部APIは観光スポットが更新されるたびに呼び出されますが、これはaddress
フィールドが変更されたときのみに限定したほうがいいでしょう。これを実装するために、after_validation
コールバックを少し修正します。
class Location < ActiveRecord::Base attr_accessible :address, :latitude, :longitude geocoded_by :address after_validation :geocode, :if => :address_changed?end
これはdirty trackingを用いて住所が変更されたかどうかをチェックして、変更されている場合のみAPIを呼び出します。
またreverse_geocoded_by
メソッドを使って、逆に緯度と経度を住所に変換することもできます。これはgeocoded_by
メソッドと同じ要領で動作します。今回のアプリケーションでは使用しません。
近くの観光スポットを取得する
ある場所を見ているときに近くの観光スポットを見ることができたら便利でしょう。データベースにいくつか観光地の情報があるので、観光スポットページにこの機能を追加してみましょう。
ある距離内に存在する観光スポットをリスト表示したいのですが、Geocoderはnearbys
メソッドで近くのスポットを簡単に取得することができます。デフォルトではこれは半径20マイル内の観光スポットを返しますが、ここでは10マイルに狭めます。近くの観光スポットをリストアップするために@location.nearbys
の各locationをループして、距離(マイル)と一緒にリンクを表示します。
<ul> <% for location in @location.nearbys(10) %> <li><%= link_to location.address, location %> ↵ (<%= location.distance.round(2) %> miles)</li> <% end %> </ul<% title "Location" %> <p> <strong>Address:</strong> <%= @location.address %> </p> <p> <strong>Latitude:</strong> <%= @location.latitude %> </p> <p> <strong>Longitude:</strong> <%= @location.longitude %> </p> <h3>Nearby Locations</h3> <ul> <% for location in @location.nearbys(10) %> <li><%= link_to location.address, location %> ↵ (<%= location.distance.round(2) %> miles)</li> <% end %> </ul> <p> <%= link_to "Edit", edit_location_path(@location) %> | <%= link_to "Destroy", @location, :confirm => ↵ 'Are you sure?', :method => :delete %> | <%= link_to "View All", locations_path %> </p>
観光スポット情報のページを見るとその近くのスポットもリスト表示されますが、200マイル離れたLiverpoolにあるスポットは表示されません。
次に観光スポットページに検索ボックスを追加して、ある街にあるスポットを検索できるようにします。これをindexビューの一番上の新しいフォームに追加します。
<% title "Locations" %> <%= form_tag locations_path, :method => :get do %> <p> <%= text_field_tag :search, params[:search] %> <%= submit_tag "Search Near", :name => nil %> <% end %> <!-- rest of page -->
検索語が送信されると、フォームはGETを使ってsearch
パラメータを付けてindex
ページを呼び出します。LocationsController
のindex
アクションでパラメータを確認し、もし存在したら近くの観光スポットを探します。
def index if params[:search].present? @locations = Location.near(params[:search], 50, ↵ :order => :distance) else @locations = Location.all end end
ページを再度読み込んでManchesterを検索すると、30マイル離れたLiverpoolに1件見つかりました。
これである街の近くにある観光スポットを探せました。
地図を追加する
地理情報を扱っているのであれば、各場所について地図を表示できたら便利でしょう。観光スポットページに、観光スポットの場所を正確に示す地図を追加してみましょう。Google Maps APIはWebページに地図を追加する各種の方法を提供していますが、今回は簡単に実装できるStatic Maps APIを使用します。簡単なimageタグで地図を追加できますが、指定されるURLには地図のいろいろな外観を指定するパラメータ(緯度、経度、サイズ、ズーム、など)がついています。近くの観光スポットのリストの上に直接地図を追加します。
<%= image_tag "http://maps.google.com/maps/api/staticmap?size=450x300&sensor=false&zoom=16&markers=#{@location.latitude}%2C#{@location.longitude}" %>
渡すパラメータはほとんどが静的に指定された値ですが、緯度と経度だけは@location
オブジェクトから取得した値です。観光スポットのページを見てみると、その場所の地図が表示されています。
静的な画像よりも手が込んだものが欲しければ、JavaScript APIとのやりとりを簡単にしてくれるGoogle Maps For Rails gemを見てみることをお勧めします。これを使えば、あなたのRailsアプリケーションに簡単にインタラクティブな地図を追加することができます。