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.
| Class | Description | Manifest mapping |
|---|---|---|
File | Generic file of any format | type: file |
RasterFile | Raster data (GeoTIFF, etc.) | type: file, format: GeoTIFF |
VectorFile | Vector data (GeoJSON, etc.) | type: file, format: GeoJSON |
TabularFile | Tabular data (CSV, etc.) | type: file, format: CSV |
JsonFile | JSON file | type: json |
ModelFile | A 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.
| Class | Description | Manifest mapping |
|---|---|---|
Directory | A directory of files | type: 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.
| Class | Description | Manifest mapping |
|---|---|---|
FileCollection | Generic file collection | type: collection, item_type: file |
RasterFileCollection | Collection of raster files | type: collection, item_type: file, format: GeoTIFF |
VectorFileCollection | Collection of vector files | type: collection, item_type: file, format: GeoJSON |
TabularFileCollection | Collection of tabular files | type: 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 annotation | R type | Manifest mapping |
|---|---|---|
"numeric" | numeric | type: number |
"integer" | integer | type: number |
"character" | character | type: string |
"logical" | logical | type: 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
└── TabularFileCollectionAll 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 stringTables, 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")
...
}| Function | Description |
|---|---|
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.