Repository: USEPA/EPATADA
Pull request #592: 590 change order of tada findnearbysites to increase efficiency
Author: login: hillarymarler
Created: 2025-05-08T21:32:13Z | Merged: 2025-05-22T00:13:09Z

With this test data set:

test <-TADA_DataRetrieval(statecode = "IL",
                             startDate = "2018-06-01",
                             endDate = "2018-09-30",
                             ask = FALSE)

This update workflow which involves grouping by buffer distance first, then only fetching NHD info for sites within the buffer distance took 2.7 min.

The old workflow which fetched all NHD catchment info, grouped by nhdplusid, then looked at buffer took 18.9 min with this data set.

The same number of nearby site results are identified in each approach. I will do some additional testing to ensure that it is the same results.


Diff:
diff --git a/NAMESPACE b/NAMESPACE
index a2ce702ff..6dcdd97e6 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -67,6 +67,7 @@ export(TADA_IDCensoredData)
 export(TADA_IDDepthProfiles)
 export(TADA_JoinWQPProfiles)
 export(TADA_MakeSpatial)
+export(TADA_NearbySitesMap)
 export(TADA_OrderCols)
 export(TADA_OverviewMap)
 export(TADA_PairForCriteriaCalc)
diff --git a/R/Figures.R b/R/Figures.R
index 0750dc55c..40190dd90 100644
--- a/R/Figures.R
+++ b/R/Figures.R
@@ -585,8 +585,6 @@ TADA_OverviewMap <- function(.data) {
 #' 1) imprecise coordinates - latitudes and/or longitudes that contain fewer
 #'    then 3 decimal places.
 #' 2) outside USA - coordinates that fall outside the bounds of the USA.
-#' 3) near other sites - groups of sites that are spatially located within
-#'    a threshold distance (defaulting to 100 m) from each other.
 #'
 #' @export
 #'
@@ -607,13 +605,9 @@ TADA_FlaggedSitesMap <- function(.data) {
   invalid <- TADA_FlagCoordinates(.data, flaggedonly = TRUE)
   lowres <- invalid[invalid$TADA.SuspectCoordinates.Flag == "Imprecise_lessthan3decimaldigits", ]
   outsideusa <- invalid[invalid$TADA.SuspectCoordinates.Flag %in% c("LAT_OutsideUSA", "LONG_OutsideUSA"), ]
-  nearby <- TADA_FindNearbySites(.data)
-  print(colnames(nearby))
-  nearby <- TADA_GetUniqueNearbySites(nearby)
 
   lowresIcon <- leaflet::makeAwesomeIcon(icon = "circle", library = "fa", iconColor = "#ffffff", markerColor = "green")
   outsideIcon <- leaflet::makeAwesomeIcon(icon = "circle", library = "fa", iconColor = "#ffffff", markerColor = "darkblue")
-  nearbyIcon <- leaflet::makeAwesomeIcon(icon = "circle", library = "fa", iconColor = "#ffffff", markerColor = "pink")
 
   map <- leaflet::leaflet() %>%
     leaflet::addProviderTiles("Esri.WorldTopoMap", group = "World topo", options = leaflet::providerTileOptions(updateWhenZooming = FALSE, updateWhenIdle = TRUE)) %>%
@@ -646,25 +640,93 @@ TADA_FlaggedSitesMap <- function(.data) {
       data = lowres
     )
   }
-  if (nrow(nearby) > 0) {
-    map <- map %>% leaflet::addAwesomeMarkers(~TADA.LongitudeMeasure,
-      ~TADA.LatitudeMeasure,
-      icon = nearbyIcon,
-      # label = ~as.character(TADA.MonitoringLocationIdentifier),
-      popup = paste0(
-        "Nearby Group Name: ", nearby$TADA.MonitoringLocationIdentifier,
-        "<br> Site ID: ", nearby$MonitoringLocationIdentifier,
-        "<br> Site Name: ", nearby$MonitoringLocationName,
-        "<br> Latitude: ", nearby$TADA.LatitudeMeasure,
-        "<br> Longitude: ", nearby$TADA.LongitudeMeasure
-      ),
-      data = nearby
-    )
+  return(map)
+}
+
+#' Create Nearby Sites Map
+#'
+#' @param .data TADA dataframe after running TADA.FindNearbySites.
+#' @param dist_buffer Distance in m to show a radius around each site marker.
+#' 
+#'
+#' @return A leaflet map that shows all sites in the dataframe that contain
+#' flagged data in the form of near other sites - groups of sites that are spatially located within
+#'    a threshold distance (defaulting to 100 m) from each other and within the same catchment.
+#'
+#' @export
+#'
+#' @examples
+#' \dontrun{
+#' # Load example dataframe:
+#' data(Data_Nutrients_UT)
+#'
+#'
+#' # Create maps:
+#' TADA_FlaggedSitesMap(Data_Nutrients_UT)
+#' TADA_FlaggedSitesMap(Data_NCTCShepherdstown_HUC12)
+#' TADA_FlaggedSitesMap(Data_6Tribes_5y_Harmonized)
+#' }
+#'
+TADA_NearbySitesMap <- function(.data, dist_buffer = 100) {
+  
+  if(c("TADA.NearbySiteGroup") %in% colnames(.data) == FALSE) {
+    .data <- TADA_FindNearbySites(.data)
   }
 
+  .data <- .data %>%
+    dplyr::filter(!is.na(TADA.NearbySiteGroup)) %>%
+    dplyr::mutate(LatitudeMeasure = as.numeric(LatitudeMeasure),
+                  LongitudeMeasure = as.numeric(LongitudeMeasure)) %>%
+    dplyr::select(LongitudeMeasure, LatitudeMeasure, TADA.MonitoringLocationIdentifier,
+                  MonitoringLocationIdentifier, MonitoringLocationName, TADA.LatitudeMeasure,
+                  TADA.LongitudeMeasure, OrganizationIdentifier, TADA.NearbySiteGroup) %>%
+    dplyr::distinct() 
+    
+  icon.colors <- grDevices::rainbow(as.numeric(length(unique(.data$TADA.NearbySiteGroup)))) 
+
+  pal <- leaflet::colorFactor(palette = icon.colors,
+                     domain = .data$TADA.NearbySiteGroup)
+     
+  map <- leaflet::leaflet(.data) %>%
+    leaflet::addProviderTiles("Esri.WorldTopoMap", group = "World topo", options = leaflet::providerTileOptions(updateWhenZooming = FALSE, updateWhenIdle = TRUE)) %>%
+    leaflet.extras::addResetMapButton() # button to reset to initial zoom and lat/long
+  if (nrow(.data) > 0) {
+    map <- map %>% leaflet::addCircleMarkers(~LongitudeMeasure,
+                                              ~LatitudeMeasure,
+                                              color = ~pal(TADA.NearbySiteGroup),
+                                              opacity = 1, 
+                                              fillColor = ~pal(TADA.NearbySiteGroup),
+                                              fillOpacity = 1,
+                                              radius = ifelse(dist_buffer > 200, 
+                                                              dist_buffer/10, 
+                                                              20),
+                                              weight = 1,
+                                              # label = ~as.character(TADA.MonitoringLocationIdentifier),
+                                              popup = paste0(
+                                                "Nearby Group Name: ", .data$TADA.MonitoringLocationIdentifier,
+                                                "<br> Nearby Site Group: ", .data$TADA.NearbySiteGroup,
+                                                "<br> Site ID: ", .data$MonitoringLocationIdentifier,
+                                                "<br> Site Name: ", .data$MonitoringLocationName,
+                                                "<br> Latitude: ", .data$LatitudeMeasure,
+                                                "<br> Longitude: ", .data$LongitudeMeasure
+                                              ),
+                                              data = .data,
+                                       clusterOptions = leaflet::markerClusterOptions(),
+    ) %>%
+      leaflet::addCircles(~LongitudeMeasure,
+                          ~LatitudeMeasure,
+                          color = ~pal(TADA.NearbySiteGroup),
+                          opacity = 0.1, 
+                          fillColor = ~pal(TADA.NearbySiteGroup),
+                          fillOpacity = 0.1,
+                          radius = dist_buffer,
+                          weight = 1)
+  }
+  
   return(map)
 }
 
+
 #' Field Values Pie Chart
 #'
 #' Function creates a ggplot2 pie chart showing the relative proportions of values in a given field in a TADA dataset.
diff --git a/R/ResultFlagsIndependent.R b/R/ResultFlagsIndependent.R
index 6fc07aa15..1a8257746 100644
--- a/R/ResultFlagsIndependent.R
+++ b/R/ResultFlagsIndependent.R
@@ -1311,7 +1311,8 @@ TADA_FindPotentialDuplicatesMultipleOrgs <- function(.data, dist_buffer = 100,
                                                      org_hierarchy = "none") {
   # from those datapoints, determine which are in adjacent sites
   if (!"TADA.NearbySites.Flag" %in% names(.data)) {
-    .data <- TADA_FindNearbySites(.data, dist_buffer = dist_buffer)
+    .data <- TADA_FindNearbySites(.data, dist_buffer = dist_buffer,
+                                  org_hierarchy = org_hierarchy)
   }
 
   dupsites <- unique(.data[, c(
diff --git a/R/Utilities.R b/R/Utilities.R
index 3213223bf..8791df3ba 100644
--- a/R/Utilities.R
+++ b/R/Utilities.R
@@ -113,7 +113,7 @@ utils::globalVariables(c(
   "dec_long_va", "end_date", "parameter_code", "parameter_name_description", 
   "parm_cd site_no", "site_tp_cd", "site_type", "st_drop_geometry", "station_nm",
   "Statistic Type Code", "Statistic Type Description", "agency_cd", "begin_date", 
-  "parm_cd", "site_no", "stat_cd", "stat_type"
+  "parm_cd", "site_no", "stat_cd", "stat_type", "grouped.sites", "n", "nearby", "rainbow"
 ))
 
 # global variables for tribal feature layers used in TADA_OverviewMap in Utilities.R
@@ -587,7 +587,7 @@ TADA_FindNearbySites <- function(.data, dist_buffer = 100,
   rm(required_cols)
 
   # retain only necessary columns unique Monitoring Locations
-  data_unique_mls <- .data %>%
+  unique.mls <- .data %>%
     dplyr::select(
       TADA.MonitoringLocationIdentifier, TADA.LongitudeMeasure, TADA.LatitudeMeasure,
       HorizontalCoordinateReferenceSystemDatumName
@@ -600,157 +600,141 @@ TADA_FindNearbySites <- function(.data, dist_buffer = 100,
     dplyr::distinct()
 
   # convert to sf object
-  data_unique_mls <- TADA_MakeSpatial(data_unique_mls)
+  unique.mls <- TADA_MakeSpatial(unique.mls)
 
-  # fetch nhdplus catchment information
-  nhd_catchments <- fetchNHD(data_unique_mls, resolution = nhd_res)
-
-  # join catchment data to TADA df, count how many times each catchment is included in df and
-  # retain only rows containing catchments which are included more than once in the df
-  data_unique_mls <- data_unique_mls %>%
-    sf::st_join(nhd_catchments, left = TRUE) %>%
-    dplyr::rename(
-      TADA.MonitoringLocationIdentifier = MonitoringLocationIdentifier,
-      TADA.LongitudeMeasure = LongitudeMeasure,
-      TADA.LatitudeMeasure = LatitudeMeasure
-    ) %>%
-    dplyr::group_by(NHD.nhdplusid) %>%
-    dplyr::mutate(n_id = length(TADA.MonitoringLocationIdentifier)) %>%
-    dplyr::filter(n_id > 1) %>%
-    dplyr::ungroup() %>%
-    dplyr::filter(!is.na(NHD.nhdplusid))
-
-  if (nrow(data_unique_mls) == 0) { # #if no groups, give a TADA.NearbySiteGroup column filled with
-    # "No nearby sites"
-    print("TADA_FindNearbySites: No nearby sites detected using input buffer distance. Columns for TADA.NearbySitesFlag and TADA.NearbySiteGroup added for tracking purposes.")
-
-    .data <- .data %>%
-      dplyr::mutate(
-        TADA.NearbySites.Flag = "No nearby sites detected using input buffer distance.",
-        TADA.NearbySiteGroup = NA
-      )
-
-    rm(data_unique_mls, nhd_catchments)
-
-    return(.data)
-  }
-
-  # check to see if any site groups are found
-  if (nrow(data_unique_mls) > 1) {
-    # remove intermediate object
-    rm(nhd_catchments)
-
-    # need to split into separate dfs based on nhdplusid group
-    df_nhdgroups_list <- split(data_unique_mls, data_unique_mls$NHD.nhdplusid)
-
-    rm(data_unique_mls)
-
-    # prepare dfs for distance matrix
-    df_nhdgroups_prep <- df_nhdgroups_list %>%
-      purrr::map(~ .x %>%
-        dplyr::select(
-          TADA.MonitoringLocationIdentifier,
-          TADA.LongitudeMeasure,
-          TADA.LatitudeMeasure
-        ))
-
-    rm(df_nhdgroups_list)
+  # create a distance matrix in meters
+  dist.matrix <- as.matrix(sf::st_distance(unique.mls)) # Great Circle distance since in lat/lon
 
-    # create a distance matrix in meters
-    dist.mats <- purrr::map(df_nhdgroups_prep, ~ {
-      dist.matrix <- as.matrix(sf::st_distance(.x)) # Great Circle distance since in lat/lon
+  # remove units from distance matrix
+  dist.matrix <- dist.matrix %>%
+    units::drop_units()
 
-      rownames(dist.matrix) <- .x$TADA.MonitoringLocationIdentifier
-      colnames(dist.matrix) <- .x$TADA.MonitoringLocationIdentifier
+    rownames(dist.matrix) <- unique.mls$MonitoringLocationIdentifier
+    colnames(dist.matrix) <- unique.mls$MonitoringLocationIdentifier
 
-      dist.matrix
+  # convert distances to those within buffer (1) and beyond buffer (0)
+    dist.mat1 <- apply(dist.matrix, c(1, 2), function(x) {
+      if (x <= dist_buffer) {
+        x <- 1
+      } else {
+        x <- 0
+      }
     })
-
+    
     # remove intermediate object
-    rm(df_nhdgroups_prep)
-
-    # remove units from distance matrix
-    dist.mats <- purrr::map(dist.mats, ~ units::drop_units(.x))
-
-    # convert distances to those within buffer (1) and beyond buffer (0)
-    binary.mats <- purrr::map(dist.mats, function(mat) {
-      bin.mat <- apply(mat, 2, function(x) as.integer(x <= dist_buffer))
-      diag(bin.mat) <- 0
-      rownames(bin.mat) <- rownames(mat)
-      colnames(bin.mat) <- colnames(mat)
-      #
-      return(bin.mat)
-    })
-
-    rm(dist.mats)
-
-    # loop through distance matrix and extract site groups
-    find_groups <- function(matrix) {
-      # create adjacency graph
-      adj.graph <- igraph::graph_from_adjacency_matrix(matrix, mode = "undirected", diag = FALSE)
-
-      # find connected sites
-      comp.results <- igraph::components(adj.graph)
-
-      # create site group dfs
-      group_sites <- data.frame(
-        Site = names(comp.results$membership),
-        Group = comp.results$membership,
-        row.names = NULL
-      )
-
-      return(group_sites)
-    }
+    rm(dist.matrix)
 
-    # apply site grouping function to matrix
-    site.groups.list <- purrr::map(binary.mats, find_groups)
+    # create adjacency graph
+    adj.graph <- igraph::graph_from_adjacency_matrix(dist.mat1, mode = "undirected", diag = FALSE)
 
-    site.groups.list <- purrr::imap(site.groups.list, ~ .x %>%
-      dplyr::mutate(df_number = .y))
+    # find connected sites
+    comp.results <- igraph::components(adj.graph)
 
-    rm(find_groups, binary.mats)
-
-    # create df of all groups and create unique id for each group
-    combined.group.df <- dplyr::bind_rows(site.groups.list) %>%
-      dplyr::group_by(Group, df_number) %>%
-      dplyr::mutate(Count = length(Site)) %>%
-      dplyr::filter(Count > 1) %>%
-      # create new TADA.MonitoringLocationIdentifier
-      dplyr::mutate(
-        TADA.MonitoringLocationIdentifier.New = paste(Site, collapse = ", "),
-        TADA.MonitoringLocationIdentifier.New = paste0(
-          "[",
-          TADA.MonitoringLocationIdentifier.New,
-          "]"
-        ),
-        TADA.NearbySiteGroup = dplyr::cur_group_id()
+    # create site group dfs
+    group.sites <- data.frame(
+      MonitoringLocationIdentifier = names(comp.results$membership),
+      Group = comp.results$membership,
+      row.names = NULL
       ) %>%
+      dplyr::group_by(Group) %>%
+      dplyr::mutate(n = length(MonitoringLocationIdentifier)) %>%
+      dplyr::filter(n > 1) %>%
+      dplyr::select(-n) %>%
       dplyr::ungroup()
+    
+    # remove intermediate objects
+    rm(dist.mat1, adj.graph, comp.results)
+    
+    if (nrow(group.sites) == 0) { # #if no groups, give a TADA.NearbySiteGroup column filled with
+      # "No nearby sites"
+      print("TADA_FindNearbySites: No nearby sites detected. Columns for TADA.NearbySitesFlag and TADA.NearbySiteGroup added for tracking purposes.")
+      
+      .data <- .data %>%
+        dplyr::mutate(
+          TADA.NearbySites.Flag = "No nearby sites detected.",
+          TADA.NearbySiteGroup = NA
+        )
+      
+      return(.data)
+    }
 
-    rm(site.groups.list)
+    # subset nearby sites
+    near.sites <- unique.mls %>%
+      dplyr::filter(MonitoringLocationIdentifier %in%
+                      group.sites$MonitoringLocationIdentifier) %>%
+      dplyr::left_join(group.sites, by = dplyr::join_by(MonitoringLocationIdentifier))
 
-    # create crosswalk of TADA.MonitoringLocationIdentifer and new TADA.MonitoringLocationIdentifier
-    ml.crosswalk <- combined.group.df %>%
-      dplyr::select(Site, TADA.MonitoringLocationIdentifier.New, TADA.NearbySiteGroup) %>%
-      dplyr::rename(TADA.MonitoringLocationIdentifier = Site) %>%
-      dplyr::distinct()
+    # break into multiple dfs
+    near.dfs <- near.sites %>%
+      dplyr::group_split(Group, .keep = FALSE)
+    
+  # fetch nhdplus catchment information
+  nhd.catch <- near.dfs %>%
+    purrr::map(~ .x %>%
+               fetchNHD(resolution = nhd_res)
+               )
 
-    rm(combined.group.df)
+  nhd.catch.all <- dplyr::bind_rows(nhd.catch)
 
-    # create df of grouped sites, including all activity start dates for the sites
-    grouped.sites <- ml.crosswalk %>%
-      dplyr::left_join(.data, by = dplyr::join_by(TADA.MonitoringLocationIdentifier)) %>%
-      dplyr::select(
-        TADA.MonitoringLocationIdentifier.New, TADA.NearbySiteGroup,
-        TADA.MonitoringLocationName, TADA.LatitudeMeasure, TADA.LongitudeMeasure,
-        TADA.MonitoringLocationTypeName, ActivityStartDate, OrganizationIdentifier
-      ) %>%
-      dplyr::distinct()
+  # join nhd catchments with monitoring locations, filter to include group/catchment
+  catch.groups <- near.sites %>%
+    sf::st_join(nhd.catch.all, left = TRUE) %>%
+    dplyr::rename(
+      TADA.MonitoringLocationIdentifier = MonitoringLocationIdentifier,
+      TADA.LongitudeMeasure = LongitudeMeasure,
+      TADA.LatitudeMeasure = LatitudeMeasure
+    ) %>%
+    dplyr::distinct() %>%
+    dplyr::group_by(Group, NHD.nhdplusid) %>%
+    dplyr::mutate(n = length(TADA.MonitoringLocationIdentifier)) %>%
+    dplyr::filter(n >1) %>%
+    dplyr::select(-n)
+  
+  # remove intermediate objects
+  rm(near.sites, nhd.catch, nhd.catch.all)
+  
+  if (nrow(catch.groups) == 0) { # #if no groups, give a TADA.NearbySiteGroup column filled with
+    # "No nearby sites"
+    print("TADA_FindNearbySites: No nearby sites detected. Columns for TADA.NearbySitesFlag and TADA.NearbySiteGroup added for tracking purposes.")
+    
+    .data <- .data %>%
+      dplyr::mutate(
+        TADA.NearbySites.Flag = "No nearby sites detected.",
+        TADA.NearbySiteGroup = NA
+      )
+    
+    return(.data)
+  }
+  
+  # create df of all groups and create unique id for each group
+  new.ids <- catch.groups %>%
+    # create new TADA.MonitoringLocationIdentifier
+    dplyr::mutate(
+      TADA.MonitoringLocationIdentifier.New = paste(TADA.MonitoringLocationIdentifier, collapse = ", "),
+      TADA.MonitoringLocationIdentifier.New = paste0(
+        "[",
+        TADA.MonitoringLocationIdentifier.New,
+        "]"
+      ),
+      TADA.NearbySiteGroup = dplyr::cur_group_id()
+    ) %>%
+    dplyr::ungroup() %>%
+    dplyr::select(TADA.MonitoringLocationIdentifier.New, TADA.MonitoringLocationIdentifier, 
+                  TADA.NearbySiteGroup) %>%
+    dplyr::distinct()
+  
+  # remove intermediate objects
+  rm(catch.groups, near.dfs, unique.mls)
 
     # create a df of unique grouped sites, do not include any activity start dates
-    grouped.no.dates <- grouped.sites %>%
-      dplyr::select(-ActivityStartDate) %>%
-      dplyr::distinct()
+    grouped.no.dates <- new.ids %>%
+      dplyr::full_join(.data, by = dplyr::join_by(TADA.MonitoringLocationIdentifier)) %>%
+      dplyr::select(TADA.MonitoringLocationName, TADA.MonitoringLocationIdentifier.New, 
+                    TADA.NearbySiteGroup, TADA.MonitoringLocationName, TADA.LatitudeMeasure, 
+                    TADA.LongitudeMeasure, TADA.MonitoringLocationTypeName, OrganizationIdentifier
+      ) %>%
+      dplyr::distinct() %>%
+      sf::st_drop_geometry()
 
     # create list of orgs from TADA df
     all.orgs <- unique(.data$OrganizationIdentifier)
@@ -993,10 +977,11 @@ TADA_FindNearbySites <- function(.data, dist_buffer = 100,
     }
 
     # remove intermediate objects
-    rm(grouped.no.dates, grouped.sites, org.meta.filter, org.string, meta.string)
+    rm(grouped.no.dates, org.meta.filter, org.string, meta.string)
 
-    # remove site group from ml.crosswalk
-    ml.crosswalk <- ml.crosswalk %>%
+    # remove site group from crosswalk
+    ml.crosswalk <- new.ids %>%
+      sf::st_drop_geometry() %>%
       dplyr::select(-TADA.NearbySiteGroup) %>%
       dplyr::distinct()
 
@@ -1035,11 +1020,11 @@ TADA_FindNearbySites <- function(.data, dist_buffer = 100,
       TADA_OrderCols()
 
     # remove intermediate objects
-    rm(select.meta, ml.crosswalk)
+    rm(select.meta, ml.crosswalk, group.sites, new.ids)
 
     # add flag for any ungrouped sites and order columns correctly
     .data <- TADA_OrderCols(.data) %>%
-      dplyr::mutate(TADA.NearbySites.Flag = ifelse(is.na(TADA.NearbySites.Flag),
+      dplyr::mutate(TADA.NearbySites.Flag = ifelse(is.na(TADA.NearbySiteGroup),
         "No nearby sites detected using input buffer distance.",
         TADA.NearbySites.Flag
       ))
@@ -1047,7 +1032,7 @@ TADA_FindNearbySites <- function(.data, dist_buffer = 100,
     # return TADA df with added columns for tracking
     return(.data)
   }
-}
+
 
 
 #' Get grouped monitoring stations that are near each other
diff --git a/man/TADA_FlaggedSitesMap.Rd b/man/TADA_FlaggedSitesMap.Rd
index 70e0884b0..5495c8c19 100644
--- a/man/TADA_FlaggedSitesMap.Rd
+++ b/man/TADA_FlaggedSitesMap.Rd
@@ -19,8 +19,6 @@ flagged data in the form of:
 \item imprecise coordinates - latitudes and/or longitudes that contain fewer
 then 3 decimal places.
 \item outside USA - coordinates that fall outside the bounds of the USA.
-\item near other sites - groups of sites that are spatially located within
-a threshold distance (defaulting to 100 m) from each other.
 }
 }
 \description{
diff --git a/man/TADA_NearbySitesMap.Rd b/man/TADA_NearbySitesMap.Rd
new file mode 100644
index 000000000..74b5219c0
--- /dev/null
+++ b/man/TADA_NearbySitesMap.Rd
@@ -0,0 +1,34 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/Figures.R
+\name{TADA_NearbySitesMap}
+\alias{TADA_NearbySitesMap}
+\title{Create Nearby Sites Map}
+\usage{
+TADA_NearbySitesMap(.data, dist_buffer = 100)
+}
+\arguments{
+\item{.data}{TADA dataframe after running TADA.FindNearbySites.}
+
+\item{dist_buffer}{Distance in m to show a radius around each site marker.}
+}
+\value{
+A leaflet map that shows all sites in the dataframe that contain
+flagged data in the form of near other sites - groups of sites that are spatially located within
+a threshold distance (defaulting to 100 m) from each other and within the same catchment.
+}
+\description{
+Create Nearby Sites Map
+}
+\examples{
+\dontrun{
+# Load example dataframe:
+data(Data_Nutrients_UT)
+
+
+# Create maps:
+TADA_FlaggedSitesMap(Data_Nutrients_UT)
+TADA_FlaggedSitesMap(Data_NCTCShepherdstown_HUC12)
+TADA_FlaggedSitesMap(Data_6Tribes_5y_Harmonized)
+}
+
+}


Reviews:
user: login: cristinamullin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-15T15:00:54Z
user: login: hillarymarler
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-15T15:03:55Z
user: login: cristinamullin
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-22T00:12:26Z

Review comments:
diff_hunk: @@ -1023,8 +1007,9 @@ TADA_FindNearbySites <- function(.data, dist_buffer = 100,
     # remove intermediate objects
     rm(grouped.no.dates, grouped.sites, org.meta.filter, org.string, meta.string)
path: R/Utilities.R
user: login: cristinamullin
body: grouped.sites in row 932 (starts with date.meta <- grouped.sites %>%) was removed/is no available now. 

This is causing a related rm note: 

```r
Warning message:
In rm(grouped.no.dates, grouped.sites, org.meta.filter, org.string,  :
  object 'grouped.sites' not found
```
created_at: 2025-05-15T15:00:54Z
author_association: COLLABORATOR
diff_hunk: @@ -1023,8 +1007,9 @@ TADA_FindNearbySites <- function(.data, dist_buffer = 100,
     # remove intermediate objects
     rm(grouped.no.dates, grouped.sites, org.meta.filter, org.string, meta.string)
path: R/Utilities.R
user: login: hillarymarler
body: Thanks for catching that - I'll take a look and fix it.
created_at: 2025-05-15T15:03:55Z
author_association: COLLABORATOR

PR comments:
user: login: hillarymarler
created_at: 2025-05-16T17:34:11Z
body: I added a new function TADA_NearbySitesMap for users to more easily review nearby sites. Using the same Utah example, it makes a lot more sense and the groupings also look to be correct using a variety of different buffer distances. The format of this map is quite different than others as I used different colors for each group, a lighter/more transparent color to show a distance buffer radius, and used clustering so that the user would be guided to click on where a site group or groups are located on the map and zoom in.

Do you all think this approach works? Or would it be better to switch back to more of the icon marker style as we have for some of the other maps?

I also removed the nearby sites icons from the TADA_FlaggedSitesMap function as I think it makes sense to consider them separately from those with suspect coordinates.

This is ready for review.
author_association: COLLABORATOR
user: login: cristinamullin
created_at: 2025-05-21T13:11:21Z
body: It looks like org_hierarchy is not being carried into the TADA_FindNearbySites when it runs within TADA_FindPotentialDuplicatesMultipleOrgs

```r
WQP_Shepherdstown_raw <- TADA_DataRetrieval(
  startDate = "2019-01-01",
  endDate = "2024-12-31",
  bBox = c(-78.2241,38.9185,-77.1041,39.7487),
  applyautoclean = TRUE,
  ask = FALSE)

> WQP_Shepherdstown_test <- TADA_FindPotentialDuplicatesMultipleOrgs(
+   WQP_Shepherdstown_raw , 
+   org_hierarchy = "21VASWCB"
+ )
[1] "TADA_FindNearbySites: No org_hierarchy supplied by user. Organization will not be taken into account during metadata selection."
```
author_association: COLLABORATOR
user: login: hillarymarler
created_at: 2025-05-21T13:13:10Z
body: Thanks - I'll check that out as well as the test coverage failure.
author_association: COLLABORATOR
user: login: hillarymarler
created_at: 2025-05-21T14:42:50Z
body: ![image](https://github.com/user-attachments/assets/e5445f9a-60b6-472e-835b-3ff634e07f86)

I added org_hierarchy to TADA_FindNearbySites when it is run as part of TADA_FindPotentialDuplicatesMulitpleOrgs
author_association: COLLABORATOR