Internals & Architecture
:::caution For Contributors & Advanced Users
This page documents internal implementation details of @spatialdata/core. These APIs are not part of the public interface and may change without notice.
If you're building an application with this library, you likely don't need anything on this page—see the other documentation sections instead.
:::
Module Architecture
The package is organized into these internal modules:
@spatialdata/core/src/
├── store/ # SpatialData class, readZarr entry point
├── models/ # Element classes and factory (index.ts), plus the loaders:
│ # VShapesSource, VPointsSource, VTableSource, VZarrDataSource
├── engine/ # Resource Resolver: resolvers, SpatialEntryStore, Resolution, errors
├── memory/ # Memory accounting (ADR 0005)
├── schemas/ # Zod validation schemas (not exported)
├── transformations/
│ ├── transformations.ts # Transformation classes
│ └── operations.ts # Helper functions
├── workers/ # Parquet worker, client and protocol
└── types.ts # Core type definitions
Points, shapes and tile loading live in top-level points*.ts and shapes*.ts
modules; see Loading points and shapes and the
Resource Resolver.
Loader divergences
VShapesSource.ts began as a copy of a Vitessce loader and has diverged: it is
TypeScript, and newer ngff:shapes metadata revisions take the same
parquet-backed path as older ones rather than being keyed to one version
string. Stable feature-id loading (ShapesElement.loadFeatureIds()) is the
behaviour to preserve. Each further divergence is documented in the file's
header; keep it scoped to geometry and feature-id loading and put vis policy
elsewhere.
TableElement.getAnnDataJS() is exposed, but loadObsIndex() and
loadObsColumns() go through the direct zarr/parquet path in VTableSource.ts.
They sit on the hot path for table-to-feature association and need to keep
working when anndata.js lags on zarr or string-dtype support.
Element Factory (Internal)
Elements are created internally when loading stores. These functions are not intended for application use:
// Internal - do not use directly
import { createElement, loadElements } from '@spatialdata/core';
// createElement instantiates a single element from parsed metadata
const element = createElement('images', sdataProps, 'my_image');
// loadElements creates all elements of a type from the parsed store
const allImages = loadElements(sdataProps, 'images');
Application code should always obtain elements via readZarr():
// Correct usage
const sdata = await readZarr('https://example.com/data.zarr');
const image = sdata.images?.['my_image'];
Transformation Parsing
The transformation system parses NGFF coordinate transformations from zarr metadata into typed classes:
parseTransformEntry
Parses a single transformation object from metadata:
import { parseTransformEntry } from '@spatialdata/core';
const entry = { type: 'scale', scale: [0.5, 0.5] };
const transform = parseTransformEntry(entry);
// Returns: Scale { scale: [0.5, 0.5] }
parseTransforms
Parses an array of transformations, returning a single BaseTransformation:
import { parseTransforms } from '@spatialdata/core';
const transforms = parseTransforms([
{ type: 'scale', scale: [0.5, 0.5] },
{ type: 'translation', translation: [1000, 2000] },
]);
// Returns: Sequence containing Scale and Translation
buildMatrix4FromTransforms
Convenience function to get a Matrix4 directly from metadata:
import { buildMatrix4FromTransforms } from '@spatialdata/core';
const matrix = buildMatrix4FromTransforms([
{ type: 'scale', scale: [2, 2] },
]);
composeTransforms
Composes element-level and dataset-level transforms:
import { composeTransforms } from '@spatialdata/core';
const fullMatrix = composeTransforms(elementTransforms, datasetTransforms);
Transformation Classes
These classes represent parsed transformations internally. They're returned by element methods but shouldn't be constructed directly in application code:
| Class | Constructor | Description |
|---|---|---|
Identity | new Identity(input?, output?) | Identity matrix |
Scale | new Scale(scale[], input?, output?) | Scale transform |
Translation | new Translation(translation[], input?, output?) | Translation |
Affine | new Affine(matrix[][], input?, output?) | Full affine |
Sequence | new Sequence(transforms[], input?, output?) | Composed transforms |
Rotation | new Rotation(matrix[][], input?, output?) | Rotation matrix (square, no translation) |
MapAxis | new MapAxis(mapAxis[], input?, output?) | Axis remapping/permutation |
Not yet implemented: byDimension, bijection, displacements, and affine/rotation
"by path" (parameters stored in an external zarr array). See
Coordinate Transformations
for the current support summary, and
dev_scripts/conformance-dingus/ for the RFC-5 conformance harness that tracks
this backlog.
All classes extend BaseTransformation and provide:
abstract class BaseTransformation {
readonly input?: CoordinateSystemRef;
readonly output?: CoordinateSystemRef;
abstract toArray(): number[]; // 16-element column-major
toMatrix(): Matrix4; // Matrix4 from @math.gl/core
inverse(): Matrix4; // Inverted Matrix4; throws if singular
abstract get type(): string;
}
Type Utilities
Type aliases for working with elements generically:
import type {
ElementInstanceMap, // Maps element names to class types
SpatialElement, // Union of spatial element types
AnyElement, // Union of all element types
ElementName, // 'images' | 'shapes' | 'labels' | 'points' | 'tables'
} from '@spatialdata/core';
Zod Schemas
Metadata schemas live in schemas/index.ts and are not exported from the
package: coordinateTransformationSchema and the image schema, plus
rasterAttrsSchema, shapesAttrsSchema, pointsAttrsSchema and
tableAttrsSchema (re-exported from models/). They validate and type
metadata during element construction; an element whose attributes fail
validation is not created, and is reported through the onBadFiles handler
passed to readZarr.
Store Parsing
Parsing zarr store contents lives in zarrextra, not in core:
openExtraConsolidated(source)- Resolves consolidated metadata and builds theZarrTree, returning aResultserializeZarrTree(tree)- Serializes for JSON output, converting the symbol-keyed attributes and array metadata to string keys
Element construction walks that tree with the guards and accessors described in
tree nodes — AbstractElement narrows an element's own
node to a group once, so no element subclass has to discriminate group from array
itself.
Result Type
Result and its utilities are implemented in zarrextra and re-exported from
@spatialdata/core; see Error Handling.