class: center, middle, inverse, title-slide # Using R to help plan the
future of transport ## Münster R Users Group ### Mark Padgham
Active Transport Futures ### Tuesday 20th November, 2018 --- class: left, middle, inverse .left-column[
atfutures
mpadge
ropensci ] .right-column[
bikesRdata
.small[mark.padgham@email.com]<br><br>
atfutures.github.io ] .box-bottom[ slides at <br> [https://github.com/mpadge/ms-user-meetup-nov2018](https://github.com/mpadge/ms-user-meetup-nov2018) ] --- class: left, top, inverse ### Primary Collaborators - Robin Lovelace (@robinlovelace) - Layik Hama (@layik) - Andreas Petutschnig (@karpfen) - Andrew Smith (@virgesmith) ### Additional
Contributions From ... .left-column[ - RichardEllison - tbuckl - skcott - hrbrmstr - maelle - mdsumner - mem48 - jimshady - angela-li ] .right-column[ - graceli8 - arfon - jimhester - tazinho - karthik - fzenoni - mhenderson - patperu - eyesofbambi ] --- class: left, top, inverse # Active Transport Futures --- background-image: url(img/amsterdam-cars.png) background-size: contain background-position: 50% 50% background-color: #000000 class: left, top, inverse # Active Transport Futures --- class: left, top, inverse background-image: url(img/ms-promenade.png) background-size: contain background-position: 50% 50% background-color: #000000 # Active Transport Futures --- class: left, top, inverse background-image: url(img/kl-bike-path.png) background-size: contain background-position: 50% 50% background-color: #000000 # [Active Transport Futures](http://www.who.int/sustainable-development/cities/en/) --- # Active Transport Futures For a given city: - Collate data on patterns of urban mobility - Collate data on transport networks <br> (road, pedestrian, public transport) - Analyse "flows" throughout the network(s) - Collate data on (spatial patterns of) air pollution - Analyse dynamic interactions between movement, air pollution, and health - Develop, compare, contrast scenarios for ameliorating negative effects while enhancing positive effects - [Health Economic Assessment Tool (HEAT)](https://heatwalkingcycling.org/#homepage) --- # Active Transport Futures For a given city: - ~~Collate data on patterns of urban mobility~~ - Collate data on transport networks <br> (road, pedestrian, public transport) - Analyse "flows" throughout the network(s) - ~~Collate data on (spatial patterns of) air pollution~~ - Analyse dynamic interactions between movement, air pollution~~, and health~~ - ~~Develop, compare, contrast scenarios for ameliorating negative effects while enhancing positive effects~~ - ~~[Health Economic Assessment Tool (HEAT)](https://heatwalkingcycling.org/#homepage)~~ --- # Active Transport Futures ## Software: R Packages - `osmdata` (Open Street Map data) - `dodgr` (Distances On Directed GRaphs) - `stplanr` (Sustainable Transport PLANning in R) - `osmplotr` (OSM plotting routines) - `geoplumber` (Turbo-charged web server for geo data) - `bikedata` (Data from public hire bicycles) - `spatialcluster` - ... plus more - demo of `geoplumber` at [`http://35.233.61.182`](http://35.233.61.182) --- # Active Transport Futures ## Software: R Packages - `osmdata` (Open Street Map data) - `dodgr` (Distances On Directed GRaphs) - ~~`stplanr` (Sustainable Transport PLANning in R)~~ - ~~`osmplotr` (OSM plotting routines)~~ - ~~`geoplumber` (Turbo-charged web server for geo data)~~ - ~~`bikedata` (Data from public hire bicycles)~~ - ~~`spatialcluster`~~ - ... plus more - demo of `geoplumber` at [`http://35.233.61.182`](http://35.233.61.182) --- class: left, top # The osmdata package OSM = Open Street Map - CRAN -
`ropensci/osmdata` - Several mentions on [OSM wiki](https://wiki.openstreetmap.org/wiki/OSM_Scientific_Tools) -- ## Why OSM data? --- class: center, top, inverse .fontBIG[ Popquiz: <br><br>What is the least mapped country on the planet? ] --- class: center, middle, inverse background-image: url(img/osm-north-korea.png) background-size: contain background-position: 50% 50% background-color: #000000 --- class: center, middle, inverse background-image: url(img/anju-bing.png) background-size: contain background-position: 50% 50% background-color: #000000 --- class: center, middle, inverse background-image: url(img/anju-google.png) background-size: contain background-position: 50% 50% background-color: #000000 --- class: center, middle, inverse background-image: url(img/anju-google2.png) background-size: contain background-position: 50% 50% background-color: #000000 --- class: center, middle, inverse background-image: url(img/anju-osm.png) background-size: contain background-position: 50% 50% background-color: #000000 --- # The osmdata package - CRAN -
`ropensci/osmdata` - Several mentions on [OSM wiki](https://wiki.openstreetmap.org/wiki/OSM_Scientific_Tools) ## Why OSM data? -- Because Open Street Map is the most comprehensive street atlas humanity has ever created. ## How? `osmdata` uses the `overpass` server to retrieve data --- # The osmdata package ```r library (osmdata) opq ("muenster de") ``` ``` ## $bbox ## [1] "51.8401448,7.4737853,52.0600251,7.7743634" ## ## $prefix ## [1] "[out:xml][timeout:25];\n(\n" ## ## $suffix ## [1] ");\n(._;>;);\nout body;" ## ## $features ## NULL ## ## attr(,"class") ## [1] "list" "overpass_query" ``` --- # The osmdata package ```r library (osmdata) opq ("48155 de") ``` ``` ## $bbox ## [1] "51.9163722,7.632622,51.9906058,7.7231164" ## ## $prefix ## [1] "[out:xml][timeout:25];\n(\n" ## ## $suffix ## [1] ");\n(._;>;);\nout body;" ## ## $features ## NULL ## ## attr(,"class") ## [1] "list" "overpass_query" ``` --- # The osmdata package ```r library (osmdata) opq ("48155 de") %>% add_osm_feature (key = "highway") ``` ``` ## $bbox ## [1] "51.9163722,7.632622,51.9906058,7.7231164" ## ## $prefix ## [1] "[out:xml][timeout:25];\n(\n" ## ## $suffix ## [1] ");\n(._;>;);\nout body;" ## ## $features ## [1] " [\"highway\"]" ## ## attr(,"class") ## [1] "list" "overpass_query" ``` --- # The osmdata package ```r library (osmdata) opq ("48155 de") %>% add_osm_feature (key = "highway") %>% osmdata_sf () ``` ``` ## Object of class 'osmdata' with: ## $bbox : 51.9163722,7.632622,51.9906058,7.7231164 ## $overpass_call : The call submitted to the overpass API ## $meta : metadata including timestamp and version numbers ## $osm_points : 'sf' Simple Features Collection with 32561 points ## $osm_lines : 'sf' Simple Features Collection with 7097 linestrings ## $osm_polygons : 'sf' Simple Features Collection with 113 polygons ## $osm_multilines : 'sf' Simple Features Collection with 0 multilinestrings ## $osm_multipolygons : 'sf' Simple Features Collection with 7 multipolygons ``` --- # The osmdata package ```r library (osmdata) opq ("48155 de") %>% add_osm_feature (key = "highway") %>% osmdata_sf () ``` - `opq`
.bold[WHERE] - `add_osm_feature`
.bold[WHAT] - `osmdata_sf`
.bold[HOW] --- # The osmdata package - Spatial Formats ```r library (osmdata) opq ("48155 de") %>% add_osm_feature (key = "highway") %>% osmdata_sf () ``` - `osmdata_sf`
.bold[Simple Features] --- # The osmdata package - Spatial Formats ```r library (osmdata) opq ("48155 de") %>% add_osm_feature (key = "highway") %>% osmdata_sf () ``` - `osmdata_sf`
.bold[Simple Features] - `osmdata_sp`
.bold[R Spatial] - `osmdata_sc`
.bold[Silicate (Simplicial Complex)] - `osmdata_xml`
.bold[XML / OSM] - `osmdata_pbf`
.bold[PBF (Protocol Buffer Format)] --- # The osmdata package ```r library (osmdata) ms48155 <- opq ("48155 de") %>% add_osm_feature (key = "highway") %>% osmdata_sf () ms48155 ``` ``` ## Object of class 'osmdata' with: ## $bbox : 51.9163722,7.632622,51.9906058,7.7231164 ## $overpass_call : The call submitted to the overpass API ## $meta : metadata including timestamp and version numbers ## $osm_points : 'sf' Simple Features Collection with 32561 points ## $osm_lines : 'sf' Simple Features Collection with 7097 linestrings ## $osm_polygons : 'sf' Simple Features Collection with 113 polygons ## $osm_multilines : 'sf' Simple Features Collection with 0 multilinestrings ## $osm_multipolygons : 'sf' Simple Features Collection with 7 multipolygons ``` --- # The osmdata package - Visualisation ```r library (osmdata) ms48155 <- opq ("48155 de") %>% add_osm_feature (key = "highway") %>% osmdata_sf () library (mapview) mapview (ms48155$osm_lines) ``` --- background-image: url(img/ms48155lines-mapview.png) background-size: contain background-position: 50% 50% class: center, top, inverse --- # The osmdata package - Visualisation How to visualise just the Promenade? ```r library (osmdata) *ms48155 <- opq ("48155 de") %>% add_osm_feature (key = "highway") %>% osmdata_sf () ``` --- # The osmdata package - Visualisation How to visualise just the Promenade? ```r library (osmdata) *promenade <- opq ("münster de") %>% add_osm_feature (key = "highway") %>% osmdata_sf () ``` --- # The osmdata package - Visualisation How to visualise just the Promenade? ```r library (osmdata) promenade <- opq ("münster de") %>% add_osm_feature (key = "highway") %>% * add_osm_feature (key = "name", value = "promenade", * value_exact = FALSE) %>% osmdata_sf () ``` --- # The osmdata package - Visualisation How to visualise just the Promenade? ```r library (osmdata) promenade <- opq ("münster de") %>% add_osm_feature (key = "highway") %>% add_osm_feature (key = "name", value = "promenade", value_exact = FALSE) %>% osmdata_sf () library (mapview) *mapview (promenade$osm_lines) ``` --- background-image: url(img/mspromenade-mapview.png) background-size: contain background-position: 50% 50% class: center, middle, inverse --- class: middle, inverse, center .fonthuge[ Digression:<br>Visualising OSM data ] --- # Visualising osmdata ```r library (mapview) mapview (ms48155$osm_lines) ``` --- background-image: url(img/ms48155lines-mapview.png) background-size: contain background-position: 50% 50% class: center, middle, inverse --- # Visualising osmdata ```r library (mapview) mapview (ms48155$osm_lines) %>% addFeatures (promenade$osm_lines) ``` ... currently will not work (coz of `leaflet`; it's not `mapview`'s fault) -- - but [`deck.gl`](https://deck.gl) will -- - and it's available in R via the `mapdeck` package brought to us by
`symbolixAU` --- # Visualising osmdata ```r promenade <- opq ("münster de") %>% add_osm_feature (key = "highway") %>% add_osm_feature (key = "name", value = "promenade", value_exact = FALSE) %>% osmdata_sf () ``` --- # Visualising osmdata A little bit of pre-processing #1: Because `mapdeck` is highly experimental and currently needs to know where to centre the map. ```r location <- ms48155$osm_lines %>% sf::st_coordinates () %>% apply (2, mean) %>% as.numeric () %>% magrittr::extract (1:2) location ``` ``` ## [1] 7.662115 51.954444 ``` --- # Visualising osmdata A little bit of pre-processing #2: Because `mapdeck` needs a personal mapbox token which can be set with `set_token()`, and automatically used to generated all maps ```r library (mapdeck) set_token (<my_mapbox_token>) ``` --- # Visualising osmdata ```r library (mapdeck) mapdeck (style = 'mapbox://styles/mapbox/dark-v9', location = location, zoom = 12) %>% add_path (data = ms48155$osm_lines, layer_id = "ms", stroke_width = 1, stroke_colour = "#4444dd", stroke_opacity = 80) %>% add_path (data = promenade$osm_lines, layer_id = "promenade", stroke_width = 5, stroke_colour = "#33cc33", stroke_opacity = 100) ``` --- class: center, middle, inverse background-image: url(img/ms-highway-promenade-mapview.png) background-size: contain background-position: 50% 50% --- # Visualising osmdata ```r system.time ( mapview (ms48155$osm_lines) ) ``` ``` ## user system elapsed ## 3.799 0.074 3.906 ``` ```r system.time ( mapdeck (style = 'mapbox://styles/mapbox/dark-v9', location = loc, zoom = 12) %>% add_path (data = ms48155$osm_lines, layer_id = "ms", stroke_width = 1, stroke_colour = "#4444dd", stroke_opacity = 80) ) ``` ``` ## user system elapsed ## 0.222 0.000 0.237 ``` --- # Visualising osmdata One further alternative for static plots is the [`osmplotr` package](https://github.com/ropensci/osmplotr) --- class: top, center, inverse .fontBIG[ Question:<br><br> Has anybody <br>used `osmdata`? ] --- class: top, center, inverse .fontBIG[ Question:<br><br> What kind of default plot method might <br>be useful? ] --- # osmdata - The fastest and easiest method in any language for extracting OSM data - Only software that allows OSM data to be extracted in 3 simple lines: ```r ms <- opq ("münster de") %>% # WHERE add_osm_feature (key = "highway") %>% # WHAT osmdata_sf (quiet = FALSE) # HOW ``` - Extremely flexible; can extract any form of OSM data. ```r ms <- opq ("münster de") %>% add_osm_feature (key = "building") %>% osmdata_sf (quiet = FALSE) ``` - For more information, see [the website](https://github.com/ropensci/osmdata) --- class: top, center, inverse .fonthuge[ **dodgr**<br> ] .fontBIG[ Distances on Directed GRaphs ] --- class: top, center, inverse .fonthuge[ **dodgr**<br> ] * CRAN <br><br> *
[`ATFutures/dodgr`](https://github.com/atfutures/dodgr) --- # dodgr Get all OSM highway data for Münster (not just 48155) ```r ms <- opq ("münster de") %>% add_osm_feature (key = "highway") %>% osmdata_sf () ms ``` ``` ## Object of class 'osmdata' with: ## $bbox : 51.8401448,7.4737853,52.0600251,7.7743634 ## $overpass_call : The call submitted to the overpass API ## $meta : metadata including timestamp and version numbers ## $osm_points : 'sf' Simple Features Collection with 157614 points ## $osm_lines : 'sf' Simple Features Collection with 33545 linestrings ## $osm_polygons : 'sf' Simple Features Collection with 510 polygons ## $osm_multilines : 'sf' Simple Features Collection with 0 multilinestrings ## $osm_multipolygons : 'sf' Simple Features Collection with 18 multipolygons ``` --- # dodgr Convert Münster OSM data to `dodgr` format ```r ms <- osm_poly2line (ms) # osmdata function ``` --- # dodgr Convert Münster OSM data to `dodgr` format ```r ms <- osm_poly2line (ms) # osmdata function library (dodgr) *net <- weight_streetnet (ms$osm_lines, wt_profile = "bicycle") dim (net); head (net) ``` ``` ## [1] 319023 13 ``` ``` ## geom_num edge_id from_id from_lon from_lat to_id to_lon ## 1 1 1 277727445 7.601811 51.93496 471016556 7.601935 ## 2 1 2 471016556 7.601935 51.93507 277727511 7.602027 ## 3 2 3 180445033 7.624554 51.95561 1396624726 7.624838 ## 4 2 4 1396624726 7.624838 51.95562 180445033 7.624554 ## 5 2 5 1396624726 7.624838 51.95562 4188292608 7.624961 ## 6 2 6 4188292608 7.624961 51.95563 1396624726 7.624838 ## to_lat d d_weighted highway way_id component ## 1 51.93507 0.014886139 0.02126591 primary 4064461 1 ## 2 51.93515 0.011135871 0.01590839 primary 4064461 1 ## 3 51.95562 0.019525720 0.02789389 primary 4064462 1 ## 4 51.95561 0.019525720 0.02789389 primary 4064462 1 ## 5 51.95563 0.008436115 0.01205159 primary 4064462 1 ## 6 51.95562 0.008436115 0.01205159 primary 4064462 1 ``` --- # dodgr ```r dim (net) ``` ``` ## [1] 319023 13 ``` ```r head (table (net$component), 10) ``` ``` ## ## 1 2 3 4 5 6 7 8 9 10 ## 313981 398 220 220 134 130 120 106 102 100 ``` --- # dodgr distances Calculate cycling distances between random points. ```r v <- dodgr_vertices (net) dim (v); head (v) ``` ``` ## [1] 152545 5 ``` ``` ## id x y component n ## 1 277727445 7.601811 51.93496 1 0 ## 2 471016556 7.601935 51.93507 1 1 ## 3 180445033 7.624554 51.95561 1 2 ## 4 1396624726 7.624838 51.95562 1 3 ## 6 4188292608 7.624961 51.95563 1 4 ``` ```r pts <- sample (v$id, size = 1000) head (pts) ``` ``` ## [1] "2901833965" "5342793850" "3526245183" "252685059" "2668848833" ## [6] "1645591348" ``` --- # dodgr distances ```r ?dodgr_dists ``` ``` ## _d_o_d_g_r__d_i_s_t_s ## ## _D_e_s_c_r_i_p_t_i_o_n: ## ## Calculate matrix of pair-wise distances between points. ## ## _U_s_a_g_e: ## ## dodgr_dists(graph, from, to, wt_profile = "bicycle", expand = 0, ## heap = "BHeap", parallel = TRUE, quiet = TRUE) ## ## _A_r_g_u_m_e_n_t_s: ## ## graph: 'data.frame' or equivalent object representing the network ## graph (see Details) ## ## from: Vector or matrix of points **from** which route distances are ## to be calculated (see Details) ## ## to: Vector or matrix of points **to** which route distances are ## to be calculated (see Details) ## ## wt_profile: Name of weighting profile for street networks (one of foot, ## horse, wheelchair, bicycle, moped, motorcycle, motorcar, ## goods, hgv, psv). ## ## expand: Only when 'graph' not given, the multiplicative factor by ## which to expand the street network surrounding the points ## defined by 'from' and/or 'to'. ## ## heap: Type of heap to use in priority queue. Options include ## Fibonacci Heap (default; 'FHeap'), Binary Heap ('BHeap'), ## 'Radix', Trinomial Heap ('TriHeap'), Extended Trinomial Heap ## ('TriHeapExt', and 2-3 Heap ('Heap23'). ## ## parallel: If 'TRUE', perform routing calculation in parallel (see ## details) ## ## quiet: If 'FALSE', display progress messages on screen. ## ## _V_a_l_u_e: ## ## square matrix of distances between nodes ## ## _N_o_t_e: ## ## 'graph' must minimally contain three columns of 'from', 'to', ## 'dist'. If an additional column named 'weight' or 'wt' is present, ## shortest paths are calculated according to values specified in ## that column; otherwise according to 'dist' values. Either way, ## final distances between 'from' and 'to' points are calculated ## according to values of 'dist'. That is, paths between any pair of ## points will be calculated according to the minimal total sum of ## 'weight' values (if present), while reported distances will be ## total sums of 'dist' values. ## ## The 'from' and 'to' columns of 'graph' may be either single ## columns of numeric or character values specifying the numbers or ## names of graph vertices, or combinations to two columns specifying ## geographical (longitude and latitude) coordinates. In the latter ## case, almost any sensible combination of names will be accepted ## (for example, 'fromx, fromy', 'from_x, from_y', or 'fr_lat, ## fr_lon'.) ## ## 'from' and 'to' values can be either two-column matrices of ## equivalent of longitude and latitude coordinates, or else single ## columns precisely matching node numbers or names given in ## 'graph$from' or 'graph$to'. If 'to' is missing, pairwise distances ## are calculated between all points specified in 'from'. If neither ## 'from' nor 'to' are specified, pairwise distances are calculated ## between all nodes in 'graph'. ## ## Calculations in parallel ('parallel = TRUE') ought very generally ## be advantageous. For small graphs, Calculating distances in ## parallel is likely to offer relatively little gain in speed, but ## increases from parallel computation will generally markedly ## increase with increasing graph sizes. ## ## _E_x_a_m_p_l_e_s: ## ## # A simple graph ## graph <- data.frame (from = c ("A", "B", "B", "B", "C", "C", "D", "D"), ## to = c ("B", "A", "C", "D", "B", "D", "C", "A"), ## d = c (1, 2, 1, 3, 2, 1, 2, 1)) ## dodgr_dists (graph) ## ## # A larger example from the included \code{\link{hampi}} data. ## graph <- weight_streetnet (hampi) ## from <- sample (graph$from_id, size = 100) ## to <- sample (graph$to_id, size = 50) ## d <- dodgr_dists (graph, from = from, to = to) ## # d is a 100-by-50 matrix of distances between \code{from} and \code{to} ## ## ## Not run: ## ## # a more complex street network example, thanks to @chrijo; see ## # https://github.com/ATFutures/dodgr/issues/47 ## ## xy <- rbind (c (7.005994, 51.45774), # limbeckerplatz 1 essen germany ## c (7.012874, 51.45041)) # hauptbahnhof essen germany ## xy <- data.frame (lon = xy [, 1], lat = xy [, 2]) ## essen <- dodgr_streetnet (pts = xy, expand = 0.2, quiet = FALSE) ## graph <- weight_streetnet (essen, wt_profile = "foot") ## d <- dodgr_dists (graph, from = xy, to = xy) ## # First reason why this does not work is because the graph has multiple, ## # disconnected components. ## table (graph$component) ## # reduce to largest connected component, which is always number 1 ## graph <- graph [which (graph$component == 1), ] ## d <- dodgr_dists (graph, from = xy, to = xy) ## # should work, but even then note that ## table (essen$level) ## # There are parts of the network on different building levels (because of ## # shopping malls and the like). These may or may not be connected, so it may be ## # necessary to filter out particular levels ## levs <- paste0 (essen$level) # because sf data are factors ## index <- which (! (levs == "-1" | levs == "1")) # for example ## library (sf) # needed for following sub-select operation ## essen <- essen [index, ] ## graph <- weight_streetnet (essen, wt_profile = "foot") ## d <- dodgr_dists (graph, from = xy, to = xy) ## ## End(Not run) ## ``` --- # dodgr distances Calculate cycling distances between random points. ```r v <- dodgr_vertices (net) dim (v); head (v) ``` ``` ## [1] 152545 5 ``` ``` ## id x y component n ## 1 277727445 7.601811 51.93496 1 0 ## 2 471016556 7.601935 51.93507 1 1 ## 3 180445033 7.624554 51.95561 1 2 ## 4 1396624726 7.624838 51.95562 1 3 ## 6 4188292608 7.624961 51.95563 1 4 ``` ```r pts <- sample (v$id, size = 1000) head (pts) ``` ``` ## [1] "2340899103" "448433851" "1195913359" "4049285256" "4680961682" ## [6] "3934100817" ``` --- # dodgr distances ```r system.time ( * d <- dodgr_dists (net, from = pts, to = pts) ) ``` ``` ## user system elapsed ## 43.847 0.013 6.042 ``` -- ```r class (d); dim (d); range (d); range (d, na.rm = TRUE) ``` ``` ## [1] matrix ## [1] 1000 1000 ## [1] NA NA ## [1] 0.00000 35.50599 ``` ```r round (1000 ^ 2 * length (which (is.na (d))) / length (d)) ``` ``` ## [1] 32709 ``` --- # dodgr distances ```r system.time ( * d <- dodgr_dists (net, from = pts, to = pts) ) ``` ``` ## user system elapsed ## 43.847 0.013 6.042 ``` - Those 1,000,000 distances could have been calculated with the google API - And that would cost `1e6 * 0.004 = $US4,000` - $US 4,000 for 6 seconds of calculation! - To date, `dodgr` provides one of the only .bold[free] ways to perform such calculations, and is also entirely .bold[open source]. --- class: left, top, inverse background-image: url(img/ms-promenade.png) background-size: contain background-position: 50% 50% background-color: #000000 --- background-image: url(img/fortune-beach.png) --- class: inverse, center, top .fonthuge[ ~~distances~~<br><br> flows ] --- # flows If one person travels between each of our 1,000,000 pairs of points, how many travel on each bit of the network? ```r d <- dodgr_dists (net, from = pts, to = pts) ``` ```r flow_mat <- matrix (1, nrow = 1000, ncol = 1000) system.time ( * f <- dodgr_flows_aggregate (net, from = pts, to = pts, * flows = flow_mat) ) ``` ``` ## user system elapsed ## 145.186 1.107 20.016 ``` --- # flows ```r head (net) ``` ``` ## geom_num edge_id from_id from_lon from_lat to_id to_lon ## 1 1 1 277727445 7.601811 51.93496 471016556 7.601935 ## 2 1 2 471016556 7.601935 51.93507 277727511 7.602027 ## 3 2 3 180445033 7.624554 51.95561 1396624726 7.624838 ## 4 2 4 1396624726 7.624838 51.95562 180445033 7.624554 ## 5 2 5 1396624726 7.624838 51.95562 4188292608 7.624961 ## 6 2 6 4188292608 7.624961 51.95563 1396624726 7.624838 ## to_lat d d_weighted highway way_id component ## 1 51.93507 0.014886139 0.02126591 primary 4064461 1 ## 2 51.93515 0.011135871 0.01590839 primary 4064461 1 ## 3 51.95562 0.019525720 0.02789389 primary 4064462 1 ## 4 51.95561 0.019525720 0.02789389 primary 4064462 1 ## 5 51.95563 0.008436115 0.01205159 primary 4064462 1 ## 6 51.95562 0.008436115 0.01205159 primary 4064462 1 ``` --- # flows ```r head (f) ``` ``` ## geom_num edge_id from_id from_lon from_lat to_id to_lon ## 1 1 1 277727445 7.601811 51.93496 471016556 7.601935 ## 2 1 2 471016556 7.601935 51.93507 277727511 7.602027 ## 3 2 3 180445033 7.624554 51.95561 1396624726 7.624838 ## 4 2 4 1396624726 7.624838 51.95562 180445033 7.624554 ## 5 2 5 1396624726 7.624838 51.95562 4188292608 7.624961 ## 6 2 6 4188292608 7.624961 51.95563 1396624726 7.624838 ## to_lat d d_weighted highway way_id component flow ## 1 51.93507 0.014886139 0.02126591 primary 4064461 1 0 ## 2 51.93515 0.011135871 0.01590839 primary 4064461 1 0 ## 3 51.95562 0.019525720 0.02789389 primary 4064462 1 0 ## 4 51.95561 0.019525720 0.02789389 primary 4064462 1 0 ## 5 51.95563 0.008436115 0.01205159 primary 4064462 1 0 ## 6 51.95562 0.008436115 0.01205159 primary 4064462 1 0 ``` --- # flows ```r fm <- merge_directed_flows (f) ``` - Adds flows .bold[A]
.bold[B] to .bold[B]
.bold[A] - Removes one of the two edges - Also removes all edges with zero flow -- ```r dim (f); dim (fm) ``` ``` ## [1] 319023 14 ``` ``` ## [1] 73718 14 ``` ```r nrow (fm) / nrow (f) ``` ``` ## [1] 0.2310742 ``` --- # flows ```r head (fm) ``` ``` ## geom_num edge_id from_id from_lon from_lat to_id to_lon ## 11 3 11 21518849 7.626498 51.95617 1396347948 7.626437 ## 13 3 13 1396347948 7.626437 51.95617 4188292644 7.626377 ## 15 3 15 4188292644 7.626377 51.95617 21518860 7.626318 ## 17 3 17 21518860 7.626318 51.95616 4188292640 7.626260 ## 19 3 19 4188292640 7.626260 51.95615 1396347945 7.626205 ## 21 3 21 1396347945 7.626205 51.95614 4188292637 7.626152 ## to_lat d d_weighted highway way_id component flow ## 11 51.95617 0.004143707 0.005919582 primary 4064463 1 91 ## 13 51.95617 0.004143515 0.005919306 primary 4064463 1 91 ## 15 51.95616 0.004143953 0.005919933 primary 4064463 1 91 ## 17 51.95615 0.004140341 0.005914774 primary 4064463 1 91 ## 19 51.95614 0.004146739 0.005923913 primary 4064463 1 91 ## 21 51.95612 0.004137829 0.005911184 primary 4064463 1 91 ``` --- # flows ```r library (ggplot2) ggplot (fm, aes (flow)) + geom_freqpoly () ``` <img src="ms-meetup-nov18_files/figure-html/flowplot-1.png" width="500px" height="400px" /> --- class: center, inverse .fonthuge[ mapdeck <br> time!<br>
] --- # flows ```r library (mapdeck) f$flow <- 20 * f$flow / max (f$flow) pal <- colorRampPalette (c ("lawngreen", "red")) loc <- apply (sf::st_coordinates (ms$osm_lines), 2, mean) [1:2] mapdeck (style = 'mapbox://styles/mapbox/dark-v9', zoom = 12, location = loc) %>% add_line (data = fm, layer_id = "ms-highways", origin = c("from_lon", "from_lat"), destination = c("to_lon", "to_lat"), stroke_colour = "flow", stroke_width = "flow", palette = pal) ``` --- background-image: url(img/ms-random-flows.png) background-size: contain background-position: 50% 50% background-color: #000000 class: center, middle, inverse --- background-image: url(img/ms-promenade100.png) background-size: contain background-position: 50% 50% background-color: #000000 class: center, middle, inverse --- class: center, inverse .fontBIG[ The Promenade<br>IS<br>cycling in Münster,<br>but there are no flows along most of it! ] --- # How to get people riding along the Promenade? - We first need to find the edges of our `dodgr net` which correspond to the Promenade. - Get OSM way IDs (making sure to exclude the "Kanalpromenade") ```r index <- grep ("promenade", ms$osm_lines$name, ignore.case = TRUE) index_kp <- grep ("kanal", ms$osm_lines$name, ignore.case = TRUE) index <- index [!index %in% index_kp] ids_prom <- ms$osm_lines$osm_id [index] %>% as.character () ``` --- # How to get people riding along the Promenade? - Presume the Promenade "feels" 10% shorter than what it actually is ```r shorter <- 0.1 g <- weight_streetnet (ms$osm_lines, wt_profile = "bicycle") index <- which (g$way_id %in% ids_prom) *g$d_weighted [index] <- g$d [index] * (1 - shorter) f <- dodgr_flows_aggregate (g, from = pts, to = pts, flows = flowmat) ``` --- # How to get people riding along the Promenade? ```r head (net) ``` ``` ## geom_num edge_id from_id from_lon from_lat to_id to_lon ## 1 1 1 277727445 7.601811 51.93496 471016556 7.601935 ## 2 1 2 471016556 7.601935 51.93507 277727511 7.602027 ## 3 2 3 180445033 7.624554 51.95561 1396624726 7.624838 ## 4 2 4 1396624726 7.624838 51.95562 180445033 7.624554 ## 5 2 5 1396624726 7.624838 51.95562 4188292608 7.624961 ## 6 2 6 4188292608 7.624961 51.95563 1396624726 7.624838 ## to_lat d d_weighted highway way_id component ## 1 51.93507 0.014886139 0.02126591 primary 4064461 1 ## 2 51.93515 0.011135871 0.01590839 primary 4064461 1 ## 3 51.95562 0.019525720 0.02789389 primary 4064462 1 ## 4 51.95561 0.019525720 0.02789389 primary 4064462 1 ## 5 51.95563 0.008436115 0.01205159 primary 4064462 1 ## 6 51.95562 0.008436115 0.01205159 primary 4064462 1 ``` --- # How to get people riding along the Promenade? - Presume the Promenade "feels" 10% shorter than what it actually is ```r shorter <- 0.1 g <- weight_streetnet (ms$osm_lines, wt_profile = "bicycle") index <- which (g$way_id %in% ids_prom) g$d_weighted [index] <- g$d [index] * (1 - shorter) f <- dodgr_flows_aggregate (g, from = pts, to = pts, flows = flowmat) ``` --- background-image: url(img/ms-promenade90.png) background-size: contain background-position: 50% 50% background-color: #000000 class: center, middle, inverse --- # How to get people riding along the Promenade? Presume the Promenade "feels" (0, 10, 20, & 30)% shorter than what it actually is ```r g <- weight_streetnet (ms$osm_lines, wt_profile = "bicycle") index <- which (g$way_id %in% ids) g$d_weighted [index] <- g$d [index] * (1 - shorter) f <- dodgr_flows_aggregate (g, from = pts, to = pts, flows = flowmat) ``` --- background-image: url(img/ms-promenade100.png) background-size: contain background-position: 50% 50% background-color: #000000 class: center, middle, inverse --- background-image: url(img/ms-promenade90.png) background-size: contain background-position: 50% 50% background-color: #000000 class: center, middle, inverse --- background-image: url(img/ms-promenade80.png) background-size: contain background-position: 50% 50% background-color: #000000 class: center, middle, inverse --- background-image: url(img/ms-promenade70.png) background-size: contain background-position: 50% 50% background-color: #000000 class: center, middle, inverse --- class: center, top, inverse .fontBIG[ The Promenade feels like it is 30% shorter than what it actually is ] --- class: center, top, inverse .fontBIG[ Münsteraner_Innen are willing to ride 30% further to ride along the Promenade ] --- class: center, top, inverse .fontBIG[ Important Lesson: Cultural specificities are important! ] -- .fontBIG[ And can be incorporated! ] --- class: center, top, inverse .fontBIG[ Popquiz: <br><br>Where in Münster are cyclists most exposed to air pollution? ] --- background-image: url(img/ms-bike-flows.png) background-size: contain background-position: 50% 50% background-color: #000000 class: center, middle, inverse --- background-image: url(img/fortune-beach.png) --- # Where is Münster are cyclists most exposed to air pollution? - Air pollution comes from automotive vehicles, so need to know where they travel - Generate another flow layer -- - Implement model to simulate dispersal to adjacent ways including bike paths --- background-image: url(img/ms-car-flows.png) background-size: contain background-position: 50% 50% background-color: #000000 class: center, middle, inverse --- background-image: url(img/ms-car-flows-dispersed.png) background-size: contain background-position: 50% 50% background-color: #000000 class: center, middle, inverse --- background-image: url(img/ms-promenade70.png) background-size: contain background-position: 50% 50% background-color: #000000 class: center, middle, inverse --- # Where is Münster are cyclists most exposed to air pollution? - Air pollution comes from automotive vehicles, so need to know where they travel - Generate another flow layer - Implement model to simulate dispersal to adjacent ways including bike paths - Quantify exposure as cyclist flows times pollutant concentrations --- background-image: url(img/ms-exposure.png) background-size: contain background-position: 50% 50% background-color: #000000 class: center, middle, inverse --- background-image: url(img/ms-car-flows-dispersed.png) background-size: contain background-position: 50% 50% background-color: #000000 class: center, middle, inverse --- background-image: url(img/ms-promenade70.png) background-size: contain background-position: 50% 50% background-color: #000000 class: center, middle, inverse --- background-image: url(img/ms-exposure.png) background-size: contain background-position: 50% 50% background-color: #000000 class: center, middle, inverse -- .fonthuge[THANK<br>YOU!] .box-bottom[May the flow be with you] --- class: center, middle, inverse background-image: url(img/ms-exposure.png) background-size: contain background-position: 50% 50% background-color: #000000 .left-column[
atfutures
mpadge
ropensci ] .right-column[
bikesRdata
.small[mark.padgham@email.com]<br><br>
atfutures.github.io ] .box-bottom[ slides at <br> [https://github.com/mpadge/ms-user-meetup-nov2018](https://github.com/mpadge/ms-user-meetup-nov2018) ]