From 4a74c0005b970cb7a70a481402e98c8a40e99a38 Mon Sep 17 00:00:00 2001 From: Michael Mahoney Date: Thu, 28 Mar 2024 12:06:06 -0400 Subject: [PATCH] Make `get_stac_data()` more modular (#54) * Make item filter function always a function * Refactor non-download pieces out of download * Separate progress bars out for each subfunction * Save test files to tmp * Remove noop * Build symmetry * Remove mask_band argument * Make function calls identical * Single download function call * Edit rescale_bands first * Name assets in simple method * Remove nesting * Fix simple download * Reduce download forks * Start making the download function prettier * Start working on exposing composite, download functions And style * Rename default_query_function() * Use new function name * CHECK notes * Fix progress bars * Add NEWS * Add returns documentation * Clean up slightly * Fixes #53 * Add new functions to pkgdown * Fix snaps * Add common use cases vignette Fixes #51 --- DESCRIPTION | 1 + NAMESPACE | 2 + NEWS.md | 14 + R/deprecated.R | 36 ++ R/download.R | 135 ++++++ R/get_stac_data.R | 383 ++++++------------ R/misc.R | 8 + R/query_and_sign.R | 18 +- R/sysdata.rda | Bin 9452 -> 9451 bytes _pkgdown.yml | 2 +- data-raw/band_mappings.R | 20 +- data/alos_palsar_band_mapping.rda | Bin 1993 -> 4809 bytes data/dem_band_mapping.rda | Bin 2160 -> 6715 bytes data/landsat_band_mapping.rda | Bin 2009 -> 4831 bytes data/sentinel1_band_mapping.rda | Bin 1421 -> 4309 bytes data/sentinel2_band_mapping.rda | Bin 2156 -> 5760 bytes man/deprecated.Rd | 28 ++ man/get_stac_data.Rd | 19 +- man/rsi_download_rasters.Rd | 61 +++ ...ult_query_function.Rd => rsi_query_api.Rd} | 16 +- man/sign_planetary_computer.Rd | 2 +- tests/testthat/_snaps/misc.md | 8 +- tests/testthat/test-get_stac_data.R | 10 +- tests/testthat/test-mask_functions.R | 1 - vignettes/articles/How-can-I-.Rmd | 124 ++++++ 25 files changed, 578 insertions(+), 310 deletions(-) create mode 100644 R/deprecated.R create mode 100644 R/download.R create mode 100644 man/deprecated.Rd create mode 100644 man/rsi_download_rasters.Rd rename man/{default_query_function.Rd => rsi_query_api.Rd} (91%) create mode 100644 vignettes/articles/How-can-I-.Rmd diff --git a/DESCRIPTION b/DESCRIPTION index b41acfd..ad8c1ba 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -20,6 +20,7 @@ Imports: future.apply, glue, jsonlite, + lifecycle, proceduralnames, rlang, rstac, diff --git a/NAMESPACE b/NAMESPACE index 6ac9de6..a27aedf 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -17,6 +17,8 @@ export(get_sentinel2_imagery) export(get_stac_data) export(landsat_mask_function) export(landsat_platform_filter) +export(rsi_download_rasters) +export(rsi_query_api) export(sentinel2_mask_function) export(sign_planetary_computer) export(spectral_indices) diff --git a/NEWS.md b/NEWS.md index 0cf3e50..b0d2dab 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,19 @@ # rsi (development version) +* Progress bars have been split into separate bars for downloading, masking, + compositing and so on. + +* `get_stac_data()` gains an argument, `download_function`, which takes a + `STACItemCollection` object and returns a data frame, where columns correspond + to distinct assets, rows correspond to distinct items, and cells contain file + paths to the downloaded data. + +* `rsi_download_rasters()` is a new function that exposes how `get_stac_data()` + downloads assets. + +* `default_query_function()` has been renamed to `rsi_query_api()`. Please + update any code using the old name; it will be removed in a future release. + * `get_alos_palsar_imagery()` and `alos_palsar_mask_function()` are new functions to help you get and mask ALOS PALSAR imagery, respectively. diff --git a/R/deprecated.R b/R/deprecated.R new file mode 100644 index 0000000..df66afe --- /dev/null +++ b/R/deprecated.R @@ -0,0 +1,36 @@ +#' Deprecated functions +#' +#' @description +#' `r lifecycle::badge("deprecated")` +#' +#' These functions have been deprecated in favor of better approaches. +#' +#' * `default_query_function()` was renamed to `rsi_query_api()`. These +#' functions are identical, and the older name will be removed in a future +#' release. +#' +#' @name deprecated +#' @keywords internal +#' @export +default_query_function <- function(bbox, + stac_source, + collection, + start_date, + end_date, + limit, + ...) { + lifecycle::deprecate_warn( + "0.2.0", + "default_query_function()", + "rsi_query_api()" + ) + rsi_query_api( + bbox = bbox, + stac_source = stac_source, + collection = collection, + start_date = start_date, + end_date = end_date, + limit = limit, + ... + ) +} diff --git a/R/download.R b/R/download.R new file mode 100644 index 0000000..11ed0ce --- /dev/null +++ b/R/download.R @@ -0,0 +1,135 @@ +#' Download specific assets from a set of STAC items +#' +#' @param items A `StacItemCollection` object, as returned by [rsi_query_api()]. +#' @param aoi Either an sf(c) object outlining the area of interest to get +#' imagery for, or a `bbox` image containing the bounding box of your AOI. +#' @param merge Logical: for each asset, should data from multiple items be +#' merged into a single downloaded file? If `TRUE`, this returns a single file +#' for each asset, which has been merged via gdalwarp. No resampling or +#' compositing is performed, but rather each pixel uses the last data +#' downloaded. This is fast, but precludes per-item masking and compositing. +#' If `FALSE`, each asset from each item is saved as a separate file. +#' @inheritParams get_stac_data +#' +#' @returns A data frame where columns correspond to distinct assets, rows +#' correspond to distinct items, and cells contain file paths to the downloaded +#' data. +#' +#' @export +rsi_download_rasters <- function(items, + aoi, + asset_names, + sign_function = NULL, + merge = FALSE, + gdalwarp_options = c( + "-r", "bilinear", + "-multi", + "-overwrite", + "-co", "COMPRESS=DEFLATE", + "-co", "PREDICTOR=2", + "-co", "NUM_THREADS=ALL_CPUS" + ), + gdal_config_options = c( + VSI_CACHE = "TRUE", + GDAL_CACHEMAX = "30%", + VSI_CACHE_SIZE = "10000000", + GDAL_HTTP_MULTIPLEX = "YES", + GDAL_INGESTED_BYTES_AT_OPEN = "32000", + GDAL_DISABLE_READDIR_ON_OPEN = "EMPTY_DIR", + GDAL_HTTP_VERSION = "2", + GDAL_HTTP_MERGE_CONSECUTIVE_RANGES = "YES", + GDAL_NUM_THREADS = "ALL_CPUS" + ), + ...) { + if (!inherits(aoi, "bbox")) aoi <- sf::st_bbox(aoi) + + check_type_and_length( + merge = logical(1) + ) + + n_tiles_out <- ifelse(merge, 1L, length(items$features)) + p <- build_progressr(length(names(asset_names)) * n_tiles_out) + + download_locations <- data.frame( + matrix( + data = replicate( + length(asset_names) * n_tiles_out, + tempfile(fileext = ".tif") + ), + ncol = length(asset_names), + nrow = n_tiles_out + ) + ) + names(download_locations) <- names(asset_names) + + if (merge) { + gdalwarp_options <- set_gdalwarp_extent(gdalwarp_options, aoi, NULL) + } + + asset_iterator <- ifelse( + merge || (n_tiles_out < ncol(download_locations)), + function(...) future.apply::future_lapply(..., future.seed = TRUE), + lapply + ) + + current_options <- gdalwarp_options + + asset_iterator( + names(download_locations), + function(asset) { + feature_iter <- seq_len(length(items$features)) + if (length(download_locations[[asset]]) == 1) { + feature_iter <- list(feature_iter) + } + + future.apply::future_mapply( + function(which_item, dl_location) { + p(glue::glue("Downloading {asset}")) + signed_items <- maybe_sign_items(items, sign_function) + url <- rstac::assets_url(signed_items, asset)[which_item] + + if (!merge) { + item_bbox <- items$features[[which_item]]$bbox + current_options <- set_gdalwarp_extent( + gdalwarp_options, + aoi, + item_bbox + ) + } + + tryCatch( + { + sf::gdal_utils( + "warp", + paste0("/vsicurl/", url), + dl_location, + options = current_options, + quiet = TRUE, + config_options = gdal_config_options + ) + }, + 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 + ) + ) + download_locations[which_item, ] <- NA + } + ) + }, + which_item = feature_iter, + dl_location = download_locations[[asset]], + future.seed = TRUE + ) + } + ) + as.data.frame(as.list(stats::na.omit(download_locations))) +} + +maybe_sign_items <- function(items, sign_function) { + if (!is.null(sign_function)) { + items <- sign_function(items) + } + items +} diff --git a/R/get_stac_data.R b/R/get_stac_data.R index ee5dd3d..911fe9c 100644 --- a/R/get_stac_data.R +++ b/R/get_stac_data.R @@ -101,9 +101,12 @@ #' images from. #' @param query_function A function that takes the output from #' [rstac::stac_search()] and executes the request. See -#' [default_query_function()] and the `query_function` slots of +#' [rsi_query_api()] and the `query_function` slots of #' [sentinel1_band_mapping], [sentinel2_band_mapping], and #' [landsat_band_mapping]. +#' @param download_function A function that takes the output from +#' `query_function` and downloads the assets attached to those items. See +#' [rsi_download_rasters()] for an example. #' @param sign_function A function that takes the output from `query_function` #' and signs the item URLs, if necessary. #' @param ... Passed to `item_filter_function`. @@ -162,7 +165,7 @@ #' ), #' stac_source = "https://planetarycomputer.microsoft.com/api/stac/v1/", #' collection = "landsat-c2-l2", -#' query_function = default_query_function, +#' query_function = rsi_query_api, #' sign_function = sign_planetary_computer, #' mask_band = "qa_pixel", #' mask_function = landsat_mask_function, @@ -187,7 +190,8 @@ get_stac_data <- function(aoi, stac_source, collection, ..., - query_function = default_query_function, + query_function = rsi_query_api, + download_function = rsi_download_rasters, sign_function = NULL, rescale_bands = TRUE, item_filter_function = NULL, @@ -215,6 +219,7 @@ get_stac_data <- function(aoi, GDAL_HTTP_MERGE_CONSECUTIVE_RANGES = "YES", GDAL_NUM_THREADS = "ALL_CPUS" )) { + # query |> filter |> download |> mask |> composite |> rescale if (!(inherits(aoi, "sf") || inherits(aoi, "sfc"))) { rlang::abort( "`aoi` must be an sf or sfc object.", @@ -223,11 +228,14 @@ get_stac_data <- function(aoi, } 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( - "The default pixel size arguments are intended for use with projected AOIs, but `aoi` appears to be in geographic coordinates.", - i = glue::glue("Pixel X size: {pixel_x_size}. Pixel Y size: {pixel_y_size}."), - i = glue::glue("These dimensions will be interpreted in the same units as `aoi` (likely degrees), which may cause errors.") - )) + rlang::warn( + c( + "The default pixel size arguments are intended for use with projected AOIs, but `aoi` appears to be in geographic coordinates.", + i = glue::glue("Pixel X size: {pixel_x_size}. Pixel Y size: {pixel_y_size}."), + i = glue::glue("These dimensions will be interpreted in the same units as `aoi` (likely degrees), which may cause errors.") + ), + class = "rsi_default_pixel_size_geographic_coords" + ) } if (!is.null(composite_function)) { @@ -279,6 +287,9 @@ get_stac_data <- function(aoi, end_date <- process_dates(end_date) } + if (is.null(item_filter_function)) item_filter_function <- \(x, ...) identity(x) + + # query items <- query_function( bbox = sf::st_bbox(sf::st_transform(aoi, 4326)), stac_source = stac_source, @@ -288,10 +299,8 @@ get_stac_data <- function(aoi, limit = limit, ... ) - - if (!is.null(item_filter_function)) { - items <- item_filter_function(items, ...) - } + # filter + items <- item_filter_function(items, ...) if (!length(items$features)) { rlang::abort( @@ -301,7 +310,16 @@ get_stac_data <- function(aoi, } if (missing(asset_names)) asset_names <- NULL - if (is.null(asset_names)) asset_names <- rstac::items_assets(items) + 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)." + )) + } + } if (is.null(names(asset_names))) names(asset_names) <- asset_names items_urls <- extract_urls(asset_names, items) @@ -311,21 +329,9 @@ get_stac_data <- function(aoi, drop_mask_band <- TRUE } - download_locations <- data.frame( - matrix( - data = replicate( - length(items_urls) * length(items$features), - tempfile(fileext = ".tif") - ), - ncol = length(items_urls), - nrow = length(items$features) - ) - ) - names(download_locations) <- names(items_urls) - scale_strings <- character() if (rescale_bands) { - scale_strings <- calc_scale_strings(download_locations, items) + scale_strings <- calc_scale_strings(names(items_urls), items) } if (length(scale_strings)) { scale_strings <- stats::setNames( @@ -336,78 +342,75 @@ get_stac_data <- function(aoi, rescale_bands <- FALSE } - if (rlang::is_installed("progressr")) { - length_progress <- figure_out_progress_length( - items_urls, - mask_band, - composite_function, - mask_function, - download_locations, - rescale_bands, - scale_strings - ) - p <- progressr::progressor(length_progress) - } else { - p <- function(...) NULL - } - - use_simple_download <- is.null(mask_function) && + merge_assets <- is.null(mask_function) && !rescale_bands && !is.null(composite_function) && composite_function == "merge" - if (use_simple_download) { - download_results <- simple_download( - items, - sign_function, - asset_names, - gdalwarp_options, - aoi_bbox, - gdal_config_options, - p + # download + # download_results is a data frame with names corresponding to "final" band + # names and rows corresponding to individual STAC items + download_results <- download_function( + items = items, + aoi = aoi_bbox, + asset_names = stats::setNames(nm = names(items_urls)), + sign_function = sign_function, + merge = merge_assets, + gdalwarp_options = gdalwarp_options, + gdal_config_options = gdal_config_options, + ... + ) + # mask + if (!is.null(mask_band)) { + download_results <- rsi_apply_masks( + download_locations = download_results, + mask_band = mask_band, + mask_function = mask_function ) + } + + download_results <- download_results[names(download_results) %in% names(asset_names)] + + # composite + output_vrt <- tempfile(fileext = ".vrt") + if (is.null(composite_function)) { + output_vrt <- replicate(nrow(download_results), tempfile(fileext = ".vrt")) + # turn each row of the DF into its own character vector, stored in a list + download_results <- apply(download_results, 1, identity, simplify = FALSE) + } else if (!merge_assets) { + download_results <- rsi_composite_bands(download_results, composite_function) } else { - download_results <- complex_download( - items, - items_urls, - download_locations, - sign_function, - asset_names, - mask_band, - gdalwarp_options, - aoi_bbox, - gdal_config_options, - p, - mask_function, - composite_function, - output_filename, - rescale_bands, - scale_strings - ) - on.exit(file.remove(unlist(download_results[["final_bands"]])), add = TRUE) + # turn DF into a character vector inside a list + download_results <- list(unlist(download_results)) + } + # rescale + if (rescale_bands) { + 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( - in_bands, + in_bands[names(items_urls)], vrt, band_names = remap_band_names(names(items_urls), asset_names) ) }, - in_bands = download_results[["final_bands"]], - vrt = download_results[["out_vrt"]] + in_bands = download_results, + vrt = output_vrt ) - on.exit(file.remove(download_results[["out_vrt"]]), add = TRUE) + on.exit(file.remove(output_vrt), add = TRUE) if (is.null(composite_function)) { 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[["final_bands"]]) + app <- app %||% seq_along(download_results) output_filename <- paste0( tools::file_path_sans_ext(output_filename), @@ -428,7 +431,7 @@ get_stac_data <- function(aoi, ) out }, - vrt = download_results[["out_vrt"]], + vrt = output_vrt, out = output_filename ) @@ -448,6 +451,7 @@ get_sentinel1_imagery <- function(aoi, stac_source = attr(asset_names, "stac_source"), collection = attr(asset_names, "collection_name"), query_function = attr(asset_names, "query_function"), + download_function = attr(asset_names, "download_function"), sign_function = attr(asset_names, "sign_function"), rescale_bands = FALSE, item_filter_function = NULL, @@ -493,6 +497,7 @@ get_sentinel2_imagery <- function(aoi, stac_source = attr(asset_names, "stac_source"), collection = attr(asset_names, "collection_name"), query_function = attr(asset_names, "query_function"), + download_function = attr(asset_names, "download_function"), sign_function = attr(asset_names, "sign_function"), rescale_bands = FALSE, item_filter_function = NULL, @@ -539,6 +544,7 @@ get_landsat_imagery <- function(aoi, stac_source = attr(asset_names, "stac_source"), collection = attr(asset_names, "collection_name"), query_function = attr(asset_names, "query_function"), + download_function = attr(asset_names, "download_function"), sign_function = attr(asset_names, "sign_function"), rescale_bands = TRUE, item_filter_function = landsat_platform_filter, @@ -583,7 +589,8 @@ get_naip_imagery <- function(aoi, asset_names = "image", stac_source = "https://planetarycomputer.microsoft.com/api/stac/v1", collection = "naip", - query_function = default_query_function, + query_function = rsi_query_api, + download_function = rsi_download_rasters, sign_function = sign_planetary_computer, rescale_bands = FALSE, output_filename = paste0(proceduralnames::make_english_names(1), ".tif"), @@ -629,6 +636,7 @@ get_alos_palsar_imagery <- function(aoi, stac_source = attr(asset_names, "stac_source"), collection = attr(asset_names, "collection_name"), query_function = attr(asset_names, "query_function"), + download_function = attr(asset_names, "download_function"), sign_function = attr(asset_names, "sign_function"), rescale_bands = FALSE, item_filter_function = NULL, @@ -674,6 +682,7 @@ get_dem <- function(aoi, stac_source = attr(asset_names, "stac_source"), collection = attr(asset_names, "collection_name"), query_function = attr(asset_names, "query_function"), + download_function = attr(asset_names, "download_function"), sign_function = attr(asset_names, "sign_function"), rescale_bands = FALSE, item_filter_function = NULL, @@ -707,36 +716,9 @@ get_dem <- function(aoi, do.call(get_stac_data, args) } -download_assets <- function(urls, - destinations, - gdalwarp_options, - gdal_config_options, - progressor) { - if (length(urls) != length(destinations)) { - rlang::abort("`urls` and `destinations` must be the same length.") - } +rsi_apply_masks <- function(download_locations, mask_band, mask_function) { + p <- build_progressr(nrow(download_locations) + (nrow(download_locations) * (ncol(download_locations) - 1))) - future.apply::future_mapply( - function(url, destination) { - progressor("Downloading assets") - sf::gdal_utils( - "warp", - paste0("/vsicurl/", url), - destination, - options = gdalwarp_options, - quiet = TRUE, - config_options = gdal_config_options - ) - }, - url = urls, - destination = destinations, - future.seed = TRUE - ) - - destinations -} - -apply_masks <- function(mask_band, mask_function, download_locations, p) { apply( download_locations, 1, @@ -761,130 +743,32 @@ apply_masks <- function(mask_band, mask_function, download_locations, p) { ) } ) -} - -simple_download <- function(items, - sign_function, - asset_names, - gdalwarp_options, - aoi_bbox, - gdal_config_options, - p) { - gdalwarp_options <- set_gdalwarp_extent(gdalwarp_options, aoi_bbox, NULL) - out <- future.apply::future_lapply( - names(asset_names), - function(asset) { - p(glue::glue("Downloading {asset}")) - signed_items <- maybe_sign_items(items, sign_function) - item_urls <- paste0("/vsicurl/", rstac::assets_url(signed_items, asset)) - out_file <- tempfile(fileext = ".tif") - sf::gdal_utils( - "warp", - source = item_urls, - destination = out_file, - options = gdalwarp_options, - config_options = gdal_config_options - ) - out_file - }, - future.seed = TRUE - ) - list( - final_bands = list(out), - out_vrt = tempfile(fileext = ".vrt") - ) -} - -complex_download <- function(items, - items_urls, - download_locations, - sign_function, - asset_names, - mask_band, - gdalwarp_options, - aoi_bbox, - gdal_config_options, - p, - mask_function, - composite_function, - output_filename, - rescale_bands, - scale_strings) { - feature_iterator <- ifelse( - length(items$features) > ncol(download_locations), - function(...) future.apply::future_lapply(..., future.seed = TRUE), - lapply - ) - feature_iterator( - seq_along(items$features), - function(i) { - item <- items$features[[i]] - - item <- maybe_sign_items(item, sign_function) - - item_urls <- extract_urls(asset_names, item) - if (!is.null(mask_band)) item_urls[[mask_band]] <- rstac::assets_url(item, mask_band) - - item_bbox <- item$bbox - current_options <- set_gdalwarp_extent(gdalwarp_options, aoi_bbox, item_bbox) - - tryCatch( - download_assets( - unlist(item_urls), - unlist(download_locations[i, , drop = FALSE]), - current_options, - gdal_config_options, - p - ), - error = function(e) { - rlang::warn(glue::glue("Failed to download {item$id %||% 'UNKNOWN'} from {item$properties$datetime %||% 'UNKNOWN'}")) - download_locations[i, ] <- NA - } - ) - } - ) - download_locations <- stats::na.omit(download_locations) - names(download_locations) <- names(items_urls) - - if (!is.null(mask_band)) apply_masks(mask_band, mask_function, download_locations, p) - - out <- make_composite_bands( - download_locations[, names(download_locations) %in% names(asset_names), drop = FALSE], - composite_function, - p - ) - if (rescale_bands) lapply(out$final_bands, rescale_band, scale_strings, p) - out + download_locations } +rsi_composite_bands <- function(download_locations, + composite_function = c("merge", "median", "mean", "sum", "min", "max")) { + composite_function <- rlang::arg_match(composite_function) -make_composite_bands <- function(downloaded_bands, composite_function, p) { - if (is.null(composite_function)) { - return( - list( - out_vrt = replicate(nrow(downloaded_bands), tempfile(fileext = ".tif")), - final_bands = apply(downloaded_bands, 1, identity, simplify = FALSE) - ) - ) - } + p <- build_progressr(length(names(download_locations))) download_dir <- file.path(tempdir(), "composite_dir") if (!dir.exists(download_dir)) dir.create(download_dir) out <- vapply( - names(downloaded_bands), + names(download_locations), function(band_name) { p(glue::glue("Compositing {band_name}")) out_file <- file.path(download_dir, paste0(toupper(band_name), ".tif")) - if (length(downloaded_bands[[band_name]]) == 1) { - file.copy(downloaded_bands[[band_name]], out_file) + if (length(download_locations[[band_name]]) == 1) { + file.copy(download_locations[[band_name]], out_file) } else if (composite_function == "merge") { do.call( terra::merge, list( - x = terra::sprc(lapply(downloaded_bands[[band_name]], terra::rast)), + x = terra::sprc(lapply(download_locations[[band_name]], terra::rast)), filename = out_file, overwrite = TRUE ) @@ -893,7 +777,7 @@ make_composite_bands <- function(downloaded_bands, composite_function, p) { do.call( terra::mosaic, list( - x = terra::sprc(lapply(downloaded_bands[[band_name]], terra::rast)), + x = terra::sprc(lapply(download_locations[[band_name]], terra::rast)), fun = composite_function, filename = out_file, overwrite = TRUE @@ -906,22 +790,19 @@ make_composite_bands <- function(downloaded_bands, composite_function, p) { character(1) ) - list( - out_vrt = tempfile(fileext = ".vrt"), - final_bands = list(out) - ) + list(out) } -calc_scale_strings <- function(download_locations, items) { +calc_scale_strings <- function(asset_names, items) { # Assign scale, offset attributes if they exist scales <- vapply( - names(download_locations), + asset_names, get_rescaling_formula, items = items, element = "scale", numeric(1) ) offsets <- vapply( - names(download_locations), + asset_names, get_rescaling_formula, items = items, element = "offset", @@ -951,7 +832,9 @@ calc_scale_strings <- function(download_locations, items) { scale_strings } -rescale_band <- function(composited_bands, scale_strings, p) { +rescale_band <- function(composited_bands, scale_strings) { + p <- build_progressr(length(names(scale_strings))) + for (band in names(scale_strings)) { p(glue::glue("Rescaling band {band}")) rescaled_file <- tempfile(fileext = ".tif") @@ -1023,25 +906,6 @@ process_dates <- function(date) { gsub("UTC", "Z", date) } -extract_urls <- function(asset_names, items) { - items_urls <- lapply( - names(asset_names), - function(asset_name) suppressWarnings(rstac::assets_url(items, asset_name)) - ) - names(items_urls) <- names(asset_names) - - items_urls <- items_urls[!vapply(items_urls, is.null, logical(1))] - - items_urls -} - -maybe_sign_items <- function(items, sign_function) { - if (!is.null(sign_function)) { - items <- sign_function(items) - } - items -} - get_rescaling_formula <- function(items, band_name, element) { elements <- vapply( items$features, @@ -1063,33 +927,18 @@ get_rescaling_formula <- function(items, band_name, element) { elements } -figure_out_progress_length <- function(items_urls, mask_band, composite_function, mask_function, download_locations, rescale_bands, scale_strings) { - # this is frankly ridiculous - - # How many steps do we walk through: - # 1. Must download all items, including the masks - length_progress <- length(unlist(items_urls)) - # 2. If masking, we are going to either run a mask function or actually mask - # all downloads (*2) - if (!is.null(mask_band)) length_progress <- length_progress * 2 - # 3. Compositing complicates things: - if (!is.null(composite_function)) { - # 3.1 We'll add 1x the number of bands: - composite_multiplier <- 1 - length_progress <- length_progress + (length(items_urls) * composite_multiplier) - # 4. But not the mask band if it exists: - if (!is.null(mask_function)) length_progress <- length_progress - 1 - } else { - # If rescaling, we'll need to scale each image separately: - composite_multiplier <- nrow(download_locations) - } - # 5. If rescaling, add one step for each rescale: - if (rescale_bands) { - length_progress <- length_progress + (length(scale_strings) * composite_multiplier) - } - length_progress -} - is_pc <- function(url) { grepl("planetarycomputer.microsoft.com/api/stac/v1", url) } + +extract_urls <- function(asset_names, items) { + items_urls <- lapply( + names(asset_names), + function(asset_name) suppressWarnings(rstac::assets_url(items, asset_name)) + ) + names(items_urls) <- names(asset_names) + + items_urls <- items_urls[!vapply(items_urls, is.null, logical(1))] + + items_urls +} diff --git a/R/misc.R b/R/misc.R index 5ada19a..0f3ae78 100644 --- a/R/misc.R +++ b/R/misc.R @@ -32,3 +32,11 @@ print.rsi_band_mapping <- function(x, ...) { names(x) <- x_names print(x) } + +build_progressr <- function(n) { + if (rlang::is_installed("progressr")) { + progressr::progressor(n, on_exit = FALSE) + } else { + function(...) NULL + } +} diff --git a/R/query_and_sign.R b/R/query_and_sign.R index 4ca8aed..459a82c 100644 --- a/R/query_and_sign.R +++ b/R/query_and_sign.R @@ -35,17 +35,17 @@ #' aoi, #' start_date = "2022-06-01", #' end_date = "2022-08-30", -#' query_function = default_query_function +#' query_function = rsi_query_api #' ) #' #' @export -default_query_function <- function(bbox, - stac_source, - collection, - start_date, - end_date, - limit, - ...) { +rsi_query_api <- function(bbox, + stac_source, + collection, + start_date, + end_date, + limit, + ...) { if (!is.null(start_date)) { datetime <- paste0(start_date, "/", end_date) } else { @@ -72,7 +72,7 @@ default_query_function <- function(bbox, #' Sign STAC items retrieved from the Planetary Computer #' -#' @param items A STACItemCollection, as returned by `default_query_function`. +#' @param items A STACItemCollection, as returned by `rsi_query_api`. #' @param subscription_key Optionally, a subscription key associated with your #' Planetary Computer account. At the time of writing, this is required for #' downloading Sentinel 1 RTC products, as well as NAIP imagery. This key will diff --git a/R/sysdata.rda b/R/sysdata.rda index 798d99bf99c1eb4996d1d9d5af82e03d426336b4..aec2921a5bed36a8c57b0572b64907dbd5496dd4 100644 GIT binary patch literal 9451 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 literal 9452 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 literal 1993 zcmV;)2R8UZT4*^jL0KkKSu0vU#Q+l-|Hc3R|NsB{{66G)ooK)R|L8yfKmb4hOex?9 ze|vV*?>u^ILG|AxPyxK{YPQ3-cuF8fh)mE;s(Bet6nc!BY3TqqpbY?MGy_cl44MXn zK*X366Ch}6r|8NarYY!8DW;k|BSGqDG#;S!000P+Mxm5oG?^aKnJ@t`OqdCRU?T`H zgG>MkgegR(jG!K))X>N@Xc`QFFoPyTO#m`z(9qM=XaXXD0B8UJGynhq00000000pw z6HOTnO*K5J;v-}-o}uY9^#*~XMuvf)05oVC2A)&Gsm!SOBhMsP`Hy>aUQR%sBM^@^ zY&7#fZGaox9_vH37(qe1nHO&ip2OGQ*Y??s2FpT%jZ~!I{^;=e>eqZ8`|r36lZ}fg z)B|Npsi()xPp%so-^^SX2&y3t^W&plwZnG!p2m~BhG5Lhw3;DKrU`~l*NfmS)0!teWRY6rWQE*2lPnQH|g&KkH;#$l?%6(URqr5$xb6DT$^z_&e zA6y?y8*{xb_k)YH5&p+(Plrc6jB>77hI0N)(4c}V$1u(r#O8pH9T>nstd_+FAQLgO zIvFt+dNB#Wh7!&8Cg26DWk3i30RRvvisRAUm=}~i3g~BNxB-on?A6F@t0hFhJZ&2a z@GyXvfKcdPIcz9sYk7S?iRf6|RkNQK!S;_j7f# z03!3vcTF%+(^Dk9Q_Bp%@}Un3b$FOZOl(XsZw) z*F=r*R~1hnBJ_6U&>M^8lbbAafCLhRMImB1D`K*BzkZ4iyWv@`s@sguzt6-F{rw3T z$FvH>9BP(`)Q7ZLs5OD2Ea14Z(? zyC;{UBIBb*`!jC9=;?6pnO2yr?uv5)1rT>ScG@U_4>SZ^@#VUx1w{@j)qDlRIgPde zFm0}M)`Bdit%1z$8}o4j_en_t|r&kArcyzaPW z4SljevM1A&`Dw2okfy%6JYJioxs4)h$OGHdGBr+7zqCKCtH2BZU|WjJ4{su9twmOzR*bVH>m>fVd$!Udy;7-qLLz&rmmLnTVc?)xWWj$_yOTS zqqu_rI#C60%~d^q^$J5L_dA`+@>W{65P1EL40*M=1XFl|BWx9c0b>C{ixmW+SdgSb zgkBO1p_zy{W&mU?NCg3yVjyN1r4VK)nE*sp2!J|33Ye#oeuwTF1(7xMXw?P8L1_is zfQ_mpPy_?2NdN>AkdsQ%Aq4_}h9F8CV$4hsp+c0U6%7y+B^5(WQ8Ph7RYC_nIu!Cs@pCJNRbDAW;bfTmrj>7HE_^D*v&X*8wjx_AUIf~P)rW< zZDl1lP4Q%2J{~NFQ?_0;xXih#HjZ{sJWo(_^P9)vy#U!9Wb8&@&XMZ#rk`ig+?cc< z_h^#V5rZOlbyG6P0=CSDr&HU^Sk#$jvMFuWWW<9}z+9iHOKm~-POEYGH+s(AGt0Qi$&)Equ`H58n3Ao^$X7}l%4bYl zfvkqEk6)VV`Q6>!ru&k;n(%N7FhST5M;tW`L8H&(Di`EI%9Jn?AOQJ(&QDvb*Wkf>5Ay5%HsK{{+RtA(gAK$} zpoyIr!A1k2SeHTDz_eQ)5Mmk;4HWcA*tyr)7w_~p>L3fMSqE#TWj8Ai!OuWvEwdm% bDkD^nPz?i+NqOXK9L3y`P81{x){pT(oO^^H diff --git a/data/dem_band_mapping.rda b/data/dem_band_mapping.rda index 1e8a8a2fabad5398e75db74de57468db4af2bfde..0d70239d47c42f00ee6a73408155befcbd9c0043 100644 GIT binary patch literal 6715 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 literal 2160 zcmV-$2#@zdT4*^jL0KkKS*v@0V*px>f589$|NsB9{5~WpoJha#|7bt}3@P9a{`x{t zfj|HN0Du4h01yBGAS5}HO$>krBOu5DrU)2-XwyIjK+plqnrLJIF&PFx4KP5&14fzv zG6sMSWYa?+0f@*l0BM2-AR08#0gyBRb0(S@01QS!kONE*F#yq~fDD140HmcMCZ34Y zz)aN88UWB~p`c^{14h((1T+OoAesOsjWSK7(@cS?dYG9SJsN2}BSwQD0D5UYAy>#J z{;wYe!IG>ps=hu~B1Z8J@!kp#a9bho%ruQ--W_3i!?bWdE>{Bv1{hd>(T4_qZC~$K zL95}zfYtu23&o<@EvTwZ!x-fh8UazbNX{EaNb`mv6hmlvis(d=Vzie`@v1-F4zkUbp_c?tn71j+`$(8Tn`E^f! z2UyiU*9R-Pz;I!MDXY%){8nWelSr$QXp_ z7!4K1PN8^IV-Ar}n?|8fxM>^2#xaWqA$ahN!(h51;m$A_#{u&UgA_?%)G!!^ zA$TW{oxL7R~jN<_zz))B%6dfaQ zk<1?mmkWnTymJjAg2DNPM=;_$2dsF-!?bzDMj=7oIpzZ4<{bjz$~6w)<_C~?2RPa$ z13VZ7F$W;HTsV&ya32V09?^iDfy7udi$z4(I>kp|pz9PKfw*%EhlpG<2`D=T@vLSa zq-_`tquMeKV08+PL5x^33k3zkfanKsF$N(#f#?f{!EkULAjUWcq+=aptbIegX&T1y z)G~?*v{Ze=V6-%iLs01%L3m>w!`dh&AwfKXfk8olpr06^ZySX}$|x`oh+r`cV*z+t z5c}M&1{gaMYe0Uhtt)z2?mv;F%$ z&c3?qd==1j*PGn#?YzG>yC`AXQ1*Q>tG2GX?{-$z--&3dRCGe9mE)HZ;RTTOLqaF@g>b$Y2sQ{lk9;?CL*x`?hZu3( zUm|XK0YLVD;m_JA8-~RER!ElZw%}O{0?`LM3%j(#x)_@wlOiU1%pNFfOr0FFf#F}8 zn(10smZgiubVIa#B7%ba9OA)fprE$aBur8nEeZ++kt;A8gMjQt+xc?Clgu&X zh4Nhz6_8b1q%7WvZFklbb117(&^yP!C*Y169Jx^}<=QkuQ$R6TAms)`d6c*g|FOXz z=y=DbJ&^um>kkO~V){csMhq|vFv7zOlkAM5;HwQv9@TXZ@;|+QLGo9;Jz?z*;OIKM zpA~qk;j8P{in{Bvyn*5!;k;GvReJT~uSHkks=d;@b*j2!x;)YH!RU_-Uy84pTn?4# z4hE~iSbm|@I#+hRLHG}*PQ?C_NGVo}u2+#=wcf7U?5}dK&tCn1@pmLsg$W9`xArhws?a|G diff --git a/data/landsat_band_mapping.rda b/data/landsat_band_mapping.rda index 1af8d7bc4dd98b0ac74615abb6d2ae53bc69b009..17de09e3b624201378ceb9a46de6166b03e75b25 100644 GIT binary patch literal 4831 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 FsMz+<1H}LU literal 2009 zcmV;~2PXJJT4*^jL0KkKS#{PquK*Jy|HA+O|NsB{{eJYX+(^Iw|LQ;h0su@Y;0S&8 zYGaRHQEcxNNCA7X{3bOOoItCQRoScLV5^j(;ylFqaXli(U1TGM5mKU$XPqfI7^ z3U5k!n?_L6Akn6P#!;XEJrH6VWc4@II0YV@_3BrCdy#jyg6kTn@NoNHF9Y}OBGjt* zo<=}m*lB(-iyxW<8s<{|rQ@6vVK))bLke5-{eQKX#!lp*Eo<1qpaX1)lRp-)gD@KV zk4~AO2$CRvlaf34+L#S%Tb;x)IYA`EZVl?rn;=Gwuz;x zotI}!vp$t7RoqO}3Z|=W;RN6o6JL}a*WLF193cR9y;6=INW-!@268|iY zZ~$Dv2)Kff*7jV%+=AS9jrPKmO;2qmdi<(Fp6%s{Cd!$@qUlKs6y+B1h#DXg-3gWj z!vi0vC>B!CC=l%^Qkk}82oZsp6jX})mPZ3}SF3ZcxpBy2y{T@EJZASgA+BXoBza=t ziZKxaV2`GcePV@yrDEz-6e^H_5ttDd9R~CRMV3GU_W9|iW`~1XBdcXHMKf>HshaDF zpj*1QtYYwLyR8b;nG|eli1NoIP)Q|SzJ>|WE?1s>8v_Uu-NAJQ@L+AUky0)M3vt** z=7>S~{o$)1#};r*WD8z6l8sYKlFtUJOB^WTnASS$7=vJJt4S0H<a3pQS~#poUh4@oPB%By_PLhU<+-{C5de~@5@r%g znG*&K#M_szm>P+!lu$&{wAJu--)9Ks+c_YJxHv_{l)IGV;VPz6(*Dn=n$Y2^%dEP zpg|CV84!%xVxS&eIYb)5nz~{mlAtHCJ>+__dy}kWqK45b(k93NmOvquM29fnk=&C| zl>!1l)BhGh5BOAe|LQ*6Zpx!mM^_2Pee_NO`}(FeHYokOS(T^VNByvd9AY>}2J-YfyFw)%n3zP*y#4=#m!e zpxQj`jRuaMf|IOND3l(K3$YV4j&$H3^6I6JoYd6UXf|(xzXrzk^t|$E@@lxTpZVd{ zw2T-AWTD!j$sz?^{0EYNv(p$_)jm0+74VSISj%j#Q$V6_m!rx;s*o=F1H{D*((~$VGTmMXid6I_9Hn$nPy`SsQKKVNRAR_(mS9?Gn4{6!-q5@M zrHGt7A%!iqk*h;Hwm{*nA_k2^I46kmB}RB)VG2lUa1t-V1j2A37BzO9uUCaE=RlfL zV<%9Bit1>~uFzC~HMLPE>l4+-S9@HDczCX$)Y)V<2*j&x%1}af@mxMJfvU{q^KjQ94rwS4dy2mx(2hfXj diff --git a/data/sentinel1_band_mapping.rda b/data/sentinel1_band_mapping.rda index 00a49d276ab9f0a2822125d584e4df6d266873c1..6ccf594d144d85af21fee05c53a3ae67ff0e867f 100644 GIT binary patch literal 4309 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 Dul3UE literal 1421 zcmV;81#G+@zF+EaQ)1s|skH`8JrE5I0BAG-&;S}S02%-iDkh^q$YKV7Xah|R z00w|G8UQrV4^SdX6xy3q#5SRz8V8~P13&-(0iXc&50sQ>uGjMr+DQ6p{RJ8z{|N~M zcJl6{A{5-pAB3QS$N14Uzg63{1L;!g)ybEh?n0R)F`L`@L$^bg+YQZ%5Fbp6qN=J? zx8}`D+{`DCU7*M+0}>|{pp0Uq1yOD#A(Ules1tFHjgtx2Kdy!#cFva+sa_2DsZtn$ zfmMe$1PdX75IF2Xy2}iSnh;sAg|W?~l1m5`-1HbYCrwyUhB9vGcQgb55(0pOaD?Ly zLX->BK$ue=9m^5?}VqhO0~SqY)S5R{ZO>Az%&L4*iVK;+Rp>7}D5G%!q&VBA=v ztb5$)wrQ!Tjm!H9&j(0maoLf}`#ldytBjmP-K z@r7%*d{?kX%)-JgOiDpu-bO>-Jyg)UmeM20>2Gq($+??A7}16VwI;gk11KChHw{B; zX`b+YbDI1x@am0L?7>qjI4hKnJa@n*AGhOW?Bych&Z=nSq04YOm|vHL#fTj=qXQnV zLsYv7hL9K`1}s<+fJPA-NEJb!1N@dHkWfWPuV>Y5w*C1aK&vDR&`{JsgSmnvvV{zN z`)lVP9)IIv+wHBb;B_~bX_$pqbp^sX~S*$dHO9AVFw? zG9XBZt7HULl+fAW%`NrT)n62UFciF2$S5Vm2qH!#QY#`XWKmHmNTzy_fdmNxLPZ%7 zNGPOKVuGs3MUqw$G7J&0BBD_kA!P`rtO{UT6|}N|N`Qf6R6|}q$ZK<{wx|$sNI^&i zzCIDs;nR^At@LU5nx6L5s?6k0AyIeK$1)# zjS&ZD)0-#rg`JegjaRbf@0%nKdZD@8x?-^YcKYz5hbH(q<#LbMaDFbxp!onFnN9tv zDwHRla$Xy3x11WUi6%YWvLt{Lhe}%^4kv<$F+mLmLDaQK6l@o##H?75JO~}E#-7~% z!x^Y*Tsqr>SRqEfOxupDj5#O-ne12y2?j)>09F{gr56co4JCneGOceQl&@ z*2`<5nGPjIDilUxixGAsG6m%%FA%iCWYI$49F}Fby`hgbnPctb++;w{NopGLqGfSp z4TJ(IH)n3^Q67U=lTDK5x~g1&I5?zA^}QrK9@M1(zF5T&IrE|}d=o6(N(`ExK`2CI zPXsK2Yapad98?1qX8$*wpNLHX1izwrvxxv?hzr1AiGqM7(}4%AdHSVn>@5|P& diff --git a/data/sentinel2_band_mapping.rda b/data/sentinel2_band_mapping.rda index 0710903aebcea07bde6167b57ae632633e7187d0..67f64566f67c7a7d4a5cb19e899acf107ade346a 100644 GIT binary patch literal 5760 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 literal 2156 zcmV-y2$T0hT4*^jL0KkKS#Xsov;ZMx|G@wM|Nr~H|NefhoyfoM|L8yf3@P9WpC2h8 z00B~{5|JuW6bc9-#)gd=4H^c300006KmY&+fXDza<3mP`28{zi00003AOHXZKx600000$N&HU&=~**2$clMqd+E8!ZkF} zh-s>1^qDlo%#ietP%;2{MuR4e4FpL<&_;yKOcG?xL9&`?@=r{VXbn9;G|*^lk?B1$ zo}*7w%71{TI4jU`(iFu=#0SwHo2d0(!$Li-&=KYf=$!ekdy(f`hT#G}2TwZF`+-9g zj9CB8G{_rXT744NmZ(U1>>KT^rL@t(*z!9aj>j{ahjT-$3N3gb9E-8V-q`s9?!`M? z4Pk^wJEiBu@VW7`rLoA`(8dg77@ACJs7{a?5oA$}Rwt_{ghB))P-KviwR*%Lg(_e$ zUeXHM$0R~AbuQ;Pf63PEQ}{abR^)O7VtU{o<{ob|LS9%%BjJadKX=Ve#E}x;t+6rYI*rV>gK&gZ;SyVAlcB~ z+!O~>0~kl|BK`Kkq3mdMv^IuiFLboz5G_UQdRw0pnW!5W4p6Vc`5!llprCKDNM#s@GL{>$hRSxS*}H&R)$Yu=^*% z@NTB;blokh45P{zABpcg8`_4qX7JM9)HMUe;_!Q?nxEqGlQ@IA1 zqn3lR9^Y@=DaSFubGX0=5MT@ti3k8iBETX-D3&7tsz;&9r73}G+D1SmSrma7!3f4a z{hl6Duf&`XL>giO*uW7t05Am2SwJEdkx+4EYFCSTu~|kA6Q{dwE7u3pe<;dVErM$G zJIpYqCmHXUN<^Co6C$gKfTN&bf|+fFA|)#U76cUv5MqeJn9z(MsR)Rz4J86oMF52j z6b&>`3mOuyZ0WRyz2p14pq&es@hF>C2Xr>WZOE18}Nkf}p*m z0s?^miogjNi5P$p2*pH2p;&-rh{h3O#xYfp0gOP9V#JUn1(1*#83;s>BxGa&2?1J0 zM3G=4N{~V^K@Epk8dOVCF;$ELFe4#)0Dx?;3PJ&N6fKZ!p%AEmsCSr$1VeTw#>BRo z1q8W@1dL*h_9C!Yiv%JHgd_CD9I+`PtQeprNU$VH1`Jpbk&IwL2tJdSS5 z>CxiWzvb)7*VzEggtwKvV>ui*w4s14w#ZF>%W-peKd9G!E#b1)35FeoR0Z)j8HyXb znBIF?>z*fr#f>au7{)MfG*Hw7t@}b!+yLC*{|yaJ7Kigv`?n!j#?D&-`kLB)n|{87 zjPr+=c5uO0i11(z?KZ~HtSuzxkS=Oe?#vI?m{VU|E*qf|i+Q_vvmIluUk8~sjC|(F zr2sObVlqZB(tDncJUw47hcm)|+m2nk*hvzz(@UP928UzKq}M+i?qeP&7q}eE#u`eD zD}G#s!BwFI!+muCGzH;_K7gXj03X-@{*L*{$Q=>5EOv&R1I=c`tQa?tj(6OAy7ao0CyT+u*Ys~A>S=Em*?l=9tNDrD{3y|vFh7Hh(2$Zsri`422&9^ zs%v8!@TLXmq+}Zc$_la(#AC+dzXO~Bz!Z=+t}viC4mqDx2EZ46Ix6kSkJGFvet#Wd z$N=|S&Q$bf0+|A43-#e1PRF^FGXOdplLw9CwHubX(Vw|iYx)jdJo%e|8;1dxM)OEo zYdwD^RIKOu-5?I5p1L^!A8OQN$E7RX4gAI++w5&IV+zn>7L;IGc(lWqE5KJ^aSl=) z*V%piULUb7%Ivu**{%?@%>cVF0n!F2Im1BX+@T$v=hEFC0KE3zw%Y2jdock95s9+I zLYTxPfE!T&pb{}vH_u(*cz<_^^S7HF4E=-5a-KgK##~`?W1D6}iB2" +) +``` + +This vignette aims to provide a "cookbook" walking through common use cases and code patterns for rsi. If you've got a problem that it seems like rsi _should_ be able to solve, hopefully this document can help -- otherwise, [open an issue](https://github.com/Permian-Global-Research/rsi/issues) and we'll see if we can get you started! And if you've got a use case that took you a second to figure out, please feel free to open a PR to add it as an example to this document. + +With that introduction out of the way, we'll go ahead and load rsi: + +```{r setup} +library(rsi) +``` + +And start answering: How can I... + +## Get one composite per year, month, or other interval? + +If you're looking to get separate files for several intervals, you'll need to call `get_stac_data()` separately for each of those intervals. The easiest way to do this is through something like `vapply()` or a for-loop. Iterate along your intervals of interest, construct your `start_date` and `end_date` inside of each iteration, and call `get_stac_data()` using those dates as arguments: + +```{r} +aoi <- sf::st_point(c(-74.912131, 44.080410)) +aoi <- sf::st_set_crs(sf::st_sfc(aoi), 4326) +aoi <- sf::st_buffer(sf::st_transform(aoi, 5070), 1000) + +downloaded_years <- vapply( + 2018:2020, + function(year) { + get_stac_data( + aoi = aoi, + start_date = glue::glue("{year}-01-01"), + end_date = glue::glue("{year}-12-31"), + asset_names = "lcpri", + stac_source = "https://planetarycomputer.microsoft.com/api/stac/v1", + collection = "usgs-lcmap-conus-v13", + output_filename = tempfile(fileext = ".tif") + ) + }, + character(1) +) + +downloaded_years +``` + +This ensures that `get_stac_data()` is run using the same arguments each time, so your outputs should be standardized across each interval! + +```{r} +invisible(lapply(lapply(downloaded_years, terra::rast), terra::plot)) +``` + +## Filter the imagery I download by cloud cover (or other metadata)? + +If you want to refine the outputs from your STAC query, the best approach is to write a custom query function using CQL2. CQL2 is a complicated topic, which is covered in a bit more detail both [in the Downloading Data vignette](https://permian-global-research.github.io/rsi/articles/Downloading-data-from-STAC-APIs-using-rsi.html#using-cql2-to-refine-queries-to-stac-apis) as well as in [the STAC website's tutorials](https://github.com/radiantearth/stac-site/blob/main/app/tutorials/r/2-using-rstac-and-cql2-to-query-stac-api/2-using-rstac-and-cql2-to-query-stac-api.en.md), but at its core is a query language that lets us filter our results down using a spatiotemporal area of interest as well as other item-level metadata. + +This requires knowing what metadata your STAC API provides for the items you're querying! Luckily, a good number of these fields are standardized via the STAC standard and various STAC extensions. For instance, items implementing the [electro-optical extension](https://github.com/stac-extensions/eo) will have an `eo:cloud_cover` field which we could use to filter the results of our query. + +Writing a query function to filter using this field might look like this: + +```{r} +aoi <- sf::st_point(c(-74.912131, 44.080410)) +aoi <- sf::st_set_crs(sf::st_sfc(aoi), 4326) +aoi <- sf::st_buffer(sf::st_transform(aoi, 5070), 1000) + +custom_query_function <- function(bbox, + stac_source, + collection, + start_date, + end_date, + limit, + ...) { + geometry <- rstac::cql2_bbox_as_geojson(bbox) + datetime <- rstac::cql2_interval(start_date, end_date) + + request <- rstac::ext_filter( + rstac::stac(stac_source), + collection == {{collection}} && + t_intersects(datetime, {{datetime}}) && + s_intersects(geometry, {{geometry}}) && + `eo:cloud_cover` < 50 + ) + rstac::items_fetch(rstac::post_request(request)) +} +``` + +And we can use this to filter down how much data we'll download! For instance, we could download Landsat imagery for our area of interest _without_ using our new query function and setting `composite_function` to `NULL`, so that we'll get one file per Landsat image: + +```{r} +unfiltered_landsat_images <- get_landsat_imagery( + aoi, + start_date = "2023-06-01", + end_date = "2023-09-01", + # Only downloading one asset, because we aren't using this data for anything + asset_names = landsat_band_mapping$planetary_computer_v1["red"], + mask_function = NULL, + mask_band = NULL, + output_filename = tempfile(fileext = ".tif"), + composite_function = NULL +) +length(unfiltered_landsat_images) +``` + +And we could compare that to a query using our custom query function, to see how many images are filtered out by our CQL2 query: + +```{r} +filtered_landsat_images <- get_landsat_imagery( + aoi, + start_date = "2023-06-01", + end_date = "2023-09-01", + query_function = custom_query_function, + asset_names = landsat_band_mapping$planetary_computer_v1["red"], + mask_function = NULL, + mask_band = NULL, + output_filename = tempfile(fileext = ".tif"), + composite_function = NULL +) +length(filtered_landsat_images) +``` + +We only wind up downloading about half the number of images!