Sustainable Transport Planning

Tools for transport planning with an emphasis on spatial transport data and non-motorized modes. Enables common transport planning tasks including: downloading and cleaning transport datasets; creating geographic "desire lines" from origin-destination (OD) data; route assignment, locally and via interfaces to routing services such as <> and calculation of route segment attributes such as bearing. The package implements the 'travel flow aggregration' method described in Morgan and Lovelace (2020) . Further information on the package's aim and scope can be found in the vignettes and in a paper in the R Journal (Lovelace and Ellison 2018) .

BuildStatus rstudio mirrordownloads CRAN_Status_Badge lifecycle

stplanr is a package for sustainable transport planning with R.

It provides functions for solving common problems in transport planning and modelling, such as how to best get from point A to point B. The overall aim is to provide a reproducible, transparent and accessible toolkit to help people better understand transport systems and inform policy.

The initial work on the project was funded by the Department of Transport (DfT) as part of the development of the Propensity to Cycle Tool (PCT). The PCT uses origin-destination data as the basis of spatial analysis and modelling work to identify where bicycle paths are most needed. See the package vignette (e.g. via vignette("introducing-stplanr")) or an academic paper on the Propensity to Cycle Tool (PCT) for more information on how it can be used. This README gives some basics.

stplanr should be useful to researchers everywhere. The function route_graphhopper(), for example, works anywhere in the world using the graphhopper routing API and read_table_builder() reads-in Australian data. We welcome contributions that make transport research easier worldwide.

Data frames representing flows between origins and destinations must be combined with geo-referenced zones or points to generate meaningful analyses and visualisations of ‘flows’ or origin-destination (OD) data. stplanr facilitates this with od2line(), which takes flow and geographical data as inputs and outputs spatial data. Some example data is provided in the package:

data(cents, flow)

Let’s take a look at this data:

flow[1:3, 1:3] # typical form of flow data
#>        Area.of.residence Area.of.workplace All
#> 920573         E02002361         E02002361 109
#> 920575         E02002361         E02002363  38
#> 920578         E02002361         E02002367  10
cents[1:3,] # points representing origins and destinations
#> class       : SpatialPointsDataFrame 
#> features    : 3 
#> extent      : -1.546463, -1.511861, 53.8041, 53.81161  (xmin, xmax, ymin, ymax)
#> coord. ref. : +init=epsg:4326 +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
#> variables   : 4
#> names       :  geo_code,  MSOA11NM, percent_fem,  avslope 
#> min values  : E02002382, Leeds 053,    0.408759, 2.284782 
#> max values  : E02002393, Leeds 064,    0.458721, 2.856563

These datasets can be combined as follows:

travel_network <- od2line(flow = flow, zones = cents)
w <- flow$All / max(flow$All) *10
plot(travel_network, lwd = w)

The package can also allocate flows to the road network, e.g. with and the OpenStreetMap Routing Machine (OSRM) API interfaces. These are supported in route_*() functions such as route_cyclestreets and route_osrm():

Route functions take lat/lon inputs:

trip <-
  route_osrm(from = c(-1, 53), to = c(-1.1, 53))

and place names, found using the Google Map API:

We can replicate this call multiple times using line2route.

intrazone <- travel_network$Area.of.residence == travel_network$Area.of.workplace
travel_network <- travel_network[!intrazone,]
t_routes <- line2route(travel_network, route_fun = route_osrm)
#> Warning in summarise_impl(.data, dots): hybrid evaluation forced for
#> `first`. Please use dplyr::first() or library(dplyr) to remove this
#> warning.
#> Warning in summarise_impl(.data, dots): hybrid evaluation forced for
#> `first`. Please use dplyr::first() or library(dplyr) to remove this
#> warning.
#> Warning in summarise_impl(.data, dots): hybrid evaluation forced for
#> `last`. Please use dplyr::last() or library(dplyr) to remove this warning.
#> Warning in summarise_impl(.data, dots): hybrid evaluation forced for
#> `last`. Please use dplyr::last() or library(dplyr) to remove this warning.

Another way to visualise this is with the leaflet package:

leaflet() %>% addTiles() %>% addPolylines(data = t_routes)

For more examples, example("line2route").

overline is a function which takes a series of route-allocated lines, splits them into unique segments and aggregates the values of overlapping lines. This can represent where there will be most traffic on the transport system, as illustrated below.

