大量の経路検索が一瞬で！？最強オープンソース「R5R」

# はじめに
国土交通省からデータアナリストとして株式会社onerootsへ転職した小池と申します。
早速ですがオープンソースの経路検索システムといえば、何を思い浮かべるでしょうか？
一般的によく知られているものとしては、OpenTripPlanner（OTP） があります。実は、この経路検索に関して、処理速度を比較した面白い論文があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789217/d7a1efaf-5f5d-4f7f-b59a-639a010806e1.png)


https://www.researchgate.net/publication/355721229_Calculating_Place-Based_Transit_Accessibility_Methods_Tools_and_Algorithmic_Dependence


この論文では、ArcGIS Pro、OpenTripPlanner（OTP）、R5R、Emme のツールを用いて移動時間の算出速度を比較しています。その結果、**R5Rが圧倒的に高速**であることが明らかになりました。特に、複数の出発地から複数の目的地への移動時間を計算する場合において、他のツールと比較して群を抜いて速いことが確認できます。今回はこのR5Rを用いた交通解析についてご紹介していきます。

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

https://conveyal.com/learn 

このR5を、Rのパッケージとして扱えるようにしたのが、**Ipea（Institute for Applied Economic Research）が開発したR5R**です。今回は、このR5Rの環境構築と分析方法について詳しくご紹介していきます。
（※Python版もありますが、こちらは別の記事で改めてご紹介予定です。）

https://ipeagit.github.io/r5r/


## R5Rの環境構築

R5RはR上で動作するパッケージですが、R5を内部で使用しているため、Javaの実行環境（JDK）も別途必要です。ここでは、Dockerを使った環境構築方法をご紹介します。以下は、R5R環境を構築するためのDockerfileです。

```php:Dockerfile
FROM rocker/r-ver:4.4.1

RUN apt-get update && apt-get install -y \
    libcurl4-openssl-dev \
    libssl-dev \
    libxml2-dev \
    libudunits2-dev \
    libgdal-dev \
    libgeos-dev \
    libproj-dev \
    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" 

RUN R -e "install.packages(c('remotes', 'sf'))"
RUN R -e "remotes::install_github('ipeaGIT/r5r', subdir='r-package')"
```
上記のDockerfileを作成後、以下のコマンドを実行すると構築が完了します。
```php:Docker実行コマンド
docker build -t r5r_accessibility .
```
## データ準備
次に、解析を行うために OpenStreetMapの地図データと、GTFSの公共交通データが必要となります。今回は徳島県つるぎ町を対象として解析を行いたいと思います。これらのデータは全て同じフォルダに保存します。
### ①OpenStreetMap
最初に分析する対象範囲のOpenStreetMapを入手するため、以下のサイトから四国の.osm.pbfファイルをダウンロードします。

https://download.geofabrik.de/asia/japan.html

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

https://docs.conveyal.com/prepare-inputs#cropping

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

https://gtfs-data.jp/search?agencyId=tsurugitown

### ③出発地・目的地
最後に、出発地・目的地の位置情報を記載した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|


## 分析方法
次に、R5Rを用いた具体的な分析方法についてご紹介します。今回は以下の４つについて分析を行います。
（１）移動時間の算出
（２）移動時間の算出（各移動手段別の時間も取得）
（３）移動経路
（４）到達圏解析

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

```php:R（ttm.r）
# メモリ割り当て
options(java.parameters = "-Xmx16G")
library(r5r)

#　フォルダの読みこみ
folder_path <- "/scripts/"

# 出発地のデータを読みこみ
points <- read.csv(paste0(folder_path,"出発地.csv"))

# 目的地のデータを読みこみ
poi <- read.csv(paste0(folder_path,"目的地.csv"))

# GTFSとOSMが保存されているフォルダを読みこみ
data_path <- folder_path

# ネットワークの構築
r5r_core <- setup_r5(data_path = data_path)

# 条件設定
mode <- c("WALK", "TRANSIT") # 徒歩と公共交通で移動
max_walk_time <- 300 # 徒歩の最大移動時間（分）
max_trip_duration <- 300 # トリップの最大移動時間（分）
walk_speed <- 4.8 # 徒歩の速度（km/h）
departure_datetime <- as.POSIXct("15-03-2025 7:45:00",
                                 format = "%d-%m-%Y %H:%M:%S") #出発時間の指定

# （１）移動時間算出
ttm <- travel_time_matrix(r5r_core = r5r_core,
                          origins = points,
                          destinations = poi,
                          mode = mode,
                          time_window = 1L,
                          departure_datetime = departure_datetime,
                          max_walk_time = max_walk_time,
                          walk_speed = walk_speed,
                          max_trip_duration = max_trip_duration)

# csvとして保存
write.csv(ttm, file = paste0(folder_path, "ttm_result.csv"), row.names = FALSE, fileEncoding = "cp932")

```
このRスクリプトをDockerで実行するには、以下のコマンドを使用します。
```php:Docker実行コマンド
docker run --rm -v ${PWD}:/scripts -w /scripts r5r_accessibility Rscript ttm.r
```

