# Visualising cycling potential with A/B Street

#### Nathanael Sheehan and Robin Lovelace

# Introduction

The `abstr` package was originally developed as part of the ActDev project, which involved the development of a prototype web application that provides evidence on active travel provision and potential in and around planned and proposed development sites, as outlined in a paper describing the tool. The ActDev website demonstrates the potential for new developments to support walking and cycling by visualising data generated using reproducible R code. A key challenge was to create a simulation for each of the case study sites based on the input origin-destination datasets. To overcome this challenge, A/B Street developers were commissioned to extend the ActDev tool to enable real time simulation of scenarios of change. At the time there was no way to get the OD data we had in the R world into the A/B Street world. This was motivation for creating the `abstr` R package. As outlined in the package’s README, the package’s main job is to take origin-destination data from `.csv` files (and other tabular file types) and outputs `.json` files that can be imported and visualised in A/B Street.

Given you have all of the above, you are ready to start transforming data-frames into simulations! So lets set an aim for the vignette

    ####
    ####
    #### AIM: Use PCT data to create scenario of change where commuting cycling levels increase and car journeys decrease which can be imported
    ####      into A/B Street city simulation software. This method should be fully reproducible for all other pct_regions.
    ####
    ####

While the PCT is a powerful and popular tool for strategic cycleway planning it has some key limitations that are addressed by A/B Street:

- The PCT only provides data on scenarios of behaviour change, providing little for people who want to visualise and explore scenarios of infrastructure change
- The PCT does not allow the user to edit the road network to help design the pro-cycling and traffic-reducing interventions that are needed to enable more people to cycle safely
- It does not provide evidence at high zoom levels, being designed for strategic planning

This vignette aims to demonstrate how data from the PCT, which provides evidence-based visions of how cycling could become the natural choice for urban travel, can be visualised in A/B Street to overcome the limitations outlined above. The first stage is to install the necessary packages (see the `abstr` vignette for a detailed introduction to software requirements).

# Installing and Loading Packages

To begin with, you need to install and load the necessary packages for this vignette.

    #### INSTALL PACKAGES ####
    cran_pkgs = c("abstr", "pct", "osmextract", "sf", "tidyverse")
    remotes::install_cran(cran_pkgs)
    #### LOAD PACKAGES ####
    library(abstr)
    library(pct)
    library(osmextract)
    library(sf)
    library(dplyr)

# Choosing a region

Data in the PCT for England and Wales is divided into regions, which can be seen below.

    pct_regions$region_name

To run the example below, replace `devon` with a different region from the list above (warning, this may not work for large regions).

    region_name = "devon"

You can select a specific local authority of interest from those available in the region of interest. You can check which are available in your region of interest as follows:

    lookup = pct::pct_regions_lookup
    table(lookup$lad16nm[lookup$region_name == region_name])

For the purposes of this article we will use Exeter as the case study:

    lad_name = "Exeter"

# Fetching PCT Data

Next you want to fetch two types of PCT data. Firstly, zone data, which is gathered using the `get_pct_zones()` function and is filtered to only include a local authority of choice, in this example we use Exeter. Secondly, commute data, which is gathered using the `get_pct_lines()` function and is also filtered to only include trips within the local authority.

    ####    READ DATA ####
    devon_zones = get_pct_zones(region = region_name, geography = "msoa") # get zone data
    # filter for exeter
    exeter_zones = devon_zones %>% filter(lad_name == lad_name) %>%
      select(geo_code)
    # get commute od data
    exeter_commute_od = get_pct_lines(region = region_name, geography = "msoa") %>%
      filter(lad_name1 == lad_name & lad_name2 == lad_name) # filter for exeter

# Data Cleaning and Transformation

