R/ee_Initialize.R

Defines functions ee_get_assethome create_table add_extra_space ee_source_python ee_get_earthengine_path ee_sessioninfo ee_user_info ee_users ee_create_credentials_gcs ee_create_credentials_drive ee_create_credentials_earthengine ee_Initialize

Documented in ee_get_assethome ee_get_earthengine_path ee_Initialize ee_user_info ee_users

#' Authenticate and Initialize Earth Engine
#'
#' Authorize rgee to manage Earth Engine resources, Google
#' Drive, and Google Cloud Storage. The \code{ee_initialize()} via
#' web-browser will ask to sign in to your Google account and
#' allows you to grant permission to manage resources. This function is
#' a wrapper around `rgee::ee$Initialize()`.
#'
#' @param email Character (optional, e.g. `data.colec.fbf@gmail.com`). The email
#' name is used as a folder inside the path `rgee::ee_get_earthengine_path()`.
#' This enable a multi-user support allowing to target a specific
#' Google identity.
#' @param drive Logical (optional). If TRUE, the drive credential
#' will be cached in the path `rgee::ee_get_earthengine_path()`.
#' @param gcs logical. If TRUE, the Google Cloud Storage
#' credential will be cached in the path `rgee::ee_get_earthengine_path()`.
#' @param quiet logical. Suppress info messages.
#' @importFrom utils read.table browseURL write.table packageVersion
#' @importFrom reticulate import_from_path import install_miniconda py_available
#' @importFrom getPass getPass
#' @importFrom cli symbol rule
#' @importFrom crayon blue green black red bold white
#' @details
#' \code{ee_Initialize(...)} can also manage Google drive and Google
#' Cloud Storage resources using the R packages googledrive and
#' googlecloudStorageR, respectively. By default, rgee does not require
#' them, these are only necessary for exporting and importing tasks.
#' All user credentials are saved in the directory
#' \code{~/.config/earthengine/}, if a user does not specified the
#' the email argument all user credentials will be saved in a subdirectory
#' called \code{ndef}.
#' @seealso remove credential function: \cr
#' \link[rgee]{ee_remove_credentials}
#' @examples
#' library(rgee)
#' ee_reattach() # reattach ee as a reserved word
#'
#' # Simple init
#' ee_Initialize()
#'
#' # Advanced init
#' ee_Initialize(
#'   email = "data.colec.fbf@gmail.com",
#'   drive = TRUE,
#'   gcs = TRUE
#' )
#'
#' ee_user_info()
#'
#' @export
ee_Initialize <- function(email = NULL,
                          drive = FALSE,
                          gcs = FALSE,
                          quiet = FALSE) {
  if (isFALSE(py_available(initialize = TRUE))) {
    init_message <- paste0(
      "No available Python version found in your system",
      ". Would you like to install Miniconda?",
      collapse = ""
    )
    switch(
      EXPR = menu(
        choices = c('yes','no'),
        title = init_message
      ) + 1,
      stop('rgee needs a Python environment in the system\n'),
      install_miniconda(),
      stop('rgee needs a Python environment in the system\n')
    )
    return(0)
  }
  if (exists('ee')) {
    if (is.null(ee$computedobject)) {
      stop('rgee does not found the Earth Engine Python API.',
           ' Run rgee::ee_reattach() before continuing.')
    }
  } else {
    stop('rgee does not found Python modules.',
         ' Run rgee::ee_reattach() before continuing.')
  }

  ee_current_version <- system.file("python/ee_utils.py", package = "rgee")
  ee_utils <- ee_source_python(ee_current_version)
  earthengine_version <- ee_py_to_r(ee_utils$ee_getversion())

  if (!quiet) {
    cat(
      rule(
        left = bold("rgee", packageVersion("rgee")),
        right = paste0("earthengine-api ", earthengine_version)
      ), "\n"
    )
  }

  # is earthengine-api greater than 0.1.215?
  if (as.numeric(gsub("\\.","",earthengine_version)) < 01215) {
    stop(
      "Update local installations to v0.1.215 or greater. ",
      "Earlier versions are not compatible with recent ",
      "changes to the Earth Engine backend."
    )
  }

  # 1. simple checking
  if (is.null(email)) {
    email <- "ndef"
  }

  if (isFALSE(quiet)) {
    if (email == "ndef") {
      cat(
        "", green(symbol$tick),
        blue("email:"),
        green("not_defined\n")
      )
    } else {
      cat(
        "", green(symbol$tick),
        blue("email:"),
        green(email), "\n"
      )
    }
  }

  # create a user's folder
  email_clean <- gsub("@gmail.com", "", email)
  ee_path <- path.expand("~/.config/earthengine")
  ee_path_user <- sprintf("%s/%s", ee_path, email_clean)
  dir.create(ee_path_user, showWarnings = FALSE, recursive = TRUE)

  # Loading all the credentials: earthengine, drive and GCS.
  drive_credentials <- NA
  gcs_credentials <- NA

  if (drive) {
    if (!quiet) {
      cat(
        "",
        green(symbol$tick),
        blue("Google Drive credentials:")
      )
    }
    drive_credentials <- ee_create_credentials_drive(email)
    if (!quiet) {
      cat(
        "\r",
        green(symbol$tick),
        blue("Google Drive credentials:"),
        # drive_credentials,
        green(" FOUND\n")
      )
    }
  }

  if (gcs) {
    if (!quiet) {
      cat(
        "",
        green(symbol$tick),
        blue("GCS credentials:")
      )
    }
    gcs_credentials <- ee_create_credentials_gcs(email)
    if (!quiet) {
      cat(
        "\r",
        green(symbol$tick),
        blue("GCS credentials:"),
        # gcs_credentials,
        green(" FOUND\n")
      )
    }
  }
  ## rgee session file
  options(rgee.gcs.auth = gcs_credentials)
  options(rgee.manage.setIamPolicy = list(bindings = list(
    list(
      role = "roles/owner",
      members = paste0("user:", email)
    ),
    list(
      role = "roles/editor",
      members = NULL
    ),
    list(
      role = "roles/viewer",
      members = NULL
    )
  )))
  if (!quiet) {
    cat(
      "", green(symbol$tick),
      blue("Initializing Google Earth Engine:")
    )
  }

  ee_create_credentials_earthengine(email_clean)
  ee$Initialize()

  if (!quiet) {
    cat(
      "\r",
      green(symbol$tick),
      blue("Initializing Google Earth Engine:"),
      green(" DONE!\n")
    )
  }

  ee_user <- ee_remove_project_chr(ee$data$getAssetRoots()[[1]]$id)
  options(rgee.ee_user = ee_user)
  ee_sessioninfo(
    email = email_clean,
    user = ee_user,
    drive_cre = drive_credentials,
    gcs_cre = gcs_credentials
  )

  if (!quiet) {
    cat("\r", green(symbol$tick), blue("Earth Engine user:"),
        green(bold(ee_user)), "\n")
    cat(rule(), "\n")
  }
  # ee_check_rgee_python_packages(quiet = TRUE)
  invisible(TRUE)
}

