Using R package sfnetworks to subset river network that is upstream of a given node

424 views Asked by At

I'm experimenting with using the sfnetworks package to query river networks. Specifically, I'd like to subset upstream portions of a river network relative to any given site.

I have a small river network dataset (available here) that I can convert into an sfnetwork, which is defined by the package as a rooted tree with spatially explicit edges. The resulting network is a directed acyclical graph (see the ggraph plot below).

library(sf)
library(sfnetworks)
library(ggraph)
library(cowplot)

ss <- st_read(file.path("stream_network_subsetS_KNIG.gpkg")) # subset small network (Lull Creek)

#' create sfnetwork object from river network
#' https://github.com/luukvdmeer/sfnetworks/discussions/143
n.ss <- as_sfnetwork(st_cast(ss, "LINESTRING"))

n.ss

plot(n.ss)

#' visualize directness of network
ggraph(n.ss, layout = "tree") +
  geom_node_point(size = 1) +
  geom_edge_link(arrow = arrow(length = unit(2, 'mm'), ends = "first"),
                 end_cap = circle(2, 'mm'),
                 start_cap = circle(2, 'mm')) +
  theme_graph()

directness of sfnetwork object What I'd like to do is subset/filter the network that is upstream of any given node that I specify. Since sfnetworks use igraph for the underlying network data structure, I tried using igraph::shortest_paths to obtain the upstream nodes:

#' this node is on the lower main stem of the network
node <- 50

#' query the nodes/vertices with a path to the node (warning issued)
x <- unique(unlist(igraph::shortest_paths(n.ss, from = node, to = igraph::V(n.ss), mode = "in")[[1]]))

#' select upstream nodes from spatial network
up.ss <- rep(FALSE, nrow(st_as_sf(n.ss, "nodes")))
up.ss[x] <- TRUE

n.ss.up <- n.ss |>
  activate("nodes") |>
  filter(up.ss)

#' plot upstream nodes
plot_grid(autoplot(n.ss), autoplot(n.ss.up))

left: entire river network, right: attempt to extract nodes upstream of node 50 Strangely, shortest_paths only returns nodes along the main stem of the river network. I'm aware of sfnetworks::st_network_paths, but I'm not sure if its suitable for what I'm trying to do. My question is, how would I use sfnetworks to select the river network (i.e., vertices/nodes and edges) that is upstream of the node I've specified?

I'm also aware that sfnetworks has a routing tutorial routing tutorial), but it clearly emphasizes street network routing.

Using dput on the example data produces an unwieldy output, so I've uploaded the dataset to Google Drive here (if anyone can point out a better solution for sharing a ~50MB file, I'll go ahead and use that in the interests of reproducibility). Thank you!

1

There are 1 answers

1
Leo Kiernan On BEST ANSWER

You're so close! you're code is looking for all river segments that can be reached downstream rather than upstream. This is probably just an artifact of how the links in your river dataset are encoded.

change this line of your code:

  • from x <- unique(unlist(igraph::shortest_paths(n.ss, from = node, to = igraph::V(n.ss), mode = "in")[1]))
  • to x <- unique(unlist(igraph::shortest_paths(n.ss, from = node, to = igraph::V(n.ss), mode = "out")[1]))

and I hope you'll see the upstream links highlighted as shown below: image showing the full basin and nodes downstream and upstream (in & out respectively)