t_routes$All <- travel_network$All
rnet <- overline(t_routes, attrib = "All", fun = sum)
lwd <- rnet$All / mean(rnet$All)
plot(rnet, lwd = lwd)


To install the stable version, use:


The development version can be installed using devtools:

# install.packages("devtools") # if not already installed

stplanr depends on rgdal, which can be tricky to install.

Installing stplanr on Linux and Mac

splanr depends on rgdal which can be installed on Ubuntu, for example, with:

sudo apt install r-cran-rgdal

To install gdal binaries on other distributions please see here:

stplanr also depends on sf. Installation instructions for Mac, Ubuntu and other Linux distros can be found here:

Instructions to install gdal and Quartz are provided at and respectively (Quartz is required for R - as described here).

Funtions, help and contributing

The current list of available functions can be seen with:

lsf.str("package:stplanr", all = TRUE)

To get internal help on a specific function, use the standard way.



stplanr imports many great packages that it depends on. Many thanks to the developers of these tools:

desc = read.dcf("DESCRIPTION")
headings = dimnames(desc)[[2]]
fields = which(headings %in% c("Depends", "Imports", "Suggests"))
pkgs = paste(desc[fields], collapse = ", ")
pkgs = gsub("\n", " ", pkgs)
strsplit(pkgs, ",")[[1]]
#>  [1] "R (>= 3.0.2)"          " sp (>= 1.3.1)"       
#>  [3] " curl (>= 3.2)"        " readr (>= 1.1.1)"    
#>  [5] " dplyr (>= 0.7.6)"     " httr (>= 1.3.1)"     
#>  [7] " jsonlite (>= 1.5)"    " stringi (>= 1.2.4)"  
#>  [9] " stringr (>= 1.3.1)"   " lubridate (>= 1.7.4)"
#> [11] " maptools (>= 0.9.3)"  " raster (>= 2.6.7)"   
#> [13] " rgdal (>= 1.3.4)"     " rgeos (>= 0.3.28)"   
#> [15] " openxlsx (>= 4.1.0)"  " methods"             
#> [17] " R.utils (>= 2.7.0)"   " geosphere (>= 1.5.7)"
#> [19] " Rcpp (>= 0.12.1)"     " igraph (>= 1.2.2)"   
#> [21] " nabor (>= 0.5.0)"     " rlang (>= 0.2.2)"    
#> [23] " lwgeom (>= 0.1.4)"    " sf (>= 0.6.3)"       
#> [25] " testthat (>= 2.0.0)"  " knitr (>= 1.20)"     
#> [27] " rmarkdown (>= 1.10)"  " dodgr (>= 0.0.3)"


  • Please report issues, feature requests and questions to the github issue tracker
  • License: MIT
  • Get citation information for stplanr in R doing citation(package = 'stplanr')
  • This project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.


stplanr 0.2.9


  • New functions od_aggregate_from() and od_aggregate_to() provide easy ways to aggregate origin-destination pairs. See #303.
  • Updated overline2() is now faster and better documented, thanks to #307
  • Updates to route_dodgr() function, which provides an interface to the dodgr package, accepts wider range of inputs
  • Better website and updated function list. See
  • The sf method for overline() has been updated so it calls the much faster overline2() function
  • Updated documentation for route_local()


  • Bug in sum_network_routes() fixed. See #267

stplanr 0.2.8


  • The stplanr paper has been published! See it here:
  • STATS19 functions such as dl_stats19() are depreciated. They have been split-out into the new package stats19
  • route_dodgr() has now been implemented
  • A new function overline2() has been added, thanks to Malcolm Morgan. This is faster than overline().
  • A substantial refactoring operation has begun. This has resulted in fewer lines of code in the od functions, a new stplanr::od_coords2line() function, and more support of sf
  • route_dodgr() has been added
  • A new example dataset, osm_net_example, has been added for local routing purposes.
  • A citation to the package has been added. Try citation("stplanr")
  • The package has a shiny new website thanks to @maelle:
  • The package looses its Imports dependency on rgdal, which has been demoted to a Suggests


stplanr 0.2.7



  • Fixed #272 by removing byvars argument of overline in preparation for overdue overhaul of overline function.


  • No longer suggests tmap to reduce install times: install.packages() installs suggested packages by default

stplanr 0.2.6


  • New function route_local()
  • New argument in line2route(): time_sleep waits a period between each route request


  • Issue with dl_stats19(), see #270
  • Make style consistent, see commit
  • Various small fixes to documentation and style