#' Authorize rgee to view and manage your Earth Engine account.
#' This is a three-step function:
#' \itemize {
#' \item First get the full path name of the Earth Engine credentials
#' considering the email address.
#' \item Second, use the file.copy function to set up the
#' "credentials" file, so that the Earth Engine Python API can read it.
#' \item Finally, if the file.copy fails at copy it, the credentials
#' will download from Internet, you will be directed to a web browser.
#' Sign in to your Google account to be granted rgee
#' permission to operate on your behalf with Google Earth Engine.
#' These user credentials are cached in a folder below your
#' home directory, `rgee::ee_get_earthengine_path()`, from
#' where they can be automatically refreshed, as necessary.
#' }
#' @noRd
ee_create_credentials_earthengine <- function(email_clean) {
  oauth_func_path <- system.file("python/ee_utils.py", package = "rgee")
  utils_py <- ee_source_python(oauth_func_path)

  # first step
  ee_path <- path.expand("~/.config/earthengine")
  main_ee_credential <- sprintf("%s/credentials", ee_path)
  user_ee_credential <- sprintf(
    "%s/%s/credentials",
    ee_path,
    email_clean
  )
  # second step
  path_condition <- file.exists(user_ee_credential)
  if (isTRUE(path_condition)) {
    path_condition <- file.copy(
      from = user_ee_credential,
      to = main_ee_credential,
      overwrite = TRUE
    )
  } else {
    oauth_codes <- ee_py_to_r(utils_py$create_codes())
    code_verifier <- oauth_codes[[1]]
    code_challenge <- oauth_codes[[2]]
    earthengine_auth <- ee$oauth$get_authorization_url(code_challenge)
    browseURL(earthengine_auth)
    auth_code <- getPass("Enter Earth Engine Authentication: ")
    token <- ee$oauth$request_token(auth_code, code_verifier)
    credential <- sprintf('{"refresh_token":"%s"}', token)
    write(credential, main_ee_credential)
    write(credential, user_ee_credential)
  }
}

