R/ee_help.R

Defines functions rgeeExtracheckmans ee_module_help ee_help_create_arg ee_real_name ee_function_docs ee_get_lhs ee_css_h3_rstudio ee_css_h3_simple ee_css_h2_rstudio ee_css_h2_simple ee_css_pre_simple ee_html_examples_rstudio ee_html_returns_rstudio ee_html_examples_simple ee_html_returns_simple ee_html_details_rstudio ee_html_details_simple ee_html_arguments_rstudio ee_html_arguments_simple ee_html_usage_rstudio ee_html_usage_simple ee_html_description_rstudio ee_html_description_simple ee_html_title_rstudio ee_html_title_simple ee_html_head_rstudio ee_html_head_simple ee_help

Documented in ee_help

#' Documentation for Earth Engine Objects
#' @param eeobject Earth Engine Object to print documentation.
#' @param browser Logical. Display documentation in the browser.
#' @importFrom reticulate py_function_docs
#' @importFrom utils tail help
#' @family helper functions
#' @return No return value, called for displaying Earth Engine documentation.
#' @examples
#' \dontrun{
#' library(rgee)
#' ee_Initialize()
#'
#' ee$Image()$geometry()$centroid %>% ee_help()
#' ee$Image()$geometry() %>% ee_help()
#' ee$Geometry$Rectangle(c(-110.8, 44.6, -110.6, 44.7)) %>% ee_help()
#' ee$Image %>% ee_help()
#' ee$Image %>% ee_help(browser = TRUE)
#' }
#' @export
ee_help <- function(eeobject, browser = FALSE) {
  if (!is.character(eeobject)) {
    eeobject_name <- deparse(substitute(eeobject))
    eeobject_name <- gsub(".*\\$(.*)$", "\\1", eeobject_name)
  } else {
    eeobject_name <- eeobject
    eeobject_name <- gsub(".*\\$(.*)$", "\\1", eeobject_name)
  }

  # If the function name exist in rgeeExtra, then stop the process.
  image_mans <- rgeeExtracheckmans(eeobject_name, "image")
  if (!is.na(image_mans[1])) {
    return(image_mans)
  }

  ic_mans <- rgeeExtracheckmans(eeobject_name, "ic")
  if (!is.na(ic_mans[1])) {
    return(ic_mans)
  }

  #obs : simple earth engine objects like ee$Number will return NULL
  eequery_scope <- try(
    expr = unlist(jsonlite::parse_json(eeobject$serialize())$scope),
    silent = TRUE
  )

  # If eeobject is an Earth Engine object get the last function
  if (!inherits(eequery_scope, 'try-error') & !is.null(eequery_scope)) {
    search_funnames <- grepl("functionName", names(eequery_scope))
    ee_functions <- eequery_scope[search_funnames]
    fun_name <- paste0("ee$",gsub("\\.","$",tail(ee_functions,1)))
  } else {
    if (is.character(eeobject)) {
      fun_name <- eeobject
      # fun_name has parenthesis
      fun_name_d <- strsplit(fun_name, "\\$")[[1]]
      exist_parenthesis <- grepl(
        pattern = "(",
        x = fun_name_d[length(fun_name_d)],
        fixed = TRUE
      )
    } else {
      exist_parenthesis <- FALSE
      wrap_lhs <- function(x) gsub("rgee", "", ee_get_lhs())
      fun_name <- wrap_lhs(eeobject)
      if (length(fun_name) == 0) {
        fun_name <- deparse(substitute(eeobject))
      }
    }

    if (is.null(eequery_scope) | exist_parenthesis) {
      components <- strsplit(fun_name, "\\$")[[1]]
      topic <- components[[length(components)]]
      source <- paste(components[1:(length(components) - 1)],
                      collapse = "$")
      # The name is a base function?
      is_a_basefunction <- tryCatch(
        expr = {eval(parse(text = sprintf("base::%s", fun_name))); TRUE},
        error = function(e) FALSE
      )
      if (isTRUE(is_a_basefunction)) {
        stop(
          "'", fun_name, "' is not subsettable. Are you using a ",
          "function name that matches the names of the R base",
          " library?. If 'base::", fun_name, "' exists ee_help will not work."
        )
      }
      if (topic == source) {
        fun_name <- topic
      } else {
        # Remove just the last parenthesis
        extract_parenthesis_text <- gregexpr("(?=\\().*?(?<=\\))",
                                             topic,
                                             perl = TRUE)
        parenthesis_text <- regmatches(topic, extract_parenthesis_text)[[1]]
        to_display <- gsub(parenthesis_text, "", topic, fixed = TRUE)
        to_display <- gsub("\\(|\\)", "", to_display)
        fun_name <- paste(source,to_display,sep = "$")
      }
    }
  }

  if (fun_name == "ee") {
    doc_to_display <- ee_module_help()
  } else {
    doc_to_display <- tryCatch(
      expr = fun_name %>%
        paste(collapse = '') %>%
        ee_function_docs,
      error = function(e) ee_real_name(fun_name) %>%
        paste(collapse = '') %>%
        ee_function_docs
    )
  }

  # Creating html to display
  temp_file <- sprintf("%s/ee_help.html", tempdir())
  file.create(temp_file)
  fileConn <- file(temp_file)

  # Are you in Rstudio?
  if (.Platform$GUI == "RStudio" & isFALSE(browser)) {
    # check rstudioapi
    ee_check_packages("ee_help", "rstudioapi")
    writeLines(
      text = c(
        ee_html_head_rstudio(doc_to_display$qualified_name),
        ee_html_title_rstudio(doc_to_display$title),
        ee_html_description_rstudio(doc_to_display$description),
        ee_html_usage_rstudio(doc_to_display),
        ee_html_arguments_rstudio(doc_to_display$parameters),
        ee_html_details_rstudio(doc_to_display$details),
        ee_html_returns_rstudio(doc_to_display$returns),
        ee_html_examples_rstudio(doc_to_display$Examples)
      ),
      con = fileConn
    )
    on.exit(close(fileConn), add = TRUE)
    rstudioapi::viewer(temp_file)
  } else {
    writeLines(
      text = c(
        ee_html_head_simple(doc_to_display$qualified_name),
        ee_html_title_simple(doc_to_display$description),
        ee_html_description_simple(doc_to_display$description),
        ee_html_usage_simple(doc_to_display),
        ee_html_arguments_simple(doc_to_display$parameters),
        ee_html_details_simple(doc_to_display$details),
        ee_html_returns_simple(doc_to_display$returns)
      ),
      con = fileConn
    )
    on.exit(close(fileConn), add = TRUE)
    browseURL(temp_file)
  }

  invisible(temp_file)
}