Now you have your data, its time to clean and transform it. In fact, you only need to transform the `exeter_commute_od` dataframe as the `exeter_zones` is already in `abstr` format. The first step in cleaning the data requires renaming variables so that we can clearly see the difference between the base scenario and the scenario of change. Next, you calculate the scenario of change, in this example we use the `uptake_pct_godutch_2020()` function which takes two arguments of `distance` and `gradient` in its model calculation. The results from this PCT function allow you to calculate the mode shift from driving to cycling. Finally, we subset the data to only include the columns which are needed to progress.

    exeter_commute_od = exeter_commute_od %>%
      mutate(cycle_base = bicycle) %>%
      mutate(walk_base = foot) %>%
      mutate(transit_base = bus + train_tube) %>% # bunch of renaming -_-
      mutate(drive_base = car_driver + car_passenger + motorbike + taxi_other) %>%
      mutate(all_base = all) %>%
      mutate(
        # create new columns
        pcycle_godutch_uptake = uptake_pct_godutch_2020(distance = rf_dist_km, gradient = rf_avslope_perc),
        cycle_godutch_additional = pcycle_godutch_uptake * drive_base,
        cycle_godutch = cycle_base + cycle_godutch_additional,
        pcycle_godutch = cycle_godutch / all_base,
        drive__godutch = drive_base - cycle_godutch_additional,
        across(c(drive__godutch, cycle_godutch), round, 0),
        all_go_dutch = drive__godutch + cycle_godutch + transit_base + walk_base
      ) %>%
      select(
        # select variables for new df
        geo_code1,
        geo_code2,
        cycle_base,
        drive_base,
        walk_base,
        transit_base,
        all_base,
        all_go_dutch,
        drive__godutch,
        cycle_godutch,
        cycle_godutch_additional,
        pcycle_godutch
      )

As a quick sanity check we can make sure our model has not generated any new commutes and we still have the same base number of commuters as before.

    # sanity check: ensure total remains the same
    # (this is not a dynamic model where population change is factored in)
    identical(exeter_commute_od$all_base, exeter_commute_od$all_go_dutch)

# Download OSM building data

Now, you need to download OSM building data to populate the AB Street simulation map. In this example we use the `osmextract` package to fetch a PBF (protocolbuffer binary format) file hosted on GeoFabrik. You then need to filter the contents of the PBF file to only include the defined building types and subset the data to only include `osm_way_id, name, building` columns. Following this, you should ensure you only include valid sf buildings and then aggregate the building data against the zone boundary.

    ####    DOWNLOAD OSM BUILDING DATA ####
    osm_polygons = osmextract::oe_read(
      "https://download.geofabrik.de/europe/great-britain/england/devon-latest.osm.pbf",
      # download osm buildings for region using geofabrik
      layer = "multipolygons"
    )

    building_types = c(
      "yes",
      "house",
      "detached",
      "residential",
      "apartments",
      "commercial",
      "retail",
      "school",
      "industrial",
      "semidetached_house",
      "church",
      "hangar",
      "mobile_home",
      "warehouse",
      "office",
      "college",
      "university",
      "public",
      "garages",
      "cabin",
      "hospital",
      "dormitory",
      "hotel",
      "service",
      "parking",
      "manufactured",
      "civic",
      "farm",
      "manufacturing",
      "floating_home",
      "government",
      "bungalow",
      "transportation",
      "motel",
      "manufacture",
      "kindergarten",
      "house_boat",
      "sports_centre"
    )
    osm_buildings  = osm_polygons %>%
      filter(building %in% building_types) %>%
      select(osm_way_id, name, building)

    osm_buildings_valid = osm_buildings[sf::st_is_valid(osm_buildings), ]

    exeter_osm_buildings_all = osm_buildings_valid[exeter_zones, ]

Subsequently, you can join the OSM buildings data with the `exeter_zones` geography in order to create the complete building table. This table is filtered to not include any `NA's` and is aggregated to only include

    ####  JOIN OSM BUILDINGS WITH ZONE DATA ####
    exeter_osm_buildings_all_joined = exeter_osm_buildings_all %>%
      sf::st_join(exeter_zones)

    exeter_osm_buildings_sample = exeter_osm_buildings_all_joined %>%
      filter(!is.na(osm_way_id))

    exeter_osm_buildings_tbl = exeter_osm_buildings_all %>%
      filter(osm_way_id %in% exeter_osm_buildings_sample$osm_way_id)

