From 3ae485b4171b85058a966134e1a837474aa287cb Mon Sep 17 00:00:00 2001 From: "Adam H. Sparks" Date: Fri, 1 Nov 2024 17:10:23 +0800 Subject: [PATCH 1/9] streamline read_cog_dt to use less memory --- R/read_cog_dt.R | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/R/read_cog_dt.R b/R/read_cog_dt.R index 6c7a15e..3776966 100644 --- a/R/read_cog_dt.R +++ b/R/read_cog_dt.R @@ -25,13 +25,15 @@ read_cog_dt <- function(data = "smips", collection = "totalbucket", day, api_key = get_key()) { - r <- read_cog( - data = data, - collection = collection, - day = day, - api_key = api_key - ) - r <- data.table::setDT(terra::as.data.frame(r, xy = TRUE)) + r <- data.table::setDT(terra::as.data.frame( + read_cog( + data = data, + collection = collection, + day = day, + api_key = api_key + ), xy = TRUE + )) + data.table::setnames(r, old = c("x", "y"), new = c("lon", "lat")) return(r) } From 0785594e018e0a646fb0e4c3242b807105275a79 Mon Sep 17 00:00:00 2001 From: "Adam H. Sparks" Date: Fri, 1 Nov 2024 20:01:04 +0800 Subject: [PATCH 2/9] delete unused file --- R/get_smips.R | 92 --------------------------------------------------- 1 file changed, 92 deletions(-) delete mode 100644 R/get_smips.R diff --git a/R/get_smips.R b/R/get_smips.R deleted file mode 100644 index db506c2..0000000 --- a/R/get_smips.R +++ /dev/null @@ -1,92 +0,0 @@ -#' Get Soil Moisture Integration and Prediction System (SMIPS) Data -#' -#' Soil Moisture Integration and Prediction System (\acronym{SMIPS}) v1.0. -#' -#' @param collection A character vector of the data collection to be queried, -#' on of: -#' * SMindex -#' * bucket1 -#' * bucket2 -#' * deepD -#' * runoff -#' * totalbucket -#' Defaults to \dQuote{totalbucket}. -#' @param day A single day's date to query, _e.g._, `day = "2017-12-31"`, both -#' `Character` and `Date` classes are accepted. -#' @param api_key A `character` string containing your \acronym{API} key, -#' a random string provided to you by \acronym{TERN}, for the request. -#' Defaults to automatically detecting your key from your local .Renviron, -#' .Rprofile or similar. Alternatively, you may directly provide your key as -#' a string here or use functionality like that from \CRANpkg{keyring}. If -#' nothing is provided, you will be prompted on how to set up your \R session -#' so that it is auto-detected and a browswer window will open at the -#' \acronym{TERN} website for you to request a key. -#' -#' @examplesIf interactive() -#' -#' r <- get_smips(day = "2024-01-01") -#' -#' # terra::plot() is re-exported for convenience -#' plot(r) -#' -#' @return A [terra::rast] object -#' @references -#' @export -get_smips <- function(collection = "totalbucket", - day, - api_key = get_key()) { - day <- lubridate::ymd(day) - url_date <- gsub("-", "", day) - url_year <- lubridate::year(day) - - .check_collection_agreement(.collection = collection, .day = day) - - approved_collections <- c( - "totalbucket", - "SMindex", - "bucket1", - "bucket2", - "deepD", - "runnoff" - ) - collection <- rlang::arg_match(collection, approved_collections) - - collection_url <- data.table::fcase( - collection == "totalbucket", - paste0("smips_totalbucket_mm_", url_date, ".tif"), - collection == "SMindex", - paste0("smips_smi_perc_", url_date, ".tif"), - collection == "bucket1", - paste0("smips_bucket1_mm_", url_date, ".tif"), - collection == "bucket2", - paste0("smips_bucket2_mm_", url_date, ".tif"), - collection == "deepD", - paste0("smips_deepD_mm_", url_date, ".tif"), - collection == "runoff", - paste0("smips_runoff_mm_", url_date, ".tif") - ) - - r <- terra::rast(paste0( - "/vsicurl/https://", - paste0("apikey:", api_key), - "@data.tern.org.au/model-derived/smips/v1_0/", - collection, - "/", - url_year, - "/", - collection_url - )) - return(r) -} - -.check_collection_agreement <- function(.collection, .day) { - .this_year <- lubridate::year(lubridate::today()) - .last_week <- lubridate::today() - 7 - .url_year <- lubridate::year(.day) - - if (.collection == "totalbucket" && - .url_year < 2005 || - .day > .last_week) { - cli::cli_abort("The data are not available before 2005 and past {.last_week}") - } -} From 2d17f8b3b7ecd73cc0ec23dbe0b229d5d9dce5d0 Mon Sep 17 00:00:00 2001 From: "Adam H. Sparks" Date: Sat, 2 Nov 2024 10:33:16 +0800 Subject: [PATCH 3/9] Save my work --- DESCRIPTION | 6 ++- NAMESPACE | 2 - R/internal_functions.R | 44 ------------------ R/read_cog.R | 103 +++++++++++++++++++++++++++++++++++++++-- R/read_cog_dt.R | 39 +++++++++++++++- man/download_gtiff.Rd | 69 --------------------------- man/get_smips.Rd | 51 -------------------- man/read_cog.Rd | 39 ++++++++++++++-- man/read_cog_dt.Rd | 25 ++++++++-- 9 files changed, 195 insertions(+), 183 deletions(-) delete mode 100644 R/internal_functions.R delete mode 100644 man/download_gtiff.Rd delete mode 100644 man/get_smips.Rd diff --git a/DESCRIPTION b/DESCRIPTION index edfeeb2..ff21b6e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -10,10 +10,12 @@ Description: Provides access to Australia's TERN (Terrestrial Ecosystem Research Network) data through the API, . License: MIT + file LICENSE Suggests: + roxyglobals, testthat (>= 3.0.0) Config/testthat/edition: 3 Encoding: UTF-8 -Roxygen: list(markdown = TRUE) +Roxygen: list(markdown = TRUE, roclets = c("collate", "namespace", "rd", + "roxyglobals::global_roclet")) RoxygenNote: 7.3.2 Imports: cli, @@ -23,3 +25,5 @@ Imports: rlang, terra, utils +Config/roxyglobals/filename: globals.R +Config/roxyglobals/unique: FALSE diff --git a/NAMESPACE b/NAMESPACE index 044bbc0..8edadce 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,8 +1,6 @@ # Generated by roxygen2: do not edit by hand -export(download_gtiff) export(get_key) -export(get_smips) export(plot) export(read_cog) export(read_cog_dt) diff --git a/R/internal_functions.R b/R/internal_functions.R deleted file mode 100644 index 59f424f..0000000 --- a/R/internal_functions.R +++ /dev/null @@ -1,44 +0,0 @@ - - - -.make_smips_url <- function(.collection, .day) { - url_date <- gsub("-", "", .day) - - approved_collections <- c("totalbucket", - "SMindex", - "bucket1", - "bucket2", - "deepD", - "runnoff") - collection <- rlang::arg_match(.collection, approved_collections) - - .check_collection_agreement(.collection = .collection, .day = .day) - - collection_url <- data.table::fcase( - collection == "totalbucket", - paste0("smips_totalbucket_mm_", url_date, ".tif"), - collection == "SMindex", - paste0("smips_smi_perc_", url_date, ".tif"), - collection == "bucket1", - paste0("smips_bucket1_mm_", url_date, ".tif"), - collection == "bucket2", - paste0("smips_bucket2_mm_", url_date, ".tif"), - collection == "deepD", - paste0("smips_deepD_mm_", url_date, ".tif"), - collection == "runoff", - paste0("smips_runoff_mm_", url_date, ".tif") - ) -} - - -.check_collection_agreement <- function(.collection, .day) { - .this_year <- lubridate::year(lubridate::today()) - .last_week <- lubridate::today() - 7 - .url_year <- lubridate::year(.day) - - if (.collection == "totalbucket" && - .url_year < 2005 || - .day > .last_week) { - cli::cli_abort("The data are not available before 2005 and past {.last_week}") - } -} diff --git a/R/read_cog.R b/R/read_cog.R index 847242a..a7f9a3e 100644 --- a/R/read_cog.R +++ b/R/read_cog.R @@ -17,8 +17,10 @@ #' * deepD #' * runoff #' * totalbucket -#' Defaults to \dQuote{totalbucket}. -#' @param day A single day's date to query, _e.g._, `day = "2017-12-31"`, both +#' Defaults to \dQuote{totalbucket}. Multiple `collections` are supported, +#' _e.g._, `collection = c("SMindex", "totalbucket")`. +#' @param day A vector of date(s) to query, _e.g._, `day = "2017-12-31"` or +#' `day = seq.Date(as.Date("2017-12-01"), as.Date("2017-12-31"), "days")`, both #' `Character` and `Date` classes are accepted. #' @param api_key A `character` string containing your \acronym{API} key, #' a random string provided to you by \acronym{TERN}, for the request. @@ -29,29 +31,52 @@ #' so that it is auto-detected and a browser window will open at the #' \acronym{TERN} website for you to request a key. #' +#' @section Argument details for `lonlat`: +#' \describe{ +#' \item{Single point}{To get a specific cell, 1/2 x 1/2 degree, supply a +#' length-two numeric vector giving the decimal degree longitude and latitude +#' in that order for data to download,\cr +#' _e.g._, `lonlat = c(-179.5, -89.5)`.} +#' +#' \item{Regional coverage}{To get a region, supply a length-four numeric +#' vector as lower left (lon, lat) and upper right (lon, lat) coordinates, +#' _e.g._, `lonlat = c(xmin, ymin, xmax, ymax)` in that order for a +#' given region, _e.g._, a bounding box for the south western corner of +#' Australia: `lonlat = c(112.5, -55.5, 115.5, -50.5)`.} +#' } +#' #' @family COGs #' #' @examplesIf interactive() #' -#' r <- read_cog(day = "2024-01-01") +#' r <- read_cog(day = "2024-01-01", api_key = "your_api_key") #' #' # terra::plot() is re-exported for convenience #' plot(r) #' #' @return A [terra::rast] object +#' +#' @autoglobal #' @references #' @export read_cog <- function(data = "smips", collection = "totalbucket", day, + longitude = NULL, + latitude = NULL, api_key = get_key()) { - day <- lubridate::ymd(day) + + day <- .check_date(day) url_year <- lubridate::year(day) + + user_longitude <- lonlat["longitude"] + user_latitude <- lonlat["latitude"] + if (data == "smips") { collection_url <- .make_smips_url(.collection = collection, .day = day) - return(terra::rast( + r <- (terra::rast( paste0( "/vsicurl/https://", paste0("apikey:", api_key), @@ -64,5 +89,73 @@ read_cog <- function(data = "smips", ) ) ) + + return(r) } } + +#' Validate Days Requested Align With Collection +#' +#' Not all dates are offered by all collections. This checks the user inputs to +#' be sure that unavailable dates are not requested from collections that do not +#' provide them. +#' +#' @param .collection The user-supplied SMIPS collection being asked for. +#' @param .day The user-supplied date being asked for. +#' +#' @autoglobal +#' +#' @noRd +#' @keywords Internal + +.check_collection_agreement <- function(.collection, .day) { + .this_year <- lubridate::year(lubridate::today()) + .last_week <- lubridate::today() - 7 + .url_year <- lubridate::year(.day) + + if (.collection == "totalbucket" && + .url_year < 2005 || + .day > .last_week) { + cli::cli_abort("The data are not available before 2005 and past {.last_week}") + } +} + +#' Create an SMIPS URL +#' +#' Creates the SMIPS specific portion of a URL to read or fetch a COG. +#' +#' @param .collection The user-supplied SMIPS collection being asked for. +#' @param .day The user-supplied date being asked for. +#' +#' @autoglobal +#' @noRd +#' @keywords Internal + +.make_smips_url <- function(.collection, .day) { + url_date <- gsub("-", "", .day) + + approved_collections <- c("totalbucket", + "SMindex", + "bucket1", + "bucket2", + "deepD", + "runnoff") + collection <- rlang::arg_match(.collection, approved_collections) + + .check_collection_agreement(.collection = .collection, .day = .day) + + collection_url <- data.table::fcase( + collection == "totalbucket", + paste0("smips_totalbucket_mm_", url_date, ".tif"), + collection == "SMindex", + paste0("smips_smi_perc_", url_date, ".tif"), + collection == "bucket1", + paste0("smips_bucket1_mm_", url_date, ".tif"), + collection == "bucket2", + paste0("smips_bucket2_mm_", url_date, ".tif"), + collection == "deepD", + paste0("smips_deepD_mm_", url_date, ".tif"), + collection == "runoff", + paste0("smips_runoff_mm_", url_date, ".tif") + ) +} diff --git a/R/read_cog_dt.R b/R/read_cog_dt.R index 3776966..e2f2d3f 100644 --- a/R/read_cog_dt.R +++ b/R/read_cog_dt.R @@ -10,14 +10,15 @@ #' @inherit read_cog #' #' @family COGs -#' +#' @author Adam H. Sparks \email{adamhsparks@@curtin.edu.au} #' @examplesIf interactive() #' -#' r <- read_cog_dt(day = "2024-01-01") +#' r <- read_cog_dt(day = "2024-01-01", api_key = "your_api_key") #' #' r #' #' @return A [data.table::data.table] object +#' @autoglobal #' @references #' @export @@ -37,3 +38,37 @@ read_cog_dt <- function(data = "smips", data.table::setnames(r, old = c("x", "y"), new = c("lon", "lat")) return(r) } + +#' Check User Input Dates for Validity +#' +#' @param x User entered date value +#' @return Validated date string as a `POSIXct` object. +#' @note This was taken from \CRANpkg{nasapower}. +#' @example .check_date(x) +#' @author Adam H. Sparks \email{adamhsparks@@curtin.edu.au} +#' @keywords Internal +#' @autoglobal +#' @noRd +.check_date <- function(x) { + tryCatch( + x <- lubridate::parse_date_time(x, + c( + "Ymd", + "dmY", + "mdY", + "BdY", + "Bdy", + "bdY", + "bdy" + ), + tz = Sys.timezone()), + warning = function(c) { + stop(call. = FALSE, + "\n", + x, + " is not in a valid date format. Please enter a valid date format.", + "\n") + } + ) + return(x) +} diff --git a/man/download_gtiff.Rd b/man/download_gtiff.Rd deleted file mode 100644 index 38bc7a8..0000000 --- a/man/download_gtiff.Rd +++ /dev/null @@ -1,69 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/download_gtiff.R -\name{download_gtiff} -\alias{download_gtiff} -\title{Download a Cloud Optimised Geotiff (\acronym{COG}) file to your local -device.} -\usage{ -download_gtiff( - data = "smips", - collection = "totalbucket", - day, - api_key = get_key(), - file = getwd(), - quiet = FALSE, - cache = TRUE -) -} -\arguments{ -\item{collection}{A character vector of the data collection to be queried, -on of: -\itemize{ -\item SMindex -\item bucket1 -\item bucket2 -\item deepD -\item runoff -\item totalbucket -Defaults to \dQuote{totalbucket}. -}} - -\item{day}{A single day's date to query, \emph{e.g.}, \code{day = "2017-12-31"}, both -\code{Character} and \code{Date} classes are accepted.} - -\item{api_key}{A \code{character} string containing your \acronym{API} key, -a random string provided to you by \acronym{TERN}, for the request. -Defaults to automatically detecting your key from your local .Renviron, -.Rprofile or similar. Alternatively, you may directly provide your key as -a string here or use functionality like that from \CRANpkg{keyring}. If -nothing is provided, you will be prompted on how to set up your \R session -so that it is auto-detected and a browswer window will open at the -\acronym{TERN} website for you to request a key.} - -\item{file}{The filename including the full file path. Defaults to the -current working directory through \code{getwd()}.} - -\item{quiet}{Show file download progress bar if TRUE} -} -\value{ -A \code{nert.files} object, , a \code{list} of Geotiff files that are locally -available. -} -\description{ -Download an entire \acronym{COG} file to your local machine -} -\examples{ -download_gtiff() -} -\references{ -\url{https://portal.tern.org.au/metadata/TERN/d1995ee8-53f0-4a7d-91c2-ad5e4a23e5e0https://geonetwork.tern.org.au/geonetwork/srv/eng/catalog.search#/metadata/d1995ee8-53f0-4a7d-91c2-ad5e4a23e5e0} -} -\seealso{ -Other COGs: -\code{\link{read_cog}()}, -\code{\link{read_cog_dt}()} -} -\author{ -Adam Sparks, adam.sparks@curtin.edu.au -} -\concept{COGs} diff --git a/man/get_smips.Rd b/man/get_smips.Rd deleted file mode 100644 index eba10c0..0000000 --- a/man/get_smips.Rd +++ /dev/null @@ -1,51 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/get_smips.R -\name{get_smips} -\alias{get_smips} -\title{Get Soil Moisture Integration and Prediction System (SMIPS) Data} -\usage{ -get_smips(collection = "totalbucket", day, api_key = get_key()) -} -\arguments{ -\item{collection}{A character vector of the data collection to be queried, -on of: -\itemize{ -\item SMindex -\item bucket1 -\item bucket2 -\item deepD -\item runoff -\item totalbucket -Defaults to \dQuote{totalbucket}. -}} - -\item{day}{A single day's date to query, \emph{e.g.}, \code{day = "2017-12-31"}, both -\code{Character} and \code{Date} classes are accepted.} - -\item{api_key}{A \code{character} string containing your \acronym{API} key, -a random string provided to you by \acronym{TERN}, for the request. -Defaults to automatically detecting your key from your local .Renviron, -.Rprofile or similar. Alternatively, you may directly provide your key as -a string here or use functionality like that from \CRANpkg{keyring}. If -nothing is provided, you will be prompted on how to set up your \R session -so that it is auto-detected and a browswer window will open at the -\acronym{TERN} website for you to request a key.} -} -\value{ -A \link[terra:rast]{terra::rast} object -} -\description{ -Soil Moisture Integration and Prediction System (\acronym{SMIPS}) v1.0. -} -\examples{ -\dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} - -r <- get_smips(day = "2024-01-01") - -# terra::plot() is re-exported for convenience -plot(r) -\dontshow{\}) # examplesIf} -} -\references{ -\url{https://portal.tern.org.au/metadata/TERN/d1995ee8-53f0-4a7d-91c2-ad5e4a23e5e0https://geonetwork.tern.org.au/geonetwork/srv/eng/catalog.search#/metadata/d1995ee8-53f0-4a7d-91c2-ad5e4a23e5e0} -} diff --git a/man/read_cog.Rd b/man/read_cog.Rd index 259f7d1..fbe9336 100644 --- a/man/read_cog.Rd +++ b/man/read_cog.Rd @@ -4,7 +4,14 @@ \alias{read_cog} \title{Read COGs from TERN} \usage{ -read_cog(data = "smips", collection = "totalbucket", day, api_key = get_key()) +read_cog( + data = "smips", + collection = "totalbucket", + day, + longitude = NULL, + latitude = NULL, + api_key = get_key() +) } \arguments{ \item{data}{A character vector of the data source to be queried, currently @@ -19,10 +26,12 @@ currenly only \dQuote{smips} is supported with the following collections: \item deepD \item runoff \item totalbucket -Defaults to \dQuote{totalbucket}. +Defaults to \dQuote{totalbucket}. Multiple \code{collections} are supported, +\emph{e.g.}, \code{collection = c("SMindex", "totalbucket")}. }} -\item{day}{A single day's date to query, \emph{e.g.}, \code{day = "2017-12-31"}, both +\item{day}{A vector of date(s) to query, \emph{e.g.}, \code{day = "2017-12-31"} or +\code{day = seq.Date(as.Date("2017-12-01"), as.Date("2017-12-31"), "days")}, both \code{Character} and \code{Date} classes are accepted.} \item{api_key}{A \code{character} string containing your \acronym{API} key, @@ -33,6 +42,11 @@ a string here or use functionality like that from \CRANpkg{keyring}. If nothing is provided, you will be prompted on how to set up your \R session so that it is auto-detected and a browser window will open at the \acronym{TERN} website for you to request a key.} + +\item{lonlat}{A numeric vector of geographic coordinates for a cell or region +entered as x, y (longitude, latitude) coordinates. See argument details +for more. Defaults to \code{NULL} returning all values available in the +\acronym{COG} for Australia.} } \value{ A \link[terra:rast]{terra::rast} object @@ -45,10 +59,26 @@ your active \R session. Currently only Soil Moisture Integration and Prediction System (\acronym{SMIPS}) v1.0 is supported. } +\section{Argument details for \code{lonlat}}{ + +\describe{ +\item{Single point}{To get a specific cell, 1/2 x 1/2 degree, supply a +length-two numeric vector giving the decimal degree longitude and latitude +in that order for data to download,\cr +\emph{e.g.}, \code{lonlat = c(-179.5, -89.5)}.} + +\item{Regional coverage}{To get a region, supply a length-four numeric +vector as lower left (lon, lat) and upper right (lon, lat) coordinates, +\emph{e.g.}, \code{lonlat = c(xmin, ymin, xmax, ymax)} in that order for a +given region, \emph{e.g.}, a bounding box for the south western corner of +Australia: \code{lonlat = c(112.5, -55.5, 115.5, -50.5)}.} +} +} + \examples{ \dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} -r <- read_cog(day = "2024-01-01") +r <- read_cog(day = "2024-01-01", api_key = "your_api_key") # terra::plot() is re-exported for convenience plot(r) @@ -59,7 +89,6 @@ plot(r) } \seealso{ Other COGs: -\code{\link{download_gtiff}()}, \code{\link{read_cog_dt}()} } \concept{COGs} diff --git a/man/read_cog_dt.Rd b/man/read_cog_dt.Rd index db34226..379e634 100644 --- a/man/read_cog_dt.Rd +++ b/man/read_cog_dt.Rd @@ -24,10 +24,12 @@ currenly only \dQuote{smips} is supported with the following collections: \item deepD \item runoff \item totalbucket -Defaults to \dQuote{totalbucket}. +Defaults to \dQuote{totalbucket}. Multiple \code{collections} are supported, +\emph{e.g.}, \code{collection = c("SMindex", "totalbucket")}. }} -\item{day}{A single day's date to query, \emph{e.g.}, \code{day = "2017-12-31"}, both +\item{day}{A vector of date(s) to query, \emph{e.g.}, \code{day = "2017-12-31"} or +\code{day = seq.Date(as.Date("2017-12-01"), as.Date("2017-12-31"), "days")}, both \code{Character} and \code{Date} classes are accepted.} \item{api_key}{A \code{character} string containing your \acronym{API} key, @@ -50,10 +52,26 @@ your active \R session as a \CRANpkg{data.table} object. Currently only Soil Moisture Integration and Prediction System (\acronym{SMIPS}) v1.0 is supported. } +\section{Argument details for \code{lonlat}}{ + +\describe{ +\item{Single point}{To get a specific cell, 1/2 x 1/2 degree, supply a +length-two numeric vector giving the decimal degree longitude and latitude +in that order for data to download,\cr +\emph{e.g.}, \code{lonlat = c(-179.5, -89.5)}.} + +\item{Regional coverage}{To get a region, supply a length-four numeric +vector as lower left (lon, lat) and upper right (lon, lat) coordinates, +\emph{e.g.}, \code{lonlat = c(xmin, ymin, xmax, ymax)} in that order for a +given region, \emph{e.g.}, a bounding box for the south western corner of +Australia: \code{lonlat = c(112.5, -55.5, 115.5, -50.5)}.} +} +} + \examples{ \dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} -r <- read_cog_dt(day = "2024-01-01") +r <- read_cog_dt(day = "2024-01-01", api_key = "your_api_key") r \dontshow{\}) # examplesIf} @@ -63,7 +81,6 @@ r } \seealso{ Other COGs: -\code{\link{download_gtiff}()}, \code{\link{read_cog}()} } \concept{COGs} From 2bcef2f541a3f481bf9ad5499b2058c0c21092e7 Mon Sep 17 00:00:00 2001 From: "Adam H. Sparks" Date: Mon, 4 Nov 2024 07:39:42 +0800 Subject: [PATCH 4/9] Add extract_cog and globals --- R/extract_cog.R | 151 ++++++++++++++++++++++++++++++++++++++++++++++++ R/globals.R | 6 ++ 2 files changed, 157 insertions(+) create mode 100644 R/extract_cog.R create mode 100644 R/globals.R diff --git a/R/extract_cog.R b/R/extract_cog.R new file mode 100644 index 0000000..3a5b8d8 --- /dev/null +++ b/R/extract_cog.R @@ -0,0 +1,151 @@ +#' Extract Values for a Region or Point of Interest From a COG +#' +#' @param x A [terra::rast()] object, typically a \acronym{TERN} provided +#' \acronym{COG}, but any `rast` object will work. +#' @param lonlat Geographic coordinates, either a single point or a bounding box +#' for a cell or region entered as x, y (longitude, latitude) coordinates. +#' See argument details for more. Defaults to `NULL` returning all values +#' available in the \acronym{COG} for Australia. +#' @section Argument details for `lonlat`: +#' \describe{ +#' \item{For a single point}{To get a specific cell supply a length-two numeric +#' vector giving the decimal degree longitude and latitude in that order for +#' data to download, _e.g._, `lonlat = c(115.5, -50.5)`.} +#' +#' \item{Bounding box}{To get a region, supply a length-four numeric +#' vector as lower left (lon, lat) and upper right (lon, lat) coordinates, +#' _e.g._, `lonlat = c(xmin, xmax, ymin, ymax)` in that order for a +#' given region, _e.g._, a bounding box for the south western corner of +#' Australia: `lonlat = c(113, 118, -35, -30)`.} +#' } +#' + +extract_cog <- function(cog, lonlat) { + + lonlat <- .check_lonlat(lonlat = lonlat) + + if (length(lonlat) == 2) { + r <- terra::extract(lonlat) + } + if (length(lonlat) == 4) { + lonlat <- terra::vect(lonlat) + r <- terra::crop(r, lonlat) + } +} + + +#' Check User-supplied `lonlat` for Validity +#' +#' Validates user entered `lonlat` values as falling within Australia +#' +#' @param lonlat User entered `lonlat` value. +#' +#' @return A list called `lonlat` +#' @keywords internal +#' @autoglobal +#' @noRd + + +.check_lonlat <- function(lonlat) { + + # these values are derived from the SLGAcloud package, + # https://github.com/AusSoilsDSM/SLGACloud/blob/17197979d042da6b26bdbb6d41b6ab28f3e3f9a1/R/rasterMethods.R#L68C21-L68C85 + + + au_lon <- c(112.904998779, 153.994995117) + au_lat <- c(-43.73500061, -9.005000114) + + if (is.numeric(lonlat) & length(lonlat) == 2) { + if (lonlat[1] < au_lon[[1]] | lonlat[1] > au_lon[[2]]) { + cli::cli_abort( + call = rlang::caller_env(), + c(i = "Please check your longitude, {.var {lonlat[1]}}, + to be sure it is valid.") + ) + } + if (lonlat[2] < au_lat[[1]] | lonlat[2] > au_lat[[2]]) { + cli::cli_abort( + call = rlang::caller_env(), + c(i = "Please check your latitude, {.val {lonlat[2]}}, + value to be sure it is valid.") + ) + } + longitude <- lonlat[1] + latitude <- lonlat[2] + } else if (length(lonlat) == 4 & is.numeric(lonlat)) { + if (any(lonlat[1] < au_lon[[1]] | + lonlat[3] < au_lon[[1]] | + lonlat[1] > au_lon[[2]] | + lonlat[3] > au_lon[[2]])) { + cli::cli_abort( + call = rlang::caller_env(), + c(i = "Please check your longitude values, {.var {lonlat[1]}} and + {.var {lonlat[3]}}, to be sure they are valid.") + ) + } else if (any(lonlat[2] < au_lat[[1]] | + lonlat[4] < au_lat[[1]] | + lonlat[2] > au_lat[[2]] | + lonlat[4] > au_lat[[2]])) { + cli::cli_abort( + call = rlang::caller_env(), + c(i = "Please check your latitude values, {.var {lonlat[2]}} and + {.var {lonlat[4]}}, to be sure they are valid.") + ) + } else if (lonlat[2] > lonlat[4]) { + cli::cli_abort( + call = rlang::caller_env(), + c( + i = "The first `lonlat` {.arg lat} value must be the minimum value.") + ) + } else if (lonlat[1] > lonlat[3]) { + cli::cli_abort( + call = rlang::caller_env(), + c( + i = "The first `lonlat` {.arg lon} value must be the minimum value.") + ) + } + bbox <- c( + "xmin" = lonlat[1], + "ymin" = lonlat[2], + "xmax" = lonlat[3], + "ymax" = lonlat[4] + ) + } else { + cli::cli_abort( + call = rlang::caller_env(), + c(i = "You have entered an invalid request for `lonlat` + {.arg {lonlat}}.")) + } + + if (!is.null(bbox)) { + lonlat_identifier <- list(bbox, identifier) + names(lonlat_identifier) <- c("bbox", "identifier") + } else if (identifier == "global") { + lonlat_identifier <- list("global") + names(lonlat_identifier) <- "identifier" + } else { + lonlat_identifier <- list(longitude, latitude, identifier) + names(lonlat_identifier) <- + c("longitude", "latitude", "identifier") + } + return(lonlat_identifier) +} + + +#' Check that the user hasn't blindly copied the "your_api_key" string from the +#' examples +#' +#' @keywords Internal +#' @autoglobal +#' @noRd + +.check_not_example_api_key <- function(.api_key) { + if (!is.null(.api_key) && .api_key == "your_api_key") { + stop("You have copied the example code and not provided a proper API key. + An API key may be requested from DPIRD or for SILO you must use your + e-mail address as an API key. See the help for the respective functions + for more.", + call. = FALSE) + } + return(invisible(NULL)) +} diff --git a/R/globals.R b/R/globals.R new file mode 100644 index 0000000..81d77b3 --- /dev/null +++ b/R/globals.R @@ -0,0 +1,6 @@ +# Generated by roxyglobals: do not edit by hand + +utils::globalVariables(c( + "identifier", # <.check_lonlat> + NULL +)) From 4db61f02e9f4f5a1981c0812767ce247ff279ca8 Mon Sep 17 00:00:00 2001 From: "Adam H. Sparks" Date: Mon, 4 Nov 2024 09:39:13 +0800 Subject: [PATCH 5/9] add detail to `get_key()` documentation --- R/get_key.R | 10 ++++++++++ man/get_key.Rd | 21 +++++++++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/R/get_key.R b/R/get_key.R index 648591d..fff2fd4 100644 --- a/R/get_key.R +++ b/R/get_key.R @@ -5,6 +5,16 @@ #' that your key that \R is using is the key that you wish to be using or for #' guidance in setting up the keys. #' +#' # Requesting an API Key +#' +#' To request an \acronym{API} key, go to +#' and click on +#' "Sign In" in the upper right corner. Sign in with your proper credentials. +#' Then, from the left-hand menu, click on "Create API Key". Once this is +#' done, copy the key and put it in your .Renviron using +#' [usethis::edit_r_environ] as `TERN_API_KEY="your_api_key"`. Restart your +#' \R session and the query should work. +#' #' @note #' \acronym{TERN} creates \acronym{API} keys that have special characters that #' include \dQuote{/}, which causes the query to fail. Currently, `get_key()` diff --git a/man/get_key.Rd b/man/get_key.Rd index 6f00999..5f5971e 100644 --- a/man/get_key.Rd +++ b/man/get_key.Rd @@ -15,18 +15,27 @@ If it's not found, then it suggests setting it up. Can be used to check that your key that \R is using is the key that you wish to be using or for guidance in setting up the keys. } -\details{ -The suggestion is to use your .Renviron to set up the API key. However, if -you regularly interact with the APIs outside of \R using some other -language you may wish to set these up in your .bashrc, .zshrc, or -config.fish for cross-language use. -} \note{ \acronym{TERN} creates \acronym{API} keys that have special characters that include \dQuote{/}, which causes the query to fail. Currently, \code{get_key()} checks for this in the \code{API_KEY} string and replaces it with \dQuote{\%2f} so that the query will work properly. } +\section{Requesting an API Key}{ +To request an \acronym{API} key, go to +\url{https://account.tern.org.au/authenticated_user/apikeys} and click on +"Sign In" in the upper right corner. Sign in with your proper credentials. +Then, from the left-hand menu, click on "Create API Key". Once this is +done, copy the key and put it in your .Renviron using +\link[usethis:edit]{usethis::edit_r_environ} as \code{TERN_API_KEY="your_api_key"}. Restart your +\R session and the query should work. + +The suggestion is to use your .Renviron to set up the API key. However, if +you regularly interact with the APIs outside of \R using some other +language you may wish to set these up in your .bashrc, .zshrc, or +config.fish for cross-language use. +} + \examples{ \dontrun{ get_key() From fef0a1f1394ce5aa30a6b48756f225371835b707 Mon Sep 17 00:00:00 2001 From: "Adam H. Sparks" Date: Mon, 4 Nov 2024 15:26:33 +0800 Subject: [PATCH 6/9] Delete extract_cog(), this will be handled externally, e.g. {extractOz} --- R/extract_cog.R | 151 ------------------------------------------------ 1 file changed, 151 deletions(-) delete mode 100644 R/extract_cog.R diff --git a/R/extract_cog.R b/R/extract_cog.R deleted file mode 100644 index 3a5b8d8..0000000 --- a/R/extract_cog.R +++ /dev/null @@ -1,151 +0,0 @@ -#' Extract Values for a Region or Point of Interest From a COG -#' -#' @param x A [terra::rast()] object, typically a \acronym{TERN} provided -#' \acronym{COG}, but any `rast` object will work. -#' @param lonlat Geographic coordinates, either a single point or a bounding box -#' for a cell or region entered as x, y (longitude, latitude) coordinates. -#' See argument details for more. Defaults to `NULL` returning all values -#' available in the \acronym{COG} for Australia. -#' @section Argument details for `lonlat`: -#' \describe{ -#' \item{For a single point}{To get a specific cell supply a length-two numeric -#' vector giving the decimal degree longitude and latitude in that order for -#' data to download, _e.g._, `lonlat = c(115.5, -50.5)`.} -#' -#' \item{Bounding box}{To get a region, supply a length-four numeric -#' vector as lower left (lon, lat) and upper right (lon, lat) coordinates, -#' _e.g._, `lonlat = c(xmin, xmax, ymin, ymax)` in that order for a -#' given region, _e.g._, a bounding box for the south western corner of -#' Australia: `lonlat = c(113, 118, -35, -30)`.} -#' } -#' - -extract_cog <- function(cog, lonlat) { - - lonlat <- .check_lonlat(lonlat = lonlat) - - if (length(lonlat) == 2) { - r <- terra::extract(lonlat) - } - if (length(lonlat) == 4) { - lonlat <- terra::vect(lonlat) - r <- terra::crop(r, lonlat) - } -} - - -#' Check User-supplied `lonlat` for Validity -#' -#' Validates user entered `lonlat` values as falling within Australia -#' -#' @param lonlat User entered `lonlat` value. -#' -#' @return A list called `lonlat` -#' @keywords internal -#' @autoglobal -#' @noRd - - -.check_lonlat <- function(lonlat) { - - # these values are derived from the SLGAcloud package, - # https://github.com/AusSoilsDSM/SLGACloud/blob/17197979d042da6b26bdbb6d41b6ab28f3e3f9a1/R/rasterMethods.R#L68C21-L68C85 - - - au_lon <- c(112.904998779, 153.994995117) - au_lat <- c(-43.73500061, -9.005000114) - - if (is.numeric(lonlat) & length(lonlat) == 2) { - if (lonlat[1] < au_lon[[1]] | lonlat[1] > au_lon[[2]]) { - cli::cli_abort( - call = rlang::caller_env(), - c(i = "Please check your longitude, {.var {lonlat[1]}}, - to be sure it is valid.") - ) - } - if (lonlat[2] < au_lat[[1]] | lonlat[2] > au_lat[[2]]) { - cli::cli_abort( - call = rlang::caller_env(), - c(i = "Please check your latitude, {.val {lonlat[2]}}, - value to be sure it is valid.") - ) - } - longitude <- lonlat[1] - latitude <- lonlat[2] - } else if (length(lonlat) == 4 & is.numeric(lonlat)) { - if (any(lonlat[1] < au_lon[[1]] | - lonlat[3] < au_lon[[1]] | - lonlat[1] > au_lon[[2]] | - lonlat[3] > au_lon[[2]])) { - cli::cli_abort( - call = rlang::caller_env(), - c(i = "Please check your longitude values, {.var {lonlat[1]}} and - {.var {lonlat[3]}}, to be sure they are valid.") - ) - } else if (any(lonlat[2] < au_lat[[1]] | - lonlat[4] < au_lat[[1]] | - lonlat[2] > au_lat[[2]] | - lonlat[4] > au_lat[[2]])) { - cli::cli_abort( - call = rlang::caller_env(), - c(i = "Please check your latitude values, {.var {lonlat[2]}} and - {.var {lonlat[4]}}, to be sure they are valid.") - ) - } else if (lonlat[2] > lonlat[4]) { - cli::cli_abort( - call = rlang::caller_env(), - c( - i = "The first `lonlat` {.arg lat} value must be the minimum value.") - ) - } else if (lonlat[1] > lonlat[3]) { - cli::cli_abort( - call = rlang::caller_env(), - c( - i = "The first `lonlat` {.arg lon} value must be the minimum value.") - ) - } - bbox <- c( - "xmin" = lonlat[1], - "ymin" = lonlat[2], - "xmax" = lonlat[3], - "ymax" = lonlat[4] - ) - } else { - cli::cli_abort( - call = rlang::caller_env(), - c(i = "You have entered an invalid request for `lonlat` - {.arg {lonlat}}.")) - } - - if (!is.null(bbox)) { - lonlat_identifier <- list(bbox, identifier) - names(lonlat_identifier) <- c("bbox", "identifier") - } else if (identifier == "global") { - lonlat_identifier <- list("global") - names(lonlat_identifier) <- "identifier" - } else { - lonlat_identifier <- list(longitude, latitude, identifier) - names(lonlat_identifier) <- - c("longitude", "latitude", "identifier") - } - return(lonlat_identifier) -} - - -#' Check that the user hasn't blindly copied the "your_api_key" string from the -#' examples -#' -#' @keywords Internal -#' @autoglobal -#' @noRd - -.check_not_example_api_key <- function(.api_key) { - if (!is.null(.api_key) && .api_key == "your_api_key") { - stop("You have copied the example code and not provided a proper API key. - An API key may be requested from DPIRD or for SILO you must use your - e-mail address as an API key. See the help for the respective functions - for more.", - call. = FALSE) - } - return(invisible(NULL)) -} From c9632208fdb11c134ee69a3cfcb810f8f5f23df2 Mon Sep 17 00:00:00 2001 From: "Adam H. Sparks" Date: Mon, 4 Nov 2024 15:27:10 +0800 Subject: [PATCH 7/9] Update read_cog() documentation --- R/read_cog.R | 35 ++++++++++++++++++----------------- man/read_cog.Rd | 32 ++------------------------------ man/read_cog_dt.Rd | 19 +++---------------- 3 files changed, 23 insertions(+), 63 deletions(-) diff --git a/R/read_cog.R b/R/read_cog.R index a7f9a3e..a7afb1e 100644 --- a/R/read_cog.R +++ b/R/read_cog.R @@ -31,25 +31,11 @@ #' so that it is auto-detected and a browser window will open at the #' \acronym{TERN} website for you to request a key. #' -#' @section Argument details for `lonlat`: -#' \describe{ -#' \item{Single point}{To get a specific cell, 1/2 x 1/2 degree, supply a -#' length-two numeric vector giving the decimal degree longitude and latitude -#' in that order for data to download,\cr -#' _e.g._, `lonlat = c(-179.5, -89.5)`.} -#' -#' \item{Regional coverage}{To get a region, supply a length-four numeric -#' vector as lower left (lon, lat) and upper right (lon, lat) coordinates, -#' _e.g._, `lonlat = c(xmin, ymin, xmax, ymax)` in that order for a -#' given region, _e.g._, a bounding box for the south western corner of -#' Australia: `lonlat = c(112.5, -55.5, 115.5, -50.5)`.} -#' } -#' #' @family COGs #' #' @examplesIf interactive() #' -#' r <- read_cog(day = "2024-01-01", api_key = "your_api_key") +#' r <- read_cog(day = "2024-01-01") #' #' # terra::plot() is re-exported for convenience #' plot(r) @@ -63,8 +49,6 @@ read_cog <- function(data = "smips", collection = "totalbucket", day, - longitude = NULL, - latitude = NULL, api_key = get_key()) { day <- .check_date(day) @@ -94,6 +78,23 @@ read_cog <- function(data = "smips", } } +#' Check that the user hasn't blindly copied the "your_api_key" string from the +#' examples +#' +#' @keywords Internal +#' @autoglobal +#' @noRd + +.check_not_example_api_key <- function(.api_key) { + if (!is.null(.api_key) && .api_key == "your_api_key") { + stop("You have copied the example code and not provided a proper API key. + An API key may be requested from TERN to access this resource. Please + see the help file for {.fn get_key} for more information.", + call. = FALSE) + } + return(invisible(NULL)) +} + #' Validate Days Requested Align With Collection #' #' Not all dates are offered by all collections. This checks the user inputs to diff --git a/man/read_cog.Rd b/man/read_cog.Rd index fbe9336..a0e03a5 100644 --- a/man/read_cog.Rd +++ b/man/read_cog.Rd @@ -4,14 +4,7 @@ \alias{read_cog} \title{Read COGs from TERN} \usage{ -read_cog( - data = "smips", - collection = "totalbucket", - day, - longitude = NULL, - latitude = NULL, - api_key = get_key() -) +read_cog(data = "smips", collection = "totalbucket", day, api_key = get_key()) } \arguments{ \item{data}{A character vector of the data source to be queried, currently @@ -42,11 +35,6 @@ a string here or use functionality like that from \CRANpkg{keyring}. If nothing is provided, you will be prompted on how to set up your \R session so that it is auto-detected and a browser window will open at the \acronym{TERN} website for you to request a key.} - -\item{lonlat}{A numeric vector of geographic coordinates for a cell or region -entered as x, y (longitude, latitude) coordinates. See argument details -for more. Defaults to \code{NULL} returning all values available in the -\acronym{COG} for Australia.} } \value{ A \link[terra:rast]{terra::rast} object @@ -59,26 +47,10 @@ your active \R session. Currently only Soil Moisture Integration and Prediction System (\acronym{SMIPS}) v1.0 is supported. } -\section{Argument details for \code{lonlat}}{ - -\describe{ -\item{Single point}{To get a specific cell, 1/2 x 1/2 degree, supply a -length-two numeric vector giving the decimal degree longitude and latitude -in that order for data to download,\cr -\emph{e.g.}, \code{lonlat = c(-179.5, -89.5)}.} - -\item{Regional coverage}{To get a region, supply a length-four numeric -vector as lower left (lon, lat) and upper right (lon, lat) coordinates, -\emph{e.g.}, \code{lonlat = c(xmin, ymin, xmax, ymax)} in that order for a -given region, \emph{e.g.}, a bounding box for the south western corner of -Australia: \code{lonlat = c(112.5, -55.5, 115.5, -50.5)}.} -} -} - \examples{ \dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} -r <- read_cog(day = "2024-01-01", api_key = "your_api_key") +r <- read_cog(day = "2024-01-01") # terra::plot() is re-exported for convenience plot(r) diff --git a/man/read_cog_dt.Rd b/man/read_cog_dt.Rd index 379e634..6eecad3 100644 --- a/man/read_cog_dt.Rd +++ b/man/read_cog_dt.Rd @@ -52,22 +52,6 @@ your active \R session as a \CRANpkg{data.table} object. Currently only Soil Moisture Integration and Prediction System (\acronym{SMIPS}) v1.0 is supported. } -\section{Argument details for \code{lonlat}}{ - -\describe{ -\item{Single point}{To get a specific cell, 1/2 x 1/2 degree, supply a -length-two numeric vector giving the decimal degree longitude and latitude -in that order for data to download,\cr -\emph{e.g.}, \code{lonlat = c(-179.5, -89.5)}.} - -\item{Regional coverage}{To get a region, supply a length-four numeric -vector as lower left (lon, lat) and upper right (lon, lat) coordinates, -\emph{e.g.}, \code{lonlat = c(xmin, ymin, xmax, ymax)} in that order for a -given region, \emph{e.g.}, a bounding box for the south western corner of -Australia: \code{lonlat = c(112.5, -55.5, 115.5, -50.5)}.} -} -} - \examples{ \dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} @@ -83,4 +67,7 @@ r Other COGs: \code{\link{read_cog}()} } +\author{ +Adam H. Sparks \email{adamhsparks@curtin.edu.au} +} \concept{COGs} From 2ac4322fca614b434a19af1e1905422da7e4869b Mon Sep 17 00:00:00 2001 From: "Adam H. Sparks" Date: Mon, 4 Nov 2024 15:28:31 +0800 Subject: [PATCH 8/9] remove latlon, externally handled --- R/globals.R | 1 - R/read_cog.R | 4 ---- 2 files changed, 5 deletions(-) diff --git a/R/globals.R b/R/globals.R index 81d77b3..594cc7e 100644 --- a/R/globals.R +++ b/R/globals.R @@ -1,6 +1,5 @@ # Generated by roxyglobals: do not edit by hand utils::globalVariables(c( - "identifier", # <.check_lonlat> NULL )) diff --git a/R/read_cog.R b/R/read_cog.R index a7afb1e..5f10245 100644 --- a/R/read_cog.R +++ b/R/read_cog.R @@ -54,10 +54,6 @@ read_cog <- function(data = "smips", day <- .check_date(day) url_year <- lubridate::year(day) - - user_longitude <- lonlat["longitude"] - user_latitude <- lonlat["latitude"] - if (data == "smips") { collection_url <- .make_smips_url(.collection = collection, .day = day) r <- (terra::rast( From 245b3713dbd2c20323985a5224959d7bd876c039 Mon Sep 17 00:00:00 2001 From: "Adam H. Sparks" Date: Mon, 4 Nov 2024 15:28:41 +0800 Subject: [PATCH 9/9] add codemeta.json --- .Rbuildignore | 1 + DESCRIPTION | 1 - codemeta.json | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 codemeta.json diff --git a/.Rbuildignore b/.Rbuildignore index 0870aec..894b93d 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -7,3 +7,4 @@ ^_pkgdown\.yml$ ^docs$ ^pkgdown$ +^codemeta\.json$ diff --git a/DESCRIPTION b/DESCRIPTION index ff21b6e..4088e8a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,7 +19,6 @@ Roxygen: list(markdown = TRUE, roclets = c("collate", "namespace", "rd", RoxygenNote: 7.3.2 Imports: cli, - curl, data.table, lubridate, rlang, diff --git a/codemeta.json b/codemeta.json new file mode 100644 index 0000000..59cc9fb --- /dev/null +++ b/codemeta.json @@ -0,0 +1,131 @@ +{ + "@context": "https://doi.org/10.5063/schema/codemeta-2.0", + "@type": "SoftwareSourceCode", + "identifier": "nert", + "description": "Provides access to Australia's TERN (Terrestrial Ecosystem Research Network) data through the API, .", + "name": "nert: An API Client for TERN Data", + "license": "https://spdx.org/licenses/MIT", + "version": "0.0.0.9000", + "programmingLanguage": { + "@type": "ComputerLanguage", + "name": "R", + "url": "https://r-project.org" + }, + "runtimePlatform": "R version 4.4.1 (2024-06-14)", + "author": [ + { + "@type": "Person", + "givenName": "Russell", + "familyName": "Edson", + "email": "russell.edson@adelaide.edu.au" + } + ], + "maintainer": [ + { + "@type": "Person", + "givenName": "Adam H.", + "familyName": "Sparks", + "email": "adamhsparks@gmail.com", + "@id": "https://orcid.org/0000-0002-0061-8359" + } + ], + "softwareSuggestions": [ + { + "@type": "SoftwareApplication", + "identifier": "roxyglobals", + "name": "roxyglobals", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=roxyglobals" + }, + { + "@type": "SoftwareApplication", + "identifier": "testthat", + "name": "testthat", + "version": ">= 3.0.0", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=testthat" + } + ], + "softwareRequirements": { + "1": { + "@type": "SoftwareApplication", + "identifier": "cli", + "name": "cli", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=cli" + }, + "2": { + "@type": "SoftwareApplication", + "identifier": "data.table", + "name": "data.table", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=data.table" + }, + "3": { + "@type": "SoftwareApplication", + "identifier": "lubridate", + "name": "lubridate", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=lubridate" + }, + "4": { + "@type": "SoftwareApplication", + "identifier": "rlang", + "name": "rlang", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=rlang" + }, + "5": { + "@type": "SoftwareApplication", + "identifier": "terra", + "name": "terra", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=terra" + }, + "6": { + "@type": "SoftwareApplication", + "identifier": "utils", + "name": "utils" + }, + "SystemRequirements": null + }, + "fileSize": "173.769KB", + "codeRepository": "https://github.com/AAGI-AUS/nert", + "readme": "https://github.com/AAGI-AUS/nert/blob/main/README.md", + "contIntegration": ["https://github.com/AAGI-AUS/nert/actions/workflows/R-CMD-check.yaml", "https://app.codecov.io/gh/AAGI-AUS/nert"] +}