@@ -92,3 +92,60 @@ accumulate_downstream <- function(x, id = "flowpath_id", toid = "flowpath_toid
9292 # Return totals aligned to input rows
9393 as.numeric(total [idx ])
9494}
95+
96+
97+ # ' Compute and add the hydrosequence to a directed acyclic network.
98+ # '
99+ # ' @param topology A data frame (or tibble) containing at least the identifier column
100+ # ' given by `id` and the downstream pointer column given by `toid`.
101+ # ' @param id Character scalar. Column name in `topology` with unique node identifiers.
102+ # ' Defaults to `"flowpath_id"`.
103+ # ' @param toid Character scalar. Column name in `topology` with the *downstream* node
104+ # ' identifier for each row. Use `NA` or `0` for outlets/terminals.
105+ # ' Defaults to `"flowpath_toid"`.
106+ # ' @param colname Character scalar. Column name to use in result.
107+ # ' Defaults to `"hydroseq"`
108+ # '
109+ # ' @returns The data frame `topology` with an additional column, named `colname`, representing the hydrosequence.
110+ # ' @importFrom igraph dfs graph_from_data_frame
111+ # ' @export
112+ add_hydroseq <- function (topology , id = " flowpath_id" , toid = " flowpath_toid" , colname = " hydroseq" ) {
113+ # Create a _transposed_ network, where traversing the network
114+ # is equivalent to traversing the hydrological network upstream.
115+ #
116+ # This assumes the outlets of this network all connect to an
117+ # ephemeral "0" node (forming a rooted tree network).
118+ edgelist <- topology [, c(toid , id )]
119+ names(edgelist ) <- c(" id" , " toid" )
120+ edgelist $ id [is.na(edgelist $ id )] <- " 0"
121+
122+ # TODO: Check if multiple components exist. If they do, then
123+ # we need to add "0" edges for each component not rooted on "0".
124+
125+ # Perform DFS from each terminal upstream to get a
126+ # distinct topological sort for the hydrosequence.
127+ sorted <- data.frame (
128+ node = as.integer(
129+ names(
130+ igraph :: dfs(
131+ igraph :: graph_from_data_frame(edgelist ),
132+ root = " 0" ,
133+ mode = " out"
134+ )$ order
135+ )
136+ )
137+ )
138+
139+ sorted $ hydroseq <- c(0 , seq_len(nrow(sorted ) - 1 ))
140+
141+ # Merge the initial hydrosequence to the edgelist and handle ties in the hydrosequence.
142+ result <- merge(edgelist , sorted , by.x = " id" , by.y = " node" , all.x = TRUE )
143+ result <- result [! is.na(result $ hydroseq ), ]
144+ result <- result [order(result $ hydroseq , result $ id , result $ toid ), c(" toid" , " id" )]
145+ result $ hydroseq <- seq_len(nrow(result ))
146+ names(result ) <- c(id , toid , " hydroseq" )
147+
148+ # Arrange into input order
149+ topology [[colname ]] <- result $ hydroseq [match(topology [[id ]], result [[id ]])]
150+ topology
151+ }
0 commit comments