Python の overpy を用いて OpenStreetMap のデータをダウンロード
【概要】Python の overpy ライブラリを使用して、OpenStreetMap から地図データをダウンロードする方法を解説する。取得したデータは、地図アプリケーションの開発、経路探索、地理情報の分析などに活用できる。本手順は Windows のパソコンで動作する。データ取得はインターネット経由で行われるため、GPU 搭載機と CPU のみの機種のいずれでも動作する。
OpenStreetMap (https://www.openstreetmap.org) は、世界規模で利用可能な無料のオンライン地図データベースである。誰でも自由に利用・編集でき、地図データを様々な形式で取得できる。
【目次】
前準備
Python 3.12 のインストール(Windows 上) [クリックして展開]
以下のいずれかの方法で Python 3.12 をインストールする。Python がインストール済みの場合、この手順は不要である。
方法1:winget によるインストール
管理者権限のコマンドプロンプトで以下を実行する。管理者権限のコマンドプロンプトを起動するには、Windows キーまたはスタートメニューから「cmd」と入力し、表示された「コマンドプロンプト」を右クリックして「管理者として実行」を選択する。
winget install --id Python.Python.3.12 -e --scope machine --silent --accept-source-agreements --accept-package-agreements --override "/quiet InstallAllUsers=1 PrependPath=1 Include_test=0 Include_pip=1 Include_launcher=1 InstallLauncherAllUsers=1 TargetDir=\"C:\Program Files\Python312\""
powershell -Command "$p='C:\Program Files\Python312'; $s=\"$p\Scripts\"; $m=[Environment]::GetEnvironmentVariable('Path','Machine'); if($m -notlike \"*$s*\") { [Environment]::SetEnvironmentVariable('Path', \"$p;$s;$m\", 'Machine') }"
--scope machine を指定すると、全ユーザー向けにインストールされる。このオプションの実行には管理者権限が必要である。インストール完了後、コマンドプロンプトを再起動すると PATH が設定される。
方法2:インストーラーによるインストール
- Python 公式サイト(https://www.python.org/downloads/)にアクセスし、「Download Python 3.x.x」ボタンから Windows 用インストーラーをダウンロードする。
- ダウンロードしたインストーラーを実行する。
- 初期画面の下部に表示される「Add python.exe to PATH」にチェックを入れてから「Customize installation」を選択する。このチェックを入れないと、コマンドプロンプトから
pythonコマンドを実行できない。 - 「Install Python 3.12 for all users」にチェックを入れ、「Install」をクリックする。
インストールの確認
コマンドプロンプトで以下を実行する。
python --version
バージョン番号(例:Python 3.12.x)が表示されればインストールは成功である。「'python' は、内部コマンドまたは外部コマンドとして認識されていません。」と表示される場合は、インストールが完了していない。
overpy ライブラリのインストール(Windows 上)
overpy は Python のライブラリであり、C/C++ コンパイラなどの追加ツールは必要としない。Python がインストールされていれば、以下の手順で導入できる。
- Windows で、管理者権限でコマンドプロンプトを起動する(手順:Windows キーまたはスタートメニュー >
cmdと入力 > 右クリック > 「管理者として実行」)。 - 以下のコマンドを実行する。
pip install -U overpy
Google マップを用いた緯度経度の取得
データをダウンロードする際には、対象地域の緯度と経度を指定する。ここでは、Google マップを使用して緯度経度を調べる方法を説明する。
- Google マップ(https://www.google.co.jp/maps)を開く。
- 目的の場所に移動する。
- 地図上で右クリックし、「この場所について」を選択する。
- 表示される緯度と経度を記録する。
Python の overpy を用いた OpenStreetMap データのダウンロード
Python プログラムの実行方法(Windows)
- コマンドプロンプトで python と入力して対話モードを起動するか、py(Python ランチャー)を用いる。
- 各コード片は、対話モードに 1 行ずつ入力して実行できる。
【サイト内の関連ページ】 Python のまとめ: 別ページ »
Overpass API と OpenStreetMap のデータ構造
Overpass API は、OpenStreetMap のデータを検索・取得するための読み取り専用 API である。クエリを送信すると、条件に合致するデータが返される。overpy は、この API を Python から利用するためのライブラリである。
OpenStreetMap のデータは、以下の 3 種類の要素で構成される。
- ノード(Node):緯度・経度を持つ点。店舗や信号機などの地物を表す。
- ウェイ(Way):複数のノードを順に結んだ線。道路や建物の輪郭を表す。
- リレーション(Relation):複数の要素をグループ化したもの。バス路線などを表す。
各要素にはタグ(属性情報)を付与できる。タグは「キー=値」の形式で、例えば highway=primary は「主要道路」を意味する。
本資料で使うクエリの読み方
- 要素の種類とタグのキーは別物:
way(...)は「ウェイ(線)」という要素の種類を指定する。[highway]は「highway というタグのキーを持つもの」に絞り込む。 - 範囲の指定順序:
way(南緯, 西経, 北緯, 東経)の順で範囲を指定する。順序を入れ替えると、エラーにならず異なる範囲を取得する。 - クエリ末尾:
(._;>;);は、取得したウェイと、それを構成するノードをまとめて取得する指定である。これによりresult.nodesに各ウェイの構成ノードが入る。out body;は属性付きで出力する指定である。
注意:公開されている Overpass API サーバ(overpass-api.de)には利用制限がある。1 日あたり約 10,000 リクエスト、ダウンロード量 1GB 以下が目安である。大量のデータを取得する場合は、Planet.osm の利用を検討する。
データのダウンロード手順
- 緯度と経度の設定
前準備で調べた緯度と経度を Python の変数に設定する。
lat = 34.4461 lon = 133.2315
- 取得するタグのキーの設定
取得対象を絞り込むタグのキーを指定する。ここでは「highway」(道路)のキーを持つウェイを取得する。
key = 'highway'
- データのダウンロードと表示
指定した緯度経度を中心に、緯度・経度ともに ±0.01 度の範囲内にあるデータを取得する(一辺は緯度方向で約 2.2km。経度方向の実距離は緯度によって変化し、高緯度ほど短くなる)。
import overpy api = overpy.Overpass() result = api.query(""" way(%f, %f, %f, %f) [%s]; (._;>;); out body; """ % (lat - 0.01, lon - 0.01, lat + 0.01, lon + 0.01, key)) for way in result.ways: print("Name: %s" % way.tags.get("name", "n/a")) print(" Highway: %s" % way.tags.get("highway", "n/a")) print(" Nodes:") for node in way.nodes: print(" Lat: %f, Lon: %f" % (node.lat, node.lon))
各道路の名前とノードの緯度経度が表示される。指定した範囲に道路がない場合、出力は空になる。出力が空のときは、(1) 指定したタグのキーが正しいか、(2) 範囲が狭すぎないか、を確認する。
- ウェイ・ノードの件数の確認
取得したウェイとノードの件数を確認する。
len(...)はリストの要素数を返す Python の組み込み関数である。print(len(result.ways)) print(len(result.nodes)) - ウェイデータの確認
取得したウェイ(道路などの線形データ)の一覧を確認する。
result.ways
各行が 1 つのウェイを表す。
nodes = [...]内の数値は、そのウェイを構成するノードの ID である。 - ノードデータの確認
取得したノード(点データ)の一覧を確認する。
result.nodes
各行が 1 つのノードを表し、ID、緯度、経度を持つ。
まとめ
- ウェイ:2 個以上のノード ID で構成される線形データ。
- ノード:ID、緯度、経度を持つ点データ。
- リレーション:複数の要素をグループ化したデータ(本資料の例では取得対象に含めていない)。
演習
本文のデータダウンロード手順を題材に、以下の演習を行う。各演習は、本文のコードを変更して実行する。
演習1.取得範囲の変更
手順
- 本文「データのダウンロードと表示」のコードを実行し、続いて
len(result.ways)でウェイの件数を確認する。 - 範囲を ±0.01 度から ±0.02 度に変更して、再度実行する。
import overpy api = overpy.Overpass() result = api.query(""" way(%f, %f, %f, %f) [%s]; (._;>;); out body; """ % (lat - 0.02, lon - 0.02, lat + 0.02, lon + 0.02, key)) print(len(result.ways))
ヒント
- 本文「範囲の指定順序」のとおり、引数は南緯・西経・北緯・東経の順である。
考察ポイント
- 範囲を広げたとき、取得されたウェイの件数がどのように変化したか。
演習2.取得するタグのキーの変更
手順
- 本文「取得するタグのキーの設定」の
keyをhighwayからbuilding(建物)に変更する。key = 'building' - 本文「データのダウンロードと表示」のコードのうち、表示部分の参照キーを
buildingに変更して実行する。import overpy api = overpy.Overpass() result = api.query(""" way(%f, %f, %f, %f) [%s]; (._;>;); out body; """ % (lat - 0.01, lon - 0.01, lat + 0.01, lon + 0.01, key)) for way in result.ways: print("Name: %s" % way.tags.get("name", "n/a")) print(" Building: %s" % way.tags.get("building", "n/a"))
ヒント
- 本文の表示用コードは
way.tags.get("highway", "n/a")を参照している。buildingの値を表示するため、参照キーをbuildingに変更する。
考察ポイント
- キーを変更したとき、取得される要素の種類がどのように変わったか。
- 同じ範囲で
highwayとbuildingのどちらのウェイが多かったか。
演習3.ノードとウェイの対応の確認
手順
- 本文「データのダウンロードと表示」のコードを実行する。
- 最初のウェイを構成するノード数を確認する。
way = result.ways[0] print(len(way.nodes)) - 取得されたノードの総数を確認する。
print(len(result.nodes))
ヒント
- 本文「クエリ末尾」のとおり、
(._;>;);によって各ウェイの構成ノードがresult.nodesに含まれる。
考察ポイント
- 1 つのウェイが何個のノードで構成されていたか。
- ウェイの総数とノードの総数を比べ、なぜノードの方が多いかを説明する。