Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue81 catch errors #89

Merged
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: rsi
Title: Efficiently Retrieve and Process Satellite Imagery
Version: 0.3.1.9000
Version: 0.3.2
Authors@R: c(
person("Michael", "Mahoney", , "[email protected]", role = c("aut", "cre"),
comment = c(ORCID = "0000-0003-2402-304X")),
Expand Down
7 changes: 7 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# rsi (development version)

* Failed downloads and merges should now be handled a bit better. Thanks
to @h-a-graham for #89 and to @lucas-johnson for #81.

* If `composite = NULL` and the resource has duplicated asset timestamps,
`get_stac_data()` will now generate unique filenames (rather than saving
multiple files to the same path). Thanks to @h-a-graham for #89 and #90.

# rsi 0.3.1

* A test that requires online resources is now skipped on CRAN. There are
Expand Down
26 changes: 23 additions & 3 deletions R/download.R
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,31 @@ rsi_download_rasters <- function(items,
)
},
error = function(e) {
rlang::warn(
glue::glue(
"Failed to download {items$features[[which_item]]$id %||% 'UNKNOWN'} from {items$features[[which_item]]$properties$datetime %||% 'UNKNOWN'}" # nolint
# stop if failure occurs when merging.
if (merge) {
rlang::abort(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this was a warning in the past in the hope that users would be able to resume an interrupted download, or take the successful files and run with those... but I don't know if that's actually that easy all things considered. Happy to leave as an abort and revisit in the future though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So (if I understand this correctly) the error will now only be thrown if the user is attempting to merge multiple files in this download step - for example when composite_fun = "merge" - I believe this was the main problem in #81 where gdalwarp was attempting to warp >180 input source files into a single one. In the common use case where we are using the other composite methods or composite=NULL, only one source file passes through this itertor at a time and so, the warning will be triggered as expected. Does that sound right to you? as you say happy to revisit if it causes futher issues :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thinking is that you could move forward with your downloaded 180 tiles and figure stuff out from there. Erroring here means (I think!) that no matter what you do next, you need to re-download anything that rsi did successfully grab, even if you decide to VRT your tiles or something like that. I haven't actually traced the logic thoroughly to see if that's actually what happened, though -- obviously enough I sort of lost track of things here as it was!

glue::glue(
"GDAL warp failed when attempting to merge ",
"{length(unlist(feature_iter))} items"
),
class = "rsi_download_warp_error", parent = e
)
}

expr1 <- items$features[[which_item]]$id %||% "UNKNOWN"
expr2 <- items$features[[which_item]]$properties$datetime %||% "UNKNOWN"

err_msg <- glue::glue(
"Failed to download {expr1} from {expr2}" # nolint
)

# stop if failure occurs when there is only one item to download.
if (length(feature_iter) == 1) {
rlang::abort(err_msg, class = "rsi_download_warp_error", parent = e)
}

# warn if failure occurs when multiple items are being downloaded.
rlang::warn(err_msg, parent = e)
download_locations[which_item, ] <<- NA
}
)
Expand Down
86 changes: 51 additions & 35 deletions R/get_stac_data.R
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@
#' (i.e., to aggregate pixel values from multiple images into a single value).
#' Options include "merge", which 'stamps' images on top of one another such that
#' the "last" value downloaded for a pixel -- which isn't guaranteed to be the most
#' recent one -- will be the only value used, or any of "sum", "mean", "median",
#' recent one -- will be the only value used, or any of "sum", "mean", "median",
#' "min", or "max", which consider all values available at each pixel.
#' Set to `NULL` to not composite
#' (i.e., to rescale and save each individual file independently).
Expand All @@ -143,7 +143,7 @@
#' argument of [sf::gdal_utils()]. The same set of options are used for all
#' downloaded data and the final output images; this means that some common
#' options (for instance, `PREDICTOR=3`) may cause errors if bands are of
#' varying data types. The default values are provided by
#' varying data types. The default values are provided by
#' [rsi_gdalwarp_options()].
#' @param gdal_config_options Options passed to `gdalwarp` through the
#' `config_options` argument of [sf::gdal_utils()]. The default values are
Expand Down Expand Up @@ -186,13 +186,13 @@
#' end_date = "2022-08-30",
#' output_filename = tempfile(fileext = ".tif")
#' )
#'
#' landsat_image |>
#'
#' landsat_image |>
#' terra::rast() |>
#' terra::stretch() |>
#' terra::plotRGB()
#'
#' # The `get_*_imagery()` functions will download
#'
#' # The `get_*_imagery()` functions will download
#' # all available "data" assets by default
#' # (usually including measured values, and excluding derived bands)
#' sentinel1_data <- get_sentinel1_imagery(
Expand All @@ -202,17 +202,17 @@
#' output_filename = tempfile(fileext = ".tif")
#' )
#' names(terra::rast(sentinel1_data))
#'
#'
#' # You can see what bands will be downloaded by a function
#' # by inspecting the corresponding `band_mapping` object:
#' sentinel2_band_mapping$planetary_computer_v1
#'
#'
#' # And you can add additional assets using `c()`:
#' c(
#' sentinel2_band_mapping$planetary_computer_v1,
#' "scl"
#' )
#'
#'
#' # Or subset the assets downloaded using `[` or `[[`:
#' sentinel2_imagery <- get_sentinel2_imagery(
#' aoi,
Expand All @@ -222,7 +222,7 @@
#' output_filename = tempfile(fileext = ".tif")
#' )
#' names(terra::rast(sentinel2_imagery))
#'
#'
#' # If you're downloading data for a particularly large AOI,
#' # and can't composite the resulting images or want to make
#' # sure you can continue an interrupted download,
Expand All @@ -239,7 +239,7 @@
#' )
#' }
#' )
#' # You'll get a list of tiles that you can then composite or
#' # You'll get a list of tiles that you can then composite or
#' # work with however you wish:
#' unlist(tiles)
#'
Expand Down Expand Up @@ -273,6 +273,17 @@ get_stac_data <- function(aoi,
)
}

