├── .Rbuildignore ├── .github ├── .gitignore └── workflows │ ├── R-CMD-check.yaml │ ├── pkgdown.yaml │ └── pr-commands.yaml ├── .gitignore ├── DESCRIPTION ├── NAMESPACE ├── NEWS.md ├── R ├── data.R ├── desire_lines.R ├── get_pct.R ├── model.R ├── msoa_centroids.R ├── uptake.R └── utils.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── codecov.yml ├── cran-comments.md ├── data-raw ├── example-data.Rmd ├── getting.Rmd ├── pct-slides-ertico-short.Rmd ├── pct_training.Rmd ├── pct_training2.R ├── refs_training.bib ├── rnets.R ├── slides │ ├── pct-slides-cac-2021.Rmd │ └── pct-slides-ie.Rmd ├── test-pct.R ├── test-setup2.R ├── test-setup3.R ├── test-uptake-2020.Rmd ├── training-dec-2021.Rmd ├── workshop-msg.Rmd ├── workshop-msg.md └── workshop-msg_files │ ├── figure-gfm │ ├── unnamed-chunk-2-1.png │ ├── unnamed-chunk-3-1.png │ ├── unnamed-chunk-3-2.png │ ├── unnamed-chunk-3-3.png │ ├── unnamed-chunk-3-4.png │ └── unnamed-chunk-4-1.png │ └── figure-html │ └── unnamed-chunk-2-1.png ├── data ├── desire_lines_leeds.rda ├── leeds_uber_sample.rda ├── mode_names.rda ├── od_leeds.rda ├── pct_regions.rda ├── pct_regions_lookup.rda ├── rnet_leeds.rda ├── routes_fast_leeds.rda ├── santiago_lines.rda ├── santiago_od.rda ├── santiago_routes_cs.rda ├── santiago_zones.rda ├── wight_centroids.rda ├── wight_lines_30.rda ├── wight_lines_pct.rda ├── wight_od.rda ├── wight_rnet.rda ├── wight_routes_30.rda ├── wight_routes_30_cs.rda ├── wight_zones.rda └── zones_leeds.rda ├── inst ├── cycling-potential-to-specific-zones.R ├── estimate-cycling-potential-rnet-leeds-simple-example.R ├── part-3-getting-pct-data.R ├── pct-wy.R ├── pct_training.R ├── pct_training_solutions.Rmd ├── rapid-geojson-download.R ├── rmd │ ├── pct-slides.Rmd │ └── refs.bib ├── test-setup.R └── uptake-model.R ├── man ├── desire_lines_leeds.Rd ├── figures │ ├── README-centroids-1.png │ ├── README-decay-1.png │ ├── README-decayhills-1.png │ ├── README-desire-1.png │ ├── README-get_pct_lines-1.png │ ├── README-rnetgove-1.png │ ├── README-routes_fast-1.png │ └── README-routes_vital-1.png ├── get_centroids_ew.Rd ├── get_desire_lines.Rd ├── get_od.Rd ├── get_pct.Rd ├── get_pct_centroids.Rd ├── get_pct_lines.Rd ├── get_pct_rnet.Rd ├── get_pct_routes_fast.Rd ├── get_pct_routes_quiet.Rd ├── get_pct_zones.Rd ├── leeds_uber_sample.Rd ├── mode_names.Rd ├── model_pcycle_pct_2020.Rd ├── od_leeds.Rd ├── pct_regions.Rd ├── pct_regions_lookup.Rd ├── rnet_leeds.Rd ├── routes_fast_leeds.Rd ├── santiago_lines.Rd ├── santiago_od.Rd ├── santiago_routes_cs.Rd ├── santiago_zones.Rd ├── uptake_pct_godutch.Rd ├── uptake_pct_govtarget.Rd ├── wight_lines_30.Rd ├── wight_od.Rd ├── wight_routes_30.Rd ├── wight_zones.Rd └── zones_leeds.Rd ├── pct-leeds-demo.png ├── pct.Rproj └── vignettes ├── .gitignore ├── cycling-potential-uk.Rmd ├── km-cycled.Rmd ├── nature.csl ├── pct-international.Rmd ├── pct.Rmd ├── refs.bib ├── refs_training.bib └── uk-cities.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^CRAN-RELEASE$ 2 | ^codecov\.yml$ 3 | ^README\.Rmd$ 4 | ^.*\.Rproj$ 5 | ^\.Rproj\.user$ 6 | data-raw/* 7 | ^test-data\.R 8 | ^.*\.log 9 | ^\.travis\.yml$ 10 | ^pct-leeds-demo\.png$ 11 | ^\.DS_Store$ 12 | ^cran-comments.md$ 13 | ^docs$ 14 | ^centroids-msoa.csv$ 15 | ^inst/rmd/libs$ 16 | ^inst/pct_training_solutions\.html$ 17 | ^\.github$ 18 | ^inst/rmd/refs\.bib$ 19 | ^data-raw$ 20 | ^_pkgdown\.yml$ 21 | ^pkgdown$ 22 | ^CRAN-SUBMISSION$ 23 | ^cran-comments\.md$ 24 | ^.*.geojson$ 25 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | 8 | name: R-CMD-check.yaml 9 | 10 | permissions: read-all 11 | 12 | jobs: 13 | R-CMD-check: 14 | runs-on: ${{ matrix.config.os }} 15 | 16 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 17 | 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | config: 22 | # - {os: macos-latest, r: 'release'} 23 | # - {os: windows-latest, r: 'release'} 24 | # - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 25 | - {os: ubuntu-latest, r: 'release'} 26 | # - {os: ubuntu-latest, r: 'oldrel-1'} 27 | 28 | env: 29 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 30 | R_KEEP_PKG_SOURCE: yes 31 | 32 | steps: 33 | - uses: actions/checkout@v4 34 | 35 | - uses: r-lib/actions/setup-pandoc@v2 36 | 37 | - uses: r-lib/actions/setup-r@v2 38 | with: 39 | r-version: ${{ matrix.config.r }} 40 | http-user-agent: ${{ matrix.config.http-user-agent }} 41 | use-public-rspm: true 42 | 43 | - uses: r-lib/actions/setup-r-dependencies@v2 44 | with: 45 | extra-packages: any::rcmdcheck 46 | needs: check 47 | 48 | - uses: r-lib/actions/check-r-package@v2 49 | with: 50 | upload-snapshots: true 51 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 52 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | release: 8 | types: [published] 9 | workflow_dispatch: 10 | 11 | name: pkgdown.yaml 12 | 13 | permissions: read-all 14 | 15 | jobs: 16 | pkgdown: 17 | runs-on: ubuntu-latest 18 | # Only restrict concurrency for non-PR jobs 19 | concurrency: 20 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 21 | env: 22 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 23 | permissions: 24 | contents: write 25 | steps: 26 | - uses: actions/checkout@v4 27 | 28 | - uses: r-lib/actions/setup-pandoc@v2 29 | 30 | - uses: r-lib/actions/setup-r@v2 31 | with: 32 | use-public-rspm: true 33 | 34 | - uses: r-lib/actions/setup-r-dependencies@v2 35 | with: 36 | extra-packages: any::pkgdown, local::. 37 | needs: website 38 | 39 | - name: Build site 40 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 41 | shell: Rscript {0} 42 | 43 | - name: Deploy to GitHub pages 🚀 44 | if: github.event_name != 'pull_request' 45 | uses: JamesIves/github-pages-deploy-action@v4.5.0 46 | with: 47 | clean: false 48 | branch: gh-pages 49 | folder: docs 50 | -------------------------------------------------------------------------------- /.github/workflows/pr-commands.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | issue_comment: 3 | types: [created] 4 | name: Commands 5 | jobs: 6 | document: 7 | if: startsWith(github.event.comment.body, '/document') 8 | name: document 9 | runs-on: macOS-latest 10 | env: 11 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: r-lib/actions/pr-fetch@master 15 | with: 16 | repo-token: ${{ secrets.GITHUB_TOKEN }} 17 | - uses: r-lib/actions/setup-r@master 18 | - name: Install dependencies 19 | run: Rscript -e 'install.packages(c("remotes", "roxygen2"))' -e 'remotes::install_deps(dependencies = TRUE)' 20 | - name: Document 21 | run: Rscript -e 'roxygen2::roxygenise()' 22 | - name: commit 23 | run: | 24 | git config --local user.email "actions@github.com" 25 | git config --local user.name "GitHub Actions" 26 | git add man/\* NAMESPACE 27 | git commit -m 'Document' 28 | - uses: r-lib/actions/pr-push@master 29 | with: 30 | repo-token: ${{ secrets.GITHUB_TOKEN }} 31 | style: 32 | if: startsWith(github.event.comment.body, '/style') 33 | name: style 34 | runs-on: macOS-latest 35 | env: 36 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 37 | steps: 38 | - uses: actions/checkout@v2 39 | - uses: r-lib/actions/pr-fetch@master 40 | with: 41 | repo-token: ${{ secrets.GITHUB_TOKEN }} 42 | - uses: r-lib/actions/setup-r@master 43 | - name: Install dependencies 44 | run: Rscript -e 'install.packages("styler")' 45 | - name: Style 46 | run: Rscript -e 'styler::style_pkg()' 47 | - name: commit 48 | run: | 49 | git config --local user.email "actions@github.com" 50 | git config --local user.name "GitHub Actions" 51 | git add \*.R 52 | git commit -m 'Style' 53 | - uses: r-lib/actions/pr-push@master 54 | with: 55 | repo-token: ${{ secrets.GITHUB_TOKEN }} 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | inst/doc 2 | .Rhistory 3 | .RData 4 | .Rproj.user 5 | .DS_Store 6 | julyukrelease_tcm77-369384.xls 7 | wu03ew_v2.csv 8 | *.csv 9 | docs/ 10 | inst/libs/ 11 | inst/pct-slides.html 12 | inst/pct_training_solutions.html 13 | inst/rmd/.gitignore 14 | inst/rmd/libs/ 15 | inst/rmd/pct-slides_files/ 16 | inst/rmd/refs.bib 17 | refs.bib 18 | *.Rds 19 | libs 20 | *.html 21 | *_files 22 | *.geojson 23 | docs 24 | pctqgis* 25 | *cache* 26 | *rsconnect* 27 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: pct 2 | Type: Package 3 | Title: Propensity to Cycle Tool 4 | Version: 0.10.0 5 | Authors@R: c( 6 | person("Robin", "Lovelace", email = "rob00x@gmail.com", role = c("aut", "cre"), 7 | comment = c(ORCID = "0000-0001-5679-6536")), 8 | person("Layik", "Hama", email = "layik.hama@gmail.com", role = c("aut"), 9 | comment = c(ORCID = "0000-0003-1912-4890")), 10 | person("Nathanael", "Sheehan", role = c("ctb"), 11 | comment = c(ORCID = "0000-0002-2779-0976")) 12 | ) 13 | Description: Functions and example data to teach and 14 | increase the reproducibility of the methods and code underlying 15 | the Propensity to Cycle Tool (PCT), a research project and web application 16 | hosted at . 17 | For an academic paper on the methods, 18 | see Lovelace et al (2017) . 19 | Depends: R (>= 3.5.0) 20 | License: GPL-3 21 | URL: https://itsleeds.github.io/pct/, https://github.com/ITSLeeds/pct 22 | BugReports: https://github.com/ITSLeeds/pct/issues 23 | Encoding: UTF-8 24 | LazyData: true 25 | Imports: 26 | boot, 27 | stplanr (>= 0.2.8), 28 | readr, 29 | sf, 30 | crul 31 | Suggests: 32 | covr, 33 | curl, 34 | dplyr, 35 | ggplot2, 36 | knitr, 37 | leaflet, 38 | pbapply, 39 | remotes, 40 | rmarkdown, 41 | tmap, 42 | bookdown 43 | VignetteBuilder: knitr 44 | RoxygenNote: 7.2.3 45 | Roxygen: list(markdown = TRUE) 46 | Language: en-GB 47 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(get_centroids_ew) 4 | export(get_desire_lines) 5 | export(get_od) 6 | export(get_pct) 7 | export(get_pct_centroids) 8 | export(get_pct_lines) 9 | export(get_pct_rnet) 10 | export(get_pct_routes_fast) 11 | export(get_pct_routes_quiet) 12 | export(get_pct_zones) 13 | export(model_pcycle_pct_2020) 14 | export(uptake_pct_ebike_2020) 15 | export(uptake_pct_godutch) 16 | export(uptake_pct_godutch_2020) 17 | export(uptake_pct_godutch_school2) 18 | export(uptake_pct_govtarget) 19 | export(uptake_pct_govtarget_2020) 20 | export(uptake_pct_govtarget_school2) 21 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # pct 0.10.0 (March 2025) 2 | 3 | - Remove un-necessary tests 4 | - Assorted changes for CRAN 5 | 6 | # pct 0.9.9 (May 2023) 7 | 8 | - Fix for CRAN check errors (pass tests without internet) (#124) 9 | - Update README with test instructions 10 | - Ignore large .geojson files in .Rbuildignore 11 | - Prevent high uptake numbers for long distances (#125) 12 | 13 | # pct 0.9.8 (February 2023) 14 | 15 | - Remove tidyverse (#123) 16 | 17 | # pct 0.9.7 18 | 19 | - Additional changes for CRAN (#222) 20 | - Message rather than error if resource unavailable (#222) 21 | 22 | # pct 0.9.6 23 | 24 | - Updates to fail gracefully (#220) 25 | 26 | # pct 0.9.5 (November 2022) 27 | 28 | - Updated docs to fix CRAN check errors (#119) 29 | 30 | # pct 0.9.4 (November 2021) 31 | 32 | - Typos fixed in README 33 | 34 | # pct 0.9.3 (November 2021) 35 | 36 | - Updated URLs to comply with CRAN policies 37 | - Make package tests quicker 38 | 39 | # pct 0.9.2 40 | 41 | - New vignette on getting and modelling PCT data 42 | 43 | # pct 0.9.1 44 | 45 | - Minor updates to vignettes to support `sf` 1.0 (#101) 46 | 47 | # pct 0.9.0 48 | 49 | - New function `uptake_pct_ebike_2020()` 50 | 51 | # pct 0.8.0 52 | 53 | - Check and update hilliness if the seem to be in incorrect units, and document (#70) 54 | - New vignette contributed by Nathanael Sheehan 55 | 56 | # pct 0.7.0 57 | 58 | - New argument `verbose`, false by default, in uptake functions 59 | - Updated training vignette 60 | - Updated introductory `pct` vignette 61 | - Fix in code for checks 62 | 63 | # pct 0.6.0 64 | 65 | - New functions `uptake_pct_govtarget_school2()` and `uptake_pct_godutch_school2()` 66 | - Enables download of other OD datasets with `get_od()` (#66) 67 | 68 | # pct 0.5.0 69 | 70 | - New `uptake_pct_govtarget_2020()` and `uptake_pct_godutch_2020()` functions approximate the uptake models used in the updated 2020 release of the PCT results 71 | - New `model_pcycle_pct_2020()` function to estimate cycling levels as a function of distance and hilliness, using the uptake function in the 2020 release of the PCT. 72 | - Fix parameters in `uptake_pct_godutch()` (#65) 73 | - Higher resolution LSOA data now the default (`geography = 'lsoa'`) 74 | 75 | # pct 0.4.1 76 | 77 | - Update vignette for recent version of `sf` (#64) 78 | 79 | # pct 0.4.0 80 | 81 | - Read `.geojson` files directly rather than downloading old `.Rds` files that were generating error messages - see https://github.com/ITSLeeds/pct/issues/57 82 | - Do not test time-consuming tests on CRAN as per #58 83 | 84 | # pct 0.3.0 85 | 86 | - Always returns objects with EPSG code 4326, see https://github.com/ropensci/stats19/issues/135 87 | 88 | # pct 0.2.7 89 | 90 | - Improved documentation for godutch uptake function 91 | - Uptake function now work when there are NAs in the distances (previously they generated errors) 92 | 93 | # pct 0.2.5 94 | 95 | - Fixed issue due to government data provider endpoint being down: https://github.com/ITSLeeds/pct/issues/51 96 | 97 | # pct 0.2.4 98 | 99 | - Updated vignettes use `tmap` instead of leaflet for easy-to-type map making code 100 | 101 | # pct 0.2.3 102 | 103 | - Remove OD pairs with no matching IDs, see https://github.com/ITSLeeds/pct/issues/47 104 | 105 | # pct 0.2.2 106 | 107 | - Updated training materials 108 | - Fixed bug in `get_od()` 109 | 110 | # pct 0.2.1 111 | 112 | - `get_od()` now gets national data by default 113 | 114 | # pct 0.2.0 115 | 116 | - Updates to allow od data to be downloaded for `pct_regions`: see https://github.com/ITSLeeds/pct/issues/44 117 | 118 | # pct 0.1.3 119 | 120 | - Fix issue with `rnet` downloads - see https://github.com/ITSLeeds/pct/issues/45 121 | 122 | # pct 0.1.2 123 | 124 | - Bug fix: `get_pct_centroids()` and `get_pct_zones()` now work as intended 125 | - Minor updates to vignettes 126 | 127 | # pct 0.1.1 128 | - Minor CRAN doi 129 | 130 | # pct 0.1.0 131 | * First release. 132 | -------------------------------------------------------------------------------- /R/data.R: -------------------------------------------------------------------------------- 1 | #' Example OD data for Leeds 2 | #' 3 | #' `od_leeds` contains the 100 most travelled work desire lines in Leeds, 4 | #' according to the 2011 Census. 5 | #' 6 | #' @docType data 7 | #' @keywords datasets 8 | #' @name od_leeds 9 | #' @examples 10 | #' # see data-raw folder for generation code 11 | #' od_leeds 12 | NULL 13 | 14 | #' Zone data for Leeds 15 | #' 16 | #' Zones in Leeds 17 | #' 18 | #' @docType data 19 | #' @keywords datasets 20 | #' @name zones_leeds 21 | #' @examples 22 | #' # see data-raw folder for generation code 23 | #' zones_leeds 24 | NULL 25 | 26 | #' Route network for Leeds 27 | #' 28 | #' @docType data 29 | #' @keywords datasets 30 | #' @name rnet_leeds 31 | #' @examples 32 | #' # see data-raw folder for generation code 33 | #' rnet_leeds 34 | NULL 35 | 36 | #' Cycle route desire lines for Leeds 37 | #' 38 | #' @docType data 39 | #' @keywords datasets 40 | #' @name desire_lines_leeds 41 | #' @examples 42 | #' # see data-raw folder for generation code 43 | #' desire_lines_leeds 44 | NULL 45 | 46 | #' Fastest cycle routes for the desire_lines_leeds 47 | #' 48 | #' @docType data 49 | #' @keywords datasets 50 | #' @name routes_fast_leeds 51 | #' @examples 52 | #' # see data-raw folder for generation code 53 | #' routes_fast_leeds 54 | NULL 55 | 56 | #' Top 15 min mean journy times within Leeds from Uber 57 | #' 58 | #' Data downloaded 4th March 2019. 59 | #' According to Uber, the dataset is from: 60 | #' 1/1/2018 - 1/31/2018 (Every day, Daily Average) 61 | #' 62 | #' @docType data 63 | #' @keywords datasets 64 | #' @name leeds_uber_sample 65 | #' @examples 66 | #' # see data-raw folder for generation code 67 | #' leeds_uber_sample 68 | NULL 69 | 70 | #' Mode names in the Census 71 | #' 72 | #' And conversion into R-friendly versions 73 | #' 74 | #' @docType data 75 | #' @keywords datasets 76 | #' @name mode_names 77 | #' @examples 78 | #' mode_names 79 | NULL 80 | 81 | #' PCT regions from www.pct.bike 82 | #' 83 | #' See data-raw folder for generation code 84 | #' 85 | #' @docType data 86 | #' @keywords datasets 87 | #' @name pct_regions 88 | #' @examples 89 | #' pct_regions 90 | NULL 91 | 92 | #' Zones in central Santiago 93 | #' 94 | #' See https://github.com/pedalea/pctSantiago folder for generation code 95 | #' 96 | #' @docType data 97 | #' @keywords datasets 98 | #' @name santiago_zones 99 | #' @examples 100 | #' # u = "https://github.com/pedalea/pctSantiago/releases/download/0.0.1/z_centre.Rds" 101 | #' # download.file(u, destfile = "z_centre.Rds", mode = "wb") 102 | #' # santiago_zones = readRDS("z_centre.Rds") 103 | #' santiago_zones 104 | NULL 105 | 106 | #' Desire lines in central Santiago 107 | #' 108 | #' See https://github.com/pedalea/pctSantiago folder for generation code 109 | #' 110 | #' @docType data 111 | #' @keywords datasets 112 | #' @name santiago_lines 113 | #' @examples 114 | #' # u = "https://github.com/pedalea/pctSantiago/releases/download/0.0.1/od_agg_zone_sub.Rds" 115 | #' # download.file(u, destfile = "od_agg_zone_sub.Rds") 116 | #' # desire_lines = readRDS("od_agg_zone_sub.Rds") 117 | #' santiago_zones 118 | NULL 119 | 120 | #' OD data in central Santiago 121 | #' 122 | #' See https://github.com/pedalea/pctSantiago folder for generation code 123 | #' 124 | #' @docType data 125 | #' @keywords datasets 126 | #' @name santiago_od 127 | #' @examples 128 | #' # u = "https://github.com/pedalea/pctSantiago/releases/download/0.0.1/santiago_od.Rds" 129 | #' # download.file(u, destfile = "santiago_od.Rds", mode = "wb") 130 | #' # santiago_od = readRDS("santiago_od.Rds") 131 | #' santiago_od 132 | NULL 133 | 134 | #' Desire lines from the PCT for the Isle of Wight 135 | #' 136 | #' This data was obtained using code shown in the introductory 137 | #' [pct package vignette](https://itsleeds.github.io/pct/articles/pct.html). 138 | #' 139 | #' @docType data 140 | #' @keywords datasets 141 | #' @name wight_lines_30 142 | #' @aliases wight_lines_pct 143 | #' @examples 144 | #' names(wight_lines_30) 145 | #' plot(wight_lines_30) 146 | NULL 147 | 148 | #' Zones and centroid data from the PCT for the Isle of Wight 149 | #' 150 | #' This data was obtained using code shown in the introductory 151 | #' [pct package vignette](https://itsleeds.github.io/pct/articles/pct.html). 152 | #' 153 | #' @docType data 154 | #' @keywords datasets 155 | #' @name wight_zones 156 | #' @aliases wight_centroids 157 | #' @examples 158 | #' library(sf) 159 | #' names(wight_lines_30) 160 | #' plot(wight_lines_30) 161 | NULL 162 | 163 | #' Official origin-destination data for the Isle of Wight 164 | #' 165 | #' This data was obtained using code shown in the introductory 166 | #' [pct package vignette](https://itsleeds.github.io/pct/articles/pct.html). 167 | #' 168 | #' @docType data 169 | #' @keywords datasets 170 | #' @name wight_od 171 | #' @examples 172 | #' names(wight_od) 173 | #' head(wight_od) 174 | NULL 175 | 176 | #' Cycle route data for the Isle of Wight 177 | #' 178 | #' This data was obtained using code shown in the introductory 179 | #' [pct package vignette](https://itsleeds.github.io/pct/articles/pct.html). 180 | #' 181 | #' @docType data 182 | #' @keywords datasets 183 | #' @name wight_routes_30 184 | #' @aliases wight_rnet wight_routes_30_cs 185 | #' @examples 186 | #' library(sf) 187 | #' names(wight_routes_30) 188 | #' head(wight_routes_30) 189 | #' plot(wight_routes_30) 190 | NULL 191 | 192 | #' 200 cycle routes in central Santiago, Chile 193 | #' 194 | #' This data was obtained using code shown in the 195 | #' International application of the PCT methods 196 | #' [vignette](https://itsleeds.github.io/pct/articles/pct-international.html). 197 | #' 198 | #' @docType data 199 | #' @keywords datasets 200 | #' @name santiago_routes_cs 201 | #' @examples 202 | #' library(sf) 203 | #' names(santiago_routes_cs) 204 | #' head(santiago_routes_cs) 205 | #' plot(santiago_routes_cs) 206 | NULL 207 | 208 | #' Lookup table matching PCT regions to local authorities 209 | #' 210 | #' For matching pct_regions object with local authority names in England and Wales. 211 | #' 212 | #' @docType data 213 | #' @keywords datasets 214 | #' @name pct_regions_lookup 215 | #' @examples 216 | #' 217 | #' names(pct_regions_lookup) 218 | #' head(pct_regions_lookup) 219 | NULL 220 | 221 | 222 | -------------------------------------------------------------------------------- /R/desire_lines.R: -------------------------------------------------------------------------------- 1 | #' Desire lines 2 | #' 3 | #' This function generates "desire lines" from census 2011 data. 4 | #' By default gets all desire lines from census in region, but 5 | #' can get the top `n`. 6 | #' 7 | #' @inheritParams get_od 8 | #' 9 | #' @export 10 | #' @examples \donttest{ 11 | #' if(curl::has_internet()) { 12 | #' desire_lines = get_desire_lines("wight") 13 | #' plot(desire_lines) 14 | #' intra_zonal = desire_lines$geo_code1 == desire_lines$geo_code2 15 | #' plot(desire_lines[intra_zonal, ]) 16 | #' } 17 | #' } 18 | get_desire_lines = function(region = NULL, n = NULL, omit_intrazonal = FALSE) { 19 | 20 | if(is.null(region)){ 21 | stop("Select a region or local authority name.") 22 | } 23 | # TODO: explore ways of returning 'intrazonal' flows 24 | od_all = get_od(region, omit_intrazonal = omit_intrazonal) 25 | # get UK zones with msoa11cd, msoa11nm and the geom for stplanr::od2line 26 | message("Downloading population weighted centroids") 27 | zones_all = get_centroids_ew() # TODO: some warning? 28 | zones = zones_all[grepl(region, zones_all$msoa11nm, ignore.case = TRUE), ] 29 | od = od_all 30 | if(!is.null(n)) { 31 | od = order_and_subset(od_all, "all", n) # subset before processing 32 | } 33 | flow_origs_in_zones = od$geo_code1 %in% zones$msoa11cd 34 | if(!all(flow_origs_in_zones)) { 35 | n_rem = sum(!flow_origs_in_zones) 36 | message("Not all flows origins have ID in centroids, removing ", n_rem, " OD pairs.") 37 | od = od[flow_origs_in_zones, ] 38 | } 39 | flow_dests_in_zones = od$geo_code2 %in% zones$msoa11cd 40 | if(!all(flow_dests_in_zones)) { 41 | n_rem = sum(!flow_dests_in_zones) 42 | message("Not all flows origins have ID in centroids, removing ", n_rem, " OD pairs.") 43 | od = od[flow_dests_in_zones, ] 44 | } 45 | # generate desire lines 46 | region_desire_lines = stplanr::od2line(flow = od, zones) 47 | region_desire_lines 48 | } 49 | #' Get origin destination data from the 2011 Census 50 | #' 51 | #' This function downloads a .csv file representing movement 52 | #' between MSOA zones in England and Wales. 53 | #' By default it returns national data, but 54 | #' `region` can be set to subset the output to a specific 55 | #' local authority or region. 56 | #' 57 | #' OD datasets available include wu03uk_v3 58 | #' and others listed on the Wicid website. 59 | #' 60 | #' @param n top n number of destinations with most trips in the 2011 census 61 | #' within the `region`. 62 | #' @param type the type of subsetting: one of `from`, `to` or `within`, specifying how 63 | #' the od dataset should be subset in relation to the `region`. 64 | #' @param base_url the base url where the OD dataset is stored 65 | #' @param filename the name of the file to download, if not the default MSOA level 66 | #' data. 67 | #' @param omit_intrazonal should intrazonal OD pairs be omited from result? 68 | #' `FALSE` by default. 69 | #' @param u full url of file to download 70 | #' @inheritParams get_pct 71 | #' @export 72 | #' @examples \donttest{ 73 | #' get_od("wight", n = 3) 74 | #' get_od() 75 | #' get_od(filename = "wu03uk_v3") 76 | #' u = "https://www.nomisweb.co.uk/output/census/2011/wf02ew_oa.zip" 77 | #' # get_od(u = u) 78 | #' } 79 | get_od = function(region = NULL, 80 | n = NULL, 81 | type = "within", 82 | omit_intrazonal = FALSE, 83 | base_url = paste0("https://s3-eu-west-1.amazonaws.com/", 84 | "statistics.digitalresources.jisc.ac.uk", 85 | "/dkan/files/FLOW/"), 86 | filename = "wu03ew_v2", 87 | u = NULL) { 88 | 89 | if(length(region) > 1L) { 90 | stop("region must be of length 0 or 1") 91 | } 92 | 93 | # Set the local edition for readr. 94 | # See https://github.com/ropensci/stats19/issues/205 95 | if (.Platform$OS.type == "windows" && utils::packageVersion("readr") >= "2.0.0") { 96 | readr::local_edition(1) 97 | } 98 | 99 | if(!is.null(region)) { 100 | 101 | valid_region = region %in% c(pct_regions_lookup$region_name) 102 | valid_region_match = grepl(region, pct_regions_lookup$region_name) 103 | valid_la_match = grepl(region, pct_regions_lookup$lad16cd) 104 | if(!valid_region & !any(valid_la_match) & !any(valid_region_match)) { 105 | stop("region must contain a valid name in the pct_regions_lookup") 106 | } 107 | 108 | # find matching las 109 | if(valid_region) { 110 | las = pct_regions_lookup$lad16nm[pct_regions_lookup$region_name %in% region] 111 | } else { 112 | las = pct_regions_lookup$lad16nm[grepl(pattern = region, pct_regions_lookup$lad16nm, ignore.case = TRUE)] 113 | } 114 | } 115 | 116 | if(is.na(region) || (region == "") || !is.character(region)) { 117 | if(is.null(region)) { 118 | message("No region provided. Returning national OD data.") 119 | } else { 120 | stop("invalid region name") 121 | } 122 | } 123 | 124 | if(!is.null(u)) { 125 | filename = basename(u) 126 | zip_file = file.path(tempdir(), paste0(filename)) 127 | census_file = file.path(tempdir(), paste0(filename, ".csv")) 128 | file_url = u 129 | if(!exists(census_file)) { 130 | file_url_ok = crul::ok(file_url) 131 | if(!file_url_ok) { 132 | message("URL not available: ", file_url) 133 | return(NULL) 134 | } 135 | utils::download.file(file_url, zip_file) 136 | utils::unzip(zip_file, exdir = tempdir()) 137 | } 138 | csv_files = list.files(tempdir(), pattern = "csv", recursive = TRUE, full.names = TRUE) 139 | message("Unzipped the following files: ", csv_files) 140 | message("Reading-in the first") 141 | od_all = readr::read_csv(csv_files[1]) 142 | return(od_all) 143 | # pct:::rename_od_variables(names(od_all)) 144 | } 145 | 146 | # get the census file to read the trip counts 147 | zip_file = file.path(tempdir(), paste0(filename, ".zip")) 148 | census_file = file.path(tempdir(), paste0(filename, ".csv")) 149 | file_url = paste0(base_url, filename, "/", filename, ".zip") 150 | if(!exists(census_file)) { 151 | file_url_ok = crul::ok(file_url) 152 | if(!file_url_ok) { 153 | message("URL not available: ", file_url) 154 | return(NULL) 155 | } 156 | utils::download.file(file_url, zip_file) 157 | utils::unzip(zip_file, exdir = tempdir()) 158 | } 159 | od_all = readr::read_csv(census_file) 160 | # format columns 161 | names(od_all) = rename_od_variables(names(od_all)) 162 | 163 | if(!filename == "wu03ew_v2") { 164 | return(od_all) 165 | } 166 | 167 | # get centroids to provide zone name lookup 168 | zones_all = get_centroids_ew() # TODO: some warning? 169 | od_all$geo_name1 = zones_all$msoa11nm[match(od_all$geo_code1, zones_all$msoa11cd)] 170 | od_all$geo_name2 = zones_all$msoa11nm[match(od_all$geo_code2, zones_all$msoa11cd)] 171 | 172 | od_all$la_1 = gsub(" [0-9][0-9][0-9]", replacement = "", x = od_all$geo_name1) 173 | od_all$la_2 = gsub(" [0-9][0-9][0-9]", replacement = "", x = od_all$geo_name2) 174 | 175 | if(is.null(region)) { 176 | return(od_all) 177 | } 178 | 179 | if(omit_intrazonal) { 180 | od_all = od_all[od_all$geo_code1 != od_all$geo_code2, ] 181 | } 182 | # is region valid? do it once 183 | 184 | # if(type == "within") { 185 | # grepl(region, od_all$geo_name1, ignore.case = TRUE) & 186 | # grepl(region, od_all$geo_name2, ignore.case = TRUE) 187 | # } 188 | 189 | od = od_all[od_all$la_1 %in% las, ] 190 | 191 | # finally 192 | if(!is.null(n)) { 193 | od = order_and_subset(od, "all", n) 194 | } 195 | od 196 | } 197 | 198 | order_and_subset = function(od, var, n) { 199 | 200 | od = od[order(od[[var]], decreasing = TRUE), ] 201 | od[1:n, ] 202 | 203 | } 204 | 205 | # does this want to be exported at some point? 206 | # x = c("region of residence", "Area of workplace", "All categories: 207 | # Method of travel to work", 208 | # "Work mainly at or from home", "Underground, metro, light rail, tram", 209 | # "Train", "Bus, minibus or coach", "Taxi", "Motorcycle, scooter or moped", 210 | # "Driving a car or van", "Passenger in a car or van", "Bicycle", 211 | # "On foot", "Other method of travel to work") 212 | # rename_od_variables(x) 213 | rename_od_variables = function(x){ 214 | pct::mode_names$variable[match(x, pct::mode_names$census_name)] 215 | } 216 | -------------------------------------------------------------------------------- /R/get_pct.R: -------------------------------------------------------------------------------- 1 | #' Generic function to get regional data from the PCT 2 | #' 3 | #' This function gets data generated for the Propensity to Cycle Tool 4 | #' project and returns objects in the modern `sf` class. 5 | #' 6 | #' @param base_url Where the data is stored. 7 | #' @param purpose Trip purpose (typically `school` or `commute`) 8 | #' @param geography Geographic resolution of outputs, `msoa` or `lsoa` (the default) 9 | #' @param region The PCT region or local authority to download data from (e.g. `west-yorkshire` or `Leeds`). 10 | #' See `View(pct_regions_lookup)` for a full list of possible region names. 11 | #' @param layer The PCT layer of interest, `z`, `c`, `l`, `rf`, `rq` or `rnet` 12 | #' for zones, centroids, desire lines, routes (fast or quiet) and route networks, respectively 13 | #' @param extension The type of file to download (only `.geojson` supported at present) 14 | #' @param national Download nationwide data? `FALSE` by default 15 | #' @export 16 | #' @examples 17 | #' \dontrun{ 18 | #' rf = get_pct(region = "isle-of-wight", layer = "rf") 19 | #' names(rf)[1:20] 20 | #' vars_to_plot = 10:13 21 | #' plot(rf[vars_to_plot]) 22 | #' z = get_pct(region = "isle-of-wight", layer = "z") 23 | #' rf = get_pct(region = "west-yorkshire", layer = "rf") 24 | #' z_all = get_pct(layer = "z", national = TRUE) 25 | #' } 26 | get_pct = function( 27 | base_url = "https://github.com/npct/pct-outputs-regional-notR/raw/master", 28 | purpose = "commute", 29 | geography = "lsoa", 30 | region = NULL, 31 | layer = NULL, 32 | extension = ".geojson", 33 | national = FALSE 34 | ) { 35 | layers = c("z", "c", "l", "rf", "rq", "rnet") 36 | if(is.null(layer) || !is.element(layer, layers)) 37 | stop(c("Layer needs to be one of: ", 38 | paste0(layers, collapse = ", "), ".")) 39 | if(national & is.character(purpose)) { 40 | extension = ".Rds" 41 | layer = paste0(layer, "_all") 42 | base_url = "https://github.com/npct/pct-outputs-national/raw/master" 43 | u_folder = paste(base_url, purpose, geography, sep = "/") 44 | f = paste0(layer, extension) 45 | u_file = paste(u_folder, f, sep = "/") 46 | read_url = function(u) readRDS(url(u)) 47 | res_sp = read_pct(u_file, read_url) 48 | return(sf_object = sf::st_as_sf(res_sp)) 49 | } else { 50 | if(length(region) != 1L) 51 | stop("'region' must be of length 1") 52 | if(is.na(region) || (region == "") || !is.character(region)) 53 | stop("invalid region name") 54 | if(geography == "msoa" && layer == "rnet") { 55 | message("No MSOA route network data available, downloading LSOA data") 56 | geography = "lsoa" 57 | } 58 | if(layer == "rnet") { 59 | layer = "rnet_full" 60 | } 61 | u_folder = paste(base_url, purpose, geography, region, sep = "/") 62 | f = paste0(layer, extension) 63 | u_file = paste(u_folder, f, sep = "/") 64 | } 65 | read_pct(u_file, fun = sf::read_sf) 66 | } 67 | #' Get zone results from the PCT 68 | #' 69 | #' Wrapper around `[get_pct()]` that gets zone data from the PCT. 70 | #' 71 | #' @inheritParams get_pct 72 | #' @export 73 | #' @examples 74 | #' \dontrun{ 75 | #' # don't test to reduce build times 76 | #' z = get_pct_zones("isle-of-wight") 77 | #' plot(z) 78 | #' } 79 | get_pct_zones = function( 80 | region = NULL, 81 | purpose = "commute", 82 | geography = "lsoa", 83 | extension = ".geojson" 84 | ) { 85 | get_pct(base_url = "https://github.com/npct/pct-outputs-regional-notR/raw/master", 86 | purpose, geography, region, 87 | layer = "z", 88 | extension = ".geojson") 89 | } 90 | 91 | #' Get centroid results from the PCT 92 | #' 93 | #' Wrapper around `[get_pct()]` that gets centroid data from the PCT. 94 | #' 95 | #' @inheritParams get_pct 96 | #' @export 97 | #' @examples 98 | #' \dontrun{ 99 | #' # don't test to reduce build times 100 | #' c = get_pct_centroids("isle-of-wight") 101 | #' plot(c) 102 | #' } 103 | get_pct_centroids = function( 104 | region = NULL, 105 | purpose = "commute", 106 | geography = "lsoa", 107 | extension = ".geojson" 108 | ) { 109 | get_pct(base_url = 110 | "https://github.com/npct/pct-outputs-regional-notR/raw/master", 111 | purpose, geography, region, 112 | layer = "c", 113 | extension = ".geojson") 114 | } 115 | 116 | #' Get desire lines results from the PCT 117 | #' 118 | #' Wrapper around `[get_pct()]` that gets l (lines) data from the PCT. 119 | #' 120 | #' @inheritParams get_pct 121 | #' @export 122 | #' @examples 123 | #' \dontrun{ 124 | #' # don't test to reduce build times 125 | #' l = get_pct_lines("isle-of-wight") 126 | #' plot(l) 127 | #' } 128 | get_pct_lines = function( 129 | region = NULL, 130 | purpose = "commute", 131 | geography = "lsoa", 132 | extension = ".geojson" 133 | ) { 134 | get_pct(base_url = 135 | "https://github.com/npct/pct-outputs-regional-notR/raw/master", 136 | purpose, geography, region, 137 | layer = "l", 138 | extension = ".geojson") 139 | } 140 | 141 | #' Get fast road network results from the PCT 142 | #' 143 | #' Wrapper around `[get_pct()]` that gets rf data from the PCT. 144 | #' 145 | #' @inheritParams get_pct 146 | #' @export 147 | #' @examples 148 | #' \dontrun{ 149 | #' # don't test to reduce build times 150 | #' rf = get_pct_routes_fast("isle-of-wight") 151 | #' plot(rf) 152 | #' } 153 | get_pct_routes_fast = function( 154 | region = NULL, 155 | purpose = "commute", 156 | geography = "lsoa", 157 | extension = ".geojson" 158 | ) { 159 | get_pct(base_url = 160 | "https://github.com/npct/pct-outputs-regional-notR/raw/master", 161 | purpose, geography, region, 162 | layer = "rf", 163 | extension = ".geojson") 164 | } 165 | 166 | #' Get quiet road network results from the PCT 167 | #' 168 | #' Wrapper around `[get_pct()]` that gets rq data from the PCT. 169 | #' 170 | #' @inheritParams get_pct 171 | #' @export 172 | #' @examples 173 | #' \dontrun{ 174 | #' # don't test to reduce build times 175 | #' rq = get_pct_routes_quiet("isle-of-wight") 176 | #' plot(rq) 177 | #' } 178 | get_pct_routes_quiet = function( 179 | region = NULL, 180 | purpose = "commute", 181 | geography = "lsoa", 182 | extension = ".geojson" 183 | ) { 184 | get_pct(base_url = 185 | "https://github.com/npct/pct-outputs-regional-notR/raw/master", 186 | purpose, geography, region, 187 | layer = "rq", 188 | extension = ".geojson") 189 | } 190 | 191 | #' Get route network results from the PCT 192 | #' 193 | #' Wrapper around `[get_pct()]` that gets route road network data from the PCT. 194 | #' 195 | #' @inheritParams get_pct 196 | #' @export 197 | #' @examples 198 | #' \dontrun{ 199 | #' # don't test to reduce build times 200 | #' rnet = get_pct_rnet("isle-of-wight") 201 | #' plot(rnet) 202 | #' } 203 | get_pct_rnet = function( 204 | region = NULL, 205 | purpose = "commute", 206 | geography = "lsoa", 207 | extension = ".geojson" 208 | ) { 209 | get_pct(base_url = "https://github.com/npct/pct-outputs-regional-notR/raw/master", 210 | purpose, geography, region, 211 | layer = "rnet", 212 | extension = ".geojson") 213 | } 214 | -------------------------------------------------------------------------------- /R/model.R: -------------------------------------------------------------------------------- 1 | #' Model cycling levels as a function of explanatory variables 2 | #' 3 | #' @param pcycle The proportion of trips by bike, e.g. 0.1, meaning 10% 4 | #' @param weights The weights used in the model, typically the total number of people per OD pair 5 | #' 6 | #' @inheritParams uptake_pct_govtarget 7 | #' @export 8 | #' @examples 9 | #' # l = get_pct_lines(region = "isle-of-wight") 10 | #' # l = get_pct_lines(region = "cambridgeshire") 11 | #' l = wight_lines_pct 12 | #' pcycle = l$bicycle / l$all 13 | #' pcycle_dutch = l$dutch_slc / l$all 14 | #' m1 = model_pcycle_pct_2020( 15 | #' pcycle, 16 | #' distance = l$rf_dist_km, 17 | #' gradient = l$rf_avslope_perc - 0.78, 18 | #' weights = l$all 19 | #' ) 20 | #' m2 = model_pcycle_pct_2020( 21 | #' pcycle_dutch, distance = l$rf_dist_km, 22 | #' gradient = l$rf_avslope_perc - 0.78, 23 | #' weights = l$all 24 | #' ) 25 | #' m3 = model_pcycle_pct_2020( 26 | #' pcycle_dutch, distance = l$rf_dist_km, 27 | #' gradient = l$rf_avslope_perc - 0.78, 28 | #' weights = rep(1, nrow(l)) 29 | #' ) 30 | #' m1 31 | #' plot(l$rf_dist_km, pcycle, cex = l$all / 100, ylim = c(0, 0.5)) 32 | #' points(l$rf_dist_km, m1$fitted.values, col = "red") 33 | #' points(l$rf_dist_km, m2$fitted.values, col = "blue") 34 | #' points(l$rf_dist_km, pcycle_dutch, col = "green") 35 | #' cor(l$dutch_slc, m2$fitted.values * l$all)^2 # 95% captured 36 | #' # identical means: 37 | #' mean(l$dutch_slc) 38 | #' mean(m2$fitted.values * l$all) 39 | #' pct_coefficients_2020 = c( 40 | #' alpha = -4.018 + 2.550, 41 | #' d1 = -0.6369 -0.08036, 42 | #' d2 = 1.988, 43 | #' d3 = 0.008775, 44 | #' h1 = -0.2555, 45 | #' i1 = 0.02006, 46 | #' i2 = -0.1234 47 | #' ) 48 | #' pct_coefficients_2020 49 | #' m2$coef 50 | #' plot(pct_coefficients_2020, m2$coeff) 51 | #' cor(pct_coefficients_2020, m2$coeff)^2 52 | #' cor(pct_coefficients_2020, m3$coeff)^2 # explains 95%+ variability in params 53 | model_pcycle_pct_2020 = function(pcycle, distance, gradient, weights) { 54 | pcycle[pcycle == 0] = 0.001 # 1/1000 is lowest level for logit link 55 | stats::glm(formula = pcycle ~ 56 | distance + sqrt(distance) + I(distance^2) + gradient + distance*gradient + sqrt(distance) * gradient, 57 | family = "quasibinomial", weights = weights) 58 | } 59 | -------------------------------------------------------------------------------- /R/msoa_centroids.R: -------------------------------------------------------------------------------- 1 | #' Download MSOA centroids for England and Wales 2 | #' 3 | #' Downloads and processes data on where people live in England and Wales. 4 | #' See [geoportal.statistics.gov.uk](https://geoportal.statistics.gov.uk/datasets/b0a6d8a3dc5d4718b3fd62c548d60f81_0). 5 | #' 6 | #' @export 7 | get_centroids_ew = function() { 8 | # vanished dataset 9 | # u = paste0("https://opendata.arcgis.com/datasets/b0a6d8a3dc5d4718b3fd62c548d60f81_0.csv?", 10 | # "outSR=%7B%22latestWkid%22%3A27700%2C%22wkid%22%3A27700%7D") 11 | # Check to see if there is internet connection 12 | has_internet = curl::has_internet() 13 | if (!has_internet) { 14 | stop("No internet connection") 15 | } 16 | u = "https://github.com/ITSLeeds/pct/releases/download/0.2.5/MSOA_2011_EW_PWC_COORD_V2.CSV" 17 | # suggestion: store locally if API changes again? 18 | # f = "centroids-msoa.csv" # store locally 19 | # download.file(u, f) 20 | # pwc = readr::read_csv(f) 21 | pwc = readr::read_csv(u) 22 | names(pwc) = c("msoa11cd", "msoa11nm", "X", "Y", "lon", "lat") 23 | sf::st_as_sf(x = pwc[c("X", "Y", "msoa11cd", "msoa11nm")], 24 | coords = c("X", "Y"), crs = 27700) 25 | } 26 | 27 | # Note: this is an attempt to get the LSOA data: 28 | # https://data.cdrc.ac.uk/dataset/cdrc-2011-population-weighted-centroids-gb 29 | # u = "https://data.cdrc.ac.uk/dataset/e95b3bef-11c6-4d1e-bd72-a3315e6c398d/resource/90de9c16-f064-4a11-ada1-ab1ab1b5a323/download/englandwelshscotlandpwc2011.csv" 30 | # pwc = readr::read_csv(u) 31 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | # define global variables 2 | utils::globalVariables(c("pct_regions_lookup")) 3 | 4 | read_pct = function(u_file, fun) { 5 | u_ok = crul::ok(u_file) 6 | if(!u_ok) { 7 | message("Could not find anything at this URL:\n", u_file) 8 | message("Check the region exists and internet connection") 9 | } else { 10 | fun(u_file) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/_pkgdown.yml -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | informational: true 10 | patch: 11 | default: 12 | target: auto 13 | threshold: 1% 14 | informational: true 15 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | I've finally got round to addressing the issues flagged by CRAN's auto checks and available at https://cran-archive.r-project.org/web/checks/2024/2024-11-18_check_results_pct.html Because I was slow to respond to these issues the package was removed from CRAN. I am now resubmitting. 2 | 3 | I have checked the package via the winbuilder service and there are only false positive typos and URL issues that I cannot reproduce locally. 4 | 5 | If there are any further issues please let me know and I can address, to help get the package back on CRAN. 6 | 7 | ## R CMD check results 8 | 9 | 0 errors | 0 warnings | 1 note 10 | 11 | * This is a new release. 12 | -------------------------------------------------------------------------------- /data-raw/example-data.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Generate input data for the pct package 3 | --- 4 | 5 | ```{r} 6 | knitr::opts_chunk$set(eval = FALSE) 7 | ``` 8 | 9 | 10 | ```{r} 11 | devtools::install_github("robinlovelace/ukboundaries") 12 | # install.packages(c("snakecase")) 13 | library(tidyverse) 14 | library(stplanr) 15 | library(sf) 16 | # Download file from: 17 | # https://wicid.ukdataservice.ac.uk/cider/wicid/downloads.php 18 | download.file("https://s3-eu-west-1.amazonaws.com/statistics.digitalresources.jisc.ac.uk/dkan/files/FLOW/wu03ew_v2/wu03ew_v2.zip", "~/Downloads/wu03ew_v2.zip") 19 | unzip("~/Downloads/wu03ew_v2.zip") 20 | od_all = read_csv("wu03ew_v2.csv") 21 | names(od_all) = snakecase::to_snake_case(names(od_all)) 22 | names(od_all)[3] = "all" 23 | names(od_all)[4] = "home" 24 | names(od_all)[5] = "metro" 25 | names(od_all)[7] = "bus" 26 | names(od_all)[9] = "motorcycle" 27 | names(od_all)[10] = "drive" 28 | names(od_all)[11] = "passenger" 29 | names(od_all)[14] = "other" 30 | summodes = colSums(od_all[4:14]) 31 | barplot(summodes) 32 | 33 | zones_leeds = ukboundaries::msoa2011_vsimple[ 34 | ukboundaries::msoa2011_vsimple$msoa11cd %in% 35 | ukboundaries::msoa2011_lds$geo_code,] 36 | summary(zones_leeds$msoa11cd %in% od_all$area_of_residence) 37 | od_leeds = od_all %>% 38 | filter(area_of_residence %in% zones_leeds$msoa11cd & area_of_workplace %in% zones_leeds$msoa11cd) %>% 39 | filter(area_of_residence != area_of_workplace) %>% 40 | top_n(10, all) 41 | # usethis::use_data(od_leeds, overwrite = TRUE) 42 | ``` 43 | 44 | ```{r} 45 | desire_lines_leeds = od2line(flow = od_leeds, zones_leeds) 46 | plot(desire_lines_leeds) 47 | routes_fast_leeds = line2route(l = desire_lines_leeds) 48 | # todo: replace with route() and call new cyclestreets package 49 | mapview::mapview(routes_fast_leeds) 50 | # pryr::object_size(routes_fast_leeds) 51 | # usethis::use_data(routes_fast_leeds) 52 | ``` 53 | 54 | ```{r} 55 | # model to predict cycling levels 56 | # note: function to combine these 2 geometries sensible 57 | routes_leeds = cbind( 58 | st_drop_geometry(desire_lines_leeds), 59 | st_drop_geometry(routes_fast_leeds) 60 | ) 61 | routes_leeds = sf::st_sf(routes_leeds, geometry = routes_fast_leeds$geometry) 62 | routes_leeds$geometry_straight = desire_lines_leeds$geometry 63 | 64 | plot(routes_leeds) 65 | mod1 = lm(formula = bicycle ~ all + length, data = routes_leeds) 66 | routes_leeds$scenario_1 = predict(object = mod1, routes_leeds) * 2 67 | 68 | plot(routes_leeds$bicycle, routes_leeds$scenario_1) 69 | 70 | rnet_leeds = overline2(routes_leeds, c("bicycle", "scenario_1")) 71 | 72 | plot(rnet_leeds) 73 | usethis::use_data(rnet_leeds) 74 | usethis::use_data(desire_lines_leeds) 75 | usethis::use_data(zones_leeds, overwrite = TRUE) 76 | ``` 77 | 78 | Uber 79 | ```{r uber} 80 | # download Travel_Time and LEEDS_MSOA.JSON from 81 | # https://movement.uber.com/explore/leeds/travel-times/query?lang=en-GB&lat.=53.7892619&lng.=-1.6582168&z.=10.47&si=260&ti=145&ag=msoa&dt[tpb]=ALL_DAY&dt[wd;]=1,2,3,4,5,6,7&dt[dr][sd]=2018-01-01&dt[dr][ed]=2018-01-31&cd=&sa;=-1.5490774,53.8007554&sdn=Leeds,%20UK&ta;=&tdn= 82 | 83 | mtt = read_csv("~/Downloads/Travel_Times.csv") 84 | leeds_msoa = sf::st_read("~/Downloads/leeds_msoa.json") 85 | # names(mtt) 86 | # nrow(mtt) 87 | # mapview::mapview(leeds_msoa) 88 | # summary(mtt$`Mean Travel Time (Seconds)`) 89 | # head(mtt$`Destination Display Name`) 90 | # summary(mtt$`Destination Display Name` %in% leeds_msoa$DISPLAY_NAME) 91 | geometry = leeds_msoa$geometry[leeds_msoa$DISPLAY_NAME %in% mtt$`Destination Display Name`] # subset based on mtt not msoa 92 | mtt = sf::st_sf(mtt, geometry) 93 | # journeys under 15 mins, Uber provides all despite map selection. 94 | mtt = mtt[mtt$`Mean Travel Time (Seconds)` <= 15 * 60,] 95 | leeds_uber_sample = top_n(mtt[order(mtt$`Mean Travel Time (Seconds)`), ], 10) 96 | object.size(leeds_uber_sample) # ~18,664 bytes 97 | # mapview::mapview(leeds_uber_sample) 98 | # usethis::use_data(leeds_uber_sample) 99 | ``` 100 | 101 | ## Mode names data 102 | 103 | ```{r} 104 | download.file("https://s3-eu-west-1.amazonaws.com/statistics.digitalresources.jisc.ac.uk/dkan/files/FLOW/wu03ew_v2/wu03ew_v2.zip", "~/Downloads/wu03ew_v2.zip") 105 | unzip("~/Downloads/wu03ew_v2.zip") 106 | od_all = read_csv("wu03ew_v2.csv") 107 | census_names = names(od_all) 108 | mode_names = readr::read_csv("https://github.com/npct/pct-shiny/raw/master/regions_www/www/static/02_codebooks/commute/od_l_rf_codebook.csv") 109 | names(mode_names) = c("variable", "description") 110 | mode_names$census_name = NA 111 | census_names 112 | row_from_home = c("from_home", "Works mainly from home", NA) 113 | mode_names = rbind( 114 | mode_names[1:10, ], 115 | row_from_home, 116 | mode_names[11:nrow(mode_names), ] 117 | ) 118 | cbind( 119 | mode_names$variable[c(2:3, 10:21)], 120 | census_names[c(1:4, 12, 13, 10:11, 6, 5, 7:9, 14)] 121 | ) 122 | mode_names$census_name[c(2:3, 10:21)] = census_names[c(1:4, 12, 13, 10:11, 6, 5, 7:9, 14)] 123 | # usethis::use_data(mode_names) 124 | ``` 125 | 126 | 127 | ## PCT Regions 128 | ```{r pct-regions} 129 | pct_regions = sf::read_sf("https://github.com/npct/pct-shiny/raw/master/regions_www/pct_regions_highres.geojson") 130 | pryr::object_size(pct_regions) 131 | #> 458 kB 132 | pct_regions = rmapshaper::ms_simplify(pct_regions, keep = 0.1,) 133 | pryr::object_size(pct_regions) 134 | #> 81.8 kB 135 | pct_regions = pct_regions[2] 136 | # usethis::use_data(pct_regions) 137 | ``` 138 | 139 | ## Get LA names + lookup 140 | 141 | ```{r} 142 | lads = sf::st_centroid(ukboundaries::lad2016_simple) 143 | pct_lads = sf::st_join(lads, pct_regions) 144 | pct_regions_lookup = pct_lads[c("lad16cd", "lad16nm", "region_name")] 145 | pct_regions_lookup = sf::st_drop_geometry(pct_regions_lookup) 146 | for(i in names(pct_regions_lookup)) 147 | pct_regions_lookup[[i]] = as.character(pct_regions_lookup[[i]]) 148 | names(pct_regions_lookup) 149 | od = get_od() 150 | pct_lad_names = unique(od$la_1) 151 | summary({sel = pct_regions_lookup$lad16nm %in% od$la_1}) 152 | pct_lads$lad16nm[!sel] 153 | summary({sel = pct_lad_names %in% pct_regions_lookup$lad16nm }) 154 | new_names = pct_lad_names[!sel] 155 | pct_regions_lookup$lad16nm[grepl("Hull", pct_regions_lookup$lad16nm)] = 156 | new_names[grepl("Hull", new_names)] 157 | pct_regions_lookup$lad16nm[grepl("Hereford", pct_regions_lookup$lad16nm)] = 158 | new_names[grepl("Hereford", new_names)] 159 | pct_regions_lookup$lad16nm[grepl("Bristol", pct_regions_lookup$lad16nm)] = 160 | new_names[grepl("Bristol", new_names)] 161 | head(pct_regions_lookup) 162 | usethis::use_data(pct_regions_lookup) 163 | ``` 164 | 165 | -------------------------------------------------------------------------------- /data-raw/pct_training2.R: -------------------------------------------------------------------------------- 1 | # Aim: get data on cycling potential in the Isle of Wight 2 | 3 | library(sf) 4 | library(pct) 5 | library(tidyverse) 6 | 7 | z = get_pct_zones("isle-of-wight", geography = "lsoa") 8 | plot(z) 9 | lines_all = get_pct_lines("isle-of-wight", geography = "lsoa") 10 | lines_active = lines_all %>% 11 | filter(rf_dist_km <= 5) %>% 12 | mutate(`Percent Active` = (bicycle + foot) / all * 100) 13 | 14 | library(tmap) 15 | tm_shape(lines_active) + 16 | tm_lines("Percent Active", palette = "RdYlBu") 17 | -------------------------------------------------------------------------------- /data-raw/rnets.R: -------------------------------------------------------------------------------- 1 | # Aim: explore PCT data for isle of wight 2 | 3 | # load packages 4 | library(pct) 5 | library(tidyverse) 6 | library(tmap) 7 | 8 | region_name = "isle-of-wight" 9 | # other regions are possible 10 | 11 | zones_all = get_pct_zones(region = region_name, geography = "msoa") 12 | lines_all = get_pct_lines(region = region_name, geography = "msoa") 13 | routes_all = get_pct_routes_fast(region = region_name, geography = "msoa") 14 | routes_all_quiet = get_pct_routes_quiet(region = region_name, geography = "msoa") 15 | rnet_all = get_pct_rnet(region = region_name, geography = "msoa") 16 | 17 | plot(zones_all$all, zones_all$bicycle) 18 | plot(zones_all$geometry) 19 | plot(lines_all$geometry, col = "blue", add = TRUE) 20 | plot(routes_all$geometry, col = "red", add = TRUE) 21 | lwd = rnet_all$bicycle / 10 22 | plot(rnet_all$geometry, col = "green", add = TRUE, lwd = lwd) 23 | 24 | tmap_mode("plot") 25 | tm_shape(rnet_all) + 26 | tm_lines(lwd = "bicycle", scale = 9) 27 | tmap_mode("view") 28 | tm_shape(rnet_all) + 29 | tm_lines(lwd = "bicycle", scale = 9) 30 | names(rnet_all) 31 | tm_shape(rnet_all) + 32 | tm_lines(lwd = "dutch_slc", scale = 9) + 33 | tm_shape(zones_all) + 34 | tm_borders() 35 | 36 | l_msoa = lines_all 37 | nrow(routes_all) 38 | nrow(lines_all) 39 | 40 | routes_all$potential = uptake_pct_godutch_2020( 41 | distance = l_msoa$rf_dist_km, 42 | gradient = l_msoa$rf_avslope_perc 43 | ) * l_msoa$all + l_msoa$bicycle 44 | 45 | plot(routes_all %>% select(potential)) 46 | rnet_new = overline(routes_all, attrib = "potential") 47 | plot(rnet_new) 48 | tm_shape(rnet_new) + 49 | tm_lines(lwd = "potential", scale = 9) 50 | -------------------------------------------------------------------------------- /data-raw/slides/pct-slides-ie.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "The Propensity to Cycle Tool" 3 | # subtitle: '`r emojifont::emoji("bike")`
For England and Wales' 4 | subtitle: '`r emojifont::emoji("bike")`
For England, Wales and beyond' 5 | author: "Robin Lovelace and Joey Talbot, ITS, University of Leeds" 6 | date: 'TII Safety Webinar, 2020-11-13

' 7 | output: 8 | xaringan::moon_reader: 9 | # css: ["default", "its.css"] 10 | # chakra: libs/remark-latest.min.js 11 | lib_dir: libs 12 | nature: 13 | highlightStyle: github 14 | highlightLines: true 15 | # bibliography: 16 | # - ../vignettes/ref.bib 17 | # - ../vignettes/ref_training.bib 18 | --- 19 | 20 | ```{r setup, include=FALSE, eval=FALSE} 21 | # get citations 22 | refs = RefManageR::ReadZotero(group = "418217", .params = list(collection = "JFR868KJ", limit = 100)) 23 | refs_df = as.data.frame(refs) 24 | # View(refs_df) 25 | # citr::insert_citation(bib_file = "vignettes/refs_training.bib") 26 | RefManageR::WriteBib(refs, "refs.bib") 27 | # citr::tidy_bib_file(rmd_file = "vignettes/pct_training.Rmd", messy_bibliography = "vignettes/refs_training.bib") 28 | options(htmltools.dir.version = FALSE) 29 | knitr::opts_chunk$set(message = FALSE) 30 | library(RefManageR) 31 | BibOptions(check.entries = FALSE, 32 | bib.style = "authoryear", 33 | cite.style = 'alphabetic', 34 | style = "markdown", 35 | first.inits = FALSE, 36 | hyperlink = FALSE, 37 | dashed = FALSE) 38 | my_bib = refs 39 | ``` 40 | 41 | ```{r, include=FALSE} 42 | library(RefManageR) 43 | my_bib = RefManageR::ReadBib("~/itsleeds/pct/inst/rmd/refs.bib") 44 | ``` 45 | 46 | ```{r, eval=FALSE, echo=FALSE, engine='bash'} 47 | # publish results online 48 | cp -Rv ~/itsleeds/pct/data-raw/slides/pct-slides-i* ~/saferactive/site/static/slides/ 49 | # cp -Rv inst/rmd/libs ~/saferactive/site/static/slides/ 50 | cd ~/saferactive/site 51 | git add -A 52 | git status 53 | git commit -am 'Update slides' 54 | git push 55 | cd - 56 | 57 | ``` 58 | 59 | 60 | background-image: url(https://media.giphy.com/media/YlQQYUIEAZ76o/giphy.gif) 61 | background-size: cover 62 | class: center, middle 63 | 64 | # How the PCT works 65 | 66 | --- 67 | 68 | ## The first prototype of the PCT 69 | 70 | - 1st prototype: Hackathon at ODI Leeds in February 2015 71 | 72 | - We identifying key routes and mapped them 73 | 74 | - For description of aims, see Lovelace et al. (2017) 75 | 76 | ```{r, echo=FALSE} 77 | knitr::include_graphics("https://raw.githubusercontent.com/npct/pct-team/master/figures/early.png") 78 | ``` 79 | 80 | --- 81 | 82 | 83 | 84 | - Launched in 2017 with the Cycling and Walking Investment Strategy ([CWIS](https://www.gov.uk/government/publications/cycling-and-walking-investment-strategy)) 85 | 86 | ![](https://raw.githubusercontent.com/npct/pct-team/master/figures/front-page-leeds-pct-demo.png) 87 | 88 | Photo: demo of the PCT to Secretary of State for Transport ([March 2017](https://environment.leeds.ac.uk/transport/news/article/187/research-showcased-to-secretary-of-state)) 89 | 90 | --- 91 | 92 | ## The important of open access models 93 | 94 | ![](https://pbs.twimg.com/media/EkRvDM0WkAIZmFV?format=jpg&name=small) 95 | 96 | --- 97 | 98 | ## The PCT in 2020 99 | 100 | - Now the go-to tool for strategic cycle network planning in England and Wales, used by most local authorities with cycling plans ([source](https://npct.github.io/pct-shiny/regions_www/www/static/03d_other_reports/2019-use-of-pct-report.pdf)). 101 | 102 | .pull-left[ 103 | 104 | ## Geographic levels in the PCT 105 | 106 | - Generate and analyse route networks for transport planning with reference to: 107 | - Zones 108 | - Origin-destination (OD) data 109 | - Geographic desire lines 110 | - Route allocation using different routing services 111 | - Route network generation and analysis 112 | ] 113 | 114 | .pull-right[ 115 | 116 | ![](https://user-images.githubusercontent.com/1825120/94201205-9758c600-feb3-11ea-8383-a01966457562.png) 117 | See these levels at [www.pct.bike](https://www.pct.bike) 118 | 119 | ] 120 | 121 | --- 122 | 123 | background-image: url(https://user-images.githubusercontent.com/1825120/96583573-d3c1eb00-12d4-11eb-88b8-ca78087b63f7.png) 124 | 125 | # Live demo of the PCT for Bristol 126 | 127 | ## See https://www.pct.bike/ 128 | 129 | --- 130 | 131 | 132 | .pull-left[ 133 | 134 | # Uses of the PCT 135 | 136 | - Visioning 137 | - Planning strategic cycle networks 138 | - Identifying corridors with high latent demand 139 | 140 | Uses that were not initially planned 141 | 142 | - Pop-up cycleway planning 143 | - LTN planning? 144 | 145 | ] 146 | 147 | -- 148 | 149 | .pull-right[ 150 | 151 | ## Deploying in new contexts 152 | 153 | - Requires survey based or synthetic OD data, to be processed by software developed at Leeds `r Citep(my_bib, "lovelace_stplanr:_2018", .opts = list(cite.style = "authoryear"))` 154 | - For mor on methods, see the [transport chapter](https://geocompr.robinlovelace.net/transport.html) (available free [online](https://geocompr.robinlovelace.net/)) `r Citep(my_bib, "lovelace_geocomputation_2019", .opts = list(cite.style = "authoryear"))` 155 | - Can also be used for specific contexts (e.g. cycling to school, cycling to public transport) `r Citep(my_bib, "goodman_scenarios_2019", .opts = list(cite.style = "authoryear"))` 156 | 157 | ] 158 | 159 | -- 160 | 161 | #### For further info, see the training materials at [itsleeds.github.io](https://itsleeds.github.io/pct/articles/pct_training.html) 162 | 163 | 164 | #### Many use cases on the PCT website: [pct.bike/manual.html](https://www.pct.bike/manual.html) 165 | 166 | - Case studies of over a dozen areas, including Greater Manchester and Herefordshire in the manual 167 | 168 | --- 169 | 170 | ## New possibilities in the PCT approach 171 | 172 | See [web.tecnico.ulisboa.pt](http://web.tecnico.ulisboa.pt/~rosamfelix/gis/declives/DeclivesLisboa.html) for interactive map 173 | 174 | ![](https://user-images.githubusercontent.com/1825120/99063305-389ef700-259c-11eb-94cc-24a85c3858db.png) 175 | --- 176 | 177 | background-image: url(https://raw.githubusercontent.com/saferactive/saferactive/master/figures/la-multipliers.gif) 178 | 179 | -- 180 | 181 | ## Estimating change in exposure 182 | 183 | ### Tackling denominator neglect 184 | 185 | ![]() 186 | 187 | --- 188 | 189 | ## Estimating safety levels in KSI/bkm at high resolution 190 | 191 | ![](https://user-images.githubusercontent.com/1825120/97208200-c7490100-17b2-11eb-858d-5eb3fc713b25.png) 192 | 193 | --- 194 | 195 | ## Estimating health benefits of cycling uptake with the PCT 196 | 197 | - The PCT uses a modified version of the HEAT methodology to calculate health benefits of scenarios of change 198 | - Based on the DfT's TAG methodology 199 | - The scenarios are **what if** scenarios not forecasts 200 | - See the PCT manual for further information: [pct.bike/manual.html](https://npct.github.io/pct-shiny/regions_www/www/static/03a_manual/pct-bike-eng-user-manual-c1.pdf) 201 | - See the DfT's [AMAT tool](https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/888754/amat-user-guidance.pdf) also 202 | 203 | ![](https://user-images.githubusercontent.com/1825120/96588892-15a25f80-12dc-11eb-990b-1fc3c66e71c9.png) 204 | 205 | --- 206 | 207 | ## From evidence to network plans 208 | 209 | Plans from Leeds City Council responding to national [guidance](https://www.gov.uk/government/publications/reallocating-road-space-in-response-to-covid-19-statutory-guidance-for-local-authorities) and [funding](https://www.gov.uk/government/news/2-billion-package-to-create-new-era-for-cycling-and-walking) for 'pop-up' cycleways (image credit: [Leeds City Council](https://news.leeds.gov.uk/news/leeds-city-council-announces-emergency-walking-and-cycling-plans-in-response-to-covid-19)): 210 | 211 | 212 | ![](https://pbs.twimg.com/media/EZ_-A0dXgAAlBzt?format=png&name=900x900) 213 | 214 | --- 215 | 216 | background-image: url(https://raw.githubusercontent.com/cyipt/popupCycleways/master/figures/results-top-leeds.png) 217 | 218 | ## The Rapid tool - see [cyipt.bike/rapid](https://www.cyipt.bike/rapid/) 219 | 220 | --- 221 | 222 | # References 223 | 224 | ```{r, 'refs', results="asis", echo=FALSE} 225 | PrintBibliography(my_bib) 226 | # RefManageR::WriteBib(my_bib, "refs-geostat.bib") 227 | ``` 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | -------------------------------------------------------------------------------- /data-raw/test-pct.R: -------------------------------------------------------------------------------- 1 | # Aim: test the pct pkg can reproduce data on www.pct.bike 2 | 3 | # see https://github.com/ITSLeeds/pct/issues/65#issuecomment-674237160 4 | 5 | # Uptake formula from 6 | # https://raw.githubusercontent.com/npct/pct-shiny/a59ebd1619af4400eeb7ffb2a8ecdd8ce4c3753d/regions_www/www/static/03a_manual/pct-bike-eng-user-manual-c1.pdf 7 | # 8 | # logit (pcycle)= -4.018 + (-0.6369 * distance) + 9 | # (1.988 * distancesqrt) + (0.008775* distancesq) + 10 | # (-0.2555* gradient) + (0.02006* distance*gradient) + 11 | # (-0.1234* distancesqrt*gradient) 12 | library(pct) 13 | 14 | l_pct_2020 = get_pct_lines(region = "isle-of-wight") 15 | pcycle_pct_2019 = uptake_pct_govtarget(distance = l_pct_2020$rf_dist_km, gradient = l_pct_2020$rf_avslope_perc) 16 | summary(pcycle_pct_2019) 17 | # Min. 1st Qu. Median Mean 3rd Qu. Max. 18 | # 0.004712 0.007869 0.011871 0.014671 0.020958 0.043210 19 | govtarget_slc_2019 = round(pcycle_pct_2019 * l_pct_2020$all + l_pct_2020$bicycle) 20 | 21 | cor(l_pct_2020$govtarget_slc, govtarget_slc_2019) # 0.9953986 22 | round(1 - mean(govtarget_slc_2019) / mean(l_pct_2020$govtarget_slc), digits = 5) * 100 23 | # [1] 4.774 # values in pct package 2019 are 5% lower 24 | 25 | # previous (2019) values: 26 | alpha = -3.959 27 | d1 = -0.5963 28 | d2 = 1.866 29 | d3 = 0.008050 30 | h1 = -0.2710 31 | i1 = 0.009394 32 | i2 = -0.05135 33 | 34 | # New (2020) values: 35 | alpha = -4.018 36 | d1 = -0.6369 37 | d2 = 1.988 38 | d3 = 0.008775 39 | h1 = -0.2555 40 | i1 = 0.02006 41 | i2 = -0.1234 42 | 43 | # try to reproduce 2020 values: 44 | pcycle_pct_2020 = uptake_pct_govtarget( 45 | distance = l_pct_2020$rf_dist_km, 46 | gradient = l_pct_2020$rf_avslope_perc, 47 | alpha = alpha, 48 | d1 = d1, 49 | d2 = d2, 50 | d3 = d3, 51 | h1 = h1, 52 | i1 = i1, 53 | i2 = i2 54 | ) 55 | 56 | govtarget_slc_2020 = round(pcycle_pct_2020 * l_pct_2020$all + l_pct_2020$bicycle) 57 | cor(l_pct_2020$govtarget_slc, govtarget_slc_2020) # 0.9953986 58 | round(1 - mean(govtarget_slc_2020) / mean(l_pct_2020$govtarget_slc), digits = 5) * 100 59 | # 11.599 60 | 61 | # from 1st principles 62 | l_pct_2020 = pct::get_pct_lines(region = "isle-of-wight") 63 | l_pct_2020$rf_avslope_perc 64 | l_pct_2020$rf_dist_km 65 | uptake_pct_govtarget_2020 = function( 66 | distance, 67 | gradient, 68 | alpha = -4.018, 69 | d1 = -0.6369, 70 | d2 = 1.988, 71 | d3 = 0.008775, 72 | h1 = -0.2555, 73 | i1 = 0.02006, 74 | i2 = -0.1234 75 | ) { 76 | ned_rf_avslope_perc = gradient - 0.78 77 | distancesqrt = sqrt(distance) 78 | distancesq = distance^2 79 | logit_pcycle = alpha + (d1 * distance) + 80 | (d2 * distancesqrt) + (d3 * distancesq) + 81 | (h1 * ned_rf_avslope_perc) + (i1 * distance*ned_rf_avslope_perc) + 82 | (i2* distancesqrt*ned_rf_avslope_perc) 83 | boot::inv.logit(logit_pcycle) 84 | } 85 | 86 | 87 | pcycle_pct_2020 = uptake_pct_govtarget_2020( 88 | distance = l_pct_2020$rf_dist_km, 89 | gradient = l_pct_2020$rf_avslope_perc 90 | ) 91 | 92 | 93 | govtarget_slc_2020 = pcycle_pct_2020 * l_pct_2020$all + l_pct_2020$bicycle 94 | plot(l_pct_2020$govtarget_slc, govtarget_slc_2020) 95 | cor(l_pct_2020$govtarget_slc, govtarget_slc_2020) # 0.9928463 96 | round(1 - mean(govtarget_slc_2020) / mean(l_pct_2020$govtarget_slc), digits = 5) * 100 97 | # New estimate is less than 0.3% out 98 | 99 | # test on national data --------------------------------------------------- 100 | 101 | download.file("https://github.com/npct/pct-outputs-national/raw/master/commute/msoa/l_all.Rds", 102 | "l_all.Rds", mode = "wb") 103 | l_all = readRDS("l_all.Rds") 104 | 105 | pcycle_govtarget_uptake = pct::uptake_pct_govtarget(distance = l_all$rf_dist_km, gradient = l_all$rf_avslope_perc) 106 | uptake_pct = round(pcycle_govtarget_uptake * l_all$all + l_all$bicycle) 107 | 108 | summary(l_all$govtarget_slc) 109 | # Min. 1st Qu. Median Mean 3rd Qu. Max. 110 | # 0.000 0.050 0.200 2.579 1.420 715.130 111 | summary(uptake_pct) 112 | # Min. 1st Qu. Median Mean 3rd Qu. Max. 113 | # 0.000 0.000 0.000 2.275 1.000 700.000 114 | plot(l_all$govtarget_slc, uptake_pct) 115 | cor(l_all$govtarget_slc, uptake_pct) 116 | # 0.9976965 117 | round(1 - mean(uptake_pct) / mean(l_all$govtarget_slc), digits = 5) * 100 118 | # [1] 11.773 # values in pct package are 12% lower 119 | 120 | 121 | -------------------------------------------------------------------------------- /data-raw/test-setup2.R: -------------------------------------------------------------------------------- 1 | # Aim: test set-up for PCT training course 2 | # A version that provides more explanation and analysis 3 | 4 | # test you can install packages 5 | install.packages("remotes", quiet = TRUE) 6 | 7 | # test you have the right packages installed 8 | pkgs = c("sf", "stplanr", "pct", "tmap", "dplyr") 9 | remotes::install_cran(pkgs, quiet = TRUE) 10 | 11 | # load packages 12 | library(sf) 13 | library(tidyverse) 14 | library(pct) 15 | library(tmap) 16 | tmap_mode("view") 17 | 18 | # test you can read-in csv files: 19 | od_data = read.csv("https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/avon/od_attributes.csv") 20 | 21 | od_data$pcycle = od_data$bicycle / od_data$all 22 | # plot(od_data$rf_dist_km, od_data$pcycle, cex = od_data$all / mean(od_data$all)) 23 | ggplot(data = od_data) + 24 | geom_point(aes(x = rf_dist_km, y = pcycle, size = all), alpha = 0.1) + 25 | geom_smooth(aes(x = rf_dist_km, y = pcycle, size = all)) + 26 | ylim(c(0, 0.5)) 27 | 28 | # u1 = "https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/avon/c.geojson" 29 | # u1b = "https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/avon/z.geojson" 30 | # centroids = read_sf(u1) 31 | # districts = read_sf(u1b) 32 | centroids = get_pct_centroids(region = "avon", geography = "msoa") 33 | districts = get_pct_zones(region = "avon", geography = "msoa") 34 | plot(districts$geometry) 35 | centroids_geo = st_centroid(districts) 36 | plot(centroids$geometry, add = TRUE) 37 | plot(centroids_geo$geometry, add = TRUE, col = "red") 38 | 39 | # check interactive mapping with tmap 40 | u2 = "https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/avon/l.geojson" 41 | desire_lines = sf::read_sf(u2) 42 | desire_lines_subset = desire_lines[desire_lines$all > 100, ] 43 | tm_shape(desire_lines_subset) + 44 | tm_lines(col = "bicycle", palette = "viridis", lwd = "all", scale = 9) 45 | 46 | # check route network generation with stplanr 47 | # u3 = "https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/avon/rf.geojson" 48 | # routes = sf::read_sf(u3) 49 | routes = get_pct_routes_fast(region = "avon", geography = "msoa") 50 | routes_1 = routes %>% 51 | slice(which.max(bicycle)) 52 | tm_shape(routes_1) + 53 | tm_lines() 54 | routes_30 = routes %>% 55 | top_n(n = 30, wt = bicycle) 56 | 57 | tm_shape(routes_30) + 58 | tm_lines() 59 | 60 | rnet = overline(routes_30, "bicycle") 61 | b = c(0, 0.5, 1, 2, 3, 8) * 1e3 62 | tm_shape(rnet) + 63 | tm_lines(scale = 2, col = "bicycle", palette = "viridis", breaks = b) 64 | 65 | routes$Potential = pct::uptake_pct_godutch_2020( 66 | distance = routes$rf_dist_km, 67 | gradient = routes$rf_avslope_perc 68 | ) * 69 | routes$all + 70 | routes$bicycle 71 | 72 | rnet_potential = overline(routes, "Potential") 73 | tm_shape(rnet_potential) + 74 | tm_lines(lwd = "Potential", scale = 9, col = "Potential", palette = "viridis", breaks = b) 75 | 76 | summary(routes$rf_dist_km) 77 | 78 | ggplot(routes) + 79 | stat_ecdf(aes(rf_dist_km), geom = "step") 80 | 81 | # Uncomment this line to get the mean cycling potential of route segments in Bristol 82 | # round(mean(rnet_potential$Potential)) 83 | 84 | # generate output report 85 | # knitr::spin(hair = "code/reproducible-example.R") 86 | 87 | # # to convert OD data into desire lines with the od package you can uncomment the following lines 88 | # # system.time({ 89 | # test_desire_lines1 = stplanr::od2line(od_data, centroids) 90 | # # }) 91 | # # system.time({ 92 | # test_desire_lines2 = od::od_to_sf(x = od_data, z = centroids) 93 | # # }) 94 | # plot(test_desire_lines2) 95 | 96 | # test routing on a single line (optional - uncomment to test this) 97 | # warning you can only get a small number, e.g. 5, routes before this stops working! 98 | # library(osrm) 99 | # single_route = route(l = desire_lines[1, ], route_fun = osrm::osrmRoute, returnclass = "sf") 100 | # mapview::mapview(desire_lines[1, ]) + 101 | # mapview::mapview(single_route) 102 | # see https://cran.r-project.org/package=cyclestreets and other routing services 103 | # for other route options, e.g. https://github.com/ropensci/opentripplanner 104 | 105 | # head(od_data_pct[1:12]) 106 | # od_data_raw = get_od(region = "avon") 107 | # nrow(od_data_raw) 108 | # nrow(od_data_pct) 109 | # summary(od_data$all) 110 | # summary(od_data_pct$all) 111 | # 112 | # od_data = od_data_raw %>% 113 | # filter(all > 20) 114 | # 115 | # nrow(od_data) 116 | # head(od_data) 117 | 118 | # ggplot(routes) + 119 | # geom_point(aes(dutch_slc, Potential)) 120 | # cor(routes$dutch_slc, routes$Potential)^2 121 | -------------------------------------------------------------------------------- /data-raw/test-setup3.R: -------------------------------------------------------------------------------- 1 | # Test set-up and R packages for estimating cycling potential 2 | 3 | library(sf) 4 | library(tidyverse) 5 | library(stplanr) 6 | library(pct) 7 | library(tmap) 8 | 9 | od_data = od_data = read.csv("https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/avon/od_attributes.csv") 10 | nrow(od_data) 11 | head(od_data) 12 | od_data$pcycle = od_data$bicycle / od_data$all 13 | plot(od_data$rf_dist_km, od_data$pcycle) 14 | plot(od_data$rf_dist_km, od_data$pcycle, cex = od_data$all / 500) 15 | 16 | # ggplot2 17 | ggplot(od_data) + 18 | geom_point(aes(rf_dist_km, pcycle, size = all), alpha = 0.1) + 19 | geom_smooth(aes(rf_dist_km, pcycle)) 20 | 21 | centroids = get_pct_centroids(region = "avon", geography = "msoa") 22 | plot(centroids) 23 | unique(centroids$lad_name) 24 | centroids_bristol = centroids %>% 25 | filter(lad_name == "Bristol, City of") 26 | plot(centroids) 27 | districts = get_pct_zones("avon", geography = "msoa") 28 | districts_bristol = districts %>% 29 | filter(lad_name == "Bristol, City of") 30 | districts_geo_centroid = sf::st_centroid(districts_bristol) 31 | plot(districts_bristol$geometry) 32 | plot(districts_geo_centroid$geometry, col = "red", add = TRUE) 33 | plot(centroids_bristol$geometry, add = TRUE) 34 | 35 | desire_lines = get_pct_lines(region = "avon", geography = "msoa") 36 | desire_lines_bristol = desire_lines %>% 37 | filter(geo_code1 %in% centroids_bristol$geo_code) %>% 38 | filter(geo_code2 %in% centroids_bristol$geo_code) 39 | plot(desire_lines_bristol) 40 | nrow(desire_lines_bristol) 41 | 42 | routes = get_pct_routes_fast(region = "avon", geography = "msoa") 43 | routes_bristol = routes %>% 44 | filter(geo_code1 %in% centroids_bristol$geo_code) %>% 45 | filter(geo_code2 %in% centroids_bristol$geo_code) 46 | mapview::mapview(routes_bristol) 47 | 48 | rnet = overline(routes_bristol, "bicycle") 49 | mapview::mapview(rnet) 50 | 51 | routes$Potential = pct::uptake_pct_godutch(distance = routes$rf_dist_km, gradient = routes$rf_avslope_perc) * 52 | routes$all 53 | rnet_potential = overline(routes, "Potential") 54 | mean(rnet_potential$Potential) 55 | -------------------------------------------------------------------------------- /data-raw/test-uptake-2020.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Testing the PCT uptake formula" 3 | output: html_document 4 | --- 5 | 6 | ```{r setup, include=FALSE} 7 | knitr::opts_chunk$set(echo = TRUE) 8 | ``` 9 | 10 | The desire line with the highest potential on the Isle of Wight looks like this: 11 | 12 | 13 | ![](https://user-images.githubusercontent.com/1825120/91195228-bf0c1100-e6f0-11ea-815e-777af7bd504f.png) 14 | 15 | The data hosted on the PCT website provides the same data with more precision: 16 | 17 | ```{r} 18 | library(dplyr) 19 | l = pct::get_pct_lines("isle-of-wight") 20 | l1 = l %>% 21 | filter(bicycle == max(bicycle)) %>% 22 | select(all, bicycle, rf_dist_km, rf_avslope_perc, govtarget_slc, dutch_slc) %>% 23 | sf::st_drop_geometry() 24 | l1 25 | distance = l1$rf_dist_km 26 | ``` 27 | 28 | Based on that information we can calculate cycling uptake using the formulae from the manual as follows, first adjusting the hilliness level: 29 | 30 | ```{r} 31 | gradient = l1$rf_avslope_perc - 0.78 32 | ``` 33 | 34 | ## Government Target 35 | 36 | The formula to calculate cycling uptake under the Government Target scenario is as follows: 37 | 38 | Equation 1A: 39 | 40 | logit (pcycle) = -4.018 + (-0.6369 * distance) + (1.988 * distance sqrt ) + (0.008775 * 41 | distance sq ) + (-0.2555 * gradient) + (0.02006 * distance*gradient) + (-0.1234 * distance sqrt *gradient) 42 | pcycle 43 | = exp ([logit (pcycle)]) / (1 + (exp([logit(pcycle)]) 44 | 45 | In code this can be written as: 46 | 47 | ```{r} 48 | logit_pcycle = -4.018 + 49 | (-0.6369 * distance) + 50 | (1.988 * sqrt(distance)) + 51 | (0.008775 * distance^2) + 52 | (-0.2555 * gradient) + 53 | (0.02006 * distance*gradient) + 54 | (-0.1234 * sqrt(distance) *gradient) 55 | logit_pcycle 56 | pcycle = exp(logit_pcycle) / (1 + exp(logit_pcycle)) 57 | ``` 58 | 59 | Based on that, the scenario level of cycling would be: 60 | 61 | ```{r} 62 | pcycle 63 | pcycle * l1$all 64 | pcycle * l1$all + l1$bicycle 65 | ``` 66 | 67 | Good news, this is the same as the `govtarget_slc` value in the data served by the PCT for that desire line (33 in both cases): 68 | 69 | ```{r} 70 | l1$govtarget_slc 71 | ``` 72 | 73 | ## Go Dutch 74 | 75 | Cycling uptake under Go Dutch is calculated as follows. 76 | 77 | Equation 1B: 78 | 79 | logit(pcycle) = = -4.018 + (-0.6369 * distance) + (1.988 * distance sqrt ) + (0.008775 * 80 | distance sq ) + (-0.2555 * gradient) + (0.02006 * distance*gradient) + (-0.1234 * distance sqrt *gradient) + (2.550 * 81 | dutch) + (-0.08036 * dutch * distance) 82 | 83 | Which can be simplified as: 84 | 85 | ```{r} 86 | logit_pcycle = -4.018 + 2.55 + 87 | ((-0.6369 - 0.08036) * distance) + 88 | (1.988 * sqrt(distance)) + 89 | (0.008775 * distance^2) + 90 | (-0.2555 * gradient) + 91 | (0.02006 * distance*gradient) + 92 | (-0.1234 * sqrt(distance) *gradient) 93 | logit_pcycle 94 | pcycle = exp(logit_pcycle) / (1 + exp(logit_pcycle)) 95 | ``` 96 | 97 | Based on that, the scenario level of cycling would be: 98 | 99 | ```{r} 100 | pcycle 101 | pcycle * l1$all 102 | ``` 103 | 104 | However, the `dutch_slc` value in the data served by the PCT for that desire line is slightly higher: 105 | 106 | ```{r} 107 | l1$dutch_slc 108 | ``` 109 | 110 | 111 | 112 | 113 | ## Implementing the uptake model in a function 114 | 115 | To ease reproducibility, the uptake formula can be represented in R functions: 116 | 117 | ```{r, eval=FALSE} 118 | remotes::install_github("itsleeds/pct") 119 | ``` 120 | 121 | 122 | ```{r} 123 | library(pct) 124 | uptake_pct_govtarget 125 | uptake_pct_godutch_2020 126 | ``` 127 | 128 | We can check these reproduce the previous results as follows: 129 | 130 | ```{r} 131 | pcycle * l1$all 132 | uptake_pct_godutch_2020(distance = l1$rf_dist_km, gradient = l1$rf_avslope_perc) * 133 | l1$all 134 | uptake_pct_govtarget_2020(distance = l1$rf_dist_km, gradient = l1$rf_avslope_perc) * 135 | l1$all + l1$bicycle 136 | ``` 137 | 138 | ## Overall quality of fit 139 | 140 | At the level of all OD pairs in the small region of the `isle-of-wight`, the correspondence between the results downloaded from the PCT website and the R implementation is as follows: 141 | 142 | ```{r} 143 | pcycle_package_govtarget = uptake_pct_govtarget(l$rf_dist_km, l$rf_avslope_perc) 144 | pcycle_package_godutch = uptake_pct_godutch_2020(l$rf_dist_km, l$rf_avslope_perc) 145 | 146 | govtarget_slc_package = pcycle_package_govtarget * l$all + l$bicycle 147 | godutch_slc_package = pcycle_package_godutch * l$all 148 | 149 | plot(l$govtarget_slc, govtarget_slc_package) 150 | plot(l$dutch_slc, godutch_slc_package) 151 | 152 | cor(l$govtarget_slc, govtarget_slc_package)^2 153 | cor(l$dutch_slc, godutch_slc_package)^2 154 | 155 | cor(l$govtarget_sic, govtarget_slc_package)^2 156 | cor(l$dutch_slc, godutch_slc_package + l$bicycle)^2 157 | 158 | mean(l$govtarget_slc) 159 | mean(govtarget_slc_package) 160 | 161 | -(1 - mean(govtarget_slc_package) / 162 | mean(l$govtarget_slc)) * 100 163 | 164 | -(1 - mean(godutch_slc_package) / 165 | mean(l$dutch_slc)) * 100 166 | 167 | pcycle_gt_package = govtarget_slc_package / l$all 168 | pcycle_gt_web = l$govtarget_slc / l$all 169 | 170 | pcycle_gd_package = godutch_slc_package / l$all 171 | pcycle_gd_web = l$dutch_slc / l$all 172 | 173 | summary(100 * (pcycle_gt_package - pcycle_gt_web)) 174 | summary(100 * (pcycle_gd_package - pcycle_gd_web)) 175 | ``` 176 | 177 | 178 | -------------------------------------------------------------------------------- /data-raw/workshop-msg.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | ```{r, include=FALSE} 7 | # knitr::opts_chunk$set(echo = FALSE) 8 | # test you can install packages 9 | # install.packages("remotes", quiet = TRUE) 10 | 11 | # test you have the right packages installed 12 | pkgs = c("sf", "stplanr", "pct", "tmap", "dplyr") 13 | remotes::install_cran(pkgs, quiet = TRUE) 14 | 15 | # test you can read-in csv files: 16 | od_data = read.csv("https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/avon/od_attributes.csv") 17 | head(od_data) 18 | od_data$pcycle = od_data$bicycle / od_data$all 19 | ``` 20 | 21 | 22 | Dear Propensity to Cycle Tool Advanced Workshop Participant, 23 | 24 | I am looking forward to the event this Thursday and wanted to share with you a few links and reminders beforehand. 25 | 26 | The **first** thing to say is that this is an **Advanced Workshop** based on the **statistical programming language R** so you will be expected to run code in R/RStudio. 27 | You will be expected to 'get you hands dirty' and write some code so it's important that you have your computer set-up and ready to before the course starts. 28 | To check that R is working on your computer, please ensure that you can run the entirety of this script in RStudio: https://github.com/ITSLeeds/pct/blob/master/inst/test-setup.R 29 | 30 | If you see this plot appear after running line 14 (shown below - see this in context in the link above), congratulations on getting a basic R set-up working. 31 | 32 | ```{r, echo=TRUE} 33 | plot(od_data$rf_dist_km, od_data$pcycle, cex = od_data$all / mean(od_data$all)) 34 | ``` 35 | 36 | 37 | ```{r, include=FALSE, fig.show='hide'} 38 | # test the sf package: read-in and download zones and centroids 39 | library(sf) 40 | u1 = "https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/avon/c.geojson" 41 | u1b = "https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/avon/z.geojson" 42 | centroids = read_sf(u1) 43 | districts = read_sf(u1b) 44 | plot(districts$geometry) 45 | centroids_geo = st_centroid(districts) 46 | plot(centroids$geometry, add = TRUE) 47 | plot(centroids_geo$geometry, add = TRUE, col = "red") 48 | 49 | # check interactive mapping with tmap 50 | library(tmap) 51 | tmap_mode("view") 52 | u2 = "https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/avon/l.geojson" 53 | desire_lines = sf::read_sf(u2) 54 | desire_lines_subset = desire_lines[desire_lines$all > 100, ] 55 | tm_shape(desire_lines_subset) + 56 | tm_lines(col = "bicycle", palette = "viridis", lwd = "all", scale = 9) 57 | 58 | # check route network generation with stplanr 59 | library(stplanr) 60 | u3 = "https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/avon/rf.geojson" 61 | routes = sf::read_sf(u3) 62 | library(dplyr) 63 | routes_1 = routes %>% 64 | slice(which.max(bicycle)) 65 | tm_shape(routes_1) + 66 | tm_lines() 67 | rnet = overline(routes, "bicycle") 68 | b = c(0, 0.5, 1, 2, 3, 8) * 1e3 69 | tm_shape(rnet) + 70 | tm_lines(scale = 2, col = "bicycle", palette = "viridis", breaks = b) 71 | 72 | # check analysis with dplyr and estimation of cycling uptake with pct function 73 | library(pct) 74 | 75 | routes$Potential = pct::uptake_pct_godutch( 76 | distance = routes$rf_dist_km, 77 | gradient = routes$rf_avslope_perc 78 | ) * 79 | routes$all 80 | rnet_potential = overline(routes, "Potential") 81 | ``` 82 | 83 | After running the lines from 15 onwards you should see a number of plots and other outputs. 84 | The following line of code should, after running all the code in the link above, tell you the average number of cyclists on route network segments in Avon under the PCT's Go Dutch scenario: 85 | 86 | ```r 87 | round(mean(rnet_potential$Potential)) 88 | ``` 89 | 90 | What number did you get? We will test you during the course so make sure you try to reproduce the result. 91 | A more visual check that your computer is set-up to do transport data science with R for modelling cycling uptake is plotting the result. 92 | If you got the plot shown below after running the following [code](https://github.com/ITSLeeds/pct/blob/8fb7b876a3b372c728c8e09f1a2a2004272b37d0/inst/test-setup.R#L59-L60) (you will need to have run all previous lines), congratulations, you're ready to go! 93 | 94 | ```{r} 95 | tm_shape(rnet_potential) + 96 | tm_lines(lwd = "Potential", scale = 9, col = "Potential", palette = "viridis", breaks = b) 97 | ``` 98 | 99 | If you have any issues reproducing the code, please double check your R install and make sure you have installed the necessary. 100 | 101 | You will need to have a recent version of R and RStudio installed. 102 | Double check you have at least R 4.0.0 and RStudio 1.2 installed. 103 | If not, [**ensure that you have the latest versions installed**](https://rstudio-education.github.io/hopr/starting.html). 104 | 105 | If you have any issues, please open an issue here: https://github.com/ITSLeeds/pct/issues 106 | (As a last resort you can email at r.lovelace@leeds.ac.uk although I may not be able to respond before the event). 107 | 108 | The **second** thing to say is that having a GitHub account will help you ask questions and share reproducible examples, so please sign-up if you have not yet registered: https://github.com 109 | 110 | After you have signed-up, please check it works by saying hi on this issue (we will also use this link to answer code related questions during the course): https://github.com/ITSLeeds/pct/issues/74 111 | 112 | The **third** thing is about routing. If you want to do routing on the network, please sign-up at https://www.cyclestreets.net/api/apply/. You should save the resulting key on your system by running the following commands: 113 | 114 | ```{r, eval=FALSE} 115 | # uncomment these lines to install cyclestreets 116 | # install.packages("usethis") 117 | # install.packages("cyclestreets") 118 | usethis::edit_r_environ() 119 | ``` 120 | 121 | Then add the following line to the file that pops up: 122 | 123 | ``` 124 | CYCLESTREETS=ca43ed677e5e6fe1 125 | ``` 126 | 127 | For more info on testing if this worked, see: 128 | 129 | The **fourth and final** thing to say: please ensure that you have at least taken a read of (and ideally have reproduced some of the code examples in) these links: 130 | 131 | - The original paper on the Propensity to Cycle Tool: https://www.jtlu.org/index.php/jtlu/article/view/862 132 | - The transport chapter ([12](https://geocompr.robinlovelace.net/transport.html)) in the open source book [*Geocomputation with R*](https://geocompr.robinlovelace.net/) 133 | - The vignette that describes the pct R package, which can be found here: https://itsleeds.github.io/pct/articles/pct.html 134 | - Information on how to create reproducible examples (this will be useful when asking for help during the workshop): https://reprex.tidyverse.org/ 135 | 136 | Hope this is useful, please prioritise getting R up-and-running and let us know if you have any issues. 137 | 138 | All the best, 139 | 140 | Robin 141 | 142 | P.s. there is one more thing to say: we plan a virtual 'to the pub' session after the event from 5pm to 6pm so make sure you have beer (or whatever refreshment takes your fancy) in stock if you want to join for a post workshop social. 143 | -------------------------------------------------------------------------------- /data-raw/workshop-msg.md: -------------------------------------------------------------------------------- 1 | 2 | Dear Propensity to Cycle Tool Advanced Workshop Participant, 3 | 4 | I am looking forward to the event this Thursday and wanted to share with 5 | you a few links and reminders beforehand. 6 | 7 | The **first** thing to say is that this is an **Advanced Workshop** 8 | based on the **statistical programming language R** so you will be 9 | expected to run code in R/RStudio. You will be expected to ‘get you 10 | hands dirty’ and write some code so it’s important that you have your 11 | computer set-up and ready to before the course starts. To check that R 12 | is working on your computer, please ensure that you can run the entirety 13 | of this script in RStudio: 14 | 15 | 16 | If you see this plot appear after running line 14 (shown below - see 17 | this in context in the link above), congratulations on getting a basic R 18 | set-up working. 19 | 20 | ``` r 21 | plot(od_data$rf_dist_km, od_data$pcycle, cex = od_data$all / mean(od_data$all)) 22 | ``` 23 | 24 | ![](workshop-msg_files/figure-gfm/unnamed-chunk-2-1.png) 25 | 26 | After running the lines from 15 onwards you should see a number of plots 27 | and other outputs. The following line of code should, after running all 28 | the code in the link above, tell you the average number of cyclists on 29 | route network segments in Avon under the PCT’s Go Dutch scenario: 30 | 31 | ``` r 32 | round(mean(rnet_potential$Potential)) 33 | ``` 34 | 35 | What number did you get? We will test you during the course so make sure 36 | you try to reproduce the result. A more visual check that your computer 37 | is set-up to do transport data science with R for modelling cycling 38 | uptake is plotting the result. If you got the plot shown below after 39 | running the following 40 | [code](https://github.com/ITSLeeds/pct/blob/8fb7b876a3b372c728c8e09f1a2a2004272b37d0/inst/test-setup.R#L59-L60) 41 | (you will need to have run all previous lines), congratulations, you’re 42 | ready to go! 43 | 44 | ``` r 45 | tm_shape(rnet_potential) + 46 | tm_lines(lwd = "Potential", scale = 9, col = "Potential", palette = "viridis", breaks = b) 47 | ``` 48 | 49 | ## Legend for line widths not available in view mode. 50 | 51 | ![](workshop-msg_files/figure-gfm/unnamed-chunk-4-1.png) 52 | 53 | If you have any issues reproducing the code, please double check your R 54 | install and make sure you have installed the necessary. 55 | 56 | You will need to have a recent version of R and RStudio installed. 57 | Double check you have at least R 4.0.0 and RStudio 1.2 installed. If 58 | not, [**ensure that you have the latest versions 59 | installed**](https://rstudio-education.github.io/hopr/starting.html). 60 | 61 | If you have any issues, please open an issue here: 62 | (As a last resort you can email 63 | at although I may not be able to respond before 64 | the event). 65 | 66 | The **second** thing to say is that having a GitHub account will help 67 | you ask questions and share reproducible examples, so please sign-up if 68 | you have not yet registered: 69 | 70 | After you have signed-up, please check it works by saying hi on this 71 | issue (we will also use this link to answer code related questions 72 | during the course): 73 | 74 | The **third** thing is about routing. If you want to do routing on the 75 | network, please sign-up at . 76 | You should save the resulting key on your system by running the 77 | following commands: 78 | 79 | ``` r 80 | # uncomment these lines to install cyclestreets 81 | # install.packages("usethis") 82 | # install.packages("cyclestreets") 83 | usethis::edit_r_environ() 84 | ``` 85 | 86 | Then add the following line to the file that pops up: 87 | 88 | CYCLESTREETS=ca43ed677e5e6fe1 89 | 90 | For more info on testing if this worked, see: 91 | 92 | The **fourth and final** thing to say: please ensure that you have at 93 | least taken a read of (and ideally have reproduced some of the code 94 | examples in) these links: 95 | 96 | - The original paper on the Propensity to Cycle Tool: 97 | 98 | - The transport chapter 99 | ([12](https://geocompr.robinlovelace.net/transport.html)) in the 100 | open source book [*Geocomputation with 101 | R*](https://geocompr.robinlovelace.net/) 102 | - The vignette that describes the pct R package, which can be found 103 | here: 104 | - Information on how to create reproducible examples (this will be 105 | useful when asking for help during the workshop): 106 | 107 | 108 | Hope this is useful, please prioritise getting R up-and-running and let 109 | us know if you have any issues. 110 | 111 | All the best, 112 | 113 | Robin 114 | 115 | P.s. there is one more thing to say: we plan a virtual ‘to the pub’ 116 | session after the event from 5pm to 6pm so make sure you have beer (or 117 | whatever refreshment takes your fancy) in stock if you want to join for 118 | a post workshop social. 119 | -------------------------------------------------------------------------------- /data-raw/workshop-msg_files/figure-gfm/unnamed-chunk-2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data-raw/workshop-msg_files/figure-gfm/unnamed-chunk-2-1.png -------------------------------------------------------------------------------- /data-raw/workshop-msg_files/figure-gfm/unnamed-chunk-3-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data-raw/workshop-msg_files/figure-gfm/unnamed-chunk-3-1.png -------------------------------------------------------------------------------- /data-raw/workshop-msg_files/figure-gfm/unnamed-chunk-3-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data-raw/workshop-msg_files/figure-gfm/unnamed-chunk-3-2.png -------------------------------------------------------------------------------- /data-raw/workshop-msg_files/figure-gfm/unnamed-chunk-3-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data-raw/workshop-msg_files/figure-gfm/unnamed-chunk-3-3.png -------------------------------------------------------------------------------- /data-raw/workshop-msg_files/figure-gfm/unnamed-chunk-3-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data-raw/workshop-msg_files/figure-gfm/unnamed-chunk-3-4.png -------------------------------------------------------------------------------- /data-raw/workshop-msg_files/figure-gfm/unnamed-chunk-4-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data-raw/workshop-msg_files/figure-gfm/unnamed-chunk-4-1.png -------------------------------------------------------------------------------- /data-raw/workshop-msg_files/figure-html/unnamed-chunk-2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data-raw/workshop-msg_files/figure-html/unnamed-chunk-2-1.png -------------------------------------------------------------------------------- /data/desire_lines_leeds.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/desire_lines_leeds.rda -------------------------------------------------------------------------------- /data/leeds_uber_sample.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/leeds_uber_sample.rda -------------------------------------------------------------------------------- /data/mode_names.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/mode_names.rda -------------------------------------------------------------------------------- /data/od_leeds.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/od_leeds.rda -------------------------------------------------------------------------------- /data/pct_regions.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/pct_regions.rda -------------------------------------------------------------------------------- /data/pct_regions_lookup.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/pct_regions_lookup.rda -------------------------------------------------------------------------------- /data/rnet_leeds.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/rnet_leeds.rda -------------------------------------------------------------------------------- /data/routes_fast_leeds.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/routes_fast_leeds.rda -------------------------------------------------------------------------------- /data/santiago_lines.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/santiago_lines.rda -------------------------------------------------------------------------------- /data/santiago_od.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/santiago_od.rda -------------------------------------------------------------------------------- /data/santiago_routes_cs.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/santiago_routes_cs.rda -------------------------------------------------------------------------------- /data/santiago_zones.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/santiago_zones.rda -------------------------------------------------------------------------------- /data/wight_centroids.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/wight_centroids.rda -------------------------------------------------------------------------------- /data/wight_lines_30.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/wight_lines_30.rda -------------------------------------------------------------------------------- /data/wight_lines_pct.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/wight_lines_pct.rda -------------------------------------------------------------------------------- /data/wight_od.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/wight_od.rda -------------------------------------------------------------------------------- /data/wight_rnet.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/wight_rnet.rda -------------------------------------------------------------------------------- /data/wight_routes_30.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/wight_routes_30.rda -------------------------------------------------------------------------------- /data/wight_routes_30_cs.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/wight_routes_30_cs.rda -------------------------------------------------------------------------------- /data/wight_zones.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/wight_zones.rda -------------------------------------------------------------------------------- /data/zones_leeds.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/data/zones_leeds.rda -------------------------------------------------------------------------------- /inst/cycling-potential-to-specific-zones.R: -------------------------------------------------------------------------------- 1 | # Cycling potential to leeds valley park 2 | 3 | library(tidyverse) 4 | library(osmdata) 5 | library(stplanr) 6 | library(pct) 7 | 8 | 9 | # with osmdata 10 | lvp_osm = opq("leeds") %>% 11 | add_osm_feature("name", "") 12 | lvp_sf = osmdata_sf(lvp_osm) 13 | 14 | # with geofabric 15 | devtools::install_github("itsleeds/geofabric") 16 | library(geofabric) 17 | # View(geofabric_zones) 18 | # wy_osm = get_geofabric("west yorkshire") 19 | # wy_osm = get_geofabric("west yorkshire", layer = "multipolygons") 20 | f = geofabric::gf_filename(name = "West Yorkshire") 21 | query = "select * from multipolygons where name = 'Leeds Valley Park'" 22 | lvp = sf::read_sf(f, query = query) 23 | plot(lvp[1, ]) 24 | lvp = lvp[1, ] 25 | sf::write_sf(lvp, "lvp.geojson") 26 | piggyback::pb_upload("lvp.geojson") 27 | 28 | # get od data 29 | od = pct::get_od() 30 | od 31 | 32 | # get centroids data 33 | centroids = pct::get_pct_centroids(region = "west-yorkshire") 34 | 35 | msoa_codes_study_area = c("E02006876", "E02002423") 36 | 37 | summary(od$geo_code2 %in% msoa_codes_study_area) 38 | od_wy = od %>% 39 | filter(geo_code1 %in% centroids$geo_code) 40 | 41 | od_wy_lvp = od_wy %>% 42 | filter(geo_code2 %in% msoa_codes_study_area) 43 | 44 | class(od_wy_lvp) 45 | class(centroids) 46 | mapview::mapview(centroids) 47 | 48 | desire_lines_lvp = od2line(flow = od_wy_lvp, centroids) 49 | mapview::mapview(desire_lines_lvp) 50 | 51 | sum(desire_lines_lvp$all) 52 | mean(centroids$all) 53 | 54 | 55 | desire_lines_lvp$hilliness = 0.01 56 | desire_lines_lvp$distance = as.numeric(sf::st_length(desire_lines_lvp)) 57 | 58 | desire_lines_lvp$potential_cycling = pct::uptake_pct_godutch( 59 | distance = desire_lines_lvp$distance, 60 | gradient = desire_lines_lvp$hilliness 61 | ) 62 | 63 | plot(desire_lines_lvp$distance, desire_lines_lvp$potential_cycling) 64 | 65 | hist(desire_lines_lvp$all, breaks = 10) 66 | 67 | # get route data from pct 68 | 69 | pct_routes = get_pct_routes_fast(region = "west-yorkshire") 70 | 71 | pct_routes_lvp = pct_routes %>% 72 | filter(geo_code1 %in% msoa_codes_study_area | geo_code2 %in% msoa_codes_study_area) 73 | 74 | ids_lvp = od_id_order(x = desire_lines_lvp) 75 | desire_lines_lvp$id = ids_lvp$stplanr.key 76 | 77 | ids_routes = od_id_order(x = pct_routes_lvp[2:3]) 78 | pct_routes_lvp$id = ids_routes$stplanr.key 79 | 80 | # check matches 81 | summary(desire_lines_lvp$id %in% pct_routes_lvp$id) 82 | 83 | names(desire_lines_lvp) 84 | pct_routes_small = pct_routes_lvp %>% 85 | select(all_bidirection = all, bicycle_bidirectional = bicycle, rf_dist_km, rf_avslope_perc, id) %>% 86 | filter(id %in% desire_lines_lvp$id) 87 | names(pct_routes_small) 88 | 89 | desire_lines_non_spatial = sf::st_drop_geometry(desire_lines_lvp) %>% 90 | filter(id %in% pct_routes_small$id) 91 | routes_lvp = right_join(pct_routes_small, desire_lines_non_spatial) 92 | 93 | mapview::mapview(routes_lvp) 94 | 95 | # recalculate cycling potential based on better data 96 | summary(routes_lvp$all) 97 | summary(pct_routes$all) 98 | plot(routes_lvp$geometry, lwd = routes_lvp$bicycle) 99 | 100 | rnet_lvp = overline2(routes_lvp, attrib = "bicycle") 101 | 102 | library(tmap) 103 | tmap_mode("view") 104 | tm_shape(rnet_lvp) + 105 | tm_lines(lwd = "bicycle", scale = 9) 106 | 107 | 108 | # calculate go dutch ------------------------------------------------------ 109 | 110 | plot(routes_lvp$bicycle, routes_lvp$bicycle_bidirectional) 111 | plot(routes_lvp$distance, routes_lvp$rf_dist_km) 112 | routes_lvp$go_dutch_percent = pct::uptake_pct_godutch( 113 | distance = routes_lvp$rf_dist_km, 114 | gradient = routes_lvp$rf_avslope_perc 115 | ) 116 | 117 | plot(routes_lvp$distance, routes_lvp$go_dutch_percent) 118 | routes_lvp$go_dutch = routes_lvp$go_dutch_percent * routes_lvp$all 119 | summary(routes_lvp$go_dutch) 120 | sum(routes_lvp$go_dutch) 121 | 122 | rnet_lvp_go_dutch = overline2(routes_lvp, attrib = "go_dutch") 123 | tm_shape(rnet_lvp_go_dutch) + 124 | # tm_basemap(server = leaflet::providers$Thunderforest.OpenCycleMap) + 125 | tm_lines(lwd = "go_dutch", scale = 9) 126 | 127 | 128 | # get postcode locations -------------------------------------------------- 129 | 130 | remotes::install_github("ropensci/PostcodesioR") 131 | library(PostcodesioR) 132 | 133 | # simple reproducible example 134 | od_postcode = readRDS(url("https://github.com/ITSLeeds/pct/releases/download/0.2.5/od_postcode_min.Rds")) 135 | post_code_points = readRDS(url("https://github.com/ITSLeeds/pct/releases/download/0.2.5/post_code_points_min.Rds")) 136 | lvp_centroid = sf::read_sf("https://github.com/ITSLeeds/pct/releases/download/0.2.5/lvp.geojson") 137 | desire_lines_lvp = stplanr::od2line(flow = od_postcode, post_code_points, lvp_centroid) 138 | 139 | # on many postcodes ------------------------------------------------------- 140 | 141 | 142 | 143 | 144 | # pcodes = readxl::read_excel("Copy of LVP 2019 Travel Survey_Postcodes only.xlsx") 145 | names(pcodes)[1] = "code" 146 | stplanr::geo_code(pcodes[[1]][181]) # test - fail 147 | stplanr::geo_code("ls10 3xe") 148 | tmaptools::geocode_OSM("ls10 3xe") 149 | PostcodesioR::postcode_lookup("ls10 3xe") 150 | PostcodesioR::bulk_postcode_lookup(postcodes = list(pcodes$code[100:109])) 151 | 152 | pc_list <- list( 153 | postcodes = c("PR3 0SG", "M45 6GN", "EX165BL")) # spaces are ignored 154 | bulk_postcode_lookup(pc_list) 155 | 156 | summary(nchar(pcodes$code)) 157 | pcodes_complete = pcodes %>% 158 | filter(str_detect(code, " ")) %>% 159 | filter(nchar(code) > 6) %>% 160 | filter(nchar(code) < 9) %>% 161 | mutate(code = gsub(pattern = " ", replacement = "", x = code)) 162 | 163 | pcodes_list = list(postcodes = pcodes_complete$code[100:109]) 164 | 165 | 166 | pcodes_list = list(postcodes = pcodes$code[100:109]) 167 | bulk_postcode_lookup(postcodes = pcodes_list) # works! 168 | 169 | pcodes_list = list(postcodes = pcodes$code[1:100]) 170 | pcode_result_1_100 = bulk_postcode_lookup(postcodes = pcodes_list) # works! 171 | 172 | library(purrr) 173 | 174 | bulk_list <- lapply(pcode_result_1_100, "[[", 2) 175 | 176 | bulk_df <- 177 | map_dfr(bulk_list, 178 | `[`, 179 | c("postcode", "longitude", "latitude")) 180 | 181 | post_code_points = sf::st_as_sf(bulk_df, coords = c("longitude", "latitude"), crs = 4326) 182 | mapview::mapview(post_code_points) 183 | 184 | pcodes_list = list(postcodes = pcodes$code[101:200]) 185 | pcode_result_100 = bulk_postcode_lookup(postcodes = pcodes_list) # works! 186 | pcodes_list = list(postcodes = pcodes$code[201:300]) 187 | pcode_result_200 = bulk_postcode_lookup(postcodes = pcodes_list) # works! 188 | pcodes_list = list(postcodes = pcodes$code[301:400]) 189 | pcode_result_300 = bulk_postcode_lookup(postcodes = pcodes_list) # works! 190 | pcodes_list = list(postcodes = pcodes$code[401:500]) 191 | pcode_result_400 = bulk_postcode_lookup(postcodes = pcodes_list) # works! 192 | pcodes_list = list(postcodes = pcodes$code[501:600]) 193 | pcode_result_500 = bulk_postcode_lookup(postcodes = pcodes_list) # works! 194 | pcodes_list = list(postcodes = pcodes$code[601:700]) 195 | pcode_result_600 = bulk_postcode_lookup(postcodes = pcodes_list) # works! 196 | pcodes_list = list(postcodes = pcodes$code[701:nrow(pcodes)]) 197 | pcode_result_700 = bulk_postcode_lookup(postcodes = pcodes_list) # works! 198 | 199 | pcodes_list_all = c( 200 | pcode_result_1_100, 201 | pcode_result_100, 202 | pcode_result_200, 203 | pcode_result_300, 204 | pcode_result_400, 205 | pcode_result_500, 206 | pcode_result_600, 207 | pcode_result_700 208 | ) 209 | 210 | bulk_list <- lapply(pcodes_list_all, "[[", 2) 211 | 212 | bulk_df <- 213 | map_dfr(bulk_list, 214 | `[`, 215 | c("postcode", "longitude", "latitude")) 216 | 217 | head(bulk_df) 218 | post_code_points = sf::st_as_sf(bulk_df, coords = c("longitude", "latitude"), crs = 4326) 219 | mapview::mapview(post_code_points) 220 | saveRDS(post_code_points, "post_code_points.Rds") 221 | 222 | lvp_centroid = sf::st_centroid(lvp) 223 | od_postcode = sf::st_drop_geometry(post_code_points) 224 | od_postcode$dest = lvp$osm_id 225 | od_postcode$all = 1 226 | summary(od_postcode$postcode %in% post_code_points$postcode) 227 | summary(od_postcode$dest %in% lvp_centroid$osm_id) 228 | 229 | # this fails: issue with stplanr? 230 | desire_lines_lvp = stplanr::od2line(flow = od_postcode, post_code_points, lvp_centroid) 231 | 232 | 233 | 234 | # out-takes --------------------------------------------------------------- 235 | 236 | 237 | # pcodes = tibble::tibble(code = c("LS7 3HB", "LS2 9JT")) 238 | # pcodes_list = list(postcodes = pcodes$code) 239 | # bulk_postcode_lookup(postcodes = pcodes_list) # works! 240 | # bulk_list <- lapply(pcode_result_1_100, "[[", 2) 241 | # 242 | # bulk_df <- 243 | # map_dfr(bulk_list, 244 | # `[`, 245 | # c("postcode", "longitude", "latitude")) 246 | # 247 | # post_code_points = sf::st_as_sf(bulk_df, coords = c("longitude", "latitude"), crs = 4326) 248 | # saveRDS(post_code_points, "post_code_points_min.Rds") 249 | # lvp_centroid = sf::st_centroid(lvp) 250 | # od_postcode = sf::st_drop_geometry(post_code_points) 251 | # od_postcode$dest = lvp$osm_id 252 | # od_postcode$all = 1 253 | # summary(od_postcode$postcode %in% post_code_points$postcode) 254 | # summary(od_postcode$dest %in% lvp_centroid$osm_id) 255 | # 256 | # mapview::mapview(post_code_points) + mapview::mapview(lvp_centroid) 257 | # # this fails: issue with stplanr? 258 | # desire_lines_lvp = stplanr::od2line(flow = od_postcode, post_code_points, lvp_centroid) 259 | # 260 | # # create-reproducible example 261 | # saveRDS(od_postcode, "od_postcode_min.Rds") 262 | # saveRDS(post_code_points, "post_code_points_min.Rds") 263 | # piggyback::pb_upload("od_postcode_min.Rds") 264 | # piggyback::pb_upload("post_code_points_min.Rds") 265 | # piggyback::pb_download_url() 266 | # 267 | # lvp_centroid 268 | 269 | -------------------------------------------------------------------------------- /inst/estimate-cycling-potential-rnet-leeds-simple-example.R: -------------------------------------------------------------------------------- 1 | library(pct) 2 | library(stplanr) 3 | library(dplyr) 4 | 5 | key = Sys.getenv("CYCLESTREETS") 6 | nchar(key) 7 | desire_lines = desire_lines_leeds # pre-saved data in pct package 8 | desire_lines 9 | 10 | plot(desire_lines_leeds[c("all", "bicycle")]) 11 | 12 | routes_fast = route(l = desire_lines_leeds, route_fun = cyclestreets::journey) 13 | 14 | plot(routes_fast) # shows segment level data 15 | nrow(routes_fast) 16 | length(unique(routes_fast$name)) # many overlapping segments 17 | length(unique(routes_fast$distances)) # many overlapping segments, some duplication 18 | length(unique(routes_fast$elevations)) # many overlapping segments, some duplication 19 | 20 | routes_fast_grouped = routes_fast %>% 21 | group_by(area_of_residence, area_of_workplace) %>% 22 | summarise( 23 | distance = sum(distances), 24 | hilliness = sum(abs(diff(elevations))) / distance, 25 | all = unique(all), 26 | bicycle = unique(bicycle) 27 | ) 28 | 29 | plot(routes_fast_grouped$distance, routes_fast_grouped$hilliness) 30 | plot(routes_fast_grouped[c("all", "bicycle")]) 31 | routes_fast_grouped$dutch_pct_proportion = uptake_pct_godutch( 32 | distance = routes_fast_grouped$distance, 33 | gradient = routes_fast_grouped$hilliness 34 | ) 35 | routes_fast_grouped$dutch_pct_proportion 36 | routes_fast_grouped$bicycle_go_dutch = routes_fast_grouped$dutch_pct_proportion * 37 | routes_fast_grouped$all + routes_fast_grouped$bicycle 38 | 39 | routes_fast_grouped_linestring = sf::st_cast(routes_fast_grouped, "LINESTRING") 40 | rnet_go_dutch = overline(routes_fast_grouped_linestring, attrib = "bicycle_go_dutch") 41 | plot(rnet_go_dutch, breaks = c(0, 500, 1000, 4000)) 42 | 43 | # an alternative way to calculate the route network - not recommended: 44 | routes_fast_joined = left_join(routes_fast, sf::st_drop_geometry(routes_fast_grouped)) 45 | routes_fast_aggregated = routes_fast_joined %>% 46 | group_by(name, distances, elevations) %>% 47 | summarise(bicycle_go_dutch2 = sum(bicycle_go_dutch)) 48 | plot(routes_fast_aggregated["bicycle_go_dutch2"], breaks = c(0, 500, 1000, 4000)) 49 | -------------------------------------------------------------------------------- /inst/part-3-getting-pct-data.R: -------------------------------------------------------------------------------- 1 | 2 | # Intro script for Part 3 - Getting and visualising PCT data ----------------- 3 | 4 | # find names of pct regions 5 | View(pct_regions_lookup) 6 | pct_regions_lookup$region_name[pct_regions_lookup$lad16nm == "Leeds"] 7 | 8 | # get pct data (travel to work; LSOA level) 9 | region_name = "isle-of-wight" 10 | 11 | iow_zones = get_pct_zones(region_name) 12 | iow_centroids = get_pct_centroids(region_name) 13 | iow_lines = get_pct_lines(region_name) 14 | iow_routes_fast = get_pct_routes_fast(region_name) 15 | iow_routes_quiet = get_pct_routes_quiet(region_name) # geometry only; cycling potential not available 16 | iow_rnet = get_pct_rnet(region_name) 17 | 18 | # get pct data at MSOA level 19 | iow_zones_msoa = get_pct_zones(region_name, geography = "msoa") 20 | iow_centroids_msoa = get_pct_centroids(region_name, geography = "msoa") 21 | iow_routes_fast_msoa = get_pct_routes_fast(region_name, geography = "msoa") 22 | iow_rnet_msoa = get_pct_rnet(region_name, geography = "msoa") 23 | 24 | # get pct data for travel to schools 25 | iow_zones_school = get_pct_zones(region_name, purpose = "school", geography = "lsoa") 26 | iow_centroids_school_msoa = get_pct_centroids(region_name, purpose = "school", geography = "msoa") 27 | iow_routes_fast_school = get_pct_routes_fast(region_name, purpose = "school") 28 | iow_rnet_school = get_pct_rnet(region_name, purpose = "school") 29 | 30 | # map route network with base-r 31 | plot(iow_rnet) 32 | plot(iow_zones$geometry) 33 | plot(iow_rnet$geometry, col = "green", add = TRUE) 34 | 35 | # map route network with mapview 36 | library(mapview) 37 | 38 | mapview(iow_rnet) 39 | mapview(iow_rnet[, "bicycle"]) # with colour gradient and legend 40 | mapview(iow_centroids) + mapview(iow_rnet[, "bicycle"]) 41 | 42 | # map route network with tmap 43 | library(tmap) 44 | 45 | tm_shape(iow_zones) + 46 | tm_polygons(col = "lightblue") + 47 | tm_shape(iow_rnet) + 48 | tm_lines(col = "bicycle", lwd = 2) + 49 | tm_basemap(server = "Esri.WorldTopoMap") 50 | 51 | # compare top desire lines and routes with top route network segments 52 | top_lines = top_n(iow_lines, 20, wt = bicycle) 53 | top_routes_fast = top_n(iow_routes_fast, 20, wt = bicycle) 54 | top_rnet_segments = top_n(iow_rnet, 100, wt = bicycle) 55 | 56 | tm_shape(top_lines) + 57 | tm_lines(col = "bicycle", lwd = 2) 58 | 59 | top_routes_fast = arrange(top_routes_fast, bicycle) 60 | tm_shape(top_routes_fast) + 61 | tm_lines(col = "bicycle", lwd = 2) 62 | 63 | tm_shape(top_rnet_segments) + 64 | tm_lines(col = "bicycle", lwd = 2) 65 | 66 | # look at different PCT cycling uptake scenarios 67 | tm_shape(iow_rnet) + 68 | tm_lines(col = "dutch_slc", lwd = 2) 69 | 70 | tm_shape(iow_zones_msoa) + 71 | tm_polygons(col = "ebike_slc") 72 | 73 | tm_shape(iow_rnet_school) + 74 | tm_lines(col = "cambridge_slc", lwd = 2) 75 | 76 | # try out different colour palettes 77 | install.packages("tmaptools") # in case the package is not already installed 78 | tmaptools::palette_explorer() 79 | 80 | tm_shape(top_rnet_segments) + 81 | tm_lines(col = "bicycle", lwd = 2, palette = "viridis") 82 | 83 | 84 | # Exercises --------------------------------------------------------------- 85 | 86 | # 1) Find the name of the PCT region for an area of your choice. Download the following data for that region and map them using either mapview or tmap 87 | # - MSOA zones 88 | # - MSOA fast routes (travel to work) 89 | # - MSOA route network (travel to work) 90 | 91 | # 2) Identify the top 10 MSOA to MSOA routes in your region (according to 2011 cycling levels), and map them using tmap. What is the mean percentage increase in cycling on these routes, when comparing the Go Dutch scenario with 2011 cycling levels? 92 | 93 | # 3) According to both 2011 cycling levels and the Go Dutch scenario, which zone has the largest number of cyclists? Which has the largest proportion of cyclists? 94 | 95 | -------------------------------------------------------------------------------- /inst/pct-wy.R: -------------------------------------------------------------------------------- 1 | # Show PCT data 2 | 3 | library(pct) 4 | library(stplanr) 5 | library(tmap) 6 | library(dplyr) 7 | tmap_mode("view") 8 | 9 | centroids = get_centroids_ew() 10 | plot(centroids$geometry) 11 | 12 | wy = pct_regions[pct_regions$region_name == "west-yorkshire", ] 13 | tm_shape(wy) + tm_polygons() 14 | 15 | centroids = st_transform(centroids, st_crs(wy)) 16 | centroids_wy = centroids[wy, ] 17 | plot(centroids_wy) 18 | 19 | od = get_od() 20 | 21 | head(od$geo_code1) 22 | head(centroids$msoa11cd) 23 | 24 | od_wy = od[od$geo_code1 %in% centroids_wy$msoa11cd, ] 25 | od_wy_ew = od_wy[od_wy$geo_code2 %in% centroids$msoa11cd, ] 26 | 27 | od_wy_ew_top = od_wy_ew %>% 28 | filter(all > 50) 29 | 30 | lines_wy_top = od2line(od_wy_ew_top, centroids) 31 | plot(lines_wy_top) 32 | 33 | tm_shape(wy) + tm_polygons() + 34 | tm_shape(lines_wy_top) + tm_lines() 35 | 36 | r = line2route(lines_wy_top[8, ], plan = "quietest") 37 | tm_shape(r) + tm_lines() 38 | 39 | # assignment -------------------------------------------------------------- 40 | 41 | system.time(r = line2route(lines_wy_top[8, ], plan = "quietest")) 42 | system.time(r <- line2route(lines_wy_top[8, ], plan = "quietest")) 43 | system.time({r = line2route(lines_wy_top[8, ], plan = "quietest")}) 44 | 45 | # comparison with infrastructure data 46 | 47 | library(osmdata) 48 | leeds_ring_road = opq("leeds") %>% 49 | add_osm_feature(key = "name", value = "Leeds Inner Ring Road") %>% 50 | osmdata_sf() 51 | lrr_points = leeds_ring_road$osm_points %>% st_transform(27700) 52 | lrr_lines = leeds_ring_road$osm_lines %>% st_transform(27700) 53 | lrr_mline = leeds_ring_road$osm_multilines %>% st_transform(27700) 54 | # qtm(lrr_lines) # looks good, with some gaps 55 | # qtm(lrr_mline) 56 | lrr_buff = lrr_lines %>% 57 | st_buffer(30) %>% 58 | st_union() 59 | # qtm(lrr_buff) 60 | lrr_buff_pol = smoothr::fill_holes(lrr_buff, 1e9) 61 | lrr_concave = concaveman::concaveman(points = lrr_points, concavity = 2) 62 | lrr_concave_10 = concaveman::concaveman(points = lrr_points, concavity = 10) 63 | 64 | # qtm(lrr_concave) # works very well... 65 | lrr_30 = lrr_concave_10 %>% 66 | st_buffer(30) 67 | lrr_100 = lrr_concave_10 %>% 68 | st_buffer(100) 69 | # qtm(lrr_100) 70 | tm_shape(lrr_100) + 71 | tm_polygons(col = "blue", alpha = 0.05, border.col = "blue") + 72 | tm_shape(lrr_buff_pol) + 73 | tm_polygons(col = "red", alpha = 0.05, border.col = "red") + 74 | tm_shape(lrr_lines) + 75 | tm_lines() + 76 | tm_scale_bar() 77 | 78 | lrr = st_transform(lrr_30, st_crs(centroids_wy)) 79 | centroid_lcc = centroids_wy[lrr, ] 80 | 81 | lines_wy_top$dist_km = as.numeric(st_length(lines_wy_top) / 1000) 82 | lines_wy_top_5km = lines_wy_top %>% filter(dist_km <= 5) 83 | lines = lines_wy_top_5km[lrr, ] %>% filter(geo_code1 != geo_code2) 84 | routes = line2route(lines) 85 | plot(routes$geometry) 86 | 87 | routes = st_sf( 88 | cbind(st_drop_geometry(routes), st_drop_geometry(lines)), 89 | geometry = routes$geometry 90 | ) 91 | 92 | pcycle_dutch = uptake_pct_godutch( 93 | distance = routes$length, 94 | gradient = routes$av_incline 95 | ) 96 | 97 | plot(routes$dist_km, pcycle_dutch) 98 | 99 | routes$pcycle_godutch = pcycle_dutch 100 | routes$bicycle_dutch = pcycle_dutch * routes$all + routes$bicycle 101 | 102 | rnet = overline2(routes, "bicycle_dutch") 103 | plot(rnet) 104 | 105 | tm_shape(rnet) + tm_lines(lwd = "bicycle_dutch", scale = 9) 106 | 107 | existing = read_sf("https://github.com/ITSLeeds/pct/releases/download/0.2.4/inst.2frmd.2fexisting.geojson") 108 | 109 | tm_shape(existing) + tm_lines() 110 | 111 | # existing_buff = geo_buffer(existing, 100) 112 | existing_buff = existing %>% 113 | st_transform(27700) %>% 114 | st_buffer(100) %>% 115 | st_transform(4326) %>% 116 | st_union() 117 | 118 | rnet_infra = st_intersection(rnet, existing_buff) 119 | plot(rnet_infra$geometry) 120 | 121 | rnet_infra = st_difference(rnet, existing_buff) 122 | 123 | tm_shape(existing_buff) + tm_polygons() + 124 | tm_shape(rnet_infra) + 125 | tm_lines(lwd = "bicycle_dutch", scale = 9) 126 | -------------------------------------------------------------------------------- /inst/pct_training_solutions.Rmd: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | title: "Propensity to Cycle Tool Training course" 4 | output: rmarkdown::html_vignette 5 | vignette: > 6 | %\VignetteIndexEntry{pct_training} 7 | %\VignetteEngine{knitr::rmarkdown} 8 | %\VignetteEncoding{UTF-8} 9 | bibliography: ../vignettes/refs_training.bib 10 | --- 11 | 12 | ```{r, include = FALSE} 13 | knitr::opts_chunk$set( 14 | collapse = TRUE, 15 | comment = "#>", 16 | out.width = "50%" 17 | ) 18 | ``` 19 | 20 | ```{r, eval=FALSE, echo=FALSE} 21 | # get citations 22 | refs = RefManageR::ReadZotero(group = "418217", .params = list(collection = "JFR868KJ", limit = 100)) 23 | refs2 = RefManageR::ReadBib("vignettes/refs.bib") 24 | refs = c(refs, refs2) 25 | citr::insert_citation(bib_file = "vignettes/refs_training.bib") 26 | RefManageR::WriteBib(refs, "vignettes/refs_training.bib") 27 | citr::tidy_bib_file(rmd_file = "vignettes/pct_training.Rmd", messy_bibliography = "vignettes/refs_training.bib") 28 | ``` 29 | 30 | These solutions assume you have worked through the exercises in the [`pct_training` vignette](https://itsleeds.github.io/pct/articles/pct_training.html) and have loaded the necessary packages. 31 | 32 | ```{r, results='hide', eval=FALSE} 33 | library(pct) 34 | library(dplyr) # in the tidyverse 35 | library(tmap) # installed alongside mapvew 36 | ``` 37 | 38 | 39 | ## Getting and viewing PCT data 40 | 41 | - G1: Using the PCT's online interface, hosted at [www.pct.bike/m/?r=isle-of-wight](https://www.pct.bike/m/?r=isle-of-wight), identify the MSOA **zone** that has the highest number of people who cycle to work. 42 | 43 | Answer: E02003582 Isle of Wight 002 44 | 45 | - G2: 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): 46 | 47 | ```{r, eval=FALSE} 48 | library(pct) 49 | library(dplyr) # suggestion: use library(tidyverse) 50 | z_original = get_pct_zones("isle-of-wight") 51 | z = z_original %>% 52 | select(geo_code, geo_name, all, bicycle, car_driver) 53 | ``` 54 | 55 | ```{r, echo=TRUE, , eval=FALSE} 56 | # the solution: 57 | z_highest_cycling = z %>% 58 | top_n(n = 1, wt = bicycle) 59 | ``` 60 | 61 | Answer: E02003582 Isle of Wight 002, check by viewing the data frame or using `print()` 62 | 63 | - G3: Use the `plot()` command to visualise where on the Ilse 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 = TURE` and a `col` argument to add the layer on top of the base layer and give it a colour). 64 | The result should look something like something this: 65 | 66 | ```{r, echo=TRUE, eval=FALSE} 67 | plot(z$geometry) 68 | plot(z_highest_cycling$geometry, col = "red", add = TRUE) 69 | ``` 70 | 71 | - G4: Using the online interface, identify the top 5 MSOA to MSOA **desire lines** that have the highest number of people who cycle to work. 72 | 73 | Answer: 74 | 75 | E02003588 E02003591 654 76 | 77 | E02003588 E02003589 615 78 | 79 | E02003582 E02003588 567 80 | 81 | E02003581 E02003588 485 82 | 83 | E02003585 E02003588 406 84 | 85 | 86 | - G5: Using the function `get_pct_lines()`, 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). 87 | - Bonus: also find the 5 desire lines with the highest number of people driving to work. Plot them and find the straight line distance of these lines with the function `st_distance()`. 88 | 89 | ```{r get routes, eval=FALSE} 90 | # Aim: get top 5 cycle routes 91 | l_original_msoa = get_pct_lines("isle-of-wight") 92 | l_msoa = l_original_msoa %>% 93 | select(geo_code1, geo_code2, all, bicycle, car_driver, rf_avslope_perc, rf_dist_km) 94 | ``` 95 | 96 | ```{r, echo=TRUE, warning=FALSE, fig.show='hold', fig.cap="Top 5 MSOA to MSOA desire lines with highest number of people cycling (left) and driving (right) in the Isle of Wight.", eval = FALSE} 97 | 98 | l = l_msoa 99 | l_top_cycling = l %>% 100 | top_n(n = 5, wt = bicycle) 101 | plot(z$geometry) 102 | plot(l_top_cycling, add = TRUE, lwd = 5, col = "green") 103 | 104 | # top 5 driving routes 105 | l_top_driving = l %>% 106 | top_n(n = 5, wt = car_driver) 107 | plot(z$geometry) 108 | plot(l_top_driving, add = TRUE, lwd = 5, col = "red") 109 | 110 | ``` 111 | 112 | - G6 (Bonus): Repeat the exercise but for LSOA to LSOA desire lines (by setting the argument `geography = "lsoa"`, remember to change the names of the objects you create). The results should look something like this: 113 | 114 | ```{r, echo=TRUE, warning=FALSE, fig.show='hold', fig.cap="Top 5 LSOA-LSOA desire lines with highest number of people cycling (left) and driving (right) in the Isle of Wight.", eval=FALSE} 115 | # at the lsoa level 116 | l_original_lsoa = get_pct_lines("isle-of-wight", geography = "lsoa") 117 | l = l_original_lsoa %>% 118 | select(geo_code1, geo_code2, all, bicycle, car_driver) 119 | l_top_cycling = l %>% 120 | top_n(n = 5, wt = bicycle) 121 | plot(z$geometry) 122 | plot(l_top_cycling, add = TRUE, lwd = 5, col = "green") 123 | 124 | # top 5 driving routes 125 | l_top_driving = l %>% 126 | top_n(n = 5, wt = car_driver) 127 | plot(z$geometry) 128 | plot(l_top_driving, add = TRUE, lwd = 5, col = "red") 129 | ``` 130 | 131 | - G7: Why are the results different? What are the advantages and disadvantages of using smaller zones, as represented by the LSOA data above? 132 | 133 | Answer: LSOAs are samller than MSOAs, so provide more spatial detail. This can be useful. However MSOAs often give a better overview. For example MSOA anlaysis will highlight commuter travel to a single to a city centre. LSOA travel is often more chaotic with many origins and desinations. 134 | 135 | As LSOAs are smaller they are more susetible to bias from outlieres, conisder how many people need to change behavoir for a 1% mode shift for and LSOA and MSOA. 136 | 137 | - G8 (bonus): do the same analysis but with the top **300** routes cycled and driven. Hint: set the line width with `lwd = l_top_cycling$bicycle / mean(l_top_cycling$bicycle)` to portray the relative importance of each route. 138 | 139 | ```{r, echo=TRUE, warning=FALSE, fig.show='hold', fig.cap="Top 300 LSOA-LSOA desire lines with highest number of people cycling (left) and driving (right) in the Isle of Wight.", eval=FALSE} 140 | # at the lsoa level 141 | l_top_cycling = l %>% 142 | top_n(n = 300, wt = bicycle) 143 | plot(z$geometry) 144 | plot(l_top_cycling, add = TRUE, lwd = l_top_cycling$bicycle / mean(l_top_cycling$bicycle), col = "green") 145 | 146 | # top 5 driving routes 147 | l_top_driving = l %>% 148 | top_n(n = 300, wt = car_driver) 149 | plot(z$geometry) 150 | plot(l_top_driving, add = TRUE, lwd = l_top_driving$car_driver / mean(l_top_driving$car_driver), col = "red") 151 | ``` 152 | 153 | ## Modifying PCT data to identify routes/roads of interest 154 | 155 | - M1: Building on the example above, add a new column called `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). 156 | 157 | ```{r p2, eval=FALSE} 158 | l_msoa$pcycle = l_msoa$bicycle / l_msoa$all * 100 159 | plot(l_msoa["pcycle"], lwd = l_msoa$all / mean(l_msoa$all), breaks = c(0, 5, 10, 20, 50)) 160 | ``` 161 | 162 | - 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")`) 163 | 164 | ```{r eval=FALSE, echo=TRUE} 165 | rnet = get_pct_rnet("isle-of-wight") 166 | ``` 167 | 168 | ## Scenarios of change 169 | 170 | - S1: Generate a 'Go Dutch' scenario for the Isle of Wight using the function `uptake_pct_godutch()` (hint: the following code chunk will create a 'Government Target' scenario): 171 | 172 | ```{r, eval=FALSE} 173 | l_msoa$euclidean_distance = as.numeric(sf::st_length(l_msoa)) 174 | l_msoa$pcycle_govtarget = uptake_pct_govtarget( 175 | distance = l_msoa$rf_dist_km, 176 | gradient = l_msoa$rf_avslope_perc 177 | ) * 100 + l_msoa$pcycle 178 | ``` 179 | 180 | ```{r change, echo=TRUE, eval=FALSE} 181 | l_msoa$pcycle_dutch = uptake_pct_godutch( 182 | distance = l_msoa$rf_dist_km, 183 | gradient = l_msoa$rf_avslope_perc 184 | ) * 100 + l_msoa$pcycle 185 | ``` 186 | 187 | ```{r dutch_pcycle, echo=TRUE, warning=FALSE, fig.show='hold', fig.cap="Percent cycling currently (left) and under a 'Go Dutch' scenario (right) in the Isle of Wight.", eval=FALSE} 188 | plot(l_msoa["pcycle"], lwd = l_msoa$all / mean(l_msoa$all), breaks = c(0, 5, 10, 20, 50)) 189 | plot(l_msoa["pcycle_dutch"], lwd = l_msoa$all / mean(l_msoa$all), breaks = c(0, 5, 10, 20, 50)) 190 | ``` 191 | 192 | - S2: Think of alternative scenarios that would be useful for your work 193 | - S3 (bonus): look inside the function [`pct_uptake_godutch()`](https://github.com/ITSLeeds/pct/blob/master/R/uptake.R#L36) - how could it be modified? 194 | 195 | ## Routing 196 | 197 | - R1: Using the function `route_osrm()` find the route associated with the most cycled desire line in the Isle of Wight. 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 - **warning: the function may need to run a few times before it works**): 198 | 199 | ```{r, eval=FALSE} 200 | library(stplanr) 201 | l_top = l_msoa %>% 202 | top_n(n = 1, wt = bicycle) 203 | ``` 204 | 205 | ```{r, eval=FALSE, echo=TRUE} 206 | r_top = stplanr::route_osrm(l_top) 207 | sf::write_sf(sf::st_as_sf(r_top), "r_top.geojson") 208 | piggyback::pb_upload("r_top.geojson") 209 | piggyback::pb_download_url() 210 | ``` 211 | 212 | ```{r, echo=TRUE, eval=FALSE} 213 | r_top = sf::read_sf("https://github.com/ITSLeeds/pct/releases/download/0.0.1/r_top.geojson") 214 | tm_shape(r_top) + 215 | tm_lines(lwd = 5) 216 | ``` 217 | 218 | - R2: What are the problems associated with this route from a cycling perspective? Take a look at the help page opened by entering `?route_osrm` to identify the reason why the route is not particularly useful from a cycling perspective. 219 | 220 | - R3: Regenerate the route using the function `line2route()`. 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.** 221 | 222 | ```{r, echo=TRUE, eval=FALSE} 223 | r_cs = stplanr::line2route(l_top) 224 | leaflet() %>% 225 | addTiles() %>% 226 | addPolylines(data = r_cs) 227 | ``` 228 | 229 | - R4 (bonus): what features of a routing service would be most useful for your work and why? 230 | 231 | ## Route networks 232 | 233 | - RN1: Generate a 'route network' showing number of people walking in the top 30 routes in the Isle of Wight, allocated to the transport network (hint: use the `overline2()` function and begin the script as follows, the results should look similar to the results below): 234 | 235 | ```{r, eval=FALSE} 236 | route_data = sf::st_sf(wight_lines_30, geometry = wight_routes_30$geometry) 237 | ``` 238 | 239 | ```{r, echo=TRUE, message=FALSE, eval=FALSE} 240 | rnet_walk = overline2(x = route_data, "foot") 241 | tm_shape(rnet_walk) + 242 | tm_lines(lwd = "foot", scale = 9) 243 | ``` 244 | 245 | ```{r, echo=TRUE, eval=FALSE} 246 | # Demo PCT Analysis# 247 | # Make a commuting quiet route network for Isle of Wight 248 | # and combine it with the travle to school route network 249 | 250 | # Step 1: Load Library 251 | library(tidyverse) 252 | library(sf) 253 | library(pct) 254 | library(stplanr) 255 | 256 | # Step 2: Get Data 257 | routes_commute = get_pct_routes_quiet(region = "isle-of-wight", 258 | purpose = "commute", 259 | geography = "lsoa") 260 | 261 | lines_commute = get_pct_lines(region = "isle-of-wight", 262 | purpose = "commute", 263 | geography = "lsoa") 264 | 265 | rnet_school = get_pct_rnet(region = "isle-of-wight", 266 | purpose = "school", 267 | geography = "lsoa") 268 | 269 | # Step 3: Prepare Data 270 | lines_commute = lines_commute %>% 271 | st_drop_geometry() %>% 272 | select(id, bicycle, dutch_slc) 273 | 274 | routes_commute = routes_commute %>% 275 | select(id) 276 | 277 | # Join Cycling Levels to Routes 278 | routes_commute = left_join(routes_commute, lines_commute) 279 | plot(routes_commute["bicycle"]) 280 | 281 | # Make a commuting Rnet 282 | rnet_commute = overline2(routes_commute, 283 | attrib = c("bicycle","dutch_slc")) 284 | plot(rnet_commute["bicycle"]) 285 | 286 | # Combine commuting and travel to schools 287 | rnet_school <- rnet_school %>% 288 | select(dutch_slc) 289 | rnet_commute <- rnet_commute %>% 290 | select(dutch_slc) 291 | rnet_commute$bicycle <- NULL 292 | 293 | 294 | rnet_both = rbind(rnet_commute, rnet_school) 295 | rnet_both = overline2(rnet_both, 296 | attrib = c("dutch_slc")) 297 | mapview::mapview(rnet_both, at = c(50,100,200,500,1000)) 298 | 299 | ``` 300 | 301 | 302 | -------------------------------------------------------------------------------- /inst/rapid-geojson-download.R: -------------------------------------------------------------------------------- 1 | 2 | # Download and map data from Rapid Cycleway Prioritisation Tool ----------- 3 | 4 | # select URLS 5 | west_of_england_existing_url = "https://www.cyipt.bike/rapid/west-of-england/cycleways.geojson" 6 | west_of_england_cohesive_url = "https://www.cyipt.bike/rapid/west-of-england/cohesive_network.geojson" 7 | west_of_england_top_url = "https://www.cyipt.bike/rapid/west-of-england/top_routes.geojson" 8 | west_of_england_spare_url = "https://www.cyipt.bike/rapid/west-of-england/spare_lanes.geojson" 9 | west_of_england_wide_url = "https://www.cyipt.bike/rapid/west-of-england/wide_lanes.geojson" 10 | north_somerset_top_url = "https://www.cyipt.bike/rapid/north-somerset/top_routes.geojson" 11 | 12 | rapid_urls = c(west_of_england_existing_url, west_of_england_cohesive_url, west_of_england_top_url, west_of_england_spare_url, west_of_england_wide_url, north_somerset_top_url) 13 | 14 | # create new directory 15 | dir.create("data/rapid-data") 16 | 17 | # assign file names and paths 18 | rapid_file_names = file.path("data/rapid-data", paste(substring(dirname(rapid_urls),30), basename(rapid_urls), sep = "_")) 19 | 20 | # download files 21 | download.file(url = rapid_urls, destfile = rapid_file_names) 22 | 23 | # read one file at a time 24 | library(sf) 25 | west_of_england_cycleways = read_sf("data/rapid-data/west-of-england_cycleways.geojson") 26 | west_of_england_cohesive_network = read_sf("data/rapid-data/west-of-england_cohesive_network.geojson") 27 | 28 | # read all files in directory 29 | library(sf) 30 | library(stringr) 31 | 32 | file_path = "data/rapid-data/" 33 | files_wanted = list.files(file_path) 34 | 35 | files_wanted %>% 36 | purrr::map(function(file_name){ 37 | assign(x = gsub("-", "_", str_remove(file_name, ".geojson")), 38 | value = read_sf(paste0(file_path, file_name)), 39 | envir = .GlobalEnv) 40 | }) 41 | 42 | # set up basemaps 43 | s = c( 44 | `Grey basemap` = "CartoDB.Positron", 45 | `Coloured basemap` = "Esri.WorldTopoMap", 46 | `OSM existing cycle provision (CyclOSM)` = "https://b.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png", 47 | `PCT commuting, Government Target` = "https://npttile.vs.mythic-beasts.com/commute/v2/govtarget/{z}/{x}/{y}.png", 48 | `PCT schools, Government Target` = "https://npttile.vs.mythic-beasts.com/school/v2/govtarget/{z}/{x}/{y}.png", 49 | `Satellite image` = "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'" 50 | ) 51 | tms = c(FALSE, FALSE, FALSE, TRUE, TRUE, FALSE) 52 | # Find other basemaps at https://leaflet-extras.github.io/leaflet-providers/preview/ 53 | 54 | # create map 55 | library(tmap) 56 | tmap_mode("view") 57 | 58 | tm_shape(west_of_england_cohesive_network) + 59 | tm_lines(col = "#9077AD", lwd = 5) + 60 | tm_shape(west_of_england_spare_lanes) + 61 | tm_lines(col = "#B91F48", lwd = 3) + 62 | tm_shape(west_of_england_wide_lanes) + 63 | tm_lines(col = "#FF7F00", lwd = 3) + 64 | tm_shape(west_of_england_top_routes) + 65 | tm_lines(col = "blue", lwd = 3) + 66 | tm_shape(west_of_england_cycleways) + 67 | tm_lines(col = "darkgreen", lwd = 1.3) + 68 | tm_basemap(server = s, tms = tms) 69 | 70 | -------------------------------------------------------------------------------- /inst/test-setup.R: -------------------------------------------------------------------------------- 1 | # Aim: test set-up for PCT training course 2 | 3 | # test you can install packages 4 | install.packages("remotes", quiet = TRUE) 5 | 6 | # test you have the right packages installed 7 | pkgs = c("sf", "stplanr", "pct", "tmap", "dplyr") 8 | remotes::install_cran(pkgs, quiet = TRUE) 9 | 10 | # test you can read-in csv files: 11 | od_data = read.csv("https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/avon/od_attributes.csv") 12 | head(od_data) 13 | od_data$pcycle = od_data$bicycle / od_data$all 14 | plot(od_data$rf_dist_km, od_data$pcycle, cex = od_data$all / mean(od_data$all)) 15 | 16 | # test the sf package: read-in and download zones and centroids 17 | library(sf) 18 | u1 = "https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/avon/c.geojson" 19 | u1b = "https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/avon/z.geojson" 20 | centroids = read_sf(u1) 21 | districts = read_sf(u1b) 22 | plot(districts$geometry) 23 | centroids_geo = st_centroid(districts) 24 | plot(centroids$geometry, add = TRUE) 25 | plot(centroids_geo$geometry, add = TRUE, col = "red") 26 | 27 | # check interactive mapping with tmap 28 | library(tmap) 29 | tmap_mode("view") 30 | u2 = "https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/avon/l.geojson" 31 | desire_lines = sf::read_sf(u2) 32 | desire_lines_subset = desire_lines[desire_lines$all > 100, ] 33 | tm_shape(desire_lines_subset) + 34 | tm_lines(col = "bicycle", palette = "viridis", lwd = "all", scale = 9) 35 | 36 | # check route network generation with stplanr 37 | library(stplanr) 38 | u3 = "https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/avon/rf.geojson" 39 | routes = sf::read_sf(u3) 40 | library(dplyr) 41 | routes_1 = routes %>% 42 | slice(which.max(bicycle)) 43 | tm_shape(routes_1) + 44 | tm_lines() 45 | rnet = overline(routes, "bicycle") 46 | b = c(0, 0.5, 1, 2, 3, 8) * 1e3 47 | tm_shape(rnet) + 48 | tm_lines(scale = 2, col = "bicycle", palette = "viridis", breaks = b) 49 | 50 | # check analysis with dplyr and estimation of cycling uptake with pct function 51 | library(pct) 52 | 53 | routes$Potential = pct::uptake_pct_godutch( 54 | distance = routes$rf_dist_km, 55 | gradient = routes$rf_avslope_perc 56 | ) * 57 | routes$all 58 | rnet_potential = overline(routes, "Potential") 59 | tm_shape(rnet_potential) + 60 | tm_lines(lwd = "Potential", scale = 9, col = "Potential", palette = "viridis", breaks = b) 61 | 62 | # Uncomment this line to get the mean cycling potential of route segments in Bristol 63 | # round(mean(rnet_potential$Potential)) 64 | 65 | # generate output report 66 | # knitr::spin(hair = "code/reproducible-example.R") 67 | 68 | # # to convert OD data into desire lines with the od package you can uncomment the following lines 69 | # # system.time({ 70 | # test_desire_lines1 = stplanr::od2line(od_data, centroids) 71 | # # }) 72 | # # system.time({ 73 | # test_desire_lines2 = od::od_to_sf(x = od_data, z = centroids) 74 | # # }) 75 | # plot(test_desire_lines2) 76 | 77 | # test routing on a single line (optional - uncomment to test this) 78 | # warning you can only get a small number, e.g. 5, routes before this stops working! 79 | # library(osrm) 80 | # single_route = route(l = desire_lines[1, ], route_fun = osrm::osrmRoute, returnclass = "sf") 81 | # mapview::mapview(desire_lines[1, ]) + 82 | # mapview::mapview(single_route) 83 | # see https://cran.r-project.org/package=cyclestreets and other routing services 84 | # for other route options, e.g. https://github.com/ropensci/opentripplanner 85 | -------------------------------------------------------------------------------- /inst/uptake-model.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | library(pct) 4 | library(tidyverse) 5 | 6 | l = get_pct_lines("south-yorkshire") 7 | l$pcycle = l$bicycle / l$all 8 | plot(l$rf_dist_km, l$pcycle) 9 | ggplot(l) + geom_point(aes(rf_dist_km, pcycle, alpha = all)) 10 | l$distance_groups = cut(l$rf_dist_km, breaks = seq(from = 0, to = 20, by = 1)) 11 | head(l$distance_groups) 12 | l_grouped = l %>% 13 | sf::st_drop_geometry() %>% 14 | group_by(dist_band = as.character(distance_groups)) %>% 15 | summarise( 16 | distance = mean(rf_dist_km), 17 | all = sum(all), bicycle = sum(bicycle), 18 | hilliness = mean(l$rf_avslope_perc) 19 | ) 20 | 21 | ggplot(l_grouped) + 22 | geom_point(aes(distance, all)) + 23 | geom_point(aes(distance, bicycle)) 24 | 25 | l_grouped = l %>% 26 | sf::st_drop_geometry() %>% 27 | group_by(dist_band = as.character(distance_groups)) %>% 28 | summarise( 29 | distance = mean(rf_dist_km), 30 | pcycle = sum(bicycle)/ sum(all), 31 | hilliness = mean(l$rf_avslope_perc) 32 | ) 33 | 34 | ggplot(l_grouped) + 35 | geom_point(aes(distance, pcycle)) 36 | 37 | l_grouped$baseline = pct::uptake_pct_govtarget( 38 | distance = l_grouped$distance, 39 | gradient = rep(0, nrow(l_grouped)) 40 | ) / 2 41 | 42 | ggplot(l_grouped) + 43 | geom_point(aes(distance, baseline)) + 44 | geom_point(aes(distance, pcycle)) + 45 | ylim(c(0, 0.10)) 46 | 47 | 48 | l_grouped$logit_pcycle = boot::logit(l_grouped$pcycle) 49 | ggplot(l_grouped) + 50 | geom_point(aes(distance, logit_pcycle)) 51 | 52 | m = lm(logit_pcycle ~ distance, hilliness, data = l_grouped) 53 | ggplot(l_grouped) + 54 | geom_point(aes(distance, pcycle)) + 55 | geom_point(aes(distance, boot::inv.logit(m$fitted.values)), col = "grey") 56 | 57 | 58 | # cid data ---------------------------------------------------------------- 59 | 60 | library(CycleInfraLnd) 61 | points = get_cid_points(type = "traffic_calming") 62 | 63 | -------------------------------------------------------------------------------- /man/desire_lines_leeds.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{desire_lines_leeds} 5 | \alias{desire_lines_leeds} 6 | \title{Cycle route desire lines for Leeds} 7 | \description{ 8 | Cycle route desire lines for Leeds 9 | } 10 | \examples{ 11 | # see data-raw folder for generation code 12 | desire_lines_leeds 13 | } 14 | \keyword{datasets} 15 | -------------------------------------------------------------------------------- /man/figures/README-centroids-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/man/figures/README-centroids-1.png -------------------------------------------------------------------------------- /man/figures/README-decay-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/man/figures/README-decay-1.png -------------------------------------------------------------------------------- /man/figures/README-decayhills-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/man/figures/README-decayhills-1.png -------------------------------------------------------------------------------- /man/figures/README-desire-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/man/figures/README-desire-1.png -------------------------------------------------------------------------------- /man/figures/README-get_pct_lines-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/man/figures/README-get_pct_lines-1.png -------------------------------------------------------------------------------- /man/figures/README-rnetgove-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/man/figures/README-rnetgove-1.png -------------------------------------------------------------------------------- /man/figures/README-routes_fast-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/man/figures/README-routes_fast-1.png -------------------------------------------------------------------------------- /man/figures/README-routes_vital-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/man/figures/README-routes_vital-1.png -------------------------------------------------------------------------------- /man/get_centroids_ew.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/msoa_centroids.R 3 | \name{get_centroids_ew} 4 | \alias{get_centroids_ew} 5 | \title{Download MSOA centroids for England and Wales} 6 | \usage{ 7 | get_centroids_ew() 8 | } 9 | \description{ 10 | Downloads and processes data on where people live in England and Wales. 11 | See \href{https://geoportal.statistics.gov.uk/datasets/b0a6d8a3dc5d4718b3fd62c548d60f81_0}{geoportal.statistics.gov.uk}. 12 | } 13 | -------------------------------------------------------------------------------- /man/get_desire_lines.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/desire_lines.R 3 | \name{get_desire_lines} 4 | \alias{get_desire_lines} 5 | \title{Desire lines} 6 | \usage{ 7 | get_desire_lines(region = NULL, n = NULL, omit_intrazonal = FALSE) 8 | } 9 | \arguments{ 10 | \item{region}{The PCT region or local authority to download data from (e.g. \code{west-yorkshire} or \code{Leeds}). 11 | See \code{View(pct_regions_lookup)} for a full list of possible region names.} 12 | 13 | \item{n}{top n number of destinations with most trips in the 2011 census 14 | within the \code{region}.} 15 | 16 | \item{omit_intrazonal}{should intrazonal OD pairs be omited from result? 17 | \code{FALSE} by default.} 18 | } 19 | \description{ 20 | This function generates "desire lines" from census 2011 data. 21 | By default gets all desire lines from census in region, but 22 | can get the top \code{n}. 23 | } 24 | \examples{ 25 | \donttest{ 26 | if(curl::has_internet()) { 27 | desire_lines = get_desire_lines("wight") 28 | plot(desire_lines) 29 | intra_zonal = desire_lines$geo_code1 == desire_lines$geo_code2 30 | plot(desire_lines[intra_zonal, ]) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /man/get_od.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/desire_lines.R 3 | \name{get_od} 4 | \alias{get_od} 5 | \title{Get origin destination data from the 2011 Census} 6 | \usage{ 7 | get_od( 8 | region = NULL, 9 | n = NULL, 10 | type = "within", 11 | omit_intrazonal = FALSE, 12 | base_url = paste0("https://s3-eu-west-1.amazonaws.com/", 13 | "statistics.digitalresources.jisc.ac.uk", "/dkan/files/FLOW/"), 14 | filename = "wu03ew_v2", 15 | u = NULL 16 | ) 17 | } 18 | \arguments{ 19 | \item{region}{The PCT region or local authority to download data from (e.g. \code{west-yorkshire} or \code{Leeds}). 20 | See \code{View(pct_regions_lookup)} for a full list of possible region names.} 21 | 22 | \item{n}{top n number of destinations with most trips in the 2011 census 23 | within the \code{region}.} 24 | 25 | \item{type}{the type of subsetting: one of \code{from}, \code{to} or \code{within}, specifying how 26 | the od dataset should be subset in relation to the \code{region}.} 27 | 28 | \item{omit_intrazonal}{should intrazonal OD pairs be omited from result? 29 | \code{FALSE} by default.} 30 | 31 | \item{base_url}{the base url where the OD dataset is stored} 32 | 33 | \item{filename}{the name of the file to download, if not the default MSOA level 34 | data.} 35 | 36 | \item{u}{full url of file to download} 37 | } 38 | \description{ 39 | This function downloads a .csv file representing movement 40 | between MSOA zones in England and Wales. 41 | By default it returns national data, but 42 | \code{region} can be set to subset the output to a specific 43 | local authority or region. 44 | } 45 | \details{ 46 | OD datasets available include wu03uk_v3 47 | and others listed on the Wicid website. 48 | } 49 | \examples{ 50 | \donttest{ 51 | get_od("wight", n = 3) 52 | get_od() 53 | get_od(filename = "wu03uk_v3") 54 | u = "https://www.nomisweb.co.uk/output/census/2011/wf02ew_oa.zip" 55 | # get_od(u = u) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /man/get_pct.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/get_pct.R 3 | \name{get_pct} 4 | \alias{get_pct} 5 | \title{Generic function to get regional data from the PCT} 6 | \usage{ 7 | get_pct( 8 | base_url = "https://github.com/npct/pct-outputs-regional-notR/raw/master", 9 | purpose = "commute", 10 | geography = "lsoa", 11 | region = NULL, 12 | layer = NULL, 13 | extension = ".geojson", 14 | national = FALSE 15 | ) 16 | } 17 | \arguments{ 18 | \item{base_url}{Where the data is stored.} 19 | 20 | \item{purpose}{Trip purpose (typically \code{school} or \code{commute})} 21 | 22 | \item{geography}{Geographic resolution of outputs, \code{msoa} or \code{lsoa} (the default)} 23 | 24 | \item{region}{The PCT region or local authority to download data from (e.g. \code{west-yorkshire} or \code{Leeds}). 25 | See \code{View(pct_regions_lookup)} for a full list of possible region names.} 26 | 27 | \item{layer}{The PCT layer of interest, \code{z}, \code{c}, \code{l}, \code{rf}, \code{rq} or \code{rnet} 28 | for zones, centroids, desire lines, routes (fast or quiet) and route networks, respectively} 29 | 30 | \item{extension}{The type of file to download (only \code{.geojson} supported at present)} 31 | 32 | \item{national}{Download nationwide data? \code{FALSE} by default} 33 | } 34 | \description{ 35 | This function gets data generated for the Propensity to Cycle Tool 36 | project and returns objects in the modern \code{sf} class. 37 | } 38 | \examples{ 39 | \dontrun{ 40 | rf = get_pct(region = "isle-of-wight", layer = "rf") 41 | names(rf)[1:20] 42 | vars_to_plot = 10:13 43 | plot(rf[vars_to_plot]) 44 | z = get_pct(region = "isle-of-wight", layer = "z") 45 | rf = get_pct(region = "west-yorkshire", layer = "rf") 46 | z_all = get_pct(layer = "z", national = TRUE) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /man/get_pct_centroids.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/get_pct.R 3 | \name{get_pct_centroids} 4 | \alias{get_pct_centroids} 5 | \title{Get centroid results from the PCT} 6 | \usage{ 7 | get_pct_centroids( 8 | region = NULL, 9 | purpose = "commute", 10 | geography = "lsoa", 11 | extension = ".geojson" 12 | ) 13 | } 14 | \arguments{ 15 | \item{region}{The PCT region or local authority to download data from (e.g. \code{west-yorkshire} or \code{Leeds}). 16 | See \code{View(pct_regions_lookup)} for a full list of possible region names.} 17 | 18 | \item{purpose}{Trip purpose (typically \code{school} or \code{commute})} 19 | 20 | \item{geography}{Geographic resolution of outputs, \code{msoa} or \code{lsoa} (the default)} 21 | 22 | \item{extension}{The type of file to download (only \code{.geojson} supported at present)} 23 | } 24 | \description{ 25 | Wrapper around \verb{[get_pct()]} that gets centroid data from the PCT. 26 | } 27 | \examples{ 28 | \dontrun{ 29 | # don't test to reduce build times 30 | c = get_pct_centroids("isle-of-wight") 31 | plot(c) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /man/get_pct_lines.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/get_pct.R 3 | \name{get_pct_lines} 4 | \alias{get_pct_lines} 5 | \title{Get desire lines results from the PCT} 6 | \usage{ 7 | get_pct_lines( 8 | region = NULL, 9 | purpose = "commute", 10 | geography = "lsoa", 11 | extension = ".geojson" 12 | ) 13 | } 14 | \arguments{ 15 | \item{region}{The PCT region or local authority to download data from (e.g. \code{west-yorkshire} or \code{Leeds}). 16 | See \code{View(pct_regions_lookup)} for a full list of possible region names.} 17 | 18 | \item{purpose}{Trip purpose (typically \code{school} or \code{commute})} 19 | 20 | \item{geography}{Geographic resolution of outputs, \code{msoa} or \code{lsoa} (the default)} 21 | 22 | \item{extension}{The type of file to download (only \code{.geojson} supported at present)} 23 | } 24 | \description{ 25 | Wrapper around \verb{[get_pct()]} that gets l (lines) data from the PCT. 26 | } 27 | \examples{ 28 | \dontrun{ 29 | # don't test to reduce build times 30 | l = get_pct_lines("isle-of-wight") 31 | plot(l) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /man/get_pct_rnet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/get_pct.R 3 | \name{get_pct_rnet} 4 | \alias{get_pct_rnet} 5 | \title{Get route network results from the PCT} 6 | \usage{ 7 | get_pct_rnet( 8 | region = NULL, 9 | purpose = "commute", 10 | geography = "lsoa", 11 | extension = ".geojson" 12 | ) 13 | } 14 | \arguments{ 15 | \item{region}{The PCT region or local authority to download data from (e.g. \code{west-yorkshire} or \code{Leeds}). 16 | See \code{View(pct_regions_lookup)} for a full list of possible region names.} 17 | 18 | \item{purpose}{Trip purpose (typically \code{school} or \code{commute})} 19 | 20 | \item{geography}{Geographic resolution of outputs, \code{msoa} or \code{lsoa} (the default)} 21 | 22 | \item{extension}{The type of file to download (only \code{.geojson} supported at present)} 23 | } 24 | \description{ 25 | Wrapper around \verb{[get_pct()]} that gets route road network data from the PCT. 26 | } 27 | \examples{ 28 | \dontrun{ 29 | # don't test to reduce build times 30 | rnet = get_pct_rnet("isle-of-wight") 31 | plot(rnet) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /man/get_pct_routes_fast.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/get_pct.R 3 | \name{get_pct_routes_fast} 4 | \alias{get_pct_routes_fast} 5 | \title{Get fast road network results from the PCT} 6 | \usage{ 7 | get_pct_routes_fast( 8 | region = NULL, 9 | purpose = "commute", 10 | geography = "lsoa", 11 | extension = ".geojson" 12 | ) 13 | } 14 | \arguments{ 15 | \item{region}{The PCT region or local authority to download data from (e.g. \code{west-yorkshire} or \code{Leeds}). 16 | See \code{View(pct_regions_lookup)} for a full list of possible region names.} 17 | 18 | \item{purpose}{Trip purpose (typically \code{school} or \code{commute})} 19 | 20 | \item{geography}{Geographic resolution of outputs, \code{msoa} or \code{lsoa} (the default)} 21 | 22 | \item{extension}{The type of file to download (only \code{.geojson} supported at present)} 23 | } 24 | \description{ 25 | Wrapper around \verb{[get_pct()]} that gets rf data from the PCT. 26 | } 27 | \examples{ 28 | \dontrun{ 29 | # don't test to reduce build times 30 | rf = get_pct_routes_fast("isle-of-wight") 31 | plot(rf) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /man/get_pct_routes_quiet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/get_pct.R 3 | \name{get_pct_routes_quiet} 4 | \alias{get_pct_routes_quiet} 5 | \title{Get quiet road network results from the PCT} 6 | \usage{ 7 | get_pct_routes_quiet( 8 | region = NULL, 9 | purpose = "commute", 10 | geography = "lsoa", 11 | extension = ".geojson" 12 | ) 13 | } 14 | \arguments{ 15 | \item{region}{The PCT region or local authority to download data from (e.g. \code{west-yorkshire} or \code{Leeds}). 16 | See \code{View(pct_regions_lookup)} for a full list of possible region names.} 17 | 18 | \item{purpose}{Trip purpose (typically \code{school} or \code{commute})} 19 | 20 | \item{geography}{Geographic resolution of outputs, \code{msoa} or \code{lsoa} (the default)} 21 | 22 | \item{extension}{The type of file to download (only \code{.geojson} supported at present)} 23 | } 24 | \description{ 25 | Wrapper around \verb{[get_pct()]} that gets rq data from the PCT. 26 | } 27 | \examples{ 28 | \dontrun{ 29 | # don't test to reduce build times 30 | rq = get_pct_routes_quiet("isle-of-wight") 31 | plot(rq) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /man/get_pct_zones.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/get_pct.R 3 | \name{get_pct_zones} 4 | \alias{get_pct_zones} 5 | \title{Get zone results from the PCT} 6 | \usage{ 7 | get_pct_zones( 8 | region = NULL, 9 | purpose = "commute", 10 | geography = "lsoa", 11 | extension = ".geojson" 12 | ) 13 | } 14 | \arguments{ 15 | \item{region}{The PCT region or local authority to download data from (e.g. \code{west-yorkshire} or \code{Leeds}). 16 | See \code{View(pct_regions_lookup)} for a full list of possible region names.} 17 | 18 | \item{purpose}{Trip purpose (typically \code{school} or \code{commute})} 19 | 20 | \item{geography}{Geographic resolution of outputs, \code{msoa} or \code{lsoa} (the default)} 21 | 22 | \item{extension}{The type of file to download (only \code{.geojson} supported at present)} 23 | } 24 | \description{ 25 | Wrapper around \verb{[get_pct()]} that gets zone data from the PCT. 26 | } 27 | \examples{ 28 | \dontrun{ 29 | # don't test to reduce build times 30 | z = get_pct_zones("isle-of-wight") 31 | plot(z) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /man/leeds_uber_sample.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{leeds_uber_sample} 5 | \alias{leeds_uber_sample} 6 | \title{Top 15 min mean journy times within Leeds from Uber} 7 | \description{ 8 | Data downloaded 4th March 2019. 9 | According to Uber, the dataset is from: 10 | 1/1/2018 - 1/31/2018 (Every day, Daily Average) 11 | } 12 | \examples{ 13 | # see data-raw folder for generation code 14 | leeds_uber_sample 15 | } 16 | \keyword{datasets} 17 | -------------------------------------------------------------------------------- /man/mode_names.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{mode_names} 5 | \alias{mode_names} 6 | \title{Mode names in the Census} 7 | \description{ 8 | And conversion into R-friendly versions 9 | } 10 | \examples{ 11 | mode_names 12 | } 13 | \keyword{datasets} 14 | -------------------------------------------------------------------------------- /man/model_pcycle_pct_2020.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/model.R 3 | \name{model_pcycle_pct_2020} 4 | \alias{model_pcycle_pct_2020} 5 | \title{Model cycling levels as a function of explanatory variables} 6 | \usage{ 7 | model_pcycle_pct_2020(pcycle, distance, gradient, weights) 8 | } 9 | \arguments{ 10 | \item{pcycle}{The proportion of trips by bike, e.g. 0.1, meaning 10\%} 11 | 12 | \item{distance}{Vector distance numeric values of routes in km 13 | (switches to km if more than 100).} 14 | 15 | \item{gradient}{Vector gradient numeric values of routes.} 16 | 17 | \item{weights}{The weights used in the model, typically the total number of people per OD pair} 18 | } 19 | \description{ 20 | Model cycling levels as a function of explanatory variables 21 | } 22 | \examples{ 23 | # l = get_pct_lines(region = "isle-of-wight") 24 | # l = get_pct_lines(region = "cambridgeshire") 25 | l = wight_lines_pct 26 | pcycle = l$bicycle / l$all 27 | pcycle_dutch = l$dutch_slc / l$all 28 | m1 = model_pcycle_pct_2020( 29 | pcycle, 30 | distance = l$rf_dist_km, 31 | gradient = l$rf_avslope_perc - 0.78, 32 | weights = l$all 33 | ) 34 | m2 = model_pcycle_pct_2020( 35 | pcycle_dutch, distance = l$rf_dist_km, 36 | gradient = l$rf_avslope_perc - 0.78, 37 | weights = l$all 38 | ) 39 | m3 = model_pcycle_pct_2020( 40 | pcycle_dutch, distance = l$rf_dist_km, 41 | gradient = l$rf_avslope_perc - 0.78, 42 | weights = rep(1, nrow(l)) 43 | ) 44 | m1 45 | plot(l$rf_dist_km, pcycle, cex = l$all / 100, ylim = c(0, 0.5)) 46 | points(l$rf_dist_km, m1$fitted.values, col = "red") 47 | points(l$rf_dist_km, m2$fitted.values, col = "blue") 48 | points(l$rf_dist_km, pcycle_dutch, col = "green") 49 | cor(l$dutch_slc, m2$fitted.values * l$all)^2 # 95\% captured 50 | # identical means: 51 | mean(l$dutch_slc) 52 | mean(m2$fitted.values * l$all) 53 | pct_coefficients_2020 = c( 54 | alpha = -4.018 + 2.550, 55 | d1 = -0.6369 -0.08036, 56 | d2 = 1.988, 57 | d3 = 0.008775, 58 | h1 = -0.2555, 59 | i1 = 0.02006, 60 | i2 = -0.1234 61 | ) 62 | pct_coefficients_2020 63 | m2$coef 64 | plot(pct_coefficients_2020, m2$coeff) 65 | cor(pct_coefficients_2020, m2$coeff)^2 66 | cor(pct_coefficients_2020, m3$coeff)^2 # explains 95\%+ variability in params 67 | } 68 | -------------------------------------------------------------------------------- /man/od_leeds.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{od_leeds} 5 | \alias{od_leeds} 6 | \title{Example OD data for Leeds} 7 | \description{ 8 | \code{od_leeds} contains the 100 most travelled work desire lines in Leeds, 9 | according to the 2011 Census. 10 | } 11 | \examples{ 12 | # see data-raw folder for generation code 13 | od_leeds 14 | } 15 | \keyword{datasets} 16 | -------------------------------------------------------------------------------- /man/pct_regions.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{pct_regions} 5 | \alias{pct_regions} 6 | \title{PCT regions from www.pct.bike} 7 | \description{ 8 | See data-raw folder for generation code 9 | } 10 | \examples{ 11 | pct_regions 12 | } 13 | \keyword{datasets} 14 | -------------------------------------------------------------------------------- /man/pct_regions_lookup.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{pct_regions_lookup} 5 | \alias{pct_regions_lookup} 6 | \title{Lookup table matching PCT regions to local authorities} 7 | \description{ 8 | For matching pct_regions object with local authority names in England and Wales. 9 | } 10 | \examples{ 11 | 12 | names(pct_regions_lookup) 13 | head(pct_regions_lookup) 14 | } 15 | \keyword{datasets} 16 | -------------------------------------------------------------------------------- /man/rnet_leeds.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{rnet_leeds} 5 | \alias{rnet_leeds} 6 | \title{Route network for Leeds} 7 | \description{ 8 | Route network for Leeds 9 | } 10 | \examples{ 11 | # see data-raw folder for generation code 12 | rnet_leeds 13 | } 14 | \keyword{datasets} 15 | -------------------------------------------------------------------------------- /man/routes_fast_leeds.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{routes_fast_leeds} 5 | \alias{routes_fast_leeds} 6 | \title{Fastest cycle routes for the desire_lines_leeds} 7 | \description{ 8 | Fastest cycle routes for the desire_lines_leeds 9 | } 10 | \examples{ 11 | # see data-raw folder for generation code 12 | routes_fast_leeds 13 | } 14 | \keyword{datasets} 15 | -------------------------------------------------------------------------------- /man/santiago_lines.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{santiago_lines} 5 | \alias{santiago_lines} 6 | \title{Desire lines in central Santiago} 7 | \description{ 8 | See https://github.com/pedalea/pctSantiago folder for generation code 9 | } 10 | \examples{ 11 | # u = "https://github.com/pedalea/pctSantiago/releases/download/0.0.1/od_agg_zone_sub.Rds" 12 | # download.file(u, destfile = "od_agg_zone_sub.Rds") 13 | # desire_lines = readRDS("od_agg_zone_sub.Rds") 14 | santiago_zones 15 | } 16 | \keyword{datasets} 17 | -------------------------------------------------------------------------------- /man/santiago_od.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{santiago_od} 5 | \alias{santiago_od} 6 | \title{OD data in central Santiago} 7 | \description{ 8 | See https://github.com/pedalea/pctSantiago folder for generation code 9 | } 10 | \examples{ 11 | # u = "https://github.com/pedalea/pctSantiago/releases/download/0.0.1/santiago_od.Rds" 12 | # download.file(u, destfile = "santiago_od.Rds", mode = "wb") 13 | # santiago_od = readRDS("santiago_od.Rds") 14 | santiago_od 15 | } 16 | \keyword{datasets} 17 | -------------------------------------------------------------------------------- /man/santiago_routes_cs.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{santiago_routes_cs} 5 | \alias{santiago_routes_cs} 6 | \title{200 cycle routes in central Santiago, Chile} 7 | \description{ 8 | This data was obtained using code shown in the 9 | International application of the PCT methods 10 | \href{https://itsleeds.github.io/pct/articles/pct-international.html}{vignette}. 11 | } 12 | \examples{ 13 | library(sf) 14 | names(santiago_routes_cs) 15 | head(santiago_routes_cs) 16 | plot(santiago_routes_cs) 17 | } 18 | \keyword{datasets} 19 | -------------------------------------------------------------------------------- /man/santiago_zones.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{santiago_zones} 5 | \alias{santiago_zones} 6 | \title{Zones in central Santiago} 7 | \description{ 8 | See https://github.com/pedalea/pctSantiago folder for generation code 9 | } 10 | \examples{ 11 | # u = "https://github.com/pedalea/pctSantiago/releases/download/0.0.1/z_centre.Rds" 12 | # download.file(u, destfile = "z_centre.Rds", mode = "wb") 13 | # santiago_zones = readRDS("z_centre.Rds") 14 | santiago_zones 15 | } 16 | \keyword{datasets} 17 | -------------------------------------------------------------------------------- /man/uptake_pct_godutch.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/uptake.R 3 | \name{uptake_pct_godutch} 4 | \alias{uptake_pct_godutch} 5 | \title{Calculate cycling uptake for UK 'Go Dutch' scenario} 6 | \usage{ 7 | uptake_pct_godutch( 8 | distance, 9 | gradient, 10 | alpha = -3.959 + 2.523, 11 | d1 = -0.5963 - 0.07626, 12 | d2 = 1.866, 13 | d3 = 0.00805, 14 | h1 = -0.271, 15 | i1 = 0.009394, 16 | i2 = -0.05135, 17 | verbose = FALSE 18 | ) 19 | } 20 | \arguments{ 21 | \item{distance}{Vector distance numeric values of routes in km 22 | (switches to km if more than 100).} 23 | 24 | \item{gradient}{Vector gradient numeric values of routes.} 25 | 26 | \item{alpha}{The intercept} 27 | 28 | \item{d1}{Distance term 1} 29 | 30 | \item{d2}{Distance term 2} 31 | 32 | \item{d3}{Distance term 3} 33 | 34 | \item{h1}{Hilliness term 1} 35 | 36 | \item{i1}{Distance-hilliness interaction term 1} 37 | 38 | \item{i2}{Distance-hilliness interaction term 2} 39 | 40 | \item{verbose}{Print messages? \code{FALSE} by default.} 41 | } 42 | \description{ 43 | This function implements the uptake model described in the original 44 | Propensity to Cycle Tool paper (Lovelace et al. 2017): 45 | https://doi.org/10.5198/jtlu.2016.862 46 | } 47 | \details{ 48 | See \code{\link[=uptake_pct_govtarget]{uptake_pct_govtarget()}}. 49 | } 50 | \examples{ 51 | # https://www.jtlu.org/index.php/jtlu/article/download/862/1381/4359 52 | # Equation 1B: 53 | distance = 15 54 | gradient = 2 55 | logit = -3.959 + 2.523 + 56 | ((-0.5963 - 0.07626) * distance) + 57 | (1.866 * sqrt(distance)) + 58 | (0.008050 * distance^2) + 59 | (-0.2710 * gradient) + 60 | (0.009394 * distance * gradient) + 61 | (-0.05135 * sqrt(distance) * gradient) 62 | logit 63 | # Result: -3.144098 64 | 65 | pcycle = exp(logit) / (1 + exp(logit)) 66 | # Result: 0.04132445 67 | boot::inv.logit(logit) 68 | uptake_pct_godutch(distance, gradient, 69 | alpha = -3.959 + 2.523, d1 = -0.5963 - 0.07626, 70 | d2 = 1.866, d3 = 0.008050, h1 = -0.2710, i1 = 0.009394, i2 = -0.05135 71 | ) 72 | # these are the default values 73 | uptake_pct_godutch(distance, gradient) 74 | l = routes_fast_leeds 75 | pcycle_scenario = uptake_pct_godutch(l$length, l$av_incline) 76 | plot(l$length, pcycle_scenario) 77 | } 78 | -------------------------------------------------------------------------------- /man/uptake_pct_govtarget.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/uptake.R 3 | \name{uptake_pct_govtarget} 4 | \alias{uptake_pct_govtarget} 5 | \alias{uptake_pct_govtarget_2020} 6 | \alias{uptake_pct_godutch_2020} 7 | \alias{uptake_pct_ebike_2020} 8 | \alias{uptake_pct_govtarget_school2} 9 | \alias{uptake_pct_godutch_school2} 10 | \title{Calculate cycling uptake for UK 'Government Target' scenario} 11 | \usage{ 12 | uptake_pct_govtarget( 13 | distance, 14 | gradient, 15 | alpha = -3.959, 16 | d1 = -0.5963, 17 | d2 = 1.866, 18 | d3 = 0.00805, 19 | h1 = -0.271, 20 | i1 = 0.009394, 21 | i2 = -0.05135, 22 | verbose = FALSE 23 | ) 24 | 25 | uptake_pct_govtarget_2020( 26 | distance, 27 | gradient, 28 | alpha = -4.018, 29 | d1 = -0.6369, 30 | d2 = 1.988, 31 | d3 = 0.008775, 32 | h1 = -0.2555, 33 | h2 = -0.78, 34 | i1 = 0.02006, 35 | i2 = -0.1234, 36 | verbose = FALSE 37 | ) 38 | 39 | uptake_pct_godutch_2020( 40 | distance, 41 | gradient, 42 | alpha = -4.018 + 2.55, 43 | d1 = -0.6369 - 0.08036, 44 | d2 = 1.988, 45 | d3 = 0.008775, 46 | h1 = -0.2555, 47 | h2 = -0.78, 48 | i1 = 0.02006, 49 | i2 = -0.1234, 50 | verbose = FALSE 51 | ) 52 | 53 | uptake_pct_ebike_2020( 54 | distance, 55 | gradient, 56 | alpha = -4.018 + 2.55, 57 | d1 = -0.6369 - 0.08036 + 0.05509, 58 | d2 = 1.988, 59 | d3 = 0.008775 - 0.000295, 60 | h1 = -0.2555 + 0.1812, 61 | h2 = -0.78, 62 | i1 = 0.02006, 63 | i2 = -0.1234, 64 | verbose = FALSE 65 | ) 66 | 67 | uptake_pct_govtarget_school2( 68 | distance, 69 | gradient, 70 | alpha = -7.178, 71 | d1 = -1.87, 72 | d2 = 5.961, 73 | h1 = -0.529, 74 | h2 = -0.63, 75 | verbose = FALSE 76 | ) 77 | 78 | uptake_pct_godutch_school2( 79 | distance, 80 | gradient, 81 | alpha = -7.178 + 3.574, 82 | d1 = -1.87 + 0.3438, 83 | d2 = 5.961, 84 | h1 = -0.529, 85 | h2 = -0.63, 86 | verbose = FALSE 87 | ) 88 | } 89 | \arguments{ 90 | \item{distance}{Vector distance numeric values of routes in km 91 | (switches to km if more than 100).} 92 | 93 | \item{gradient}{Vector gradient numeric values of routes.} 94 | 95 | \item{alpha}{The intercept} 96 | 97 | \item{d1}{Distance term 1} 98 | 99 | \item{d2}{Distance term 2} 100 | 101 | \item{d3}{Distance term 3} 102 | 103 | \item{h1}{Hilliness term 1} 104 | 105 | \item{i1}{Distance-hilliness interaction term 1} 106 | 107 | \item{i2}{Distance-hilliness interaction term 2} 108 | 109 | \item{verbose}{Print messages? \code{FALSE} by default.} 110 | 111 | \item{h2}{Hilliness term 2} 112 | } 113 | \description{ 114 | Uptake model that takes distance and hilliness and returns 115 | a percentage of trips that could be made by cycling along a desire line 116 | under scenarios of change. Source: appendix of pct paper, hosted at: 117 | \href{https://www.jtlu.org/index.php/jtlu/article/download/862/1381/4359}{www.jtlu.org} 118 | which states that: "To estimate cycling potential,the Propensity to Cycle Tool (PCT) was 119 | designed to use the best available geographically disaggregated 120 | data sources on travel patterns." 121 | } 122 | \details{ 123 | The functional form of the cycling uptake model used in the PCT is as follows: 124 | (Source: \href{https://npct.github.io/pct-shiny/regions_www/www/static/03a_manual/pct-bike-eng-user-manual-c1.pdf}{npct.github.io}) 125 | 126 | \if{html}{\out{
}}\preformatted{logit (pcycle) = -3.959 + # alpha 127 | (-0.5963 * distance) + # d1 128 | (1.866 * distancesqrt) + # d2 129 | (0.008050 * distancesq) + # d3 130 | (-0.2710 * gradient) + # h1 131 | (0.009394 * distance * gradient) + # i1 132 | (-0.05135 * distancesqrt *gradient) # i2 133 | 134 | pcycle = exp ([logit (pcycle)]) / (1 + (exp([logit(pcycle)]) 135 | }\if{html}{\out{
}} 136 | 137 | \code{uptake_pct_govtarget_2020()} and 138 | \code{uptake_pct_godutch_2020()} 139 | approximate the uptake models used in the updated 2020 release of 140 | the PCT results. 141 | 142 | If the \code{distance} parameter is greater than 100, it is assumed that it is in m. 143 | If for some reason you want to model cycling uptake associated with trips with 144 | distances of less than 100 m, convert the distances to km first. 145 | } 146 | \examples{ 147 | distance = 15 148 | gradient = 2 149 | logit_pcycle = -3.959 + # alpha 150 | (-0.5963 * distance) + # d1 151 | (1.866 * sqrt(distance)) + # d2 152 | (0.008050 * distance^2) + # d3 153 | (-0.2710 * gradient) + # h1 154 | (0.009394 * distance * gradient) + # i1 155 | (-0.05135 * sqrt(distance) * gradient) # i2 156 | boot::inv.logit(logit_pcycle) 157 | uptake_pct_govtarget(15, 2) 158 | l = routes_fast_leeds 159 | pcycle_scenario = uptake_pct_govtarget(l$length, l$av_incline) 160 | pcycle_scenario_2020 = uptake_pct_govtarget_2020(l$length, l$av_incline) 161 | plot(l$length, pcycle_scenario, ylim = c(0, 0.2)) 162 | points(l$length, pcycle_scenario_2020, col = "blue") 163 | 164 | # compare with published PCT data: 165 | \dontrun{ 166 | l_pct_2020 = get_pct_lines(region = "isle-of-wight") 167 | # test for another region: 168 | # l_pct_2020 = get_pct_lines(region = "west-yorkshire") 169 | l_pct_2020$rf_avslope_perc[1:5] 170 | l_pct_2020$rf_dist_km[1:5] 171 | govtarget_slc = uptake_pct_govtarget( 172 | distance = l_pct_2020$rf_dist_km, 173 | gradient = l_pct_2020$rf_avslope_perc 174 | ) * l_pct_2020$all + l_pct_2020$bicycle 175 | govtarget_slc_2020 = uptake_pct_govtarget_2020( 176 | distance = l_pct_2020$rf_dist_km, 177 | gradient = l_pct_2020$rf_avslope_perc 178 | ) * l_pct_2020$all + l_pct_2020$bicycle 179 | mean(l_pct_2020$govtarget_slc) 180 | mean(govtarget_slc) 181 | mean(govtarget_slc_2020) 182 | godutch_slc = uptake_pct_godutch( 183 | distance = l_pct_2020$rf_dist_km, 184 | gradient = l_pct_2020$rf_avslope_perc 185 | ) * l_pct_2020$all + l_pct_2020$bicycle 186 | godutch_slc_2020 = uptake_pct_godutch_2020( 187 | distance = l_pct_2020$rf_dist_km, 188 | gradient = l_pct_2020$rf_avslope_perc 189 | ) * l_pct_2020$all + l_pct_2020$bicycle 190 | mean(l_pct_2020$dutch_slc) 191 | mean(godutch_slc) 192 | mean(godutch_slc_2020) 193 | } 194 | # Take an origin destination (OD) pair between an LSOA centroid and a 195 | # secondary school. In this OD pair, 30 secondary school children travel, of 196 | # whom 3 currently cycle. The fastest route distance is 3.51 km and the 197 | # gradient is 1.11\%. The 198 | # gradient as centred on Dutch hilliness levels is 1.11 – 0.63 = 0.48\%. 199 | # The observed number of cyclists is 2. ... Modelled baseline= 30 * .0558 = 1.8. 200 | uptake_pct_govtarget_school2(3.51, 1.11) 201 | # pcycle = exp ([logit (pcycle)])/(1 + (exp([logit(pcycle)]))). 202 | # pcycle = exp(1.953)/(1 + exp(1.953)) = .8758, or 87.58\%. 203 | uptake_pct_godutch_school2(3.51, 1.11) 204 | } 205 | -------------------------------------------------------------------------------- /man/wight_lines_30.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{wight_lines_30} 5 | \alias{wight_lines_30} 6 | \alias{wight_lines_pct} 7 | \title{Desire lines from the PCT for the Isle of Wight} 8 | \description{ 9 | This data was obtained using code shown in the introductory 10 | \href{https://itsleeds.github.io/pct/articles/pct.html}{pct package vignette}. 11 | } 12 | \examples{ 13 | names(wight_lines_30) 14 | plot(wight_lines_30) 15 | } 16 | \keyword{datasets} 17 | -------------------------------------------------------------------------------- /man/wight_od.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{wight_od} 5 | \alias{wight_od} 6 | \title{Official origin-destination data for the Isle of Wight} 7 | \description{ 8 | This data was obtained using code shown in the introductory 9 | \href{https://itsleeds.github.io/pct/articles/pct.html}{pct package vignette}. 10 | } 11 | \examples{ 12 | names(wight_od) 13 | head(wight_od) 14 | } 15 | \keyword{datasets} 16 | -------------------------------------------------------------------------------- /man/wight_routes_30.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{wight_routes_30} 5 | \alias{wight_routes_30} 6 | \alias{wight_rnet} 7 | \alias{wight_routes_30_cs} 8 | \title{Cycle route data for the Isle of Wight} 9 | \description{ 10 | This data was obtained using code shown in the introductory 11 | \href{https://itsleeds.github.io/pct/articles/pct.html}{pct package vignette}. 12 | } 13 | \examples{ 14 | library(sf) 15 | names(wight_routes_30) 16 | head(wight_routes_30) 17 | plot(wight_routes_30) 18 | } 19 | \keyword{datasets} 20 | -------------------------------------------------------------------------------- /man/wight_zones.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{wight_zones} 5 | \alias{wight_zones} 6 | \alias{wight_centroids} 7 | \title{Zones and centroid data from the PCT for the Isle of Wight} 8 | \description{ 9 | This data was obtained using code shown in the introductory 10 | \href{https://itsleeds.github.io/pct/articles/pct.html}{pct package vignette}. 11 | } 12 | \examples{ 13 | library(sf) 14 | names(wight_lines_30) 15 | plot(wight_lines_30) 16 | } 17 | \keyword{datasets} 18 | -------------------------------------------------------------------------------- /man/zones_leeds.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{zones_leeds} 5 | \alias{zones_leeds} 6 | \title{Zone data for Leeds} 7 | \description{ 8 | Zones in Leeds 9 | } 10 | \examples{ 11 | # see data-raw folder for generation code 12 | zones_leeds 13 | } 14 | \keyword{datasets} 15 | -------------------------------------------------------------------------------- /pct-leeds-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsleeds/pct/8b68960c60c5e05e782f53e3b259c72ef48f49a6/pct-leeds-demo.png -------------------------------------------------------------------------------- /pct.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: XeLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /vignettes/cycling-potential-uk.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Cycling potential in UK cities" 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{Cycling potential in UK cities} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | ```{r, include = FALSE} 11 | knitr::opts_chunk$set( 12 | collapse = TRUE, 13 | comment = "#>", 14 | eval = FALSE 15 | ) 16 | ``` 17 | 18 | Note: this vignette is not evaluated to reduce package build times. 19 | See https://rpubs.com/RobinLovelace/749112 for the results with the code running. 20 | 21 | What modal shift to cycling do you think we could achieve in UK cities in an ideal scenario? 22 | Answers to that question are vital to support high level ambition for active transport and wider sustainability policies in settlements worldwide. 23 | In UK cities such as Leeds (where the motivation for this article originated, via Twitter^[ 24 | See `https://twitter.com/PaulChatterton9/status/1190925153322098690` for the original tweet. 25 | ]) there is already a wealth of freely available data on cycling potential, based on the Propensity to Cycle Tool ([PCT](https://www.pct.bike/)) project, of which this package is a part. 26 | This article will briefly explain how to get cycling potential data for any city in England and Wales, based on a case study of Leeds. 27 | 28 | # Getting the region of interest 29 | 30 | The first stage in many projects involving geographic data is to define the region of interest. 31 | In our case we will use the boundary of the Leeds local authority as the basis of analysis. 32 | The PCT is based on 'PCT regions' that we can visualise as follows: 33 | 34 | 35 | ```{r setup, message=FALSE, warning=FALSE} 36 | library(pct) 37 | library(sf) 38 | library(dplyr) 39 | library(tmap) 40 | tm_shape(pct_regions) + 41 | tm_polygons() + 42 | tm_text("region_name", size = 0.6) 43 | ``` 44 | 45 | We are interested in Leeds in the `west-yorkshire` area. 46 | Using the `pct` package we can get zone data from the PCT as follows (note: this would work by setting `region_of_interest` to any of the regions shown in the map above: 47 | 48 | ```{r} 49 | region_of_interest = "west-yorkshire" 50 | zones_region = get_pct_zones(region = region_of_interest) 51 | # zones_region = get_pct_zones(region = region_of_interest, geography = "lsoa") # for smaller zones 52 | names(zones_region) 53 | plot(zones_region["bicycle"]) 54 | ``` 55 | 56 | ```{r, eval=FALSE, echo=FALSE} 57 | 58 | # tm_shape(zones_region) + 59 | # tm_fill("bicycle", palette = "RdYlBu") + 60 | # tm_shape(pct_regions) + 61 | # tm_borders() + 62 | # tm_text("region_name") 63 | 64 | # reproducible example of fail 65 | 66 | remotes::install_github("mtennekes/tmap") 67 | library(tmap) 68 | u = "https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/lsoa/isle-of-wight/z.geojson" 69 | z = sf::st_read(u) 70 | plot(z["bicycle"]) 71 | qtm(z) 72 | tm_shape(z) + 73 | tm_fill("bicycle", palette = "RdYlBu") 74 | tmap_mode("view") 75 | qtm(z) 76 | 77 | # another region 78 | tmap_mode("plot") 79 | u = "https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/lsoa/west-yorkshire/z.geojson" 80 | z = sf::st_read(u) 81 | plot(z["bicycle"]) 82 | qtm(z) 83 | tm_shape(z) + 84 | tm_fill("bicycle", palette = "RdYlBu") 85 | tmap_mode("view") 86 | qtm(z) 87 | mapview::mapview(z) 88 | 89 | devtools::session_info() 90 | ``` 91 | 92 | 93 | 94 | This shows the number of people who say they cycled to work in the 2011 Census. 95 | This may have changed slightly as cycling becomes more popular in some places, but is a good proxy for current cycling levels. 96 | But there are two big problems with this: the map is of West Yorkshire not Leeds, and it's showing current cycling rates, not future potential. 97 | 98 | # Getting cycling potential accross local authorities 99 | 100 | We can find out what local authorities there are in the zones as follows: 101 | 102 | ```{r} 103 | unique(zones_region$lad_name) 104 | ``` 105 | 106 | We are interested in the zones in Leeds, which we can get as follows: 107 | 108 | ```{r} 109 | zones = zones_region %>% 110 | filter(lad_name == "Leeds") 111 | tm_shape(zones) + 112 | tm_fill("bicycle", palette = "RdYlBu") 113 | ``` 114 | 115 | Great, this represents the current level of cycling across the region of interest. 116 | What about cycling potential? Based on the modelling work in the PCT, we have pre-calculated this for a number of scenarios. 117 | Let's take a look at cycling to work under the Government Target (near market), Go Dutch and Ebikes scenarios for cycling to work: 118 | 119 | ```{r} 120 | scenarios_of_interest = c("govnearmkt_slc", "dutch_slc", "ebike_slc") 121 | tm_shape(zones) + 122 | tm_fill(scenarios_of_interest, palette = "RdYlBu", n = 9, title = "N. cycling") + 123 | tm_facets(nrow = 1, free.scales = FALSE) + 124 | tm_layout(panel.labels = scenarios_of_interest) 125 | ``` 126 | 127 | That's great, but it doesn't answer the question of mode share. 128 | 129 | # Cycling mode share 130 | 131 | We can calculate this by dividing the number of people cycling to work by the total: 132 | 133 | ```{r} 134 | zones_mode_share = zones %>% 135 | select(scenarios_of_interest) %>% 136 | mutate_at(scenarios_of_interest, .funs = list(~ ./zones$all * 100)) 137 | tm_shape(zones_mode_share) + 138 | tm_fill(scenarios_of_interest, palette = "RdYlBu", title = "% cycling") + 139 | tm_facets(nrow = 1, free.scales = FALSE) + 140 | tm_layout(panel.labels = scenarios_of_interest) 141 | ``` 142 | 143 | The summary mode shares can be estimated as follows, with results showing the % cycling currently (according to the 2011 census) and under scenarios of change: 144 | 145 | ```{r} 146 | zones_region %>% 147 | st_drop_geometry() %>% 148 | group_by(lad_name) %>% 149 | select(`2011 census` = bicycle, c(scenarios_of_interest, "all")) %>% 150 | summarise_all(.funs = ~ round(sum(.)/sum(all)* 100)) %>% 151 | select(-all, `Local Authority / % Cycling in scenario:` = lad_name) %>% 152 | knitr::kable() 153 | ``` 154 | 155 | 156 | 157 | 158 | 159 | # Cycling potential nationwide 160 | 161 | To generalise the analysis outlined above, we can download national data from the PCT project as follows: 162 | 163 | ```{r national-dl} 164 | zones_national = read_sf("https://github.com/npct/pct-outputs-national/raw/master/commute/msoa/z_all.geojson") 165 | ``` 166 | 167 | A summary of cycling potential nationwide can then be calculated as follows: 168 | 169 | ```{r} 170 | national_commute_totals = zones_national %>% 171 | st_drop_geometry() %>% 172 | select(all, census_2011 = bicycle, govtarget_slc, dutch_slc) %>% 173 | summarise_all(.funs = ~sum(.)) 174 | national_commute_percentages = national_commute_totals / national_commute_totals$all * 100 175 | ``` 176 | 177 | ```{r, echo=FALSE} 178 | knitr::kable(bind_rows(national_commute_totals, national_commute_percentages), digits = 1, 179 | caption = "Total counts and percentages of cycle commuters under different scenarios") 180 | ``` 181 | 182 | We can calculate the mode share of cycling under these same scenarios for any area (e.g. the boundary of Greater London) or for a list of named local authorities, as follows: 183 | 184 | ```{r} 185 | r = read.csv(stringsAsFactors = FALSE, text = "area 186 | Greater London 187 | Greater Manchester 188 | Birmingham 189 | Leeds 190 | Glasgow 191 | Liverpool 192 | Newcastle 193 | Bristol 194 | Cardiff 195 | Belfast 196 | Southampton 197 | Sheffield 198 | ") 199 | matching_las = pct_regions_lookup$lad16nm[pct_regions_lookup$lad16nm %in% r$area] 200 | matching_regions = c("london", "greater-manchester") 201 | pct_lookup = pct_regions_lookup %>% 202 | rename(lad_name = lad16nm) 203 | zones_national = inner_join(zones_national, pct_lookup) 204 | zones_national = zones_national %>% 205 | mutate(area = case_when( 206 | region_name == "london" ~ "Greater London", 207 | region_name == "greater-manchester" ~ "Greater Manchester", 208 | lad_name %in% matching_las ~ lad_name, 209 | TRUE ~ "Other" 210 | )) 211 | table(zones_national$area) 212 | zones_aggregated = zones_national %>% 213 | sf::st_drop_geometry() %>% 214 | group_by(area) %>% 215 | summarise( 216 | Commuters = sum(all, na.rm = TRUE), 217 | Bicycle_census = sum(bicycle), 218 | Bicycle_govtarget = sum(govtarget_slc), 219 | Bicycle_godutch = sum(dutch_slc) 220 | ) 221 | 222 | # plot(zones_aggregated["Commuters"], border = NA) 223 | zones_aggregated %>% 224 | inner_join(r, .) %>% 225 | knitr::kable(digits = 0) 226 | ``` 227 | 228 | Next we can calculate the mode splits as follows: 229 | 230 | ```{r} 231 | zones_aggregated_percents = zones_aggregated %>% 232 | mutate_at(vars(-Commuters, -area), funs(./Commuters * 100)) 233 | names(zones_aggregated_percents)[3:5] = paste0(names(zones_aggregated_percents)[3:5], "_percent") 234 | zones_aggregated_percents %>% 235 | inner_join(r, .) %>% 236 | knitr::kable(digits = 1) 237 | ``` 238 | 239 | # Considerations 240 | 241 | The results above show that there is huge potential for cycling grow across England and Wales. 242 | Around 16% of commuters in Leeds could cycle to work if we 'Go Dutch' (25% if we Go Dutch and see high uptake of electric bikes) for the single mode journey to work data under consideration. 243 | However, there are some wider considerations that are not captured in the numbers (see the [PCT paper](https://www.jtlu.org/index.php/jtlu/article/view/862) for details and other considerations): 244 | 245 | - The PCT commute layer (don't forget the travel to schools layer!) only considers single stage journeys - if we were to include cycling to the station or bus stop potential it would be greater than 16%, perhaps more like 30% if potential cycle/rail and cycle/bus trips are accounted for. 246 | - Work patterns are changing, and with new digital work practices or a shift towards local economies, more people could work from home or at least near to home. 247 | - The scenarios are snapshots of how cycling levels could change based current trip patterns, not predictions of what will happen. There are many other scenarios that could be developed, including intermediate scenarios between Go Dutch and the Government Target, and more ambitious scenarios, e.g. 'Go Utrecht', where cycling has reached around 60% of trips ([source](https://www.bloomberg.com/news/articles/2019-07-05/how-the-dutch-made-utrecht-a-bicycle-first-city)). 248 | 249 | Other considerations that are often mentioned are hills and people who are unable to cycle. 250 | In fact the PCT accounts for hills and, as the example of Bristol shows, hilly cities can see rapid increases in cycling. 251 | Regarding ability to cycle, it is a more inclusive mode than driving, with 8 to 80 year-olds being able to cycle, and many more people being able to afford a bike than a car (which, thanks to the expense of buying, running and insuring the vehicle, is largely the mode of choice of the wealthy). 252 | The PCT does not account for the fact that some trips to work are also 'caregiving' trips, but with electric cargo bikes and [multi-person cycles](https://en.wikipedia.org/wiki/Cycle_rickshaw) cycling-focused cities can, to a greater extent than car-dominated transport systems, deliver for all. 253 | 254 | ## Challenge 255 | 256 | To anyone interested in cycling in your city: try re-running the reproducible code above for your PCT region and local authority. 257 | Let us know on social media and [GitHub](https://github.com/ITSLeeds/pct/issues) how you get on. 258 | Any comments/contributions to this analysis: [welcome.](https://github.com/ITSLeeds/pct/edit/master/vignettes/cycling-potential-uk.Rmd) 259 | -------------------------------------------------------------------------------- /vignettes/km-cycled.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Estimating distance cycled per zone in England and Wales regions" 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{Estimating distance cycled per zone in England and Wales regions} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | author: 9 | - "Nathanael Sheehan" 10 | --- 11 | 12 | In the context of society, cycling acts as a vessel to mobilise citizens to navigate through spatial geography in a sustainable fashion. In doing so, cycling obtains numerous socio-spatial benefits both at the micro (individual health benefits from active transport) and macro (lower pollution from vehicles) scale. With the ambition of building a sustainable future, the PCT project promotes cycle uptake through providing open tools for policy-makers, transport planners and academics to plan future cycle infrastructure for the built environment. 13 | 14 | Using data from the 2011 census and models built into the PCT package, this article asks the question of "How many km's per LSOA do commuters cycle through each year?". The results of which provide valuable insight into future sustainable transport planning decisions and highlight the versatility of the PCT package through a reproducible method. 15 | 16 | Set `eval=TRUE` to run this code when knitting: 17 | 18 | ```{r} 19 | knitr::opts_chunk$set(eval = FALSE) 20 | ``` 21 | 22 | 23 | ## Prerequisites 24 | 25 | The following packages must be installed and loaded to run the code in this vignette. 26 | 27 | ```{r, message=FALSE, warning=FALSE} 28 | # Load packages 29 | library(pct) 30 | library(tmap) 31 | ``` 32 | 33 | ## Estimating distance cycled for a region based on 2011 census data 34 | 35 | The PCT package permits an easy method for fetching road network and commuting origin/destination data for any region in England and Wales. This data will be used as the basis for understanding the current context of cycling for a given region (A definition of regions can be found in this earlier article). For this first example, we will apply the pct method to the region of Oxfordshire. To kick things off, we use the ```get_pct_rnet``` to retrieve road network data for the region. This data is then used to calculate the length of the road network cycled using ```base ``` r functions. 36 | 37 | ```{r, message=FALSE, warning=FALSE} 38 | #Get road network for preselected regin 39 | rnet = pct::get_pct_rnet(region = "oxfordshire") 40 | #Calculate road length 41 | rnet$segment_length = as.numeric(sf::st_length(rnet)) 42 | #Calculate daily km's cycled 43 | rnet$m_cycled_per_working_day = rnet$segment_length * rnet$bicycle 44 | ``` 45 | 46 | Simples! Now we can use the ```get_pct_zones``` function to get LSOA geographies in order to calculate the number of km's cycled per day through each LSOA. 47 | 48 | ```{r, message=FALSE, warning=FALSE} 49 | # Get road network for preselected regin 50 | zones = pct::get_pct_zones(region = "oxfordshire") 51 | summary(sf::st_is_valid(zones)) 52 | zones = sf::st_make_valid(zones) 53 | summary(sf::st_is_valid(zones)) 54 | cycled_m_per_zone = aggregate(rnet["m_cycled_per_working_day"], zones, FUN = sum) 55 | ``` 56 | 57 | While this is great information, it doesn't provide answer the long term question of how many distance are cycled per year. In order to achieve this, we must multiple the number of trips per day by the number of days in a standard working year (200) and divide by a million. Once again, this is achieved using ```base``` r functionality. 58 | 59 | ```{r, message=FALSE, warning=FALSE} 60 | zones$mkm_cycled_for_commuting_per_year_estimated = cycled_m_per_zone$m_cycled_per_working_day * 61 | 2 * 200 / # estimate of trips days per year, morning and afternoon 62 | 1e9 63 | ``` 64 | 65 | With this calculated, we can now answer our question and visualize yearly km's cycled using ```tmap```. 66 | 67 | ```{r, message=FALSE, warning=FALSE, fig.align="center", fig.width = 7, fig.height = 6} 68 | tm_shape(zones) + 69 | tm_fill( 70 | col = "mkm_cycled_for_commuting_per_year_estimated", 71 | style = "quantile", 72 | palette = "plasma", 73 | title = "Yearly distance cycled by commuters per day\n(2011 Census Data)", 74 | legend.size.is.portrait = TRUE 75 | ) + 76 | tm_layout( 77 | title = "OXFORDSHIRE", 78 | title.position = c("left", "top"), 79 | bg.color = "honeydew3", 80 | outer.bg.color = "honeydew", 81 | legend.stack = "horizontal", 82 | legend.outside = TRUE, 83 | legend.outside.position = "left", 84 | frame.lwd = 5 85 | ) 86 | ``` 87 | 88 | Wow, that's a lot of miles cycled! The plot breaks down the number km's cycled by quartiles and thus paints the picture of where cycling is most prominent. In the context of Oxfordshire, the map depecits the region obtainaing a rather high cycle uptake; with its spatial distribution primarily in the centre of the region. 89 | 90 | ## Comparing Regions 91 | 92 | Now say we wanted to compare the results of Oxfordshire, with its regional neighbor Cambridgeshire. This can be achieved by wrapping the previous method into a function. 93 | 94 | ```{r, message=FALSE, warning=FALSE} 95 | pct_zones_rnet_current = function(region_name) { 96 | # Get road network for preselected regin 97 | rnet = pct::get_pct_rnet(region = region_name) 98 | # Calculate road length 99 | rnet$segment_length = as.numeric(sf::st_length(rnet)) 100 | # Calculate daily km cycled 101 | rnet$m_cycled_per_working_day = rnet$segment_length * rnet$bicycle 102 | # Convert to centroids to avoid double counting flows that cross zones 103 | rnet_centroids = sf::st_centroid(rnet) 104 | # Get LSOA spatial data 105 | zones = sf::st_make_valid(pct::get_pct_zones(region = region_name)) 106 | # Calculate cyced miles per zone 107 | cycled_m_per_zone = aggregate(rnet_centroids["m_cycled_per_working_day"], zones, FUN = sum) 108 | # Calculate miles cycled per year from commuting 109 | zones$mkm_cycled_for_commuting_per_year_estimated = cycled_m_per_zone$m_cycled_per_working_day * 110 | 2 * 200 / # estimate of trips days per year, morning and afternoon 111 | 1e9 112 | # Plot results 113 | tmap_mode("plot") 114 | tm_shape(zones) + 115 | tm_fill( 116 | col = "mkm_cycled_for_commuting_per_year_estimated", 117 | style = "quantile", 118 | palette = "plasma", 119 | title = "Million km's cycled by commuters per year\n(2011 Census Data)", 120 | legend.size.is.portrait = TRUE 121 | ) + 122 | tm_layout( 123 | title = toupper(region_name), 124 | title.position = c("left", "top"), 125 | bg.color = "honeydew3", 126 | outer.bg.color = "honeydew", 127 | legend.stack = "horizontal", 128 | legend.outside = TRUE, 129 | legend.outside.position = "bottom", 130 | frame.lwd = 5 131 | ) 132 | 133 | } 134 | ``` 135 | 136 | With our function built, we can now easily compare regional differences. 137 | 138 | ```{r, message=FALSE, warning=FALSE, fig.align="center", fig.width = 7, fig.height = 6} 139 | oxfordshire_results = pct_zones_rnet_current(region_name = "oxfordshire") 140 | cambrideshire_results = pct_zones_rnet_current(region_name = "cambridgeshire") 141 | tmap_arrange(oxfordshire_results, cambrideshire_results, ncol = 2) 142 | ``` 143 | 144 | In comparison to Oxfordshire, Cambridgeshire spatial distribution of km's cycled exhibit a high number of cycling not only in the centre of the region. While Oxfordshire obtains a zone with a higher number of km's cycled through it, in general Cambridgeshire displays a higher number in its quartile range. Interesting! 145 | 146 | Now lets say we wanted to compare two cities. Lets take London and Greater Manchester. 147 | 148 | ```{r, message=FALSE, warning=FALSE, fig.align="center", fig.width = 7, fig.height = 6} 149 | london_results = pct_zones_rnet_current(region_name = "london") 150 | gm_results = pct_zones_rnet_current(region_name = "greater-manchester") 151 | #tmap_mode("view") 152 | tmap_arrange(london_results, gm_results, ncol = 2) 153 | ``` 154 | 155 | Jheeze! The maps provides insight at a highly granular level. Both Greater Manchester and London have similar quartile ranges and are fairly monocentric, however, LSOA's in London such as ```E01032739``` obtain billions in km's cycled per year; likely due to working population differences. Nonetheless, outer LSOA's in both Greater Manchester and London also feature in the highest quartile, representing that cycle uptake is not dominant solely in the centre of the regions. 156 | 157 | ## Using PCT models to visualize potential cycle uptake 158 | 159 | While the results based on the 2011 census provide interesting and valuable results, a lot has changed since 2011 and other scenarios should be explored. The PCT package features numerous models (e.g. ```govtarget_slc```,```gendereq_slc``` and ```ebike_slc```). In this example, lets examine London and Manchester again, but this time, lets use the PCT E-bike model scenario. The PCT E-Bike model assumes commuters will use e-Bikes for longer or hillier trips (based on data from Dutch and Swiss travel surveys). In order to model this, we will build a function similar to what we used for the census data, but change the daily miles cycled to use the ```ebike_slc``` column. 160 | 161 | ```{r, message=FALSE, warning=FALSE} 162 | pct_zones_rnet_ebikes <- function(region_name) { 163 | # Get road network for pre-selected region 164 | rnet = pct::get_pct_rnet(region = region_name) 165 | # Calculate road length 166 | rnet$segment_length = as.numeric(sf::st_length(rnet)) 167 | # Calculate daily miles cycled 168 | rnet$m_cycled_per_working_day = rnet$segment_length * rnet$ebike_slc 169 | # Convert to centroids to avoid double counting flows that cross zones 170 | rnet_centroids = sf::st_centroid(rnet) 171 | # Get LSOA spatial data 172 | zones = sf::st_make_valid(pct::get_pct_zones(region = region_name)) 173 | # Calculate cycled miles per zone 174 | cycled_m_per_zone = aggregate(rnet_centroids["m_cycled_per_working_day"], zones, FUN = sum) 175 | # Calculate km cycled per year from commuting 176 | zones$mkm_cycled_for_commuting_per_year_estimated = cycled_m_per_zone$m_cycled_per_working_day * 177 | 2 * 200 / # estimate of trips days per year, morning and afternoon 178 | 1e9 179 | #Plot results 180 | tmap_mode("plot") 181 | tm_shape(zones) + 182 | tm_fill( 183 | col = "mkm_cycled_for_commuting_per_year_estimated", 184 | style = "quantile", 185 | palette = "plasma", 186 | title = "Million km's cycled by commuters per year\n(E-Bike model)", 187 | legend.size.is.portrait = TRUE 188 | ) + 189 | tm_layout( 190 | title = toupper(region_name), 191 | title.position = c("left", "top"), 192 | bg.color = "honeydew3", 193 | outer.bg.color = "honeydew", 194 | legend.stack = "horizontal", 195 | legend.outside = TRUE, 196 | legend.outside.position = "bottom", 197 | frame.lwd = 5 198 | ) 199 | } 200 | ``` 201 | 202 | With our E-Bikes function built, we can map the results to visualise the effect an E-bike scenario may have on these two cities. 203 | 204 | ```{r, message=FALSE, warning=FALSE, fig.align="center", fig.width = 7, fig.height = 6} 205 | london_results_ebikes = pct_zones_rnet_ebikes(region_name = "london") 206 | gm_results_ebikes = pct_zones_rnet_ebikes(region_name = "greater-manchester") 207 | #tmap_mode("view") 208 | tmap_arrange(london_results_ebikes, gm_results_ebikes, ncol = 2) 209 | ``` 210 | 211 | Wow! The maps show how an E-Bike scenario could increase cycle uptake by ~500%. As this model illustrates, an E-Bike scenario would mean more LSOA's would be in the highest quartile of distance cycled. This would mean safer roads, cleaner air and a more sustainable future. 212 | 213 | ## Conclusions and next steps 214 | 215 | As demonstrated, the PCT packages provides easy methods to visualize cycle potential. This article has briefly shown how you can use the PCT package models to understand what future cycle uptake could look like and how to calculate the number of km's cycled per year at a granular level. If this article is of use/interest, why not try for your local region showing the potential based on different models. You can also try the following resources (in R and online): 216 | 217 | * Have a play with the PCT web application 218 | * For more of a deep dive into the methods, see the pct_training vignette 219 | * See the pct-international to see how to apply the methods internationally 220 | 221 | We’re interested to know how you’ve used the methods/data so please get in touch on social media and GitHub letting us know how get on. Any comments/contributions to this analysis: welcome. 222 | -------------------------------------------------------------------------------- /vignettes/nature.csl: -------------------------------------------------------------------------------- 1 | 2 | 126 | -------------------------------------------------------------------------------- /vignettes/pct-international.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "International application of the PCT methods" 3 | author: "Robin Lovelace, Layik Hama" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{International application of the PCT} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | ```{r setup, include = FALSE} 13 | knitr::opts_chunk$set( 14 | collapse = TRUE, 15 | comment = "#>" 16 | ) 17 | ``` 18 | 19 | ## Introduction 20 | 21 | The package README shows how the PCT can be used to get and reproduce some of the datasets from the PCT package, based on an example in the city of Leeds. 22 | This vignette shows how the package can be used to create estimates of cycling potential in other cities. 23 | 24 | Set `eval=TRUE` to run this code when knitting: 25 | 26 | ```{r} 27 | knitr::opts_chunk$set(eval = FALSE) 28 | ``` 29 | 30 | Let's start by loading the package: 31 | 32 | ```{r libraries, message=FALSE} 33 | # devtools::install_github("ATFutures/geoplumber") 34 | # require("geojsonsf") 35 | library(pct) 36 | ``` 37 | 38 | ## Input data 39 | 40 | The input data for this vignetted was created using code in the [pctSantiago](https://github.com/pedalea/pctSantiago) project. 41 | It looks like this, in terms of the flow data: 42 | 43 | ```{r} 44 | head(santiago_od) 45 | ``` 46 | 47 | In terms of the zone data, they look like this: 48 | 49 | ```{r} 50 | sf:::print.sf(santiago_zones) 51 | plot(santiago_zones) 52 | ``` 53 | 54 | Note that we have cycling estimates for each desire line. 55 | If this data is not known, current cycling levels can be approximated for all desire lines as the city-wide average, e.g. around 5% for Santiago. 56 | 57 | ## Creating desire lines 58 | 59 | The origin-destination data can be converted to geographic desire lines using the `stplanr` function `od2line` as follows: 60 | 61 | ```{r, warning=FALSE} 62 | desire_lines = stplanr::od2line(flow = santiago_od, zones = santiago_zones) 63 | ``` 64 | 65 | The resulting lines can then be plotted on top of zone data as follows: 66 | 67 | ```{r, out.width="100%"} 68 | plot(santiago_zones$geometry) 69 | plot(santiago_lines["pcycle"], lwd = santiago_lines$n / 3, add = TRUE) 70 | # gj = geojsonsf::sf_geojson(santiago_lines) 71 | # path = file.path(tempdir(), "dl.geojson") 72 | # write(gj, path) 73 | # html_map = geoplumber::gp_map(path, browse_map = FALSE) 74 | # htmltools::includeHTML(html_map) 75 | ``` 76 | 77 | The previous map suggests that the data is reliable: we have created a good approximation of the travel pattern in central Santiago. 78 | 79 | ## Estimating cycling uptake 80 | 81 | To estimate cycling potential, we need estimates of distance and hilliness. 82 | The area under investigation is relatively flat so we can make the simplifying assumption that hilliness is 0% for all lines (normally we would get this information from a routing service): 83 | 84 | ```{r} 85 | desire_lines$hilliness = 0 86 | ``` 87 | 88 | And what about the distance? 89 | We can calculated it as follows (note we converted this into a numeric object to prevent issues associated with the `units` package): 90 | 91 | ```{r} 92 | desire_lines$distance = as.numeric(sf::st_length(desire_lines)) 93 | ``` 94 | 95 | Now we have (very) crude estimates of distance and hilliness, we can estimate the cycling potential as follows: 96 | 97 | ```{r} 98 | desire_lines$godutch_pcycle = uptake_pct_godutch(distance = desire_lines$distance, gradient = 0) 99 | ``` 100 | 101 | Let's take a look at the results, compared with the current levels of cycling, and compared with distance: 102 | 103 | ```{r} 104 | cor(x = desire_lines$pcycle, y = desire_lines$godutch_pcycle) 105 | plot(x = desire_lines$pcycle, y = desire_lines$godutch_pcycle) 106 | plot(x = desire_lines$distance, y = desire_lines$godutch_pcycle, ylim = c(0, 1)) 107 | ``` 108 | 109 | As expected, there is a positive (albeit small) positive correlation between current and potential levels of cycling. 110 | The result shows clearly that distance decay kicks in just after 2km, but still at 8 km there is a 25% mode share, suggesting a major switch to cycling. 111 | 112 | We can put the results on a map as follows: 113 | 114 | ```{r, out.width="50%", fig.show='hold'} 115 | library(leaflet) 116 | leaflet(width = "100%") %>% 117 | addTiles() %>% 118 | addPolylines(data = desire_lines, weight = desire_lines$pcycle * 5) 119 | leaflet(width = "100%") %>% 120 | addTiles() %>% 121 | addPolylines(data = desire_lines, weight = desire_lines$godutch_pcycle * 5) 122 | ``` 123 | 124 | The results show that there is substantial potential for increasing the levels of cycling in central Santiago, based on origin-destination data alone. 125 | However, to inform policy, more detailed estimates of cycling potential are needed, with results that go down to the level of individual streets. 126 | This involves routing. 127 | 128 | ## Routing 129 | 130 | There are many ways to do calculate a path on the street network. 131 | The main options are local routing, where the calculation is done based on data stored locally (on your computer) and routing services, where the data is done remotely ('in the cloud'). 132 | Let's use a remote routing service to convert the straight lines generated in the previous section into routes that could realistically be taken by people cycling (the next line does not run by default because it requires an API key saved as an environment variable, see the [documentation of `route_cyclestreets()` for details](https://docs.ropensci.org/stplanr/reference/index.html#section-routing) for details). 133 | 134 | ```{r, eval=FALSE} 135 | santiago_routes_cs = stplanr::line2route(desire_lines) 136 | # > 10 % out of 200 distances calculated 137 | # > 20 % out of 200 distances calculated 138 | # > 30 % out of 200 distances calculated 139 | # > 40 % out of 200 distances calculated 140 | # > 50 % out of 200 distances calculated 141 | # > 60 % out of 200 distances calculated 142 | # > 70 % out of 200 distances calculated 143 | # > 80 % out of 200 distances calculated 144 | # > 90 % out of 200 distances calculated 145 | # > 100 % out of 200 distances calculated 146 | # > Warning message: 147 | # > In value[[3L]](cond) : Fail for line number 32 148 | ``` 149 | 150 | Note that one of the routes failed. 151 | We can look at this route as follows, to try to understand what happened: 152 | 153 | ```{r} 154 | leaflet() %>% 155 | addTiles() %>% 156 | addPolylines(data = santiago_routes_cs[32, ]) 157 | ``` 158 | 159 | The result shows that one end of the route connects to the Sendero Ciclistas, which the routing service may be unable to reach. 160 | At this stage there are 2 main options: 1) to omit the data point from the analysis, acknowledging that the data is incomplete; or 2) identify a nearby location where the service *can* route to. 161 | In this case we will take option 1. 162 | Before we remove the offending line 32 from the analysis, we will join the data from the desire lines back onto the results: 163 | 164 | ```{r} 165 | routes = sf::st_sf( 166 | cbind(sf::st_drop_geometry(santiago_routes_cs), 167 | sf::st_drop_geometry(desire_lines)), 168 | geometry = santiago_routes_cs$geometry 169 | ) 170 | ``` 171 | 172 | Update results... 173 | 174 | ```{r} 175 | routes$godutch_slc = round(routes$godutch_pcycle * routes$all) 176 | rnet = stplanr::overline2(routes, "godutch_slc") 177 | plot(rnet, lwd = rnet$godutch_slc / mean(rnet$godutch_slc)) 178 | # library(tmap) 179 | # tmap_mode("view") 180 | # tm_shape(rnet) + 181 | # tm_lines(lwd = "godutch_slc", scale = 9) 182 | ``` 183 | 184 | 185 | ## Next steps 186 | 187 | Clearly to do this in a production environment we would use a larger dataset, but the concepts would be the same. 188 | We would refine the method in multiple ways. 189 | The next basic step, however, would be to convert the straight desire lines into routes, to calculate more accurate distance and hilliness levels for each OD pair. 190 | Then we would be able to create a route network to help prioritise cycling across the city. 191 | 192 | 193 | ```{r, eval=FALSE} 194 | route_segments_1_5 = route(l = desire_lines[1:5, ], route_fun = cyclestreets::journey) 195 | mapview::mapview(route_segments_1_5) 196 | ``` 197 | -------------------------------------------------------------------------------- /vignettes/refs.bib: -------------------------------------------------------------------------------- 1 | @TechReport{department_for_transport_national_2016, 2 | type = {Active {Travel}}, 3 | title = {National {Propensity} to {Cycle} {Tool} {Project}: full report with annexes}, 4 | url = {https://www.gov.uk/government/publications/national-propensity-to-cycle-first-phase-development-study}, 5 | institution = {Department for Transport}, 6 | author = {{Department for Transport}}, 7 | year = {2016}, 8 | } 9 | 10 | @TechReport{department_for_transport_local_2017, 11 | title = {Local {Cycling} and {Walking} {Infrastructure} {Plans}}, 12 | url = {https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/607016/cycling-walking-infrastructure-technical-guidance.pdf}, 13 | urldate = {2019-03-24TZ}, 14 | institution = {Department for Transport}, 15 | author = {{Department for Transport}}, 16 | year = {2017}, 17 | } 18 | 19 | @Article{goodman_scenarios_2019, 20 | title = {Scenarios of cycling to school in {England}, and associated health and carbon impacts: {Application} of the ‘{Propensity} to {Cycle} {Tool}’}, 21 | volume = {12}, 22 | issn = {2214-1405}, 23 | shorttitle = {Scenarios of cycling to school in {England}, and associated health and carbon impacts}, 24 | url = {http://www.sciencedirect.com/science/article/pii/S2214140518301257}, 25 | doi = {10.1016/j.jth.2019.01.008}, 26 | abstract = {Background 27 | The Propensity to Cycle Tool (PCT) is a freely available, interactive tool help prioritise cycling initially launched in England in 2017 and based on adult commuting data. This paper applies the method to travel to school data, and assesses health and carbon benefits based on nationwide scenarios of cycling uptake. 28 | Methods 29 | The 2011 National School Census provides origin-destination data for all state-funded schools in England (N = 7,442,532 children aged 2–18 in 21,443 schools). Using this dataset, we modelled propensity to cycle as a function of route distance and hilliness between home and school. We generated scenarios, including ‘Go Dutch’ – in which English children were as likely to cycle as Dutch children, accounting for trip distance and hilliness. We estimated changes in the level of cycling, walking, and driving, and associated impacts on physical activity and carbon emissions. 30 | Results 31 | In 2011, 1.8\% of children cycled to school (1.0\% in primary school, 2.7\% in secondary school). If Dutch levels of cycling were reached, under the Go Dutch scenario, this would rise to 41.0\%, a 22-fold increase. This is larger than the 6-fold increase in Go Dutch for adult commuting. This would increase total physical activity among pupils by 57\%, and reduce transport-related carbon emissions by 81 kilotonnes/year. These impacts would be substantially larger in secondary schools than primary schools (a 96\% vs. 9\% increase in physical activity, respectively). 32 | Conclusion 33 | Cycling to school is uncommon in England compared with other Northern European countries. Trip distances and hilliness alone cannot explain the difference, suggesting substantial unmet potential. We show that policies resulting in substantial uptake of cycling to school would have important health and environmental benefits. At the level of road networks, the results can inform local investment in safe routes to school to help realise these potential benefits.}, 34 | urldate = {2019-03-04TZ}, 35 | journal = {Journal of Transport \& Health}, 36 | author = {Anna Goodman and Ilan Fridman Rojas and James Woodcock and Rachel Aldred and Nikolai Berkoff and Malcolm Morgan and Ali Abbas and Robin Lovelace}, 37 | month = {mar}, 38 | year = {2019}, 39 | keywords = {Active travel, Carbon emissions, Cycling, Modelling, Physical activity, School}, 40 | pages = {263--278}, 41 | } 42 | 43 | @Article{lovelace_propensity_2017, 44 | title = {The {Propensity} to {Cycle} {Tool}: {An} open source online system for sustainable transport planning}, 45 | volume = {10}, 46 | copyright = {Copyright (c) 2016 Robin Lovelace, Anna Goodman, Rachel Aldred, Nikolai Berkoff, Ali Abbas, James Woodcock}, 47 | issn = {1938-7849}, 48 | shorttitle = {The {Propensity} to {Cycle} {Tool}}, 49 | url = {https://www.jtlu.org/index.php/jtlu/article/view/862}, 50 | doi = {10.5198/jtlu.2016.862}, 51 | abstract = {Getting people cycling is an increasingly common objective in transport planning institutions worldwide. A growing evidence base indicates that high quality infrastructure can boost local cycling rates. Yet for infrastructure and other cycling measures to be effective, it is important to intervene in the right places, such as along ‘desire lines’ of high latent demand. This creates the need for tools and methods to help answer the question ‘where to build?’. Following a brief review of the policy and research context related to this question, this paper describes the design, features and potential applications of such a tool. The Propensity to Cycle Tool (PCT) is an online, interactive planning support system that was initially developed to explore and map cycling potential across England (see www.pct.bike). Based on origin-destination data it models cycling levels at area, desire line, route and route network levels, for current levels of cycling, and for scenario-based ‘cycling futures.’ Four scenarios are presented, including ‘Go Dutch’ and ‘Ebikes,’ which explore what would happen if English people had the same propensity to cycle as Dutch people and the potential impact of electric cycles on cycling uptake. The cost effectiveness of investment depends not only on the number of additional trips cycled, but on wider impacts such as health and carbon benefits. The PCT reports these at area, desire line, and route level for each scenario. The PCT is open source, facilitating the creation of scenarios and deployment in new contexts. We conclude that the PCT illustrates the potential of online tools to inform transport decisions and raises the wider issue of how models should be used in transport planning.}, 52 | language = {en}, 53 | number = {1}, 54 | urldate = {2017-06-01TZ}, 55 | journal = {Journal of Transport and Land Use}, 56 | author = {Robin Lovelace and Anna Goodman and Rachel Aldred and Nikolai Berkoff and Ali Abbas and James Woodcock}, 57 | month = {jan}, 58 | year = {2017}, 59 | note = {bibtex:lovelace\_propensity\_2017}, 60 | keywords = {Cycling, Participatory, Planning, modelling}, 61 | } 62 | -------------------------------------------------------------------------------- /vignettes/uk-cities.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Reproducing cycling potential estimates in UK cities" 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{Reproducing cycling potential estimates in UK cities} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | author: 9 | - "Nathanael Sheehan" 10 | --- 11 | 12 | ```{r, include = FALSE} 13 | knitr::opts_chunk$set( 14 | collapse = TRUE, 15 | comment = "#>" 16 | ) 17 | ``` 18 | 19 | Where is cycle infrastructure most needed in UK cities? Clear answers can fundamentally change our built environment and support national initiatives to build a more sustainable future. With the aim of enabling cycling uptake, the PCT project provides transport planners, policy-makers and academics with free tools to prioritise investments and interventions. 20 | 21 | Evidence-based interventions can make streets safer for future generations. 22 | This article demonstrates how to retrieve cycling potential data for any UK city, down to the route network level (see the `cycling-potential-uk` [here](https://itsleeds.github.io/pct/articles/cycling-potential-uk.html) or by running [`vignette("cycling-potential-uk")`](https://itsleeds.github.io/pct/articles/cycling-potential-uk.html) in R for a tutorial showing how the package can provide cycling uptake estimates at the area level), and how to use the results to see which streets will have high potential. 23 | These results can help prioritise investment in new cycleways and other interventions. 24 | 25 | Set `eval=TRUE` to run this code when knitting: 26 | 27 | ```{r} 28 | knitr::opts_chunk$set(eval = FALSE) 29 | ``` 30 | 31 | # Prerequisites 32 | 33 | The following packages must be installed and loaded to run the code in this vignette. 34 | 35 | ```{r setup, message=FALSE, warning=FALSE} 36 | library(pct) 37 | library(dplyr) 38 | library(sf) 39 | library(tmap) 40 | ``` 41 | 42 | # Picking a study area 43 | 44 | ## Part A) Choosing a region 45 | 46 | Exploring study areas with PCT can be achieved by running `view(pct_regions)` in the R console. `pct_regions` are based on ‘regions’ from the geographic structure for England [(ONS)](https://www.ons.gov.uk/methodology/geography/ukgeographies/administrativegeography/england). Formerly known as Government offices for the regions or GOR’s (established in 1994), regions reflect a number of government departments who work with their local community to maximise prosperity and the quality of life within their area. Northern Ireland, Scotland and Wales were not subdivided into GOR’s but are listed with them for UK wide statistics. 47 | 48 | Based on data from the most recent census (2011), PCT provides spatial and travel data for all 45 regions across England and Wales. To begin with, this example will demonstrate the PCT method for the city of Cardiff which belongs to the region of Wales. 49 | 50 | ```{r} 51 | region_name = "wales" 52 | zones_all = get_pct_zones(region_name) 53 | ``` 54 | 55 | ## Part A) Picking a Local Authority 56 | 57 | Each region in England and Wales is composed of multiple local authorities. Among other things, each local authority takes the lead for local planning, housing and environmental health. 58 | 59 | How do you find all the local authorities part of your region? 60 | ```{r include=TRUE} 61 | unique(zones_all$lad_name) 62 | ``` 63 | 64 | Aha! As we can see Wales is composed of 42 local authorities. As we are looking at Cardiff in this example, we should declare that as our ```la_name```. 65 | ```{r} 66 | la_name = "Cardiff" 67 | ``` 68 | 69 | # Plotting Current Cycle Uptake 70 | 71 | Now we have chosen our region and city, we can easily plot the total number of cyclists based on the 2011 census. 72 | ```{r include=TRUE, fig.align='center', fig.width = 7, fig.height = 6} 73 | zones = zones_all %>% 74 | filter(lad_name == la_name) 75 | plot(zones["bicycle"]) 76 | ``` 77 | 78 | TaDa! The plot shows us that cycling is most prominent within the centre of Cardiff. While this is a first step in understanding where new cycle infrastructure is needed, it doesn't outline the road network where new demand is likely to occur. 79 | 80 | # Plotting New Demand Based On PCT Models 81 | 82 | PCT provides an easy way to get road network data for any ```pct_region```. The road network data can then be filtered to the zones within our local authority. 83 | ```{r} 84 | rnet_all = pct::get_pct_rnet(region_name) 85 | rnet = rnet_all[zones, ] 86 | ``` 87 | 88 | With our road network data fitted to our study area, we are now ready to plot where investment should be prioritised based on a demand model. PCT is built with numerous demand models based on varying situations (e.g. `govtarget_slc`,`gendereq_slc` and `ebike_slc`. In this example, we use the `go dutch` demand model; where demand is modelled on a cycle uptake equivalent to that in the Netherlands. 89 | 90 | ```{r include=TRUE, fig.align='center', fig.width = 7, fig.height = 6} 91 | plot(zones$geometry) 92 | plot(rnet["dutch_slc"], add = TRUE) 93 | ``` 94 | 95 | Voila! The blue lines represent the road network with colour proportional to estimated potential based on the go dutch model. This plot helps us understand where to prioritise cycle infrastructure and what a new demand may look like. 96 | 97 | # Comparing Two Cities 98 | 99 | Now we have seen the ease and versatility the PCT package provides, we can now use the method to explore more cities. Take Devon for example, the region has two major cities Exeter and Plymouth. Both cities obtain major universities, growing industries and have a relatively low cycle uptake. The latter should change, and the PCT package can provide the tools to help that. 100 | 101 | We can now wrap the method we used for Cardiff into a function in order to compare the differences between the two cities. 102 | 103 | ```{r} 104 | pct_zones_rnet = function(la_name, region_name = "devon") { 105 | zones_all = pct::get_pct_zones(region_name) 106 | zones = zones_all %>% 107 | filter(lad_name == la_name) 108 | plot(zones["bicycle"]) 109 | rnet_all = pct::get_pct_rnet(region_name) 110 | rnet = rnet_all[zones, ] 111 | plot(zones$geometry) 112 | plot(rnet["dutch_slc"], add = TRUE) 113 | list(zones = zones, rnet = rnet) 114 | } 115 | 116 | plymouth_results = pct_zones_rnet(la_name = "Plymouth") 117 | exeter_results = pct_zones_rnet(la_name = "Exeter") 118 | ``` 119 | 120 | 121 | Nice! The plots indicate a spatial spread for cycling in Plymouth, with investment necessary not only in the centre of the city. While for Exeter the distribution remains monocentric, with investment necessary mostly in the centre. 122 | 123 | We can create side-by-side interactive maps of the route network potential for of same cities for a more detailed comparison as follow: 124 | 125 | ```{r} 126 | tmap_mode("view") 127 | b = c(0, 100, 200, 500, 1000) 128 | m1 = tm_shape(plymouth_results$rnet) + 129 | tm_lines("dutch_slc", breaks = b, palette = "viridis", lwd = 2) + 130 | tm_scale_bar() 131 | m2 = tm_shape(exeter_results$rnet) + 132 | tm_lines("dutch_slc", breaks = b, palette = "viridis", lwd = 2) + 133 | tm_scale_bar() 134 | tmap_arrange(m1, m2, ncol = 2) 135 | ``` 136 | 137 | # Conclusions and next steps 138 | 139 | As demonstrated, the PCT package provides an easy way to understand current cycle statistics and potential cycle uptake in any UK city. The methods used in this article can be used for planning future cycle infrastructure and can be expanded on using the other methods part of the PCT package. If this article is of use/interest, why not try for your local region showing the potential based on different models. 140 | You can also try the following resources (in R and online): 141 | 142 | - Have a play with the [PCT web application](https://www.pct.bike/) 143 | - For more of a deep dive into the methods, see the [`pct_training`](https://itsleeds.github.io/pct/articles/pct_training.html) vignette 144 | - See the [`pct-international`](https://itsleeds.github.io/pct/articles/pct-international.html) to see how to apply the methods internationally 145 | 146 | 147 | We're interested to know how you've used the methods/data so please get in touch. --------------------------------------------------------------------------------