Abstract
{r5r} is an R package for rapid realistic routing on
multimodal transport networks (walk, bike, public transport and car)
using R5. The package allows users to generate detailed
routing analysis or calculate travel time matrices using seamless
parallel computing on top of the R5 Java machine https://github.com/conveyal/r5
r5r is an R package for rapid realistic routing on multimodal transport networks (walk, bike, public transport and car). It provides a simple and friendly interface to R5, a really fast and open source Java-based routing engine developed separately by Conveyal. R5 stands for Rapid Realistic Routing on Real-world and Reimagined networks. More details about r5r can be found on the package webpage or on this paper.
You can install {r5r} from CRAN, or the development
version from github.
# from CRAN
install.packages('r5r')
# dev version with latest features
devtools::install_github("ipea/r5r", subdir = "r-package"){r5r} requires the Java Development Kit (JDK)
21. Any free, open-source JDK works, for example:
The easiest way to install JDK 21 is with the {rJavaEnv} package in R:
# install {rJavaEnv} from CRAN
install.packages("rJavaEnv")
# check version of Java currently installed (if any)
rJavaEnv::java_check_version_rjava()
## if this is the first time you use {rJavaEnv}, you might need to run this code
## below to consent the installation of Java.
# rJavaEnv::rje_consent(provided = TRUE)
# install Java 21
rJavaEnv::java_quick_install(version = 21)
# check if Java was successfully installed
rJavaEnv::java_check_version_rjava()First, set the memory available to Java with the
java.parameters option (2 GB is enough for the sample
data). Do this before loading {r5r} or any
other Java-based package: rJava starts the Java Virtual
Machine only once per R session, so later changes only take effect after
restarting R.
old_options <- options(java.parameters = "-Xmx2G")
# By default, {r5r} uses all CPU cores available. If you want to limit the
# number of CPUs to 4, for example, you can run:
# options(java.parameters = c("-Xmx2G", "-XX:ActiveProcessorCount=4"))Then we can load the packages used in this vignette:
{r5r} has eight fundamental
functions:
| Function | Returns |
|---|---|
build_network() |
A routable multimodal transport network |
accessibility() |
Access to opportunities from each origin, given a decay function |
travel_time_matrix() |
Travel times between origin/destination pairs for a departure time |
arrival_travel_time_matrix() |
Travel times for an arrival time, with routes used (and a time
breakdown with breakdown = TRUE) |
expanded_travel_time_matrix() |
Travel times per departure minute, with routes used (and a time
breakdown with breakdown = TRUE) |
detailed_itineraries() |
One or more alternative routes per origin/destination pair, detailed by trip segment |
pareto_frontier() |
The trade-off between travel time and monetary cost of route alternatives |
isochrone() |
Areas reachable from an origin within given travel times |
Most of them can also account for monetary travel costs (fare structure vignette).
Support functions:
| Function | Returns |
|---|---|
street_network_to_sf() |
The OpenStreetMap street network of a network.dat file,
as sf |
transit_network_to_sf() |
The public transport network of a network.dat file, as
sf |
find_snap() |
Where input points snap to the street network |
r5r_sitrep() |
A situation report to help debug errors |
To use {r5r}, you will need:
.pbf
format (mandatory)GTFS.zip format
(optional).tif
format (optional)Here are a few places from where you can download these data sets:
{r5r} includes sample data for Porto Alegre
(Brazil):
poa_osm.pbfpoa_eptc.zip and
poa_trensurb.zippoa_elevation.tifpoa_hexgrid.csv file with spatial coordinates of a
regular hexagonal grid covering the sample area, which can be used as
origin/destination pairs in a travel time matrix calculation.poa_points_of_interest.csv file containing the names
and spatial coordinates of 15 places within Porto Alegrefares_poa.zip file with the fare rules of the city’s
public transport system.data_path <- system.file("extdata/poa", package = "r5r")
list.files(data_path)
#> [1] "fares" "gtfs_errors.csv"
#> [3] "network.dat" "network_settings.json"
#> [5] "poa_elevation.tif" "poa_eptc.zip"
#> [7] "poa_hexgrid.csv" "poa_ls_lts.rds"
#> [9] "poa_osm.pbf" "poa_osm.pbf.mapdb"
#> [11] "poa_osm.pbf.mapdb.p" "poa_osm_congestion.csv"
#> [13] "poa_osm_lts.csv" "poa_points_of_interest.csv"
#> [15] "poa_poly_congestion.rds" "poa_trensurb.zip"
#> [17] "r5r-log.log"Points of interest, used below as origins and destinations:
poi <- fread(file.path(data_path, "poa_points_of_interest.csv"))
head(poi)
#> id lat lon
#> <char> <num> <num>
#> 1: public_market -30.02756 -51.22781
#> 2: bus_central_station -30.02329 -51.21886
#> 3: gasometer_museum -30.03404 -51.24095
#> 4: santa_casa_hospital -30.03043 -51.22240
#> 5: townhall -30.02800 -51.22865
#> 6: piratini_palace -30.03363 -51.23068Hexagonal grid points; we use a random sample of 200:
points <- fread(file.path(data_path, "poa_hexgrid.csv"))
# sample points
sampled_rows <- sample(1:nrow(points), 200, replace = FALSE)
points <- points[ sampled_rows, ]
head(points)
#> id lon lat population schools jobs healthcare
#> <char> <num> <num> <int> <int> <int> <int>
#> 1: 89a901290afffff -51.14038 -30.06368 39 0 12 0
#> 2: 89a901291bbffff -51.15836 -30.05661 67 0 830 0
#> 3: 89a90129cbbffff -51.17919 -30.04513 836 0 818 0
#> 4: 89a9012a3d3ffff -51.21209 -30.10610 27 0 3 0
#> 5: 89a901286a3ffff -51.18374 -30.08219 34 0 0 0
#> 6: 89a9012b383ffff -51.15706 -30.09971 10 0 0 0build_network()build_network() (1) downloads the R5 JAR (on
first use or when the R5 version changes) and caches it
locally; and (2) combines the .pbf, GTFS .zip
and optional elevation .tif files in data_path
into a routable network.
accessibility() is the fastest way to estimate
accessibility. Here we count the schools and healthcare facilities
reachable in less than 60 minutes by public transport and walking (accessibility
vignette).
# set departure datetime input
departure_datetime <- as.POSIXct("13-05-2019 14:00:00",
format = "%d-%m-%Y %H:%M:%S")
# calculate accessibility
access <- accessibility(
r5r_network,
origins = points,
destinations = points,
opportunities_colnames = c("schools", "healthcare"),
mode = c("WALK", "TRANSIT"),
departure_datetime = departure_datetime,
decay_function = "step",
cutoffs = 60
)
head(access)
#> id opportunity percentile cutoff accessibility
#> <char> <char> <int> <int> <num>
#> 1: 89a901290afffff schools 50 60 18
#> 2: 89a901290afffff healthcare 50 60 13
#> 3: 89a901291bbffff schools 50 60 30
#> 4: 89a901291bbffff healthcare 50 60 23
#> 5: 89a90129cbbffff schools 50 60 32
#> 6: 89a90129cbbffff healthcare 50 60 26For fast routing analysis, r5r currently has three
core functions: travel_time_matrix(),
expanded_travel_time_matrix() and
detailed_itineraries().
travel_time_matrix() computes travel times between
origin/destination pairs. Origins and destinations can be an
sf POINT object or a data.frame with columns
id, lon and lat.
max_walk_time and max_trip_duration are in
minutes, as are the resulting travel times.
It can also capture travel time variation across departures within a time window (time window vignette).
# set inputs
mode <- c("WALK", "TRANSIT")
max_walk_time <- 30 # minutes
max_trip_duration <- 120 # minutes
departure_datetime <- as.POSIXct("13-05-2019 14:00:00",
format = "%d-%m-%Y %H:%M:%S")
# calculate a travel time matrix
ttm <- travel_time_matrix(
r5r_network,
origins = poi,
destinations = poi,
mode = mode,
departure_datetime = departure_datetime,
max_walk_time = max_walk_time,
max_trip_duration = max_trip_duration
)
head(ttm)
#> from_id to_id travel_time_p50
#> <char> <char> <int>
#> 1: public_market public_market 0
#> 2: public_market bus_central_station 14
#> 3: public_market gasometer_museum 12
#> 4: public_market santa_casa_hospital 15
#> 5: public_market townhall 3
#> 6: public_market piratini_palace 17expanded_travel_time_matrix() works like
travel_time_matrix() but also returns, for each
origin/destination pair, the routes used and (with
breakdown = TRUE) the access, waiting, in-vehicle and
transfer times. It can be very memory intensive for large data sets.
# calculate a travel time matrix
ettm <- expanded_travel_time_matrix(
r5r_network,
origins = poi,
destinations = poi,
mode = mode,
departure_datetime = departure_datetime,
breakdown = TRUE,
max_walk_time = max_walk_time,
max_trip_duration = max_trip_duration
)
head(ettm)
#> from_id to_id departure_time draw_number access_time wait_time
#> <char> <char> <char> <int> <num> <num>
#> 1: public_market public_market 14:00:00 1 0 0
#> 2: public_market public_market 14:01:00 1 0 0
#> 3: public_market public_market 14:02:00 1 0 0
#> 4: public_market public_market 14:03:00 1 0 0
#> 5: public_market public_market 14:04:00 1 0 0
#> 6: public_market public_market 14:05:00 1 0 0
#> ride_time transfer_time egress_time routes n_rides total_time
#> <num> <num> <num> <char> <int> <num>
#> 1: 0 0 0 [WALK] 0 0
#> 2: 0 0 0 [WALK] 0 0
#> 3: 0 0 0 [WALK] 0 0
#> 4: 0 0 0 [WALK] 0 0
#> 5: 0 0 0 [WALK] 0 0
#> 6: 0 0 0 [WALK] 0 0Most routing packages return only the fastest route.
detailed_itineraries() can also return alternative routes
between origin/destination pairs, detailed by trip segment: transport
mode, waiting time, travel time and distance.
Below, alternative routes for a single origin/destination pair:
# set inputs
origins <- poi[10,]
destinations <- poi[12,]
mode <- c("WALK", "TRANSIT")
max_walk_time <- 60 # minutes
departure_datetime <- as.POSIXct("13-05-2019 14:00:00",
format = "%d-%m-%Y %H:%M:%S")
# calculate detailed itineraries
det <- detailed_itineraries(
r5r_network,
origins = origins,
destinations = destinations,
mode = mode,
departure_datetime = departure_datetime,
max_walk_time = max_walk_time,
shortest_path = FALSE
)
head(det)
#> Simple feature collection with 6 features and 16 fields
#> Geometry type: LINESTRING
#> Dimension: XY
#> Bounding box: xmin: -51.24094 ymin: -30.05002 xmax: -51.19762 ymax: -29.99729
#> Geodetic CRS: WGS 84
#> from_id from_lat from_lon to_id to_lat
#> 1 farrapos_station -29.99772 -51.19762 praia_de_belas_shopping_center -30.04995
#> 2 farrapos_station -29.99772 -51.19762 praia_de_belas_shopping_center -30.04995
#> 3 farrapos_station -29.99772 -51.19762 praia_de_belas_shopping_center -30.04995
#> 4 farrapos_station -29.99772 -51.19762 praia_de_belas_shopping_center -30.04995
#> 5 farrapos_station -29.99772 -51.19762 praia_de_belas_shopping_center -30.04995
#> 6 farrapos_station -29.99772 -51.19762 praia_de_belas_shopping_center -30.04995
#> to_lon option departure_time total_duration total_distance segment mode
#> 1 -51.22875 1 14:08:16 36.7 9537 1 WALK
#> 2 -51.22875 1 14:08:16 36.7 9537 2 RAIL
#> 3 -51.22875 1 14:08:16 36.7 9537 3 WALK
#> 4 -51.22875 1 14:08:16 36.7 9537 4 BUS
#> 5 -51.22875 1 14:08:16 36.7 9537 5 WALK
#> 6 -51.22875 2 14:09:34 47.5 8856 1 WALK
#> segment_duration wait distance route geometry
#> 1 5.1 0.0 174 LINESTRING (-51.1981 -29.99...
#> 2 6.6 1.7 4796 LINHA1 LINESTRING (-51.19763 -29.9...
#> 3 4.0 0.0 256 LINESTRING (-51.22827 -30.0...
#> 4 10.4 4.4 4083 188 LINESTRING (-51.22926 -30.0...
#> 5 4.6 0.0 228 LINESTRING (-51.22949 -30.0...
#> 6 5.1 0.0 174 LINESTRING (-51.1981 -29.99...The output is an sf data.frame, ready to map.
street_network_to_sf() extracts the OSM street network
used in routing, to give the map geographic context:
# extract OSM network
street_net <- r5r::street_network_to_sf(r5r_network)
# extract public transport network
transit_net <- r5r::transit_network_to_sf(r5r_network)
# plot
ggplot() +
geom_sf(data = street_net$edges, color='gray85') +
geom_sf(data = det, aes(color=mode)) +
facet_wrap(.~option) +
theme_void()Stop the network and run Java’s garbage collector to free the memory it used:
If you have any suggestions or want to report an error, please visit the package GitHub page.