最近まで高精度地図データ整備や交通情報生成の業務に携わっていたmoshiです。
オープンソースの地図データ「OpenStreetMap」ではどんなことができるのかなと、ふと気になって最近調べてました。その中で、最短経路探索・可視化まで手軽にできる「OSMnx」が面白いなと思ったので、紹介したいと思います。
OSMnxとは
OSMnxは、OpenStreetMapから地理空間データをダウンロードし、実世界の道路網やその他の地理空間形状をモデル化・投影・可視化・分析することができるPythonパッケージ。
たった一行のPythonコードで、歩きやすい、運転しやすい、または自転車に乗りやすい都市ネットワークをダウンロードしてモデル化し、簡単に分析して視覚化することができる。
また他のインフラタイプ・公共施設/ポイント・建物の占有面積・標高データ・道路の方位/方向・速度/移動時間なども同様に簡単にダウンロードして操作することができる。
道路情報取得 〜 最短経路探索・可視化手順
愛知県名古屋市中村区を対象地域例として、車両走行道路における最短経路探索・可視化までの手順を以下に紹介します。
0. 環境準備
下記pipコマンドにより必要なライブラリをインストール (可視化用にfoliumも必要)。
pip install osmnx folium
ただし、上記はWindowsでエラーが出ることがあるそうです。 (コンパイル時の問題?)
エラーが出る場合は、以下の方法を試すと良さそうです。
- OSMnxの依存パッケージGDAL・Fionaをwheel形式ファイルにより事前に直接ダウンロード・インストールする。
その後、pipコマンドを利用してインストールpip install osmnx folium
- wheel形式パッケージのGDAL・Fionaダウンロード・インストールは下記記事が参考になります。
GeoPandasのインストールに失敗した場合の対処法
- wheel形式パッケージのGDAL・Fionaダウンロード・インストールは下記記事が参考になります。
- pipではなくAnacondaのcondaコマンドを利用してインストール
conda install osmnx folium
1. 対象地域の道路情報(グラフネットワーク)取得・可視化
import folium
import osmnx as ox
# 対象地域の道路情報取得 (愛知県名古屋市中村区)
query = "Nakamuraku,Nagoya,Aichi,Japan"
G = ox.graph_from_place(query, network_type="drive")
# 道路グラフネットワーク可視化
fmap = ox.plot_graph_folium(G)
fmap.save(outfile="road_network.html")
opts = {"node_size": 5, "bgcolor": "white", "node_color": "blue", "edge_color": "blue"}
ox.plot_graph(G, show=False, save=True, filepath="road_network.png", **opts)
愛知県名古屋市中村区の道路情報は、クエリーを"Nakamuraku,Nagoya,Aichi,Japan"
として、ox.graph_from_place()
関数を利用することで取得できる。
※ その他に、ある座標地点から1km範囲内の道路情報取得、等も可能です (道路情報取得用の関数一覧はこちら)。
PythonライブラリのNetworkXにより、道路情報はグラフネットワークとして格納されます。
⇒ NetworkXについては、こちらのサイトが分かりやすく参考になるかと思います。
道路情報の可視化は下記の2パターンで行うことができる。
-
foliumによるHTML形式での可視化 (road_network.html)
ox.plot_graph_folium()
関数を利用した出力結果
-
matplotlibによるPNG形式での可視化 (road_network.png)
ox.plot_graph()
関数による出力結果
2. 取得道路情報のCSV出力
# 道路グラフネットワークの各ノード・エッジ取得・CSV出力
nodes, edges = ox.graph_to_gdfs(G)
nodes.to_csv("road_network_nodes.csv")
edges.to_csv("road_network_edges.csv")
ox.graph_to_gdfs()
関数により、グラフネットワーク(G)の各ノード・エッジデータを取得できる。
PandasのDataFrameを地理情報データ用に拡張したGeoDataFrame(gdf)形式でデータは取得される。
to_csv()
関数によりCSV形式で各ノード・エッジデータのファイル出力・確認ができる。
3. 任意の二地点間での最短経路探索・可視化
# 最短経路探索
start_point = (35.18253738854321, 136.85996828365532)
start_node = ox.get_nearest_node(G, start_point)
end_point = (35.16163249834248, 136.8824509819242)
end_node = ox.get_nearest_node(G, end_point)
shortest_path = ox.shortest_path(G, start_node, end_node)
# 最短経路探索結果の可視化
new_fmap = ox.plot_route_folium(G, shortest_path, route_map=fmap, color="red")
folium.Marker(location=start_point, tooltip="start").add_to(new_fmap)
folium.Marker(location=end_point, tooltip="end").add_to(new_fmap)
new_fmap.save(outfile="shortest_path_road_network.html")
outfile = "shortest_path_road_network.png"
opts = {"node_size": 5, "bgcolor": "white", "node_color": "blue", "edge_color": "blue"}
ox.plot_graph_route(G, shortest_path, show=False, save=True, filepath=outfile, **opts)
ox.get_nearest_node()
関数で始終点座標の各最近傍ノードを取得し、
ox.shortest_path()
関数により二地点間での最短経路探索が実行される。
最短経路探索結果は以下のように可視化される。
-
foliumによるHTML形式での可視化 (shortest_path_road_network.html)
ox.plot_route_folium()
関数を利用した出力結果
下図のマーカーで示す始点・終点間で問題なく最短経路が算出されていそう。
-
matplotlibによるPNG形式での可視化 (shortest_path_road_network.png)
ox.plot_graph_route()
関数による出力結果
まとめ
OSMnxを利用することで、数行のPythonコードでOSMの道路可視化・分析ができたので、とても便利だなと思いました。
これだけのデータ可視化・分析がオープンソースで手軽に実施できるのは、とてもありがたいことだなと感じます。
最後に最短経路探索&可視化を利用しやすいようにひとまとめにしたコードを記載します。
import os
from pathlib import Path
import folium
import osmnx as ox
# 対象地域検索クエリ (愛知県名古屋市中村区)
query = "Nakamuraku,Nagoya,Aichi,Japan"
# 各種出力ファイルパス
outdir_path = Path(query.replace(",", "_"))
os.makedirs(outdir_path, exist_ok=True)
# 道路グラフネットワーク取得
graphml_outfile = outdir_path / "road_network.graphml"
if not os.path.isfile(graphml_outfile):
# 走行可能な道路グラフネットワークを取得
G = ox.graph_from_place(query, network_type="drive")
# 取得データを再利用目的でGraphml形式にて保存
ox.save_graphml(G, filepath=graphml_outfile)
else:
# 前回取得の道路グラフネットワークを再利用
G = ox.load_graphml(graphml_outfile)
# 道路グラフネットワーク可視化
fmap = ox.plot_graph_folium(G)
folium_outfile = outdir_path / "road_network.html"
fmap.save(outfile=str(folium_outfile))
png_outfile = outdir_path / "road_network.png"
opts = {"node_size": 5, "bgcolor": "white", "node_color": "blue", "edge_color": "blue"}
ox.plot_graph(G, show=False, save=True, filepath=png_outfile, **opts)
# 道路グラフネットワークの各ノード・エッジ取得・CSV出力
nodes, edges = ox.graph_to_gdfs(G)
nodes_csv_outfile = outdir_path / "road_network_nodes.csv"
nodes.to_csv(nodes_csv_outfile)
edges_csv_outfile = outdir_path / "road_network_edges.csv"
edges.to_csv(edges_csv_outfile)
# 最短経路探索
start_point = (35.18253738854321, 136.85996828365532)
start_node = ox.get_nearest_node(G, start_point)
end_point = (35.16163249834248, 136.8824509819242)
end_node = ox.get_nearest_node(G, end_point)
shortest_path = ox.shortest_path(G, start_node, end_node)
# 最短経路探索結果の可視化
new_fmap = ox.plot_route_folium(G, shortest_path, route_map=fmap, color="red")
folium.Marker(location=start_point, tooltip="start").add_to(new_fmap)
folium.Marker(location=end_point, tooltip="end").add_to(new_fmap)
folium_path_outfile = outdir_path / "shortest_path_road_network.html"
new_fmap.save(outfile=str(folium_path_outfile))
path_png_outfile = outdir_path / "shortest_path_road_network.png"
ox.plot_graph_route(
G, shortest_path, show=False, save=True, filepath=path_png_outfile, **opts
)