This vignette documents how rcrisp performs across a
diverse set of European city-river cases. The goal is to identify which
pipeline steps are most computationally intensive and how run time
varies with the scale and complexity of the urban river system being
delineated.
Benchmarks were run on a single machine in a single session using OSM
and SRTM data downloaded live; the get_osm() and
get_dem() timings therefore include network latency and
reflect the volume of spatial data available for each case, not just CPU
work.
raw <- read.csv(
system.file("extdata", "benchmark_timings.csv", package = "rcrisp"),
stringsAsFactors = FALSE
)
# Pipeline steps in processing order
io_steps <- c("get_osm", "get_dem")
compute_steps <- c("delineate_valley", "as_network", "delineate_corridor",
"delineate_segments", "delineate_riverspace")
all_steps <- c(io_steps, compute_steps)
step_labels <- c(
get_osm = "get_osm()",
get_dem = "get_dem()",
delineate_valley = "delineate_valley()",
as_network = "as_network()",
delineate_corridor = "delineate_corridor()",
delineate_segments = "delineate_segments()",
delineate_riverspace = "delineate_riverspace()"
)
raw <- raw |>
mutate(case = paste0(city_name, " (", river_name, ")"))
# Cases that completed the full pipeline
case_order <- raw |>
filter(step == "total", status == "success") |>
arrange(elapsed_s) |>
pull(case)The table below summarises the 36 city-river cases included in the benchmark. Cases marked Incomplete did not produce a final delineation; the step at which the run was interrupted is shown in the Note column.
| City | River | Total (s) | Note |
|---|---|---|---|
| Iași | Bahlui | 15.6 | |
| Malmö | Sege å | 15.9 | |
| Porto | Rio Douro | 17.2 | |
| Plovdiv | Марица | 17.2 | |
| Tallinn | Pirita jõgi | 19.6 | |
| Timișoara | Bega | 23.7 | |
| Cluj-Napoca | Someșul Mic | 24.6 | |
| Münster | Werse | 26 | |
| Geneva | L’Arve | 26.2 | |
| Brașov | Timiș | 26.4 | |
| København | Mølleåen | 29.9 | |
| Montpellier | Le Lez | 31.8 | |
| Århus | Aarhus Å | 35.3 | |
| Trondheim | Nidelva | 38.1 | |
| Helsinki | Vantaanjoki | 40.1 | |
| Ankara | Ankara Çayı | 40.6 | |
| Ljubljana | Sava | 47.7 | |
| Oslo | Alna | 57.7 | |
| Dublin | Tolka | 60.9 | |
| Poznań | Warta | 65 | |
| Rīga | Daugava | 65.9 | |
| Vilnius | Neris | 67.9 | |
| Liège | La Meuse | 72.1 | |
| Zagreb | Sava | 102.3 | |
| Bratislava | Dunaj | 104.4 | |
| Amsterdam | Amstel | 113.1 | |
| Praha | Vltava | 123.3 | |
| Madrid | Río Manzanares | 200.9 | |
| Budapest | Duna | 225.5 | |
| Roma | Tevere | 282.6 | |
| Paris | La Seine | 446 | |
| Athina | Κηφισός | Incomplete | Failed at get_osm(): No city boundary found. The city name may be incorrect. |
| Craiova | Jiu | Incomplete | Failed at delineate_corridor(): Corridor start- and end-points coincide! |
| Galați | Prut | Incomplete | Failed at delineate_corridor(): No river crossings found. Corridor cannot be delineated. |
| Sheffield | River Don | Incomplete | Failed at delineate_riverspace(): TopologyException: side location conflict at 598983.54113248398 5920051.9688930158. This can occur if the input geometry is invalid. |
| Wien | Donau | Incomplete | Failed at delineate_corridor(): No river crossings found. Corridor cannot be delineated. |
| Athina | Κηφισός | Incomplete | Failed at get_osm(): No city boundary found. The city name may be incorrect. |
The chart below shows total run time per case, broken into
data download (get_osm() +
get_dem()) and computation (all
delineation steps). Only the 31 cases that completed the full pipeline
are included. Cases are sorted from fastest (bottom) to slowest
(top).
bar_data <- raw |>
filter(case %in% case_order, step %in% all_steps, status == "success") |>
mutate(
step_type = if_else(step %in% io_steps, "Data download", "Computation"),
case = factor(case, levels = case_order)
) |>
group_by(case, step_type) |>
summarise(elapsed_s = sum(elapsed_s), .groups = "drop") |>
mutate(step_type = factor(step_type, levels = c("Data download", "Computation")))
ggplot(bar_data, aes(x = elapsed_s, y = case, fill = step_type)) +
geom_bar(stat = "identity") +
scale_x_continuous(expand = expansion(mult = c(0, 0.03))) +
scale_fill_manual(
values = c("Data download" = "#4C72B0", "Computation" = "#DD8452"),
breaks = c("Data download", "Computation")
) +
labs(
x = "Elapsed time (s)",
y = NULL,
fill = NULL
) +
theme_minimal(base_size = 11) +
theme(
legend.position = "top",
panel.grid.major.y = element_blank()
)Data download accounts for the majority of wall-clock time in most
cases. The variation in get_osm() time is driven primarily
by the volume of OSM data within the area of interest: large
metropolitan areas (Paris, Roma, Budapest) have substantially more
street and building data than smaller or less densely documented
cities.
The heatmap below shows elapsed time for each pipeline step individually. The colour scale is logarithmic so that the fast computation steps remain visible alongside the much longer download steps. Grey tiles indicate steps that were not reached due to an earlier failure.
# Include incomplete runs (except Athina where get_osm itself failed)
heatmap_cases <- c(case_order, setdiff(
raw |>
filter(step %in% all_steps) |>
distinct(case) |>
filter(!case %in% case_order) |>
pull(case),
"Athina (Κηφισός)"
))
heatmap_data <- raw |>
filter(case %in% heatmap_cases, step %in% all_steps) |>
mutate(
step_type = factor(
if_else(step %in% io_steps, "Data download", "Computation"),
levels = c("Data download", "Computation")
),
step_label = factor(step_labels[step], levels = step_labels[all_steps]),
elapsed_plot = if_else(status == "success", elapsed_s, NA_real_),
case = factor(case, levels = rev(heatmap_cases))
)
ggplot(heatmap_data, aes(x = step_label, y = case, fill = elapsed_plot)) +
geom_tile(colour = "white", linewidth = 0.4) +
facet_grid(~ step_type, scales = "free_x", space = "free_x") +
scale_fill_viridis_c(
option = "plasma",
na.value = "grey85",
trans = "log10",
name = "Seconds\n(log scale)",
labels = \(x) formatC(x, format = "g", digits = 2)
) +
labs(x = NULL, y = NULL) +
theme_minimal(base_size = 10) +
theme(
axis.text.x = element_text(angle = 40, hjust = 1),
strip.text = element_text(face = "bold"),
panel.grid = element_blank(),
legend.key.height = unit(1.5, "cm")
)Looking across all cases, a few patterns stand out:
get_osm() dominates total run time.
It accounts for 75–95% of wall-clock time in most cases, with a wide
range driven by OSM data volume (from ~8 s for Plovdiv to ~395 s for
Paris).
get_dem() is fast and consistent,
typically 1.5–7 s regardless of city size, because the DEM tile count
depends on geographic extent rather than urban density.
Among computation steps,
delineate_riverspace() is the most variable. In
cases with large building footprints or complex water surfaces
(Budapest, Amsterdam, Brașov), it can exceed 5–30 s; in simpler cases it
runs in under 1 s.
as_network() and
delineate_corridor() scale with street network
size. Both steps are noticeably slower for large, dense cities
(Paris, Budapest, Roma) where the network within the search buffer
contains many more edges.
delineate_valley() and
delineate_segments() are generally fast (under 3 s
in most cases), though delineate_valley() can be slower
when the study area covers a large elevation range with many raster
cells.