Skip to main content

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:

ClassConstructorDescription
Identitynew Identity(input?, output?)Identity matrix
Scalenew Scale(scale[], input?, output?)Scale transform
Translationnew Translation(translation[], input?, output?)Translation
Affinenew Affine(matrix[][], input?, output?)Full affine
Sequencenew Sequence(transforms[], input?, output?)Composed transforms
Rotationnew Rotation(matrix[][], input?, output?)Rotation matrix (square, no translation)
MapAxisnew 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 the ZarrTree, returning a Result
  • serializeZarrTree(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.