Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .changeset/consume-resource-resolvers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
"@spatialdata/vis": patch
---

useLayerData consumes the Resource Resolvers via a single reconcile loop.

`useLayerData` now drives layer loading through `@spatialdata/core`'s
`SpatialEntryStore.reconcile()` over per-kind `ResourceResolver`s — `PointsResolver`
/ `ShapesResolver` from `core`, `ImagesResolver` / `LabelsResolver` from `vis` —
instead of the previous per-kind `Promise.all` load switch. Shapes geometry/tooltip/
fill-colour rows, image and labels channel defaults, and points preload are all read
from their resolvers; points continue to run through the stable `PointsDataEngine`,
which the store borrows via a non-owning proxy so a dataset swap does not dispose it.

Purely an internal restructuring behind ADR 0004 (Step 1 consumption): the 17-member
public surface is unchanged and guarded by `useLayerData.spec.tsx`.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
"${workspaceFolder}/python",
"${workspaceFolder}/python/spatialdata-codec-writer/scripts"
],
"typescript.tsdk": "node_modules/typescript/lib"
"typescript.tsdk": "node_modules/typescript/lib",
"js/ts.experimental.useTsgo": false
}
34 changes: 24 additions & 10 deletions docs/docs/vis/spatial-canvas-status.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ sidebar_position: 1
- **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 still a minimal scatter path:** `PointsElement.loadPoints()`
loads coordinate columns into an ndarray-ish object and
`@spatialdata/vis` renders them directly with `ScatterplotLayer`. There is
not yet stable point identity, table-backed feature state, viewport/row-group
filtering, progressive multi-file Parquet loading, or a GeoArrow-backed
renderer.
- **Points render through a data engine, not a raw scatter path:**
`PointsElement.loadPoints()` feeds `PointsDataEngine` (in `@spatialdata/layers`,
with its cache/lifecycle half — `PointsResolver` — in `@spatialdata/core`), which
owns a resident preload window with a configurable memory cap, a full-dataset
feature catalog, per-row feature codes, and a whole-dataset feature-index scan that
streams matching points in progressively. Rendering is via the `@spatialdata/layers`
`PointsLayer` composite with table-backed feature state (filter and colour by
feature). Still open: a GeoArrow-backed fast path, and viewport/tile-based loading
for points (shapes and points tiling are marked *Future* in ADR 0002/0003).

## Upstream Viv Follow-ups

Expand All @@ -37,13 +40,22 @@ sidebar_position: 1

