R/copernicus_download.R

Defines functions copernicus_is_ready copernicus_test copernicus_download

Documented in copernicus_download copernicus_is_ready copernicus_test

#' @title Download data from Copernicus Marine
#'
#' @description
#' Downloads .nc files from the Copernicus Marine catalog. Allows specifying all options of the Python function.
#' Uses stored credentials from options/environment variables if available.
#'
#' @param dataset_id ID of the dataset (exact).
#' @param variables Vector or list of variables to download.
#' @param start_date Download start date (YYYY-MM-DD).
#' @param end_date Download end date (YYYY-MM-DD).
#' @param bbox Vector of 4 values (xmin, xmax, ymin, ymax) for the region.
#' @param depth Vector of 2 values: minimum and maximum depth.
#' @param dataset_version Dataset version.
#' @param output_file Output file path. By default, generates one in tempdir().
#' @param username Copernicus Marine username (optional, will try to get from stored credentials).
#' @param password Copernicus Marine password (optional, will try to get from stored credentials).
#' @param verbose_download Show detailed messages.
#' @param ... Other extra arguments passed to the Python function.
#' @return Absolute path to the downloaded file, or NULL if it fails.
#' @importFrom reticulate r_to_py
#' @export
copernicus_download <- function(dataset_id, variables, start_date, end_date,
                                bbox = c(-180, 179.92, -80, 90),
                                depth = c(0.494, 0.494),
                                dataset_version = "202406",
                                output_file = NULL,
                                username = NULL,
                                password = NULL,
                                verbose_download = TRUE,
                                ...) {

  # Get credentials using the centralized system
  credentials <- copernicus_get_credentials(mask_password = FALSE)

  # Use provided parameters or fall back to stored credentials
  if (is.null(username)) {
    username <- credentials$username
  }
  if (is.null(password)) {
    password <- credentials$password
  }

  # If still no credentials, prompt interactively ONLY in interactive mode
  if (is.null(username) && interactive()) {
    message("No username found in stored credentials.")
    username <- readline(prompt = "Enter your Copernicus Marine username: ")
  }
  if (is.null(password) && interactive()) {
    message("No password found in stored credentials.")
    if (requireNamespace("getPass", quietly = TRUE)) {
      password <- getPass::getPass("Enter your Copernicus Marine password: ")
    } else {
      password <- readline(prompt = "Enter your Copernicus Marine password: ")
    }
  }

  # Validate we have both credentials
  if (is.null(username) || is.null(password) || username == "" || password == "") {
    stop("Username and password are required. Use copernicus_setup_credentials() to store them.")
  }

  # Check that the environment is configured
  copernicus_env <- .copernicus_env()
  if (!exists("cm", envir = copernicus_env)) {
    stop("Copernicus Marine is not configured. Run setup_copernicus() first.")
  }

  cm <- get("cm", envir = copernicus_env)
  variables_py <- reticulate::r_to_py(as.list(variables))

  # Automatically generate output file name if not specified
  if (is.null(output_file)) {
    start_clean <- gsub("-", "", start_date)
    end_clean <- gsub("-", "", end_date)
    output_file <- file.path(tempdir(), paste0("copernicus_", start_clean, "-", end_clean, ".nc"))
  } else {
    output_file <- file.path(tempdir(), basename(output_file))
  }

  # Create output directory if it doesn't exist
  output_dir <- dirname(output_file)
  if (!dir.exists(output_dir)) {
    dir.create(output_dir, recursive = TRUE)
  }

  if (verbose_download) {
    message("Downloading: ", dataset_id)
    message("Period: ", start_date, " to ", end_date)
    message("Variables: ", paste(variables, collapse = ", "))
    message("Region: lon[", bbox[1], ",", bbox[2], "] lat[", bbox[3], ",", bbox[4], "]")
    if (!all(depth == c(0.494, 0.494))) {
      message("Depth: ", depth[1], " to ", depth[2], "m")
    }
    message("File: ", output_file)
    message("Starting download...")
  }

  start_time <- Sys.time()

  tryCatch({
    args_py <- list(
      dataset_id = dataset_id,
      dataset_version = dataset_version,
      variables = variables_py,
      start_datetime = paste0(start_date, "T00:00:00"),
      end_datetime = paste0(end_date, "T00:00:00"),
      minimum_longitude = bbox[1],
      maximum_longitude = bbox[2],
      minimum_latitude = bbox[3],
      maximum_latitude = bbox[4],
      minimum_depth = depth[1],
      maximum_depth = depth[2],
      coordinates_selection_method = "strict-inside",
      output_filename = output_file,
      username = username,
      password = password
    )

    # Add additional arguments
    dots <- list(...)
    if (length(dots) > 0) args_py <- c(args_py, dots)

    # Execute download
    result <- do.call(cm$subset, args_py)
    end_time <- Sys.time()

    # Check if download was successful
    if (file.exists(output_file)) {
      size_mb <- round(file.size(output_file) / 1024 / 1024, 2)
      time_mins <- round(difftime(end_time, start_time, units = "mins"), 2)

      if (verbose_download) {
        message("Download successful!")
        message("Size: ", size_mb, " MB")
        message("Time: ", time_mins, " minutes")
        message("Location: ", normalizePath(output_file))
      }

      return(normalizePath(output_file))
    } else {
      warning("File was not created")
      return(NULL)
    }

  }, error = function(e) {
    warning("Download error: ", e$message)

    # Specific help messages
    if (grepl("date|time", e$message, ignore.case = TRUE)) {
      message("Dates may not be available. Check:")
      message("   \u2022 That the dates are in YYYY-MM-DD format")
      message("   \u2022 That they fall within the dataset's temporal range")
      message("   \u2022 Try more recent dates")
    } else if (grepl("variable", e$message, ignore.case = TRUE)) {
      message("Variable issue. Check:")
      message("   \u2022 That the variables exist in this dataset")
      message("   \u2022 Use copernicus_describe() to see available variables")
    } else if (grepl("credential|auth|login", e$message, ignore.case = TRUE)) {
      message("Authentication issue. Check your username/password.")
    } else if (grepl("longitude|latitude|bbox|coordinates", e$message, ignore.case = TRUE)) {
      message("Coordinate issue. Check:")
      message("   \u2022 That bbox is in [xmin, xmax, ymin, ymax] format")
      message("   \u2022 That the coordinates are within the dataset's range")
      message("   \u2022 Longitude: -180 to 180, Latitude: -90 to 90")
    } else if (grepl("depth", e$message, ignore.case = TRUE)) {
      message("Depth issue. Check:")
      message("   \u2022 That the dataset has depth data")
      message("   \u2022 That the values are within the available range")
    } else if (grepl("network|connection|timeout", e$message, ignore.case = TRUE)) {
      message("Connection issue. Try:")
      message("   \u2022 Checking your internet connection")
      message("   \u2022 Retrying the download later")
      message("   \u2022 Reducing the download size")
    }

    return(NULL)
  })
}



