Examples
Three complete examples demonstrating common block patterns: raster processing, tabular analysis, and a map block that processes items in parallel.
Example 1: NDVI calculation from multispectral raster🔗
This block reads a multispectral GeoTIFF, computes the Normalized Difference Vegetation Index (NDVI), and writes the result as a single-band raster.
Handler🔗
library(spade)
handler <- function(source, red_band, nir_band) {
r <- terra::rast(source@path)
red <- r[[red_band]]
nir <- r[[nir_band]]
ndvi <- (nir - red) / (nir + red)
out_path <- file.path(tempdir(), "ndvi.tif")
terra::writeRaster(ndvi, out_path, overwrite = TRUE)
RasterFile(path = out_path)
}
spade_types(handler) <- list(
source = "RasterFile",
red_band = "integer",
nir_band = "integer",
.return = "RasterFile"
)
attr(handler, "spade_description") <- "Compute NDVI from a multispectral raster."
run(handler)params.yaml🔗
red_band: 3
nir_band: 4Generated manifest🔗
cat(yaml::as.yaml(build(handler)))description: Compute NDVI from a multispectral raster.
inputs:
source:
type: file
format: GeoTIFF
red_band:
type: number
nir_band:
type: number
outputs:
raster:
type: file
format: GeoTIFFHow it works🔗
sourcearrives as aRasterFile. The handler reads it withterra::rast()via the@pathslot.red_bandandnir_bandare integers fromparams.yaml, used to select raster layers by index.- The handler returns a single
RasterFile, which the library writes tooutputs/raster/.
Example 2: CSV summary statistics🔗
This block reads a CSV file, computes summary statistics for a specified column, and writes the results as JSON.
Handler🔗
library(spade)
handler <- function(data, column, na_rm) {
df <- read.csv(data@path)
if (!(column %in% names(df))) {
stop("Column '", column, "' not found in input CSV.")
}
values <- df[[column]]
if (!is.numeric(values)) {
values <- suppressWarnings(as.numeric(values))
}
stats <- list(
column = column,
n = length(values),
n_valid = sum(!is.na(values)),
mean = mean(values, na.rm = na_rm),
sd = sd(values, na.rm = na_rm),
min = min(values, na.rm = na_rm),
max = max(values, na.rm = na_rm),
q25 = unname(quantile(values, 0.25, na.rm = na_rm)),
q50 = unname(quantile(values, 0.50, na.rm = na_rm)),
q75 = unname(quantile(values, 0.75, na.rm = na_rm))
)
out_path <- file.path(tempdir(), "summary.json")
jsonlite::write_json(stats, out_path, auto_unbox = TRUE, pretty = TRUE)
JsonFile(path = out_path)
}
spade_types(handler) <- list(
data = "TabularFile",
column = "character",
na_rm = "logical",
.return = "JsonFile"
)
attr(handler, "spade_description") <- "Compute summary statistics for a CSV column."
run(handler)params.yaml🔗
column: temperature
na_rm: trueGenerated manifest🔗
description: Compute summary statistics for a CSV column.
inputs:
data:
type: file
format: CSV
column:
type: string
na_rm:
type: boolean
outputs:
json:
type: jsonHow it works🔗
dataarrives as aTabularFile. The handler reads it withread.csv().columnandna_rmare scalars fromparams.yaml.- The handler computes statistics using base R functions and writes the result as JSON.
- Returning a
JsonFilecauses the library to write it tooutputs/json/.
Example 3: Tile merger (map block)🔗
This block demonstrates working with a collection of raster tiles. It receives multiple GeoTIFF files, merges them into a single raster, and also produces a JSON metadata sidecar.
Handler🔗
library(spade)
handler <- function(tiles, nodata) {
# Load all tiles from the collection
rasters <- lapply(tiles@paths, terra::rast)
# Merge into a single raster
if (length(rasters) == 1) {
merged <- rasters[[1]]
} else {
merged <- do.call(terra::merge, rasters)
}
# Replace nodata sentinel
merged[merged == nodata] <- NA
# Write the merged raster
raster_path <- file.path(tempdir(), "merged.tif")
terra::writeRaster(merged, raster_path, overwrite = TRUE)
# Write metadata
meta <- list(
n_tiles = length(tiles@paths),
crs = terra::crs(merged, describe = TRUE)$code,
extent = as.list(as.vector(terra::ext(merged))),
n_cells = terra::ncell(merged),
res = as.list(terra::res(merged))
)
meta_path <- file.path(tempdir(), "metadata.json")
jsonlite::write_json(meta, meta_path, auto_unbox = TRUE, pretty = TRUE)
# Return multiple outputs as a named list
list(
merged = RasterFile(path = raster_path),
metadata = JsonFile(path = meta_path)
)
}
spade_types(handler) <- list(
tiles = "RasterFileCollection",
nodata = "numeric"
)
attr(handler, "spade_description") <- "Merge raster tiles and produce metadata."
run(handler)params.yaml🔗
nodata: -9999Block manifest🔗
id: raster-tools.merge-tiles
version: 0.1.0
kind: standard
network: false
description: Merge raster tiles and produce metadata.
inputs:
tiles:
type: collection
item_type: file
format: GeoTIFF
nodata:
type: number
outputs:
merged:
type: file
format: GeoTIFF
metadata:
type: jsonHow it works🔗
tilesarrives as aRasterFileCollection. The@pathsslot is a character vector, solapply()iterates over each file path to load them withterra::rast().nodatais a numeric scalar fromparams.yaml.- The handler returns a named list with two entries, producing two outputs: the merged raster at
outputs/merged/and the metadata JSON atoutputs/metadata/. - The keys of the returned list (
merged,metadata) correspond to the output names declared in the block manifest.
Patterns to note🔗
Across all three examples, the structure is consistent:
- Load inputs by reading from the
@pathor@pathsslot of the S4 objects. - Read scalars directly -- they arrive as plain R values.
- Process using any R package (
terra,jsonlite, base R, etc.). - Write results to temporary files, then wrap them in the appropriate Spade type constructor.
- Return a single typed object or a named list for multiple outputs.
run(handler)at the end handles everything else.