Skip to content

Commit 02ffef9

Browse files
committed
fix #69
1 parent 5a9ed61 commit 02ffef9

File tree

4 files changed

+195
-80
lines changed

4 files changed

+195
-80
lines changed

R/gdalControls.R

Lines changed: 171 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -202,76 +202,183 @@ searchGDALW <- function(DL = "C:/", quiet = TRUE) {
202202
gdal1$py <- gdal_py[gdalInstallations$installation_type != "unknown"]
203203
return(gdal1)
204204
}
205-
#'@title Search recursively for valid 'GDAL' installation(s) on a 'X-based' OS
206-
#'@name searchGDALX
207-
#'@description Search for valid 'GDAL' installations on a 'X-based' OS
208-
#'@param MP drive letter default is '/usr/bin'
209-
#'@param quiet boolean switch for supressing messages default is TRUE
210-
#'@return A dataframe with the 'GDAL' root folder(s) the version name(s) and the installation type(s).
211-
#'@author Chris Reudenbach
212-
#'@export
213-
#'@keywords internal
205+
#' Search recursively for valid GDAL installation(s) on Linux/macOS
214206
#'
215-
#'@examples
207+
#' Searches for an executable `gdalinfo` and returns a normalized installations
208+
#' table plus best-effort lists of GDAL binaries (`gdal*`) and python tools (`*.py`)
209+
#' found alongside the detected `gdalinfo`.
216210
#'
217-
#' run = FALSE
218-
#' if (run) {
219-
#' # get all valid GDAL installation folders and params
220-
#' searchGDALX()
211+
#' This implementation is portable: it does NOT use GNU-only `find` primaries
212+
#' such as `-readable`, and it uses `system2(..., args=...)` with proper token
213+
#' separation (no shell parsing assumptions).
214+
#'
215+
#' @param MP Character. Search root. `"default"` expands to `c("~","/opt","/usr/local","/usr")`.
216+
#' You may also pass a single directory (e.g. `"/usr"`).
217+
#' @param quiet Logical. If `TRUE`, suppress messages.
218+
#'
219+
#' @return A list with:
220+
#' \describe{
221+
#' \item{gdalInstallations}{data.frame with columns `binDir`, `baseDir`, `installation_type`.}
222+
#' \item{bin}{list of data.frames (column `gdal_bin`) with detected GDAL binaries per installation.}
223+
#' \item{py}{list of data.frames (column `gdal_py`) with detected GDAL python tools per installation.}
221224
#' }
222-
searchGDALX <- function(MP = "/usr/bin", quiet = TRUE) {
223-
if (MP == "default")
224-
MP <- "/usr/bin"
225-
if (!exists("GiEnv"))
226-
GiEnv <- new.env(parent = globalenv())
227-
# trys to find a osgeo4w installation at the mounting point disk returns root directory and version name recursive
228-
# dir for gdal*.bat returns all version of gdal bat files
229-
if (!quiet)
230-
cat("\nsearching for GDAL Toolbox installations - this may take a while\n")
231-
if (!quiet)
232-
cat("For providing the path manually see ?searchGDALX \n")
233-
raw_GDAL <- options(show.error.messages = FALSE)
234-
options(warn = -1)
235-
raw_GDAL <- try(system2("find", paste(MP, " ! -readable -prune -o -type f -executable -iname 'gdalinfo' -print"), stdout = TRUE))
236-
if (identical(raw_GDAL, character(0)))
237-
raw_GDAL <- "File not found"
238-
if (grepl(raw_GDAL, pattern = "File not found") | grepl(raw_GDAL, pattern = "Datei nicht gefunden")) {
239-
class(raw_GDAL) <- c("try-error", class(raw_GDAL))
225+
#'
226+
#' @export
227+
#' @keywords internal
228+
#'
229+
#' @examples
230+
#' \dontrun{
231+
#' x <- searchGDALX()
232+
#' x$gdalInstallations
233+
#' }
234+
searchGDALX <- function(MP = "default", quiet = TRUE) {
235+
236+
# ---- 1) mountpoints / roots ----
237+
if (identical(MP, "default")) {
238+
MP <- c("~", "/opt", "/usr/local", "/usr")
239+
} else {
240+
MP <- as.character(MP)
240241
}
241-
options(show.error.messages = TRUE)
242-
options(warn = 0)
243-
gdal1 <- gdal_py <- gdal_bin <- list()
244-
if (class(raw_GDAL)[1] != "try-error") {
245-
# if (!grepl(MP,raw_GDAL)) stop('\n At ',MP,' no GDAL installation found') trys to identify valid gdal
246-
# installations and their version numbers
247-
gdalInstallations <- lapply(seq(length(raw_GDAL)), function(i) {
248-
# TODO strip version from GDAL /usr/bin/gdalcli_BandMath -version 'This is the BandMath application,
249-
# version 6.0.0'
250-
# if the the tag 'OSGEO4W64' exists set installation_type
251-
root_dir <- substr(raw_GDAL[i], 1, gregexpr(pattern = "gdalinfo", raw_GDAL[i])[[1]][1] - 1)
252-
# put the existing GISBASE directory, version number and installation type in a data frame
253-
data.frame(binDir = root_dir, stringsAsFactors = FALSE)
254-
}) # end lapply
255-
# bind the df lines
256-
gdalInstallations <- do.call("rbind", gdalInstallations)
257-
for (i in 1:nrow(gdalInstallations)) {
258-
gdal_bin[[i]] <- as.data.frame(grep(Sys.glob(file.path(R.utils::getAbsolutePath(gdalInstallations[[1]][i]), "gdal*")),
259-
pattern = "\\.(py)$", value = TRUE, invert = TRUE))
260-
names(gdal_bin[[i]]) <- "gdal_bin"
261-
gdal_py[[i]] <- as.data.frame(grep(Sys.glob(file.path(R.utils::getAbsolutePath(gdalInstallations[[1]][i]), "gdal*")),
262-
pattern = "\\.(py)$", value = TRUE))
263-
names(gdal_py[[i]]) <- "gdal_py"
242+
243+
MP <- path.expand(MP)
244+
MP <- MP[file.exists(MP)]
245+
246+
if (!length(MP)) {
247+
if (!quiet) message("No valid search mount points.")
248+
return(list(
249+
gdalInstallations = data.frame(
250+
binDir = character(0),
251+
baseDir = character(0),
252+
installation_type = character(0),
253+
stringsAsFactors = FALSE
254+
),
255+
bin = list(),
256+
py = list()
257+
))
258+
}
259+
260+
if (!quiet) {
261+
cat("\nsearching for GDAL installations in:\n")
262+
cat(paste0(" - ", MP, collapse = "\n"), "\n")
263+
}
264+
265+
# ---- helpers ----
266+
normp <- function(p) normalizePath(p, mustWork = FALSE)
267+
268+
# Prefer `gdalinfo` (no extension) on Unix-like systems.
269+
# We search for executable files named gdalinfo (case-insensitive).
270+
find_gdalinfo <- function(root) {
271+
out <- suppressWarnings(try(system2(
272+
"find",
273+
args = c(root, "-type", "f", "-executable", "-iname", "gdalinfo", "-print"),
274+
stdout = TRUE,
275+
stderr = TRUE
276+
), silent = TRUE))
277+
278+
if (inherits(out, "try-error") || !length(out)) return(character(0))
279+
280+
# Remove common noise; keep real paths only.
281+
out <- out[nzchar(trimws(out))]
282+
out <- out[!grepl("Permission denied", out, fixed = TRUE)]
283+
unique(out)
284+
}
285+
286+
# Given a gdalinfo path, derive binDir/baseDir and classify type.
287+
derive_install_row <- function(gdalinfo_path) {
288+
gdalinfo_path <- normp(gdalinfo_path)
289+
binDir <- normp(dirname(gdalinfo_path))
290+
baseDir <- normp(file.path(binDir, ".."))
291+
292+
# Rough classification (best-effort)
293+
installation_type <- "system"
294+
lp <- tolower(gdalinfo_path)
295+
if (grepl("/osgeo4w", lp, fixed = TRUE)) installation_type <- "osgeo4w"
296+
if (grepl("/qgis", lp, fixed = TRUE)) installation_type <- "qgis"
297+
if (grepl("/conda", lp, fixed = TRUE) || grepl("/miniconda", lp, fixed = TRUE)) installation_type <- "conda"
298+
if (grepl("/grass", lp, fixed = TRUE)) installation_type <- "grass"
299+
if (grepl("/otb", lp, fixed = TRUE)) installation_type <- "otb"
300+
301+
data.frame(
302+
binDir = binDir,
303+
baseDir = baseDir,
304+
installation_type = installation_type,
305+
stringsAsFactors = FALSE
306+
)
307+
}
308+
309+
# List candidate tools in binDir
310+
list_bin_and_py <- function(binDir) {
311+
binDir <- normp(binDir)
312+
313+
# "gdal*" excluding *.py
314+
gdal_bins <- character(0)
315+
gdal_pys <- character(0)
316+
317+
if (dir.exists(binDir)) {
318+
# Use Sys.glob to mimic your legacy behaviour but safely
319+
g <- Sys.glob(file.path(binDir, "gdal*"))
320+
if (length(g)) {
321+
g <- normp(g)
322+
gdal_pys <- g[grepl("\\.py$", g, ignore.case = TRUE)]
323+
gdal_bins <- g[!grepl("\\.py$", g, ignore.case = TRUE)]
324+
}
264325
}
265-
} else {
266-
if (!quiet)
267-
cat("Did not find any valid GDAL installation at mount point", MP)
268-
return(gdalInstallations <- FALSE)
326+
327+
list(
328+
bin = data.frame(gdal_bin = gdal_bins, stringsAsFactors = FALSE),
329+
py = data.frame(gdal_py = gdal_pys, stringsAsFactors = FALSE)
330+
)
269331
}
270-
gdal1$gdalInstallations <- gdalInstallations
271-
gdal1$bin <- gdal_bin
272-
gdal1$py <- gdal_py
273-
return(gdal1)
332+
333+
# ---- 2) collect gdalinfo hits ----
334+
hits <- unique(unlist(lapply(MP, find_gdalinfo), use.names = FALSE))
335+
336+
if (!length(hits)) {
337+
if (!quiet) message("::: NO GDAL installation found in given locations.")
338+
return(list(
339+
gdalInstallations = data.frame(
340+
binDir = character(0),
341+
baseDir = character(0),
342+
installation_type = character(0),
343+
stringsAsFactors = FALSE
344+
),
345+
bin = list(),
346+
py = list()
347+
))
348+
}
349+
350+
# ---- 3) build normalized installations table ----
351+
inst_rows <- lapply(hits, derive_install_row)
352+
inst_df <- do.call(rbind, inst_rows)
353+
354+
# De-duplicate by binDir (multiple gdalinfo in same bin)
355+
inst_df <- inst_df[!duplicated(inst_df$binDir), , drop = FALSE]
356+
rownames(inst_df) <- seq_len(nrow(inst_df))
357+
358+
# ---- 4) collect gdal bin/py lists per installation ----
359+
gdal_bin <- vector("list", nrow(inst_df))
360+
gdal_py <- vector("list", nrow(inst_df))
361+
362+
for (i in seq_len(nrow(inst_df))) {
363+
x <- list_bin_and_py(inst_df$binDir[i])
364+
gdal_bin[[i]] <- x$bin
365+
gdal_py[[i]] <- x$py
366+
}
367+
368+
out <- list(
369+
gdalInstallations = inst_df,
370+
bin = gdal_bin,
371+
py = gdal_py
372+
)
373+
374+
if (!quiet) {
375+
message("::: Found ", nrow(inst_df), " GDAL installation(s).")
376+
}
377+
378+
out
274379
}
380+
381+
275382
#'@title Search recursivly existing 'GDAL binaries' installation(s) at a given drive/mountpoint
276383
#'@name findGDAL
277384
#'@description Provides an list of valid 'GDAL' installation(s)
@@ -330,7 +437,7 @@ getrowGDALVer <- function(paths) {
330437
tmp2 <- strsplit(x = tmp, split = ", released ")[[1]][1]
331438
if (tmp2 > highestVer ){
332439
highestVer <- tmp2
333-
pathI <- I
440+
pathI <- i
334441
}
335442
}
336443
}

R/linkGDAL.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ linkGDAL <- function(bin_GDAL = NULL, searchLocation = NULL, ver_select = FALSE,
4141
# ! -readable -prune -o -type f -executable -iname 'gdalinfo' -print'),stdout = TRUE) bin_GDAL <-
4242
# substr(params_GDAL,1,nchar(params_GDAL) - 6) pathGDAL <- bin_GDAL params_GDAL <- searchGDALW() if just one valid
4343
# installation was found take it
44-
if (nrow(params_GDAL$gdalInstallations) != FALSE) {
44+
if (!is.null(params_GDAL$gdalInstallations) && nrow(params_GDAL$gdalInstallations) > 0) {
4545
# if (Sys.info()['sysname'] != 'Windows'){
4646
if (nrow(params_GDAL$gdalInstallations) == 1) {
4747
pathGDAL <- params_GDAL$gdalInstallations[[1]][1]

R/sagaControl.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ findSAGA <- function(searchLocation = "default", quiet = TRUE) {
193193
if (searchLocation == "default") {
194194
searchLocation <- "C:/"
195195
} else {
196-
searchLocation <- normalizePath(searchLocation)
196+
searchLocation <- normalizePath(searchLocation, mustWork = FALSE)
197197
}
198198
if (grepl(paste0(LETTERS, ":", collapse = "|"), substr(toupper(searchLocation), start = 1, stop = 2))) {
199199
link <- link2GI::searchSAGAW(DL = searchLocation, quiet = quiet)

man/searchGDALX.Rd

Lines changed: 22 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)