Route networks represent the network of highways, cycleways, footways and other ways along which transport happens. Unlike routes, each segment geometry the network can only appear once in a single route network.
stplanr can be used to convert a series of routes into a route network, using the function overline2()
, as illustrated below:
sample_routes <- routes_fast_sf[2:6, 1]
sample_routes$value <- rep(1:3, length.out = 5)
rnet <- overline2(sample_routes, attrib = "value")
plot(sample_routes["value"], lwd = sample_routes$value, main = "Routes")
plot(rnet["value"], lwd = rnet$value, main = "Route network")
The above figure shows how overline2()
breaks the routes into segments with the same values and removes overlapping segments. It is a form of geographic aggregation.
An important feature of route networks is that they are simultaneously spatial and graph entities. This duality is captured in sfNetwork
objects, which can be created by the function SpatialLinesNetwork()
:
sln
has both spatial and graph components, with the number of lines equal to the number graph edges:
class(sln@sl)
#> [1] "sf" "tbl_df" "tbl" "data.frame"
nrow(sln@sl)
#> [1] 8
class(sln@g)
#> [1] "igraph"
length(igraph::edge.attributes(sln@g)[["weight"]])
#> [1] 8
class(sln@nb)
#> [1] "list"
length(unique(unlist(sln@nb)))
#> [1] 8
identical(sln@sl$geometry, rnet$geometry)
#> [1] TRUE
rnet_coordinates <- sf::st_coordinates(rnet)
set.seed(85)
x <- runif(n = 2, min = min(rnet_coordinates[, 1]), max = max(rnet_coordinates[, 1]))
y <- runif(n = 2, min = min(rnet_coordinates[, 2]), max = max(rnet_coordinates[, 2]))
crs <- sf::st_crs(rnet)
xy_sf <- sf::st_as_sf(data.frame(n = 1:2, x, y), coords = c("x", "y"), crs = crs)
xy_nodes <- stplanr::find_network_nodes(sln = sln, x = x, y = y)
Currently not running due to issues with dev version of dplyr
:
https://github.com/ropensci/stplanr/issues/383
# plot(rnet$geometry)
# plot(sln_nodes, add = TRUE)
# xy_path <- sum_network_routes(sln = sln, start = xy_nodes[1], end = xy_nodes[2], sumvars = "length")
# # xy_path = sum_network_links(sln = sln, start = xy_nodes[1], end = xy_nodes[2])
# plot(rnet$geometry)
# plot(xy_sf$geometry, add = TRUE)
# plot(xy_path$geometry, add = TRUE, lwd = 5)
New nodes can be added to the network, although this should be done before the graph representation is created. Imagine we want to create a point half way along the the most westerly route segment in the network, near the coordinates -1.540, 53.826:
new_point_coordinates <- c(-1.540, 53.826)
p <- sf::st_sf(geometry = sf::st_sfc(sf::st_point(new_point_coordinates)), crs = crs)
We can identify the nearest point on the network at this point and use that to split the associated linestring:
sln_new <- sln_add_node(sln = sln, p = p)
#> although coordinates are longitude/latitude, st_nearest_feature assumes that they are planar
route_new <- route_local(sln = sln_new, from = p, to = xy_sf[1, ])
#> Warning: `select_()` is deprecated as of dplyr 0.7.0.
#> Please use `select()` instead.
#> This warning is displayed once every 8 hours.
#> Call `lifecycle::last_warnings()` to see where this warning was generated.
#> Warning: `group_by_()` is deprecated as of dplyr 0.7.0.
#> Please use `group_by()` instead.
#> See vignette('programming') for more help
#> This warning is displayed once every 8 hours.
#> Call `lifecycle::last_warnings()` to see where this warning was generated.
plot(sln_new)
plot(p, add = TRUE)
plot(route_new, lwd = 5, add = TRUE)
#> Warning in plot.sf(route_new, lwd = 5, add = TRUE): ignoring all but the first
#> attribute
Other approaches to working with route networks include: