#' @title Check package dependencies
#' @description The function allows to graphically check that all the
#' dependencies are installed.
#' @return NULL
#' @details This package needs some external dependencies to run:
#' - Python
#' - GDAL
#' - Wget
#' - sen2cor
#'
#' This function opens a GUI which allows to check that these dependencies
#' are installed. This check is highly suggested before using the library for
#' the fist time, in order to avoid errors.
#' @author Luigi Ranghetti, phD (2018) \email{ranghetti.l@@irea.cnr.it}, Pascal Obstetar, (2018) \email{pascal.obstetar@@gmail.com}
#' @note License: GPL 3.0
#' @importFrom shiny actionButton addResourcePath br code conditionalPanel div em
#' fluidPage fluidRow h3 helpText htmlOutput icon modalButton
#' modalDialog observe observeEvent outputOptions p reactive
#' reactiveFileReader reactivePoll reactiveValues renderText renderUI runApp
#' shinyApp showModal span strong textOutput uiOutput verbatimTextOutput
#' @importFrom shinyjs hide html useShinyjs extendShinyjs
#' @importFrom shinyWidgets confirmSweetAlert
#' @importFrom utils capture.output
#' @importFrom jsonlite fromJSON toJSON
#' @export
#' @examples
#' \dontrun{
#'
#' check_theia2r_deps()
#' }
check_theia2r_deps <- function() {
jscode <- "shinyjs.closeWindow = function() { window.close(); }"
settings.ui <- fluidPage(
# header
shinyjs::useShinyjs(),
extendShinyjs(text = jscode, functions = c("closeWindow")),
fluidRow(column(
title="Dependencies",
width=12,
h3("GDAL"),
helpText(em(
"GDAL is a mandatory dependency of the package:",
"it is needed for all the processing operations",
"and to retrieve metadata from SAFE and THEIA products"
)),
span(style="display:inline-block;vertical-align:center;padding-top:5px;",
actionButton("check_gdal", "Check GDAL", width=200),
"\u2000"),
span(style="display:inline-block;vertical-align:center;",
htmlOutput("check_gdal_icon")),
conditionalPanel(
condition = "output.os_name == 'Windows'",
h3("Wget"),
helpText(em(
"Wget is the downloader used by the package;",
"it is required by the package, unless you choose to work offline."
)),
span(style="display:inline-block;vertical-align:center;padding-top:5px;",
actionButton("check_wget", "Check Wget", width=200),
"\u2000"),
span(style="display:inline-block;vertical-align:center;",
htmlOutput("check_wget_icon"))
),
h3("aria2"),
helpText(em(
"aria2 is an alternative (faster) downloader downloader which can be",
"used to download SAFE archives;",
"its installation is optional."
)),
span(style="display:inline-block;vertical-align:center;padding-top:5px;",
actionButton("check_aria2", "Check aria2", width=200),
"\u2000"),
span(style="display:inline-block;vertical-align:center;",
htmlOutput("check_aria2_icon")),
h3("sen2cor"),
helpText(em(
"sen2cor is used to perform atmospheric correction of Sentinel-2",
"Level-1C products: it is required by the package,",
"unless you choose not to correct products locally",
"(using only Level-1C \u2013 TOA products",
"or dowloading directly Level-2A products)."
)),
span(style="display:inline-block;vertical-align:center;padding-top:5px;",
actionButton("check_sen2cor", "Check sen2cor", width=200),
"\u2000"),
span(style="display:inline-block;vertical-align:center;",
htmlOutput("check_sen2cor_icon")),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
uiOutput("footer_buttons")
)) # end of fluidRow Dependencies
) # end of fluidPage
settings.server <- function(input, output, session) {
# link to www directory and objects
addResourcePath("www", system.file("www", package="theia2r"))
rv <- reactiveValues()
# output OS name (to be used in conditionalPanel)
output$os_name <- renderText(Sys.info()["sysname"])
outputOptions(output, "os_name", suspendWhenHidden = FALSE)
# list of dependencies
dependencies <- c(
"gdalbuildvrt", "gdal_translate", "gdalwarp", "gdal_calc", "gdaldem", "gdalinfo", "ogrinfo",
"python", "sen2cor", "wget"
)
# load binpaths
binpaths <- reactivePoll(1000, session, function() {}, load_binpaths)
##-- Perform checks of dependencies --##
#-- Check GDAL --#
# build the icon of the check
observe({
input$check_gdal
rv$check_gdal_isvalid <- if (!is.null(binpaths()$gdalinfo)) {
file.exists(binpaths()$gdalinfo)
} else {FALSE}
})
output$check_gdal_isvalid <- renderText(rv$check_gdal_isvalid)
output$check_gdal_icon <- renderUI({
if (is.na(rv$check_gdal_isvalid)) {
""
} else if (rv$check_gdal_isvalid) {
span(style="color:darkgreen;", "\u2714")
} else {
span(style="color:red;", "\u2718")
}
})
outputOptions(output, "check_gdal_isvalid", suspendWhenHidden = FALSE)
# build the modal dialog
check_gdal_modal <- reactive({
modalDialog(
title = "GDAL check",
size = "s",
uiOutput("check_gdal_message"),
verbatimTextOutput("check_gdal_outmessages"),
easyClose = FALSE,
footer = NULL
)
})
# use a reactive output for install GDAL message
# (this because otherwise it does not react to check_gdal_valid chages)
output$check_gdal_message <- renderUI({
if (is.na(rv$check_gdal_isvalid)) {
div(
align="center",
p(style="text-align:center;font-size:500%;color:darkgrey;",
icon("cog", class = "fa-spin"))
)
} else if (!rv$check_gdal_isvalid) {
if (Sys.info()["sysname"] == "Windows") {
div(
p(style="text-align:center;font-size:500%;color:red;",
icon("times-circle")),
p("GDAL needs to be installed.",
"To do it, download the OSGeo4W installer using the button below:",
"then, give the administrator rules when required,",
"choose the \"Advanced install\" and",
"continue clicking \"Next\";",
"when the window to chose the packages to install will appear,",
"check the package \"gdal-python\" and install it."),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
actionButton("install_gdal_button", strong("\u2000Download"), icon=icon("download")),
modalButton("\u2000Cancel", icon = icon("ban")))
)
} else {
div(
p(style="text-align:center;font-size:500%;color:red;",
icon("times-circle")),
p("GDAL needs to be installed.",
"To do it, install the package \"python-gdal\",",
"then repeat this check."),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
modalButton("\u2000Close", icon = icon("check")))
)
}
} else if (rv$check_gdal_isvalid) {
div(
p(style="text-align:center;font-size:500%;color:darkgreen;",
icon("check-circle")),
p("GDAL is correctly installed."),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
modalButton("\u2000Close", icon = icon("check")))
)
}
})
# build the modal dialog
install_gdal_modal <- reactive({
modalDialog(
title = "Install GDAL",
size = "s",
uiOutput("install_gdal_message"),
easyClose = FALSE,
footer = NULL
)
})
# do the check when button is pressed
observeEvent(input$check_gdal, {
# reset check value
rv$check_gdal_isvalid <- NA # FIXME not working
# open modaldialog
showModal(check_gdal_modal())
shinyjs::html(
"check_gdal_message",
as.character(div(
align="center",
p(style="text-align:center;font-size:500%;color:darkgrey;",
icon("spinner", class = "fa-pulse"))
))
)
# do the check
withCallingHandlers({
shinyjs::html("check_gdal_outmessages", "")
rv$check_gdal_isvalid <- check_gdal(abort = FALSE, force = TRUE)
},
message = function(m) {
shinyjs::html(id = "check_gdal_outmessages", html = m$message, add = TRUE)
})
shinyjs::hide("check_gdal_outmessages")
})
# install osgeo
observeEvent(input$install_gdal_button, {
showModal(install_gdal_modal())
# create the text to show in the modaldialog
shinyjs::html(
"install_gdal_message",
as.character(div(
br(),
p(style="color:darkgrey;text-align:center;font-size:500%;","\u23F3"),
p("Wait while the OSGeo4W installer is being downloaded...")
)),
add=FALSE
)
# Download wget
osgeo4w_url <- paste0(
"http://download.osgeo.org/osgeo4w/osgeo4w-setup-x86",
if (Sys.info()["machine"]=="x86-64") {"_64"},".exe"
)
osgeo4w_path <- tempfile(pattern="dir",fileext = ".exe")
# use wget instead of download.file() because the internal function
# fails in downloading .exe (the output file is different - biggger - from the source).
system(paste0(
"\"",binpaths()$wget, "\" -q \"", osgeo4w_url,
"\" -O \"", osgeo4w_path, "\""
), intern=TRUE)
# download.file(osgeo4w_url, osgeo4w_path)
if (file.exists(osgeo4w_path)) {
shinyjs::html(
"install_gdal_message",
as.character(div(
p("OSGeo4W was correctly downloaded."),
p("The installer window was opened;",
"please install the package \"gdal-python\" following the instructions.")
)),
add = TRUE
)
shell(osgeo4w_path)
shinyjs::html(
"install_gdal_message",
as.character(div(
p("The installation was terminated;",
"please repeat the check to be sure all was correctly installed."),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
modalButton("\u2000Close", icon = icon("check")))
)),
add = TRUE
)
} else {
shinyjs::html(
"install_gdal_message",
as.character(div(
p("Something went wrong during the download; please retry."),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
modalButton("\u2000Close", icon = icon("check")))
)),
add = TRUE
)
}
})
#-- Check Wget --#
# build the icon of the check
observe({
input$check_wget
rv$check_wget_isvalid <- if (Sys.info()["sysname"] != "Windows") {
TRUE
} else if (!is.null(binpaths()$wget)) {
file.exists(binpaths()$wget)
} else {FALSE}
})
output$check_wget_isvalid <- renderText(rv$check_wget_isvalid)
output$check_wget_icon <- renderUI({
if (is.na(rv$check_wget_isvalid)) {
""
} else if (rv$check_wget_isvalid) {
span(style="color:darkgreen;", "\u2714")
} else {
span(style="color:red;", "\u2718")
}
})
outputOptions(output, "check_wget_isvalid", suspendWhenHidden = FALSE)
output$check_wget_message <- renderUI({
if (is.na(rv$check_wget_isvalid)) {
""
} else if (!rv$check_wget_isvalid) {
div(
align = "center",
p(style="color:red;text-align:center;font-size:500%;",
icon("times-circle")),
p("wget needs to be downloaded."),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
actionButton("install_wget_button", strong("\u2000Download"), icon=icon("download")),
modalButton("\u2000Cancel", icon = icon("ban")))
)
} else if (rv$check_wget_isvalid) {
div(
align = "center",
p(style="text-align:center;font-size:500%;color:darkgreen;",
icon("check-circle")),
p("Wget is correctly installed."),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
modalButton("\u2000Close", icon = icon("check")))
)
}
})
# build the modalDialog
check_wget_modal <- modalDialog(
title = "wget check",
size = "s",
uiOutput("check_wget_message"),
easyClose = FALSE,
footer = NULL
)
# open the modaldialog when button is pressed
observeEvent(input$check_wget, {
# open the dialog
showModal(check_wget_modal)
})
# install wget
observeEvent(input$install_wget_button, {
shinyjs::html(
"check_wget_message",
as.character(div(
align="center",
p(style="text-align:center;font-size:500%;color:darkgrey;",
icon("cog", class = "fa-spin")),
p("Wait while Wget is being installed...")
))
)
Sys.sleep(0.5)
check_wget_outerr <- tryCatch(
install_wget(),
error = function(e) {print(e)}
)
# remove the text
if (is(check_wget_outerr, "error")) {
shinyjs::html(
"check_wget_message",
as.character(div(
align="center",
p(style="text-align:center;font-size:500%;color:red;",
icon("times-circle")),
p("Some errors occurred:"),
p(code(check_wget_outerr)),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
modalButton("\u2000Close", icon = icon("check")))
))
)
rv$check_wget_isvalid <- FALSE
} else {
shinyjs::html(
"check_wget_message",
as.character(div(
align="center",
p(style="text-align:center;font-size:500%;color:darkgreen;",
icon("check-circle")),
p("Wget was correctly installed."),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
modalButton("\u2000Close", icon = icon("check")))
))
)
rv$check_wget_isvalid <- TRUE
}
})
#-- Check aria2 --#
# build the icon of the check
observe({
input$check_aria2
rv$check_aria2_isvalid <- if (!is.null(binpaths()$aria2c)) {
file.exists(binpaths()$aria2c)
} else {FALSE}
})
output$check_aria2_isvalid <- renderText(rv$check_aria2_isvalid)
output$check_aria2_icon <- renderUI({
if (is.na(rv$check_aria2_isvalid)) {
""
} else if (rv$check_aria2_isvalid) {
span(style="color:darkgreen;", "\u2714")
} else {
span(style="color:red;", "\u2718")
}
})
outputOptions(output, "check_aria2_isvalid", suspendWhenHidden = FALSE)
output$check_aria2_message <- renderUI({
if (is.na(rv$check_aria2_isvalid)) {
""
} else if (!rv$check_aria2_isvalid) {
div(
align = "center",
p(style="color:red;text-align:center;font-size:500%;",
icon("times-circle")),
if (Sys.info()["sysname"] == "Windows") {
div(
p("aria2 needs to be downloaded."),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
actionButton("install_aria2_button", strong("\u2000Download"), icon=icon("download")),
modalButton("\u2000Cancel", icon = icon("ban")))
)
} else {
div(
p("aria2 needs to be installed",
"To do it, install the package \"aria2\",",
"then repeat this check."),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
modalButton("\u2000Close", icon = icon("check")))
)
}
)
} else if (rv$check_aria2_isvalid) {
div(
align = "center",
p(style="text-align:center;font-size:500%;color:darkgreen;",
icon("check-circle")),
p("aria2 is correctly installed."),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
modalButton("\u2000Close", icon = icon("check")))
)
}
})
# build the modalDialog
check_aria2_modal <- modalDialog(
title = "aria2 check",
size = "s",
uiOutput("check_aria2_message"),
easyClose = FALSE,
footer = NULL
)
# open the modaldialog when button is pressed
observeEvent(input$check_aria2, {
# do the check
import_s2download(with_aria2 = TRUE)
# update the check
rv$check_aria2_isvalid <- if (!is.null(load_binpaths("aria2")$aria2c)) {
file.exists(load_binpaths()$aria2c)
} else {
FALSE
}
# open modaldialog
showModal(check_aria2_modal)
})
# install aria2
observeEvent(input$install_aria2_button, {
shinyjs::html(
"check_aria2_message",
as.character(div(
align="center",
p(style="text-align:center;font-size:500%;color:darkgrey;",
icon("cog", class = "fa-spin")),
p("Wait while aria2 is being installed...")
))
)
Sys.sleep(0.5)
check_aria2_outerr <- tryCatch(
install_aria2(),
error = function(e) {print(e)}
)
# remove the text
if (is(check_aria2_outerr, "error")) {
shinyjs::html(
"check_aria2_message",
as.character(div(
align="center",
p(style="text-align:center;font-size:500%;color:red;",
icon("times-circle")),
p("Some errors occurred:"),
p(code(check_aria2_outerr)),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
modalButton("\u2000Close", icon = icon("check")))
))
)
rv$check_aria2_isvalid <- FALSE
} else {
shinyjs::html(
"check_aria2_message",
as.character(div(
align="center",
p(style="text-align:center;font-size:500%;color:darkgreen;",
icon("check-circle")),
p("aria2 was correctly installed."),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
modalButton("\u2000Close", icon = icon("check")))
))
)
rv$check_aria2_isvalid <- TRUE
}
})
#-- Check sen2cor --#
# build the icon of the check
observe({
input$check_sen2cor # redo when the button is pressed
rv$check_sen2cor_isvalid <- if (!is.null(binpaths()$sen2cor)) {
file.exists(binpaths()$sen2cor)
} else {FALSE}
})
output$check_sen2cor_isvalid <- renderText(rv$check_sen2cor_isvalid)
output$check_sen2cor_icon <- renderUI({
if (is.na(rv$check_sen2cor_isvalid)) {
""
} else if (rv$check_sen2cor_isvalid) {
span(style="color:darkgreen;", "\u2714")
} else {
span(style="color:red;", "\u2718")
}
})
outputOptions(output, "check_sen2cor_isvalid", suspendWhenHidden = FALSE)
output$check_sen2cor_message <- renderUI({
if (is.na(rv$check_sen2cor_isvalid)) {
""
} else if (rv$check_sen2cor_isvalid) {
div(
align = "center",
p(style="color:darkgreen;text-align:center;font-size:500%;",
icon("check-circle")),
p("sen2cor is correctly installed."),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
modalButton("\u2000Close", icon = icon("check")))
)
} else {
div(
align = "center",
p(style="color:red;text-align:center;font-size:500%;",
icon("times-circle")),
p("sen2cor needs to be downloaded and installed."),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
actionButton("install_sen2cor_button", strong("\u2000Download"), icon=icon("download")),
modalButton("\u2000Cancel", icon = icon("ban")))
)
}
})
# build the modalDialog
check_sen2cor_modal <- modalDialog(
title = "sen2cor check",
size = "s",
uiOutput("check_sen2cor_message"),
easyClose = FALSE,
footer = NULL
)
# open the modaldialog when button is pressed
observeEvent(input$check_sen2cor, {
# open the dialog
showModal(check_sen2cor_modal)
})
# install sen2cor
observeEvent(input$install_sen2cor_button, {
# create the text to show in the modaldialog
shinyjs::html(
"check_sen2cor_message",
as.character(div(
align="center",
p(style="text-align:center;font-size:500%;color:darkgrey;",
icon("cog", class = "fa-spin")),
p("Wait while sen2cor is being installed...")
))
)
check_sen2cor_outmess <- capture.output(
check_sen2cor_outerr <- tryCatch(
.install_sen2cor(interactive = FALSE),
error = function(e) {print(e)}
),
type = "message"
)
# remove the text
if (is(check_sen2cor_outerr, "error")) {
rv$check_sen2cor_isvalid <- FALSE
shinyjs::html(
"check_sen2cor_message",
as.character(div(
p(code(check_sen2cor_outmess)),
p(code(check_sen2cor_outerr)),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
modalButton("\u2000Close", icon = icon("check")))
))
)
} else {
rv$check_sen2cor_isvalid <- TRUE
shinyjs::html(
"check_sen2cor_message",
as.character(div(
align="center",
p(style="text-align:center;font-size:500%;color:darkgreen;",
icon("check-circle")),
p("sen2cor was correctly installed."),
hr(style="margin-top: 0.75em; margin-bottom: 0.75em;"),
div(style="text-align:right;",
modalButton("\u2000Close", icon = icon("check")))
))
)
}
})
##-- Footer buttons --##
observe({
rv$check_all_isvalid <- all(c(
rv$check_gdal_isvalid, rv$check_wget_isvalid, rv$check_aria2_isvalid,
rv$check_sen2cor_isvalid
))
})
output$footer_buttons <- renderUI({
div(
style = "vertical-align:center;text-align:right;",
if (rv$check_all_isvalid) {
span(
style = "display:inline-block;",
"All the dependencies are satisfied, you can safely use the library.\u2000"
)
# actionButton("close_gui", "\u2000Close", icon = icon("check"), class = "darkbutton")
},
actionButton(
"close_gui", "\u2000Close",
icon = icon(ifelse(rv$check_all_isvalid, "check", "exclamation-triangle")),
class = "darkbutton"
)
)
})
# Close the connection when button is pressed
observeEvent(input$close_gui, {
if (!rv$check_all_isvalid) {
confirmSweetAlert(
session = session, inputId = "confirm_close", type = "warning",
title = "Closing the GUI?",
text = paste0(
"Are you sure do you want to quit? ",
"Running the package with unsatisfied ",
"dependencies can lead to errors."
),
danger_mode = TRUE, btn_labels = c("Cancel", "Close window")
)
} else {
shinyjs::js$closeWindow()
stopApp()
}
})
observeEvent(input$confirm_close, {
if (input$confirm_close) {
shinyjs::js$closeWindow()
stopApp()
}
})
# Close the connection when window is closed
session$onSessionEnded(function() {
stopApp()
})
}
settings.shiny <- shinyApp(
ui = settings.ui, server = settings.server,
options = list(width="400px", height="400px")
)
# run
if (interactive()) {
options(device.ask.default = FALSE)
return(runApp(settings.shiny))
} else {
stop("The function must be run from an interactive R session.")
}
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.