stplanr 0.2.5


  • New function line_via() for identifying intermediary points on a transport network


  • Bug associated with SpatialLinesNetwork() fixed (see #249)

stplanr 0.2.4


  • New function geo_length() returns numeric vector of line lengths from sp or sf objects.


  • ?route_graphhopper no longer mentions the depreciated 'bike2' profile - see #246
  • ?route_osrm mentions that the public API only routes for cars - see #246
  • Updated introducing-stplanr vignette to show new function and make more robust

stplanr 0.2.3


  • stplanr now imports lwgeom, needed for sf::st_length(), used in SpatialLinesNetwork().
  • Plotting behaviour updated for sfNetwork objects: now only plots the geometry by default.
  • Improved documentation for SpatialLinesNetwork() and plot() for spatial networks.


  • Bug in sum_network_routes() fixed (see #240).

stplanr 0.2.2


  • In this release sp is demoted from a Depends to an Imports, meaning that all its functions will not be attached to your namespace (it will not be loaded) when you run library(stplanr), making it less tied to sp. This is a continuation of the work to support sf and will make it easier for the package to work with alternative representations of geographic data.


  • Bug in geo_select_aeq.sf() was fixed by Jakub Nowosad in pull #238
  • An issue with od_aggregate.sf() was fixed making it much faster

stplanr 0.2.0


  • This is the largest release since the package was created, with dozens of changes to support simple features - see for details.
  • Support for sf. The package now support the new spatial class system for most functions.
  • New function geo_bb() supercedes bb2poly(). The new function can return polygons, points and matrix objects determined by the output argument. It also allows bounding boxes to be extended in metres, and scaled in x and y dimensions.
  • geo_code() now uses nominatim by default to find locations on the maps.
  • New function od_coords() takes a wide range of input data types to return a consistent output representing OD data as a data frame of origin and destination coordinates. This is used behind the scenes to make other functions more modular.


Plans for the next release

  • New generic route() function for routing. This is more flexible and user-friendly than the existing line2route() and route_*() functions it enhances.
  • Updated function names to make using stplanr easier and more intuitive.

stplanr 0.1.9


  • Dependency cull: we have removed dependencies on foreach and doParallel
  • route_cyclestreet() now also called (correctly) route_cyclestreets()
  • New geo_code() function replaces dependency on RGoogleMaps


stplanr 0.1.8


  • New argument combinations added to sum_network_routes() so it runs quicker - see pull/177.
  • New examples added to sum_network_routes(), weightfield() and find_network_nodes() - see e.g. example(sum_network_routes) for details.
  • New dataset l_poly added.
  • stplanr now has a website! See


  • Serious bug with SpatialLinesNetwork() fixed.
  • Depreciated _each() dplyr functions replaced with equivalent _at or _all functions. See here for more.

stplanr 0.1.7


  • There is a new vignette! See vignettes/stplanr-paper.Rmd and vignette("stplanr-paper") for details.
  • The original introducing-stplanr vignette has been updated. It now provides a more basic introduction for people new to R for spatial and transport data.
  • line2route() has been refactored to improve error detection and allow n_processes arguments. Thanks @nikolai-b. See pull/151 for details.
  • line_match() function added, a wrapper around rgeos::gDistance(), to find similar routes.
  • RCurl and data.table dependencies have been removed
  • leaflet has been demoted from an import to a suggest. This should reduce install times.
  • New functions od_aggregate() and sp_aggregate() have been added, to enable OD data to be aggregated to new geographic levels.


  • #141 fixed: viaroute() works again.
  • #153 fixed: bidirectional = TRUE returns a different result in line_bearing() now.


  • A new branch that uses sf is being tested. We may eventually transition to using simple features classes instead of sp classes.

stplanr 0.1.6


  • onewayid() is now a generic function, meaning it can handle spatial and non-spatial data
  • New arguments provided for line2route() allow you to specify variables to join-by - also has updated and more sensible defaults
  • New function od_id_order() to put origin-destination ids in order, to identify 2 way duplicates (split out from onewayid())


  • See the issue tracker
  • Bug in route_cyclestreet() leading change_elev and av_incline being wrong now fixed
  • Bug making variable names with spaces in the id columns failed - now fixed #138

stplanr 0.1.5


  • New argument destinations added to od2line(). See example(od2line) for an example.
  • New dataset destinations for showing how OD matrix with destinations can be converted to spatial data
  • New argument list_output allows the route information to be saved as a list, allowing save_raw = TRUE (which does not return a Spatial object) to be passed to the route_ function.
  • tmap dependency removed for faster installs


  • Bug with line2route() (#124) fixed
  • Various improvements to documentation

stplanr 0.1.4


  • New function reproject() is a simple wrapper around spTransform() that uses crs_select_aeq() to convert a spatial object in geographic (lat/lon) coordinates into on with projected coordinates, with units of 1 m. This is useful for various spatial operations, such as finding the length and area of an object.

  • Implement gprojected(), a function for performing GIS operations on a temporary, projected, version of spatial objects.

  • Addition of line_bearing() to return the bearing of lines based on start and end points.

  • Addition of angle_diff() for finding the angular difference between lines: are they roughly parallel or perpendicular?


  • line2df() now works on lines with multiple vertices and is faster.

  • Fixes in the examples used to illustrate how od_dist() works.

stplanr 0.1.3


  • Update to OSRM functions to support API v5.

  • New parameter byvars in the overline() function, to allow disaggregation of results by a grouping variable (see example(overline)).

  • Faster implementation of od2line(): od2line2(). Plan is to replace the original if no issues are found with new implementation.

  • New function od2odf() which converts OD data into a dataframe of origins and destinations (feeds od2line2() but also useful as self-standing function).

  • New argument new_proj in buff_geo() allows the results to be exported to any coordinate reference system (CRS).

  • New function gprojected() generalises concept of buff_geo(), building on crs_select_aeq() to allow any GIS query to be conducted on a temporary projected version of spatial objects with geographical CRSs.

  • New function od_dist() can quickly calculate Euclidean distances of OD pairs without converting to spatial objects.


  • Bug fix in onewayid() so it captures all lines.

  • Various improvements to documentation and code.

stplanr 0.1.2


  • Interface to the Google Distance Matrix API with dist_google.

  • New transport planning API added, with route_transportapi_public (for testing).

  • Update to line2route, allowing it to accept different routing funtions via the new argument route_fun (for testing - tested with route_fun = route_cyclestreet).

  • New functions for creating origin-destination data frames (point2odf) and SpatialLinesDataFrames (points2flow).

  • Addition of n_vertices and is_linepoint for identifying the number of vertices in spatial objects and whether the 'line' is really a point.


  • line2route refactored, with 10 fold speed increases on large (1000+) batches of lines.

stplanr 0.1.0


  • Addition of new class definition SpatialLinesNetwork, methods for plot and summary and functions calc_network_routes and find_network_nodes allowing fast route calculations via igraph and other network analysis functions.

  • Functions for removing beginning and end of lines: toptail and toptailgs. Helper functions buff_geo, crs_select_aeq and line2points added.

  • Functionality for reading in the UK's stats19 data: read_stats19_* functions download, unzip and re-categorise the data.

  • read_table functions added for reading Australian OD data.

  • decode_gl added to decode Google polylines and other functions for querying and reading data from OSRM services.

  • gtfs2sldf added to import GTFS routes as SpatialLinesDataFrames.

stplanr 0.0.2

  • Published on CRAN

Reference manual

It appears you don't have a PDF plugin for this browser. You can click here to download the reference manual.


0.8.2 by Robin Lovelace, 9 days ago,

Report a bug at

Browse source code at

Authors: Robin Lovelace [aut, cre] , Richard Ellison [aut] , Malcolm Morgan [aut] , Barry Rowlingson [ctb] , Nick Bearman [ctb] , Nikolai Berkoff [ctb] , Scott Chamberlain [rev] (Scott reviewed the package for rOpenSci , see , Mark Padgham [ctb] , Andrea Gilardi [ctb]

Documentation:   PDF Manual  

Task views: Analysis of Spatial Data, Handling and Analyzing Spatio-Temporal Data

MIT + file LICENSE license

Imports sp, curl, dplyr, httr, jsonlite, stringr, maptools, raster, rgeos, methods, geosphere, Rcpp, igraph, nabor, rlang, lwgeom, sf, magrittr, sfheaders, data.table, pbapply

Suggests testthat, knitr, rmarkdown, dodgr, stats19, cyclestreets, leaflet, rgdal, pct, tmap, bench, openxlsx, osrm, geodist, mapsapi, s2

Linking to RcppArmadillo, Rcpp

System requirements: GNU make

Imported by pct.

Suggested by cyclestreets.

See at CRAN