Skip to content

Commit 2321511

Browse files
committed
Update results
1 parent 7347205 commit 2321511

File tree

8 files changed

+147
-32
lines changed

8 files changed

+147
-32
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ ISPRSguidelines_authors*
88
*.log
99
*.gz
1010
*.xlsx
11+
*.Rds

ISPRStemplate.tex

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@
7070
$highlighting-macros$
7171
$endif$
7272

73+
\usepackage{booktabs} % To thicken table lines
74+
7375
\begin{document}
7476

7577
\title{GUIDELINES FOR AUTHORS PREPARING A FULL PAPER TO BE SUBMITTED TO ISPRS EVENTS}
@@ -317,8 +319,8 @@
317319
% \subsubsection{References from Software Repository:}
318320
% References from software repositories should be cited like~\cite{gago2016}.
319321

320-
\section*{ACKNOWLEDGEMENTS (Optional)}\label{ACKNOWLEDGEMENTS}
321-
Acknowledgements of support for the project/paper/author are welcome if they do not violate the anonymity of the submitted paper. Otherwise, they should be masked.
322+
\section*{ACKNOWLEDGEMENTS}\label{ACKNOWLEDGEMENTS}
323+
We thank Lisbon Municipal Government and Transport Infrastructure Ireland for funding this research.
322324

