Types

The R Spade library uses S4 classes to represent typed inputs and outputs. If you have not encountered S4 before: S4 is R's formal object-oriented system, defined with setClass(). Each S4 object has named slots (similar to fields or attributes in other languages) that you access with the @ operator instead of $. The Spade library defines these classes for you -- you only need to know how to read their slots and construct them for return values.

File types🔗

All file types inherit from the base File class and carry a single slot, @path, containing the absolute path to the file on disk.

ClassDescriptionManifest mapping
FileGeneric file of any formattype: file
RasterFileRaster data (GeoTIFF, etc.)type: file, format: GeoTIFF
VectorFileVector data (GeoJSON, etc.)type: file, format: GeoJSON
TabularFileTabular data (CSV, etc.)type: file, format: CSV
JsonFileJSON filetype: json
ModelFileA fitted model (since 0.4)type: file, format: SpadeModel

Using file types🔗

When the library delivers a file input to your handler, you access the path through the @path slot:

handler <- function(source) {
  # Read the file using any R package
  r <- terra::rast(source@path)
  # ...
}
spade_types(handler) <- list(source = "RasterFile")

When returning a file output, construct the appropriate type with its constructor function:

out_path <- file.path(tempdir(), "result.tif")
terra::writeRaster(result, out_path, overwrite = TRUE)
RasterFile(path = out_path)

Each constructor (File(), RasterFile(), VectorFile(), TabularFile(), JsonFile()) takes a single path argument.

Directory type🔗

The Directory class represents a directory input or output. Like file types, it carries a @path slot pointing to the directory.

ClassDescriptionManifest mapping
DirectoryA directory of filestype: directory
handler <- function(tile_dir) {
  tifs <- list.files(tile_dir@path, pattern = "\\.tif$", full.names = TRUE)
  # process each file ...
}
spade_types(handler) <- list(tile_dir = "Directory")

Collection types🔗

Collection types represent multiple files. They carry a @paths slot -- a character vector of file paths.

ClassDescriptionManifest mapping
FileCollectionGeneric file collectiontype: collection, item_type: file
RasterFileCollectionCollection of raster filestype: collection, item_type: file, format: GeoTIFF
VectorFileCollectionCollection of vector filestype: collection, item_type: file, format: GeoJSON
TabularFileCollectionCollection of tabular filestype: collection, item_type: file, format: CSV

Using collection types🔗

The @paths slot is a standard character vector, so you can iterate over it with lapply(), for, or any other R idiom:

handler <- function(tiles) {
  rasters <- lapply(tiles@paths, terra::rast)
  merged <- do.call(terra::merge, rasters)
  # ...
}
spade_types(handler) <- list(tiles = "RasterFileCollection")

To return a collection, pass the character vector of output paths to the constructor:

out_paths <- c("outputs/a.tif", "outputs/b.tif")
RasterFileCollection(paths = out_paths)

Scalar types🔗

Scalar parameters come from params.yaml and map to ordinary R types. You do not need to construct S4 objects for scalars -- the library passes them as plain R values.

Type annotationR typeManifest mapping
"numeric"numerictype: number
"integer"integertype: number
"character"charactertype: string
"logical"logicaltype: boolean
handler <- function(source, buffer, method, verbose) {
  if (verbose) message("Using method: ", method)
  # buffer is a plain numeric, method is a character string
  # ...
}
spade_types(handler) <- list(
  source   = "RasterFile",
  buffer   = "numeric",
  method   = "character",
  verbose  = "logical",
  .return  = "RasterFile"
)

Inheritance hierarchy🔗

The S4 class hierarchy is straightforward:

File
 ├── RasterFile
 ├── VectorFile
 ├── TabularFile
 └── JsonFile

Directory

FileCollection
 ├── RasterFileCollection
 ├── VectorFileCollection
 └── TabularFileCollection

All file subtypes inherit from File, so any function that accepts a File also works with RasterFile, VectorFile, and so on. The same holds for collection subtypes and FileCollection.

Validation🔗

The library validates objects at construction time. A File or Directory must have a non-empty, single-element character string for @path. A FileCollection must have a character vector for @paths. Invalid objects raise an error immediately:

File(path = "")
#> Error: path must be a non-empty single character string

File(path = c("a.tif", "b.tif"))
#> Error: path must be a non-empty single character string

Tables, columns and models🔗

Since spadelib 0.4.

A column selector is the string a user writes to name columns of a table input: comma-separated names, globs (*, ?, [...], matched against whole column names), backtick-quoted literals for names containing ,, * or a leading -, and - exclusions. B*,MB*,-B0 is every B and MB column except B0; if the first item is an exclusion, the selection starts from every eligible column. Selection is strict: a name that is not a column, a glob that matches nothing, or an exclusion that removes nothing is an error, and the message suggests near matches and lists the columns that do exist -- so a user who did not know the upstream schema learns it from the first failed run. Columns passed as reserved (the target, a weights column) are skipped by globs and refused by name, with the role in the message.

library(spadelib)

handler <- function(data, target, features) {
  df <- spade_read_table(data, as_string = c("COUNTYCD"))
  cols <- spade_select_columns(df, features, reserved = c(setNames("the target", target)),
                               input = "data")
  ...
}
FunctionDescription
spade_select_columns(table, selector, reserved = NULL, input = NULL)Resolve a selector against a character vector of names or a data frame. reserved is a named character vector, e.g. c(ESTIMATE = "the target").
spade_require_column(table, name, input = NULL)Return name if the table has it; otherwise signal the same error.
spade_read_table(file, as_string = character())Read a CSV or Parquet table (chosen by extension) into a data.frame, keeping column names exactly. as_string keeps identifier columns as text when reading CSV.
spade_write_table(df, path, like = NULL)Write CSV or Parquet by extension, or in the format of like. Returns a TabularFile.
spade_table_format(file)"parquet" or "csv", from the extension.
spade_read_model(file, input = "model")Read a SpadeModel archive; returns list(envelope, payload).
spade_write_model(path, envelope, payload = NULL)Write a SpadeModel archive, byte-stable, with doubles at 17 significant digits; returns a ModelFile.

Failures signal a condition of class spade_column_error, whose $kind is one of not_found, no_match, reserved, removes_nothing, syntax.

Parquet needs the nanoparquet package, and model I/O needs jsonlite and zip; spadelib lists them under Suggests, so a collection that uses these functions adds them to its own DESCRIPTION Imports.

ModelFile is the type of a fitted model (type: file, format: SpadeModel).

The selector grammar, the error messages and the model archive are specified in spec/models.md (§4 and §6); a shared fixture keeps the Python, R and Rust implementations identical.