From e83e872d4697f41b0fa9652464e50d7d391fdfc4 Mon Sep 17 00:00:00 2001 From: Ben Bond-Lamberty Date: Tue, 14 Oct 2025 13:16:20 -0400 Subject: [PATCH 1/4] Add nearest_neighbor_TMP --- DESCRIPTION | 2 +- NAMESPACE | 1 + R/datalogger.R | 4 ++-- R/spatial.r | 43 +++++++++++++++++++++++++++++++++++++ man/nearest_neighbor_TMP.Rd | 31 ++++++++++++++++++++++++++ man/read_datalogger_file.Rd | 4 ++-- 6 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 R/spatial.r create mode 100644 man/nearest_neighbor_TMP.Rd diff --git a/DESCRIPTION b/DESCRIPTION index b8c7e2b..916c69e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -12,7 +12,7 @@ URL: https://compass.pnnl.gov License: MIT + file LICENSE Encoding: UTF-8 Roxygen: list(markdown = TRUE) -RoxygenNote: 7.3.2 +RoxygenNote: 7.3.3 Depends: R (>= 3.4.0) Imports: readr, diff --git a/NAMESPACE b/NAMESPACE index 392d6c1..b92c55f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,6 +1,7 @@ # Generated by roxygen2: do not edit by hand export(list_directories) +export(nearest_neighbor_TMP) export(process_aquatroll_dir) export(process_sapflow_dir) export(process_teros_dir) diff --git a/R/datalogger.R b/R/datalogger.R index a88246a..112abc4 100644 --- a/R/datalogger.R +++ b/R/datalogger.R @@ -4,10 +4,10 @@ #' #' @param filename Fully-qualified filename of a raw Campbell datalogger file #' @param quiet Print diagnostic messages? Logical -#' @param ... Other parameters to pass on to \code{\link{read_csv}} +#' @param ... Other parameters to pass on to \code{read_csv} #' @description This function reads a local file of raw sapflow data, #' extracts the logger number from the header, and uses -#' \code{\link[readr]{read_csv}} to parse the file into a data frame. +#' \code{read_csv} to parse the file into a data frame. #' @author Stephanie Pennington and Ben Bond-Lamberty #' @return A \code{\link[tibble]{tibble}} with the data. #' @export diff --git a/R/spatial.r b/R/spatial.r new file mode 100644 index 0000000..b6aac3c --- /dev/null +++ b/R/spatial.r @@ -0,0 +1,43 @@ +# spatial.R + +#' Find the nearest-neighbor grid squares - TEMPEST +#' +#' @param location Location grid cell ID (A1 to J8), character +#' @param radius Radius desired, 0 or greater (see examples), numeric +#' +#' @returns A vector of grid square IDs in a box of radius \code{radius} +#' @export +#' +#' @examples +#' nearest_neighbor_TMP("C2") +#' nearest_neighbor_TMP("A1") # returns four squares (other are out) +#' nearest_neighbor_TMP("F3", radius = 0) # returns F3 +#' nearest_neighbor_TMP("F3", radius = 1) # returns the nine squares around F3 +#' +#' # Construct a data frame of neighboring grid squares for a vector of locations +#' locations <- c("C2", "F3") +#' grid_map <- lapply(locations, function(x) data.frame(neighbor = nearest_neighbor_TMP(x))) +#' names(grid_map) <- locations +#' dplyr::bind_rows(grid_map, .id = "location") +nearest_neighbor_TMP <- function(location, radius = 1) { + + # Sanity checking + stopifnot(length(location) == 1) + if(!grepl("^[A-J][1-8]$", location)) { + stop(location, " does not appear to be a TEMPEST grid cell") + } + + x <- which(LETTERS == substr(location, 1, 1)) + xseq <- seq(x - radius, x + radius) + # remove x values not in 1-10 (i.e., A-J) range + xseq <- xseq[xseq > 0 & xseq < 11] + + y <- as.integer(substr(location, 2, 2)) + yseq <- seq(y - radius, y + radius) + # remove x values not in 1-8 range + yseq <- yseq[yseq > 0 & yseq < 9] + + # Generate all grid cell combinations + df <- expand.grid(LETTERS[xseq], yseq) + return(paste0(df[,1], df[,2])) +} diff --git a/man/nearest_neighbor_TMP.Rd b/man/nearest_neighbor_TMP.Rd new file mode 100644 index 0000000..67ae69a --- /dev/null +++ b/man/nearest_neighbor_TMP.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/spatial.r +\name{nearest_neighbor_TMP} +\alias{nearest_neighbor_TMP} +\title{Find the nearest-neighbor grid squares - TEMPEST} +\usage{ +nearest_neighbor_TMP(location, radius = 1) +} +\arguments{ +\item{location}{Location grid cell ID (A1 to J8), character} + +\item{radius}{Radius desired, 0 or greater (see examples), numeric} +} +\value{ +A vector of grid square IDs in a box of radius \code{radius} +} +\description{ +Find the nearest-neighbor grid squares - TEMPEST +} +\examples{ +nearest_neighbor_TMP("C2") +nearest_neighbor_TMP("A1") # returns four squares (other are out) +nearest_neighbor_TMP("F3", radius = 0) # returns F3 +nearest_neighbor_TMP("F3", radius = 1) # returns the nine squares around F3 + +# Construct a data frame of neighboring grid squares for a vector of locations +locations <- c("C2", "F3") +grid_map <- lapply(locations, function(x) data.frame(neighbor = nearest_neighbor_TMP(x))) +names(grid_map) <- locations +dplyr::bind_rows(grid_map, .id = "location") +} diff --git a/man/read_datalogger_file.Rd b/man/read_datalogger_file.Rd index f7638be..4cde94c 100644 --- a/man/read_datalogger_file.Rd +++ b/man/read_datalogger_file.Rd @@ -11,7 +11,7 @@ read_datalogger_file(filename, quiet = FALSE, ...) \item{quiet}{Print diagnostic messages? Logical} -\item{...}{Other parameters to pass on to \code{\link{read_csv}}} +\item{...}{Other parameters to pass on to \code{read_csv}} } \value{ A \code{\link[tibble]{tibble}} with the data. @@ -19,7 +19,7 @@ A \code{\link[tibble]{tibble}} with the data. \description{ This function reads a local file of raw sapflow data, extracts the logger number from the header, and uses -\code{\link[readr]{read_csv}} to parse the file into a data frame. +\code{read_csv} to parse the file into a data frame. } \examples{ fn <- system.file("PNNL_11_sapflow_1min.dat", package = "compasstools") From b94c45e71b1307aacf33c2687c11723726b61df2 Mon Sep 17 00:00:00 2001 From: Ben Bond-Lamberty Date: Tue, 14 Oct 2025 13:19:29 -0400 Subject: [PATCH 2/4] Update spatial.r --- R/spatial.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/spatial.r b/R/spatial.r index b6aac3c..96ca739 100644 --- a/R/spatial.r +++ b/R/spatial.r @@ -10,7 +10,7 @@ #' #' @examples #' nearest_neighbor_TMP("C2") -#' nearest_neighbor_TMP("A1") # returns four squares (other are out) +#' nearest_neighbor_TMP("A1") # returns four squares (others are out) #' nearest_neighbor_TMP("F3", radius = 0) # returns F3 #' nearest_neighbor_TMP("F3", radius = 1) # returns the nine squares around F3 #' From 9529bc0cee958c1366dd4c6e3ed2f245bb96f292 Mon Sep 17 00:00:00 2001 From: Ben Bond-Lamberty Date: Tue, 14 Oct 2025 13:23:04 -0400 Subject: [PATCH 3/4] Simplify example --- R/spatial.r | 6 +++--- man/nearest_neighbor_TMP.Rd | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/R/spatial.r b/R/spatial.r index 96ca739..451ab14 100644 --- a/R/spatial.r +++ b/R/spatial.r @@ -16,9 +16,9 @@ #' #' # Construct a data frame of neighboring grid squares for a vector of locations #' locations <- c("C2", "F3") -#' grid_map <- lapply(locations, function(x) data.frame(neighbor = nearest_neighbor_TMP(x))) -#' names(grid_map) <- locations -#' dplyr::bind_rows(grid_map, .id = "location") +#' grid_map <- lapply(locations, function(x) +#' data.frame(location = x, neighbor = nearest_neighbor_TMP(x))) +#' dplyr::bind_rows(grid_map) nearest_neighbor_TMP <- function(location, radius = 1) { # Sanity checking diff --git a/man/nearest_neighbor_TMP.Rd b/man/nearest_neighbor_TMP.Rd index 67ae69a..c696d19 100644 --- a/man/nearest_neighbor_TMP.Rd +++ b/man/nearest_neighbor_TMP.Rd @@ -19,13 +19,13 @@ Find the nearest-neighbor grid squares - TEMPEST } \examples{ nearest_neighbor_TMP("C2") -nearest_neighbor_TMP("A1") # returns four squares (other are out) +nearest_neighbor_TMP("A1") # returns four squares (others are out) nearest_neighbor_TMP("F3", radius = 0) # returns F3 nearest_neighbor_TMP("F3", radius = 1) # returns the nine squares around F3 # Construct a data frame of neighboring grid squares for a vector of locations locations <- c("C2", "F3") -grid_map <- lapply(locations, function(x) data.frame(neighbor = nearest_neighbor_TMP(x))) -names(grid_map) <- locations -dplyr::bind_rows(grid_map, .id = "location") +grid_map <- lapply(locations, function(x) + data.frame(location = x, neighbor = nearest_neighbor_TMP(x))) +dplyr::bind_rows(grid_map) } From e23d47ff48245cc532df81c34b48467641489d96 Mon Sep 17 00:00:00 2001 From: Ben Bond-Lamberty Date: Tue, 14 Oct 2025 13:36:43 -0400 Subject: [PATCH 4/4] Add test code --- R/spatial.r | 1 + tests/testthat/test-spatial.r | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 tests/testthat/test-spatial.r diff --git a/R/spatial.r b/R/spatial.r index 451ab14..d0b35b2 100644 --- a/R/spatial.r +++ b/R/spatial.r @@ -23,6 +23,7 @@ nearest_neighbor_TMP <- function(location, radius = 1) { # Sanity checking stopifnot(length(location) == 1) + stopifnot(radius >= 0) if(!grepl("^[A-J][1-8]$", location)) { stop(location, " does not appear to be a TEMPEST grid cell") } diff --git a/tests/testthat/test-spatial.r b/tests/testthat/test-spatial.r new file mode 100644 index 0000000..c9090f8 --- /dev/null +++ b/tests/testthat/test-spatial.r @@ -0,0 +1,20 @@ +test_that("spatial works", { + # handles bad inputs + expect_error(nearest_neighbor_TMP(1:2), regexp = "length\\(location") + expect_error(nearest_neighbor_TMP("xx"), regexp = "does not appear to be a TEMPEST grid cell") + expect_error(nearest_neighbor_TMP("A1", radius = -1), regexp = "radius >= 0") + + # 0-radius + expect_identical(nearest_neighbor_TMP("A1", radius = 0), "A1") + # 1-radius + expect_identical(sort(nearest_neighbor_TMP("C2", radius = 1)), + sort(c("B1", "C1", "D1", "B2", "C2", "D2", "B3", "C3", "D3"))) + # edge case + expect_identical(sort(nearest_neighbor_TMP("A2", radius = 1)), + sort(c("A1", "B1", "A2", "B2", "A3", "B3"))) + # corner case + expect_identical(sort(nearest_neighbor_TMP("A1", radius = 1)), + sort(c("A1", "B1", "A2", "B2"))) + # 2-radius + expect_length(nearest_neighbor_TMP("C3", radius = 2), 25) +})