6

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!

mbcaradima
  • 251
  • 2
  • 11
  • If I understand correctly the problem is: pruning every subtree following a node. In the data.tree package there is a function that does it, Prune (I guess it could also be done directly in igraph). – Arthur Welle Jan 07 '23 at 14:32
  • To clarify my problem statement, for any given node on a directed `sfnetwork` object, I'd like to subset the portion of the directed network that can reach that node. Ideally I'd like to use `sfnetworks` or potentially `igraph` if needed as the former package uses the latter package. I'll take a look at `data.tree` as an option. – mbcaradima Jan 08 '23 at 20:45

1 Answers1

2

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)

  • Thanks Leo, I think your post answers my question. It seems I misunderstood the arguments of shortest_path. For others following, the key piece of info is in the help file for `?igraph::shortest_paths` which describes the `mode` argument as follows: "[mode] gives whether the shortest paths to or from the given vertices should be calculated for directed graphs. If out then the shortest paths from the vertex, if in then to it will be considered" So using "out" return viable paths originating from the vertex; in a directed stream network, that means all upstream paths. – mbcaradima Jan 12 '23 at 22:41