- [`packages/vis/src/SpatialCanvas/index.tsx`](https://github.com/Taylor-CCB-Group/SpatialData.js/blob/main/packages/vis/src/SpatialCanvas/index.tsx) — `SpatialCanvas` UI and store wiring
- [`packages/vis/src/SpatialCanvas/VivSpatialViewer.tsx`](https://github.com/Taylor-CCB-Group/SpatialData.js/blob/main/packages/vis/src/SpatialCanvas/VivSpatialViewer.tsx) — Viv + deck layer composition
- [`packages/vis/src/SpatialCanvas/useLayerData.ts`](https://github.com/Taylor-CCB-Group/SpatialData.js/blob/main/packages/vis/src/SpatialCanvas/useLayerData.ts) — Loaders and Viv layer props
- [`packages/vis/src/SpatialCanvas/useLayerData.ts`](https://github.com/Taylor-CCB-Group/SpatialData.js/blob/main/packages/vis/src/SpatialCanvas/useLayerData.ts) — the Resource Resolver reconcile loop (`SpatialEntryStore`) and Viv layer props
- [`packages/vis/src/SpatialCanvas/types.ts`](https://github.com/Taylor-CCB-Group/SpatialData.js/blob/main/packages/vis/src/SpatialCanvas/types.ts) — `LayerConfig`, `ChannelConfig`
- [`packages/core/src/tableAssociations.ts`](https://github.com/Taylor-CCB-Group/SpatialData.js/blob/main/packages/core/src/tableAssociations.ts) — feature/table row alignment following Python `spatialdata` semantics
- [`packages/layers/src/shapeColorEncoding.ts`](https://github.com/Taylor-CCB-Group/SpatialData.js/blob/main/packages/layers/src/shapeColorEncoding.ts) — renderer-agnostic shape column-to-colour encoding over resolved row alignment

## Recently landed (check repo for details)

- **Resource Resolver + reconcile loop (ADR 0004).** `@spatialdata/core` defines the
`ResourceResolver` interface, `Resolution<T>`, `SpatialEntryError` / `EntryNotice`,
and a kind-blind `SpatialEntryStore` reconcile loop. `useLayerData` now *consumes*
per-kind resolvers — `PointsResolver` / `ShapesResolver` in `core`, `ImagesResolver`
/ `LabelsResolver` in `vis` — through a single `store.reconcile()` commit-effect
instead of a per-kind load switch, with its 17-member public surface unchanged. See
[ADR 0004](https://github.com/Taylor-CCB-Group/SpatialData.js/blob/main/docs/adr/0004-resource-resolver-owned-by-core.md)
and the resolver handoff / Step 1 consumption plans; Step 2 (points state model,
shapes loader seam, memory) and Step 3 (renderer-adapter cleanup) remain.
- **`@spatialdata/avivatorish`** — shared loaders and Zustand stores; SpatialCanvas / ImageView import from the package.
- **`@spatialdata/layers`** — **`SpatialLayer`** + Zod **`SpatialLayerProps`** + **`migrateSpatialLayerProps`** (sublayer wiring still to grow).
- **Feature/table split** — `@spatialdata/core` now exposes a
Expand All @@ -63,9 +75,11 @@ controlled, UI-free embedding (MDV, Vitessce, local experiments). See

- **Histogram** (optional) and richer channel UX (MDV parity).
- **Fold or replace `ImageView`** once SpatialCanvas covers the single-image case well.
- **Feature/table foundations:** finish routing tooltip and pick-event row
resolution through the shared core resolver shape, then add annotation column
discovery for direct shape annotations and future matrix-backed values.
- **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](./feature-table-associations).

## Medium-term roadmap
Expand Down
36 changes: 33 additions & 3 deletions docs/plans/resource-resolver-handoff.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Resource Resolver — implementation handoff

**Status:** ready for implementation
**Status:** Step 0 + Step 1 **landed** — see [Progress](#progress-2026-07-15). Step 2
(Tracks A / B / C) and Step 3 (Renderer Adapter cleanup) remain.
**Decisions:** [ADR 0004 — Resource Resolver Owned By Core](../adr/0004-resource-resolver-owned-by-core.md), [ADR 0005 — Memory Accounting Before Management](../adr/0005-memory-accounting-before-management.md)
**Supersedes:** [layer-data-engine-decomposition.md](layer-data-engine-decomposition.md)
**Vocabulary:** [CONTEXT.md](../../CONTEXT.md) — *Resource Resolver, Renderer Adapter, Spatial Entry, Resolution, Spatial Entry Error, Entry Notice, Encoded/Decoded Tier, Resource Ceiling*
Expand All @@ -9,6 +10,32 @@ Read the two ADRs first. This document is sequencing, not rationale.

---

## Progress (2026-07-15)

- **Step 0 — shared contracts: landed** (#85). `Resolution<T>`, `SpatialEntryError`,
`EntryNotice`, `toSpatialEntryError`, `fromResult` in `packages/core/src/engine`.
- **Step 1 — interface + four adapters: landed** (#85, unconsumed) **and now
consumed** (branch `claude/resource-resolver-adr-88a5c8`). `PointsResolver` /
`ShapesResolver` in `core`, `ImagesResolver` / `LabelsResolver` in `vis`, plus
`SpatialEntryStore`. `useLayerData` is now a single `store.reconcile(contexts)`
commit-effect over per-kind resolvers instead of the ~400-line `Promise.all`
kind-switch; its 17-member public surface is unchanged and guarded by
`useLayerData.spec.tsx`. Executed per [step1-consumption-tactics.md](step1-consumption-tactics.md).
- Points is owned by the stable `PointsDataEngine` (panels subscribe to it), so the
store holds it through a **non-owning proxy** whose `dispose` is a no-op — a
deliberate exception to the store's ownership model, documented at the
`createNonOwningResolver` call site.
- Deferred within Step 1 by design: the render-phase
`pointsEngine.ensureMatchingFeaturesLoaded` / `ensureRowFeatureCodes` calls in
`getLayers` stay put (they migrate into `plan()` under Track A), so the points
reconcile context carries only the memory cap.
- **Remaining:** Step 2 Tracks A (points state model / `RequestSlot` / races R1–R5),
B (shapes loader seam + tooltip ping-pong), C (memory, ADR 0005 rungs 1–3); Step 3
(Renderer Adapter `project()`/`render()`, `'use no memo'` removal, dead-surface
cleanup). None started.

---

## The shape

```text
Expand Down Expand Up @@ -53,7 +80,7 @@ defence in `PointsDataEngine` disappears with it.

## Sequence

### Step 0 — shared contracts (land first, land alone)
### Step 0 — shared contracts (land first, land alone) — ✅ LANDED (#85)

`packages/core/src/engine/{resolution,errors}.ts`. The types, plus the two small
functions that are inseparable from them. No imports beyond `core`'s own.
Expand All @@ -72,7 +99,7 @@ Also here: `fromResult()`, a three-liner lifting `getTransformation`'s existing
> **Do not** put `Resolution` in `zarrextra`. Do not push it down into `core`'s leaf
> loaders — they keep throwing, and the resolver classifies at the seam.

### Step 1 — the resolver interface + four thin adapters (shared; the fork point)
### Step 1 — the resolver interface + four thin adapters (shared; the fork point) — ✅ LANDED (#85 + consumption)

Extract the interface **from the shape `PointsDataEngine` already has**, generalised.
Write `Shapes` / `Images` / `Labels` resolvers as **thin adapters holding today's
Expand All @@ -95,6 +122,9 @@ race fixes, no memory work.

`useLayerData` becomes a loop over resolvers instead of a switch over kinds. Keep
its 17-member public surface intact behind a compat shim — MDV consumes it.
**Done** on `claude/resource-resolver-adr-88a5c8`: it is now one
`SpatialEntryStore.reconcile(contexts)` commit-effect; the surface is unchanged and
guarded by `useLayerData.spec.tsx`. See [step1-consumption-tactics.md](step1-consumption-tactics.md).

**This is the commit that unblocks parallel work.** After it lands, the tracks below
touch different files.
Expand Down
Loading
Loading