--- title: "OTB Wrapper in link2GI" author: "Chris Reudenbach" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{OTB Wrapper in link2GI} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ``` knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = FALSE ) ``` The Orfeo ToolBox (OTB) provides a collection of command-line applications for remote sensing and image processing. `link2GI` wraps these applications in R with a focus on **reproducibility, transparency, and robustness across OTB versions**. `link2GI` supports two layers: * a **legacy** wrapper layer (kept for backward compatibility) * a **new Self-describing** API (recommended) that derives parameter metadata from OTB’s own `-help` output # Two APIs: legacy vs. Self-describing ## Legacy API (`parseOTBFunction()` / `runOTB()`) The legacy layer builds a modifiable R list by parsing the output of: ``` otbcli -help ``` Characteristics: * permissive and convenient * minimal upfront constraints * useful for older scripts Limitations: * required parameters and defaults are inferred heuristically * sensitive to OTB help formatting changes * less suitable for strict, version-stable workflows ## Self-describing API (recommended) The Self-describing API treats the OTB CLI help output as the **single source of truth** for parameters and their status (mandatory vs. optional). It supports a strict “write-to-disk” workflow and avoids implicit assumptions. Core functions: * `otb_capabilities()`: retrieve CLI help text (source of truth) * `otb_args_spec()`: normalized parameter spec table parsed from `-help` * `otb_build_cmd()`: create a `runOTB()`-compatible command template (valid keys only) * `otb_set_out()`: set file-based outputs safely (including `[pixel]` outputs) # Minimal workflow (Self-describing) ```{r eval=FALSE} library(link2GI) otb <- link2GI::linkOTB(searchLocation = "~/apps/otb911/") cmd <- link2GI::otb_build_cmd( "DimensionalityReduction", otb, include_optional = "defaults", require_output = TRUE ) cmd[["in"]] <- "in.tif" cmd[["method"]] <- "pca" cmd[["nbcomp"]] <- "3" cmd <- link2GI::otb_set_out(cmd, otb, key = "out", path = "out.tif") str(cmd) ``` # Typical transparent workflow (Self-describing) This example demonstrates a complete workflow: 1. link an installed OTB 2. read and display the raw `-help` output (source of truth) 3. inspect the parsed parameter table (`spec`) 4. build a command template from valid keys 5. set input and explicit output 6. run via `runOTB()` and verify the output file NOTE (CRAN/vignette hygiene): this vignette writes outputs into `tempdir()`. ## Example: `DimensionalityReduction` (PCA) This example avoids guessing parameter names. The only manual step is choosing the correct parameter key for “number of components” by reading the `spec` table printed from your local ```{r eval=FALSE} # OTB build (for OTB 9.1.1 this is `nbcomp`). library(link2GI) library(terra) # 0) Link OTB otb <- link2GI::linkOTB(searchLocation="~/apps/otb911/") # 1) Choose algorithm algo <- "DimensionalityReduction" # 2) Read the OTB help text (source of truth) caps <- link2GI::otb_capabilities(algo = algo, gili = otb, include_param_help = FALSE) cat(paste(head(caps$text, 60), collapse = "\n"), "\n") # 3) Parsed parameter table spec <- link2GI::otb_args_spec(algo, otb) print(spec[, c("key", "class", "mandatory", "default")], row.names = FALSE) # 4) Build a template containing only valid keys (mandatory + defaults) cmd <- link2GI::otb_build_cmd( algo, otb, include_optional = "defaults", require_output = TRUE ) # 5) Identify component-count key by inspecting the spec table (no heuristics) pca_related <- spec[grepl("^method\.pca\.|^nbcomp$|outdim|comp", spec$key), ] print(pca_related[, c("key", "mandatory", "default", "desc")], row.names = FALSE) # 6) Set PCA method + number of components (OTB 9.1.1: nbcomp) cmd[["method"]] <- "pca" cmd[["nbcomp"]] <- "3" # 7) Input + output (explicit on-disk paths) infile <- system.file("ex/elev.tif", package = "terra") out_dir <- file.path(tempdir(), "link2gi_otb_vignette") dir.create(out_dir, recursive = TRUE, showWarnings = FALSE) cmd[["in"]] <- infile cmd[["out"]] <- file.path(out_dir, "pca.tif") # Show the exact CLI that would be executed cat(link2GI::runOTB(cmd, otb, retCommand = TRUE), "\n") # Run (writes to disk) res <- link2GI::runOTB(cmd, otb, quiet = FALSE) # Verify output exists file.exists(cmd[["out"]]) ``` # Legacy workflow (supported but not recommended) The legacy workflow builds a command list by parsing `-help` output and then executes it. Because the legacy parser may expose parameter names that vary between OTB versions/apps, the safe pattern is: 1. parse the command list 2. inspect `names(cmd)` / `cmd$help` (if present) 3. set inputs/outputs and required parameters explicitly 4. run ```{r eval=FALSE} library(link2GI) otb <- link2GI::linkOTB() algo <- "EdgeExtraction" cmd <- link2GI::parseOTBFunction(algo = algo, gili = otb) # inspect keys (legacy API may expose app-specific names) names(cmd) # then set required parameters according to the returned structure: # cmd[[""]] <- "in.tif" # cmd[[""]] <- "out.tif" # cmd[[""]] <- "value" # finally execute # link2GI::runOTB(cmd, gili = otb, quiet = FALSE) ``` # What changed (Self-describing) The new Self-describing API removes fragile heuristics and centralizes metadata on: * OTB’s own `Parameters:` block emitted by ` -help` Consequences: * `otb_capabilities()` / `otb_args_spec()` become the source of truth * `otb_build_cmd()` prevents invalid keys (reduces “parameter does not exist” failures) * `runOTB()` on Linux/macOS uses the OTB launcher plus explicit environment, matching the help/spec stack * Windows isolation is handled by `runOTB_isolated()` via `otbenv.ps1/.bat` # Compatibility and deprecations ## Compatibility * Existing scripts using `parseOTBFunction()` plus `runOTB()` should continue to work (best-effort). * The Self-describing API is stable as long as OTB provides a parseable `Parameters:` section. ## Deprecations (explicit list) * `parseOTBFunction()` is legacy (supported, but not recommended for new code). * `parseOTBAlgorithms()` is legacy (if present in your build; prefer `otb_capabilities()`). For new projects, prefer: `otb_capabilities()` + `otb_args_spec()` + `otb_build_cmd()` + `otb_set_out()` then `runOTB()`.