#' Create init table - R Documentation Simple
#' @noRd
ee_html_head_simple <- function(fun_name) {
  message <- '<table width="100%" summary="page for fun_rgee {rgee}">
     <tbody>
     <tr>
       <td align="left"> fun_rgee {rgee}</td>
       <td align="right">R Documentation</td>
     </tr>
     </tbody>
   </table>'
  message <- gsub("\n", "", message)
  gsub("fun_rgee", fun_name, message)
}

#' Create init table - R Documentation Rstudio
#' @noRd
ee_html_head_rstudio <- function(fun_name) {
  td_style <- "font-family: sans-serif;font-size: 10pt;"
  message <- '<table width="100%" summary="page for fun_rgee {rgee}">
     <tbody>
     <tr>
       <td align="left" style="td_style"> fun_rgee {rgee} </td>
       <td align="right" style="td_style"> R Documentation </td>
     </tr>
     </tbody>
   </table>'
  message <- gsub("\n", "", message)
  message_norgee <- gsub("fun_rgee", fun_name, message)
  gsub("td_style", td_style, message_norgee)
}


#' Create main title - R Documentation Simple
#' @noRd
ee_html_title_simple <- function(title) {
  sprintf('<h2 style="%s">%s</h2>', ee_css_h2_simple(), title)
}

#' Create main title - R Documentation Rstudio
#' @noRd
ee_html_title_rstudio <- function(title) {
  sprintf('<h2 style="%s">%s</h2>', ee_css_h2_rstudio(), title)
}

#' Create description - R Documentation Simple
#' @noRd
ee_html_description_simple <- function(descrp) {
  api_ref <- "https://developers.google.com/earth-engine/api_docs/"
  gee_message <- sprintf(
    ' Documentation obtained from this <a href="%s">link</a>.',
    api_ref
  )
  sprintf(
    '<h3 style="%s">Description</h3>\n\n<p>%s%s<p>',
    ee_css_h3_simple(),
    descrp,
    gee_message
  )
}

