4月末に近づいた頃にようやく今月初めての記事。あまりに書かなすぎた。
だいぶ前になるけど、OpenStreetMap(OSM)からガソリンスタンドの位置情報を取得してプロットしたデータアートめいた画像を見た。なにこれかっこいい。
Population density through the number of gas stations in Europe. #dataviz @AGE_Oficial @mipazos @simongerman600 @openstreetmap pic.twitter.com/eIUx2yn7ej
— Dominic Royé (@dr_xeo) 2018年2月25日
地図ではなく全くの白紙にプロットしてるにも関わらず、欧州・北アフリカの海岸線や主要道路が見て取れる。
あまりにかっこいい画だったので日本でも似たようなことをやってみた。緯度経度をそのまま座標としてプロットしただけで日本列島とわかる姿になる。沖縄だけでなく東京都の島嶼部なんかも確認できる。
今回のリポジトリはこちら。
OSMからデータを取得するライブラリ
openstreetmap python
などと検索するとosmapiがヒットする。しかしこちらはOSMにデータを書き込む用途に向いたライブラリ。
データの取得だけが目的なのであればoverpyを使うのがよい。
とあるようにOverpass API
のPython用ラッパーだ。
Overpass API is 何
公式Wiki - Overpass APIには以下のようにある。
Overpass API(別名 OSM3S)とは読み出し専用のAPIであり、OSM地図データの中から個別に選択された部分を取り出します。Webを介したデータベースとして動作します。利用者はAPIに対してクエリを送り、クエリに対応したデータ セットを受け取ります。
クエリはOverpass QL
と呼ばれ、OSMからデータを読み出すためのSQLのようなものといえる。
OSMのデータには、駅や店舗、施設などを表すノード、道路や河川、鉄道路線を表すウェイ、ノードとウェイを組み合わせたリレーションがあるようだ。
Overpass QLを書いてみる
東京都にあるガソリンスタンドを抽出するOverpass QLを書いてみる。
area["name"~"東京都"]; node(area)["amenity"="fuel"]; out;
WebベースのOSMデータマイニングツールOverpass turboでの(実行結果)http://overpass-turbo.eu/s/yb9にアクセスしてクエリを実行すると取得結果をOSM上で確認できる。
データ取得範囲は area["name"~"東京都|神奈川県"];
のように|で区切ることで複数指定できる。新宿区といったより細かい範囲も指定可能。
node(area)["amenity"="fuel"];
でノードの種類を指定している。OSMではキー=値
という形のタグでオブジェクトに情報を与えている。OSMのタグ情報はtaginfoが詳しい。
鉄道の駅であれば "railway"="station"
、コンビニは"shop"="convenience"
と指定する。ただ、コンビニの場合はコンビニエンスストア以外の小さな商店なども含まれているようなので、主要なコンビニリストを作ってノードの他のタグname
をフィルタリングしたりする必要がありそう。
overpyを使ってみる
overpyでデータを取得してシリアライズするスクリプトを書いた。
pickle
ではシリアライズできなかったのでpickleの拡張モジュールであるdillを使った。
日本全国を範囲指定するので、都道府県を並べたテキストファイルの中身を結合してクエリに突っ込んでいる。
overpass_plot/script at master · NaoY-2501/overpass_plot · GitHub
from datetime import datetime import os import dill import overpy def get_prefs(): prefs = '' with open('pref.txt', mode='r') as f: for row in f.readlines(): prefs += '{}|'.format(row.rstrip()) return prefs[:-1] def input_node_info(): key = input('Input node key:') tag = input('Input tag of key:') return key, tag def save_result(result, key, tag): now = datetime.now() date = '{}{}{}'.format(now.year, now.month, now.day) filename = 'data/{}-{}_{}.pkl'.format(date, key, tag) print('Save as {}'.format(filename)) if not os.path.exists(filename): with open(filename, 'wb') as f: dill.dump(result, f) print('Save complete.') def fetch_result(key, tag): api = overpy.Overpass() prefs = get_prefs() query = ( 'area["name"~"{prefs}"];\n' 'node(area)["{key}"="{tag}"];\n' 'out;' ).format(prefs=prefs, key=key, tag=tag) print('Fetch query...') result = api.query(query) print('Fetch complete') save_result(result, key, tag) def main(): key, tag = input_node_info() fetch_result(key, tag) if __name__ == "__main__": main()
bokehでプロットする
可視化にはズームして、分布を細かく見たいという理由でbokehを使った。実際にやってみると重くてズームしづらいことに気づいた。bokehがGitHub上ではプレビューできないので保存した画像を貼る。
駅の分布
駅をプロットするので自然と路線そのものが浮かび上がっている。
コンビニの分布
流石にコンビニ全国各地にありすぎだろ……。小さい商店なんかも含まれてることが想像できるので、これは見直す必要がある。
というわけでOSMラッパーライブラリOverpy
の紹介と実験結果の話だった。