diff --git a/DESCRIPTION b/DESCRIPTION index ff66df7..a195276 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -47,7 +47,8 @@ Suggests: sf, stars, testthat (>= 3.0.0), - fs + fs, + gdalraster Config/testthat/edition: 3 URL: https://github.com/njtierney/geotargets, http://geotargets.njtierney.com BugReports: https://github.com/njtierney/geotargets/issues diff --git a/R/tar-terra-rast.R b/R/tar-terra-rast.R index c5eb1b4..bc76dd3 100644 --- a/R/tar-terra-rast.R +++ b/R/tar-terra-rast.R @@ -83,13 +83,22 @@ tar_terra_rast <- function(name, drv <- get_gdal_available_driver_list("raster") filetype <- rlang::arg_match0(filetype, drv$name) - # currently only "drop" and "zip" are valid options + # various methods for packaging geospatial data and auxiliary files preserve_metadata <- preserve_metadata %||% "drop" - preserve_metadata <- rlang::arg_match0(preserve_metadata, c("drop", "zip")) + preserve_metadata <- rlang::arg_match0(preserve_metadata, c("drop", "zip", "gdalraster_sozip")) # ensure that user-passed `resources` doesn't include `custom_format` check_user_resources(resources) + if (preserve_metadata == "gdalraster_sozip") { + check_pkg_installed("gdalraster") + } + + # ensure that user-passed `resources` doesn't include `custom_format` + if ("custom_format" %in% names(resources)) { + cli::cli_abort("{.val custom_format} cannot be supplied to targets created with {.fn tar_terra_rast}") + } + name <- targets::tar_deparse_language(substitute(name)) envir <- targets::tar_option_get("envir") @@ -135,13 +144,17 @@ tar_terra_rast <- function(name, } tar_rast_read <- function(preserve_metadata) { - switch(preserve_metadata, - zip = function(path) { - tmp <- withr::local_tempdir() - zip::unzip(zipfile = path, exdir = tmp) - terra::rast(file.path(tmp, basename(path))) - }, - drop = function(path) terra::rast(path) + switch( + preserve_metadata, + zip = function(path) { + tmp <- withr::local_tempdir() + zip::unzip(zipfile = path, exdir = tmp) + terra::rast(file.path(tmp, basename(path))) + }, + gdalraster_sozip = function(path) { + terra::rast(paste0("/vsizip/{", path, "}/", basename(path))) + }, + drop = function(path) terra::rast(path) ) } @@ -179,6 +192,34 @@ tar_rast_write <- function(filetype, gdal, preserve_metadata) { overwrite = TRUE, gdal = gdal ) + }, + gdalraster_sozip = function(object, path) { + tmp <- withr::local_tempdir() + dirpath <- file.path(tmp, dirname(path)) + tmppath <- file.path(tmp, path) + dir.create(dirpath, recursive = TRUE) + terra::writeRaster( + object, + tmppath, + filetype = filetype, + overwrite = TRUE, + gdal = gdal + ) + raster_files <- list.files(dirpath, full.names = TRUE) + # create seek-optimized zip file using gdalraster + # always create sozip regardless of file size (sozip_enabled = "YES") + gdalraster::addFilesInZip( + path, + raster_files, + full_paths = FALSE, + overwrite = TRUE, + sozip_enabled = "YES", + num_threads = 1, + quiet = TRUE + ) + # TODO: allow user control of number of threads? + # how does num_threads interact multiple workers etc.? + unlink(tmppath) } ) } diff --git a/tests/testthat/test-tar-terra-rast.R b/tests/testthat/test-tar-terra-rast.R index 2acdef3..2dc1377 100644 --- a/tests/testthat/test-tar-terra-rast.R +++ b/tests/testthat/test-tar-terra-rast.R @@ -151,3 +151,32 @@ tar_test("metadata is maintained", { expect_equal(terra::units(x), rep("m", 3)) expect_equal(terra::time(x), as.Date("2024-10-01") + c(0, 1, 2)) }) + +tar_test("metadata is maintained (gdalraster SOZip)", { + skip_if_not_installed("gdalraster") + tar_script({ + library(targets) + library(geotargets) + library(terra) + geotargets_option_set(terra_preserve_metadata = "gdalraster_sozip") + make_rast <- function() { + f <- system.file("ex/elev.tif", package = "terra") + r <- terra::rast(f) + r <- c(r, r + 10, r / 2) + terra::units(r) <- rep("m", 3) + terra::time(r) <- as.Date("2024-10-01") + c(0, 1, 2) + terra::metags(r) <- "FOO=BAR" + terra::metags(r, layer = 1) <- "asdf=hjkl" + r + } + list( + tar_terra_rast(r, make_rast(), filetype = "HFA") + ) + }) + tar_make() + x <- tar_read(r) + expect_equal(terra::units(x), rep("m", 3)) + expect_equal(terra::time(x), as.Date("2024-10-01") + c(0, 1, 2)) + expect_equal(terra::metags(x), c(FOO = "BAR")) + expect_equal(terra::metags(x, 1), data.frame(layer = 1, name = "asdf", value = "hjkl")) +})