323325
% {
324326
% \begin{spacing}{1.17}

README.Rmd

Lines changed: 106 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ editor_options:
1515
unzip("ISPRSguidelines_authors_fullpaper_latex_2021_09_09.zip")
1616
tinytex::pdflatex("ISPRSguidelines_authors_fullpaper.tex")
1717
rmarkdown::render("README.Rmd")
18-
browseURL("README.pdf")
18+
file.rename("README.pdf", "foss4g-paper-jittering.pdf")
19+
browseURL("foss4g-paper-jittering.pdf")
20+
piggyback::pb_upload("foss4g-paper-jittering.pdf")
1921
```
2022

2123
<!-- README.md is generated from README.Rmd. Please edit that file -->
@@ -37,17 +39,17 @@ knitr::opts_chunk$set(
3739
# devtools::install_github("itsleeds/od")
3840
library(sf)
3941
library(tmap)
40-
library(dplyr)
42+
library(tidyverse)
4143
library(stplanr)
44+
library(cyclestreets)
4245
# rbbt::bbt_update_bib(path_rmd = "README.Rmd", path_bib = "foss4g2022.bib")
4346
```
4447

4548
# Introduction
4649

4750
Origin-destination (OD) datasets provide information on aggregate travel patterns between zones and geographic entities.
4851
OD datasets are 'implicitly geographic', containing identification codes of the geographic objects from which trips start and end.
49-
A common approach to converting OD datasets to geographic entities, for example represented using the simple features standard [@ogcopengeospatialconsortiuminc_opengis_2011] and saved in file formats such as GeoPackage and GeoJSON, is to represent each OD record as a straight line between zone centroids.
50-
This approach to representing OD datasets on the map has been since at least the 1950s [@boyce_forecasting_2015] and is still in use today [e.g. @rae_spatial_2009].
52+
A common approach to converting OD datasets to geographic entities, for example represented using the simple features standard [@ogcopengeospatialconsortiuminc_opengis_2011] and saved in file formats such as GeoPackage and GeoJSON, is to represent each OD record as a straight line between zone centroids. This approach to representing OD datasets on the map has been since at least the 1950s [@boyce_forecasting_2015] and is still in use today [e.g. @rae_spatial_2009].
5153

5254
Beyond simply visualising aggregate travel patterns, centroid-based geographic desire lines are also used as the basis of many transport modelling processes.
5355
The following steps can be used to convert OD datasets into route networks, in a process that can generate nationally scalable results [@morgan_travel_2020]:
@@ -59,7 +61,7 @@ The following steps can be used to convert OD datasets into route networks, in a
5961
- Aggregation of routes into route networks, with values on each segment representing the total amount of travel ('flow') on that part of the network, using functions such as `overline()` in the open source R package `stplanr` [@lovelace_stplanr_2018]
6062

6163
This approach is tried and tested.
62-
The OD -> desire line -> route -> route network processing pipeline forms the basis of the route network results in the Propensity to Cycle Tool, an open source and publicly available map-based web application for informing strategic cycle network investment, 'visioning' and prioritisation [@lovelace_propensity_2017; @goodman_scenarios_2019].
64+
The OD -\> desire line -\> route -\> route network processing pipeline forms the basis of the route network results in the Propensity to Cycle Tool, an open source and publicly available map-based web application for informing strategic cycle network investment, 'visioning' and prioritisation [@lovelace_propensity_2017; @goodman_scenarios_2019].
6365
However, the approach has some key limitations:
6466

6567
- Flows are concentrated on transport network segments leading to zone centroids, creating distortions in the results and preventing the simulation of the diffuse networks that are particularly important for walking and cycling
@@ -100,60 +102,144 @@ This approach has been implemented efficiently in the Rust crate `odjitter`, the
100102

101103
Jittering leads to more spatially diffuse representations of OD datasets than the common approach to desire lines that go from and to zone centroids.
102104
We have used the approach to add value to numerous OD datasets for projects based in Ireland, Norway, Portugal, New Zealand and beyond.
103-
Figure \@ref(fig:lisbon1) shows the difference between desire lines with centroids approach and the jittering approach.
105+
Figure \@ref(fig:lisbon1) shows the difference between desire lines with centroids approach and the jittering approach.
104106

105107
```{r lisbon1, include=FALSE, cache=TRUE, fig.cap="Illustration of jittered (left) compared with unjittered (right) origin-destination data."}
106108
od_all = readRDS(url("https://github.com/U-Shift/biclar/releases/download/0.0.1/TRIPSmode_freguesias.Rds"))
107109
zones = readRDS(url("https://github.com/U-Shift/biclar/releases/download/0.0.1/FREGUESIASgeo.Rds"))
108110
osm_data_region = readRDS(url("https://github.com/U-Shift/biclar/releases/download/0.0.1/osm_data_region.Rds"))
109111
```
110112

111-
112113
```{r include=FALSE, cache=TRUE}
113114
## For Lisbon only
114115
lisbon_zones = zones %>% filter(Concelho == "Lisboa")
115116
od_lisbon = od_all %>%
116117
filter(DICOFREor11 %in% lisbon_zones$Dicofre & DICOFREde11 %in% lisbon_zones$Dicofre)
117-
118-
od_lisbon_sf = od::od_to_sf(od_lisbon, lisbon_zones) #desire lines
118+
od_lisbon_with_bikes = od_lisbon %>% filter(Bike > 0)
119+
od_lisbon_sf = od::od_to_sf(od_lisbon_with_bikes, lisbon_zones) #desire lines
119120
120121
set.seed(42)
121122
od_lisbon_jittered = odjitter::jitter( #jitter
122-
od = od_lisbon,
123+
od = od_lisbon_with_bikes,
124+
zones = lisbon_zones,
125+
subpoints = osm_data_region,
126+
disaggregation_key = "Total",
127+
disaggregation_threshold = round(max(od_lisbon_with_bikes$Total) + 1) ##30? 50? 100?
128+
)
129+
od_lisbon_jittered_500 = odjitter::jitter( #jitter
130+
od = od_lisbon_with_bikes,
123131
zones = lisbon_zones,
124132
subpoints = osm_data_region,
125133
disaggregation_key = "Total",
126-
disaggregation_threshold = 100 ##30? 50? 100?
134+
disaggregation_threshold = 500 ##30? 50? 100?
127135
)
128-
nrow(od_lisbon_jittered) # 9042 (17784 with 100 disagreg_thr)
136+
# nrow(od_lisbon_jittered) # 9042 (17784 with 100 disagreg_thr)
129137
```
130138

131139
```{r}
132-
counters = readxl::read_excel("DadosAbertos_IST_CML_ContagensCiclistas_20172021.xlsx", sheet = "Maio2017")
140+
counters = readxl::read_excel("DadosAbertos_IST_CML_ContagensCiclistas_20172021.xlsx", sheet = "Out2021")
133141
counters_sf = counters %>%
134-
sf::st_as_sf(coords = c("lon", "lat"))
142+
filter(TurnoNor %in% c("M1", "M2", "T1", "T2")) %>%
143+
group_by(Ponto) %>%
144+
summarise(SumCiclistas = sum(SumCiclistas, na.rm = TRUE), lon = mean(lon), lat = mean(lat)) %>%
145+
select(-Ponto) %>%
146+
sf::st_as_sf(coords = c("lon", "lat"), crs = 4326)
135147
```
136148

137-
138149
```{r jitteredoverview, echo=FALSE, fig.cap="\\label{poltlisbon}Trips represented with desire lines from centroids and with jittering, for Lisbon (Portugal)", fig.show='hold', fig.ncol=2, out.width="50%"}
139150
plot(od_lisbon_sf$geometry, lwd = 0.2)
140151
plot(counters_sf, col = "red", add = TRUE)
141152
plot(od_lisbon_jittered$geometry, lwd = 0.1)
142153
plot(counters_sf, col = "red", add = TRUE)
143154
```
144155

156+
```{r, eval=FALSE, echo=FALSE}
157+
# Routing unjittered:
158+
routes_unjittered_quietest = route(l = od_lisbon_sf , route_fun = journey, plan = "quietest")
159+
write_rds(routes_unjittered_quietest, "routes_unjittered_quietest.Rds")
160+
routes_unjittered_balanced = route(l = od_lisbon_sf , route_fun = journey, plan = "balanced")
161+
write_rds(routes_unjittered_balanced, "routes_unjittered_balanced.Rds")
162+
routes_unjittered_fastest = route(l = od_lisbon_sf , route_fun = journey, plan = "fastest")
163+
write_rds(routes_unjittered_fastest, "routes_unjittered_fastest.Rds")
164+
# Routing unjittered:
165+
routes_jittered_quietest = route(l = od_lisbon_jittered , route_fun = journey, plan = "quietest")
166+
write_rds(routes_jittered_quietest, "routes_jittered_quietest.Rds")
167+
routes_jittered_balanced = route(l = od_lisbon_jittered , route_fun = journey, plan = "balanced")
168+
write_rds(routes_jittered_balanced, "routes_jittered_balanced.Rds")
169+
routes_jittered_fastest = route(l = od_lisbon_jittered , route_fun = journey, plan = "fastest")
170+
write_rds(routes_jittered_fastest, "routes_jittered_fastest.Rds")
171+
172+
```
173+
174+
```{r}
175+
routes_unjittered_quietest = readRDS("routes_unjittered_quietest.Rds")
176+
routes_unjittered_balanced = readRDS("routes_unjittered_balanced.Rds")
177+
routes_unjittered_fastest = readRDS("routes_unjittered_fastest.Rds")
178+
routes_jittered_quietest = readRDS("routes_jittered_quietest.Rds")
179+
routes_jittered_balanced = readRDS("routes_jittered_balanced.Rds")
180+
routes_jittered_fastest = readRDS("routes_jittered_fastest.Rds")
181+
rnet_unjittered_quietest = overline(routes_unjittered_quietest, attrib = "Bike")
182+
rnet_unjittered_balanced = overline(routes_unjittered_balanced, attrib = "Bike")
183+
rnet_unjittered_fastest = overline(routes_unjittered_fastest, attrib = "Bike")
184+
rnet_jittered_quietest = overline(routes_jittered_quietest, attrib = "Bike")
185+
rnet_jittered_balanced = overline(routes_jittered_balanced, attrib = "Bike")
186+
rnet_jittered_fastest = overline(routes_jittered_fastest, attrib = "Bike")
187+
```
188+
189+
```{r, echo=FALSE}
190+
# rnet_quiet = readRDS(url("https://github.com/U-Shift/biclar/releases/download/0.0.1/rnet_enmac_region_quietest_top_20000.Rds"))
191+
192+
counters_sf_joined = st_join(counters_sf,
193+
rnet_unjittered_quietest %>% rename(Bikes_unjittered_quietest = Bike),
194+
join = sf::st_nearest_feature)
195+
counters_sf_joined = st_join(counters_sf_joined,
196+
rnet_unjittered_balanced %>% rename(Bikes_unjittered_balanced = Bike),
197+
join = sf::st_nearest_feature)
198+
counters_sf_joined = st_join(counters_sf_joined,
199+
rnet_unjittered_fastest %>% rename(Bikes_unjittered_fastest = Bike),
200+
join = sf::st_nearest_feature)
201+
counters_sf_joined = st_join(counters_sf_joined,
202+
rnet_jittered_quietest %>% rename(Bikes_jittered_quietest = Bike),
203+
join = sf::st_nearest_feature)
204+
counters_sf_joined = st_join(counters_sf_joined,
205+
rnet_jittered_balanced %>% rename(Bikes_jittered_balanced = Bike),
206+
join = sf::st_nearest_feature)
207+
counters_sf_joined = st_join(counters_sf_joined,
208+
rnet_jittered_fastest %>% rename(Bikes_jittered_fastest = Bike),
209+
join = sf::st_nearest_feature)
210+
# # head(counters_sf_joined)
211+
# corrplot::corrplot(counters_sf_joined %>% sf::st_drop_geometry())
212+
# counters_sf_joined %>%
213+
# sf::st_drop_geometry() %>%
214+
# plot()
215+
```
216+
145217
Although useful for visualising the complex and spatially diffuse reality of travel patterns, we found that the most valuable use of jittering is as a pre-processing stage before routing and route network generation.
146-
Route networks generated from jittered desire lines are more diffuse, and potentially more realistic, that centroid-based desire lines.
218+
Route networks generated from jittered desire lines are more diffuse, and potentially more realistic, than centroid-based desire lines.
147219

148220
We also found that the approach, implemented in Rust and with bindings to R and Python (in progress), is fast.
149221
Benchmarks show that the approach can 'jitter' desire lines representing millions of trips in a major city in less than a minute on consumer hardware.
150222

151223
We also found that the results of jittering depend on the geographic input datasets representing start points and trip attractors, and the use of weights.
152-
This highlights the importance of exploring the parameter space for optimal jittered desire line creation.
153224

154-
# Next steps
225+
```{r}
226+
results = tibble::tribble(
227+
~`Jittering`, ~`Routing`, ~`Nrow`, ~`R-Squared`,
228+
"Unjittered", "quietest", nrow(od_lisbon_sf), cor(counters_sf_joined$SumCiclistas, counters_sf_joined$Bikes_unjittered_quietest),
229+
"Unjittered", "balanced", nrow(od_lisbon_sf), cor(counters_sf_joined$SumCiclistas, counters_sf_joined$Bikes_unjittered_balanced),
230+
"Unjittered", "fastest", nrow(od_lisbon_sf), cor(counters_sf_joined$SumCiclistas, counters_sf_joined$Bikes_unjittered_fastest),
231+
"Jittered, no disaggregation", "quietest", nrow(od_lisbon_jittered), cor(counters_sf_joined$SumCiclistas, counters_sf_joined$Bikes_jittered_quietest),
232+
"Jittered, no disaggregation", "balanced", nrow(od_lisbon_jittered), cor(counters_sf_joined$SumCiclistas, counters_sf_joined$Bikes_jittered_balanced),
233+
"Jittered, no disaggregation", "fastest", nrow(od_lisbon_jittered), cor(counters_sf_joined$SumCiclistas, counters_sf_joined$Bikes_jittered_fastest),
234+
)
235+
knitr::kable(results, digits = 2, booktabs = TRUE, caption = "Results showing counter/model fit for route networks generated from different routing and jittering parameters")
236+
```
237+
238+
# Conclusion
155239

156-
We plan to create/improve R/Python interfaces to the `odjitter` and enable others to benefit from it.
157-
<!-- Although an R interface to the `odjitter` crate has already been developed, it uses system calls, not bindings provided by the R package `rextendr`. --> We plan to improve the package's documentation and to test its results, supporting reproducible sustainable transport research worldwide.
240+
Building on previous work , we have explored the relative importance of parameters that 'jitter' and disaggregate OD data to create more spatially diverse geographic represenations of travel between zone *and* the routing settings used.
241+
We found that the combination of careful selection of routing profiles, in addition to careful and iteratively selected jittering parameters is needed for realistic route network results, based on a case study of Lisbon, Portugal.
242+
We cannot draw conclusions about the optimal settings for accurate route network generation in other cities because each route network and set of cycling preferences is different.
243+
Future work should seek to test a wider range of jittering parameters in multiple case study areas with larger ground truth datasets.
158244

159245
# References

README.md

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,21 +113,20 @@ This approach has been implemented efficiently in the Rust crate
113113

114114
# Results
115115

116-
We have found that jittering leads to more spatially diffuse
117-
representations of OD datasets than the common approach to desire lines
118-
that go from and to zone centroids. We have used the approach to add
119-
value to numerous OD datasets for projects based in Ireland, Norway,
120-
Portugal, New Zealand and beyond. For instance, Fig. shows the
121-
difference between desire lines with centroids approach and the
122-
jittering approach.
116+
Jittering leads to more spatially diffuse representations of OD datasets
117+
than the common approach to desire lines that go from and to zone
118+
centroids. We have used the approach to add value to numerous OD
119+
datasets for projects based in Ireland, Norway, Portugal, New Zealand
120+
and beyond. Figure @ref(fig:lisbon1) shows the difference between desire
121+
lines with centroids approach and the jittering approach.
123122

124123
<img src="README_files/figure-gfm/jitteredoverview-1.png" title="\label{poltlisbon}Trips represented with desire lines from centroids and with jittering, for Lisbon (Portugal)" alt="\label{poltlisbon}Trips represented with desire lines from centroids and with jittering, for Lisbon (Portugal)" width="50%" style="display: block; margin: auto;" /><img src="README_files/figure-gfm/jitteredoverview-2.png" title="\label{poltlisbon}Trips represented with desire lines from centroids and with jittering, for Lisbon (Portugal)" alt="\label{poltlisbon}Trips represented with desire lines from centroids and with jittering, for Lisbon (Portugal)" width="50%" style="display: block; margin: auto;" />
125124

126125
Although useful for visualising the complex and spatially diffuse
127126
reality of travel patterns, we found that the most valuable use of
128127
jittering is as a pre-processing stage before routing and route network
129128
generation. Route networks generated from jittered desire lines are more
130-
diffuse, and potentially more realistic, that centroid-based desire
129+
diffuse, and potentially more realistic, than centroid-based desire
131130
lines.
132131

133132
We also found that the approach, implemented in Rust and with bindings
@@ -137,8 +136,16 @@ major city in less than a minute on consumer hardware.
137136

138137
We also found that the results of jittering depend on the geographic
139138
input datasets representing start points and trip attractors, and the
140-
use of weights. This highlights the importance of exploring the
141-
parameter space for optimal jittered desire line creation.
139+
use of weights.
140+
141+
| Jittering parameters | Routing parameters | Nrow | R-Squared |
142+
|:----------------------------|:-------------------|-----:|----------:|
143+
| Unjittered | quietest | 574 | 0.23 |
144+
| Unjittered | balanced | 574 | 0.22 |
145+
| Unjittered | fastest | 574 | 0.10 |
146+
| Jittered, no disaggregation | quietest | 574 | 0.26 |
147+
| Jittered, no disaggregation | balanced | 574 | 0.11 |
148+
| Jittered, no disaggregation | fastest | 574 | 0.00 |
142149

143150
# Next steps
144151

71.7 KB
Loading
55.1 KB
Loading
10.3 KB
Loading

foss4g2022.bib

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,22 @@ @article{rae_spatial_2009
126126
}
127127

128128

129+
130+
@article{lovelace_jittering_2022b,
131+
title = {Jittering: {{A Computationally Efficient Method}} for {{Generating Realistic Route Networks}} from {{Origin-Destination Data}}},
132+
shorttitle = {Jittering},
133+
author = {Lovelace, Robin and F{\'e}lix, Rosa and Carlino, Dustin},
134+
year = {2022},
135+
month = apr,
136+
journal = {Findings},
137+
pages = {33873},
138+
publisher = {{Findings Press}},
139+
doi = {10.32866/001c.33873},
140+
url = {https://findingspress.org/article/33873-jittering-a-computationally-efficient-method-for-generating-realistic-route-networks-from-origin-destination-data},
141+
urldate = {2022-05-05},
142+
copyright = {Creative Commons Attribution-ShareAlike 4.0 International Licence (CC-BY-SA)},
143+
langid = {english},
144+
file = {/home/robin/Zotero/storage/MW7B8GVG/Lovelace et al. - 2022 - Jittering A Computationally Efficient Method for .pdf;/home/robin/Zotero/storage/QZU3J666/33873-jittering-a-computationally-efficient-method-for-generating-realistic-route-networks-from.html}
145+
}
146+
147+

0 commit comments

Comments
 (0)