# Using Abstr to Generate Scenarios

You now have everything in place to generate AB Street scenarios for both our base commute rate and our go_active commute rate. However, `abstr` takes a strict column name definition as to adhere to the AB street documentation. This means you need to rename mode columns for scenario generation. In order to make things easy, we can create a simple logic gate that renames our mode columns depending on the boolean value `go_active`.

    set.seed(2021) # for reproducible builds
    ####  LOGIC GATE ####
    # Logic gate for go_dutch scenario of change, where cycling levels increase to a proportion reflecting the Netherlands.
    # Switch to FALSE if you want census commuting OD
    go_dutch = TRUE
    if (go_dutch == TRUE) {
      exeter_od = exeter_commute_od %>%
        mutate(All = all_go_dutch) %>%
        mutate(Bike = cycle_godutch) %>%
        mutate(Transit = transit_base) %>%
        mutate(Drive = drive_base) %>%
        mutate(Walk = walk_base) %>%
        select(geo_code1, geo_code2, All, Bike, Transit, Drive, Walk,geometry)
    } else {
      exeter_od = exeter_commute_od %>%
        mutate(All = all_base) %>%
        mutate(Bike = cycle_base) %>%
        mutate(Drive = drive_base) %>%
        mutate(Transit = transit_base) %>%
        mutate(Walk = walk_base) %>%
        select(geo_code1, geo_code2, All, Bike, Transit, Drive, Walk, geometry)
    }

Voila, you are ready to generate simulation files from your data-frames. Lets start by using the `ab_scenario()` function with our `exeter_od`, `exeter_zones` and `exeter_osm_buildings_tbl` data-frames.

    ####  GENERATE A/B STREET SCENARIO ####
    output_sf = ab_scenario(
      od = exeter_od,
      zones = exeter_zones,
      zones_d = NULL,
      origin_buildings = exeter_osm_buildings_tbl,
      destination_buildings = exeter_osm_buildings_tbl,
      pop_var = 3,
      time_fun = ab_time_normal,
      output = "sf",
      modes = c("Walk", "Bike", "Drive", "Transit")
    )

To conclude you will need to generate a `JSON` format using the `ab_json()` function and then save the json file to your local machine using the `ab_save()` function.

    #### SAVE JSON FILE ####
    output_json = ab_json(output_sf, time_fun = ab_time_normal, scenario_name = "Go Dutch")
    ab_save(output_json, f = "dutch.json")

    # Upload the json file for future reference
    piggyback::pb_upload("dutch.json")
    piggyback::pb_download_url("dutch.json")

# Importing scenario files into A/B Street

Now that you’ve generated a scenario, you can simulate it. First install the latest build of A/B Street for your platform. Run the software, and choose “Sandbox” on the title screen.

You can then change the default map to other cities across the world. A/B Street contains over 40 sites in England from the ActDev project. If the local authority you have generated a scenario for is not included, you can also import a new city from the user interface.

After loading the correct map, change the traffic scenario from the default “weekday” or “none” pattern. Choose “import JSON scenario,” then select your `dutch` file.

After selecting your scenario, you should see something like this, a Go Dutch scenario of cycling taking place before your eyes, making the OD data frames come to life in a real time simulation!

Then let your eyes wonder on the simulation you have created and let your imagination explore the possibilities of transforming your local area into an active travel utopia.

# Conclusions and next steps

This vignette is by no means simple, and if you get stuck please raise an issue in the Github. If you succeed in generating a simulation for chosen city please share it on social media tagging the authors `abstr` so we can see how you have used the methods/data. If you are looking to extend this work presented in this vignette, why not try:

- building a simulation with a different uptake model
- exploring route network data representing cycling potential to schools from the PCT package
- creating your own uptake model (see the `getting` vignette from the `pct` R package)