#' Create credentials - Google Drive
#' @noRd
ee_create_credentials_drive <- function(email) {
  if (!requireNamespace("googledrive", quietly = TRUE)) {
    stop("The googledrive package is not installed. ",
      'Try install.packages("googledrive")',
      call. = FALSE
    )
  }
  # setting drive folder
  ee_path <- path.expand("~/.config/earthengine")
  email_clean <- gsub("@gmail.com", "", email)
  ee_path_user <- sprintf("%s/%s", ee_path, email_clean)
  # drive_credentials
  repeat {
    full_credentials <- list.files(path = ee_path_user, full.names = TRUE)
    drive_condition <- grepl("@gmail.com", full_credentials)
    if (!any(drive_condition)) {
      suppressMessages(
        googledrive::drive_auth(
          email = NULL,
          cache = ee_path_user
        )
      )
    } else {
      drive_credentials <- full_credentials[drive_condition]
      email <- sub("^[^_]*_", "", drive_credentials)
      suppressMessages(
        googledrive::drive_auth(
          email = email,
          cache = ee_path_user
        )
      )
      break
    }
  }
  # from user folder to EE folder
  unlink(list.files(ee_path, "@gmail.com", full.names = TRUE))
  file.copy(
    from = drive_credentials,
    to = sprintf("%s/%s", ee_path, basename(drive_credentials)),
    overwrite = TRUE
  )
  invisible(drive_credentials)
}


#' Authorize Google Cloud Storage
#'
#' Authorize Google Cloud Storage to view and manage your gcs files.
#'
#' @details
#' *.json is the authentication file you have downloaded
#' from your Google Project
#' (https://console.cloud.google.com/apis/credentials/serviceaccountkey).
#' Is necessary to save it (manually) inside the folder ~/.R/earthengine/USER/.
#' @noRd
ee_create_credentials_gcs <- function(email) {
  if (!requireNamespace("googleCloudStorageR", quietly = TRUE)) {
    stop("The googleCloudStorageR package is not installed. Try",
      ' install.packages("googleCloudStorageR")',
      call. = FALSE
    )
  }
  # setting gcs folder
  ee_path <- path.expand("~/.config/earthengine")
  email_clean <- gsub("@gmail.com", "", email)
  ee_path_user <- sprintf("%s/%s", ee_path, email_clean)
  # gcs_credentials
  full_credentials <- list.files(path = ee_path_user, full.names = TRUE)
  gcs_condition <- grepl(".json", full_credentials)
  if (!any(gcs_condition)) {
    stop(
      "Unable to find GCS_AUTH_FILE.json in ", ee_path_user, ". \n",
      "First download and save the Google Project JSON file in ",
      "the path mentioned before.\n",
      "A compressible tutorial to obtain the GCS_AUTH_FILE.json is ",
      "available here: \n",
      "- https://github.com/csaybar/GCS_AUTH_FILE.json\n",
      "- https://console.cloud.google.com/apis/credentials/serviceaccountkey ",
      "(download link)\n"
    )
  } else {
    gcs_credentials <- full_credentials[gcs_condition]
    googleCloudStorageR::gcs_auth(gcs_credentials)
  }
  unlink(list.files(ee_path, ".json", full.names = TRUE))
  file.copy(
    from = gcs_credentials,
    to = sprintf("%s/%s", ee_path, basename(gcs_credentials)),
    overwrite = TRUE
  )
  invisible(gcs_credentials)
}

#' Display credentials of all users as a table
#'
#' Display Earth Engine, Google Drive, and Google Cloud Storage Credentials as
#' a table.
#' @examples
#' \dontrun{
#' library(rgee)
#' ee_users()
#' }
#' @export
ee_users <- function() {
  #space among columns
  wsc <- "     "
  title  <- c('user', ' EE', ' GD', ' GCS')

  # get all dirfiles
  ee_path <- path.expand("~/.config/earthengine") %>%
    list.dirs(full.names = FALSE) %>%
    '['(-1)

  if (length(ee_path) == 0) {
    stop('does not exist active users',
         ', run rgee::ee_Initialize() to fixed.')
  }

  #define space in the first column
  max_char <- max(nchar(ee_path))
  add_space <- max_char - nchar(ee_path)
  title[1] <- add_extra_space(name = title[1],
                              space = max_char - nchar(title[1]))

  cat("",bold(paste0(title, collapse = wsc)),"\n")
  users <- add_extra_space(ee_path, add_space)
  for (user in users) {
    create_table(user,wsc)
  }
  cat("\n")
  invisible(TRUE)
}