#' Create description - R Documentation Simple
#' @noRd
ee_html_description_rstudio <- function(descrp) {
  api_ref <- "https://developers.google.com/earth-engine/api_docs/"
  gee_message <- sprintf(
    ' Documentation obtained from this <a href="%s">link</a>.',
    api_ref
  )
  p_style <- "font-family: sans-serif; font-size: 10pt;"
  sprintf(
    '<h3 style="%s">Description</h3><p style="%s">%s%s<p>',
    ee_css_h3_rstudio(),
    p_style,
    descrp,
    gee_message
  )
}

#' Create usage - R Documentation Simple
#' @noRd
ee_html_usage_simple <- function(pydoc) {
  name_strong <- sprintf("<strong>%s</strong>", pydoc$qualified_name)
  if (is.null(pydoc$signature)) {
    return("")
  }
  extract_parenthesis_text <- gregexpr("(?=\\().*?(?<=\\))",
    pydoc$signature,
    perl = TRUE
  )
  if (length(extract_parenthesis_text) == 0) {
    return("")
  }
  to_display <- name_strong %>%
    paste0(regmatches(pydoc$signature, extract_parenthesis_text)[[1]])
  sprintf(
    '<h3 style="%s">Usage</h3><p style = "%s">%s</p>',
    ee_css_h3_simple(),
    ee_css_pre_simple(),
    to_display
  )
}

#' Create usage - R Documentation Simple
#' @noRd
ee_html_usage_rstudio <- function(pydoc) {
  name_strong <- sprintf("<strong>%s</strong>", pydoc$qualified_name)
  if (is.null(pydoc$signature)) {
    return("")
  }
  extract_parenthesis_text <- gregexpr("(?=\\().*?(?<=\\))",
    pydoc$signature,
    perl = TRUE
  )
  if (length(extract_parenthesis_text) == 0) {
    return("")
  }
  to_display <- name_strong %>%
    paste0(regmatches(pydoc$signature, extract_parenthesis_text)[[1]])
  sprintf('<h3 style="%s">Usage</h3><p style = "%s">%s</p>',
          ee_css_h3_rstudio(),
          ee_css_pre_simple(),
          to_display)
}

#' Create arguments - R Documentation Simple
#' @noRd
ee_html_arguments_simple <- function(parameters) {
  if (is.null(parameters)) {
    return("")
  }
  if (length(parameters) == 0) {
    return("")
  }
  p_style <- paste0("display: block;margin-block-start: 1em;",
                    "margin-block-end: 1em;margin-inline-start: 0px;",
                    "margin-inline-end: 0px;")
  table_style <- paste0("display: table;border-collapse: separate;",
                        "border-spacing: 2px;border-color: grey;")
  td_style <- "display: table-cell;vertical-align: inherit;"
  table_head <- sprintf('<table summary="R argblock" style="%s">', table_style)
  table_end <- "</table>"
  rows <- ""
  for (index in seq_along(parameters)) {
    parameter <- parameters[index]
    param_name <- names(parameter)
    row_message <- paste0(
      sprintf('<tr valign="top"><td style="%s"><code>%s</code></td>',
              td_style,
              param_name),
      sprintf('<td style="%s"><p style="%s">%s</p></td></tr>',
              td_style,
              p_style,
              parameter)
    )
    rows <- paste0(rows, row_message)
  }
  arguments_table <- paste0(table_head, rows, table_end)
  sprintf('<h3 style="%s">Arguments</h3>%s',
          ee_css_h3_simple(),
          arguments_table)
}

#' Create arguments - R Documentation Simple
#' @noRd
ee_html_arguments_rstudio <- function(parameters) {
  if (is.null(parameters)) {
    return("")
  }
  if (length(parameters) == 0) {
    return("")
  }
  p_style <- paste0("display: block;margin-block-start: 1em;",
                    "margin-block-end: 1em;margin-inline-start: 0px;",
                    "margin-inline-end: 0px;")
  table_style <- paste0("display: table;border-collapse: separate;",
                        "border-spacing: 10px;border-color: grey;")
  td_style <- paste0("display: table-cell;vertical-align: inherit;",
                     "font-family: sans-serif;font-size: 10pt;")
  table_head <- sprintf('<table summary="R argblock" style="%s">', table_style)
  table_end <- "</table>"
  rows <- ""
  for (index in seq_along(parameters)) {
    parameter <- parameters[index]
    param_name <- names(parameter)
    row_message <- paste0(
      sprintf('<tr valign="top"><td style="%s"><code>%s</code></td>',
              td_style,
              param_name),
      sprintf('<td style="%s"><p style="%s">%s</p></td></tr>', td_style,
              p_style,
              parameter)
    )
    rows <- paste0(rows, row_message)
  }
  arguments_table <- paste0(table_head, rows, table_end)
  sprintf('<h3 style="%s">Arguments</h3>%s',
          ee_css_h3_rstudio(),
          arguments_table)
}