aoi_bbox <- sf::st_bbox(aoi)
if (aoi_bbox$xmin == aoi_bbox$xmax | aoi_bbox$ymin == aoi_bbox$ymax) {
rlang::abort(
c(
"`aoi` has no extent.",
"i" = "This can occur when `aoi` is a single point geometry."
),
class = "rsi_aoi_is_point"
)
}

if (sf::st_is_longlat(aoi) && !(is.null(pixel_x_size) || is.null(pixel_y_size)) && all(c(pixel_x_size, pixel_y_size) %in% c(10, 30))) {
rlang::warn(
c(
Expand Down Expand Up @@ -360,13 +371,14 @@ get_stac_data <- function(aoi,
if (is.null(asset_names)) {
asset_names <- rstac::items_assets(items)
if (length(asset_names) > 1) {
rlang::warn(c(
"`asset_names` was `NULL`, so rsi is attempting to download all assets in items in this collection.",
i = "This includes multiple assets, so rsi is attempting to download all of them using the same download function.",
i = "This might cause errors or not be what you want! Specify `asset_names` to fix this (and to silence this warning)."
),
class = "rsi_missing_asset_names"
)
rlang::warn(
c(
"`asset_names` was `NULL`, so rsi is attempting to download all assets in items in this collection.",
i = "This includes multiple assets, so rsi is attempting to download all of them using the same download function.",
i = "This might cause errors or not be what you want! Specify `asset_names` to fix this (and to silence this warning)."
),
class = "rsi_missing_asset_names"
)
}
}
if (is.null(names(asset_names))) names(asset_names) <- asset_names
Expand Down Expand Up @@ -412,6 +424,9 @@ get_stac_data <- function(aoi,
if (!is.null(stats::na.action(download_results))) {
items$features[stats::na.action(download_results)] <- NULL
}

temp_ras_files <- unlist(download_results)
on.exit(file.remove(temp_ras_files), add = TRUE)
# mask
if (!is.null(mask_band)) {
download_results <- rsi_apply_masks(
Expand Down Expand Up @@ -440,10 +455,7 @@ get_stac_data <- function(aoi,
lapply(download_results, rescale_band, scale_strings)
}

on.exit(file.remove(unlist(download_results)), add = TRUE)

if (drop_mask_band) items_urls[[mask_band]] <- NULL

mapply(
function(in_bands, vrt) {
stack_rasters(
Expand All @@ -462,7 +474,8 @@ get_stac_data <- function(aoi,
app <- tryCatch(rstac::items_datetime(items), error = function(e) NA)
app <- gsub(":", "", app) # #29, #32
if (any(is.na(app))) app <- NULL
app <- app %||% seq_along(download_results)
app <- app %||% as.character(seq_along(download_results))
app <- make.unique(app, sep = "_")

output_filename <- paste0(
tools::file_path_sans_ext(output_filename),
Expand Down Expand Up @@ -724,15 +737,17 @@ rsi_composite_bands <- function(download_locations,
)
)
} else {
do.call(
terra::mosaic,
list(
x = terra::sprc(lapply(download_locations[[band_name]], terra::rast)),
fun = composite_function,
filename = out_file,
overwrite = TRUE
capture.output({
do.call(
terra::mosaic,
list(
x = terra::sprc(lapply(download_locations[[band_name]], terra::rast)),
fun = composite_function,
filename = out_file,
overwrite = TRUE
)
)
)
})
}

out_file
Expand Down Expand Up @@ -845,11 +860,12 @@ get_rescaling_formula <- function(items, band_name, element) {
)

if (length(unique(elements)) != 1) {
rlang::warn(c(
glue::glue("Images in band {band_name} have different {element}s."),
i = "Returning images without rescaling."
),
class = "rsi_multiple_scaling_formulas"
rlang::warn(
c(
glue::glue("Images in band {band_name} have different {element}s."),
i = "Returning images without rescaling."
),
class = "rsi_multiple_scaling_formulas"
)
elements <- NA_real_
}
Expand Down
Binary file modified data/alos_palsar_band_mapping.rda
Binary file not shown.
Binary file modified data/dem_band_mapping.rda
Binary file not shown.
Binary file modified data/landsat_band_mapping.rda
Binary file not shown.
Binary file modified data/sentinel1_band_mapping.rda
Binary file not shown.
Binary file modified data/sentinel2_band_mapping.rda
Binary file not shown.
13 changes: 13 additions & 0 deletions tests/testthat/test-get_stac_data.R
Original file line number Diff line number Diff line change
Expand Up @@ -386,3 +386,16 @@ test_that("Providing no asset names fires the expected warning", {
)
)
})

test_that("Providing a single point geometry throws an error", {
expect_error(
get_landsat_imagery(
aoi = sf::st_sfc(sf::st_point(c(-74.912131, 44.080410)), crs = 4326),
start_date = "2022-06-01",
end_date = "2022-08-01",
composite_function = NULL,
output_filename = tempfile(fileext = ".tif")
),
class = "rsi_aoi_is_point"
)
})
Loading