5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonで爆速ルート検索!R5pyの構築とR5Rとの違いを紹介!

Last updated at Posted at 2025-11-27

はじめに

国土交通省からデータアナリストとして株式会社onerootsへ転職した小池と申します。
前回の記事では、Rを用いて大量の経路検索が高速に算出できる「R5R」についてご紹介しました。

今回はそのpython版である「R5py」の使い方や、「R5R」との実装機能の違い・どちらを使うべきかご紹介していきます。
※一部、前回の記事と内容が重複する部分もありますのでご了承ください。

R5とは?

R5(Rapid Realistic Routing on Real-world and Reimagined networks) は、Conveyalが開発したオープンソースの経路検索エンジンで、到達圏解析複数の出発地・目的地の組み合わせの計算を高速に実行できる特徴があります。これにより、都市スケールの大規模な分析でも短時間で結果を得ることができます。

このR5を、Rのライブラリとして扱えるようにしたのが、Ipea(Institute for Applied Economic Research)が開発したR5Rです。さらにこのR5Rを参考にpythonのライブラリとして実装されたのがR5pyです。今回は、このR5pyの環境構築と分析方法について詳しくご紹介していきます。

R5pyの環境構築

R5pyは、R5Rと同様R5を内部で使用しているため、Javaの実行環境(JDK)も別途必要です。ここでは、Dockerを使った環境構築方法をご紹介します。以下は、R5py環境を構築するためのDockerfileです。

Dockerfile
FROM python:3.12.2-bullseye

