Non-dendritic networks


vignette("hydroloom") and vignette("advanced_network") talks about the basics of network topology representation and attributes that build on a strictly dendritic network. This vignette expands those topics by describing hydroloom functionality that supports non-dendritic networks.

Non-dendritic topology attributes.

Non-dendritic networks represent downstream diverted flow where one path is primary and all others are thought to be secondary. The following attributes are supported by hydroloom to help track and work with this primary and secondary downstream categorization.

fromnode and tonode

The attributes fromnode and tonode are used to store a flow network as a edge node topology where every network feature has one and only one node upstream and one and only one node downstream. Nodes are useful if converting a flow network to a graph and are useful in many analysis contexts as there is a single identifier for a confluence or divergence.


The divergence attribute indicates if a downstream connection is primary (1) or secondary (2). If 0, a connection is not downstream of a divergence. This attribute is useful as it facilitates following a flow network in the “downstream mainstem” direction at every divergence.

return divergence

The return divergence attribute indicates that one or more of the features upstream of a given feature originates from a divergence. If 0, the upstream features are not part of a diversion. If 1, one or more of the upstream features is part of a diversion.

stream calculator

The stream calculator attribute is part of the modified Strahler stream order as implemented in the NHDPlus data model. It indicates if a given feature is part of the downstream mainstem dendritic network or is part of a diverted path. If 0, the path is part of a diversion. Otherwise stream calculator will be equal to stream order. When generating Strahler stream order, if stream calculator is 0 for a given feature, that feature is not considered for incrementing downstream stream order.


As a system, stream calculator, divergence and return divergence support network navigation and processing in the context of diverted paths.

  1. A feature at the top of a diversion will have divergence set to 1.
  2. All features that are part of a diversion that has not yet recombined with a main path, will have stream calculator set to 0.
  3. A feature that is just downstream of where a diversion path recombines with a main path will have return diversion set to 1.

Bringing it all together

The example below shows how we can recreate the non-dendritic attributes and use them in practice.

We’ll start with the small sample watershed that’s included in hydroloom and select only the attributes required to recreate the non-dendritic network.

x <- sf::read_sf(system.file("extdata/new_hope.gpkg", 
                             package = "hydroloom"))

# First we select only an id, a name, and a feature type.
flow_net <- x |>
  select(COMID, GNIS_ID, FTYPE) |>

# Now we convert the geometric network to an attribute topology
# and convert that to a node topology and join our attributes back
flow_net <- flow_net |>
  make_attribute_topology(min_distance = 5) |>
  hydroloom::make_node_topology(add_div = TRUE) |>
  left_join(sf::st_drop_geometry(flow_net), by = "COMID")

# We only have one outlet so it doesn't matter if it is coastal
# or inland but we have to provide it.
outlets <- filter(flow_net, !tonode %in% fromnode)

# We have these feature types. A larger dataset might include 
# things like canals which would not be considered  "major"
#> [1] "StreamRiver"    "Connector"      "ArtificialPath"

# now we run the add_divergence, add_toids, and add_streamorder
flow_net <- add_divergence(flow_net, 
                           coastal_outlet_ids = c(), 
                           inland_outlet_ids = outlets$COMID, 
                           name_attr = "GNIS_ID", 
                           type_attr = "FTYPE", 
                           major_types = unique(flow_net$FTYPE)) |>
  add_toids() |>
  add_streamorder() |>

# Make sure we reproduce what came from our source NHDPlus data.
sum(flow_net$divergence == 2)
#> [1] 84
sum(x$Divergence == 2)
#> [1] 84
all(flow_net$divergence == x$Divergence)
#> [1] TRUE
sum(flow_net$return_divergence == x$RtnDiv)
#> [1] 662

#>  [1] "COMID"             "toid"              "fromnode"         
#>  [4] "tonode"            "GNIS_ID"           "FTYPE"            
#>  [7] "divergence"        "stream_order"      "stream_calculator"
#> [10] "return_divergence"

With the above code, we removed all attributes other than an ID, a name and a feature type and recreated both a dendritic (toid) and non-dendritic (fromnode tonode) topology. We added divergence attribute, stream_order, stream_calculator, and return_divergence attributes.