This vignette supports workshops on advanced usage and development of the Propensity to Cycle Tool (PCT). Beginner and intermediate PCT events focus on using the PCT via the web application hosted at www.pct.bike and the data provided by the PCT in QGIS.
The focus here is on analysing cycling potential in the open source statistical programming language R, in which the majority of the PCT was built. It will show how the code underlying the PCT works, how the underlying data can be accessed for reproducible analysis, and how the methods can be used to generate new scenarios of cycling uptake.
If you are an intermediate user, it may be worth brushing-up on your R skills, e.g. by taking a free online course such as that provided by DataCamp or by working through Chapter 2 onwards of the open source book Geocomputation with R (see reading list below for more transport-specific resources).
In addition to computer hardware (a laptop) and software (an up-to-date R set-up and experience using R) pre-requisites, you should have read, or at least have working knowledge of the contents of, the following publications, all of which are freely available online:
To ensure your computer is ready for the course, you should be able to run the following lines of R code on your computer:
install.packages("remotes")
= c(
pkgs "cyclestreets",
"mapview",
"pct",
"sf",
"stats19",
"stplanr",
"tidyverse",
"devtools"
)::install_cran(pkgs)
remotes# remotes::install_github("ITSLeeds/pct")
To test your computer is ready to work with PCT data in R, try running the following command:
source("https://github.com/ITSLeeds/pct/raw/master/inst/test-setup.R")
Part 1: how the Propensity to Cycle Tool works + demo: ‘I do’
test-setup.R
(14:15 - 14:30)Part 2: Co-coding session: getting started with transport data in R: ‘we do’
Working through the code in pct_training.R
(15:00 - 15:30)
Live demo: overlaying PCT data with data from the Rapid tool and OSM (15:30 - 15:45)
☕☕☕ 15 minute break ☕☕☕
Part 3: using PCT data for local transport planning: ‘you do’
Getting set-up with RStudio and input data (16:00 - 16:15, Robin)
Break-out rooms (16:15 - 17:00)
get_pct_rnet
)Presentation of work and next steps (17:00 - 17:15)
Networking and ‘ideas with beers’ 🍻🍻🍻 (17:20 - 18:00)
These links may be useful when working through the exercises below:
The PCT provides data at 4 geographic levels:
Which types of data are most appropriate to tackle each of the questions/problems you identified?
In this section we will get PCT data at the MSOA level and plot the result in a simple map.
library(pct)
library(dplyr) # in the tidyverse
library(tmap) # installed alongside mapview
= "isle-of-wight"
region_name = get_pct_zones(region_name, geography = "msoa")
zones_all = get_pct_lines(region_name, geography = "msoa")
lines_all = get_pct_routes_fast(region_name, geography = "msoa")
routes_all = get_pct_rnet(region_name) rnet_all
plot(zones_all$geometry)
plot(lines_all$geometry, col = "blue", add = TRUE)
plot(routes_all$geometry, col = "green", add = TRUE)
plot(rnet_all$geometry, col = "red", lwd = rnet_all$bicycle / 10, add = TRUE)
At its heart, the PCT is a data visualisation tool.
tmap
packagetm_shape(rnet_all) +
tm_lines(lwd = "dutch_slc", scale = 9)
#> Legend labels were too wide. Therefore, legend.text.size has been set to 0.47. Increase legend.width (argument of tm_layout) to make the legend wider and therefore the labels larger.
tmap
package# interactive plot
tmap_mode("view")
#> tmap mode set to interactive viewing
tm_shape(rnet_all) +
tm_lines(lwd = "dutch_slc", scale = 9)
#> Legend for line widths not available in view mode.
# basic plot
= 7
max_distance # plot(zones_all$geometry)
# plot(lines_all$geometry[lines_all$all > 500], col = "red", add = TRUE)
# create 'active' desire lines (less than 5 km)
= lines_all %>%
active mutate(`Percent Active` = (bicycle + foot) / all * 100) %>%
filter(e_dist_km < max_distance)
tm_shape(active) +
tm_lines("Percent Active", palette = "RdYlBu", lwd = "all", scale = 9)
# Create car dependent desire lines
= lines_all %>%
car_dependent mutate(`Percent Drive` = (car_driver) / all * 100) %>%
filter(e_dist_km < max_distance)
tm_shape(car_dependent) +
tm_lines("Percent Drive", palette = "-RdYlBu", lwd = "all", scale = 9)
#> Legend for line widths not available in view mode.
Advanced: visualise the PCT data using a range of visualisation techniques. For inspiration, check out the Making maps with R chapter of Geocomputation with R.
E1: Using the PCT’s online interface, hosted at www.pct.bike/m/?r=isle-of-wight, identify the MSOA zone that has the highest number of people who cycle to work.
E2: Using data downloaded with the command get_pct_zones()
, identify the zone that has highest level of cycling with the function top_n()
and save the result as an object called z_highest_cycling
(hint: you may want to start by ‘cleaning’ the data you have downloaded to include only a few key columns with the function select()
, as follows):
= zones_all %>%
z select(geo_code, geo_name, all, foot, bicycle, car_driver)
plot()
command to visualise where on the Isle of Wight this ‘high cycling’ zone is (hint: you will need to use the plot()
function twice, once to plot z$geometry
, and again with the argument add = TRUE
and a col
argument to add the layer on top of the base layer and give it a colour). The result should look something like something this:E4: Using the online interface, identify the top 5 MSOA to MSOA desire lines that have the highest number of people who cycle to work.
E5: Using the function top_n(n = 5, wt = bicycle)
, identify the top 5 MSOA to MSOA desire lines that have the highest number of people who cycle to work (hint: you might want to start with the code shown below).
st_length()
.# Aim: get top 5 cycle routes
= lines_all %>%
l_msoa select(geo_code1, geo_code2, all, foot, bicycle, car_driver, rf_avslope_perc, rf_dist_km)
Top 5 MSOA to MSOA desire lines with highest number of people cycling (left) and driving (right) in the Isle of Wight.
geography = "lsoa"
, remember to change the names of the objects you create). The results should look like this:Top 10 LSOA-LSOA desire lines with highest number of people cycling (left) and driving (right) in the Isle of Wight.
pcycle
to the object l_msoa
that contains the % who cycle to work (hint: you might want to start this by typing l_msoa$pcycle = ...
) and plot the results (shown in left hand panel in plot below).$pcycle = l_msoa$bicycle / l_msoa$all * 100
l_msoa# plot(l_msoa["pcycle"], lwd = l_msoa$all / mean(l_msoa$all), breaks = c(0, 5, 10, 20, 50))
M2 (bonus): identify road segments with the highest estimated number of people cycling currently, and under the Go Dutch scenario (hint: you can download the route network with get_pct_rnet("isle-of-wight")
)
M3 Calculate the proportion of trips in the Isle of Wight that are less than 10 km in length and (bonus) plot the cumulative distribution graph for the fastest route distances
#> [1] 0.5726026
plot(l_less_than_10km %>% filter(foot > 5) %>% select(foot))
plot(l_less_than_10km %>% filter(bicycle > 5) %>% select(bicycle))
plot(l_less_than_10km %>% filter(car_driver > 5) %>% select(car_driver))
uptake_pct_godutch()
(hint: the following code chunk will create a ‘Government Target’ scenario):$euclidean_distance = as.numeric(sf::st_length(l_msoa))
l_msoa$pcycle_govtarget = uptake_pct_govtarget_2020(
l_msoadistance = l_msoa$rf_dist_km,
gradient = l_msoa$rf_avslope_perc
* 100 + l_msoa$pcycle )
Percent cycling currently (left) and under a ‘Go Dutch’ scenario (right) in the Isle of Wight.
pct_uptake_godutch()
- how could it be modified?route()
find the route associated with the most cycled desire line in the Isle of Wight. If you use the arguments route_fun = osrmRoute
returnclass = "sf"
, the result should look similar to that displayed in the map below (hint: you may want to start your answer with the following lines of code):= l_msoa %>%
l_top top_n(n = 1, wt = bicycle)
R2: What are the problems associated with this route from a cycling perspective? Take a look at the help page opened by entering ?route
to identify the reason why the route is not particularly useful from a cycling perspective.
R3: Regenerate the route using the following command: route(l = l_top, route_fun = cyclestreets::journey)
. What is the difference in the length between each route, and what other differences can you spot? Note: this exercise requires an API Key from CycleStreets.net.
R4 (bonus): what features of a routing service would be most useful for your work and why?
overline()
function and begin the script as follows, the results should look similar to the results below):= sf::st_sf(wight_lines_30, geometry = wight_routes_30$geometry) route_data