From 5b284ddb8389c7914072b84101fc9cb01d906b1c Mon Sep 17 00:00:00 2001 From: Andrew Gene Brown Date: Sun, 28 Apr 2024 15:07:04 -0700 Subject: [PATCH 1/3] tar_terra_rast_wrap: draft multi-target method to preserve SpatRaster metadata - Source data file is written using `terra::wrapCache()` to a user-specified cache directory - Target is created for PackedSpatRaster based on cache - Target is created for cache files - Add cache-managing functions `geotargets_destroy_cache()` and `geotargets_init_cache()` --- NAMESPACE | 3 + R/geotargets-cache.R | 47 ++++++ R/geotargets-option.R | 14 +- R/tar-terra-wrap.R | 153 +++++++++++++++++ man/geotargets-cache.Rd | 44 +++++ man/geotargets-options.Rd | 8 +- man/tar_terra_rast_wrap.Rd | 255 +++++++++++++++++++++++++++++ tests/testthat/_snaps/tar-terra.md | 40 +++++ tests/testthat/test-tar-terra.R | 52 ++++++ 9 files changed, 611 insertions(+), 5 deletions(-) create mode 100644 R/geotargets-cache.R create mode 100644 R/tar-terra-wrap.R create mode 100644 man/geotargets-cache.Rd create mode 100644 man/tar_terra_rast_wrap.Rd diff --git a/NAMESPACE b/NAMESPACE index 063312a..d7941e6 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,8 +1,11 @@ # Generated by roxygen2: do not edit by hand +export(geotargets_destroy_cache) +export(geotargets_init_cache) export(geotargets_option_get) export(geotargets_option_set) export(tar_terra_rast) +export(tar_terra_rast_wrap) export(tar_terra_sprc) export(tar_terra_vect) importFrom(rlang,"%||%") diff --git a/R/geotargets-cache.R b/R/geotargets-cache.R new file mode 100644 index 0000000..c70593c --- /dev/null +++ b/R/geotargets-cache.R @@ -0,0 +1,47 @@ +#' Manage geotargets Cache Files +#' +#' The `geotargets` cache is a folder containing one subfolder for each target created using `tar_terra_rast_wrap()` method. Each subfolder contains one or more files containing data and metadata to support a `terra` `SpatRaster` object. +#' +#' The cache directory can be customized using `geotargets_option_set("cache.dir")` or environment variable `GEOTARGETS_CACHE_DIR`. +#' +#' Periodically you may want to purge files in the cache using `geotargets_destroy_cache()`. +#' +#' @param name character. Target name (default: `NULL` will delete all target cache files) +#' @param init logical. Re-create empty cache directory? Default: `FALSE` +#' +#' @return integer. 0 for success, 1 for failure, invisibly. +#' @export +#' @rdname geotargets-cache +#' @examples +#' \dontrun{ +#' +#' # delete cache folder for target named "foo" +#' geotargets_destroy_cache("foo") +#' +#' # create empty folder +#' geotargets_init_cache("foo") +#' +#' # delete and recreate folder +#' geotargets_destroy_cache("foo", init = TRUE) +#' +#' # delete all cache files +#' geotargets_destroy_cache() +#' +#' } +geotargets_destroy_cache <- function(name = NULL, init = FALSE) { + cachedir <- geotargets_option_get("cache.dir") + target_cache_dir <- file.path(cachedir %||% "geotargets_cache", name %||% "") + res <- unlink(target_cache_dir, recursive = TRUE) + if (init) geotargets_init_cache(name = name) + invisible(res) +} + +#' @rdname geotargets-cache +#' @export +geotargets_init_cache <- function(name = NULL) { + cachedir <- geotargets_option_get("cache.dir") + target_cache_dir <- file.path(cachedir %||% "geotargets_cache", name %||% "") + dir.create(target_cache_dir, + showWarnings = FALSE, + recursive = TRUE) +} diff --git a/R/geotargets-option.R b/R/geotargets-option.R index b8c7d8a..a422076 100644 --- a/R/geotargets-option.R +++ b/R/geotargets-option.R @@ -21,7 +21,10 @@ #' a unique set of creation options. For example, with the default `"GeoJSON"` #' driver: #' -#' +#' @param cache_dir character. Path to directory where file sources for +#' `PackedSpatRaster` objects can be stored. Default: `"geotargets_cache"` +#' when `geotargets::geotargets_option_get("cache.dir")` +#' is not set. #' @details #' These options can also be set using `options()`. For example, #' `geotargets_options_set(gdal_raster_driver = "GTiff")` is equivalent to @@ -34,14 +37,16 @@ geotargets_option_set <- function( gdal_raster_driver = NULL, gdal_raster_creation_options = NULL, gdal_vector_driver = NULL, - gdal_vector_creation_options = NULL + gdal_vector_creation_options = NULL, + cache_dir = NULL ) { options( "geotargets.gdal.raster.driver" = gdal_raster_driver, "geotargets.gdal.raster.creation.options" = gdal_raster_creation_options, "geotargets.gdal.vector.driver" = gdal_raster_creation_options, - "geotargets.gdal.vector.creation.options" = gdal_raster_creation_options + "geotargets.gdal.vector.creation.options" = gdal_raster_creation_options, + "geotargets.cache.dir" = cache_dir ) } @@ -58,7 +63,8 @@ geotargets_option_get <- function(name) { "geotargets.gdal.raster.driver", "geotargets.gdal.raster.creation.options", "geotargets.gdal.vector.driver", - "geotargets.gdal.vector.creation.options" + "geotargets.gdal.vector.creation.options", + "geotargets.cache.dir" )) env_name <- gsub("\\.", "_", toupper(option_name)) diff --git a/R/tar-terra-wrap.R b/R/tar-terra-wrap.R new file mode 100644 index 0000000..8c2fb7d --- /dev/null +++ b/R/tar-terra-wrap.R @@ -0,0 +1,153 @@ +#' Create a wrapped terra SpatRaster target +#' +#' Provides a target format for [terra::SpatRaster-class] objects backed by +#' [terra::PackedSpatRaster-class] and file-based targets in the `geotargets` +#' cache folder (`cachedir`). +#' +#' @param filetype character. File format expressed as GDAL driver names passed +#' to [terra::writeRaster()] +#' @param gdal character. GDAL driver specific datasource creation options +#' passed to [terra::writeRaster()] +#' @param cachedir character. Path to directory where file sources for `PackedSpatRaster` objects can be stored. Default: `"geotargets_cache"` when `geotargets::geotargets_option_get("cache.dir")` is not set. +#' @param ... Additional arguments not yet used +#' @seealso [geotargets_destroy_cache()] [geotargets_init_cache()] +#' @note Although you may pass any supported GDAL vector driver to the +#' `filetype` argument, not all formats are guaranteed to work with +#' `geotargets`. At the moment, we have tested `GTiff` and `GPKG` and +#' they appear to work generally. +#' +#' @inheritParams targets::tar_target +#' @importFrom rlang %||% arg_match0 +#' @seealso [targets::tar_target_raw()] +#' @export +#' @examples +#' if (Sys.getenv("TAR_LONG_EXAMPLES") == "true") { +#' targets::tar_dir({ # tar_dir() runs code from a temporary directory. +#' library(geotargets) +#' targets::tar_script({ +#' list( +#' geotargets::tar_terra_rast( +#' terra_rast_example, +#' system.file("ex/elev.tif", package = "terra") |> terra::rast() +#' ) +#' ) +#' }) +#' targets::tar_make() +#' x <- targets::tar_read(terra_rast_example) +#' }) +#'} +tar_terra_rast_wrap <- function(name, + command, + pattern = NULL, + filetype = "GTiff", + gdal = geotargets::geotargets_option_get("gdal.raster.creation.options"), + cachedir = geotargets::geotargets_option_get("cache.dir"), + ..., + tidy_eval = targets::tar_option_get("tidy_eval"), + packages = targets::tar_option_get("packages"), + library = targets::tar_option_get("library"), + repository = targets::tar_option_get("repository"), + iteration = targets::tar_option_get("iteration"), + error = targets::tar_option_get("error"), + memory = targets::tar_option_get("memory"), + garbage_collection = targets::tar_option_get("garbage_collection"), + deployment = targets::tar_option_get("deployment"), + priority = targets::tar_option_get("priority"), + resources = targets::tar_option_get("resources"), + storage = targets::tar_option_get("storage"), + retrieval = targets::tar_option_get("retrieval"), + cue = targets::tar_option_get("cue")) { + filetype <- filetype %||% "GTiff" + gdal <- gdal %||% character(0) + cachedir <- cachedir %||% "geotargets_cache" + + #check that filetype option is available + drv <- get_gdal_available_driver_list("raster") + filetype <- rlang::arg_match0(filetype, drv$name) + + check_pkg_installed("terra") + + name <- targets::tar_deparse_language(substitute(name)) + + envir <- targets::tar_option_get("envir") + + command <- targets::tar_tidy_eval( + expr = as.expression(substitute(command)), + envir = envir, + tidy_eval = tidy_eval + ) + + pattern <- targets::tar_tidy_eval( + expr = as.expression(substitute(pattern)), + envir = envir, + tidy_eval = tidy_eval + ) + + .format_terra_rast_wrap_write <- eval(substitute(function(object, path) { + # TODO: provide mapping of major file types to extensions + extension <- switch(filetype, + "GTiff" = ".tif", + "GPKG" = ".gpkg", + "") + saveRDS(terra::wrapCache(object, + filename = file.path(cachedir, + basename(path), + paste0(basename(path), + extension)), + filetype = filetype, + gdal = gdal, + overwrite = TRUE), + file = path) + }, list(cachedir = cachedir, filetype = filetype, gdal = gdal))) + + geotargets_init_cache(name = name) + + # rast_cache_init <- targets::tar_target_raw( + # paste0(name, "_cache_init"), + # str2expression(paste0("normalizePath(file.path(", + # shQuote(cachedir), ", ", + # shQuote(name), "))")), + # format = "file" + # ) + + rast_wrap <- targets::tar_target_raw( + name = name, + command = command, + pattern = pattern, + packages = packages, + library = library, + format = targets::tar_format( + read = function(path) terra::unwrap(readRDS(path)), + write = .format_terra_rast_wrap_write, + marshal = function(object) terra::wrap(object), + unmarshal = function(object) terra::unwrap(object) + ), + repository = repository, + iteration = iteration, + error = error, + memory = memory, + garbage_collection = garbage_collection, + deployment = deployment, + priority = priority, + resources = resources, + storage = storage, + retrieval = retrieval, + cue = cue + ) + + rast_cache_files <- targets::tar_target_raw( + paste0(name, "_cache_files"), + str2expression(paste0(" + list.files( + file.path(", shQuote(cachedir), ", ", shQuote(name),"), + full.names = TRUE, + recursive = TRUE + )")), + format = "file_fast", + deps = name + ) + + list(#rast_cache_init, + rast_wrap, + rast_cache_files) +} diff --git a/man/geotargets-cache.Rd b/man/geotargets-cache.Rd new file mode 100644 index 0000000..d0992fa --- /dev/null +++ b/man/geotargets-cache.Rd @@ -0,0 +1,44 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/geotargets-cache.R +\name{geotargets_destroy_cache} +\alias{geotargets_destroy_cache} +\alias{geotargets_init_cache} +\title{Manage geotargets Cache Files} +\usage{ +geotargets_destroy_cache(name = NULL, init = FALSE) + +geotargets_init_cache(name = NULL) +} +\arguments{ +\item{name}{character. Target name (default: \code{NULL} will delete all target cache files)} + +\item{init}{logical. Re-create empty cache directory? Default: \code{FALSE}} +} +\value{ +integer. 0 for success, 1 for failure, invisibly. +} +\description{ +The \code{geotargets} cache is a folder containing one subfolder for each target created using \code{tar_terra_rast_wrap()} method. Each subfolder contains one or more files containing data and metadata to support a \code{terra} \code{SpatRaster} object. +} +\details{ +The cache directory can be customized using \code{geotargets_option_set("cache.dir")} or environment variable \code{GEOTARGETS_CACHE_DIR}. + +Periodically you may want to purge files in the cache using \code{geotargets_destroy_cache()}. +} +\examples{ +\dontrun{ + +# delete cache folder for target named "foo" +geotargets_destroy_cache("foo") + +# create empty folder +geotargets_init_cache("foo") + +# delete and recreate folder +geotargets_destroy_cache("foo", init = TRUE) + +# delete all cache files +geotargets_destroy_cache() + +} +} diff --git a/man/geotargets-options.Rd b/man/geotargets-options.Rd index dfbcc77..44ba4e1 100644 --- a/man/geotargets-options.Rd +++ b/man/geotargets-options.Rd @@ -9,7 +9,8 @@ geotargets_option_set( gdal_raster_driver = NULL, gdal_raster_creation_options = NULL, gdal_vector_driver = NULL, - gdal_vector_creation_options = NULL + gdal_vector_creation_options = NULL, + cache_dir = NULL ) geotargets_option_get(name) @@ -37,6 +38,11 @@ a unique set of creation options. For example, with the default \code{"GeoJSON"} driver: \url{https://gdal.org/drivers/vector/geojson.html#layer-creation-options}} +\item{cache_dir}{character. Path to directory where file sources for +\code{PackedSpatRaster} objects can be stored. Default: \code{"geotargets_cache"} +when \code{geotargets::geotargets_option_get("cache.dir")} +is not set.} + \item{name}{character; option name to get.} } \description{ diff --git a/man/tar_terra_rast_wrap.Rd b/man/tar_terra_rast_wrap.Rd new file mode 100644 index 0000000..7796eb7 --- /dev/null +++ b/man/tar_terra_rast_wrap.Rd @@ -0,0 +1,255 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/tar-terra-wrap.R +\name{tar_terra_rast_wrap} +\alias{tar_terra_rast_wrap} +\title{Create a wrapped terra SpatRaster target} +\usage{ +tar_terra_rast_wrap( + name, + command, + pattern = NULL, + filetype = "GTiff", + gdal = geotargets::geotargets_option_get("gdal.raster.creation.options"), + cachedir = geotargets::geotargets_option_get("cache.dir"), + ..., + tidy_eval = targets::tar_option_get("tidy_eval"), + packages = targets::tar_option_get("packages"), + library = targets::tar_option_get("library"), + repository = targets::tar_option_get("repository"), + iteration = targets::tar_option_get("iteration"), + error = targets::tar_option_get("error"), + memory = targets::tar_option_get("memory"), + garbage_collection = targets::tar_option_get("garbage_collection"), + deployment = targets::tar_option_get("deployment"), + priority = targets::tar_option_get("priority"), + resources = targets::tar_option_get("resources"), + storage = targets::tar_option_get("storage"), + retrieval = targets::tar_option_get("retrieval"), + cue = targets::tar_option_get("cue") +) +} +\arguments{ +\item{name}{Symbol, name of the target. A target +name must be a valid name for a symbol in R, and it +must not start with a dot. Subsequent targets +can refer to this name symbolically to induce a dependency relationship: +e.g. \code{tar_target(downstream_target, f(upstream_target))} is a +target named \code{downstream_target} which depends on a target +\code{upstream_target} and a function \code{f()}. In addition, a target's +name determines its random number generator seed. In this way, +each target runs with a reproducible seed so someone else +running the same pipeline should get the same results, +and no two targets in the same pipeline share the same seed. +(Even dynamic branches have different names and thus different seeds.) +You can recover the seed of a completed target +with \code{tar_meta(your_target, seed)} and run \code{\link[targets:tar_seed_set]{tar_seed_set()}} +on the result to locally recreate the target's initial RNG state.} + +\item{command}{R code to run the target.} + +\item{pattern}{Language to define branching for a target. +For example, in a pipeline with numeric vector targets \code{x} and \code{y}, +\code{tar_target(z, x + y, pattern = map(x, y))} implicitly defines +branches of \code{z} that each compute \code{x[1] + y[1]}, \code{x[2] + y[2]}, +and so on. See the user manual for details.} + +\item{filetype}{character. File format expressed as GDAL driver names passed +to \code{\link[terra:writeRaster]{terra::writeRaster()}}} + +\item{gdal}{character. GDAL driver specific datasource creation options +passed to \code{\link[terra:writeRaster]{terra::writeRaster()}}} + +\item{cachedir}{character. Path to directory where file sources for \code{PackedSpatRaster} objects can be stored. Default: \code{"geotargets_cache"} when \code{geotargets::geotargets_option_get("cache.dir")} is not set.} + +\item{...}{Additional arguments not yet used} + +\item{tidy_eval}{Logical, whether to enable tidy evaluation +when interpreting \code{command} and \code{pattern}. If \code{TRUE}, you can use the +"bang-bang" operator \verb{!!} to programmatically insert +the values of global objects.} + +\item{packages}{Character vector of packages to load right before +the target runs or the output data is reloaded for +downstream targets. Use \code{tar_option_set()} to set packages +globally for all subsequent targets you define.} + +\item{library}{Character vector of library paths to try +when loading \code{packages}.} + +\item{repository}{Character of length 1, remote repository for target +storage. Choices: +\itemize{ +\item \code{"local"}: file system of the local machine. +\item \code{"aws"}: Amazon Web Services (AWS) S3 bucket. Can be configured +with a non-AWS S3 bucket using the \code{endpoint} argument of +\code{\link[targets:tar_resources_aws]{tar_resources_aws()}}, but versioning capabilities may be lost +in doing so. +See the cloud storage section of +\url{https://books.ropensci.org/targets/data.html} +for details for instructions. +\item \code{"gcp"}: Google Cloud Platform storage bucket. +See the cloud storage section of +\url{https://books.ropensci.org/targets/data.html} +for details for instructions. +} + +Note: if \code{repository} is not \code{"local"} and \code{format} is \code{"file"} +then the target should create a single output file. +That output file is uploaded to the cloud and tracked for changes +where it exists in the cloud. The local file is deleted after +the target runs.} + +\item{iteration}{Character of length 1, name of the iteration mode +of the target. Choices: +\itemize{ +\item \code{"vector"}: branching happens with \code{vctrs::vec_slice()} and +aggregation happens with \code{vctrs::vec_c()}. +\item \code{"list"}, branching happens with \verb{[[]]} and aggregation happens with +\code{list()}. +\item \code{"group"}: \code{dplyr::group_by()}-like functionality to branch over +subsets of a non-dynamic data frame. +For \code{iteration = "group"}, the target must not by dynamic +(the \code{pattern} argument of \code{\link[targets:tar_target]{tar_target()}} must be left \code{NULL}). +The target's return value must be a data +frame with a special \code{tar_group} column of consecutive integers +from 1 through the number of groups. Each integer designates a group, +and a branch is created for each collection of rows in a group. +See the \code{\link[targets:tar_group]{tar_group()}} function to see how you can +create the special \code{tar_group} column with \code{dplyr::group_by()}. +}} + +\item{error}{Character of length 1, what to do if the target +stops and throws an error. Options: +\itemize{ +\item \code{"stop"}: the whole pipeline stops and throws an error. +\item \code{"continue"}: the whole pipeline keeps going. +\item \code{"abridge"}: any currently running targets keep running, +but no new targets launch after that. +(Visit \url{https://books.ropensci.org/targets/debugging.html} +to learn how to debug targets using saved workspaces.) +\item \code{"null"}: The errored target continues and returns \code{NULL}. +The data hash is deliberately wrong so the target is not +up to date for the next run of the pipeline. +}} + +\item{memory}{Character of length 1, memory strategy. +If \code{"persistent"}, the target stays in memory +until the end of the pipeline (unless \code{storage} is \code{"worker"}, +in which case \code{targets} unloads the value from memory +right after storing it in order to avoid sending +copious data over a network). +If \code{"transient"}, the target gets unloaded +after every new target completes. +Either way, the target gets automatically loaded into memory +whenever another target needs the value. +For cloud-based dynamic files +(e.g. \code{format = "file"} with \code{repository = "aws"}), +this memory strategy applies to the +temporary local copy of the file: +\code{"persistent"} means it remains until the end of the pipeline +and is then deleted, +and \code{"transient"} means it gets deleted as soon as possible. +The former conserves bandwidth, +and the latter conserves local storage.} + +\item{garbage_collection}{Logical, whether to run \code{base::gc()} +just before the target runs.} + +\item{deployment}{Character of length 1. If \code{deployment} is +\code{"main"}, then the target will run on the central controlling R process. +Otherwise, if \code{deployment} is \code{"worker"} and you set up the pipeline +with distributed/parallel computing, then +the target runs on a parallel worker. For more on distributed/parallel +computing in \code{targets}, please visit +\url{https://books.ropensci.org/targets/crew.html}.} + +\item{priority}{Numeric of length 1 between 0 and 1. Controls which +targets get deployed first when multiple competing targets are ready +simultaneously. Targets with priorities closer to 1 get dispatched earlier +(and polled earlier in \code{\link[targets:tar_make_future]{tar_make_future()}}).} + +\item{resources}{Object returned by \code{tar_resources()} +with optional settings for high-performance computing +functionality, alternative data storage formats, +and other optional capabilities of \code{targets}. +See \code{tar_resources()} for details.} + +\item{storage}{Character of length 1, only relevant to +\code{\link[targets:tar_make_clustermq]{tar_make_clustermq()}} and \code{\link[targets:tar_make_future]{tar_make_future()}}. +Must be one of the following values: +\itemize{ +\item \code{"main"}: the target's return value is sent back to the +host machine and saved/uploaded locally. +\item \code{"worker"}: the worker saves/uploads the value. +\item \code{"none"}: almost never recommended. It is only for +niche situations, e.g. the data needs to be loaded +explicitly from another language. If you do use it, +then the return value of the target is totally ignored +when the target ends, but +each downstream target still attempts to load the data file +(except when \code{retrieval = "none"}). + +If you select \code{storage = "none"}, then +the return value of the target's command is ignored, +and the data is not saved automatically. +As with dynamic files (\code{format = "file"}) it is the +responsibility of the user to write to +the data store from inside the target. + +The distinguishing feature of \code{storage = "none"} +(as opposed to \code{format = "file"}) +is that in the general case, +downstream targets will automatically try to load the data +from the data store as a dependency. As a corollary, \code{storage = "none"} +is completely unnecessary if \code{format} is \code{"file"}. +}} + +\item{retrieval}{Character of length 1, only relevant to +\code{\link[targets:tar_make_clustermq]{tar_make_clustermq()}} and \code{\link[targets:tar_make_future]{tar_make_future()}}. +Must be one of the following values: +\itemize{ +\item \code{"main"}: the target's dependencies are loaded on the host machine +and sent to the worker before the target runs. +\item \code{"worker"}: the worker loads the targets dependencies. +\item \code{"none"}: the dependencies are not loaded at all. +This choice is almost never recommended. It is only for +niche situations, e.g. the data needs to be loaded +explicitly from another language. +}} + +\item{cue}{An optional object from \code{tar_cue()} to customize the +rules that decide whether the target is up to date.} +} +\description{ +Provides a target format for \link[terra:SpatRaster-class]{terra::SpatRaster} objects backed by +\link[terra:SpatRaster-class]{terra::PackedSpatRaster} and file-based targets in the \code{geotargets} +cache folder (\code{cachedir}). +} +\note{ +Although you may pass any supported GDAL vector driver to the +\code{filetype} argument, not all formats are guaranteed to work with +\code{geotargets}. At the moment, we have tested \code{GTiff} and \code{GPKG} and +they appear to work generally. +} +\examples{ +if (Sys.getenv("TAR_LONG_EXAMPLES") == "true") { + targets::tar_dir({ # tar_dir() runs code from a temporary directory. + library(geotargets) + targets::tar_script({ + list( + geotargets::tar_terra_rast( + terra_rast_example, + system.file("ex/elev.tif", package = "terra") |> terra::rast() + ) + ) + }) + targets::tar_make() + x <- targets::tar_read(terra_rast_example) + }) +} +} +\seealso{ +\code{\link[=geotargets_destroy_cache]{geotargets_destroy_cache()}} \code{\link[=geotargets_init_cache]{geotargets_init_cache()}} + +\code{\link[targets:tar_target_raw]{targets::tar_target_raw()}} +} diff --git a/tests/testthat/_snaps/tar-terra.md b/tests/testthat/_snaps/tar-terra.md index 28044fc..ffd52e6 100644 --- a/tests/testthat/_snaps/tar-terra.md +++ b/tests/testthat/_snaps/tar-terra.md @@ -47,3 +47,43 @@ 1 Diekirch 2 Diekirch 218 32543 1 Diekirch 3 Redange 259 18664 +# tar_terra_rast_wrap works + + Code + x + Output + class : SpatRaster + dimensions : 90, 95, 1 (nrow, ncol, nlyr) + resolution : 0.008333333, 0.008333333 (x, y) + extent : 5.741667, 6.533333, 49.44167, 50.19167 (xmin, xmax, ymin, ymax) + coord. ref. : lon/lat WGS 84 (EPSG:4326) + source : rast1.gpkg + name : elevation + min value : 141 + max value : 547 + unit : m + +--- + + Code + y + Output + class : SpatRaster + dimensions : 90, 95, 1 (nrow, ncol, nlyr) + resolution : 0.008333333, 0.008333333 (x, y) + extent : 5.741667, 6.533333, 49.44167, 50.19167 (xmin, xmax, ymin, ymax) + coord. ref. : lon/lat WGS 84 (EPSG:4326) + source : rast2.tif + categories : category + name : category + min value : low + max value : hi + +--- + + Code + z + Output + [1] "geotargets_cache/rast1/rast1.gpkg" + [2] "geotargets_cache/rast1/rast1.gpkg.aux.json" + diff --git a/tests/testthat/test-tar-terra.R b/tests/testthat/test-tar-terra.R index a4c550f..8d1cf50 100644 --- a/tests/testthat/test-tar-terra.R +++ b/tests/testthat/test-tar-terra.R @@ -48,3 +48,55 @@ targets::tar_test("tar_terra_vect() works", { expect_snapshot(y) expect_equal(terra::values(x), terra::values(y)) }) + +targets::tar_test("tar_terra_rast_wrap works", { + targets::tar_script({ + + make_rast1 <- function() { + x <- terra::rast(system.file("ex/elev.tif", package = "terra")) + terra::units(x) <- "m" + terra::varnames(x) <- "elev" + x + } + + make_rast2 <- function() { + x <- terra::rast(system.file("ex/elev.tif", package = "terra")) + y <- terra::classify(x, cbind(c(0, 300, 500), + c(300, 500, 1000), + 1:3)) + levels(y) <- data.frame(value = 1:3, + category = c("low", "med", "hi")) + y + } + + list( + geotargets::tar_terra_rast_wrap( + rast1, + make_rast1(), + filetype = "GPKG" + ), + geotargets::tar_terra_rast_wrap( + rast2, + make_rast2(), + filetype = "GTiff" + ) + ) + }) + + targets::tar_make() + + x <- targets::tar_read(rast1) + y <- targets::tar_read(rast2) + z <- targets::tar_read(rast1_cache_files) + x_raw <- readRDS("_targets/objects/rast1") + + expect_s4_class(terra::rast(z[1]), "SpatRaster") + + expect_snapshot(x) + expect_snapshot(y) + expect_snapshot(z) + + expect_true("units" %in% names(x_raw@attributes)) + expect_equal(terra::units(x), "m") + expect_true(!is.null(terra::levels(y))) +}) From 3067d895af44cd9fcdf578920659620501b05f99 Mon Sep 17 00:00:00 2001 From: Andrew Gene Brown Date: Mon, 21 Oct 2024 10:02:11 -0700 Subject: [PATCH 2/3] Update R/geotargets-cache.R Co-authored-by: Eric R. Scott --- R/geotargets-cache.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/geotargets-cache.R b/R/geotargets-cache.R index c70593c..49d1be5 100644 --- a/R/geotargets-cache.R +++ b/R/geotargets-cache.R @@ -40,7 +40,7 @@ geotargets_destroy_cache <- function(name = NULL, init = FALSE) { #' @export geotargets_init_cache <- function(name = NULL) { cachedir <- geotargets_option_get("cache.dir") - target_cache_dir <- file.path(cachedir %||% "geotargets_cache", name %||% "") + target_cache_dir <- file.path(cachedir %||% "_geotargets", name %||% "") dir.create(target_cache_dir, showWarnings = FALSE, recursive = TRUE) From 4b3c82610519bf980761562b10b9c505b719b57f Mon Sep 17 00:00:00 2001 From: Andrew Gene Brown Date: Mon, 21 Oct 2024 10:03:06 -0700 Subject: [PATCH 3/3] Update R/tar-terra-wrap.R Co-authored-by: Eric R. Scott --- R/tar-terra-wrap.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/tar-terra-wrap.R b/R/tar-terra-wrap.R index 8c2fb7d..afd9f30 100644 --- a/R/tar-terra-wrap.R +++ b/R/tar-terra-wrap.R @@ -56,7 +56,8 @@ tar_terra_rast_wrap <- function(name, resources = targets::tar_option_get("resources"), storage = targets::tar_option_get("storage"), retrieval = targets::tar_option_get("retrieval"), - cue = targets::tar_option_get("cue")) { + cue = targets::tar_option_get("cue"), + description = targets::tar_option_get("description")) { filetype <- filetype %||% "GTiff" gdal <- gdal %||% character(0) cachedir <- cachedir %||% "geotargets_cache"