Ruby geocoderがすごい

住所を緯度経度に直したり、緯度経度から住所を求めたりする操作をgeocodingと言って、Google Maps APIを使うとまあたいていのことはできる。
ロケタッチAPIとか、Yahoo!ジオコーダAPIという手もある。

それはともかく、そのへんをパチパチ叩くコードを書いていて、「こんなのもうとっくに誰かが書いてんじゃないかなー」と思ってぐぐってみたらなんかすごいのが出てきた。


geocoder.png

Ruby Geocoder

住所と緯度経度の相互変換はもちろん、距離や範囲の扱い、Google以外のAPIへの対応、キャッシュ処理など、「実装しようかなー。でもめんどくさいよね」とか思って先送りしていたような機能がほとんど全部実装されている。

住所の取得

require 'geocoder'
# 日本語ロケールに設定
Geocoder.configure( :language  => :ja,   :units => :km )
Geocoder.address("東京タワー")
# => "日本, 〒105-0011 東京都港区芝公園4丁目4−2−8 東京タワー"

もちろん、住所しか取れないなんて馬鹿なことはなくて、GoogleMapsAPIの結果をそのままもらうこともできます

Geocoder.search("東京タワー")
# => [#
[{"long_name"=>"東京タワー",
"short_name"=>"東京タワー",
"types"=>["point_of_interest", "establishment"]},
:中略
{"long_name"=>"芝公園",
"short_name"=>"芝公園",
"types"=>["sublocality_level_1", "sublocality", "political"]},
{"long_name"=>"港区",
"short_name"=>"港区",
"types"=>["locality", "political"]},
{"long_name"=>"東京都",
"short_name"=>"東京都",
"types"=>["administrative_area_level_1", "political"]},
{"long_name"=>"日本",
"short_name"=>"JP",
"types"=>["country", "political"]},
{"long_name"=>"105-0011",
"short_name"=>"105-0011",
"types"=>["postal_code"]}],
"formatted_address"=>"日本, 〒105-0011 東京都港区芝公園4丁目4−2−8 東京タワー",
"geometry"=>
{"location"=>{"lat"=>35.6585805, "lng"=>139.7454329},
"location_type"=>"APPROXIMATE",
"viewport"=>
{"northeast"=>{"lat"=>35.6676459, "lng"=>139.7614403},
"southwest"=>{"lat"=>35.6495141, "lng"=>139.7294255}}},
"types"=>["point_of_interest", "establishment"]}>]

緯度経度から住所へ(リバースジオコーディング)

Geocoder.address("39.70150773,141.13672256")
# => "日本, 〒020-0034 岩手県盛岡市盛岡駅前通 県道2号線"
Geocoder.search("39.70150773,141.13672256")
[#"日本, 〒020-0034 岩手県盛岡市盛岡駅前通 県道2号線",
:(中略)
"types"=>["route"]}>,
#"日本, 盛岡駅前(バス)(岩手)",
:(中略)
"types"=>["bus_station", "transit_station", "establishment"]}>,
#"〒020-0034, 日本",
:(中略)
"types"=>["postal_code"]}>,
#"日本, 盛岡駅(岩手)",
:(中略)
"types"=>["train_station", "transit_station", "establishment"]}>,
:(以下略)

IPアドレスの場所を取得

Geocoder.search("210.194.198.60")
[#"210.194.198.60",
"country_code"=>"JP",
"country_name"=>"Japan",
"region_code"=>"19",
"region_name"=>"Kanagawa",
"city"=>"Yokohama",
"zipcode"=>"",
"latitude"=>35.4478,
"longitude"=>139.6425,
"metro_code"=>"",
"areacode"=>""}>]
Geocoder.address("106.187.50.98")
# => "Japan" # IPアドレスによってはこうなってしまう(笑)

距離や角度の演算

t1 = Geocoder.search("東京タワー")[0].geometry['location'].values.join(',')
t2 = Geocoder.search("東京スカイツリー")[0].geometry['location'].values.join(',')
Geocoder::Calculations.distance_between(t1,t2)
# => 8.20771162081327 # km
Geocoder::Calculations.bearing_between(t1,t2)
# => 51.834180520275936 # degree
Geocoder::Calculations.compass_point Geocoder::Calculations.bearing_between(t1,t2)
=> "NE"
# railsと組み合わせるとobj.distance_to みたいなこともできるらしい。

Bing APIを使ってみる

# APIキーが必要。http://www.bingmapsportal.com
Geocoder.configure( :lookup => :bing, :api_key=>'*****' )
Geocoder.search("東京タワー")
# => [#"Location:http://schemas.microsoft.com/search/local/ws/rest/v1", "bbox"=>[35.62964091461487, 139.69803606567407, 35.687581680599976, 139.79308331909155], "name"=>"Tokyo Tower, Japan", "point"=>{"type"=>"Point", "coordinates"=>[35.65861129760742, 139.7455596923828]}, "address"=>{"countryRegion"=>"Japan", "formattedAddress"=>"Tokyo Tower, Japan", "landmark"=>"Tokyo Tower"}, "confidence"=>"High", "entityType"=>"LandmarkBuilding", "geocodePoints"=>[{"type"=>"Point", "coordinates"=>[35.65861129760742, 139.7455596923828], "calculationMethod"=>"Rooftop", "usageTypes"=>["Display"]}], "matchCodes"=>["Good"]}, @cache_hit=nil>]
Geocoder.address("東京タワー")
# => "Tokyo Tower, Japan"

ほかにも、API呼び出しをキャッシュさせたり、Railsと組み合わせることで Hotel.near(“Vancouver, Canada”) みたいなこともできるようになるらしいです。

もっと早く探しておけばよかったなー。