#' Create details - R Documentation Simple
#' @noRd
ee_html_details_simple <- function(details) {
  if (is.null(details)) {
    return(details)
  }
  if (nchar(details) < 1) {
    details
  } else {
    sprintf('<h3 style="%s">Details</h3><p>%s<p>',
            ee_css_h3_simple(),
            details)
  }
}

#' Create details - R Documentation Rstudio
#' @noRd
ee_html_details_rstudio <- function(details) {
  if (is.null(details)) {
    return(details)
  }
  if (nchar(details) < 1) {
    details
  } else {
    p_style <- "font-family: sans-serif; font-size: 10pt;"
    sprintf('<h3 style="%s">Details</h3><p style="%s">%s<p>',
            ee_css_h3_rstudio(),
            p_style,
            details)
  }
}

#' Create returns - R Documentation Simple
#' @noRd
ee_html_returns_simple <- function(returns) {
  if (is.null(returns)) {
    return(returns)
  }
  if (nchar(returns) < 1) {
    returns
  } else {
    sprintf('<h3 style="%s">Returns</h3><p>%s<p>',
            ee_css_h3_simple(),
            returns)
  }
}

#' Create examples - R Documentation Simple
#' @noRd
ee_html_examples_simple <- function(examples) {
  if (is.null(examples)) {
    return(examples)
  }
  if (length(examples) == 0) {
    return(NULL)
  }

  if (nchar(examples) < 1) {
    examples
  } else {
    sprintf('<h3 style="%s">Examples</h3><p>%s<p>',
            ee_css_h3_simple(),
            examples)
  }
}

#' Create returns - R Documentation Simple
#' @noRd
ee_html_returns_rstudio <- function(returns) {
  if (is.null(returns)) {
    return(returns)
  }
  if (nchar(returns) < 1) {
    returns
  } else {
    p_style <- "font-family: sans-serif; font-size: 10pt;"
    sprintf('<h3 style="%s">Returns</h3><p style="%s">%s<p>',
            ee_css_h3_rstudio(),
            p_style,
            returns)
  }
}


#' Create examples - R Documentation Simple
#' @noRd
ee_html_examples_rstudio <- function(examples) {
  if (is.null(examples)) {
    return(examples)
  }
  if (length(examples) == 0) {
    return(NULL)
  }
  if (nchar(examples) < 1) {
    examples
  } else {
    p_style <- "font-family: sans-serif; font-size: 10pt;"
    sprintf(
      '<h3 style="%s">Examples</h3><p style="%s">%s<p>',
      ee_css_h3_rstudio(),
      p_style,
      examples
    )
  }
}

#' pre tag CSS-style - Simple
#' @noRd
ee_css_pre_simple <- function() {
  "font-family: monospace;"
}

#' h2 tag CSS-style - Simple
#' @noRd
ee_css_h2_simple <- function() {
  paste0(
    "background: white;color: rgb(40%, 40%, 40%);font-family: monospace",
    ";font-size: large;text-align: center;"
  )
}

#' h2 tag CSS-style - Rstudio
#' @noRd
ee_css_h2_rstudio <- function() {
  "font-size: x-large;font-weight: normal;font-family: sans-serif;"
}

#' h3 tag CSS-style - Simple
#' @noRd
ee_css_h3_simple <- function() {
  paste0(
    "background: white;color: rgb(40%, 40%, 40%);font-family: monospace",
    ";font-size: large;"
  )
}

#' h3 tag CSS-style - Rstudio
#' @noRd
ee_css_h3_rstudio <- function() {
  paste0(
    "background: white;color: rgb(35%, 35%, 35%);font-family: sans-serif",
    ";font-size: 15px;"
  )
}

#' Get lhs argument from a pipe
#' @noRd
ee_get_lhs <- function() {
  parents <- lapply(sys.frames(), parent.env)
  is_magrittr_env <-
    vapply(parents, identical, logical(1), y = environment(`%>%`))
  if (any(is_magrittr_env)) {
    deparse(get("lhs", sys.frames()[[max(which(is_magrittr_env))]]))
  }
}

