Skip to main content

SpatialCanvas and images — status and roadmap

@spatialdata/vis includes SpatialCanvas, a React UI for picking a coordinate system, toggling layers, and panning/zooming. It can render OME-Zarr images together with shapes and points in one deck.gl + Viv view.

Scope today: 2D only. There is no 3D scene rendering — no orbit navigation, pitch/bearing, or volume views. See Known limitations.

What works​

  • Combined canvas: Image layers (via Viv DetailView and loadOmeZarr) draw under vector layers; composition follows the same pattern as MDV’s Viv + deck stacking.
  • Coordinate system selection and layer visibility toggles from the SpatialData store.
  • Advanced API: You can pass per-image channel settings on the spatial canvas store using LayerConfig.channels (colors, contrast limits, channel visibility, z/c/t selections). See the SpatialCanvas JSDoc example in the source package.

Known limitations​

  • No 3D rendering: SpatialCanvas, SpatialCanvasViewer, and the underlying Viv/deck stack are 2D Cartesian only. The viewer uses Viv DetailView (orthographic pan/zoom), not OrbitView or volume rendering. You can select an image z slice (or c / t) through LayerConfig.channels, but that is OME axis indexing — not 3D scene navigation. ViewState includes a ViewState3D type stub, but pitch, bearing, and orbit state are not wired through to rendering. 3D is not supported for MDV v1 integration, but a high priority following that.
  • Channel UI is basic: the properties pane exposes per-channel RGB (0–255), contrast min/max, z/c/t, and visibility (up to six channels). There is no histogram or brush yet; defaults from automatic stats can still look wrong until you tune values.
  • Single primary image path in the Viv viewer composition (first enabled image layer drives DetailView.getLayers).
  • useLayerData prefers explicit LayerConfig.channels when arrays are non-empty; further override flags may still be useful for edge cases.
  • Points are loaded by core's PointsResolver and drawn by PointsLayer through PointsDataEngine (both described in layers): a resident preload window under a memory cap, or viewport tiles for Morton-sorted and points-zarr elements, with a feature catalog and filter and colour by feature. See Loading points and shapes. A GeoArrow-backed fast path is still open.
  • Shapes load non-blocking: geometry refines an already-painted canvas.

Upstream Viv Follow-ups​

  • Labels currently carry local Viv workarounds: SpatialCanvas labels use local single-scale and multiscale wrappers instead of Viv ImageLayer / MultiscaleImageLayer because the current Viv prop schemas reject null callback defaults such as onHover, onClick, and onViewportLoad.
  • Viv interpolation prop typing also needs cleanup: current Viv layer defaults still type interpolation incorrectly on some paths, which triggers deck.gl prop validation when labels request 'nearest' / 'linear'.
  • Shader path should converge back toward upstream Viv: labels already use GLSL 300 ES + UBO-style uniforms locally; once the Viv shader/toolchain work is published we should revisit these wrappers and delete any compatibility code that is no longer needed.

Architecture​

  • Resource Resolver (ADR 0004). useLayerData drives a kind-blind SpatialEntryStore.reconcile() over per-kind resolvers (PointsResolver / ShapesResolver in core, ImagesResolver / LabelsResolver in vis). See Resource Resolver and ADR 0004. Per-layer typed errors and notices surface as getEntryStatus (headless guide).
  • Feature/table split. Core owns row alignment (FeatureTableAlignment); @spatialdata/layers owns the column-to-colour encoders; SpatialCanvas consumes both (details).
  • @spatialdata/avivatorish holds the shared loaders and Zustand stores.
  • SpatialCanvas has a loader registry context (VivLoaderRegistryProvider), layers and properties sidebars, drag-and-drop layer order, fullscreen, and channel controls for image layers.

Headless embedding​

SpatialCanvasViewer and useSpatialCanvasRenderer are exported for controlled, UI-free embedding (MDV, Vitessce, local experiments). See Headless viewer guide.

Near-term roadmap​

  • Histogram (optional) and richer channel UX (MDV parity).
  • Remove the deprecated ImageView (#215).
  • Feature/table foundations: tooltip and pick-event row resolution now route through the shared core resolver shape; remaining work is closing the per-element tooltip cache ping-pong (two layers over one element with different tooltipFields invalidate each other — Track B) and adding annotation column discovery for direct shape annotations and future matrix-backed values. See Feature table associations and annotation columns.

Medium-term roadmap​

  • Grow SpatialLayer beyond shapes sublayers, keeping SpatialLayerProps migrations honest.
  • Harden @spatialdata/avivatorish for MDV adoption (telemetry hooks, docs).
  • MDV integration checklist: replace vendored avivatorish, adopt shared layers, scatter/table-backed props, phased contour extraction.
  • GeoArrow / Parquet paths for shapes and points; clarify @spatialdata/core vs deck-facing buffers.
  • 3D view mode and time (t) in the public scene contract.

Core and fixtures​

  • Test fixtures cover spatialdata 0.5.0, 0.6.1, 0.7.2 and 0.8.0.
  • NGFF unit schemas and axis-aware translation/scale transforms live in @spatialdata/core (see Transformations).