計算が完了すると、ttm_result.csv に以下のような形式で移動時間が出力されます。
このうち、travel_time_p50 列が移動時間（分）を表します。


|from_id|to_id|travel_time_p50|
|---|---|---|
|出発地１|町役場本庁|6|
|出発地１|町役場支所|36|
|出発地１|道の駅|15|
|出発地２|町役場本庁|79|
|出発地２|町役場支所|36|
|出発地２|道の駅|89|
|出発地３|町役場本庁|7|
|出発地３|町役場支所|36|
|出発地３|道の駅|15|

### （２）移動時間の算出（各移動手段別の時間も取得）
先ほどの結果では、移動全体にかかる所要時間のみが出力され、徒歩やバスといった各移動手段ごとの所要時間は含まれていませんでした。ここでは、徒歩・バス・待ち時間などの内訳も含めた移動時間を取得します。徒歩速度や最大移動時間などの条件は前回と同じまま、以下のコードを実行することで取得できます。
```php:R
# （２）移動時間の算出（各移動手段別の時間も取得）
ettm <- expanded_travel_time_matrix(r5r_core = r5r_core,
                                    origins = points,
                                    destinations = poi,
                                    mode = mode,
                                    time_window = 1L,
                                    departure_datetime = departure_datetime,
                                    max_walk_time = max_walk_time,
                                    walk_speed = walk_speed,
                                    breakdown = TRUE,
                                    max_trip_duration = max_trip_duration)

# csvとして保存                                    
write.csv(ettm, file = paste0(folder_path, "ettm_result.csv"), row.names = FALSE, fileEncoding = "cp932")
```
計算が完了すると、以下のような結果が出力されます。出発地からバス停までの徒歩時間や公共交通の乗車時間、待ち時間、どの路線に乗ったか、乗換回数等が取得できました。

<table>
  <thead>
    <tr>
      <th>$\textsf{from_id}\hspace{1em}$</th>
      <th>$\textsf{to_id}\hspace{3em}$</th>
      <th>$\textsf{departure_time}$</th>
      <th>$\textsf{draw_number}$</th>
      <th>$\textsf{access_time}$</th>
      <th>$\textsf{wait_time}$</th>
      <th>$\textsf{ride_time}$</th>
      <th>$\textsf{transfer_time}$</th>
      <th>$\textsf{egress_time}$</th>
      <th>$\textsf{routes}\hspace{1em}$</th>
      <th>$\textsf{n_rides}$</th>
      <th>$\textsf{total_time}$</th>
    </tr>
  </thead>
  <tbody>
    <tr><td>出発地１</td><td>町役場本庁</td><td>7:45:00</td><td>1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>[WALK]</td><td>0</td><td>6.5</td></tr>
    <tr><td>出発地１</td><td>町役場支所</td><td>7:45:00</td><td>1</td><td>5.9</td><td>1.1</td><td>29</td><td>0</td><td>0.4</td><td>10</td><td>1</td><td>36.4</td></tr>
    <tr><td>出発地１</td><td>道の駅</td><td>7:45:00</td><td>1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>[WALK]</td><td>0</td><td>15.6</td></tr>
    <tr><td>出発地２</td><td>町役場本庁</td><td>7:45:00</td><td>1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>[WALK]</td><td>0</td><td>79.2</td></tr>
    <tr><td>出発地２</td><td>町役場支所</td><td>7:45:00</td><td>1</td><td>0.4</td><td>19.6</td><td>16</td><td>0</td><td>0.4</td><td>10</td><td>1</td><td>36.4</td></tr>
    <tr><td>出発地２</td><td>道の駅</td><td>7:45:00</td><td>1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>[WALK]</td><td>0</td><td>89.3</td></tr>
    <tr><td>出発地３</td><td>町役場本庁</td><td>7:45:00</td><td>1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>[WALK]</td><td>0</td><td>7</td></tr>
    <tr><td>出発地３</td><td>町役場支所</td><td>7:45:00</td><td>1</td><td>2.5</td><td>5.5</td><td>28</td><td>0</td><td>0.4</td><td>10</td><td>1</td><td>36.4</td></tr>
    <tr><td>出発地３</td><td>道の駅</td><td>7:45:00</td><td>1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>[WALK]</td><td>0</td><td>15.1</td></tr>
  </tbody>
