LoginSignup
40
36

More than 3 years have passed since last update.

【Python】OSMnxで車両走行道路の最短経路探索&可視化をしてみる

Last updated at Posted at 2021-04-12

最近まで高精度地図データ整備や交通情報生成の業務に携わっていた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
  • 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()関数を利用した出力結果
    folium_road_network.png
  • matplotlibによるPNG形式での可視化 (road_network.png)
    ox.plot_graph()関数による出力結果
    road_network.png

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()関数を利用した出力結果
    下図のマーカーで示す始点・終点間で問題なく最短経路が算出されていそう。
    shortest_path_road_network.png
  • matplotlibによるPNG形式での可視化 (shortest_path_road_network.png)
    ox.plot_graph_route()関数による出力結果
    folium_shortest_path_road_network.png

まとめ

OSMnxを利用することで、数行のPythonコードでOSMの道路可視化・分析ができたので、とても便利だなと思いました。
これだけのデータ可視化・分析がオープンソースで手軽に実施できるのは、とてもありがたいことだなと感じます。

最後に最短経路探索&可視化を利用しやすいようにひとまとめにしたコードを記載します。

osmnx_search_shortest_path.py
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
)

参考

40
36
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
40
36