PythonでOSMデータを触る方法
PythonでOpenStreetMap(OSM)のデータを触る方法を調査する。
OSMのデータを編集したりするのであればWeb上などで可能だが、今回はOSMに登録されているスポット情報をPythonで取得する方法を調べる。
具体的には、緯度軽度などで範囲を指定し、その範囲に存在する店の情報を取得するようなことをPythonで行う。
実行環境は、なるべくローカルを汚したくないので、Google Colabを使用したい。
2年くらい前にもOSMのデータを触る方法を調べてnoteに書いたことがあった。
これらについても軽くまとめ直す。overpass turboを使用する
overpass turboというサービスを利用してデータを取得する。
overpass turboでデータを取得するには、Overpass QLというものを使用する必要がある。
[out:json];
(
node['shop'~'.'](35.002374919567885,135.75808220871573,35.00499106117767,135.7612159415514);
);
(._;>;);
out body;
四条烏丸周辺に登録されているshop
タグの付いたNode情報を取得するOverpass QL
これをPythonで実行しようと思うと、以下のようなコードで行う。
min_lat = 35.002374919567885
min_lon = 135.75808220871573
max_lat = 35.00499106117767
max_lon = 135.7612159415514
query = ""
query += "[out:json];"
query += "("
query += "node['shop'~'.'](" + str(min_lat) + "," + str(min_lon) + "," + str(max_lat) + "," + str(max_lon) + ");"
query += ");"
query += "(._;>;);"
query += "out body;"
s_quote = urllib.parse.quote(query)
url = "http://overpass-api.de/api/interpreter?data=" + s_quote
req = urllib.request.Request(url)
with urllib.request.urlopen(req) as res:
body = json.load(res)
json_data = body["elements"]
for data in json_data:
print(data["tags"]["shop"])
overpass turboを使用する場合、note記事にも書いていたが、大量のデータを取得しようとするとoverpass turboに大量のリクエストを投げる必要が発生してしまう。
※一度に大量のデータを取得しようとするとタイムアウトが発生して取得できなくなる。
また、overpass turboに登録されているOSMの情報がいつのタイミングのものか分からないため、データを取得するタイミングが異なる場合、地図情報が更新されていて別の内容になる可能性がある。
そのため、OSMのデータをローカルにダウンロードして、そこから取得できるようにした方が良いかなと考えた。
※overpass turboはOSSになっているみたいなので、ローカルにサーバを立てて使用することもできるかもしれないが、そこまでは調べられていない。
osmreadを使用する
OSMのデータをローカルにダウンロードして、Pythonのライブラリのosmreadを使用して読み込む。
OSMのデータはgeofabrikというサイトからダウンロードして使用した。
データはpbfという拡張子になっている。
実際のコードは以下。
from osmread import parse_file, Node
file_path = join(dirname(realpath(__file__)), '../files/kansai-latest.osm.pbf')
file = parse_file(file_path)
for data in file:
if isinstance(data, Node):
if len(data.tags.keys() & {'shop'}) > 0:
one_data = {
'tags':data.tags,
'lon': data.lon,
'lat': data.lat
}
Osmread is a simple library for reading OpenStreetMap data files in Python. It supports XML and PBF file formats as inputs. It is not designed for fast processing of big planet dumps but can be used for simple processing of smaller files such as regional extracts.
If you need fast processing of large files look at imposm.parser library.
このように書かれている。
高速に処理はできないので、大規模なファイルに対してはimposm.parserというライブラリを使用した方がいいらしい。
高速処理以外にも、
osmreadでは、広範囲の情報の入ったpbfファイルから緯度軽度などで範囲を絞ってデータを取得することができなさそうだった。
なので、狭い範囲からデータを取得するときには、必要な範囲のみのpbfファイルを用意する必要がある。
pbfファイルから範囲を絞る方法をググったらいくつか記事が出てきた。
osmreadで取得されるNode情報のデータの形式もメモしておく。
Node(
id=...,
version=...,
changeset=...,
timestamp=...,
uid=...,
tags={...},
lon=...,
lat=...
)
__parse_**
のメソッドを見たら、他のWayなどのデータ構造も確認できる。
imposm.parserを使用する
osmreadのREADMEで説明されているimposm.parserを使用してみる。
使おうと思ったが、Python3をサポートしておらず、Google Colabにインストールすることができなかった。
GitHubのIssueにPython3サポートについてのIssueが立っていた。
IssueのコメントでPython3で動くようにしたもののリポジトリのリンクが投稿されていた。
リポジトリを使ってインストールしようとしたが、上手くインストールできなかった。
時間があれば、再度チャレンジする。
pyrosmを使用する
Pythonのライブラリであるpyrosmを使用する。
pyrosmではpbfをダウンロードするメソッドも用意されている。
from pyrosm import get_data
fp = get_data("kansai")
print(fp)
/tmp/pyrosm/kansai-latest.osm.pbf
ダウンロードされたpbfファイルから緯度軽度で範囲を絞る方法。
import pyrosm
osm = pyrosm.OSM(fp ,bounding_box=[135.75808220871573,35.002374919567885,135.7612159415514,35.00499106117767])
この際、緯度軽度の指定が、[lon, lat, lon, lat]
の指定方法になっているのに注意が必要。
指定した範囲のOSM情報からスポットの情報を取得する。
custom_filter = {"shop": True, 'amenity': True}
pois = osm.get_pois(custom_filter=custom_filter)