</table>

### （３）移動経路
続いて、特定の出発地から目的地までの詳細な移動経路を取得します。今回は例として、出発地3から町役場支所までの経路を取得します。以下のコードを実行することで、詳細な経路情報を取得できます。
```php:R
library(sf)

#（３）移動経路
det <- detailed_itineraries(r5r_core = r5r_core,
                            origins = points[3,],
                            destinations = poi[2,],
                            mode = mode,
                            time_window = 1L,
                            departure_datetime = departure_datetime,
                            max_walk_time = max_walk_time,
                            walk_speed = walk_speed,
                            max_trip_duration = max_trip_duration)

# geojsonとして保存
st_write(det, "det_result.geojson", driver = "GeoJSON", delete_dsn = TRUE)
```
GeoJSON形式で出力されたデータは、QGISなどのGISツールで表示することができます。 実際にQGIS上で表示すると、出発地から目的地までの徒歩・バス経路が可視化できます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789217/fb5e90e6-dc62-46e0-8a17-0a5f0f4bd4af.jpeg" width=60%>

### （４）到達圏解析
最後に、町役場本庁から到達可能な範囲を算出します。今回は、10分単位で0〜60分までの到達圏を計算します。以下のコードを実行することで、到達可能な範囲を取得できます。

```php:R
#（４）到達圏解析
iso1 <- isochrone(r5r_core,
                  origins = poi[1,],
                  mode = "TRANSIT",
                  mode_egress = "WALK",
                  cutoffs = seq(0, 60, 10),
                  sample_size = 1,
                  polygon_output = FALSE,
                  departure_datetime = departure_datetime,
                  max_walk_time = max_walk_time,
                  max_trip_duration = max_trip_duration,
                  time_window = 1L)
                       
# geojsonとして保存
st_write(iso1, "isochrones.geojson", driver = "GeoJSON", delete_dsn = TRUE)
```
GeoJSON形式で出力されたデータをQGIS上で表示すると、道路網に沿った移動時間ごとの到達可能範囲を可視化できます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789217/32d062ae-513f-43f2-b5e2-06823b43f6c5.jpeg" width=80%>

## 坂道を考慮した分析
これまでは2次元空間での分析を紹介してきましたが、標高データを取り入れることで、坂道を考慮した分析も可能になります。方法としては、国土地理院が提供する基盤地図情報から標高データを取得し、GeoTIFF形式に変換したうえで、GTFSやOSMデータを保存しているフォルダと同じ場所に保存します。これにより、標高を考慮したルート計算が可能になります。GeoTIFFへの変換方法については、以下のサイトに詳しく紹介されていますので、参考にしてみてください。


https://qiita.com/HidKamiya/items/66b1d98503301446460c

https://www.kkaneko.jp/db/heightmap/demtool.html

算出方法としては、デフォルトでToblerのハイキング関数が設定されています。 詳細な内容については以下に論文なども含めて掲載されていますので、参考にしてみてください。

https://github.com/ipeaGIT/r5r/issues/243

実際に標高データを入れて到達圏解析を実施したところ、導入後の方が下流側において若干移動範囲が広くなる結果となりました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789217/cb2ef0df-2e81-4b26-81a2-aaffb7a76325.gif" width=80%>

## 最後に
本記事では、R5Rを用いて公共交通における到達圏解析や移動時間の算出を行う方法をご紹介しました。R5Rは処理速度が非常に速く、大規模な都市に対しても高速に解析が可能で、非常に有用なツールです。実際にこのツールを使用して、移動利便性の評価を行う業務も行っています。

https://www.jstage.jst.go.jp/article/jsaisigtwo/2025/BI-026/2025_21/_article/-char/ja

次回はR5のPython版である「R5py」についてもご紹介したいと思いますのでよろしくお願い致します。