はじめに
こんにちは。カイザーです。今回はRubyから、Open Street MapのNominatimを使ってみましたので、紹介します。
Nominatimは、Open Street Mapのデータを、建物名や住所から検索したり、リバースジオコーディングしたりして、利用するAPIです。
これを、Rubyから簡単に利用できるGemを紹介します。
導入
以下をGemfileに追記します。
1 | gem 'open_street_map' |
もしくは、直接インストールします。
1 | $ gem install open_street_map |
検索
建物や地名のジオ情報を、キーワードや住所によって検索します。検索結果については、こちらのデモページで実際に確認することができます。
建物名による検索の実装例
OpenStreetMap::Client
の
search
メソッドを使用することで、検索APIを呼び出せます。
q
に建物名や、住所を渡すことで、検索することができます。
1 2 3 | require 'open_street_map' client = OpenStreetMap::Client.new result = client.search(q: '品川駅', format: 'json', addressdetails: '1', accept_language: 'ja') |
以下のようなハッシュが
result
に入ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | [{"place_id"=>199025592, "licence"=> "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright", "osm_type"=>"relation", "osm_id"=>2106999, "boundingbox"=>["35.6268175", "35.6319685", "139.7377369", "139.7411235"], "lat"=>"35.6293681", "lon"=>"139.739272894919", "display_name"=>"品川駅, 第一京浜, 六丁目, 港区, 東京都, 関東地方, 108-8410, 日本", "class"=>"building", "type"=>"yes", "importance"=>0.621089661829055, "address"=> {"building"=>"品川駅", "road"=>"第一京浜", "hamlet"=>"六丁目", "city"=>"港区", "state"=>"東京都", "region"=>"関東地方", "postcode"=>"108-8410", "country"=>"日本", "country_code"=>"jp"}}] |
Hashで返って来るため、以下のように内容を取り出すことができます。
1 | result[0]['display_name'] #"品川駅, 第一京浜, 六丁目, 港区, 東京都, 関東地方, 108-8410, 日本" |
住所による検索の実装例
住所による検索も可能ですが、番地レベルまで指定すると、検索結果は0件になりました。
うまくできた例を紹介します。
Request:
1 | result = client.search(q: '東京都港区高輪', format: 'json', addressdetails: '1', accept_language: 'ja') |
Response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | [{"place_id"=>247238264, "licence"=> "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright", "osm_type"=>"node", "osm_id"=>6126523480, "boundingbox"=>["35.6340705", "35.6341705", "139.7379083", "139.7380083"], "lat"=>"35.6341205", "lon"=>"139.7379583", "display_name"=>"高輪, 第一京浜, 六丁目, 港区, 東京都, 関東地方, 108-8612, 日本", "class"=>"tourism", "type"=>"information", "importance"=>0.001, "icon"=> "https://nominatim.openstreetmap.org/images/mapicons/amenity_information.p.20.png", "address"=> {"information"=>"高輪", "road"=>"第一京浜", "hamlet"=>"六丁目", "city"=>"港区", "state"=>"東京都", "region"=>"関東地方", "postcode"=>"108-8612", "country"=>"日本", "country_code"=>"jp"}}] |
海外の住所では、詳細な住所でもうまくいくケースが多いようです。
リバースジオコーディング
緯度経度情報から、ジオ情報を取得するAPIです。リバース結果については、こちらのデモページで実際に確認することができます。
実装例
Request:
1 2 3 4 | require 'open_street_map' client = OpenStreetMap::Client.new result = client.reverse(format: 'json', lat: '35.689495', lon: '139.691709', zoom: 18, accept_language: 'ja') |
zoom
は、ジオ情報の詳細レベルを指定します。0が国レベルで、18が建物レベルです。
必ず、緯度経度近くの建物名が欲しい時は、
18
を設定しておきます。逆に、大まかな地域だけで良い場合は、
10
(City)を設定しておくと良いでしょう。
詳細レベルについては、先ほどのデモページで試しながら、決めるのがオススメです。
Response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | {"place_id"=>95331758, "licence"=> "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright", "osm_type"=>"way", "osm_id"=>89877471, "lat"=>"35.68949565", "lon"=>"139.691709362688", "display_name"=>"東京都庁舎 第一本庁舎, 緑の橋, 新宿区, 東京都, 関東地方, 163-8001, 日本", "address"=> {"townhall"=>"東京都庁舎 第一本庁舎", "pedestrian"=>"緑の橋", "city"=>"新宿区", "state"=>"東京都", "region"=>"関東地方", "postcode"=>"163-8001", "country"=>"日本", "country_code"=>"jp"}, "boundingbox"=>["35.6890004", "35.6900025", "139.691372", "139.6920481"]} |
このように、近くに建物があれば、建物名までレスポンスされます。精度が高く、驚きました。
近くに建物がない場合は、市町村区レベルまではレスポンスされます。
住所の整形
海外向けの場合、レスポンスの
display_name
をそのまま表示すれば良いですが、日本向けの場合は住所表記を逆転する必要があります。
また、”日本”や”○○地方”も表記不要のため、こちらも除去していきます。
1 2 3 4 5 6 | # カンマ区切りで配列化した後、リバースし、"○○地方"と"日本"は取り除く address_array = result['display_name'].split(', ').reverse.select do |item| !item.match(/(^.+地方$|^日本$)/) end # 連結して文字列化する address = address_array.join(' ') # <span class="s1">"163-8001 東京都 新宿区 緑の橋 東京都庁舎 第一本庁舎"</span> |
Open Street Map Nominatimの制約事項
デフォルトでは、
https://nominatim.openstreetmap.org/
にリクエストするようになっていますが、この場合はNomatim Usage Policyが適用されます。
https://operations.osmfoundation.org/policies/nominatim/
制約は、個人的・小規模なサービスであればクリアできそうですが、大規模なサービスの場合は、同APIを提供するMapQuestのサブスクリプション等を利用するか、自前でNomatimサーバを構築する必要があります。
具体的には、以下のような制約があります。
- 1IPからの1秒間に1回のリクエストまで
- 1IPからの並列リクエスト、分散インスタンスからのリクエストの禁止
- 同じリクエストを何度も送信することは禁止。(キャッシュさせる必要がある)
- オートコンプリートの実装禁止
- データを全部取得するような、体系的なクエリの禁止
- 詳細ページのスクレイピング禁止
- ODbLライセンスを守ること
さいごに
このGemは、Nominatimを簡単に使用できるため、ぜひ試してみてください。