From bf49008a0d642c8ad2553fd16f437ef81b34a8fe Mon Sep 17 00:00:00 2001 From: David Blodgett <dblodgett@usgs.gov> Date: Sat, 30 Nov 2024 21:31:22 -0600 Subject: [PATCH] waterbody outlet pois for #153 --- workspace/00_get_data.Rmd | 3 +- workspace/R/02_POI_creation_functions.R | 750 ++++++++++++++++++- workspace/R/NHD_navigate.R | 611 --------------- workspace/_targets/02_POI_creation_targets.R | 21 +- workspace/cache/data_paths.json | 1 + 5 files changed, 771 insertions(+), 615 deletions(-) diff --git a/workspace/00_get_data.Rmd b/workspace/00_get_data.Rmd index 9c1d2af..3e53781 100644 --- a/workspace/00_get_data.Rmd +++ b/workspace/00_get_data.Rmd @@ -26,7 +26,8 @@ if(!dir.exists("data")) {dir.create("data")} if(!dir.exists("bin")) {dir.create("bin")} data_dir <- "data" -out_list <- list("data_dir" = data_dir) +out_list <- list("data_dir" = data_dir, + nhdplushr_dir = file.path(data_dir, "nhdplushr")) out_file <- file.path("cache", "data_paths.json") sevenz <- "7z" diff --git a/workspace/R/02_POI_creation_functions.R b/workspace/R/02_POI_creation_functions.R index a1a371e..5de2321 100644 --- a/workspace/R/02_POI_creation_functions.R +++ b/workspace/R/02_POI_creation_functions.R @@ -379,4 +379,752 @@ create_hilarri_pois <- function(tmp_POIs, hilarri_data, wb_poi_lst, flowline, po list(tmp_POIs = tmp_POIs, wb_pois = wb_poi_lst) -} \ No newline at end of file +} + +#' create waterbody outlet pois +#' @param tmp_POIs POIs from previous step +#' @param NHDPlus_NHDArea NHDArea features from NHDPlusV2 +#' @param wb_poi_lst wb_pois from hilarri output list +#' @param resops_wb_df wb_resops from resops output list +#' @param flowline flowlines to attach pois to +#' @param events events from gage output list +#' @param nav_gpkg name of gpkg for current vpu +#' @param data_paths list of data sources +#' @param proj_crs projected crs for overall project +#' @param gages gages from gage_info_gpkg +#' @param poi_name name for poi set +create_wb_outlet_pois <- function(tmp_POIs, + NHDPlus_NHDArea, + wb_poi_lst, + resops_wb_df, + flowline, + events, + nav_gpkg, + data_paths, + proj_crs, + gages, + poi_name) { + + # Hillari IDs + hilarri_pois <- filter(tmp_POIs, !is.na(Type_hilarri)) + + # Get NHDARea polygons already define + nhd_area_vpu <- NHDPlus_NHDArea %>% + select(COMID, AREASQKM) %>% + mutate(source = "NHDv2Area") %>% + st_compatibalize(wb_poi_lst) + + # Non-nhdv2 waterbodies from resopsus + other_wbs <- filter(resops_wb_df, !wb_id %in% wb_poi_lst$wb_id, + source != "REACH") %>% + select(grand_id, member_comid = wbareacomi, resops_flowlcomid = flowlcomid, + source, accounted) %>% + left_join(select(st_drop_geometry(hilarri_pois), + COMID, hilarriid = Type_hilarri), + by = c("resops_flowlcomid" = "COMID")) %>% + group_by(resops_flowlcomid) %>% + filter(row_number() == 1) %>% + ungroup() %>% + left_join(select(nhd_area_vpu, COMID) %>% + mutate(COMID = as.character(COMID)), + by = c("member_comid" = "COMID")) %>% + st_as_sf() %>% + mutate(wb_id = ifelse(source == "NHDAREA", member_comid, NA)) %>% + st_drop_geometry() + + # copy empty geometries to wb_list for use in wb POI function + if(nrow(other_wbs) > 0){ + wb_poi_list_fn <- data.table::rbindlist(list(wb_poi_lst, + other_wbs), + fill = TRUE) %>% + distinct() %>% + st_as_sf() + } else { + wb_poi_list_fn <- wb_poi_lst + } + + # Final waterbody list with all requirements + final_wb_list <- filter(wb_poi_list_fn, + area_sqkm > wb_area_thresh | + !is.na(resops_flowlcomid) | + !is.na(hilarriid)) + + new_path <- "?prefix=StagedProducts/Hydrography/NHDPlusHR/VPU/Current/GDB/" + assign("nhdhr_file_list", new_path, envir = nhdplusTools:::nhdplusTools_env) + + # fix something here + wb_layers <- wbout_POI_creaton(filter(flowline, poi == 1), final_wb_list, + data_paths, tmp_POIs, proj_crs, wb_poi_lst, poi_name) + + wb_events <- poi_missing_wb <- wb_missing_poi <- double_wb <- double_wb_pois <- NULL + + if(!is.data.frame(wb_layers)){ + # write_sf(wb_layers$nhd_wb_table, temp_gpkg, "wb_flowpaths") + # + wbs <- wb_layers$WBs + wb_pois <- wb_layers$POIs %>% filter(!is.na(Type_WBOut)) + missing_wb <- filter(wbs, !wb_id %in% wb_pois$Type_WBOut) + print(nrow(missing_wb)) + + double_wb <- wbs %>% group_by(wb_id) %>% filter(n() > 1) + + double_wb_pois <- wb_pois %>% + group_by(Type_WBOut) %>% + filter(n() > 1) + + wb_missing_poi <- filter(wbs, + !wb_id %in% wb_pois$Type_WBOut) + + poi_missing_wb <- filter(wb_pois, !Type_WBOut %in% wb_layers$WBs$wb_id) + + #************************************* + if(all(!is.na(wb_layers$events))){ + wb_events <- wb_layers$events %>% + select(COMID, REACHCODE, REACH_meas, POI_identifier = wb_id, + event_type, nexus) %>% + st_compatibalize(., events) + + events <- rbind(events, wb_events) + } + + tmp_POIs <- wb_layers$POIs + # Write out waterbodies + WB_VPU_pois <- wb_layers$WBs + } else { + tmp_POIs <- wb_layers + WB_VPU_pois <- final_wb_list + } + + #12047928 + wb_out_col <- wb_poi_collapse(tmp_POIs, flowline, events, gages) + tmp_POIs <- wb_out_col$POIs + + if(all(!is.na(wb_out_col$events_ret))){ + events <- wb_out_col$events_ret + } + + list(tmp_POIs = tmp_POIs, wb_out_col = wb_out_col, + all_events = events, wb_events = wb_events, + double_wb = double_wb, double_wb_pois = double_wb_pois, + wb_missing_poi = wb_missing_poi, poi_missing_wb = poi_missing_wb, + updated_flowline = wb_layers$nhd_wb_table) +} + +#' Creates Waterbody POIs, calls a few other functions +#' @param nhd (sf data.frame) flowline data.frame +#' @param WBs_VPU (sf data.frame) +#' @param data_paths (list) data paths to data layers +#' @param crs (integer) CRS to use (epsg code) +#' +#' @return (sf data.frame) dataframe of waterbody outlet POIs +#' +wbout_POI_creaton <- function(nhd, wb_table, data_paths, tmp_POIs, crs, wb_poi_lst, poi_name){ + + wb_components_table <- st_drop_geometry(wb_table) %>% + dplyr::mutate(member_comid = strsplit(member_comid, ",")) %>% + tidyr::unnest(cols = member_comid) %>% + mutate(member_comid = as.integer(member_comid)) %>% + distinct() + + # R17, some wierd stuff going on in the waterbodies + if("947070203" %in% wb_components_table$member_comid){ + wb_components_table <- wb_components_table %>% + filter(!wb_id %in% c(1503847, 1513298)) + } + + # Bring wb_id to NHD data frame + if(("wb_id") %in% colnames(nhd)){ + nhd <- select(nhd, -wb_id) + } + + nhd <- nhd %>% + left_join(distinct(st_drop_geometry(wb_components_table), wb_id, member_comid) %>% + mutate(member_comid = as.numeric(member_comid)), + by = c("WBAREACOMI" = "member_comid")) + + + # Resops that are either NHDArea or MHD HR source and are missing in the + # nhd attributiong table + resops_wb_other_sources <- filter(wb_table, source == + "HR ID AVAILABLE" | (source == "NHDAREA" & + !wb_id %in% nhd$wb_id) & + accounted == 0) + + # Resops that are not accounted for and NHDv2 waterbody or NHDArea sourced + resops_main <- filter(wb_table, !is.na(resops_flowlcomid) & + !wb_id %in% resops_wb_other_sources$wb_id, + accounted == 0) + + # Remaining WB that don't have outlet COMID defined and aren't in ResOPsUS + WB_outlet <- filter(wb_table, is.na(resops_flowlcomid), + !wb_id %in% tmp_POIs$Type_WBOut) + + # Size-based waterbody outlet POIs ot in NHDArea, HR, or + # manually defined with ResOPS + wbout_COMIDs <- nhd %>% + filter(wb_id %in% WB_outlet$wb_id) %>% + group_by(wb_id) %>% + slice(which.min(Hydroseq)) %>% + ungroup() %>% + switchDiv(., nhd) %>% + select(COMID, wb_id) %>% + mutate(wbtype = "size") %>% + st_drop_geometry() %>% + distinct() + + # ResOpsUS defined outlets + resops_main_outlets <- select(st_drop_geometry(resops_main), COMID = resops_flowlcomid, + wb_id) %>% + mutate(wbtype = "resops") + + # combine together + wbout_coms <- distinct(rbind(wbout_COMIDs, resops_main_outlets)) + + # Get NHDArea and HI-Res waterbodies for ResOpsUS locaitons + WBs_VPU_areaHR <- HR_Area_coms(nhd, resops_wb_other_sources, data_paths, crs) + + # if there are no HR/Area waterbodies generated + if(all(is.na(WBs_VPU_areaHR))){ + tmp_POIs <- POI_creation(wbout_coms, + nhd, "wb_id") %>% + addType(., tmp_POIs, "wb_id", nexus = TRUE) %>% + mutate(Type_WBOut = ifelse(is.na(Type_WBOut), Type_wb_id, Type_WBOut)) %>% + select(-Type_wb_id) %>% + left_join(select(st_drop_geometry(resops_main), GRAND_ID, resops_FlowLcomid), + by = c("COMID" = "resops_FlowLcomid")) %>% + mutate(Type_resops = ifelse(!is.na(GRAND_ID), GRAND_ID, Type_resops)) %>% + select(-GRAND_ID) + } else { + # WBarea out comids + wbareaout_coms <- select(st_drop_geometry(nhd), COMID, Hydroseq) %>% + inner_join(filter(WBs_VPU_areaHR$nhd_wb_table, !is.na(outlet)), + by = c("COMID" = "comid")) %>% + group_by(wb_id) %>% + filter(Hydroseq == min(Hydroseq)) %>% #, row_number() == 1 + ungroup() %>% + inner_join(select(WBs_VPU_areaHR$wb, -source), by = "wb_id") %>% + filter(source != "NHDHR") %>% + select(COMID, wb_id) %>% + mutate(wbtype = "resops") + + if(nrow(wbareaout_coms) > 0){ + wbout_coms <- rbind(wbout_coms, wbareaout_coms) + } + + tmp_POIs <- POI_creation(select(wbout_coms, COMID, wb_id), + nhd, poi_name) %>% + addType(., tmp_POIs, poi_name, nexus = TRUE) %>% + mutate(Type_WBOut = ifelse(is.na(Type_WBOut) & (!nexus | is.na(nexus)), + Type_wb_id, Type_WBOut)) %>% + select(-Type_wb_id) %>% + left_join(select(st_drop_geometry(resops_main), grand_id, resops_flowlcomid), + by = c("COMID" = "resops_flowlcomid")) %>% + mutate(Type_resops = ifelse(!is.na(grand_id) & (!nexus | is.na(nexus)), + grand_id, Type_resops)) %>% + select(-grand_id) + } + + # If the return is not NA + if(inherits(WBs_VPU_areaHR, "list")){ + # Attribute flowlines that intersect NHDARea and/or NHD HR waterbodies with + # waterbody COMIDs + # For NHDHR, two waterbodies might be back to back and share an intersecting + # flowline, this may cause problems below, so deal with this case + WBs_VPU_areaHR$nhd_wb_table <- WBs_VPU_areaHR$nhd_wb_table %>% + left_join(select(st_drop_geometry(resops_wb_other_sources), + resops_flowlcomid, member_comid, grand_id), + by = c("comid" = "resops_flowlcomid", + "wb_comid" = "member_comid")) %>% + group_by(comid) %>% + filter(case_when(n() == 2 ~ !is.na(grand_id), + TRUE ~ n() == 1)) %>% + ungroup() + + nhd <- nhd %>% + left_join(distinct(WBs_VPU_areaHR$nhd_wb_table, comid, wb_id_hr = wb_id), + by = c("COMID" = "comid")) %>% + mutate(wb_id = ifelse(!is.na(wb_id_hr), wb_id_hr, wb_id)) %>% + select(-wb_id_hr) + + # NHD flowlines within the HR waterbodies + nhd_WBs <- filter(nhd, wb_id %in% + filter(WBs_VPU_areaHR$nhd_wb_table, source == "NHDHR")$wb_id) + + if(nrow(nhd_WBs) > 0){ + # Create outlet events for splitting HR and NHDArea waterbodies + wb_outlet_events <- WB_event(WBs_VPU_areaHR, nhd_WBs, "outlet") %>% + st_compatibalize(., tmp_POIs) %>% + mutate(nexus = TRUE, Type_resops = 2) + + tmp_POIs <- data.table::rbindlist(list(tmp_POIs, + select(wb_outlet_events, COMID, Type_WBOut = wb_id, Type_resops, nexus)), fill = TRUE) %>% + st_as_sf() + } else { + wb_outlet_events <- NA + } + + WBs_VPU_areaHR$wb <- WBs_VPU_areaHR$wb %>% + sfheaders::sf_remove_holes(.) %>% + st_compatibalize(wb_poi_lst) + + WBs_VPU_areaHR$wb <- nhdplusTools::st_compatibalize( + WBs_VPU_areaHR$wb, wb_table) + + wb_table_filtered <- wb_table %>% + filter(!st_is_empty(wb_table)) + + # Append NHDArea and NHDHR waterbodies to waterbody layer + WBs_VPU <- data.table::rbindlist(list(wb_table_filtered, WBs_VPU_areaHR$wb), + fill = TRUE) %>% + st_as_sf() + + st_geometry(WBs_VPU) <- st_make_valid(st_geometry(WBs_VPU)) + + return(list(POIs = tmp_POIs, events = wb_outlet_events, WBs = WBs_VPU, + nhd_wb_table = nhd)) + + } else { + return(list(POIs = tmp_POIs, events = NA, WBs = wb_table, + nhd_wb_table = nhd)) + } +} + +### USEDIN wbout_POI_creaton and wbin_POIcreation +#' Retrieves waterbodies from NHDArea layer or NHD Hi-Res for ResOpsUs +#' locations if absent from NHD waterbody +#' @param nhd (sf data.frame) Fowlines for given VPU +#' @param WBs (sf data.frame) waterbodiess 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, crs){ + + # Pull out rows for VPU that are NHDArea + nhd_area_resops <- WBs %>% + filter(resops_flowlcomid %in% nhd$COMID, source == "NHDAREA") + + # Pull out rows for VPU that are NHD HR + nhd_hr_wb_resops <- WBs %>% + filter(member_comid != 65000300139130) %>% # R09, in Canada + filter(resops_flowlcomid %in% nhd$COMID, source == "HR ID AVAILABLE") %>% + st_drop_geometry() + + # 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% nhd_hr_wb_resops$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$member_comid) %>% + mutate(source = "NHDv2Area") + + wb <- st_transform(nhd_area_vpu, crs) + } + + # 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){ + print(x) + vpu <- unique(substr(x, 1, 2)) + nhd_file <- list.files(data_paths$nhdplushr_dir, pattern = paste0(".*",x,".*\\.gdb$"), + recursive = TRUE, full.names = TRUE,include.dirs = TRUE) + + if(length(nhd_file) < 1) { + download_nhdplushr(data_paths$nhdplus_dir, unique(huc04)) + } + + nhd_file <- list.files(data_paths$nhdplushr_dir, pattern = paste0(".*",x,".*\\.gdb$"), + recursive = TRUE, full.names = TRUE,include.dirs = TRUE) + + if(length(nhd_file) != 1) { + stop("something wrong with nhdplushr file in ", x) + } + + # Format to similar to NHDArea/Waterbody layers + read_sf(nhd_file, "NHDWaterbody")})) #%>% + + # convert to lower case and designate new shape field + names(hr_wb) <- tolower(names(hr_wb)) + st_geometry(hr_wb) <- "shape" + + hr_wb <- filter(hr_wb, permanent_identifier %in% WBs$member_comid) %>% + rename(GNIS_NAME = gnis_name, GNIS_ID = gnis_id, + Permanent_Identifier = permanent_identifier, AREASQKM = areasqkm, + FTYPE = ftype, FCODE = fcode, FDATE = fdate, + REACHCODE = reachcode) %>% + #select(-c(permanent_identifier, visibilityfilter, vpuid)) %>% + st_zm(.) %>% + st_as_sf() %>% + mutate(source = "NHDHR", + wb_id = ifelse(!is.na(GNIS_ID), GNIS_ID, Permanent_Identifier)) %>% + st_transform(crs) + + # Bind or create new object + if(exists("wb")){ + hr_wb <- st_compatibalize(hr_wb, 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 <- read.csv("data/reservoir_data/ISTARF-CONUS.csv") %>% + # filter(WBAREACOMI %in% wb$COMID | HR_NHDPLUSID %in% wb$COMID) + + # Clear out columns not needed + WBs_sub <- WBs %>% + select(-any_of(c("COMID", "GNIS_ID"))) + + WBs_fin <- st_drop_geometry(WBs_sub) %>% + mutate(member_comid = member_comid) %>% + inner_join(select(wb, Permanent_Identifier, GNIS_ID, GNIS_NAME2 = GNIS_NAME), + by = c("member_comid" = "Permanent_Identifier")) %>% + mutate(wb_id = ifelse(GNIS_ID %in% c(NA, " "), member_comid, GNIS_ID), + GNIS_NAME = ifelse(is.na(GNIS_NAME), GNIS_NAME2, GNIS_NAME)) %>% + select(-c(GNIS_ID, GNIS_NAME2)) %>% + st_as_sf() + + # Create table of all flowlines that intersect the waterbody + nhd_wb <- st_intersects(st_transform(nhd, st_crs(wb)), 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,]$Permanent_Identifier, + outlet = ifelse(comid %in% WBs_fin$resops_flowlcomid, "outlet", NA), + comid = as.integer(as.character(comid))) %>% + #left_join(select(wb_table, DAM_ID, DAM_NAME, resops_FlowLcomid), + # by = c("comid" = "resops_FlowLcomid")) %>% + left_join(select(st_drop_geometry(wb), wb_id, Permanent_Identifier, GNIS_ID, + source), + by = c("wb_comid" = "Permanent_Identifier")) + + return(list(nhd_wb_table = nhd_wb_all, wb = WBs_fin)) +} + +### USEDIN wbout_POI_creaton and wbin_POIcreation +#' 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$nhd_wb_table + WBs_layer <- WBs$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, Hydroseq %in% outlet_fl$DnHydroseq, + LevelPathI %in% outlet_fl$LevelPathI) %>% + rbind(outlet_fl) %>% + arrange(desc(Hydroseq)) %>% + group_by(LevelPathI) %>% + # union together + summarize(do_union = TRUE) %>% + st_cast("LINESTRING") + + WBs_HR <- filter(WBs_layer, source == "HR ID AVAILABLE") + + # 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(st_transform(nhd_wb, st_crs(WBs_HR)), + WBs_HR)) > 0,] %>% + group_by(wb_id) %>% + 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(wb_id) %>% + mutate(pnt_id = row_number()) %>% + ungroup() + + outlet_pnts <- do.call("rbind", lapply(unique(WB_FL_pnts$wb_id), function(x){ + fl <- filter(WB_FL_pnts, wb_id == x) + wb <- filter(WBs_HR, wb_id == x) + + # Determine which points intersect waterbody + WB_outlet_pnts <- fl[lengths(st_intersects(fl, wb)) > 0,] %>% + st_drop_geometry() %>% + group_by(wb_id) %>% + mutate(within_wb_id = row_number()) %>% + filter(within_wb_id >= max(within_wb_id)) %>% + ungroup() %>% + select(wb_id, 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 <- fl %>% + inner_join(WB_outlet_pnts, by = "wb_id") %>% + select(wb_id, pnt_id, orig_pnt_id, within_wb_id) %>% + filter(pnt_id >= orig_pnt_id) %>% + group_by(wb_id) %>% + summarize(do_union = F) %>% + st_cast("LINESTRING") %>% + filter(wb_id %in% wb$wb_id) + + outlet_pnt <- sf::st_intersection(outlet_FL, WBs_HR) %>% + st_cast("MULTIPOINT") %>% + st_cast("POINT") %>% # was point + group_by(wb_id) %>% + filter(row_number() == max(row_number(), na.rm = T)) %>% + ungroup() %>% + mutate(id = row_number()) %>% + filter(wb_id == wb_id.1) + })) + + # Derive the events + if(exists("wb_events")){ + hr_events <- get_flowline_index(st_transform(nhd_wb, st_crs(outlet_pnts)), + outlet_pnts) %>% + inner_join(select(st_drop_geometry(nhd_wb), COMID, wb_id), by = "COMID") %>% + filter(wb_id %in% WBs_HR$wb_id) %>% + mutate(event_type = type) %>% + cbind(select(outlet_pnts, geom)) %>% + st_as_sf() %>% + st_transform(st_crs(wb_events)) %>% + st_compatibalize(., wb_events) + + wb_events <- rbind(wb_events, hr_events) %>% + distinct() + } else { + wb_events <- do.call("rbind", lapply(unique(outlet_pnts$wb_id), function(x){ + outlet_pnt <- filter(outlet_pnts, wb_id == x) + get_flowline_index(nhd_wb, outlet_pnt)})) %>% + left_join(distinct(st_drop_geometry(outlet_pnts), resops_flowlcomid, wb_id), + by = c("COMID" = "resops_flowlcomid")) %>% + 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( + st_transform(start_pts, st_crs(WBs_layer)), WBs_layer)) == 0,] %>% + rbind(filter(nhd_wb, Hydroseq %in% .$DnHydroseq, + LevelPathI %in% .$LevelPathI)) %>% + arrange(desc(Hydroseq)) %>% + unique() + + inlet_ls <- inlet_FL %>% + group_by(LevelPathI) %>% + st_cast("POINT") %>% + summarize(do_union = F) %>% + st_cast("LINESTRING") %>% + ungroup() + + inlet_pnts_int <- sf::st_intersection( + st_transform(inlet_ls, st_crs(WBs_layer)), WBs_layer) #%>% + #st_cast("LINESTRING") + + int_point <- inlet_pnts_int[st_geometry_type(inlet_pnts_int) %in% c("POINT"),] + if(nrow(int_point) > 0){ + inlet_fl <- inlet_pnts_int[st_geometry_type(inlet_pnts_int) %in% c("LINESTRING", "MULTILINESTRING"),] %>% + st_cast("LINESTRING") + inlet_pnts_int <- rbind(int_point, inlet_fl) + } + + inlet_pnts <- inlet_pnts_int %>% + group_by(LevelPathI) %>% + st_cast("POINT") %>% + mutate(id = row_number()) %>% + filter(id == min(id), !is.na(wb_id)) %>% + ungroup() + + wb_events <- get_flowline_index(st_transform(nhd_wb, st_crs(inlet_pnts)), + inlet_pnts) %>% + inner_join(select(st_drop_geometry(nhd_wb), COMID, wb_id, LevelPathI), by = "COMID") %>% + mutate(event_type = type) %>% + inner_join(select(inlet_pnts, LevelPathI), by = "LevelPathI") %>% + #group_by(COMID, LEVELPATHI) %>% + mutate(nexus = T) %>% + rename(POI_identifier = wb_id) %>% + select(-c(id, offset)) %>% + st_as_sf() + } + return(wb_events) +} + +### used as part of waterbody poi vreation in the workflow +#' Collapses POIs us/ds of waterbody POIs +#' @param (sf data.frame) rolling POI data frame +#' @param nhd (sf data.frame) nhd flowlines +#' @param events (sf data.frame) waterbody events +#' @param gages gages from gage selection +#' +#' @return (sf data.frame) dataframe of wb inlet POIs collapsed +wb_poi_collapse <- function(tmp_POIs, nhd, events, gages){ + gage_dist_node <- function(x, wb_ds_ds, gage_add, events){ + print (x) + wb_out_fl <- distinct(filter(wb_ds_ds, COMID == x)) + gage_ds <- filter(wb_ds_ds, Hydroseq %in% wb_out_fl$Hydroseq | + Hydroseq %in% wb_out_fl$DnHydroseq) + + gage_reach <- gage_ds %>% + group_by(REACHCODE) %>% + summarize(do_union = TRUE, + total_length = sum(LENGTHKM)) + + #print(nrow(gage_reach)) + gage_event <- filter(gage_add, COMID %in% gage_ds$COMID) %>% + filter(reach_meas == min(reach_meas)) + wb_event <- filter(events, COMID == x, event_type == "outlet") %>% + unique() + + if(nrow(gage_reach) == 0){ + print("no gages") + } + + if(nrow(gage_event) == 0){ + return("no events") + } else if(gage_event$COMID != wb_out_fl$COMID) { + gage_reach <- gage_reach %>% + filter(REACHCODE == unique(gage_event$reachcode)) %>% + mutate(gage_dist = ifelse(gage_event$nexus == TRUE, + total_length * (1 - (gage_event$reach_meas/100)), + total_length)) %>% + mutate(gage_comid = gage_event$COMID, + wbout_comid = x) + } else if(gage_event$COMID == wb_out_fl$COMID){ + if(nrow(wb_event) >0){ + wb_out_meas <- min(wb_event$REACH_meas) + wb_RC <- wb_event$REACHCODE + } else { + wb_out_meas <- min(wb_out_fl$FromMeas) + wb_RC <- wb_out_fl$REACHCODE + } + + # gage reach + gage_reach <- gage_reach %>% + filter(REACHCODE == gage_event$reachcode) %>% + mutate(gage_dist = total_length * (1 - (gage_event$reach_meas/100))) + + # wb info + wb_reach <- gage_reach %>% + filter(REACHCODE == unique(wb_RC)) %>% + mutate(wb_dist = total_length * (1 - (wb_out_meas /100))) + + gage_reach <- gage_reach %>% + mutate(gage_dist = abs(wb_reach$wb_dist - gage_dist), + gage_comid = gage_event$COMID, + wbout_comid = x) + } + } + + # Previously identified streamgages within Gage_Selection.Rmd + streamgages_VPU <- gages %>% + rename(COMID = comid) %>% + filter(COMID %in% nhd$COMID) %>% + switchDiv(., nhd) + + # get waterbody outlets + wb_out <- filter(tmp_POIs, !is.na(Type_WBOut), is.na(Type_Gages)) + + wb_out_nexus <- filter(wb_out, nexus) + wb_out_nhd <- filter(nhd, COMID %in% wb_out$COMID) + wb_ds_ds <- wb_out_nhd %>% + rbind(filter(nhd, LevelPathI %in% .$LevelPathI & Hydroseq %in% .$DnHydroseq)) + + # get gages + gage_wb <- filter(tmp_POIs, !is.na(Type_Gages) & is.na(Type_WBOut)) #%>% + # filter(COMID %in% wb_ds_ds$COMID) + #gage_nhd <- filter(nhd, COMID %in% gage_wb$COMID) + #gage_node <- filter(gage_wb, nexus == FALSE) + #gage_nexus <- filter(gage_wb, nexus == TRUE) + + gage_add <- filter(streamgages_VPU, site_no %in% gage_wb$Type_Gages) %>% + select(COMID, reachcode, reach_meas, site_no) %>% + inner_join(select(st_drop_geometry(gage_wb), site_no = Type_Gages, nexus), + by = "site_no") %>% + distinct() + + # 8693865 + output <- lapply(wb_out$COMID, + gage_dist_node, wb_ds_ds, gage_add, events) + output_length <- output[lengths(output) > 1] + + if(length(output_length) == 0){ + return(list(POIs = tmp_POIs, events_ret = NA)) + } + + output_full <- do.call("rbind", output[lengths(output) > 1]) %>% + filter(gage_dist < 1) %>% + left_join(select(st_drop_geometry(nhd), COMID, TotDASqKM_WB = TotDASqKM), + by = c("wbout_comid" = "COMID")) %>% + left_join(select(st_drop_geometry(nhd), COMID, TotDASqKM_gage = TotDASqKM), + by = c("gage_comid" = "COMID")) %>% + mutate(DAR = TotDASqKM_WB / TotDASqKM_gage) %>% + filter(gage_dist < 1, DAR > .975) %>% + st_drop_geometry() + + gage_POI <- filter(tmp_POIs, COMID %in% output_full$gage_comid) %>% + select(COMID, Type_HUC12_ds = Type_HUC12, Type_Gages_ds = Type_Gages, + Type_TE_ds = Type_TE, nexus) %>% + st_drop_geometry() %>% + group_by(COMID) %>% + summarise(Type_HUC12_ds = last(na.omit(Type_HUC12_ds)), + Type_Gages_ds = last(na.omit(Type_Gages_ds)), + Type_TE_ds = last(na.omit(Type_TE_ds)), + nexus = last(na.omit(nexus))) + + WB_POI <- filter(tmp_POIs, COMID %in% output_full$wbout_comid, !is.na(Type_WBOut)) %>% + inner_join(select(output_full, gage_comid, wbout_comid), by = c("COMID" = "wbout_comid")) %>% + inner_join(select(gage_POI, -nexus), by = c("gage_comid" = "COMID")) %>% + mutate(Type_HUC12 = ifelse(!is.na(Type_HUC12_ds), Type_HUC12_ds, Type_HUC12), + Type_Gages = ifelse(!is.na(Type_Gages_ds), Type_Gages_ds, Type_Gages), + Type_TE = ifelse(!is.na(Type_TE_ds), Type_TE_ds, Type_TE)) %>% + select(-c(Type_HUC12_ds, Type_Gages_ds, Type_TE_ds)) + + tmp_POIs_fin <- filter(tmp_POIs, !COMID %in% c(WB_POI$COMID, WB_POI$gage_comid)) %>% + rbind(select(WB_POI, -gage_comid)) + + if(any(gage_POI$nexus == TRUE)){ + gage_events <- filter(gage_POI, nexus == TRUE) + events <- filter(events, !POI_identifier %in% gage_events$Type_Gages_ds) + } + + + return(list(POIs = tmp_POIs_fin, events_ret = events)) +} diff --git a/workspace/R/NHD_navigate.R b/workspace/R/NHD_navigate.R index 9d6764b..b00ffa9 100644 --- a/workspace/R/NHD_navigate.R +++ b/workspace/R/NHD_navigate.R @@ -508,477 +508,7 @@ split_elev <- function(in_POI_ID, split_DF, elev_diff, max_DA){ return(split_elev_DF) } -### USEDIN wbout_POI_creaton and wbin_POIcreation -#' Retrieves waterbodies from NHDArea layer or NHD Hi-Res for ResOpsUs -#' locations if absent from NHD waterbody -#' @param nhd (sf data.frame) Fowlines for given VPU -#' @param WBs (sf data.frame) waterbodiess 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, crs){ - - # Pull out rows for VPU that are NHDArea - nhd_area_resops <- WBs %>% - filter(resops_flowlcomid %in% nhd$COMID, source == "NHDAREA") - - # Pull out rows for VPU that are NHD HR - nhd_hr_wb_resops <- WBs %>% - filter(member_comid != 65000300139130) %>% # R09, in Canada - filter(resops_flowlcomid %in% nhd$COMID, source == "HR ID AVAILABLE") %>% - st_drop_geometry() - - # 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% nhd_hr_wb_resops$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$member_comid) %>% - mutate(source = "NHDv2Area") - - wb <- st_transform(nhd_area_vpu, crs) - } - - # 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){ - print(x) - vpu <- unique(substr(x, 1, 2)) - - 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)) - } - - gdb <- list.files(file.path(data_paths$nhdplus_dir, substr(vpu, 1, 2)), - pattern = paste0("_", x, "_.+gdb"), full.names = TRUE) - - # Format to similar to NHDArea/Waterbody layers - read_sf(gdb, "NHDWaterbody")})) #%>% - - # convert to lower case and designate new shape field - names(hr_wb) <- tolower(names(hr_wb)) - st_geometry(hr_wb) <- "shape" - - hr_wb <- filter(hr_wb, permanent_identifier %in% WBs$member_comid) %>% - rename(GNIS_NAME = gnis_name, GNIS_ID = gnis_id, - Permanent_Identifier = permanent_identifier, AREASQKM = areasqkm, - FTYPE = ftype, FCODE = fcode, FDATE = fdate, - REACHCODE = reachcode) %>% - #select(-c(permanent_identifier, visibilityfilter, vpuid)) %>% - st_zm(.) %>% - st_as_sf() %>% - mutate(source = "NHDHR", - wb_id = ifelse(!is.na(GNIS_ID), GNIS_ID, Permanent_Identifier)) %>% - st_transform(crs) - - # Bind or create new object - if(exists("wb")){ - hr_wb <- st_compatibalize(hr_wb, 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 <- read.csv("data/reservoir_data/ISTARF-CONUS.csv") %>% - # filter(WBAREACOMI %in% wb$COMID | HR_NHDPLUSID %in% wb$COMID) - - # Clear out columns not needed - WBs_sub <- WBs %>% - select(-any_of(c("COMID", "GNIS_ID"))) - - WBs_fin <- st_drop_geometry(WBs_sub) %>% - mutate(member_comid = member_comid) %>% - inner_join(select(wb, Permanent_Identifier, GNIS_ID, GNIS_NAME2 = GNIS_NAME), - by = c("member_comid" = "Permanent_Identifier")) %>% - mutate(wb_id = ifelse(GNIS_ID %in% c(NA, " "), member_comid, GNIS_ID), - GNIS_NAME = ifelse(is.na(GNIS_NAME), GNIS_NAME2, GNIS_NAME)) %>% - select(-c(GNIS_ID, GNIS_NAME2)) %>% - st_as_sf() - - # Create table of all flowlines that intersect the waterbody - nhd_wb <- st_intersects(st_transform(nhd, st_crs(wb)), 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,]$Permanent_Identifier, - outlet = ifelse(comid %in% WBs_fin$resops_flowlcomid, "outlet", NA), - comid = as.integer(as.character(comid))) %>% - #left_join(select(wb_table, DAM_ID, DAM_NAME, resops_FlowLcomid), - # by = c("comid" = "resops_FlowLcomid")) %>% - left_join(select(st_drop_geometry(wb), wb_id, Permanent_Identifier, GNIS_ID, - source), - by = c("wb_comid" = "Permanent_Identifier")) - - return(list(nhd_wb_table = nhd_wb_all, wb = WBs_fin)) -} - -### USEDIN wbout_POI_creaton and wbin_POIcreation -#' 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$nhd_wb_table - WBs_layer <- WBs$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, Hydroseq %in% outlet_fl$DnHydroseq, - LevelPathI %in% outlet_fl$LevelPathI) %>% - rbind(outlet_fl) %>% - arrange(desc(Hydroseq)) %>% - group_by(LevelPathI) %>% - # union together - summarize(do_union = TRUE) %>% - st_cast("LINESTRING") - - WBs_HR <- filter(WBs_layer, source == "HR ID AVAILABLE") - - # 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(st_transform(nhd_wb, st_crs(WBs_HR)), - WBs_HR)) > 0,] %>% - group_by(wb_id) %>% - 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(wb_id) %>% - mutate(pnt_id = row_number()) %>% - ungroup() - - outlet_pnts <- do.call("rbind", lapply(unique(WB_FL_pnts$wb_id), function(x){ - fl <- filter(WB_FL_pnts, wb_id == x) - wb <- filter(WBs_HR, wb_id == x) - - # Determine which points intersect waterbody - WB_outlet_pnts <- fl[lengths(st_intersects(fl, wb)) > 0,] %>% - st_drop_geometry() %>% - group_by(wb_id) %>% - mutate(within_wb_id = row_number()) %>% - filter(within_wb_id >= max(within_wb_id)) %>% - ungroup() %>% - select(wb_id, 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 <- fl %>% - inner_join(WB_outlet_pnts, by = "wb_id") %>% - select(wb_id, pnt_id, orig_pnt_id, within_wb_id) %>% - filter(pnt_id >= orig_pnt_id) %>% - group_by(wb_id) %>% - summarize(do_union = F) %>% - st_cast("LINESTRING") %>% - filter(wb_id %in% wb$wb_id) - - outlet_pnt <- sf::st_intersection(outlet_FL, WBs_HR) %>% - st_cast("MULTIPOINT") %>% - st_cast("POINT") %>% # was point - group_by(wb_id) %>% - filter(row_number() == max(row_number(), na.rm = T)) %>% - ungroup() %>% - mutate(id = row_number()) %>% - filter(wb_id == wb_id.1) - })) - - # Derive the events - if(exists("wb_events")){ - hr_events <- get_flowline_index(st_transform(nhd_wb, st_crs(outlet_pnts)), - outlet_pnts) %>% - inner_join(select(st_drop_geometry(nhd_wb), COMID, wb_id), by = "COMID") %>% - filter(wb_id %in% WBs_HR$wb_id) %>% - mutate(event_type = type) %>% - cbind(select(outlet_pnts, geom)) %>% - st_as_sf() %>% - st_transform(st_crs(wb_events)) %>% - st_compatibalize(., wb_events) - - wb_events <- rbind(wb_events, hr_events) %>% - distinct() - } else { - wb_events <- do.call("rbind", lapply(unique(outlet_pnts$wb_id), function(x){ - outlet_pnt <- filter(outlet_pnts, wb_id == x) - get_flowline_index(nhd_wb, outlet_pnt)})) %>% - left_join(distinct(st_drop_geometry(outlet_pnts), resops_flowlcomid, wb_id), - by = c("COMID" = "resops_flowlcomid")) %>% - 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( - st_transform(start_pts, st_crs(WBs_layer)), WBs_layer)) == 0,] %>% - rbind(filter(nhd_wb, Hydroseq %in% .$DnHydroseq, - LevelPathI %in% .$LevelPathI)) %>% - arrange(desc(Hydroseq)) %>% - unique() - - inlet_ls <- inlet_FL %>% - group_by(LevelPathI) %>% - st_cast("POINT") %>% - summarize(do_union = F) %>% - st_cast("LINESTRING") %>% - ungroup() - - inlet_pnts_int <- sf::st_intersection( - st_transform(inlet_ls, st_crs(WBs_layer)), WBs_layer) #%>% - #st_cast("LINESTRING") - - int_point <- inlet_pnts_int[st_geometry_type(inlet_pnts_int) %in% c("POINT"),] - if(nrow(int_point) > 0){ - inlet_fl <- inlet_pnts_int[st_geometry_type(inlet_pnts_int) %in% c("LINESTRING", "MULTILINESTRING"),] %>% - st_cast("LINESTRING") - inlet_pnts_int <- rbind(int_point, inlet_fl) - } - - inlet_pnts <- inlet_pnts_int %>% - group_by(LevelPathI) %>% - st_cast("POINT") %>% - mutate(id = row_number()) %>% - filter(id == min(id), !is.na(wb_id)) %>% - ungroup() - wb_events <- get_flowline_index(st_transform(nhd_wb, st_crs(inlet_pnts)), - inlet_pnts) %>% - inner_join(select(st_drop_geometry(nhd_wb), COMID, wb_id, LevelPathI), by = "COMID") %>% - mutate(event_type = type) %>% - inner_join(select(inlet_pnts, LevelPathI), by = "LevelPathI") %>% - #group_by(COMID, LEVELPATHI) %>% - mutate(nexus = T) %>% - rename(POI_identifier = wb_id) %>% - select(-c(id, offset)) %>% - st_as_sf() - } - return(wb_events) -} - -#' Creates Waterbody POIs, calls a few other functions -#' @param nhd (sf data.frame) flowline data.frame -#' @param WBs_VPU (sf data.frame) -#' @param data_paths (list) data paths to data layers -#' @param crs (integer) CRS to use (epsg code) -#' -#' @return (sf data.frame) dataframe of waterbody outlet POIs -#' -wbout_POI_creaton <- function(nhd, wb_table, data_paths, tmp_POIs, crs){ - - wb_components_table <- st_drop_geometry(wb_table) %>% - dplyr::mutate(member_comid = strsplit(member_comid, ",")) %>% - tidyr::unnest(cols = member_comid) %>% - mutate(member_comid = as.integer(member_comid)) %>% - distinct() - - # R17, some wierd stuff going on in the waterbodies - if("947070203" %in% wb_components_table$member_comid){ - wb_components_table <- wb_components_table %>% - filter(!wb_id %in% c(1503847, 1513298)) - } - - # Bring wb_id to NHD data frame - if(("wb_id") %in% colnames(nhd)){ - nhd <- select(nhd, -wb_id) - } - - nhd <- nhd %>% - left_join(distinct(st_drop_geometry(wb_components_table), wb_id, member_comid) %>% - mutate(member_comid = as.numeric(member_comid)), - by = c("WBAREACOMI" = "member_comid")) - - - # Resops that are either NHDArea or MHD HR source and are missing in the - # nhd attributiong table - resops_wb_other_sources <- filter(wb_table, source == - "HR ID AVAILABLE" | (source == "NHDAREA" & - !wb_id %in% nhd$wb_id) & - accounted == 0) - - # Resops that are not accounted for and NHDv2 waterbody or NHDArea sourced - resops_main <- filter(wb_table, !is.na(resops_flowlcomid) & - !wb_id %in% resops_wb_other_sources$wb_id, - accounted == 0) - - # Remaining WB that don't have outlet COMID defined and aren't in ResOPsUS - WB_outlet <- filter(wb_table, is.na(resops_flowlcomid), - !wb_id %in% tmp_POIs$Type_WBOut) - - # Size-based waterbody outlet POIs ot in NHDArea, HR, or - # manually defined with ResOPS - wbout_COMIDs <- nhd %>% - filter(wb_id %in% WB_outlet$wb_id) %>% - group_by(wb_id) %>% - slice(which.min(Hydroseq)) %>% - ungroup() %>% - switchDiv(., nhd) %>% - select(COMID, wb_id) %>% - mutate(wbtype = "size") %>% - st_drop_geometry() %>% - distinct() - - # ResOpsUS defined outlets - resops_main_outlets <- select(st_drop_geometry(resops_main), COMID = resops_flowlcomid, - wb_id) %>% - mutate(wbtype = "resops") - - # combine together - wbout_coms <- distinct(rbind(wbout_COMIDs, resops_main_outlets)) - - # Get NHDArea and HI-Res waterbodies for ResOpsUS locaitons - WBs_VPU_areaHR <- HR_Area_coms(nhd, resops_wb_other_sources, data_paths, crs) - - # if there are no HR/Area waterbodies generated - if(all(is.na(WBs_VPU_areaHR))){ - tmp_POIs <- POI_creation(wbout_coms, - nhd, "wb_id") %>% - addType(., tmp_POIs, "wb_id", nexus = TRUE) %>% - mutate(Type_WBOut = ifelse(is.na(Type_WBOut), Type_wb_id, Type_WBOut)) %>% - select(-Type_wb_id) %>% - left_join(select(st_drop_geometry(resops_main), GRAND_ID, resops_FlowLcomid), - by = c("COMID" = "resops_FlowLcomid")) %>% - mutate(Type_resops = ifelse(!is.na(GRAND_ID), GRAND_ID, Type_resops)) %>% - select(-GRAND_ID) - } else { - # WBarea out comids - wbareaout_coms <- select(st_drop_geometry(nhd), COMID, Hydroseq) %>% - inner_join(filter(WBs_VPU_areaHR$nhd_wb_table, !is.na(outlet)), - by = c("COMID" = "comid")) %>% - group_by(wb_id) %>% - filter(Hydroseq == min(Hydroseq)) %>% #, row_number() == 1 - ungroup() %>% - inner_join(select(WBs_VPU_areaHR$wb, -source), by = "wb_id") %>% - filter(source != "NHDHR") %>% - select(COMID, wb_id) %>% - mutate(wbtype = "resops") - - if(nrow(wbareaout_coms) > 0){ - wbout_coms <- rbind(wbout_coms, wbareaout_coms) - } - - tmp_POIs <- POI_creation(select(wbout_coms, COMID, wb_id), - nhd, "wb_id") %>% - addType(., tmp_POIs, "wb_id", nexus = TRUE) %>% - mutate(Type_WBOut = ifelse(is.na(Type_WBOut) & (!nexus | is.na(nexus)), - Type_wb_id, Type_WBOut)) %>% - select(-Type_wb_id) %>% - left_join(select(st_drop_geometry(resops_main), grand_id, resops_flowlcomid), - by = c("COMID" = "resops_flowlcomid")) %>% - mutate(Type_resops = ifelse(!is.na(grand_id) & (!nexus | is.na(nexus)), - grand_id, Type_resops)) %>% - select(-grand_id) - } - - # If the return is not NA - if(inherits(WBs_VPU_areaHR, "list")){ - # Attribute flowlines that intersect NHDARea and/or NHD HR waterbodies with - # waterbody COMIDs - # For NHDHR, two waterbodies might be back to back and share an intersecting - # flowline, this may cause problems below, so deal with this case - WBs_VPU_areaHR$nhd_wb_table <- WBs_VPU_areaHR$nhd_wb_table %>% - left_join(select(st_drop_geometry(resops_wb_other_sources), - resops_flowlcomid, member_comid, grand_id), - by = c("comid" = "resops_flowlcomid", - "wb_comid" = "member_comid")) %>% - group_by(comid) %>% - filter(case_when(n() == 2 ~ !is.na(grand_id), - TRUE ~ n() == 1)) %>% - ungroup() - - nhd <- nhd %>% - left_join(distinct(WBs_VPU_areaHR$nhd_wb_table, comid, wb_id_hr = wb_id), - by = c("COMID" = "comid")) %>% - mutate(wb_id = ifelse(!is.na(wb_id_hr), wb_id_hr, wb_id)) %>% - select(-wb_id_hr) - - # NHD flowlines within the HR waterbodies - nhd_WBs <- filter(nhd, wb_id %in% - filter(WBs_VPU_areaHR$nhd_wb_table, source == "NHDHR")$wb_id) - - if(nrow(nhd_WBs) > 0){ - # Create outlet events for splitting HR and NHDArea waterbodies - wb_outlet_events <- WB_event(WBs_VPU_areaHR, nhd_WBs, "outlet") %>% - st_compatibalize(., tmp_POIs) %>% - mutate(nexus = TRUE, Type_resops = 2) - - tmp_POIs <- data.table::rbindlist(list(tmp_POIs, - select(wb_outlet_events, COMID, Type_WBOut = wb_id, Type_resops, nexus)), fill = TRUE) %>% - st_as_sf() - } else { - wb_outlet_events <- NA - } - - WBs_VPU_areaHR$wb <- WBs_VPU_areaHR$wb %>% - sfheaders::sf_remove_holes(.) %>% - st_compatibalize(wb_poi_lst) - - WBs_VPU_areaHR$wb <- nhdplusTools::st_compatibalize( - WBs_VPU_areaHR$wb, wb_table) - - wb_table_filtered <- wb_table %>% - filter(!st_is_empty(wb_table)) - - # Append NHDArea and NHDHR waterbodies to waterbody layer - WBs_VPU <- data.table::rbindlist(list(wb_table_filtered, WBs_VPU_areaHR$wb), - fill = TRUE) %>% - st_as_sf() - - st_geometry(WBs_VPU) <- st_make_valid(st_geometry(WBs_VPU)) - - return(list(POIs = tmp_POIs, events = wb_outlet_events, WBs = WBs_VPU, - nhd_wb_table = nhd)) - - } else { - return(list(POIs = tmp_POIs, events = NA, WBs = wb_table, - nhd_wb_table = nhd)) - } -} #' Creates Waterbody POIs, calls a few other functions #' @param nhd (sf data.frame) flowline data.frame @@ -1286,147 +816,6 @@ wb_inlet_collapse <- function(tmp_POIs, nhd, events){ } } -### used as part of waterbody poi vreation in the workflow -#' Collapses POIs us/ds of waterbody POIs -#' @param (sf data.frame) rolling POI data frame -#' @param nhd (sf data.frame) nhd flowlines -#' @param events (sf data.frame) waterbody events -#' -#' @return (sf data.frame) dataframe of wb inlet POIs collapsed -wb_poi_collapse <- function(tmp_POIs, nhd, events){ - gage_dist_node <- function(x, wb_ds_ds, gage_add, events){ - print (x) - wb_out_fl <- distinct(filter(wb_ds_ds, COMID == x)) - gage_ds <- filter(wb_ds_ds, Hydroseq %in% wb_out_fl$Hydroseq | - Hydroseq %in% wb_out_fl$DnHydroseq) - - gage_reach <- gage_ds %>% - group_by(REACHCODE) %>% - summarize(do_union = TRUE, - total_length = sum(LENGTHKM)) - - #print(nrow(gage_reach)) - gage_event <- filter(gage_add, COMID %in% gage_ds$COMID) %>% - filter(reach_meas == min(reach_meas)) - wb_event <- filter(events, COMID == x, event_type == "outlet") %>% - unique() - - if(nrow(gage_reach) == 0){ - print("no gages") - } - - if(nrow(gage_event) == 0){ - return("no events") - } else if(gage_event$COMID != wb_out_fl$COMID) { - gage_reach <- gage_reach %>% - filter(REACHCODE == unique(gage_event$reachcode)) %>% - mutate(gage_dist = ifelse(gage_event$nexus == TRUE, - total_length * (1 - (gage_event$reach_meas/100)), - total_length)) %>% - mutate(gage_comid = gage_event$COMID, - wbout_comid = x) - } else if(gage_event$COMID == wb_out_fl$COMID){ - if(nrow(wb_event) >0){ - wb_out_meas <- min(wb_event$REACH_meas) - wb_RC <- wb_event$REACHCODE - } else { - wb_out_meas <- min(wb_out_fl$FromMeas) - wb_RC <- wb_out_fl$REACHCODE - } - - # gage reach - gage_reach <- gage_reach %>% - filter(REACHCODE == gage_event$reachcode) %>% - mutate(gage_dist = total_length * (1 - (gage_event$reach_meas/100))) - - # wb info - wb_reach <- gage_reach %>% - filter(REACHCODE == unique(wb_RC)) %>% - mutate(wb_dist = total_length * (1 - (wb_out_meas /100))) - - gage_reach <- gage_reach %>% - mutate(gage_dist = abs(wb_reach$wb_dist - gage_dist), - gage_comid = gage_event$COMID, - wbout_comid = x) - } - } - - # Previously identified streamgages within Gage_Selection.Rmd - streamgages_VPU <- gages %>% - rename(COMID = comid) %>% - filter(COMID %in% nhd$COMID) %>% - switchDiv(., nhd) - - # get waterbody outlets - wb_out <- filter(tmp_POIs, !is.na(Type_WBOut), is.na(Type_Gages)) - - wb_out_nexus <- filter(wb_out, nexus) - wb_out_nhd <- filter(nhd, COMID %in% wb_out$COMID) - wb_ds_ds <- wb_out_nhd %>% - rbind(filter(nhd, LevelPathI %in% .$LevelPathI & Hydroseq %in% .$DnHydroseq)) - - # get gages - gage_wb <- filter(tmp_POIs, !is.na(Type_Gages) & is.na(Type_WBOut)) #%>% - # filter(COMID %in% wb_ds_ds$COMID) - #gage_nhd <- filter(nhd, COMID %in% gage_wb$COMID) - #gage_node <- filter(gage_wb, nexus == FALSE) - #gage_nexus <- filter(gage_wb, nexus == TRUE) - - gage_add <- filter(streamgages_VPU, site_no %in% gage_wb$Type_Gages) %>% - select(COMID, reachcode, reach_meas, site_no) %>% - inner_join(select(st_drop_geometry(gage_wb), site_no = Type_Gages, nexus), - by = "site_no") %>% - distinct() - - # 8693865 - output <- lapply(wb_out$COMID, - gage_dist_node, wb_ds_ds, gage_add, events) - output_length <- output[lengths(output) > 1] - - if(length(output_length) == 0){ - return(list(POIs = tmp_POIs, events_ret = NA)) - } - - output_full <- do.call("rbind", output[lengths(output) > 1]) %>% - filter(gage_dist < 1) %>% - left_join(select(st_drop_geometry(nhd), COMID, TotDASqKM_WB = TotDASqKM), - by = c("wbout_comid" = "COMID")) %>% - left_join(select(st_drop_geometry(nhd), COMID, TotDASqKM_gage = TotDASqKM), - by = c("gage_comid" = "COMID")) %>% - mutate(DAR = TotDASqKM_WB / TotDASqKM_gage) %>% - filter(gage_dist < 1, DAR > .975) %>% - st_drop_geometry() - - gage_POI <- filter(tmp_POIs, COMID %in% output_full$gage_comid) %>% - select(COMID, Type_HUC12_ds = Type_HUC12, Type_Gages_ds = Type_Gages, - Type_TE_ds = Type_TE, nexus) %>% - st_drop_geometry() %>% - group_by(COMID) %>% - summarise(Type_HUC12_ds = last(na.omit(Type_HUC12_ds)), - Type_Gages_ds = last(na.omit(Type_Gages_ds)), - Type_TE_ds = last(na.omit(Type_TE_ds)), - nexus = last(na.omit(nexus))) - - WB_POI <- filter(tmp_POIs, COMID %in% output_full$wbout_comid, !is.na(Type_WBOut)) %>% - inner_join(select(output_full, gage_comid, wbout_comid), by = c("COMID" = "wbout_comid")) %>% - inner_join(select(gage_POI, -nexus), by = c("gage_comid" = "COMID")) %>% - mutate(Type_HUC12 = ifelse(!is.na(Type_HUC12_ds), Type_HUC12_ds, Type_HUC12), - Type_Gages = ifelse(!is.na(Type_Gages_ds), Type_Gages_ds, Type_Gages), - Type_TE = ifelse(!is.na(Type_TE_ds), Type_TE_ds, Type_TE)) %>% - select(-c(Type_HUC12_ds, Type_Gages_ds, Type_TE_ds)) - - tmp_POIs_fin <- filter(tmp_POIs, !COMID %in% c(WB_POI$COMID, WB_POI$gage_comid)) %>% - rbind(select(WB_POI, -gage_comid)) - - if(any(gage_POI$nexus == TRUE)){ - gage_events <- filter(gage_POI, nexus == TRUE) - events <- filter(events, !POI_identifier %in% gage_events$Type_Gages_ds) - } - - - return(list(POIs = tmp_POIs_fin, events_ret = events)) -} - ### used when poi_move.R is sourced in POI collapse phase of workflow ### consider calling at the end of each poi creation process? #' Collapses POIs together based on criteria diff --git a/workspace/_targets/02_POI_creation_targets.R b/workspace/_targets/02_POI_creation_targets.R index e7d511f..8a1d8f5 100644 --- a/workspace/_targets/02_POI_creation_targets.R +++ b/workspace/_targets/02_POI_creation_targets.R @@ -1,6 +1,6 @@ library(targets) -tar_option_set(packages = c("dplyr", "sf", "hyfabric", "hydroloom"), +tar_option_set(packages = c("dplyr", "sf", "hyfabric", "hydroloom", "nhdplusTools"), memory = "transient", garbage_collection = TRUE, error = "null", storage = "worker", retrieval = "worker", deployment = "main") @@ -63,5 +63,22 @@ list(tar_target(data_paths_file, "cache/data_paths.json", format = "file"), hilarri_data, resops_pois$wb_pois, flowline, "hilarri"), - pattern = map(resops_pois, flowline), deployment = "worker") + pattern = map(resops_pois, flowline), deployment = "worker"), + # TODO: work on cleaning up parameters of this function + tar_target(wb_outlet_pois, create_wb_outlet_pois(hilarri_pois$tmp_POIs, + filter(combined_waterbodies, layer == "NHDArea"), + hilarri_pois$wb_pois, + resops_pois$wb_resops, + flowline, + gage_pois$events, + nav_gpkg, + data_paths, + proj_crs, + read_sf(gage_info_gpkg_file, "Gages"), + "wb_id"), + pattern = map(hilarri_pois, resops_pois, gage_pois,flowline, nav_gpkg), + deployment = "worker"), + tar_target(write_wb_flowline_mod, write_sf(wb_outlet_pois$updated_flowline, + nav_gpkg, nhd_flowline), + pattern = map(wb_outlet_pois, nav_gpkg)) ) \ No newline at end of file diff --git a/workspace/cache/data_paths.json b/workspace/cache/data_paths.json index f4eeb12..bfdbba1 100644 --- a/workspace/cache/data_paths.json +++ b/workspace/cache/data_paths.json @@ -1,5 +1,6 @@ { "data_dir": "data", + "nhdplushr_dir": "data/nhdplushr", "hu12_points_path": "data/102020wbd_outlets.gpkg", "SWIM_points_path": "data/SWIM_gage_loc", "SWIM_dbf": "data/SWIMGFinfo.dbf", -- GitLab