From e844edc8435075a1098e4c11641ca659aa65b9c0 Mon Sep 17 00:00:00 2001
From: Bock <abock@usgs.gov>
Date: Thu, 30 Jun 2022 10:48:47 -0600
Subject: [PATCH] Added functions for WB events

---
 workspace/R/NHD_navigate.R | 238 +++++++++++++++++++++++++++++++++++++
 1 file changed, 238 insertions(+)

diff --git a/workspace/R/NHD_navigate.R b/workspace/R/NHD_navigate.R
index 9ff7e7c..2253a59 100644
--- a/workspace/R/NHD_navigate.R
+++ b/workspace/R/NHD_navigate.R
@@ -775,3 +775,241 @@ split_tt <- function(in_POI_ID, split_DF, tt_diff, max_DA){
 }
 
 
+#'  Retrieves waterbodies from NHDArea layer or NHD Hi-Res for ResOpsUs 
+#'  locations if absent from NHD waterbody
+#'  @param nhd (sf data.frame) flowlines for given VPU
+#'  @param WBs  (sf data.frame) waterbodys for discretization within VPU
+#'  @param data_paths (list) data paths to data layers
+#' 
+#'  @return (list) wbs - sf data frame for NHDArea and HR waterbodies
+#'                 wb_table - table of flowlines and outlet info for each 
+#'                            feature in wb
+HR_Area_coms <- function(nhd, WBs, data_paths){
+  # Read in resops table
+  resops_wb_df <- read.csv(data_paths$resops_NID_CW)
+  
+  # Pull out rows for VPU that are NHDArea
+  nhd_area_resops <- resops_wb_df %>%
+    filter(FlowLcomid %in% nhd$COMID, COMI_srclyr == "NHDAREA")
+  
+  # Pull out rows for VPU that are NHD HR
+  nhd_hr_wb_resops <-  resops_wb_df %>%
+    filter(FlowLcomid %in% nhd$COMID, COMI_srclyr == "HR ID AVAILABLE")
+  
+  # Get reachcodes for waterbody outlets, so we have an idea of which
+  #     NHD HR 4-digit geodatabase we may need to retrieve
+  RC <- filter(nhd, COMID %in% c(nhd_area_resops$FlowLcomid, 
+                                 nhd_hr_wb_resops$FlowLcomid))$REACHCODE
+  
+  # If no NHDArea or HR waterbodies needed return NULL
+  if (nrow(nhd_area_resops) == 0 & nrow(nhd_hr_wb_resops) == 0){
+    return(NA)
+  }
+  
+  # If NHDArea feature needed retrieve from National GDB
+  if (nrow(nhd_area_resops) > 0){
+    nhd_area_vpu <- read_sf(data_paths$nhdplus_gdb, "NHDArea") %>%
+      filter(COMID %in% nhd_area_resops$NHDAREA) %>%
+      mutate(source = "NHDv2Area")
+    
+    wb <- nhd_area_vpu
+  }
+  
+  # If NHDHR feature needed
+  if (nrow(nhd_hr_wb_resops) > 0){
+    # HUC04 we need to download
+    huc04 <- substr(RC, 1, 4)
+    
+    # Download NHD HR HUC04 if we dont' have it, other wise load and
+    # Bind NHDHR waterbodies into one layer
+    hr_wb <- do.call("rbind", lapply(unique(huc04), function(x){
+      
+      if(!file.exists(file.path(data_paths$nhdplus_dir, vpu, 
+                                paste0("NHDPLUS_H_", x, "_HU4_GDB.gdb")))){
+        download_nhdplushr(data_paths$nhdplus_dir, unique(huc04))
+      }
+      
+      # Format to similar to NHDArea/Waterbody layers
+      read_sf(file.path(data_paths$nhdplus_dir, substr(vpu, 1, 2), 
+                        paste0("NHDPLUS_H_", x, "_HU4_GDB.gdb")), "NHDWaterbody")})) %>%
+      filter(NHDPlusID %in% nhd_hr_wb_resops$HR_NHDPLUSID) %>%
+      rename(COMID = NHDPlusID, GNIS_NAME = GNIS_Name, RESOLUTION = Resolution, AREASQKM = AreaSqKm, ELEVATION = Elevation,
+             FTYPE = FType, FCODE = FCode, FDATE = FDate, REACHCODE = ReachCode) %>%
+      select(-c(Permanent_Identifier, VisibilityFilter, VPUID)) %>%
+      st_zm(.) %>%
+      st_as_sf() %>%
+      mutate(source = "NHDHR")
+    
+    # Bind or create new object
+    if(exists("wb")){
+      wb <- data.table::rbindlist(list(wb, hr_wb), fill = TRUE) %>%
+        st_as_sf()
+    } else {
+      wb <- hr_wb
+    }
+  }
+  
+  # get the outlt rows from the table
+  resops_outlet <- filter(resops_wb_df, NHDAREA %in% wb$COMID | HR_NHDPLUSID %in% wb$COMID)
+  
+  # Create table of all flowlines that intersect the waterbody
+  nhd_wb <- st_intersects(nhd, wb) 
+  comid <- nhd[lengths(nhd_wb) > 0,]$COMID
+  nhd_wb_all <- nhd_wb[lengths(nhd_wb) > 0] %>%
+    purrr::set_names(comid) %>%
+    stack() %>%
+    # Ind is the default name for the set_names
+    rename(comid = ind, nhd_wb = values) %>%
+    mutate(wb_comid = wb[nhd_wb,]$COMID,
+           outlet = ifelse(comid %in% resops_outlet$FlowLcomid, "outlet", NA),
+           comid = as.integer(as.character(comid))) %>%
+    left_join(select(resops_wb_df, DAM_ID, DAM_NAME, FlowLcomid), by = c("comid" = "FlowLcomid")) %>%
+    left_join(select(st_drop_geometry(wb), COMID, source), by = c("wb_comid" = "COMID"))
+  
+  return(list(wb_table = nhd_wb_all, wb = wb))
+}
+
+#'  Creates wb inlet and outlet events for splitting in hyRefactor
+#'          for waterbodies derived from NHDArea and NHDHR waterbodies
+#'  @param WBs  (sf data.frame) return from HR_Area_coms
+#'  @param nhd_wb (sf data.frame) flowlines that intersect waterbodies
+#'  @param type (character) whether to derive inlet or outlet points
+#' 
+#'  @return (sf data.frame) dataframe of WB inlet and outlet points to split
+#'  
+WB_event <- function(WBs, nhd_wb, type){
+  # split into features and table
+  WBs_table <- WBs_VPU_areaHR$wb_table
+  WBs_layer <- WBs_VPU_areaHR$wb
+  
+  if (type == "outlet"){
+    # get the outlet comid from the ResOps Table
+    outlet_fl <- filter(nhd_wb, COMID %in% filter(WBs_table, outlet == "outlet")$comid)
+    
+    # Get the downstream flowline for continuity
+    ds_fl <- filter(nhd_wb, DnHydroseq %in% outlet_fl$Hydroseq,
+                    LevelPathI %in% outlet_fl$LevelPathI) %>%
+      rbind(outlet_fl) %>%
+      group_by(WBAREACOMI) %>%
+      # union together 
+      summarize(do_union = T)
+    
+    WBs_area <- filter(WBs_layer, source == "NHDv2Area")
+    WBs_HR <- filter(WBs_layer, source == "NHDHR")
+    
+    # For NHDArea waterbodies (better congruity with th flowline st)
+    if (nrow(WBs_area) > 0){
+      
+      # Intersect unioned FL with waterbody and get intersecting point
+      outlet_pnts <- sf::st_intersection(ds_fl, WBs_area) %>%
+        # Cast to point
+        st_cast("POINT") %>%
+        group_by(WBAREACOMI) %>%
+        # Get furthest downstream point of intersection
+        filter(row_number() == max(row_number(), na.rm = T)) %>%
+        ungroup()
+      
+      # Derive event from point to use for splitting within hyRefactor
+      wb_events <- get_flowline_index(nhd_wb, outlet_pnts) %>%
+        inner_join(select(st_drop_geometry(nhd_wb), COMID, WBAREACOMI), by = "COMID") %>%
+        mutate(event_type = type) %>%
+        cbind(select(outlet_pnts, geom)) %>%
+        st_as_sf()
+    }
+    
+    # For NHD HR waterbody outlets its a bit more complicated
+    if (nrow(WBs_HR) > 0){
+      # Get the flowlines intersecting the HR waterbody and find one with the
+      #     max DA
+      outlet_wb_int <- nhd_wb[lengths(st_intersects(nhd_wb, WBs_HR)) > 0,] %>%
+        group_by(WBAREACOMI) %>%
+        filter(TotDASqKM == max(TotDASqKM)) %>%
+        ungroup() 
+      
+      # get the ds flo with the same levepath (JIC)
+      ds_fl <- filter(nhd_wb, DnHydroseq %in% outlet_wb_int$Hydroseq, 
+                      LevelPathI %in% outlet_wb_int$LevelPathI)
+      
+      outlet_fl <- rbind(outlet_wb_int, ds_fl)
+      
+      # Cast flowlines within NHDHR waterbody to point
+      WB_FL_pnts <- outlet_wb_int %>%
+        st_cast("POINT") %>%
+        group_by(WBAREACOMI) %>%
+        mutate(pnt_id = row_number()) %>%
+        ungroup()
+      
+      # Determine which points intersect waterbody
+      WB_outlet_pnts <- WB_FL_pnts[lengths(st_intersects(WB_FL_pnts, WBs_HR)) > 0,] %>%
+        st_drop_geometry() %>%
+        group_by(WBAREACOMI) %>%
+        mutate(within_wb_id = row_number()) %>%
+        filter(within_wb_id >= max(within_wb_id)) %>%
+        ungroup() %>%
+        select(WBAREACOMI, orig_pnt_id = pnt_id, within_wb_id)
+      
+      # Deriv new linestring by concating points from most upstream point
+      #       within waterbody to downstream so we can split at FL/waterbody
+      #       nexus
+      outlet_FL <- WB_FL_pnts %>%
+        inner_join(WB_outlet_pnts, by = "WBAREACOMI") %>%
+        select(WBAREACOMI, pnt_id, orig_pnt_id, within_wb_id) %>%
+        filter(pnt_id >= orig_pnt_id) %>%
+        group_by(WBAREACOMI) %>%
+        summarize(do_union = F) %>%
+        st_cast("LINESTRING")
+      
+      # now run the intersection with the modified flowline
+      outlet_pnts <- sf::st_intersection(outlet_FL, WBs_HR) %>%
+        st_cast("POINT") %>%
+        group_by(WBAREACOMI) %>%
+        filter(row_number() == max(row_number(), na.rm = T)) %>%
+        ungroup()
+      
+      # Derive the events
+      if(exists("wb_events")){
+        hr_events <- get_flowline_index(nhd_wb, outlet_pnts) %>%
+          inner_join(select(st_drop_geometry(nhd_wb), COMID, WBAREACOMI), by = "COMID") %>%
+          mutate(event_type = type) %>%
+          cbind(select(outlet_pnts, geom)) %>%
+          st_as_sf()
+        
+        wb_events <- rbind(wb_events, hr_events)
+      } else {
+        wb_events <- get_flowline_index(nhd_wb, outlet_pnts) %>%
+          inner_join(select(st_drop_geometry(nhd_wb), COMID, WBAREACOMI), by = "COMID") %>%
+          mutate(event_type = type) %>%
+          cbind(select(outlet_pnts, geom)) %>%
+          st_as_sf()
+      }
+      
+    }
+    
+  # For inlet points its alot easier for both NHDARea and NHDHR
+  } else {
+    start_pts <- get_node(nhd_wb, position = "start") %>%
+      cbind(st_drop_geometry(nhd_wb))
+    
+    inlet_FL <- nhd_wb[lengths(st_intersects(start_pts, WBs_layer)) == 0,] %>%
+      rbind(filter(nhd_wb, Hydroseq %in% .$DnHydroseq, 
+                   LevelPathI %in% .$LevelPathI))
+    
+    inlet_ls <- inlet_FL %>%
+      group_by(LevelPathI) %>%
+      summarize(do_union = T)
+    
+    inlet_pnts <- sf::st_intersection(inlet_ls, WBs_layer) %>%
+      st_cast("POINT") %>%
+      group_by(LevelPathI) %>%
+      filter(row_number() == min(row_number())) %>%
+      ungroup() 
+    
+    wb_events <- get_flowline_index(nhd_wb, inlet_pnts) %>%
+      inner_join(select(st_drop_geometry(nhd_wb), COMID, WBAREACOMI, LevelPathI), by = "COMID") %>%
+      mutate(event_type = type) %>%
+      inner_join(select(inlet_pnts, LevelPathI), by = "LevelPathI") %>%
+      select(-LevelPathI) %>%
+      st_as_sf()
+  }
+  return(wb_events)
+} 
\ No newline at end of file
-- 
GitLab