From be7b2124f4d691ecf8779427b21223944071325b Mon Sep 17 00:00:00 2001 From: Luigi Ranghetti <6874239+ranghetti@users.noreply.github.com> Date: Fri, 11 Oct 2019 17:28:00 +0200 Subject: [PATCH] RC for v. 1.2.0 (#224) Starting from this version sen2r supports ordering products from Long Term Archive (LTA) (see news at https://inthub.copernicus.eu/userguide/LongTermArchive). Now the user can automatically order SAFE products which are not available for direct download, and use them when made available. Some internal functions can be exploited to manually manage orders. Here above the related changes: ## New functions * `safe_is_valid()` to check if an order was processed; * `s2_order()` to order products from LTA. ## New arguments * `sen2r()` and `s2_download()` have a new argument `order_lta` (default: TRUE) to order SAFE archives not available for direct fownload; * `s2_gui()` has a new checkbox to set the previous argument. ## Other changes (not related with LTA) * Function `build_example_param_file()` does no more compute TOA and RGB432T (this to avoid downloading 2 SAFE archives). * Code coverage was expanded. --- .travis.yml | 3 - CRAN-RELEASE | 4 +- DESCRIPTION | 2 +- NAMESPACE | 2 + NEWS.md | 22 ++ R/build_example_param_file.R | 5 +- R/check_param_list.R | 5 +- R/s2_download.R | 131 +++++++--- R/s2_gui.R | 47 +++- R/s2_list.R | 56 +++- R/s2_order.R | 225 ++++++++++++++++ R/safe_is_online.R | 74 ++++++ R/sen2r.R | 174 +++++++----- README.Rmd | 1 + README.md | 25 +- _pkgdown.yaml | 4 + docs/404.html | 3 + docs/CONDUCT.html | 3 + docs/LICENSE.html | 3 + docs/articles/docker.html | 3 + docs/articles/index.html | 3 + docs/articles/installation.html | 3 + docs/authors.html | 3 + docs/index.html | 20 +- docs/news/index.html | 50 +++- docs/reference/abs2rel.html | 3 + docs/reference/add_rgb_image.html | 3 + docs/reference/ask_permission.html | 3 + docs/reference/build_example_param_file.html | 5 +- docs/reference/check_gdal.html | 3 + docs/reference/check_param_list.html | 3 + docs/reference/check_sen2r_deps.html | 3 + docs/reference/compute_s2_paths.html | 3 + docs/reference/comsub.html | 3 + docs/reference/create_indices_db.html | 3 + docs/reference/create_s2_dop.html | 3 + docs/reference/editModPoly.html | 3 + docs/reference/expand_path.html | 3 + docs/reference/fix_envi_format.html | 3 + docs/reference/gdal_abs2rel_rel2abs.html | 3 + docs/reference/gdal_formats_db.html | 3 + docs/reference/gdal_warp.html | 3 + docs/reference/gdalwarp_grid.html | 3 + docs/reference/geograbber_process.html | 3 + docs/reference/index.html | 15 ++ docs/reference/init_python.html | 3 + docs/reference/install_aria2.html | 3 + docs/reference/install_sen2cor.html | 3 + docs/reference/list_indices.html | 3 + docs/reference/load_binpaths.html | 3 + docs/reference/load_extent.html | 3 + docs/reference/mountpoint.html | 3 + docs/reference/nn.html | 3 + docs/reference/normalize_path.html | 3 + docs/reference/path_check.html | 3 + docs/reference/print_message.html | 3 + docs/reference/projpar.html | 3 + docs/reference/raster2rgb.html | 3 + docs/reference/raster_metadata.html | 3 + docs/reference/s2_calcindices.html | 5 +- docs/reference/s2_defNA.html | 3 + docs/reference/s2_dop.html | 29 +- docs/reference/s2_download.html | 46 ++-- docs/reference/s2_gui.html | 3 + docs/reference/s2_list.html | 32 ++- docs/reference/s2_mask.html | 5 +- docs/reference/s2_merge.html | 3 + docs/reference/s2_order.html | 247 ++++++++++++++++++ docs/reference/s2_rgb.html | 7 +- docs/reference/s2_thumbnails.html | 3 + docs/reference/s2_tiles-1.png | Bin 50537 -> 50826 bytes docs/reference/s2_tiles.html | 7 +- docs/reference/s2_translate.html | 3 + docs/reference/safe_getMetadata.html | 3 + docs/reference/safe_is_online.html | 220 ++++++++++++++++ docs/reference/safe_shortname.html | 3 + docs/reference/scihub_login.html | 3 + docs/reference/sen2cor.html | 3 + docs/reference/sen2r.html | 78 +++--- docs/reference/sen2r_getElements.html | 3 + docs/reference/smooth_mask.html | 3 + docs/reference/st_crs2.html | 3 + docs/reference/stack2rgb.html | 3 + docs/reference/str_pad2.html | 3 + docs/reference/tiles_intersects.html | 3 + docs/reference/trace_function.html | 3 + index.Rmd | 1 + index.md | 16 +- inst/CITATION | 4 +- inst/WORDLIST | 6 + inst/extdata/settings/indices.json | 4 +- man/s2_download.Rd | 35 ++- man/s2_list.Rd | 28 +- man/s2_order.Rd | 72 +++++ man/safe_is_online.Rd | 48 ++++ man/sen2r.Rd | 35 +-- tests/testthat/test-t05_load_binpaths.R | 46 ++++ .../test-t08_build_example_param_file.R | 27 ++ tests/testthat/test-t10_s2_list.R | 83 +++--- tests/testthat/test-t11_s2_download.R | 20 +- tests/testthat/test-t14_s2_order.R | 116 ++++++++ tests/testthat/test-t20_s2_merge.R | 8 +- tests/testthat/test-t21_s2_warp.R | 115 ++++---- tests/testthat/test-t22_s2_mask.R | 20 +- tests/testthat/test-t23_s2_indices.R | 20 +- tests/testthat/test-t24_s2_rgb.R | 9 +- tests/testthat/test-t36_mountpoint.R | 21 ++ utils/code/code_coverage.R | 21 ++ 108 files changed, 2070 insertions(+), 396 deletions(-) create mode 100644 R/s2_order.R create mode 100644 R/safe_is_online.R create mode 100644 docs/reference/s2_order.html create mode 100644 docs/reference/safe_is_online.html create mode 100644 man/s2_order.Rd create mode 100644 man/safe_is_online.Rd create mode 100644 tests/testthat/test-t05_load_binpaths.R create mode 100644 tests/testthat/test-t08_build_example_param_file.R create mode 100644 tests/testthat/test-t14_s2_order.R create mode 100644 tests/testthat/test-t36_mountpoint.R create mode 100644 utils/code/code_coverage.R diff --git a/.travis.yml b/.travis.yml index c311bb13..d5daf042 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,9 +36,6 @@ script: - R CMD build . - travis_wait 120 R CMD check *tar.gz -after_success: - - Rscript -e 'covr::codecov(line_exclusions = c("R/s2_gui.R", "R/add_rgb_image.R", "R/geograbber_process.R", "R/editModPoly.R", "R/install_aria2.R", "R/ask_permission.R", "R/check_sen2r_deps.R", "R/create_indices_db.R", "R/helpers_extent.R", "R/create_indices_db.R", "R/gdal_formats_db.R", "R/path_check.R", "R/list_sen2r_paths.R", "R/check_param_list.R", "R/convert_datatype.R"))' - env: global: - R_LIBS="http://cran.rstudio.com" diff --git a/CRAN-RELEASE b/CRAN-RELEASE index 179db9bb..41e05cc3 100644 --- a/CRAN-RELEASE +++ b/CRAN-RELEASE @@ -1,2 +1,2 @@ -This package was submitted to CRAN on 2019-10-03. -Once it is accepted, delete this file and tag the release (commit 62a869015a). +This package was submitted to CRAN on 2019-10-07. +Once it is accepted, delete this file and tag the release (commit 9bb8e0ae30). diff --git a/DESCRIPTION b/DESCRIPTION index d43cb642..ba5faf78 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: sen2r Type: Package Title: Find, Download and Process Sentinel-2 Data -Version: 1.1.0 +Version: 1.2.0 Authors@R: c(person("Luigi", "Ranghetti", email = "luigi@ranghetti.info", role = c("aut", "cre"), diff --git a/NAMESPACE b/NAMESPACE index 3067c313..b97bd7ab 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -30,12 +30,14 @@ export(s2_gui) export(s2_list) export(s2_mask) export(s2_merge) +export(s2_order) export(s2_perc_masked) export(s2_rgb) export(s2_thumbnails) export(s2_tiles) export(s2_translate) export(safe_getMetadata) +export(safe_is_online) export(safe_isvalid) export(safe_shortname) export(sen2cor) diff --git a/NEWS.md b/NEWS.md index a86ac5f2..6c486e86 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,25 @@ +# Version 1.2.0 + +Starting from this version sen2r supports ordering products from Long Term Archive (LTA) +(see news at https://inthub.copernicus.eu/userguide/LongTermArchive). +Now the user can automatically order SAFE products which are not available for direct download, and use them when made available. +Some internal functions can be exploited to manually manage orders. + +Here above the related changes: + +## New functions +* `safe_is_valid()` to check if an order was processed; +* `s2_order()` to order products from LTA. + +## New arguments +* `sen2r()` and `s2_download()` have a new argument `order_lta` (default: TRUE) to order SAFE archives not available for direct fownload; +* `s2_gui()` has a new checkbox to set the previous argument. + +## Other changes (not related with LTA) +* Function `build_example_param_file()` does no more compute TOA and RGB432T (this to avoid downloading 2 SAFE archives). +* Code coverage was expanded. + + # Version 1.1.0 ## Changes in default values diff --git a/R/build_example_param_file.R b/R/build_example_param_file.R index af905cba..b48e7375 100644 --- a/R/build_example_param_file.R +++ b/R/build_example_param_file.R @@ -48,6 +48,7 @@ build_example_param_file <- function( "s2_levels" = c("l1c", "l2a"), "sel_sensor" = c("s2a", "s2b"), "online" = TRUE, + "order_lta" = TRUE, "downloader" = "builtin", "overwrite_safe" = FALSE, "rm_safe" = "no", @@ -58,9 +59,9 @@ build_example_param_file <- function( "extent" = system.file("extdata/vector/barbellino.geojson", package = "sen2r"), "s2tiles_selected" = NA, "s2orbits_selected" = NA, - "list_prods" = c("TOA", "BOA", "SCL"), + "list_prods" = c("BOA", "SCL"), "list_indices" = c("MSAVI2", "NDVI"), - "list_rgb" = c("RGB432B", "RGB432T", "RGB843B"), + "list_rgb" = c("RGB432B", "RGB843B"), "rgb_ranges" = list( c(0, 2500), c(0, 2500), diff --git a/R/check_param_list.R b/R/check_param_list.R index 33843173..601e468a 100644 --- a/R/check_param_list.R +++ b/R/check_param_list.R @@ -76,7 +76,7 @@ check_param_list <- function(pm, type = "string", check_paths = FALSE, correct = # -- Parameters of length 1: check length pm_length1 <- c( - "preprocess", "online", "downloader", "overwrite_safe", "rm_safe", + "preprocess", "online", "order_lta", "downloader", "overwrite_safe", "rm_safe", "step_atmcorr", "max_cloud_safe", "timeperiod", "extent_name", "index_source", "mask_type", "max_mask", "mask_smooth", "mask_buffer", "clip_on_extent", "extent_as_mask", "reference_path", "res_s2", "unit", "proj", "resampling", @@ -132,6 +132,9 @@ check_param_list <- function(pm, type = "string", check_paths = FALSE, correct = # -- online -- + # -- order_lta -- + + # -- downloader -- if (!pm$downloader %in% c("builtin", "aria2")) { print_message( diff --git a/R/s2_download.R b/R/s2_download.R index 4fad610b..8f91b0f7 100644 --- a/R/s2_download.R +++ b/R/s2_download.R @@ -1,19 +1,23 @@ #' @title Download S2 products. -#' @description The function downloads a single S2 product. -#' Input filename must be an element obtained with +#' @description The function downloads S2 products. +#' Input filenames must be elements obtained with #' [s2_list] function -#' (the content must be a URL, and the name the product name). -#' @param s2_prodlist List of the products to be downloaded +#' (each element must be a URL, and the name the product name). +#' @param s2_prodlist Named character: list of the products to be downloaded #' (this must be the output of [s2_list] function). +#' Alternatively, it can be the path of a JSON file exported by [s2_order]. #' @param downloader Executable to use to download products #' (default: "builtin"). Alternatives are "builtin" or "aria2" #' (this requires aria2c to be installed). #' @param apihub Path of the "apihub.txt" file containing credentials #' of SciHub account. #' If NA (default), the default location inside the package will be used. -#' @param tile Single Sentinel-2 Tile string (5-length character) +#' @param tile Deprecated argument #' @param outdir (optional) Full name of the existing output directory #' where the files should be created (default: current directory). +#' @param order_lta Logical: if TRUE (default), products which are not available +#' for direct download are ordered from the Long Term Archive; +#' if FALSE, they are simply skipped. #' @param overwrite Logical value: should existing output archives be #' overwritten? (default: FALSE) #' @return NULL (the function is called for its side effects) @@ -22,6 +26,7 @@ #' @author Lorenzo Busetto, phD (2019) \email{lbusett@@gmail.com} #' @note License: GPL 3.0 #' @importFrom httr GET RETRY authenticate progress write_disk +#' @importFrom foreach foreach "%do%" #' @export #' #' @examples @@ -40,33 +45,69 @@ #' #' # Download the whole product - using aria2 #' s2_download(single_s2, outdir=tempdir(), downloader = "aria2") #' -#' # Download a specific tile -#' s2_download(single_s2, tile="32TQQ", outdir=tempdir()) -#' # (for products with compact names, the two above commands produce equivalent -#' # results: the first one downloads a SAFE archive, while the second one -#' # downloads single product files) -#' -#' # Download a serie of products -#' pos <- st_sfc(st_point(c(12.0, 44.8)), crs=st_crs(4326)) -#' time_window <- as.Date(c("2017-05-01","2017-07-30")) -#' example_s2_list <- s2_list(spatial_extent=pos, tile="32TQQ", time_interval=time_window) -#' s2_download(example_s2_list, outdir=tempdir()) +#' # Download more products, ordering the ones stored in the Long Term Archive +#' pos <- sf::st_sfc(sf::st_point(c(-57.8815,-51.6954)), crs = 4326) +#' time_window <- as.Date(c("2018-02-21", "2018-03-20")) +#' list_safe <- s2_list(spatial_extent = pos, time_interval = time_window) +#' s2_download(list_safe, outdir=tempdir()) #' } -s2_download <- function(s2_prodlist = NULL, - downloader = "builtin", - apihub = NA, - tile = NULL, - outdir = ".", - overwrite = FALSE) { +s2_download <- function( + s2_prodlist = NULL, + downloader = "builtin", + apihub = NA, + tile = NULL, + outdir = ".", + order_lta = TRUE, + overwrite = FALSE +) { + + # warn for deprecated arguments + if (!missing("tile")) { + warning("argument 'tile' is deprecated and will not be used") + } + + .s2_download( + s2_prodlist = s2_prodlist, + downloader = downloader, + apihub = apihub, + outdir = outdir, + order_lta = order_lta, + overwrite = overwrite, + .s2_availability = NULL + ) + +} + +# internal function, used internally in order not to repeat the check +# for online availability +.s2_download <- function( + s2_prodlist = NULL, + downloader = "builtin", + apihub = NA, + outdir = ".", + order_lta = TRUE, + overwrite = FALSE, + .s2_availability = NULL +) { # convert input NA arguments in NULL - for (a in c("s2_prodlist","tile","apihub")) { + for (a in c("s2_prodlist", "apihub")) { if (suppressWarnings(all(is.na(get(a))))) { assign(a,NULL) } } + # exit if empty + if (length(nn(s2_prodlist)) == 0) { + return(invisible(NULL)) + } + + # import s2_prodlist if it is a path + if (all(length(s2_prodlist) == 1, file.exists(s2_prodlist))) { + s2_prodlist <- unlist(fromJSON(s2_prodlist)) + } + # read credentials creds <- read_scihub_login(apihub) @@ -80,7 +121,28 @@ s2_download <- function(s2_prodlist = NULL, downloader <- "builtin" } - for (i in seq_len(length(s2_prodlist))) { + # Split products to be downloaded from products to be ordered + s2_availability <- if (is.null(.s2_availability)) { + print_message( + type = "message", + date = TRUE, + "Check if products are available for download..." + ) + safe_is_online(s2_prodlist) + } else { + .s2_availability + } + + + # Order products stored from the Long Term Archive + if (order_lta == TRUE) { + ordered_products <- .s2_order(s2_prodlist, .s2_availability = s2_availability) + } + + + ## Download products available for download + + for (i in which(s2_availability)) { link <- s2_prodlist[i] zip_path <- file.path(outdir, paste0(names(s2_prodlist[i]),".zip")) @@ -91,8 +153,8 @@ s2_download <- function(s2_prodlist = NULL, print_message( type = "message", date = TRUE, - "Downloading Sentinel-2 image ", i," of ",length(s2_prodlist), - " (",basename(safe_path),")..." + "Downloading Sentinel-2 image ", which(i == which(s2_availability)), + " of ",sum(s2_availability)," (",basename(safe_path),")..." ) if (downloader %in% c("builtin", "wget")) { # wget left for compatibility @@ -127,6 +189,16 @@ s2_download <- function(s2_prodlist = NULL, }) } + + # check if the user asked to download a LTA product + download_is_lta <- if (inherits(download, "response")) { + download$status_code == 202 + } else if (inherits(download, "integer")) { + download == 22 + } else FALSE + if (download_is_lta) { + # TODO + } if (inherits(download, "try-error")) { suppressWarnings(file.remove(zip_path)) @@ -142,8 +214,7 @@ s2_download <- function(s2_prodlist = NULL, sel_md5 <- httr::GET( url = gsub("\\$value$", "Checksum/Value/$value", as.character(link)), config = httr::authenticate(creds[1], creds[2]), - httr::write_disk(md5file <- tempfile(), overwrite = TRUE), - times = 10 + httr::write_disk(md5file <- tempfile(), overwrite = TRUE) ) md5 <- toupper(readLines(md5file, warn = FALSE)) == toupper(tools::md5sum(zip_path)) file.remove(md5file) @@ -177,8 +248,8 @@ s2_download <- function(s2_prodlist = NULL, print_message( type = "message", date = TRUE, - "Skipping Sentinel-2 image ", i," of ",length(s2_prodlist), - " (",basename(safe_path),") ", + "Skipping Sentinel-2 image ", i," of ",which(i == which(s2_availability)), + " of ",sum(s2_availability),") ", "since the corresponding folder already exists." ) diff --git a/R/s2_gui.R b/R/s2_gui.R index e2a4ff1e..7c1a7766 100644 --- a/R/s2_gui.R +++ b/R/s2_gui.R @@ -351,7 +351,7 @@ s2_gui <- function(param_list = NULL, choiceNames = list("Online", "Offline"), choiceValues = list(TRUE, FALSE), selected = TRUE, - inline = FALSE + inline = TRUE ), # SciHub credentials @@ -359,10 +359,18 @@ s2_gui <- function(param_list = NULL, condition = "input.online == 'TRUE'", div( style = "padding-bottom:10px;", + checkboxInput( + "make_lta_order", + label = span( + "Order from LTA\u2000", + actionLink("help_lta_order", icon("question-circle")) + ), + value = TRUE + ), actionButton( "scihub_md", label = "\u2000Login to SciHub", - icon=icon("user-circle") + icon = icon("user-circle") ) ) ) @@ -2633,6 +2641,37 @@ s2_gui <- function(param_list = NULL, )) }) + observeEvent(input$help_lta_order, { + showModal(modalDialog( + title = "Order from LTA", + p(HTML( + "Starting from September 2019, SAFE archives older than 12 months", + "(Level-1C) or 18 months (Level-2A) are generally not available", + "for direct download, but must be ordered from the Long Term Archive", + "(see this page for any details)." + )), + p(HTML( + "Checking this option, products which are not available for direct", + "download are ordered, so to be available at a later time.", + "There is no way to know when the will be made available; the user", + "can re-launch the same sen2r processing chain at a later time:", + "in this way, when missing SAFE archives will be made available", + "they will be downloaded and the output prodcut archive will be updated." + )), + p(HTML( + "Alternatively, specific non-interactive functions are available", + "to manage orders (see", + "safe_is_online() and", + "s2_order())." + )), + easyClose = TRUE, + footer = NULL + )) + }) + observeEvent(input$help_downloader, { showModal(modalDialog( title = "Downloader", @@ -3363,6 +3402,7 @@ s2_gui <- function(param_list = NULL, rl$s2_levels <- c(if(safe_req$l1c==TRUE){"l1c"}, if(safe_req$l2a==TRUE){"l2a"}) # required S2 levels ("l1c","l2a") rl$sel_sensor <- input$sel_sensor # sensors to use ("s2a", "s2b") rl$online <- as.logical(input$online) # TRUE if online mode, FALSE if offline mode + rl$order_lta <- as.logical(input$make_lta_order) # TRUE to order from LTA, FALSE to skip rl$downloader <- input$downloader # downloader ("builtin" or "aria2") rl$overwrite_safe <- as.logical(input$overwrite_safe) # TRUE to overwrite existing SAFE, FALSE not to rl$rm_safe <- input$rm_safe # "yes" to delete all SAFE, "l1c" to delete only l1c, "no" not to remove @@ -3519,6 +3559,7 @@ s2_gui <- function(param_list = NULL, updateCheckboxGroupInput(session, "list_levels", selected = pl$s2_levels) updateCheckboxGroupInput(session, "sel_sensor", selected = pl$sel_sensor) updateRadioButtons(session, "online", selected = pl$online) + updateRadioButtons(session, "make_lta_order", selected = pl$order_lta) updateRadioButtons(session, "downloader", selected = pl$downloader) updateRadioButtons(session, "overwrite_safe", selected = pl$overwrite_safe) updateRadioButtons(session, "rm_safe", selected = pl$rm_safe) @@ -3692,7 +3733,7 @@ s2_gui <- function(param_list = NULL, sendSweetAlert( session, NULL, paste0( - "Please select at least one product, spectral index or RGB image", + "Please select at least one product, spectral index or RGB image ", "before continuing." ), type = "error" diff --git a/R/s2_list.R b/R/s2_list.R index 9b50991f..802e135f 100644 --- a/R/s2_list.R +++ b/R/s2_list.R @@ -25,13 +25,29 @@ #' If NA (default), the default location inside the package will be used. #' @param max_cloud Integer number (0-100) containing the maximum cloud #' level of the tiles to be listed (default: no filter). +#' @param availability Character argument, determining which products have +#' to be returned: +#' - "online" : only archive names already available for download are returned; +#' - "lta": only archive names stored in the Long Term Archive +#' (see https://scihub.copernicus.eu/userguide/LongTermArchive) +#' are returned; +#' - "check": all archive names are returned, checking if they are +#' available or not for download (see "Value" to know +#' how to distinguish each other); +#' - "ignore" (default): all archive names are returned, without doing the check +#' (running the function is faster). #' @param output_type Character: if 'vector' (default), the function returns #' a vector or URLs, whose names are the SAFE names; #' if 'data.table', the output is a data.table with metadata. -#' @return Depending on the value of argument `output_type``, -#' a vector of available products (being each element an URL -#' and its name the product name), or a data.table with product metadata. -#' +#' @return Depending on the value of argument `output_type`: +#' if `output_type = "vector"`, a vector of available products +#' (being each element an URL and its name the product name), +#' together with the attributes `online` (with the index of products available +#' for download) and `lta` (with the index of products stored in the Long Term +#' Archive) in the case `availability == "check"`; +#' if `output_type = "data.table"`, a data.table with product metadata +#' (including the logical field `online` to allow distinguishing products +#' available for download and stored in the Long Term Archive). #' @author Lorenzo Busetto, phD (2019) \email{lbusett@@gmail.com} - Inspired by #' function `getSentinel_query` of package `getSpatialData` by J. Schwalb-Willmann #' (https://github.com/16EAGLE/getSpatialData) @@ -88,6 +104,7 @@ s2_list <- function(spatial_extent = NULL, level = "auto", apihub = NA, max_cloud = 100, + availability = "ignore", output_type = "vector") { if (!level %in% c("auto", "L2A", "L1C")) { @@ -104,6 +121,14 @@ s2_list <- function(spatial_extent = NULL, ) } + if (!availability %in% c("ignore", "check", "online", "lta")) { + print_message( + type = "error", + "`availability` must be one among \"online\", \"lta\", ", + "\"check\" and \"ignore\"" + ) + } + if (inherits(try(as.Date(time_interval)), "try-error")) { print_message( type = "error", @@ -113,7 +138,7 @@ s2_list <- function(spatial_extent = NULL, } # to avoid NOTE on check - . <- i <- NULL + . <- i <- online <- NULL # convert input NA arguments in NULL for (a in c("spatial_extent","tile","orbit","time_interval","apihub")) { @@ -282,7 +307,7 @@ s2_list <- function(spatial_extent = NULL, 'start=', start, '&rows=', rows, '&q=', foot, - ' AND platformname:Sentinel-2 ', + ' AND platformname:Sentinel-2', ' AND beginposition:[', time_intervals[t_int,1], 'T00:00:00.000Z', ' TO ', time_intervals[t_int,2], 'T23:59:59.000Z]', ' AND cloudcoverpercentage:[0 TO ', max_cloud,']' @@ -369,6 +394,13 @@ s2_list <- function(spatial_extent = NULL, if (nrow(out_dt) == 0) {return(character(0))} + # check online availability + out_dt$online <- if (availability == "ignore") { + NA + } else { + as.logical(safe_is_online(out_dt$url)) + } + # remove "wrong" tiles and orbits if needed if (!is.null(tile)) { out_dt <- out_dt[tileid %in% tile,] @@ -394,11 +426,23 @@ s2_list <- function(spatial_extent = NULL, if (nrow(out_dt) == 0) {return(character(0))} out_dt <- out_dt[order(date),] + # filter by availability + if (availability == "online") { + out_dt <- out_dt[online == TRUE,] + } else if (availability == "lta") { + out_dt <- out_dt[online == FALSE,] + } + + # return output if (output_type == "data.table") { return(out_dt) } else { out_vector <- out_dt$url names(out_vector) <- out_dt$name + if (availability == "check") { + attr(out_vector, "online") <- which(out_dt$online) + attr(out_vector, "lta") <- which(!out_dt$online) + } return(out_vector) } } diff --git a/R/s2_order.R b/R/s2_order.R new file mode 100644 index 00000000..5a165cf0 --- /dev/null +++ b/R/s2_order.R @@ -0,0 +1,225 @@ +#' @title Order S2 products. +#' @description The function orders S2 products from Long Term Archive +#' (https://scihub.copernicus.eu/userguide/LongTermArchive). +#' @param s2_prodlist Named character: list of the products to be ordered +#' (this must be the output of [s2_list] function). +#' Alternatively, it can be the path of a JSON file exported by a previous +#' execution of [s2_order], in case the user wants, for any reason, to +#' resubmit the order. +#' @param export_prodlist Logical or character: if TRUE (default), +#' the list of ordered products is saved in a JSON text file, so to be easily +#' retrievable at a later stage with [safe_is_online] or [s2_download]; +#' if FALSE, no output files are generated. +#' It is also possible to pass the path of an existing folder in which the +#' JSON file will be saved (otherwise, a default path is used). +#' @param delay Numeric: time frame (in seconds) to leave between two +#' consecutive orders. Default is 5 seconds: use a higher value if you +#' encountered errors (i.e. not all the products were correctly ordered). +#' @param apihub Path of the "apihub.txt" file containing credentials +#' of SciHub account. +#' If NA (default), the default location inside the package will be used. +#' @return A named vector, containing the selection of `s2_prodlist` elements +#' which were ordered. +#' Moreover, the vector includes the following attributes: +#' - "available" with the elements of `s2_prodlist` which were already +#' available for download, +#' - "notordered" with the elements of `s2_prodlist` which were not ordered +#' for any reasons, +#' - "path" (only if argument `export_prodlist` is not FALSE) with the path of +#' the text file in which the list of the ordered products was saved. +#' +#' @author Luigi Ranghetti, phD (2019) \email{luigi@@ranghetti.info} +#' @note License: GPL 3.0 +#' @importFrom httr GET authenticate +#' @importFrom foreach foreach "%do%" +#' @importFrom jsonlite fromJSON toJSON +#' @export +#' +#' @examples +#' \donttest{ +#' # Generate the lists of products +#' pos <- sf::st_sfc(sf::st_point(c(-57.8815,-51.6954)), crs = 4326) +#' time_window <- as.Date(c("2018-02-21", "2018-03-20")) +#' list_safe <- s2_list(spatial_extent = pos, time_interval = time_window) +#' print(list_safe) +#' # (at the time the documentation was written, this list was containing 5 +#' # archives already available online and 2 stored in the Long Term Archive) +#' +#' # Order the products +#' ordered_prods <- s2_order(list_safe) +#' +#' # Check in a second time if the product was made available +#' (order_path <- attr(ordered_prods, "path")) +#' safe_is_online(order_path) +#' } + +s2_order <- function( + s2_prodlist = NULL, + export_prodlist = TRUE, + delay = 5, + apihub = NA +) { + .s2_order( + s2_prodlist = s2_prodlist, + export_prodlist = export_prodlist, + delay = delay, + apihub = apihub, + .s2_availability = NULL + ) +} + +# internal function, used internally in order not to repeat the check +# for online availability +.s2_order <- function( + s2_prodlist = NULL, + export_prodlist = TRUE, + delay = 5, + apihub = NA, + .s2_availability = NULL +) { + + # to avoid NOTE on check + i <- NULL + + # convert input NA arguments in NULL + for (a in c("s2_prodlist", "export_prodlist", "apihub")) { + if (suppressWarnings(all(is.na(get(a))))) { + assign(a,NULL) + } + } + + # exit if empty + if (length(nn(s2_prodlist)) == 0) { + return(invisible(NULL)) + } + + # check export_prodlist + if (all(is.character(export_prodlist), length(export_prodlist) > 0)) { + if (!dir.exists(export_prodlist)) { + print_message( + type = "error", + "Argument 'export_prodlist' must be TRUE, FALSE or the path of an existing folder." + ) + } + } + + # check delay to be numeric + if (any(length(delay) == 0, !is.numeric(delay))) { + print_message( + type = "error", + "Argument 'delay' must be numeric" + ) + } + + # import s2_prodlist if it is a path + if (all(length(s2_prodlist) == 1, file.exists(s2_prodlist))) { + s2_prodlist <- unlist(fromJSON(s2_prodlist)) + } + + # replace apihub with dhus + s2_prodlist <- gsub("apihub", "dhus", s2_prodlist) + + # read credentials + creds <- read_scihub_login(apihub) + + # Split products to be downloaded from products to be ordered + s2_availability <- if (is.null(.s2_availability)) { + print_message( + type = "message", + date = TRUE, + "Check if products are already available for download..." + ) + safe_is_online(s2_prodlist) + } else { + .s2_availability + } + + # If some products are already available, print a message + if (sum(s2_availability) > 0) { + print_message( + type = "message", + date = TRUE, + sum(s2_availability)," Sentinel-2 images are already available ", + "and will not be ordered." + ) + } + + + ## Order products stored in Long Term Archive + if (sum(!s2_availability) > 0) { + print_message( + type = "message", + date = TRUE, + "Ordering ",sum(!s2_availability)," Sentinel-2 images ", + "stored in the Long Term Archive..." + ) + } + ordered_products <- foreach(i = which(!s2_availability), .combine = c) %do% { + # delay after previous order + if (i != which(!s2_availability)[1]) { + Sys.sleep(delay) + } + # order products + make_order <- httr::GET( + url = as.character(s2_prodlist[i]), + config = httr::authenticate(creds[1], creds[2]) + ) + # check if the order was successful + if (inherits(make_order, "response")) { + make_order$status_code == 202 + } else FALSE + } + + out_list <- s2_prodlist[!s2_availability][ordered_products] + attr(out_list, "available") <- s2_prodlist[s2_availability] + attr(out_list, "notordered") <- s2_prodlist[!s2_availability][!nn(ordered_products)] + order_time <- Sys.time() + + # export prodlist, unless export_prodlist == FALSE + if (any(export_prodlist != FALSE) & length(out_list) > 0) { + prodlist_dir <- if (is.logical(export_prodlist)) { + file.path(dirname(attr(load_binpaths(), "path")), "lta_orders") + } else { + export_prodlist + } + dir.create(prodlist_dir, showWarnings = FALSE) + prodlist_path <- file.path( + prodlist_dir, + strftime(order_time, format = "lta_%Y%m%d_%H%M%S.json") + ) + writeLines( + toJSON(as.list(out_list), pretty = TRUE), + prodlist_path + ) + attr(out_list, "path") <- prodlist_path + } + + if (sum(ordered_products) > 0) { + print_message( + type = "message", + date = TRUE, + sum(ordered_products)," of ",sum(!s2_availability)," Sentinel-2 images ", + "were correctly ordered. ", + "You can check at a later time if the ordered products were made available ", + "using the command:\n\n", + if (is.null(attr(out_list, "path"))) {paste0( + 'safe_is_online(c(\n "',paste(out_list, collapse = '",\n "'),'"\n))' + )} else {paste0( + 'safe_is_online("',attr(out_list, "path"),'")' + )}, + "\n" + ) + } + if (sum(!nn(ordered_products)) > 0) { + print_message( + type = "warning", + date = TRUE, + sum(!ordered_products)," of ",sum(!s2_availability)," Sentinel-2 images ", + "were not correctly ordered. ", + "Try using a higher value for the argument \"delay\"." + ) + } + + return(out_list) + +} diff --git a/R/safe_is_online.R b/R/safe_is_online.R new file mode 100644 index 00000000..b5e5817b --- /dev/null +++ b/R/safe_is_online.R @@ -0,0 +1,74 @@ +#' @title Check if SAFE is available for download +#' @description The function checks if the required SAFE archives are +#' available for download, or if they have to be ordered from the Long Term +#' Archive. +#' @param s2_prodlist Named character: list of the products to be checked +#' (this must be the output of [s2_list] function). +#' Alternatively, it can be the path of a JSON file exported by [s2_order]. +#' @param apihub Path of the "apihub.txt" file containing credentials +#' of SciHub account. +#' If NA (default), the default location inside the package will be used. +#' @return A logical vector of the same length and names of the SAFE products +#' passed with `s2_prodlist`, +#' in which each element is TRUE if the corresponding SAFE archive is +#' available for download, FALSE if it is not or NA in case of errors with +#' the SAFE url. +#' @author Luigi Ranghetti, phD (2019) \email{luigi@@ranghetti.info} +#' @note License: GPL 3.0 +#' @importFrom httr GET authenticate content +#' @importFrom jsonlite fromJSON +#' @export +#' +#' @examples +#' \donttest{ +#' # Generate the lists of products +#' pos <- sf::st_sfc(sf::st_point(c(-57.8815,-51.6954)), crs = 4326) +#' time_window <- as.Date(c("2018-02-21", "2018-03-20")) +#' list_safe <- s2_list(spatial_extent = pos, time_interval = time_window) +#' # (at the time the documentation was written, this list was containing 5 +#' # archives already available online and 2 stored in the Long Term Archive) +#' +#' # Check for availability +#' safe_is_online(list_safe) +#' } + +safe_is_online <- function(s2_prodlist = NULL, apihub = NA) { + + # convert input NA arguments in NULL + for (a in c("s2_prodlist", "apihub")) { + if (suppressWarnings(all(is.na(get(a))))) { + assign(a,NULL) + } + } + + # exit if empty + if (length(nn(s2_prodlist)) == 0) { + return(invisible(NULL)) + } + + # import s2_prodlist if it is a path + if (all(length(s2_prodlist) == 1, file.exists(s2_prodlist))) { + s2_prodlist <- unlist(fromJSON(s2_prodlist)) + } + + # replace apihub with dhus + s2_prodlist <- gsub("apihub", "dhus", s2_prodlist) + + # read credentials + creds <- read_scihub_login(apihub) + + # check for availability + s2_availability <- as.logical(sapply(s2_prodlist, function(p) { + tryCatch( + httr::content(httr::GET( + url = gsub("\\$value$", "Online/$value", p), + config = httr::authenticate(creds[1], creds[2]) + ), as = "parsed", encoding = "UTF-8"), + error = function(e) {NA} + ) + })) + names(s2_availability) <- names(s2_prodlist) + s2_availability + +} + \ No newline at end of file diff --git a/R/sen2r.R b/R/sen2r.R index 310370a0..1d0fff35 100644 --- a/R/sen2r.R +++ b/R/sen2r.R @@ -35,6 +35,9 @@ #' @param online (optional) Logical: TRUE (default) to search for available #' products on SciHub (and download if needed); FALSE to work #' only with already downloaded SAFE products. +#' @param order_lta (optional) Logical: TRUE (default) to order products from +#' the Long Term Archive if unavailable for direct download; FALSE to simply +#' skip them (this option has effect only in online mode). #' @param apihub Path of the text file containing credentials #' of SciHub account. #' If NA (default), the default location inside the package will be used. @@ -396,6 +399,7 @@ sen2r <- function(param_list = NULL, s2_levels = c("l1c","l2a"), sel_sensor = c("s2a","s2b"), online = TRUE, + order_lta = TRUE, apihub = NA, downloader = "builtin", overwrite_safe = FALSE, @@ -480,6 +484,7 @@ sen2r <- function(param_list = NULL, s2_levels = s2_levels, sel_sensor = sel_sensor, online = online, + order_lta = order_lta, apihub = apihub, downloader = downloader, overwrite_safe = overwrite_safe, @@ -573,6 +578,7 @@ sen2r <- function(param_list = NULL, s2_levels, sel_sensor, online, + order_lta, apihub, downloader, overwrite_safe, @@ -630,7 +636,7 @@ sen2r <- function(param_list = NULL, # to avoid NOTE on check . <- sensing_datetime <- creation_datetime <- mission <- level <- id_orbit <- id_tile <- name <- id_baseline <- prod_type <- name <- sel_group_A <- - i_group_A <- sel_apihub_path <- i_group_B <- sensing_date <- NULL + i_group_A <- sel_apihub_path <- i_group_B <- sensing_date <- lta <- NULL ### Preliminary settings ### @@ -1016,7 +1022,7 @@ sen2r <- function(param_list = NULL, ### Find SAFE and compute the names of required files ### ## 2. List required products ## - s2_lists <- list() + s2_lists <- s2_lists_islta <- list() if (pm$online == TRUE) { @@ -1041,8 +1047,12 @@ sen2r <- function(param_list = NULL, orbit = pm$s2orbits_selected, level = "L1C", max_cloud = pm$max_cloud_safe, + availability = "check", apihub = pm$apihub ) + # save lta availability (TRUE if on LTA, FALSE if online) + s2_lists_islta[["l1c"]] <- seq_along(s2_lists[["l1c"]]) %in% attr(s2_lists[["l1c"]], "lta") + names(s2_lists_islta[["l1c"]]) <- names(s2_lists[["l1c"]]) } if ("l2a" %in% pm$s2_levels) { # list of SAFE (L1C or/and L2A) needed for required L2A @@ -1064,8 +1074,12 @@ sen2r <- function(param_list = NULL, "L1C" }, max_cloud = pm$max_cloud_safe, + availability = "check", apihub = pm$apihub ) + # save lta availability (TRUE if on LTA, FALSE if online) + s2_lists_islta[["l2a"]] <- seq_along(s2_lists[["l2a"]]) %in% attr(s2_lists[["l2a"]], "lta") + names(s2_lists_islta[["l2a"]]) <- names(s2_lists[["l2a"]]) } } else { @@ -1106,7 +1120,8 @@ sen2r <- function(param_list = NULL, } s2_list <- unlist(s2_lists)[!duplicated(unlist(lapply(s2_lists, names)))] - rm(s2_lists) + s2_list_islta <- unlist(s2_lists_islta)[!duplicated(unlist(lapply(s2_lists_islta, names)))] + rm(s2_lists, s2_lists_islta) # If s2_list is empty, exit if (length(s2_list)==0) { @@ -1142,6 +1157,7 @@ sen2r <- function(param_list = NULL, info="nameinfo" ), stringsAsFactors = FALSE)[-1,] } + s2_dt[,"lta":=if (is.null(s2_list_islta)) {FALSE} else {s2_list_islta}] s2_dt[,c("name","url"):=list(nn(names(s2_list)),nn(s2_list))] s2_dt[,c("sensing_datetime","creation_datetime"):= list(as.POSIXct(sensing_datetime, format="%s"), @@ -1189,16 +1205,17 @@ sen2r <- function(param_list = NULL, !is.na(match(s2_meta_pasted, s2_existing_meta_pasted)), name := s2_existing_list[na.omit(match(s2_meta_pasted, s2_existing_meta_pasted))] ] - s2_dt[!is.na(match(s2_meta_pasted, s2_existing_meta_pasted)), url:=""] + s2_dt[!is.na(match(s2_meta_pasted, s2_existing_meta_pasted)), c("url","lta"):=list("",FALSE)] } } # removing duplicated products - # (in case of products with different baseline / ingestion time, keep the most recent one) + # (in case of products with different baseline / ingestion time, keep the most + # recent one / the existing or downloadable) s2_dt <- if (!is.null(s2_dt$id_baseline)) { - s2_dt[order(-sensing_datetime, -creation_datetime, -id_baseline),] + s2_dt[order(-sensing_datetime, lta, -creation_datetime, -id_baseline),] } else { - s2_dt[order(-sensing_datetime, -creation_datetime),] + s2_dt[order(-sensing_datetime, lta, -creation_datetime),] } s2_dt <- s2_dt[!duplicated(paste( prod_type, version, mission, level, @@ -1245,10 +1262,21 @@ sen2r <- function(param_list = NULL, s2_dt <- s2_dt[id_orbit %in% pm$s2orbits_selected,] } # setorder(s2_dt, -sensing_datetime) - s2_list_l1c <- s2_dt[level=="1C",url] # list of required L1C - s2_list_l2a <- s2_dt[level=="2A",url] # list of required L2A - names(s2_list_l1c) <- s2_dt[level=="1C",name] - names(s2_list_l2a) <- s2_dt[level=="2A",name] + + s2_list_lta <- s2_dt[lta==TRUE, url] # list of SAFE to be ordered + s2_list_l1c <- s2_dt[lta==FALSE & level=="1C", url] # list of required L1C + s2_list_l2a <- s2_dt[lta==FALSE & level=="2A", url] # list of required L2A + names(s2_list_lta) <- s2_dt[lta==TRUE, name] + names(s2_list_l1c) <- s2_dt[lta==FALSE & level=="1C", name] + names(s2_list_l2a) <- s2_dt[lta==FALSE & level=="2A", name] + + # Order products from LTA if required + if (pm$online == TRUE & pm$order_lta == TRUE) { + s2_list_ordered <- .s2_order( + s2_list_lta, + .s2_availability = rep(FALSE, length(s2_list_lta)) + ) + } # add expected L2A names (after sen2cor) if (pm$step_atmcorr %in% c("auto","scihub")) { @@ -1336,6 +1364,8 @@ sen2r <- function(param_list = NULL, if (any(s2_l1c_orphan)) {paste0( "This issue can be avoided by setting argument \"step_atmcorr\" ", "to 'auto' or 'scihub', or \"online\" to TRUE, ", + "or re-launching the processing when products ordered from the ", + "Long Term Archive will be made available, ", "so that missing Level-2A can be produced or downloaded." )} ) @@ -1637,35 +1667,39 @@ sen2r <- function(param_list = NULL, overwrite = pm$overwrite_safe ) } else { # otherwise, launch one per tile - lapply(pm$s2tiles_selected, function(tile) { - if (length(pm$s2tiles_selected) > 1) { - print_message( - type = "message", - date = TRUE, - "Cycle ",grep(tile, pm$s2tiles_selected), - " of ",length(pm$s2tiles_selected), - " (tile ",tile, - if (grep(tile, pm$s2tiles_selected)==1) { - " and products with a compact name)" - } else { - " within products with an old long name)" - } - ) - } - s2_download( - sel_s2_list_l2a, # ask to download all the images, - # and not only the non existing ones, - # because of the cycle on tiles - # (with compactname products, all the zips are downloaded - # during the first execution, since argument "tile" is - # ignored). - outdir = path_l2a, - downloader = pm$downloader, - tile = tile, - apihub = sel_apihub_path, - overwrite = pm$overwrite_safe - ) - }) + print_message( + type = "error", + "Old name SAFE products are no longer supported." + ) + # lapply(pm$s2tiles_selected, function(tile) { + # if (length(pm$s2tiles_selected) > 1) { + # print_message( + # type = "message", + # date = TRUE, + # "Cycle ",grep(tile, pm$s2tiles_selected), + # " of ",length(pm$s2tiles_selected), + # " (tile ",tile, + # if (grep(tile, pm$s2tiles_selected)==1) { + # " and products with a compact name)" + # } else { + # " within products with an old long name)" + # } + # ) + # } + # s2_download( + # sel_s2_list_l2a, # ask to download all the images, + # # and not only the non existing ones, + # # because of the cycle on tiles + # # (with compactname products, all the zips are downloaded + # # during the first execution, since argument "tile" is + # # ignored). + # outdir = path_l2a, + # downloader = pm$downloader, + # tile = tile, + # apihub = sel_apihub_path, + # overwrite = pm$overwrite_safe + # ) + # }) } print_message( @@ -1713,34 +1747,38 @@ sen2r <- function(param_list = NULL, overwrite = pm$overwrite_safe ) } else { # otherwise, launch one per tile - lapply(pm$s2tiles_selected, function(tile) { - if (length(pm$s2tiles_selected) > 1) { - print_message( - type = "message", - date = TRUE, - "Cycle ",grep(tile, pm$s2tiles_selected), - " of ",length(pm$s2tiles_selected), - " (tile ",tile, - if (grep(tile, pm$s2tiles_selected)==1) { - " and products with a compact name)" - } else { - " within products with an old long name)" - } - ) - } - s2_download( - sel_s2_list_l1c, # ask to download all the images, - # and not only the non existing ones, - # because of the cycle on tiles - # (with compactname products, all the zips are downloaded - # during the first execution, since argument "tile" is - # ignored). - outdir = path_l1c, - downloader = pm$downloader, - tile = tile, - overwrite = pm$overwrite_safe - ) - }) + print_message( + type = "error", + "Old name SAFE products are no longer supported." + ) + # lapply(pm$s2tiles_selected, function(tile) { + # if (length(pm$s2tiles_selected) > 1) { + # print_message( + # type = "message", + # date = TRUE, + # "Cycle ",grep(tile, pm$s2tiles_selected), + # " of ",length(pm$s2tiles_selected), + # " (tile ",tile, + # if (grep(tile, pm$s2tiles_selected)==1) { + # " and products with a compact name)" + # } else { + # " within products with an old long name)" + # } + # ) + # } + # s2_download( + # sel_s2_list_l1c, # ask to download all the images, + # # and not only the non existing ones, + # # because of the cycle on tiles + # # (with compactname products, all the zips are downloaded + # # during the first execution, since argument "tile" is + # # ignored). + # outdir = path_l1c, + # downloader = pm$downloader, + # tile = tile, + # overwrite = pm$overwrite_safe + # ) + # }) } # FIXME this operation can be very long with oldname products but tiled: # Sentinel-download.py scans within single xml files and discharges diff --git a/README.Rmd b/README.Rmd index 475132de..449c8a01 100644 --- a/README.Rmd +++ b/README.Rmd @@ -21,6 +21,7 @@ knitr::opts_chunk$set( ![Docker Automated build](https://img.shields.io/docker/automated/ranghetti/sen2r.svg) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1240384.svg)](https://doi.org/10.5281/zenodo.1240384) [![CRAN Status](http://www.r-pkg.org/badges/version/sen2r)](https://cran.r-project.org/package=sen2r) +[![Coverage Status](http://img.shields.io/codecov/c/github/ranghetti/sen2r/master.svg)](http://codecov.io/github/ranghetti/sen2r?branch=master) [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](http://www.gnu.org/licenses/gpl-3.0) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v1.4%20adopted-ff69b4.svg)](http://sen2r.ranghetti.info/CONDUCT.html) diff --git a/README.md b/README.md index 11529f22..bf66ee98 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,11 @@ + - [sen2r: Find, Download and Process Sentinel-2 + Data](#sen2r-find-download-and-process-sentinel-2-data) + - [Installation](#installation) + - [Usage](#usage) + - [Credits](#credits) + - [Contributing](#contributing) + @@ -10,6 +17,8 @@ build](https://img.shields.io/docker/automated/ranghetti/sen2r.svg) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1240384.svg)](https://doi.org/10.5281/zenodo.1240384) [![CRAN Status](http://www.r-pkg.org/badges/version/sen2r)](https://cran.r-project.org/package=sen2r) +[![Coverage +Status](http://img.shields.io/codecov/c/github/ranghetti/sen2r/master.svg)](http://codecov.io/github/ranghetti/sen2r?branch=master) [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](http://www.gnu.org/licenses/gpl-3.0) [![Contributor @@ -97,8 +106,7 @@ sen2r() ``` this opens a GUI which allows to set the required processing parameters, -and then launches the main -function. +and then launches the main function.

@@ -138,8 +146,7 @@ Other specific functions can be used to run single steps separately: to convert Sentinel-2 products from SAFE format to a format managed by GDAL; - [`s2_merge()`](http://sen2r.ranghetti.info/reference/s2_merge.html) - to merge Sentinel-2 tiles which have the same date and - orbit; + to merge Sentinel-2 tiles which have the same date and orbit; - [`gdal_warp()`](http://sen2r.ranghetti.info/reference/gdal_warp.html) to clip, reproject and warp raster files (this is a wrapper to call [gdal\_translate](http://www.gdal.org/gdal_translate.html) or @@ -149,12 +156,10 @@ Other specific functions can be used to run single steps separately: apply a cloud mask to Sentinel-2 products; - [`s2_rgb()`](http://sen2r.ranghetti.info/reference/s2_rgb.html) to generate RGB images from Sentinel-2 Surface Reflectance multiband - raster - files; + raster files; - [`s2_calcindices()`](http://sen2r.ranghetti.info/reference/s2_calcindices.html) to compute maps of spectral indices from Sentinel-2 Surface - Reflectance multiband raster - files; + Reflectance multiband raster files; - [`s2_thumbnails()`](http://sen2r.ranghetti.info/reference/s2_thumbnails.html) to generate RGB thumbnails (JPEG or PNG) of the products. @@ -179,7 +184,7 @@ International license](https://creativecommons.org/licenses/by-sa/4.0) To cite this library, please use the following entry: Ranghetti, L. and Busetto, L. (2019). *sen2r: Find, Download and Process -Sentinel-2 Data*. R package version 1.1.0. DOI: +Sentinel-2 Data*. R package version 1.0.0. DOI: [10.5281/zenodo.1240384](https://dx.doi.org/10.5281/zenodo.1240384). URL: . @@ -188,7 +193,7 @@ URL: . title = {sen2r: Find, Download and Process Sentinel-2 Data}, author = {Luigi Ranghetti and Lorenzo Busetto}, year = {2019}, - note = {R package version 1.1.0}, + note = {R package version 1.0.0}, doi = {10.5281/zenodo.1240384}, url = {http://sen2r.ranghetti.info}, } diff --git a/_pkgdown.yaml b/_pkgdown.yaml index 00032200..59f676f0 100644 --- a/_pkgdown.yaml +++ b/_pkgdown.yaml @@ -13,7 +13,9 @@ reference: contents: - '`s2_list`' - '`s2_download`' + - '`s2_order`' - '`sen2cor`' + - '`safe_is_online`' - '`read_scihub_login`' - title: Read and convert SAFE format desc: ~ @@ -67,6 +69,8 @@ navbar: href: index.html - text: "Function reference" href: reference/index.html + - text: "News" + href: news/index.html right: - icon: fa-github fa-lg href: https://github.com/ranghetti/sen2r/ diff --git a/docs/404.html b/docs/404.html index f677dc9c..bd2de39f 100644 --- a/docs/404.html +++ b/docs/404.html @@ -88,6 +88,9 @@

  • Function reference +
  • +
  • + News
  • -
    +

    -New functions

    +New functions
    -
    +

    -New functions

    +New functions
    • tiles_intersects(): when an extent is loaded, now only required tiles are automatically used, instead than all the overlapping ones.
    • @@ -421,9 +458,9 @@

      Version 0.3.2

      This is an improvement of version 0.3.1, with several fixes and improvements.

      -
      +

      -New functions

      +New functions
      • check_sen2r_deps(): a new GUI to help checking dependencies.

      • Add function s2_perc_masked() to compute the percentage of cloud-masked surface.

      • @@ -541,6 +578,7 @@

        Contents

      #> Carico il pacchetto richiesto: sp
      #> Computing index: EVI
      #> Processing chunk 1 of 4
      #> Processing chunk 2 of 4
      #> Processing chunk 3 of 4
      #> Processing chunk 4 of 4
      ex_out
      #> [1] "/tmp/RtmpL9pop3/S2A2A_20170703_022_Barbellino_EVI_10.tif"
      +)
      #> Carico il pacchetto richiesto: sp
      #> Computing index: EVI
      #> Processing chunk 1 of 4
      #> Processing chunk 2 of 4
      #> Processing chunk 3 of 4
      #> Processing chunk 4 of 4
      ex_out
      #> [1] "/tmp/RtmpqeawSI/S2A2A_20170703_022_Barbellino_EVI_10.tif"
      # Show output oldpar <- par(mfrow = c(1,2), mar = rep(0,4)) image(stars::read_stars(ex_in), rgb = 4:2, maxColorValue = 3500) diff --git a/docs/reference/s2_defNA.html b/docs/reference/s2_defNA.html index 1accea05..2e0fa37e 100644 --- a/docs/reference/s2_defNA.html +++ b/docs/reference/s2_defNA.html @@ -92,6 +92,9 @@
    • Function reference +
    • +
    • + News
    -

    The function downloads a single S2 product. -Input filename must be an element obtained with +

    The function downloads S2 products. +Input filenames must be elements obtained with s2_list function -(the content must be a URL, and the name the product name).

    +(each element must be a URL, and the name the product name).

    s2_download(s2_prodlist = NULL, downloader = "builtin", apihub = NA,
    -  tile = NULL, outdir = ".", overwrite = FALSE)
    + tile = NULL, outdir = ".", order_lta = TRUE, overwrite = FALSE)

    Arguments

    - + @@ -152,12 +156,18 @@

    Arg

    - + + + + + @@ -189,17 +199,11 @@

    Examp #' # Download the whole product - using aria2 s2_download(single_s2, outdir=tempdir(), downloader = "aria2") -# Download a specific tile -s2_download(single_s2, tile="32TQQ", outdir=tempdir()) -# (for products with compact names, the two above commands produce equivalent -# results: the first one downloads a SAFE archive, while the second one -# downloads single product files) - -# Download a serie of products -pos <- st_sfc(st_point(c(12.0, 44.8)), crs=st_crs(4326)) -time_window <- as.Date(c("2017-05-01","2017-07-30")) -example_s2_list <- s2_list(spatial_extent=pos, tile="32TQQ", time_interval=time_window) -s2_download(example_s2_list, outdir=tempdir()) +# Download more products, ordering the ones stored in the Long Term Archive +pos <- sf::st_sfc(sf::st_point(c(-57.8815,-51.6954)), crs = 4326) +time_window <- as.Date(c("2018-02-21", "2018-03-20")) +list_safe <- s2_list(spatial_extent = pos, time_interval = time_window) +s2_download(list_safe, outdir=tempdir()) }

    s2_prodlist

    List of the products to be downloaded -(this must be the output of s2_list function).

    Named character: list of the products to be downloaded +(this must be the output of s2_list function). +Alternatively, it can be the path of a JSON file exported by s2_order.

    downloader
    tile

    Single Sentinel-2 Tile string (5-length character)

    Deprecated argument

    outdir

    (optional) Full name of the existing output directory where the files should be created (default: current directory).

    order_lta

    Logical: if TRUE (default), products which are not available +for direct download are ordered from the Long Term Archive; +if FALSE, they are simply skipped.

    overwrite
    @@ -179,6 +182,21 @@

    Arg

    + + + + @@ -190,9 +208,15 @@

    Arg

    Value

    -

    Depending on the value of argument `output_type``, -a vector of available products (being each element an URL -and its name the product name), or a data.table with product metadata.

    +

    Depending on the value of argument output_type: +if output_type = "vector", a vector of available products +(being each element an URL and its name the product name), +together with the attributes online (with the index of products available +for download) and lta (with the index of products stored in the Long Term +Archive) in the case availability == "check"; +if output_type = "data.table", a data.table with product metadata +(including the logical field online to allow distinguishing products +available for download and stored in the Long Term Archive).

    Note

    License: GPL 3.0

    diff --git a/docs/reference/s2_mask.html b/docs/reference/s2_mask.html index 9a32e772..48fe6609 100644 --- a/docs/reference/s2_mask.html +++ b/docs/reference/s2_mask.html @@ -95,6 +95,9 @@
  • Function reference +
  • +
  • + News
  • max_cloud

    Integer number (0-100) containing the maximum cloud level of the tiles to be listed (default: no filter).

    availability

    Character argument, determining which products have +to be returned:

      +
    • "online" : only archive names already available for download are returned;

    • +
    • "lta": only archive names stored in the Long Term Archive +(see https://scihub.copernicus.eu/userguide/LongTermArchive) +are returned;

    • +
    • "check": all archive names are returned, checking if they are +available or not for download (see "Value" to know +how to distinguish each other);

    • +
    • "ignore" (default): all archive names are returned, without doing the check +(running the function is faster).

    • +
    output_type
    + + + + + + + + + + + + + + + + + +
    s2_prodlist

    Named character: list of the products to be ordered +(this must be the output of s2_list function). +Alternatively, it can be the path of a JSON file exported by a previous +execution of s2_order, in case the user wants, for any reason, to +resubmit the order.

    export_prodlist

    Logical or character: if TRUE (default), +the list of ordered products is saved in a JSON text file, so to be easily +retrievable at a later stage with safe_is_online or s2_download; +if FALSE, no output files are generated. +It is also possible to pass the path of an existing folder in which the +JSON file will be saved (otherwise, a default path is used).

    delay

    Numeric: time frame (in seconds) to leave between two +consecutive orders. Default is 5 seconds: use a higher value if you +encountered errors (i.e. not all the products were correctly ordered).

    apihub

    Path of the "apihub.txt" file containing credentials +of SciHub account. +If NA (default), the default location inside the package will be used.

    + +

    Value

    + +

    A named vector, containing the selection of s2_prodlist elements +which were ordered. +Moreover, the vector includes the following attributes:

    + +

    Note

    + +

    License: GPL 3.0

    + +

    Examples

    +
    # \donttest{ +# Generate the lists of products +pos <- sf::st_sfc(sf::st_point(c(-57.8815,-51.6954)), crs = 4326) +time_window <- as.Date(c("2018-02-21", "2018-03-20")) +list_safe <- s2_list(spatial_extent = pos, time_interval = time_window) +print(list_safe)
    #> S2B_MSIL1C_20180224T133629_N0206_R038_T21FVC_20180224T194845.SAFE +#> "https://scihub.copernicus.eu/apihub/odata/v1/Products('c88086ac-8087-4adb-866e-baeffec5eb68')/$value" +#> S2B_MSIL1C_20180227T134629_N0206_R081_T21FVC_20180227T200327.SAFE +#> "https://scihub.copernicus.eu/apihub/odata/v1/Products('fa8cba66-7b8b-45aa-9038-9521d7658bad')/$value" +#> S2A_MSIL1C_20180301T133641_N0206_R038_T21FVC_20180301T145524.SAFE +#> "https://scihub.copernicus.eu/apihub/odata/v1/Products('d9a50ba4-8213-421e-92da-e5a487766dc2')/$value" +#> S2B_MSIL1C_20180306T133629_N0206_R038_T21FVC_20180306T183210.SAFE +#> "https://scihub.copernicus.eu/apihub/odata/v1/Products('5ae358f2-8589-493b-bb6d-09df04ec0ff9')/$value" +#> S2A_MSIL1C_20180311T133641_N0206_R038_T21FVC_20180311T163528.SAFE +#> "https://scihub.copernicus.eu/apihub/odata/v1/Products('266e369c-9ab5-486e-83b3-7180b6956616')/$value" +#> S2A_MSIL1C_20180314T134631_N0206_R081_T21FVC_20180314T150724.SAFE +#> "https://scihub.copernicus.eu/apihub/odata/v1/Products('c2cfc404-a877-45cf-8a80-026a7851cff7')/$value" +#> S2B_MSIL1C_20180316T133639_N0206_R038_T21FVC_20180316T145348.SAFE +#> "https://scihub.copernicus.eu/apihub/odata/v1/Products('30729a65-ba41-47e6-ab40-476db732a36e')/$value"
    # (at the time the documentation was written, this list was containing 5 +# archives already available online and 2 stored in the Long Term Archive) + +# Order the products +ordered_prods <- s2_order(list_safe)
    #> [2019-10-11 16:48:20] Check if products are already available for download...
    #> [2019-10-11 16:48:22] 6 Sentinel-2 images are already available and will not be ordered.
    #> [2019-10-11 16:48:22] Ordering 1 Sentinel-2 images stored in the Long Term Archive...
    #> [2019-10-11 16:48:23] 1 of 1 Sentinel-2 images were correctly ordered. You can check at a later time if the ordered products were made available using the command: +#> +#> safe_is_online("/home/lranghetti/.sen2r/lta_orders/lta_20191011_164823.json")
    +# Check in a second time if the product was made available +(order_path <- attr(ordered_prods, "path"))
    #> [1] "/home/lranghetti/.sen2r/lta_orders/lta_20191011_164823.json"
    safe_is_online(order_path)
    #> S2B_MSIL1C_20180224T133629_N0206_R038_T21FVC_20180224T194845.SAFE +#> FALSE
    # } +
    +
    + +
    + + + + + + + + + + + + diff --git a/docs/reference/s2_rgb.html b/docs/reference/s2_rgb.html index efe3567f..443eb7fc 100644 --- a/docs/reference/s2_rgb.html +++ b/docs/reference/s2_rgb.html @@ -90,6 +90,9 @@
  • Function reference +
  • +
  • + News