#' @title Test Copernicus integration
#'
#' @description
#' Performs a small test download to validate that the whole system works.
#' Uses stored credentials if available. Downloads to tempdir().
#'
#' @param username Copernicus Marine username (optional). Will try to get from stored credentials first.
#' @param password Copernicus Marine password (optional). Will try to get from stored credentials first.
#' @return TRUE if the test was successful.
#' @export
copernicus_test <- function(username = NULL, password = NULL) {
  message("Testing download from Copernicus Marine...")

  # Get credentials using the centralized system
  credentials <- copernicus_get_credentials(mask_password = FALSE)

  # Use provided parameters or fall back to stored credentials
  if (is.null(username)) {
    username <- credentials$username
  }
  if (is.null(password)) {
    password <- credentials$password
  }

  # If still no credentials, prompt interactively ONLY in interactive mode
  if (is.null(username) && interactive()) {
    message("No username found in stored credentials.")
    username <- readline(prompt = "Enter your Copernicus Marine username: ")
  }
  if (is.null(password) && interactive()) {
    message("No password found in stored credentials.")
    if (requireNamespace("getPass", quietly = TRUE)) {
      password <- getPass::getPass("Enter your Copernicus Marine password: ")
    } else {
      password <- readline(prompt = "Enter your Copernicus Marine password: ")
    }
  }

  # Validate we have both credentials
  if (is.null(username) || is.null(password) || username == "" || password == "") {
    message("Username and password are required.")
    message("Use copernicus_setup_credentials() to store them.")
    return(FALSE)
  }

  # Use date from 3 days ago for higher chance of success
  test_date <- as.character(Sys.Date() - 3)

  # Always use tempdir() for test files
  test_file <- file.path(tempdir(), "test_copernicus_download.nc")

  file <- copernicus_download(
    dataset_id = "cmems_mod_glo_phy_anfc_0.083deg_P1D-m",
    variables = "zos",
    start_date = test_date,
    end_date = test_date,
    bbox = c(0, 1, 40, 41),
    output_file = test_file,
    username = username,
    password = password,
    verbose_download = FALSE
  )

  if (!is.null(file) && file.exists(file)) {
    file_size_kb <- round(file.size(file) / 1024, 1)
    message("Test download successful!")
    message("File created: ", basename(file), " (", file_size_kb, " KB)")
    message("Cleaning up test file...")
    file.remove(file)
    return(TRUE)
  } else {
    message("Error in test download")
    message("Check your configuration with copernicus_is_ready()")
    return(FALSE)
  }
}

#' @title Check if Copernicus Marine Python module is ready
#'
#' @description
#' Checks if the Python module is properly loaded and credentials are configured
#' to use Copernicus Marine. Returns TRUE if everything is ready.
#'
#' @param verbose Show detailed status information.
#' @return TRUE if the Python module is loaded and credentials are available.
#' @export
copernicus_is_ready <- function(verbose = TRUE) {

  copernicus_env <- .copernicus_env()

  # Check Python module
  module_ok <- exists("cm", envir = copernicus_env) && !is.null(get("cm", envir = copernicus_env))

  # Check credentials
  credentials <- copernicus_get_credentials(mask_password = FALSE)
  credentials_ok <- !is.null(credentials$username) && !is.null(credentials$password)

  if (verbose) {
    message("Checking Copernicus Marine environment:\n")

    # Python module status
    if (module_ok) {
      message("Python module copernicusmarine: OK")
    } else {
      message("Python module copernicusmarine: NOT AVAILABLE")
      message("Run setup_copernicus() to configure")
    }

    # Credentials status
    if (credentials_ok) {
      message("Credentials configured for user: ", credentials$username)
    } else {
      message("Credentials: NOT CONFIGURED")
      message("Run copernicus_setup_credentials() to configure")
    }

    message("")

    if (module_ok && credentials_ok) {
      message("Ready to use Copernicus Marine!")
      message("Run copernicus_test() for a test download")
    } else {
      message("Setup incomplete:")
      if (!module_ok) {
        message("1. Run: setup_copernicus()")
      }
      if (!credentials_ok) {
        message("2. Run: copernicus_setup_credentials('username', 'password')")
      }
    }
  }

  return(module_ok && credentials_ok)
}

Try the copernicusR package in your browser

Any scripts or data that you put into this service are public.

copernicusR documentation built on Aug. 8, 2025, 7:06 p.m.