#' Display credentials and general info of the initialized user
#'
#' @examples
#' \dontrun{
#' library(rgee)
#' ee_reattach() # reattach ee as a reserved word
#' ee_Initialize()
#' ee_user_info()
#' }
#' @export
ee_user_info <- function() {
  user_session <- ee_get_earthengine_path()
  user_session_list <- list.files(user_session,full.names = TRUE)
  user <- ee$data$getAssetRoots()[[1]]$id

  cat(rule(right = bold(paste0("Earth Engine user info"))))
  # python version
  py_used <- py_discover_config()$python
  cat(blue$bold("\nReticulate python version:"))
  cat("\n - ", py_used)

  # asset home
  asset_home <- ee_remove_project_chr(user)
  cat(blue$bold('\nEarth Engine Asset Home:'))
  cat("\n - ", asset_home)

  # credentials directory path
  cat(blue$bold('\nCredentials Directory Path:'))
  cat("\n - ", user_session)

  # google drive
  gd <- user_session_list[grepl("@gmail.com", user_session_list)]
  cat(blue$bold('\nGoogle Drive Credentials:'))
  cat("\n - ", basename(gd))
  email_drive <- sub("[^_]+_(.*)@.*", "\\1", basename(gd))

  # google cloud storage
  gcs <- user_session_list[grepl(".json", user_session_list)]
  cat(blue$bold('\nGoogle Cloud Storage Credentials:'))
  cat("\n - ",basename(gcs))

  cat("\n", rule())

  ee_user <- ee_exist_credentials()
  if (isFALSE(grepl(email_drive, ee_user$email)) & ee_user$email != "ndef") {
    message(
      "\nNOTE: Google Drive credential does not match with your Google",
      " Earth Engine credentials. All functions which depend on Google",
      " Drive will not work (e.g. ee_image_to_drive)."
    )
  }
  ee_check_rgee_python_packages(quiet = TRUE)
  invisible(TRUE)
}

#' Create session info of the last init inside the
#' folder ~/.config/earthengine/
#' @noRd
ee_sessioninfo <- function(email = NULL,
                           user = NULL,
                           drive_cre = NULL,
                           gcs_cre = NULL) {
  sessioninfo <- sprintf(
    "%s/rgee_sessioninfo.txt",
    path.expand("~/.config/earthengine")
  )
  df <- data.frame(
    email = email, user = user, drive_cre = drive_cre, gcs_cre = gcs_cre,
    stringsAsFactors = FALSE
  )
  write.table(df, sessioninfo, row.names = FALSE)
}


#' Get the path where the credentials are stored by rgee
#' @export
ee_get_earthengine_path <- function() {
  ee_path <- path.expand("~/.config/earthengine")
  sessioninfo <- sprintf(
    "%s/rgee_sessioninfo.txt",
    path.expand("~/.config/earthengine")
  )
  if (file.exists(sessioninfo)) {
    user <- read.table(sessioninfo,
      header = TRUE,
      stringsAsFactors = FALSE
    )[[1]]
    if (is.na(user)) {
      stop("rgee_sessioninfo.txt malformed")
    }
  } else {
    stop(
      "rgee_sessioninfo.txt does not exist, ",
      "run rgee::ee_Initialize() to fixed."
    )
  }
  return(sprintf("%s/%s/", ee_path, user))
}

#' Read and evaluate a python script
#' @noRd
ee_source_python <- function(oauth_func_path) {
  module_name <- gsub("\\.py$", "", basename(oauth_func_path))
  module_path <- dirname(oauth_func_path)
  import_from_path(module_name, path = module_path, convert = FALSE)
}

#' Function used in ee_user
#'
#' Add extra space to usernames to form a nice table
#' @noRd
add_extra_space <- function(name, space) {
  iter <- length(space)
  result <- rep(NA,iter)
  for (z in seq_len(iter)) {
    add_space <- paste0(rep(" ",space[z]), collapse = "")
    result[z] <- paste0(name[z], add_space)
  }
  result
}

#' Function used in ee_user
#'
#' Search if credentials exist and display
#' it as tick and crosses.
#'
#' @noRd
create_table <- function(user, wsc) {
  ee_path <- path.expand("~/.config/earthengine")
  user_clean <- gsub(" ", "", user, fixed = TRUE)
  credentials <- list.files(sprintf("%s/%s",ee_path,user_clean))

  #google drive
  if (any(grepl("@gmail.com",credentials))) {
    gmail_symbol <- green(symbol$tick)
  } else {
    gmail_symbol <- red(symbol$cross)
  }

  #GCS
  if (any(grepl(".json",credentials))) {
    gcs_symbol <- green(symbol$tick)
  } else {
    gcs_symbol <- red(symbol$cross)
  }

  #Earth Engine
  if (any(grepl("credentials",credentials))) {
    ee_symbol <- green(symbol$tick)
  } else {
    ee_symbol <- red(symbol$cross)
  }

  cat("\n",
      user,
      wsc,
      gmail_symbol,
      wsc,
      gcs_symbol,
      wsc,
      ee_symbol
    )
}

#' Get the Asset home name
#' @examples
#' \dontrun{
#' ee_reattach()
#' ee_Initialize()
#' ee_get_assethome()
#' }
#' @export
ee_get_assethome <- function() {
  options('rgee.ee_user')[[1]]
}
ryali93/rgee documentation built on May 13, 2020, 4:34 a.m.