#' Scaffold R wrappers for Python functions
#'
#' @param python_function Fully qualified name of Python function or class
#' constructor (e.g. ee$Image()$geometry()$Rectangle)
#' @noRd
ee_function_docs <- function(ee_function) {
  inspect <- import("inspect")
  function_docs <- inspect$getdoc(eval(parse(text = ee_function)))
  output_help <- py_function_docs(ee_function)
  real_description <- paste(output_help$description,output_help$details)
  real_args <- ee_help_create_arg(function_docs)
  output_help$title <- output_help$description
  output_help$description <- gsub("\n"," ",real_description)
  output_help$details <- ""
  output_help$parameters <- real_args$arg
  output_help$Examples <- gsub(">>>", "<br>", output_help$sections$Examples)
  output_help$sections <- NULL
  output_help$signature <- sprintf(
    "%s(%s, ...)",
    gsub( " *\\(.*?\\) *", "", output_help$signature),
    real_args$signature)
  output_help
}

#' Get the real name of the function
#' @noRd
ee_real_name <- function(ee_function){
  components <- strsplit(ee_function, "\\$")[[1]]
  topic <- components[[length(components)]]
  source <- paste(components[1:(length(components) - 1)],
                  collapse = "$")
  fn_name <- paste0(source,"$name()")
  ee_object_name <- tryCatch(
    expr = eval(parse(text = fn_name)),
    error = function(e) stop(
      "ee_help was not able to determinate the function name."
    )
  )
  sprintf("ee$%s$%s",ee_object_name,topic)
}


#' Create args argument
#' @noRd
ee_help_create_arg <- function(function_docs) {
  # get just the argument text
  arguments <- strsplit(function_docs,"(\nArgs:\n) ")[[1]][2]
  if (is.na(arguments)) {
    return(list(signature = "cls", arg = ""))
  }
  arguments <- gsub("Returns.*","", arguments)
  groups <- strsplit(arguments,"\n")[[1]]
  group_condition <- grepl("^\\s*[aA0-zZ9|**]*:", groups, perl = TRUE)

  #Create text groups
  walk <- 0
  result <- rep(NA, length(group_condition))
  for (index in seq_along(group_condition)) {
    cond <- group_condition[index]
    walk <- walk + cond
    result[index] <- walk
  }
  # Handling text inside the groups
  arguments_des <- rep(NA, length(unique(result)))
  arguments_name <- rep(NA, length(unique(result)))

  for (group in unique(result)) {
    message <- paste0(groups[which(result == group)],collapse = "")
    arg <- sub("^([^:]+:).+$", "\\1", message)
    arg_clean  <-  trimws(sub(":","",sub("\\s","",arg)))
    message_clean <- trimws(gsub("\\s+"," ", sub(arg,"",message, fixed = TRUE)))
    arguments_des[group] <- message_clean
    arguments_name[group] <- arg_clean
  }
  names(arguments_des) <- arguments_name
  arguments_des <- arguments_des[!names(arguments_des) %in% "DEPRECATED"]
  arguments_name <- arguments_name[!arguments_name == "DEPRECATED"]
  signature_text <- paste(arguments_name, collapse = ", ")
  return(list(arg = arguments_des, signature = signature_text))
}


#' ee module help
#' @noRd
ee_module_help <- function() {
  list(
    name = "",
    qualified_name = "ee",
    description = "Interface to main Earth Engine module. Provides access to top level classes and functions as well as sub-modules (e.g. ee$Image, ee$FeatureCollection$first, etc.).",
    details = "",
    signature = "ee",
    parameters = "",
    sections = list(),
    returns = NULL,
    title = "Main Earth Engine module"
  )
}

#' Create init table - R Documentation Simple
#' @noRd
rgeeExtracheckmans <- function(fun_name, type="image") {
  if (type=="image") {
    checkmanfiles <- sprintf("%s%s", "ee_Image_", fun_name)
  } else if (type=="ic") {
    checkmanfiles <- sprintf("%s%s", "ee_ImageCollection_", fun_name)
  }
  utils::help(checkmanfiles, package = "rgeeExtra", verbose = FALSE)
}
r-spatial/rgee documentation built on July 4, 2024, 9:33 a.m.