RUN apt-get update && apt-get install -y \
    libcurl4-openssl-dev \
    libssl-dev \
    libxml2-dev \
    libudunits2-dev \
    libgdal-dev \
    libgeos-dev \
    libproj-dev \
    gdal-bin \
    curl \
    pv \
    && rm -rf /var/lib/apt/lists/*

RUN curl -L -o /tmp/OpenJDK21U-jdk_x64_linux_hotspot_21.0.5_11.tar.gz https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.5%2B11/OpenJDK21U-jdk_x64_linux_hotspot_21.0.5_11.tar.gz \
    && mkdir -p /usr/lib/jvm \
    && tar -xzf /tmp/OpenJDK21U-jdk_x64_linux_hotspot_21.0.5_11.tar.gz -C /usr/lib/jvm \
    && rm /tmp/OpenJDK21U-jdk_x64_linux_hotspot_21.0.5_11.tar.gz

ENV JAVA_HOME="/usr/lib/jvm/jdk-21.0.5+11"
ENV PATH="$JAVA_HOME/bin:$PATH"

WORKDIR /py

RUN pip install geopandas==1.0.1 pandas==2.2.1 shapely==2.0.4 r5py==1.0.7

上記のDockerfileを作成後、以下のコマンドを実行すると構築が完了します。

R5py
docker build -t r5py_accessibility .

データ準備

次に、解析を行うために OpenStreetMapの地図データと、GTFSの公共交通データが必要となります。今回も徳島県つるぎ町を対象として解析を行いたいと思います。
なお、坂道を考慮した解析を行いたい場合はTIF形式の標高データも必要になりますが、こちらは前回の記事で紹介しているため、今回は割愛します。

①OpenStreetMap

最初に分析する対象範囲のOpenStreetMapを入手するため、以下のサイトから四国の.osm.pbfファイルをダウンロードします。

ダウンロードした四国全体のデータをそのまま使うことも可能ですが、処理時間を短縮するために、つるぎ町だけを抽出して使用します。抽出ツールについては、こちらのConveyalの公式ドキュメントに載っていますので参考にしてみてください。

②GTFS

続いて、つるぎ町の公共交通データ(GTFS)を入手します。今回は、以下のサイトからつるぎ町のGTFSデータをダウンロードします。

③出発地・目的地

最後に、出発地・目的地の位置情報を記載したcsvファイルを作成します。
今回は、以下のようなcsvを作成します。

「出発地.csv」

id lat lon
出発地1 34.040408 134.067784
出発地2 34.000502 134.08444
出発地3 34.035032 134.06151

「目的地.csv」

id lat lon
町役場本庁 34.03734 134.064147
町役場支所 33.95496 134.064578
道の駅 34.042613 134.060105

分析方法

次に、R5pyを用いた具体的な分析方法についてご紹介します。今回は以下の3つについて分析を行います。
(1)移動時間の算出
(2)詳細な移動経路・交通手段別移動時間の算出
(3)到達圏解析

(1)移動時間の算出

まず、全ての出発地から全ての目的地までの移動時間を算出します。以下のコードを実行することで、移動時間を計算できます。

python(ttm.py)
import r5py
import pandas as pd
import geopandas as gpd
import datetime
import os
from shapely.geometry import Point
from pathlib import Path

#OSMとGTFSを読み込んで、ネットワークを作成
tn = r5py.TransportNetwork(
    osm_pbf = Path("徳島県つるぎ町(抽出済み).osm.pbf"),
    gtfs = Path("feed_tsurugitown_tsurugitownbus_20240401_20240325151601.zip"),
    #坂道を考慮したい場合は以下にTIFを記載
    #elevation_model = Path("徳島県つるぎ町.tif")
)

#出発地と目的地のデータを読みこんで、GeoDataFrame形式に変換
origins_csv = Path("出発地.csv")
destinations_csv = Path("目的地.csv")

origins = pd.read_csv(origins_csv)
destinations = pd.read_csv(destinations_csv)

orig_geometry = [Point(lon, lat) for lon, lat in zip(origins["lon"], origins["lat"])]
orig_gdf = gpd.GeoDataFrame(origins, geometry=orig_geometry, crs="EPSG:4326")

dest_geometry = [Point(lon, lat) for lon, lat in zip(destinations["lon"], destinations["lat"])]
dest_gdf = gpd.GeoDataFrame(destinations, geometry=dest_geometry, crs="EPSG:4326")

# 条件設定
transport_modes = [r5py.TransportMode.TRANSIT] # 公共交通で移動
departure = datetime.datetime(2025, 3, 15, 7, 45, 0) #出発時間の指定
speed_walking = 4.8 # 徒歩の速度(km/h)
max_time_walking = datetime.timedelta(minutes=300)# 徒歩の最大移動時間(分)
max_time = datetime.timedelta(minutes=300) # トリップの最大移動時間(分)

# 移動時間の算出
travel_time_matrix = r5py.TravelTimeMatrix(
    tn,
    origins = orig_gdf,
    destinations = dest_gdf,
    transport_modes = transport_modes,
    departure = departure,
    departure_time_window = datetime.timedelta(minutes=1),
    speed_walking = speed_walking,
    snap_to_network = True,
    max_time_walking = max_time_walking,
    max_time = max_time
)

travel_time_matrix.to_csv("ttm_result.csv", index=False,encoding="utf-8-sig")

このpythonスクリプトをDockerで実行するには、以下のコマンドを使用します。

Docker実行コマンド
docker run --rm -v ${PWD}:/py -w /py r5py_accessibility python ttm.py

計算が完了すると、ttm_result.csv に以下のような形式で移動時間(分)が出力されます。

from_id to_id travel_time
出発地1 町役場本庁 6
出発地1 町役場支所 36
出発地1 道の駅 15
出発地2 町役場本庁 79
出発地2 町役場支所 36
出発地2 道の駅 89
出発地3 町役場本庁 7
出発地3 町役場支所 36
出発地3 道の駅 15

(2)詳細な移動経路・交通手段別移動時間の算出

先ほどの結果では、移動全体にかかる所要時間のみが出力され、移動経路や徒歩・バスといった各移動手段ごとの所要時間は含まれていませんでした。ここでは、徒歩・バス・待ち時間などの内訳も含めた移動時間を取得します。徒歩速度や最大移動時間などの条件は前回と同じまま、以下のコードを実行することで取得できます。

python
detailed_itineraries = r5py.DetailedItineraries(
    tn,
    origins = orig_gdf,
    destinations = dest_gdf,
    departure = departure,
    departure_time_window = datetime.timedelta(minutes=5),
    transport_modes = transport_modes,
    snap_to_network = True,
    force_all_to_all = True,
    speed_walking = speed_walking,
    max_time_walking = max_time_walking,
    max_time = max_time
)

detailed_itineraries.to_csv("detailed_result.csv", index=False,encoding="utf-8-sig")

# geojsonで保存したい場合は以下を実行
'''
detailed_itineraries["mode"] = detailed_itineraries.transport_mode.astype(str)
detailed_itineraries["travel_time"] = detailed_itineraries.travel_time.apply(lambda t: round(t.total_seconds() / 60.0, 2))
detailed_itineraries["wait_time"] = detailed_itineraries.wait_time.apply(lambda t: round(t.total_seconds() / 60.0, 2))
detailed_itineraries.to_file("detailed_result.geojson")
'''

計算が完了すると、以下のような結果が出力されます。出発地からバス停までの徒歩時間や公共交通の乗車時間、待ち時間、乗車した路線番号、乗降したバス停番号、各移動手段ごとの経路情報などを取得できます。
また、optionは移動の候補を、segmentはその候補内での移動順序を表しています。これにより、複数の移動手段ごとの所要時間を、経路と合わせて可視化することも可能です。

$\textsf{from_id}\hspace{1em}$ $\textsf{to_id}\hspace{3em}$ $\textsf{option}$ $\textsf{segment}$ $\textsf{transport_mode}\hspace{2em}$ $\textsf{departure_time}$ $\textsf{distance}\hspace{2em}$ $\textsf{travel_time}\hspace{2em}$ $\textsf{wait_time}\hspace{2em}$ $\textsf{feed}\hspace{23em}$ $\textsf{agency_id}\hspace{2em}$ $\textsf{route_id}$ $\textsf{start_stop_id}$ $\textsf{end_stop_id}$ $\textsf{geometry}\hspace{13em}$
出発地1 町役場本庁 0 0 TransportMode.WALK 495.876 0 days 00:06:17 0 days 00:00:00 LINESTRING (134.0677325 34.0404837,・・・)
出発地1 町役場本庁 1 0 TransportMode.WALK 2025/3/15 7:45 460.39 0 days 00:05:51 0 days 00:00:00 LINESTRING (134.0677325 34.0404837,・・・)
出発地1 町役場本庁 1 1 TransportMode.BUS 2025/3/15 7:52 641.6566235 0 days 00:02:00 0 days 00:01:05 feed_tsurugitown_tsurugitownbus_20240401_20240325151601 7000020364681 10 28_02 26_02 LINESTRING (134.0643179 34.0376819,・・・)
出発地1 町役場本庁 1 2 TransportMode.WALK 2025/3/15 7:55 709.311 0 days 00:08:58 0 days 00:00:00 LINESTRING (134.0628277 34.0321073,・・・)
出発地1 町役場支所 0 0 TransportMode.WALK 13850.141 0 days 02:53:35 0 days 00:00:00 LINESTRING (134.0677325 34.0404837,・・・)
出発地1 町役場支所 1 0 TransportMode.WALK 2025/3/15 7:45 460.39 0 days 00:05:51 0 days 00:00:00 LINESTRING (134.0677325 34.0404837,・・・)
出発地1 町役場支所 1 1 TransportMode.BUS 2025/3/15 7:52 11778.21714 0 days 00:29:00 0 days 00:01:05 feed_tsurugitown_tsurugitownbus_20240401_20240325151601 7000020364681 10 28_02 10_02 LINESTRING (134.0643179 34.0376819,・・・)
出発地1 町役場支所 1 2 TransportMode.WALK 2025/3/15 8:22 309.929 0 days 00:03:54 0 days 00:00:00 LINESTRING (134.0634543 33.9521764,・・・)
出発地1 道の駅 0 0 TransportMode.WALK 1217.266 0 days 00:15:23 0 days 00:00:00 LINESTRING (134.0676386 34.0402084,・・・)
出発地1 道の駅 1 0 TransportMode.WALK 2025/3/15 7:45 460.39 0 days 00:05:51 0 days 00:00:00 LINESTRING (134.0677325 34.0404837,・・・)
出発地1 道の駅 1 1 TransportMode.BUS 2025/3/15 7:52 641.6566235 0 days 00:02:00 0 days 00:01:05 feed_tsurugitown_tsurugitownbus_20240401_20240325151601 7000020364681 10 28_02 26_02 LINESTRING (134.0643179 34.0376819,・・・)
出発地1 道の駅 1 2 TransportMode.WALK 2025/3/15 7:55 1566.456 0 days 00:19:48 0 days 00:00:00 LINESTRING (134.0628277 34.0321073,・・・)
出発地2 町役場本庁 0 0 TransportMode.WALK 6298.544 0 days 01:18:58 0 days 00:00:00 LINESTRING (134.0853198 34.0000355,・・・)

(3)到達圏解析

最後に、到達可能な範囲を算出します。今回は、出発地3から5分単位で0〜15分までの到達圏を計算します。以下のコードを実行することで、到達可能な範囲を算出・地図で表示することができます。

python
isochrones = r5py.Isochrones(
    tn,
    origins = orig_gdf.iloc[:1],
    departure = departure,
    transport_modes = transport_modes,
    point_grid_resolution = 20,
    point_grid_sample_ratio = 1,
    isochrones = [5, 10, 15],
    speed_walking = speed_walking,
    max_time_walking = max_time_walking
)

isochrones["travel_time"] = isochrones["travel_time"].apply(str)
isochrones.explore(column="travel_time", cmap="tab10").save("isochrones.html")

isochrones.htmlを開くと以下のような図が表示され、移動時間ごとに到達可能なエリアを可視化できます。

R5RとR5pyどちらを使うべき?

結論としては、機能の充実度から「R5R」の利用を推奨します。
2025年11月27日時点では、R5Rには実装されているが、R5pyには未実装の機能が複数存在します。例えば以下の項目があります。

R5pyの未実装機能(R5Rでは実装)

  • 詳細な移動経路・交通手段別移動時間を、最短移動時間のみ出力
  • 車の速度の設定
  • 道路網に沿った到達圏の図示
  • 到着時刻基準の移動時間算出

一方R5Rは、R5pyが提供する機能のほぼ全てに対応しており、進捗表示などの便利機能も備えているため、環境的な制約がなければ R5R の使用をおすすめします。

なお、これらのオープンソースを使用する際には適切な引用が必要です。
Google Scholarで論文引用数を比較すると、R5py は比較的新しいライブラリということもありますが、R5Rの方が多く引用されています。
一方で、どちらのライブラリも継続的にアップデートが行われており、今後さらに使いやすくなることが期待できます。

R5R と R5py の引用数推移(2025.11.27 時点)

年間引用数(R5R) 年間引用数(R5py)

最後に

本記事では、R5pyを用いた移動時間の算出方法やR5Rとどちらを使うべきかについてご紹介しました。R5R・R5pyどちらも処理速度が速く、大規模な都市を対象とした解析にも対応できる非常に有用なツールです。
また、現在実際にこのツールを使用して、移動利便性の評価を行う業務も行っています。

【人工知能学会 第26回SIG-BI研究会】
移動利便性の評価指標の検討

【経営情報学会 2025年 全国研究発表大会】
合成人口データを活用した到達コストに基づく移動利便性指標
※公開され次第掲載予定です。

5
3
0

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
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?