@@ -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(" \n searching 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(" \n searching 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 }
0 commit comments