カテゴリー: Server

RubyでOSM Nominatimを使ってみた

はじめに

こんにちは。カイザーです。今回はRubyから、Open Street MapのNominatimを使ってみましたので、紹介します。

Nominatimは、Open Street Mapのデータを、建物名や住所から検索したり、リバースジオコーディングしたりして、利用するAPIです。
これを、Rubyから簡単に利用できるGemを紹介します。

導入

以下をGemfileに追記します。

gem 'open_street_map'

もしくは、直接インストールします。

$ gem install open_street_map

 

検索

建物や地名のジオ情報を、キーワードや住所によって検索します。検索結果については、こちらのデモページで実際に確認することができます。

建物名による検索の実装例

OpenStreetMap::Clientsearchメソッドを使用することで、検索APIを呼び出せます。
qに建物名や、住所を渡すことで、検索することができます。

require 'open_street_map'
client = OpenStreetMap::Client.new
result = client.search(q: '品川駅', format: 'json', addressdetails: '1', accept_language: 'ja')

以下のようなハッシュがresultに入ります。

[{"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で返って来るため、以下のように内容を取り出すことができます。

result[0]['display_name']  #"品川駅, 第一京浜, 六丁目, 港区, 東京都, 関東地方, 108-8410, 日本"

住所による検索の実装例

住所による検索も可能ですが、番地レベルまで指定すると、検索結果は0件になりました。
うまくできた例を紹介します。

Request:

result = client.search(q: '東京都港区高輪', format: 'json', addressdetails: '1', accept_language: 'ja')

Response:

[{"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:

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:

{"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をそのまま表示すれば良いですが、日本向けの場合は住所表記を逆転する必要があります。
また、”日本”や”○○地方”も表記不要のため、こちらも除去していきます。

# カンマ区切りで配列化した後、リバースし、"○○地方"と"日本"は取り除く
address_array = result['display_name'].split(', ').reverse.select do |item|
  !item.match(/(^.+地方$|^日本$)/)
end
# 連結して文字列化する
address = address_array.join(' ') # "163-8001 東京都 新宿区 緑の橋 東京都庁舎 第一本庁舎"

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を簡単に使用できるため、ぜひ試してみてください。

おすすめ書籍

   

カイザー

シェア
執筆者:
カイザー
タグ: Ruby

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

4週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

1か月 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前