#' @title Checking paths to QGIS applications
#' @description `check_apps()` checks if software applications necessary to
#' run QGIS (QGIS and Python plugins) are installed in the correct
#' locations.
#' @param root Path to the root directory. Usually, this is 'C:/OSGEO4~1',
#' '/usr' and '/Applications/QGIS.app/' for the different platforms.
#' @param ... Optional arguments used in `check_apps()`. Under Windows,
#' `set_env()` passes function argument `dev` to `check_apps()`.
#' @return The function returns a list with the paths to all the necessary
#' QGIS-applications.
#' @keywords internal
#' @examples
#' \dontrun{
#' check_apps()
#' }
#' @author Jannes Muenchow, Patrick Schratz
check_apps = function(root, ...) {
if (Sys.info()["sysname"] == "Windows") {
path_apps = file.path(root, "apps")
my_qgis = grep("qgis", dir(path_apps), value = TRUE)
# use the LTR (default), if available
dots = list(...)
if (length(dots) > 0 && !isTRUE(dots$dev)) {
my_qgis = ifelse("qgis-ltr" %in% my_qgis, "qgis-ltr", my_qgis[1])
# stop("When using QGIS3, you have to use the developer version, so please",
# " use dev = TRUE and make sure that you have installed QGIS3.")
} else {
# use ../apps/qgis, i.e. most likely the most recent QGIS version
my_qgis = my_qgis[1]
}
apps = c(
file.path(path_apps, my_qgis),
file.path(path_apps, my_qgis, "python/plugins")
)
} else if (Sys.info()["sysname"] == "Linux" | Sys.info()["sysname"] == "FreeBSD") {
# paths to check
apps = file.path(root, c("bin/qgis", "share/qgis/python/plugins"))
} else if (Sys.info()["sysname"] == "Darwin") {
# paths to check
apps = file.path(root, c("Contents", "Contents/Resources/python/plugins"))
} else {
stop("Sorry, you can use RQGIS3 only under Windows and UNIX-based
operating systems.")
}
out =
lapply(apps, function(app) {
if (file.exists(app)) {
app
} else {
path = NULL
# apps necessary to run the QGIS-API
stop(
"Folder ", dirname(app), " could not be found under ",
basename(app), " Please install it."
)
}
})
names(out) = c("qgis_prefix_path", "python_plugins")
# return your result
out
}
#' @title Open the GRASS online help
#' @description `open_grass_help()` opens the GRASS online help for a specific
#' GRASS geoalgorithm.
#' @param alg The name of the GRASS geoalgorithm for which one wishes to open
#' the online help.
#' @keywords internal
#' @examples
#' \dontrun{
#' open_grass_help("grass7:r.sunmask")
#' }
#' @author Jannes Muenchow
open_grass_help = function(alg) {
grass_name = gsub(".*:", "", alg)
url = ifelse(grepl(7, alg),
"http://grass.osgeo.org/grass75/manuals/",
"http://grass.osgeo.org/grass64/manuals/"
)
url_ind = paste0(url, "full_index.html")
doc = RCurl::getURL(url_ind)
doc2 = XML::htmlParse(doc)
root = XML::xmlRoot(doc2)
grass_funs = XML::xpathSApply(root[["body"]], "//a/@href")
grass_funs = gsub(".html", "", grass_funs)
# grass_funs = grep(".*\\..*", grass_funs, value = TRUE)
# grass_funs = grass_funs[!grepl("^http:", grass_funs)]
# grep("^(d.|db.|g\\.|i.|m.|ps.|r.|r3.|t.|v.)", grass_funs, value = TRUE)
# ind = paste0(c("d", "db", "g", "i", "m", "ps", "r", "r3", "t", "v"), "\\.")
# ind = paste(ind, collapse = "|")
# ind = paste0("^(", ind, ")")
# grass_funs = grep(ind, grass_funs, value = TRUE)
if (!grass_name %in% grass_funs) {
grass_name = gsub("(.*?*)\\..*", "\\1", grass_name)
}
# if the name can still not be found, terminate
if (!grass_name %in% grass_funs) {
stop(gsub(".*:", "", alg), " could not be found in the online help!")
}
url = paste0(url, grass_name, ".html")
utils::browseURL(url)
}
#' @title Reproduce o4w_env.bat script in R
#' @description Windows helper function to start QGIS application. Basically,
#' the code found in all .bat files found in etc/ini (most likely
#' "C:/OSGEO4~1/etc/ini") is reproduced within R.
#' @importFrom readr read_file
#' @param qgis_env Environment settings containing all the paths to run the QGIS
#' API. For more information, refer to [set_env()].
#' @return The function changes the system settings using [base::Sys.setenv()].
#' @keywords internal
#' @author Jannes Muenchow
#' @examples
#' \dontrun{
#' run_ini()
#' }
#'
run_ini = function(qgis_env = set_env()) {
files = dir(file.path(qgis_env$root, "etc/ini"), full.names = TRUE)
files = files[-grep("msvcrt|rbatchfiles", files)]
# root = gsub("\\\\", "\\\\\\\\", qgis_env$root)
ls = lapply(files, function(file) {
tmp = read_file(file)
tmp = gsub("%OSGEO4W_ROOT%", qgis_env$root, tmp)
tmp = strsplit(tmp, split = "\r\n|\n")[[1]]
tmp
})
cmds = do.call(c, ls)
# remove everything followed by a semi-colon but not if the colon is followed
# by %PATH%
cmds = gsub(";%([^PATH]).*", "", cmds)
cmds = gsub(";%PYTHONPATH%", "", cmds) # well, not really elegant...
cmds = gsub("\\\\", "/", cmds)
for (i in cmds) {
if (grepl("^(SET|set)", i)) {
tmp = gsub("^(SET|set) ", "", i)
tmp = strsplit(tmp, "=")[[1]]
args = list(tmp[2])
names(args) = tmp[1]
# if the environment variable exists but does not contain our path, add it
# to the already existing one
if (Sys.getenv(names(args)) != "" &
!grepl(
gsub("\\\\", "\\\\\\\\", args[[1]]),
Sys.getenv((names(args)))
)) {
args[[1]] = paste(args[[1]], Sys.getenv(names(args)), sep = ";")
} else if (Sys.getenv(names(args)) != "" &
grepl(
gsub("\\\\", "\\\\\\\\", args[[1]]),
Sys.getenv((names(args)))
)) {
# if the environment variable already exists and already contains the
# correct path, do nothing
next
}
do.call(Sys.setenv, args)
}
if (grepl("^(path|PATH)", i)) {
tmp = gsub("^(PATH|path) ", "", i)
path = Sys.getenv("PATH")
path = gsub("\\\\", "\\\\\\\\", path)
tmp = gsub("%PATH%", path, tmp)
Sys.setenv(PATH = tmp)
}
if (grepl("^if not defined HOME", i)) {
if (Sys.getenv("HOME") == "") {
use_prof = shell("ECHO %USERPROFILE%", intern = TRUE)
Sys.setenv(HOME = use_prof)
}
}
}
}
#' @title Reset PATH
#' @description Since [run_ini()] starts with a clean PATH, this function makes
#' sure to add the original paths to PATH. Note that this function is a
#' Windows-only function.
#' @param settings A list as derived from `as.list(Sys.getenv())`.
#' @author Jannes Muenchow
reset_path = function(settings) {
# PATH re-setting: not the most elegant solution...
if (Sys.info()["sysname"] == "Windows") {
# first delete any other Anaconda or Python installations from PATH
tmp = grep(
"Anaconda|Python", unlist(strsplit(settings$PATH, ";")),
value = TRUE
)
# we don't want to delete any paths containing OSGEO (and Python)
if (any(grepl("OSGeo", tmp))) {
tmp = tmp[-grep("OSGeo", tmp)]
}
# replace \\ by \\\\ and collapse by |
tmp = paste(gsub("\\\\", "\\\\\\\\", tmp), collapse = "|")
# delete it from settings
repl = gsub(tmp, "", settings$PATH)
# get rid off repeated semi-colons
settings$PATH = gsub(";+", ";", repl)
# We need to make sure to not append over and over again the same paths
# when running open_app several times
if (grepl(gsub("\\\\", "\\\\\\\\", Sys.getenv("PATH")), settings$PATH)) {
# if the OSGeo stuff is already in PATH (which is the case after having
# run open_app for the fist time), use exactly this PATH
Sys.setenv(PATH = settings$PATH)
} else {
# if the OSGeo stuff has not already been appended (as is the case when
# running open_app for the first time), append it
paths = paste(Sys.getenv("PATH"), settings$PATH, sep = ";")
Sys.setenv(PATH = paths)
}
}
}
#' @title Set all Windows paths necessary to start the QGIS application
#' @description Windows helper function to start the QGIS application by setting
#' all necessary path especially through running [run_ini()].
#' @param qgis_env Environment settings containing all the paths to run the QGIS
#' API. For more information, refer to [set_env()].
#' @return The function changes the system settings using [base::Sys.setenv()].
#' @importFrom readr read_file
#' @importFrom stringr str_extract
#' @keywords internal
#' @author Jannes Muenchow
#' @examples
#' \dontrun{
#' setup_win()
#' }
#'
setup_win = function(qgis_env = set_env()) {
# call o4w_env.bat from within R
# not really sure, if we need the next line (just in case)
Sys.setenv(OSGEO4W_ROOT = qgis_env$root)
# shell("ECHO %OSGEO4W_ROOT%")
# REM start with clean path
# windir = shell("ECHO %WINDIR%", intern = TRUE)
# such error messages occurred:
# [1]"'\\\\helix.klient.uib.no\\BioHome\\nboga'"
# Jannes: this was the working directory apparently a server
# [2] "CMD.EXE was started with the above path as the current directory."
# [3] "UNC paths are not supported. Defaulting to Windows directory."
# [4] "C:\\Windows"
# Therefore, pick the last element (not sure if this will always work, well,
# we will find out). Another solution would be to hard-code "C:/Windows" but
# I don't know if system32 can always be found there...
# windir = windir[length(windir)]
# maybe this is a more generic approach
cwd = getwd()
on.exit(setwd(cwd))
setwd("C:/")
windir = shell("ECHO %WINDIR%", intern = TRUE)
windir = normalizePath(windir, "/")
# start with a fresh PATH
Sys.setenv(PATH = paste(
file.path(qgis_env$root, "bin"),
file.path(windir, "system32"),
windir,
file.path(windir, "WBem"),
sep = ";"
))
# call all bat-files
run_ini(qgis_env = qgis_env)
# qt5_env.bat
Sys.setenv(PATH = paste(file.path(qgis_env$root, "apps/qt5/bin"),
Sys.getenv("PATH"),
sep = ";"
))
Sys.setenv(
QT_PLUGIN_PATH = paste(file.path(qgis_env$root, "apps/qt5/plugins"),
Sys.getenv("QT_PLUGIN_PATH"),
sep = ";"
)
)
# py3_env.bat; make more generic, Arch is already using Python37
# make it generic by reading out what kind of Python is stated in py3_env.bat:
py3 = read_file(file.path(qgis_env$root, "/bin/py3_env.bat"))
# extract Python3 version
py3 = str_extract(py3, "Python3\\d")
Sys.setenv(PYTHONHOME = file.path(qgis_env$root, "apps", py3))
Sys.setenv(PATH = paste(file.path(qgis_env$root, "apps", py3),
file.path(qgis_env$root, "apps", py3, "Scripts"),
Sys.getenv("PATH"),
sep = ";"
))
# we need to make sure that qgis-ltr can also be used...
my_qgis = gsub(".*/", "", qgis_env$qgis_prefix_path)
# add the directories where the QGIS libraries reside to search path
# of the dynamic linker
Sys.setenv(PATH = paste(
Sys.getenv("PATH"),
# this fails:
# file.path(qgis_env$root, "apps", my_qgis),
# so you need to use /bin
file.path(qgis_env$root, "apps", my_qgis, "bin"),
sep = ";"
))
# Sys.setenv(GDAL_FILENAME_IS_UTF8 = "YES")
# set the PYTHONPATH variable, so that QGIS knows where to search for
# QGIS libraries and appropriate Python modules
python_path = Sys.getenv("PYTHONPATH")
python_add = file.path(qgis_env$root, "apps", my_qgis, "python")
if (!grepl(python_add, python_path)) {
python_path = paste(python_path, python_add, sep = ";")
# if PYTHONPATH = "", this results in ';C:/OSGeo4W64/apps/qgis/python'
python_path = gsub("^;", "", python_path)
Sys.setenv(PYTHONPATH = python_path)
}
# defining QGIS prefix path (i.e. without bin)
Sys.setenv(QGIS_PREFIX_PATH = file.path(qgis_env$root, "apps", my_qgis))
Sys.setenv(
QT_PLUGIN_PATH = paste(file.path(qgis_env$root, "apps/qgis/qtplugins"),
file.path(qgis_env$root, "apps/qt5/plugins"),
sep = ";"
)
)
# shell.exec("python") # yeah, it works!!!
# !!!Try to make sure that the right Python version is used!!!
use_python(
file.path(qgis_env$root, "bin/python3.exe"),
required = TRUE
)
# compare py_config path with set_env path!!
a = py_config()
# py_config() adds following paths to PATH:
# "C:\\OSGeo4W64\\bin;C:\\OSGeo4W64\\bin\\Scripts;
if (!grepl(qgis_env$root, normalizePath(a$python, "/"))) {
stop("Wrong Python binary. Restart R and check again!")
}
}
#' @title Set all Linux paths necessary to start QGIS
#' @description Helper function to start QGIS application under Linux.
#' @param qgis_env Environment settings containing all the paths to run the QGIS
#' API. For more information, refer to [set_env()].
#' @return The function changes the system settings using [base::Sys.setenv()].
#' @keywords internal
#' @author Jannes Muenchow
#' @examples
#' \dontrun{
#' setup_linux()
#' }
#'
setup_linux = function(qgis_env = set_env()) {
# append PYTHONPATH to import qgis.core etc. packages
python_path = Sys.getenv("PYTHONPATH")
qgis_python_path = paste0(qgis_env$root, "/share/qgis/python")
reg_exp = grepl(paste0(qgis_python_path, ":"), python_path) |
grepl(paste0(qgis_python_path, "$"), python_path)
if (python_path != "" & reg_exp) {
qgis_python_path = python_path
} else if (python_path != "" & !reg_exp) {
qgis_python_path = paste(
qgis_python_path, Sys.getenv("PYTHONPATH"),
sep = ":"
)
}
Sys.setenv(PYTHONPATH = qgis_python_path)
# append LD_LIBRARY_PATH
ld_lib_path = Sys.getenv("LD_LIBRARY_PATH")
qgis_ld_path = file.path(qgis_env$root, "lib")
reg_exp = grepl(paste0(qgis_ld_path, ":"), ld_lib_path) |
grepl(paste0(qgis_ld_path, "$"), ld_lib_path)
if (ld_lib_path != "" & reg_exp) {
qgis_ld_path = ld_lib_path
} else if (ld_lib_path != "" & !reg_exp) {
qgis_ld_path = paste(
qgis_ld_path, Sys.getenv("LD_LIBRARY_PATH"),
sep = ":"
)
}
Sys.setenv(LD_LIBRARY_PATH = qgis_ld_path)
# setting here the QGIS_PREFIX_PATH also works instead of running it twice
# later on
Sys.setenv(QGIS_PREFIX_PATH = qgis_env$root)
# make sure to use Python3
# in QGIS Python console run
# import sys
# sys.version # which python version is used
# sys.exectutable # and where to find the executable
# use_python("/usr/bin/python2.7", required = TRUE)
use_python("/usr/bin/python3", required = TRUE)
}
#' @title Set all Mac paths necessary to start QGIS
#' @description Helper function to start QGIS application under macOS.
#' @param qgis_env Environment settings containing all the paths to run the QGIS
#' API. For more information, refer to [set_env()].
#' @return The function changes the system settings using [base::Sys.setenv()].
#' @keywords internal
#' @author Patrick Schratz
#' @examples
#' \dontrun{
#' setup_mac()
#' }
#'
setup_mac = function(qgis_env = set_env()) {
# append PYTHONPATH to import qgis.core etc. packages
# FIXME: Currently we are overriding the PYTHONPATH variable completely -> might cause trouble for some users?
if (!grepl("homebrew", qgis_env$platform)) {
qgis_python_path = glue::glue(
glue::glue("{qgis_env$qgis_prefix_path}/Resources/python:/Applications/QGIS3.4.app/Contents/Frameworks/QtCore.framework/") # ,
# glue::glue("{qgis_env$qgis_prefix_path}/Frameworks/Python.framework/Versions/Current/lib/python3.7/site-packages")
)
# this is the PYTHONPATH from the QGIS interpreter in the GUI
qgis_python_path = "/Applications/QGIS3.4.app/Contents/MacOS/../Resources/python:/Users/pjs/Library/Application Support/QGIS/QGIS3/profiles/default/python:/Users/pjs/Library/Application Support/QGIS/QGIS3/profiles/default/python/plugins:/Applications/QGIS3.4.app/Contents/MacOS/../Resources/python/plugins:/Applications/QGIS3.4.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.7:/Applications/QGIS3.4.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.7/site-packages/geos:/Applications/QGIS3.4.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.7/site-packages:/Applications/QGIS3.4.app/Contents/Resources/python:/Applications/QGIS3.4.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.7/lib-dynload:/Applications/QGIS3.4.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python37.zip:/Users/pjs/Library/Application Support/QGIS/QGIS3/profiles/default/python"
} else {
# homebrew
qgis_python_path = "/usr/local/opt/lib/python3.7/site-packages:/usr/local/opt/osgeo-qgis/lib/python3.7/site-packages:/usr/local/opt/osgeo-qgis/QGIS.app/Contents/Resources/python:/usr/local/opt/osgeo-gdal-python/lib/python3.7/site-packages:$PYTHONPATH"
}
# FIXME: Works without?
# Sys.setenv(QGIS_PREFIX_PATH = paste0(qgis_env$root, "/Contents/MacOS/"))
Sys.setenv(PYTHONPATH = qgis_python_path)
# FIXME: Works without?
# Sys.setenv(LD_LIBRARY_PATH = "/usr/local/Cellar/osgeo-qgis/3.8.0_1/QGIS.app/Contents/MacOS/lib/:/Applications/QGIS.app/Contents/Frameworks/")
# define path where QGIS libraries reside to search path of the
# dynamic linker
qgis_ld = glue::glue(
glue::glue("{qgis_env$qgis_prefix_path}/MacOS/lib/:"),
glue::glue("{qgis_env$qgis_prefix_path}/Contents/Frameworks/")
)
Sys.setenv(LD_LIBRARY_PATH = qgis_ld)
if (!grepl("homebrew", qgis_env$platform)) {
use_python("/Applications/QGIS3.4.app/Contents/Frameworks/Python.framework/Versions/Current/bin/python3", required = TRUE)
} else {
use_python("/usr/local/bin/python3", required = TRUE)
}
}
#' @title Save spatial objects
#' @description The function saves spatial objects (`sp`, `sf` and `raster`) to
#' a temporary folder on the computer's hard drive.
#' @param params A parameter-argument list as returned by [pass_args()].
#' @param type_name A character string containing the QGIS parameter type for
#' each parameter (boolean, multipleinput, extent, number, etc.) of `params`.
#' The Python method `RQGIS3.get_args_man()` returns a Python dictionary with
#' one of its elements corresponding to the type_name (see also the example
#' section).
#' @param NA_flag Value used for NAs when exporting raster objects.
#' @keywords internal
#' @examples
#' \dontrun{
#' library("RQGIS3")
#' library("raster")
#' library("reticulate")
#' r = raster(ncol = 100, nrow = 100)
#' r1 = crop(r, extent(-10, 11, -10, 11))
#' r2 = crop(r, extent(0, 20, 0, 20))
#' r3 = crop(r, extent(9, 30, 9, 30))
#' r1[] = 1:ncell(r1)
#' r2[] = 1:ncell(r2)
#' r3[] = 1:ncell(r3)
#' alg = "grass7:r.patch"
#' out = py$RQGIS3$get_args_man(alg)
#' params = get_args_man(alg)
#' params$input = list(r1, r2, r3)
#' params[] = save_spatial_objects(
#' params = params,
#' type_name = out$type_name
#' )
#' }
#' @author Jannes Muenchow
save_spatial_objects = function(params, type_name, NA_flag = -99999) {
lapply(seq_along(params), function(i) {
tmp = class(params[[i]])
if (tmp == "list" && type_name[i] == "multilayer" &&
# if the class of params[[i]][[1]] is a character (and not a spatial
# object), then it is (hopefully) a list containing file paths to
# spatial objects
class(params[[i]][[1]]) != "character") {
names(params[[i]]) = paste0("inp", 1:length(params[[i]]))
out = save_spatial_objects(params = params[[i]], NA_flag = NA_flag)
return(out)
}
# GEOMETRY and GEOMETRYCOLLECTION not supported
if (any(tmp %in% c("sfc_GEOMETRY", "sfc_GEOMETRYCOLLECTION"))) {
stop("RQGIS3 does not support GEOMETRY or GEOMETRYCOLLECTION classes")
}
# check if the function argument is a SpatialObject
if (any(grepl("^Spatial(Points|Lines|Polygons)DataFrame$", tmp)) |
any(tmp %in% c("sf", "sfc", "sfg"))) {
# if it is an sp-object convert it into sf, if it already is an attributed
# sf-object, nothing happens
params[[i]] = st_as_sf(params[[i]])
# write sf as a shapefile to a temporary location while overwriting any
# previous versions.
# This is a Windows-only problem (see also github-branch unlock)
fname = tempfile(fileext = ".shp")
write_sf(params[[i]], fname, quiet = TRUE)
# if (inherits(test, "try-error")) {
# while (tolower(basename(fname)) %in% tolower(dir(tempdir()))) {
# fname = paste0(gsub(".shp", "", fname), 1, ".shp")
# }
# write_sf(params[[i]], fname, quiet = TRUE)
# }
# return the result
normalizePath(fname, winslash = "/")
} else if (tmp == "RasterLayer") {
fname = tempfile(fileext = ".tif")
writeRaster(params[[i]],
filename = fname, format = "GTiff",
prj = TRUE, overwrite = TRUE, NAflag = NA_flag
)
# if (inherits(test, "try-error")) {
# while (tolower(basename(fname)) %in% tolower(dir(tempdir()))) {
# fname = paste0(gsub(".tif", "", fname), 1, ".tif")
# }
# writeRaster(
# params[[i]], filename = fname, format = "GTiff",
# prj = TRUE, overwrite = TRUE, NAflag = NA_flag
# )
# }
# return the result
normalizePath(fname, winslash = "/")
} else if (type_name[i] %in% c("vector", "raster", "table") &&
file.exists(params[[i]])) {
# if the user provided a path to a vector or a raster (and its not an
# output file: we use save_spatial_objects only for non-output files in
# pass_args), then normalize this path in case a Windows user has
# used backslashes which might lead to trouble when sth. like \t \n or
# alike appears in the path
tmp = normalizePath(params[[i]], winslash = "/")
# if a network folder is given, normalizePath will convert //, \\, \\\\
# always into \\\\, however Python doesn't like that
gsub("^\\\\\\\\", "//", tmp)
} else {
params[[i]]
}
})
}
#' @title Retrieve the joint extent of all specified spatial objects
#' @description Retrieve the joint extent of all specified spatial objects by
#' running through a parameter-argument list while merging the extents of all
#' spatial objects. This is mostly needed for the `GRASS_REGION_PARAMETER`.
#' Still there are geoalgorithms which require an extent object.
#' @param params A parameter-argument list as returned by [get_args_man()] or
#' [pass_args()], which contains all spatial objects from which the joint
#' extent should be retrieved.
#' @param type_name A character string containing the QGIS parameter type for
#' each parameter (boolean, multipleinput, extent, number, etc.) of `params`.
#' The Python method `RQGIS3.get_args_man()` returns a Python dictionary with one
#' of its elements corresponding to the type_name (see also the example
#' section).
#' @keywords internal
#' @examples
#' \dontrun{
#' library("RQGIS3")
#' library("raster")
#' library("reticulate")
#' r = raster(ncol = 100, nrow = 100)
#' r1 = crop(r, extent(-10, 11, -10, 11))
#' r2 = crop(r, extent(0, 20, 0, 20))
#' r3 = crop(r, extent(9, 30, 9, 30))
#' r1[] = 1:ncell(r1)
#' r2[] = 1:ncell(r2)
#' r3[] = 1:ncell(r3)
#' alg = "grass7:r.patch"
#' out = py$RQGIS3$get_args_man()
#' params = get_args_man(alg)
#' params$input = list(r1, r2, r3)
#' get_extent(params = params, type_name = out$type_name)
#' # or if we save the input rasters in files stored on disk
#' params[] = save_spatial_objects(
#' params = params,
#' type_name = out$type_name
#' )
#' get_extent(params = params, type_name = out$type_name)
#' }
#' @author Jannes Muenchow
get_extent = function(params, type_name) {
ext = mapply(function(x, y) {
if (y == "multilayer") {
# in the case of multiple input use recursion:
# if the input is a list of rasters/shapefiles, unlist it, otherwise split
# the strings by ; which separates multiple file store locations on disk
if (is.list(x)) {
get_extent(x, "")
} else {
get_extent(unlist(strsplit(x, split = ";")), "")
}
} else {
# determine bbox in the case of a vector/raster layer residing in R
tmp = try(expr = extent(x), silent = TRUE)
# determine bbox in the case of a raster stored on disk
if (!inherits(tmp, "try-error")) {
tmp
} else {
tmp = try(
expr = {
ext = GDALinfo(x, returnStats = FALSE)
# xmin, xmax, ymin, ymax
extent(c(
ext["ll.x"],
ext["ll.x"] + ext["columns"] * ext["res.x"],
ext["ll.y"],
ext["ll.y"] + ext["rows"] * ext["res.y"]
))
}, silent = TRUE
)
}
# determine bbox in the case of a vector layer stored on disk
if (!inherits(tmp, "try-error")) {
tmp
} else {
# We cannot simply use gsub as we have done before (gsub("[.].*",
# "",basename(x))) if the filename itself also contains dots, e.g.,
# gis.osm_roads_free_1.shp
# We could use regexp to cut off the file extension
# my_layer = stringr::str_extract(basename(x), "[A-z].+[^\\.[A-z]]")
# but let's use an already existing function
tmp = try(
# [c(1, 3, 2, 4)] (xmin, ymin, xmax, ymax...) -> check if this is the
# case for all vector formats (hopefully)
expr = {
my_layer = file_path_sans_ext(basename(as.character(x)))
extent(ogrInfo(
dsn = dirname(as.character(x)),
layer = my_layer
)$extent[c(1, 3, 2, 4)])
}, silent = TRUE
)
}
# return tmp if an extent could be determined, if not return NA for the
# given object object
if (!inherits(tmp, "try-error")) {
tmp
} else {
NA
}
}
}, x = params, y = type_name, SIMPLIFY = FALSE)
# now that we have possibly several extents, union them
ext = ext[!is.na(ext)]
ext = Reduce(raster::merge, ext)
if (is.null(ext)) {
stop(
"Either you forgot to specify an input shapefile/raster or the",
" input file does not exist"
)
}
# sometimes the extent is given back with dec = ","; you need to change that
ext = gsub(",", ".", ext[1:4])
ext
}
#' @title Check if RQGIS3 is loaded on a server
#' @description Performs cross-platform (Unix, Windows) and OS (Debian/Ubuntu) checks for a server infrastructure
#' @importFrom parallel detectCores
#' @keywords internal
#' @author Patrick Schratz
#' @export
check_for_server = function() {
# try to get an output of 'lsb_release -a'
if (detectCores() > 10 && .Platform$OS.type == "unix") {
test = try(
suppressWarnings(system2(
"lsb_release", "-a",
stdout = TRUE,
stderr = TRUE
)),
silent = TRUE
)
if (!inherits(test, "try-error")) {
get_regex = grep("Distributor ID:", test, value = TRUE)
platform = gsub("Distributor ID:\t", "", get_regex)
# check for Debian | Ubuntu
if (platform == "Debian") {
warning(paste0(
"Hey there! According to our internal checks, you are trying to run RQGIS3 on a server.\n",
"Please note that this is only possible if you imitate a x-display.\n",
"QGIS needs this in the background to be able to execute its processing modules.\n",
"Since we detected you are running a Debian server, the following R command should solve the problem:\n",
"system('export DISPLAY=:99 && xdpyinfo -display $DISPLAY > /dev/null || Xvfb $DISPLAY -screen 99 1024x768x16 &').\n",
"Note that you need to run this as root.",
collapse = "\n"
))
# set warn = -1 to only display this warning once per session (warn = -1 ignores warnings commands)
options(warn = -1)
}
if (platform == "Ubuntu") {
warning(paste0(
"Hey there! According to our internal checks, you are trying to run RQGIS3 on a server.\n",
"Please note that this is only possible if you imitate a x-display.\n",
"QGIS needs this in the background to be able to execute its processing modules.\n",
"Since we detected you are running a Debian server, the following R command should solve the problem:\n",
"system('export DISPLAY=:99 && /etc/init.d/xvfb && start && sleep 3').\n",
"Note that you need to run this as root.",
collapse = "\n"
))
# set warn = -1 to only display this warning once per session (warn = -1 ignores warnings commands)
options(warn = -1)
}
}
}
if (detectCores() > 10 && Sys.info()["sysname"] == "Windows") {
warning(paste0(
"Hey there! According to our internal checks, you are trying to run RQGIS3 on a Windows server.\n",
"Please note that this is only possible if you imitate a x-display.\n",
"QGIS needs this in the background to be able to execute its processing modules.\n",
"Note that you need to start the x-display with admin rights",
collapse = "\n"
))
# set warn = -1 to only display this warning once per session (warn = -1 ignores warnings commands)
options(warn = -1)
}
}
#' @title convert recursively NULL, TRUE, FALSE to Python equivalents None,
#' True, False
#' @param params A parameter-argument list as returned by [get_args_man()] or
#' [pass_args()].
#' @keywords internal
#' @author Jannes Muenchow
#' @examples
#' \dontrun{
#' library("RQGIS3")
#' params = get_args_man("native:centroids")
#' convert_ntf(params)
#' # and just to show that it also works recursively
#' params$INPUT = list("None", "None")
#' convert_ntf(params)
#' }
convert_ntf = function(x) {
lapply(x, function(y) {
if (class(y) == "list") {
convert_ntf(y)
} else {
if (y == "None") {
y = r_to_py(NULL)
}
if (y == "True") {
y = r_to_py(TRUE)
}
if (y == "False") {
y = r_to_py(FALSE)
}
}
# return your result
y
})
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.