In this notebook we explore how to delineate a river corridor using Bucharest as the study area using OpenStreetMap (OSM) data. We focus on one of the rivers and use a specific projected CRS (coordinate reference system) for the analysis. A project CRS is required for calculating distances along the street network during delineation. Also, we make sure that we include a given area around the city boundaries.
city_name <- "Bucharest, Romania" # Be specific and spell as in OSM
river_name <- "Dâmbovița" # Spell as in OSM
epsg_code <- 32635 # UTM zone 35N
bbox_buffer <- 2000 # Buffer around the city boundary in meters
Setting the area of interest
# Get the bounding box from the Nominatim API provided by OSM.
bb <- getbb(city_name)
aoi <- define_aoi(bb, epsg_code, bbox_buffer)
city_boundary <- osmdata_as_sf("place", "city", bb)$osm_multipolygons |>
st_transform(epsg_code) |>
st_geometry()
Constructing the initial corridor
river_centerline <- osmdata_as_sf("waterway", "river", bb)$osm_multilines |>
filter(name == river_name) |>
st_transform(epsg_code) |>
st_geometry() |>
st_intersection(st_buffer(aoi, bbox_buffer))
river_surface <- osmdata_as_sf("natural", "water", bb)
river_surface <- river_surface$osm_multipolygons |>
bind_rows(river_surface$osm_polygons) |>
st_transform(epsg_code) |>
st_filter(river_centerline, .predicate = st_intersects) |>
st_geometry() |>
st_union()
With these features we can now delineate a theoretical river corridor
representing the maximum area from which the river could be reached
within a walking distance. We construct the initial corridor from the
merged geometries of river_centerline
and
river_surface
, with a buffer of 500m around them:
corridor_buffer <- 500
corridor_initial <- c(river_centerline, river_surface) |>
st_buffer(corridor_buffer) |>
st_union()
We can now plot the initial corridor:
Getting the street network
Querying the Overpass API for the highway
key:
highway_values <- c("motorway", "primary", "secondary", "tertiary")
streets <- osmdata_as_sf("highway", highway_values, bb)
streets <- merge_streets(streets) |>
select("highway")
#> Warning in st_cast.sf(highways$osm_polygons, "LINESTRING"): repeating
#> attributes for all sub-geometries for which they may not be constant
We split the area of interest in two parts using the river centreline as separator. The two areas can then be used to split the network in two as a preparation to the shortest path algorithm used for edge delineation in the next step:
aoi_split <- aoi |>
split_aoi(river_centerline)
net <- create_network(streets, epsg_code)
net_1 <- net |>
trim_network(aoi_split[1], corridor_initial) |>
clean_network() |>
calc_weights() |>
sfnetworks::activate("nodes") |>
filter(tidygraph::group_components() == 1)
#> Warning: to_spatial_subdivision assumes attributes are constant over geometries
net_2 <- net |>
trim_network(aoi_split[2], corridor_initial) |>
clean_network() |>
calc_weights() |>
sfnetworks::activate("nodes") |>
filter(tidygraph::group_components() == 1)
#> Warning: to_spatial_subdivision assumes attributes are constant over geometries
Corridor edge delineation
Determine the “vertices” of the initial river corridor as the intersections of the initial river corridor with the AoI boundary. We will use these points as extremes for the corridor edges:
vertices_1 <- get_vertices(aoi_split[1], corridor_initial |>
st_cast("LINESTRING"))
vertices_2 <- get_vertices(aoi_split[2], corridor_initial |>
st_cast("LINESTRING"))
corridor_edge_1 <- get_corridor_edge(net_1, aoi_split[1], vertices_1)
corridor_edge_2 <- get_corridor_edge(net_2, aoi_split[2], vertices_2)
corridor_edges <- st_union(corridor_edge_1, corridor_edge_2)
Capping the corridor
capped_corridor <- city_boundary |>
lwgeom::st_split(corridor_edges) |>
sf::st_collection_extract("POLYGON") |>
sf::st_as_sf() |>
sf::st_filter(river_centerline, .predicate = sf::st_intersects)
# TODO replace with call to cap_corridor()
corridor <- cap_corridor(corridor_edges, river_centerline, epsg_code, bb)
Note that this is not ideal, as the municipal boundaries can be arbitrary and might exclude important end features of the corridors, so the user should have the option to input their own feature to cap the corridor ends. In the case of Bucharest, this can be the ring road.
All in one
The delineate_corridor()
function carries out the entire
delineation in one step and returns a list that by default contains the
following output elements: corridor
, segments
,
and riverspace
. Although not included by default, the used
input objects river_line
, river_polygon
, and
buildings
can also be included in the resulting object. The
initial corridor boundary, either a buffer or valley edges derived from
the digital elevation model, can also be included in the output
object.
# TODO this function should run the entire urc delineation process in one step
urc <- delineate_corridor(city_name, river_name,
segments = TRUE, riverspace = TRUE)
# `$corridor` of the resulting object is an sf polygon representing the corridor
urc$corridor
# `$segments` is an sf polygon representing the segments of the corridor,
# TODO make sure the segments are numbered from upstream to downstream
urc$segments
# `$riverspace` is an sf polygon representing the delineated river space
urc$riverspace
# All three elements of delineation
plot(urc$riverspace, col = "green")
plot(urc$river, col = "blue")
plot(urc$segments, col = "lightblue", add = TRUE)
plot(urc$corridor, add = TRUE, col = "red", wt = 2, add = TRUE)