Exciting times! {#exciting-times}

A number of packages are available to calculate travel times in R.

On one hand, several packages are available that obtain travel times from Google using the Google Travel Time Matrix. The advantage of using Google is that (1) it is possible to calculate travel times for different modes of transport, (2) that it is possible to draw from Google’s travel times on a congested network, thus obtaining realistic travel times for private motorized transport and (3) no expert knowledge is required to convert a road network and a public transport schedule into format suitable to calculate travel times. There are several disadvantages to using the Google API: (1) there is no such thing as a free lunch: if you exceed a number of queries, it is necessary to pay per query, (2) it is not possible to account for future changes in the network or public transport schedules and (3) travel times might not be available and can not be corrected easily using own schedules. I am for instance based in Basel, Switzerland. Basel neighbours France and Germany, and trams and buses ply to neighboring cities of St. Louis, Weil-am-Rhein and Lörrach regularly. These services are integrated into the schedule and the fare system.Despite these disadvantages, we have good experiences using the Google API in R. I will write on the usage of the Google API in a separate post.

On other hand, a number of packages are available that make use of General Transit Feed Specification (also known as GTFS) and, in some cases, OpenStreetMap, the most recent of these packages being r5r. In this post, I will look at sr5r and describe my experiences. There are number of other packages available to perform routing in R; these are listed in on the r5r page. Some of these packages include:

Installation {#installation}

First, make sure you have the package devtools installed. I make use of a helper function, usePackage, to determine where I have the package available and to subsequently load the package.

Then you can install r5r. I ran into some issues when install r5r on OS X. Make sure to have Java 11 Java installed and to run R CMD javareconf without root privelages in your terminal.

Also, I ran into issues with the dependency data.table. I installed zliband pkg-config with brew and followed the recommendations to set some environment variables (LDFLAGS, CPPFLAGS, PKG_CONFIG_PATH).

Despite this, I still had issues with Java, probably because OpenJDK is intalled in parallel on my machine. Finally, I started RStudio with the command LD_LIBRARY_PATH=$(/usr/libexec/java_home)/jre/lib/server: open -a RStudio to ensure that the correct Java version is used. Prior to running this command, I ensured that the linked version of Java was Java 11 (more on that in this gist). You will note that r5r gives you a message concerning the amount of memory that should be allocated to the Java virtual machine. This should be allocated before loading rjava, which is called by r5r. Hence, I added the java.parameters before installing and loading r5r.

Now that is out of the way, we can install some other packages. The objective is to create the canton of Zürich and the surrounding municipalities. It is possible to load administrative boundaries from GADM with the rasterpackage. The package sf allows for conveniently manipulating spatial data. The tidyverse packages require no future introduction. There are some prerequisites. I installed gdal with brew previously.

Let’s get administrative boundaries for Switzerland and filter the municipality of Zurich. This is done with the function getData; country=CH indicate Switzerland and level indicatates the administrative level, with level 0 being the country and higher numbers indicating lower administrative levels. The exact arrangement varies between countries.

# Get the GADM data
switzerland <- raster::getData('GADM', country='CH', level=2)
# Convert the data to a spatial data frame for more convenience
switzerland_sf <- sf::st_as_sf(switzerland)
# Filter the canton of Zurich
switzerland_sel_sf <- switzerland_sf %>% dplyr::filter(NAME_2=='Zürich')
# Project the area 
switzerland_sel_sf_2056 <- sf::st_transform(switzerland_sel_sf, st_crs(2056))

Now we can create a grid for Zürich and calculate the centroids. The grid has a cell size of 100 meters; by setting the arguement square to FALSE hexagonal cells are calculated. Subsequently, the centroids are calculated with the function st_centroid(). The function sfc_as_cols is a helper function, included in the functions.R file. It appends the longitude and latitude of the cells.

Experiences {#experiences}

From here on we can continue with the example from the r5r read me. I made some small adjustments, as I want to run r5 with my own data set. I noted the following:
* The function setup_r5 creates a r5 network. If this processes does not complete, it leaves two files behind in the folder specified with data_path. These files have the extension *.mapdband `*.mapdb.p. Make sure that these are deleted when attempting to the setup the network a second (or third) time.
* Make sure you specify sufficient memory prior to loading rjava and r5r. In RStudio, this requires removing rm(list = ls()) all objects from your environment and restarting the R session
* R5 is apparently pretty sensitive with regards to the GTFS format. I ran into issues with some GTFS files.
* It is not clearly documented how R5 (and r5r) maps modes in GTFS files to modes that are understandable. Some modes are listed on R5 github page in the TransitMode.java class here; other modes include CARand BICYCLE. The list of modes that r5r expects can be found in the documentation of the method travel_time_matrix.
* The function travel_time_matrix will not run when only a single origin is provided. Instead, it will provide you with the rather cryptic message ’Error in .jcall("RJavaTools", "Ljava/lang/Object;", "invokeMethod", cl, : java.lang.NoSuchMethodException: No suitable method for the given parameters.

Data {#data}

I obtained a GTFS feed that was compatible with R5 from the Open Data portal of the municipality of Zurich here and downloaded an OpenStreetMap pbf file from BBBike here. This link provides you with exact area. Both data sets are stored relative to this file in the folder _data.

##    fromId toId travel_time
## 1:   5000   78          49
## 2:   5000   84          35
## 3:   5000   85          36
## 4:   5000   86          51
## 5:   5000   88          49
## 6:   5000   96          34

Visualization {#visualization}

Now, I would like to visualize the results. Given that r5r returns the origin id and the destination id, it is possible to join the spatial data joined the results back to the original data frame with the areas.

Finally, it is possible to create a plot with the data set. I choose to use tmap. Have a look at book Geocomputation in R by Robin Lovelave if you would like to learn more about maps in R. Jacob Nowosad lists the coloring scales available in tmap here.


Concluding, the authors of the r5r did a great job in bring r5 to R. R5 – the Java version – has a number of other functionalities . One of these functionalities include the calculation of average and median travel times over a time window using simulation, instead of calculating the travel time for only a single departure time. Based on the available documentation in r5r this functionality is not made available. Admittingly, the authors of the r5r acknowledge this and are looking this.