Closed Aariq closed 8 months ago
Related comment from PR #7:
I had a bit of a play and I couldn't quite come up with one, I thought you could pass the argument in and R's lexical scoping would handle it, but not quite.
I think this might be another way to implement this - it is initially more code, but the core of the function I think becomes easier to extend. Thank you to @maelle for showing me https://rlang.r-lib.org/reference/arg_match.html
write_raster_gtiff <- function(object, path) {
function(object, path) {
terra::writeRaster(
x = object,
filename = path,
overwrite = TRUE,
filetype = "GTiff"
)
}
}
write_raster_netcdf <- function(object, path) {
function(object, path) {
terra::writeRaster(
x = object,
filename = path,
overwrite = TRUE,
filetype = "netCDF"
)
}
}
create_write_fun <- function(filetype = c("GTiff", "netCDF")) {
rlang::arg_match(filetype)
switch(filetype,
"GTiff" = write_raster_netcdf(filetype),
"netCDF" = write_raster_gtiff(filetype)
)
}
create_write_fun("GTiff")
#> function(object, path) {
#> terra::writeRaster(
#> x = object,
#> filename = path,
#> overwrite = TRUE,
#> filetype = "netCDF"
#> )
#> }
#> <environment: 0x14f561368>
create_write_fun("netCDF")
#> function(object, path) {
#> terra::writeRaster(
#> x = object,
#> filename = path,
#> overwrite = TRUE,
#> filetype = "GTiff"
#> )
#> }
#> <environment: 0x139751520>
create_write_fun("wat")
#> Error in `create_write_fun()`:
#> ! `filetype` must be one of "GTiff" or "netCDF", not "wat".
Created on 2024-03-11 with reprex v2.1.0
_Originally posted by @njtierney in https://github.com/njtierney/geotargets/pull/7#discussion_r1519025261_
We don't actually need to write it out each time!
Here's a demo that shows that filetype
is just not evaluated until you use the function:
write_raster_filetype <- function(filetype) {
function(object, path) {
cat(filetype)
# terra::writeRaster(
# x = object,
# filename = path,
# overwrite = TRUE,
# filetype = filetype
# )
}
}
create_write_fun <- function(filetype) {
rlang::arg_match0(filetype, c("GTiff", "netCDF"))
write_raster_filetype(filetype)
}
thingy <- create_write_fun("GTiff")
thingy
#> function(object, path) {
#> cat(filetype)
#> # terra::writeRaster(
#> # x = object,
#> # filename = path,
#> # overwrite = TRUE,
#> # filetype = filetype
#> # )
#> }
#> <environment: 0x1306915c0>
# but then it prints the filetype!
thingy()
#> GTiff
Created on 2024-03-12 with reprex v2.1.0
I think we can generalize the above approaches a bit further so we do not have to pre-define functions for all the combinations of filetype
, nor do we have to independently define the choices for filetype
. I define a function create_format_terra_raster()
(could just be format_terra_raster()
) to do the work.
As above, when the supplied write
function is evaluated by targets, the filetype
or whatever object have you that is not an argument of the constructed function result will not be defined.
To work around this, we can create a function of the right form, then modify the body of that function to inject constant values for filetype
or other parameters. So while we are at it we can also specify custom GDAL creation options via gdal
argument to writeRaster()
Something like:
tar_terra_rast <- function(name,
command,
pattern = NULL,
filetype = NULL,
gdal = NULL,
...,
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")) {
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
)
# could pull defaults from geotargets package options
if (is.null(filetype)) {
filetype <- "GTiff"
}
targets::tar_target_raw(
name = name,
command = command,
pattern = pattern,
packages = packages,
library = library,
format = create_format_terra_raster(filetype = filetype, gdal = gdal, ...),
# ...
)
}
#' @param filetype File format expressed as GDAL driver names passed to `terra::writeRaster()`
#' @param gdal GDAL driver specific datasource creation options passed to `terra::writeRaster()`
#' @param ... Additional arguments not yet used
#' @noRd
create_format_terra_raster <- function(filetype, gdal, ...) {
if (!requireNamespace("terra")) {
stop("package 'terra' is required", call. = FALSE)
}
# get list of drivers available for writing depending on what the user's GDAL supports
drv <- terra::gdal(drivers = TRUE)
drv <- drv[drv$type == "raster" & grepl("write", drv$can), ]
filetype <- match.arg(filetype, drv$name)
if (is.null(filetype)) {
filetype <- "GTiff"
}
.write_terra_raster <- function(object, path) {
terra::writeRaster(
object,
path,
filetype = NULL,
overwrite = TRUE,
gdal = NULL
)
}
body(.write_terra_raster)[[2]][["filetype"]] <- filetype
body(.write_terra_raster)[[2]][["gdal"]] <- gdal
targets::tar_format(
read = function(path) terra::rast(path),
write = .write_terra_raster,
marshal = function(object) terra::wrap(object),
unmarshal = function(object) terra::unwrap(object)
)
}
That's cool. Never seen body()
before, but that's what I was looking for.
Implemented for tar_terra_rast()
in #15 but still needs implementation in tar_terra_vect()
If
tar_*
functions are specific to packages and data types, then adding afiletype
argument somewhere would be a way for users to override defaults for what kind of file targets are stored as (e.g. GeoTIFF vs netCDF). I could imaginefiletype
being an argument totar_terra_rast()
or an argument to a function supplied to theformat
argument oftar_terra_rast()
For example: Option 1
Option 2
where
format_terra_rast()
returns the result of a call totar_format()
The first option is probably preferable, unless there are other customizations that users might need to do to the format