@jax-data-science/sci-image-visualizer 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/lib/contracts/display-types.mjs +28 -1
- package/esm2022/lib/contracts/ports/spatial-data.port.mjs +3 -1
- package/esm2022/lib/contracts/spatial-dataset.contract.mjs +3 -1
- package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
- package/esm2022/lib/implementations/napari-js/napari-navigator.mjs +195 -0
- package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +10 -2
- package/esm2022/lib/implementations/napari-js/napari-spatial-tiles.mjs +1098 -0
- package/esm2022/lib/implementations/napari-js/napari-spatial-tooltip.mjs +6 -3
- package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +185 -8
- package/esm2022/lib/implementations/spatial-data-http/spatial-data-http.service.mjs +120 -2
- package/esm2022/lib/implementations/spatial-data-http/spatial-wire.mjs +76 -1
- package/esm2022/lib/routing-visualizer.service.mjs +10 -1
- package/esm2022/lib/spatial/spatial-tiles.mjs +506 -0
- package/esm2022/lib/spatial-controls/spatial-controls.component.mjs +659 -19
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +2909 -62
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
- package/lib/contracts/display-types.d.ts +94 -1
- package/lib/contracts/ports/spatial-data.port.d.ts +59 -1
- package/lib/contracts/spatial-dataset.contract.d.ts +167 -0
- package/lib/contracts/visualizer.contract.d.ts +22 -1
- package/lib/implementations/napari-js/napari-navigator.d.ts +72 -0
- package/lib/implementations/napari-js/napari-region-overlay.d.ts +3 -0
- package/lib/implementations/napari-js/napari-spatial-tiles.d.ts +240 -0
- package/lib/implementations/napari-js/napari-visualizer.service.d.ts +42 -2
- package/lib/implementations/spatial-data-http/spatial-data-http.service.d.ts +26 -2
- package/lib/implementations/spatial-data-http/spatial-wire.d.ts +34 -1
- package/lib/spatial/spatial-tiles.d.ts +222 -0
- package/lib/spatial-controls/spatial-controls.component.d.ts +234 -3
- package/package.json +1 -1
|
@@ -113,7 +113,8 @@ export interface SpatialViewState {
|
|
|
113
113
|
* setting for a backdrop and a useless one for the measurement.
|
|
114
114
|
*/
|
|
115
115
|
volumeOpacity: number;
|
|
116
|
-
/** Draw the observation cloud
|
|
116
|
+
/** Draw the observation markers — the 3D cloud, or the 2D circles. In 2D they also
|
|
117
|
+
* stand in for cells too small on screen to outline. */
|
|
117
118
|
showPoints: boolean;
|
|
118
119
|
/**
|
|
119
120
|
* Restrict the cloud to ONE imaged section, by index into the dataset's sampled
|
|
@@ -178,5 +179,97 @@ export interface SpatialViewState {
|
|
|
178
179
|
* the markers: turning the cells down to read the field underneath must not
|
|
179
180
|
* turn the field down with them. */
|
|
180
181
|
geneMapOpacity: number;
|
|
182
|
+
/**
|
|
183
|
+
* Draw cell boundaries (for a dataset with `polygonTiles` or `polygons`).
|
|
184
|
+
*
|
|
185
|
+
* Outlines are level-of-detail: coarse rings when zoomed out, every vertex at the
|
|
186
|
+
* finest zoom, and none at all once a cell is a few pixels wide — there the
|
|
187
|
+
* observation markers already draw it as a dot.
|
|
188
|
+
*
|
|
189
|
+
* `null` (the default) means automatic: on for any dataset that has outlines, since a
|
|
190
|
+
* segmented cell is better shown as its shape than as a circle. A boolean is the
|
|
191
|
+
* user's explicit choice.
|
|
192
|
+
*/
|
|
193
|
+
showCells: boolean | null;
|
|
194
|
+
/** Which boundary set (`cell`, `nucleus`, or `both` — cells as set by
|
|
195
|
+
* {@link cellDraw}, nuclei outlined over them); null takes the dataset's default. */
|
|
196
|
+
cellSet: string | null;
|
|
197
|
+
/**
|
|
198
|
+
* What colours the cells — Xenium Explorer's "Cell Color": the active group
|
|
199
|
+
* ({@link cellTypeColumn}), one gene's expression ({@link cellColorGene}), the cell's
|
|
200
|
+
* transcript density, one flat colour, or how the cell was segmented.
|
|
201
|
+
*/
|
|
202
|
+
cellColorMode: 'group' | 'gene' | 'transcriptDensity' | 'single' | 'segmentation';
|
|
203
|
+
cellColorGene: string | null;
|
|
204
|
+
/** `#rrggbb` for {@link cellColorMode} `single`. */
|
|
205
|
+
cellSingleColor: string;
|
|
206
|
+
/**
|
|
207
|
+
* Categories of {@link cellTypeColumn} switched off in the groups list: their cells are
|
|
208
|
+
* not drawn, nor the transcripts inside them. Cleared when the group column changes.
|
|
209
|
+
*/
|
|
210
|
+
hiddenGroups: string[];
|
|
211
|
+
/** Draw the tissue image (the Images section's switch). */
|
|
212
|
+
showImage: boolean;
|
|
213
|
+
/** Draw the annotation regions (the Annotations section's switch). Selecting from them
|
|
214
|
+
* still works while they are hidden. */
|
|
215
|
+
showAnnotations: boolean;
|
|
216
|
+
/** Fill the cells with their colour, outline them, or both. */
|
|
217
|
+
cellDraw: 'fill' | 'outline' | 'both';
|
|
218
|
+
/** Opacity of the cell fill (outlines draw opaque). */
|
|
219
|
+
cellOpacity: number;
|
|
220
|
+
/**
|
|
221
|
+
* The categorical column that says what TYPE each cell is: it colours the cells
|
|
222
|
+
* whenever nothing else is chosen, and it colours every transcript by the cell it
|
|
223
|
+
* falls in. Null picks the dataset's first categorical column. Swapping between a
|
|
224
|
+
* pipeline's clustering and a curated annotation is swapping this.
|
|
225
|
+
*/
|
|
226
|
+
cellTypeColumn: string | null;
|
|
227
|
+
/** Genes whose transcripts are drawn. */
|
|
228
|
+
transcriptGenes: string[];
|
|
229
|
+
/** Selected genes switched off with their eye toggle — kept in the list, not drawn. */
|
|
230
|
+
transcriptHiddenGenes: string[];
|
|
231
|
+
/** Per-gene marker colour (`#rrggbb`); genes not listed take the palette by position. */
|
|
232
|
+
transcriptGeneColors: Record<string, string>;
|
|
233
|
+
/** Named gene groups (e.g. marker genes per cell type) for the selected-genes tree. */
|
|
234
|
+
transcriptGeneGroups: {
|
|
235
|
+
name: string;
|
|
236
|
+
genes: string[];
|
|
237
|
+
}[];
|
|
238
|
+
/**
|
|
239
|
+
* Draw EVERY gene's transcripts instead of {@link transcriptGenes}: grouped into bins
|
|
240
|
+
* sized by how many transcripts they hold, finer as the camera zooms in, down to one
|
|
241
|
+
* marker per transcript once those fit {@link transcriptBudget}.
|
|
242
|
+
*/
|
|
243
|
+
transcriptAllGenes: boolean;
|
|
244
|
+
/**
|
|
245
|
+
* Most transcript markers drawn at once. The level of detail is chosen to stay under it,
|
|
246
|
+
* which is what keeps pan and zoom responsive however dense the data is.
|
|
247
|
+
*/
|
|
248
|
+
transcriptBudget: number;
|
|
249
|
+
/**
|
|
250
|
+
* How transcripts are drawn: `circles` sized by how many transcripts each stands for,
|
|
251
|
+
* `glyphs` (one icon shape per gene), or a `density` raster. Circles and glyphs are
|
|
252
|
+
* level-of-detail — aggregated when zoomed out, one per transcript at the finest zoom.
|
|
253
|
+
*/
|
|
254
|
+
transcriptMode: 'off' | 'circles' | 'glyphs' | 'density';
|
|
255
|
+
/** Colour transcripts by the type of the cell they fall in, or by gene. */
|
|
256
|
+
transcriptColorBy: 'cellType' | 'gene';
|
|
257
|
+
/** Glyph per gene for `glyphs` mode; genes not listed take one by position. */
|
|
258
|
+
transcriptGlyphs: Record<string, TranscriptGlyphName>;
|
|
259
|
+
/** Multiplier on transcript marker size. */
|
|
260
|
+
transcriptScale: number;
|
|
261
|
+
transcriptOpacity: number;
|
|
262
|
+
/** `high` keeps only confidently decoded transcripts. */
|
|
263
|
+
transcriptQuality: 'high' | 'all';
|
|
264
|
+
/** Opacity of the transcript density raster. */
|
|
265
|
+
densityOpacity: number;
|
|
266
|
+
/** Density bin edge in observation units (µm): 10, 20, 40 or 80. */
|
|
267
|
+
densityBin: number;
|
|
268
|
+
/** Colour window in transcripts per unit² (µm²); null derives it from the data. */
|
|
269
|
+
densityRange: [number, number] | null;
|
|
270
|
+
/** Colormap for the density bins; null uses Inferno. */
|
|
271
|
+
densityColormap: ColormapValue | null;
|
|
181
272
|
}
|
|
273
|
+
/** Transcript glyph names — see `TRANSCRIPT_GLYPHS` in `spatial/spatial-tiles.ts`. */
|
|
274
|
+
export type TranscriptGlyphName = 'circle' | 'star' | 'triangle' | 'square' | 'diamond' | 'cross' | 'hexagon' | 'triangle-down' | 'pentagon' | 'x';
|
|
182
275
|
export declare const DEFAULT_SPATIAL_VIEW: SpatialViewState;
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import { InjectionToken } from '@angular/core';
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
|
-
import { SpatialColumn, SpatialDataset, SpatialEmbedding, SpatialPolygons } from '../spatial-dataset.contract';
|
|
3
|
+
import { SpatialColumn, SpatialDataset, SpatialDensityRaster, SpatialEmbedding, SpatialPolygonTile, CategoricalColumnMeta, SpatialPolygons, SpatialTranscriptCounts, SpatialTranscriptSummary, SpatialTranscriptTile } from '../spatial-dataset.contract';
|
|
4
|
+
/** Options for {@link SpatialDataPort.getTranscriptTile}. */
|
|
5
|
+
export interface TranscriptTileQuery {
|
|
6
|
+
/**
|
|
7
|
+
* Genes to include, by name; the tile's `gene` codes index this list. `[ALL_GENES]`
|
|
8
|
+
* asks for every gene, unaggregated, at level 0 only and clipped to {@link box} — the
|
|
9
|
+
* `gene` codes are then the dataset's own gene indices.
|
|
10
|
+
*/
|
|
11
|
+
genes: string[];
|
|
12
|
+
/** `high` (default) keeps only confidently decoded transcripts. */
|
|
13
|
+
quality?: 'high' | 'all';
|
|
14
|
+
/** Clip to `[x0, y0, x1, y1]` in observation units. */
|
|
15
|
+
box?: [number, number, number, number];
|
|
16
|
+
}
|
|
17
|
+
/** {@link TranscriptTileQuery.genes} value meaning "every gene". */
|
|
18
|
+
export declare const ALL_GENES = "*";
|
|
4
19
|
/**
|
|
5
20
|
* Spatial-omics data access, inverted as a port so the visualization library
|
|
6
21
|
* never learns how the data is stored or served — exactly like
|
|
@@ -66,6 +81,49 @@ export interface SpatialDataPort {
|
|
|
66
81
|
* `polygons`. Optional — a spot-based assay (Visium) has no segmentation.
|
|
67
82
|
*/
|
|
68
83
|
getPolygons?(): Promise<SpatialPolygons>;
|
|
84
|
+
/**
|
|
85
|
+
* One tile of boundaries from `polygonTiles`. An empty tile resolves with `count` 0 —
|
|
86
|
+
* a tile outside the tissue is an answer, not an error. Optional: only datasets that
|
|
87
|
+
* advertise `polygonTiles` need it.
|
|
88
|
+
*/
|
|
89
|
+
getPolygonTile?(set: string, level: number, gx: number, gy: number): Promise<SpatialPolygonTile>;
|
|
90
|
+
/**
|
|
91
|
+
* One tile of transcripts from `transcriptTiles`, restricted to `query.genes`.
|
|
92
|
+
* Optional: only datasets that advertise `transcriptTiles` need it.
|
|
93
|
+
*/
|
|
94
|
+
getTranscriptTile?(level: number, gx: number, gy: number, query: TranscriptTileQuery): Promise<SpatialTranscriptTile>;
|
|
95
|
+
/**
|
|
96
|
+
* One tile of the all-gene grouping pyramid (`transcriptBins`): an entry per bin, with
|
|
97
|
+
* `weight` its transcript count and `observation` its dominant cell.
|
|
98
|
+
*/
|
|
99
|
+
getTranscriptBins?(level: number, tx: number, ty: number): Promise<SpatialTranscriptTile>;
|
|
100
|
+
/**
|
|
101
|
+
* What is inside `box` (observation units): transcript count, distinct genes and cells,
|
|
102
|
+
* the most frequent of each — optionally restricted to `genes` — plus display ids for
|
|
103
|
+
* `cells`. Omit `box` for a cell-id lookup only. Optional; drives the hover details of
|
|
104
|
+
* transcript markers.
|
|
105
|
+
*/
|
|
106
|
+
getTranscriptSummary?(query: {
|
|
107
|
+
box?: [number, number, number, number];
|
|
108
|
+
genes?: string[];
|
|
109
|
+
cells?: number[];
|
|
110
|
+
}): Promise<SpatialTranscriptSummary>;
|
|
111
|
+
/**
|
|
112
|
+
* The summed transcript density of `genes` (`[ALL_GENES]` for every gene) on the
|
|
113
|
+
* dataset's `density` raster, re-binned to `binSize` observation units when given.
|
|
114
|
+
* Optional: only datasets that advertise `density` need it.
|
|
115
|
+
*/
|
|
116
|
+
getDensity?(genes: string[], binSize?: number): Promise<SpatialDensityRaster>;
|
|
117
|
+
/**
|
|
118
|
+
* Import a cell grouping — a CSV/TSV of `cell_id` and `group` — under `label`. The new
|
|
119
|
+
* categorical column is added to the dataset (which re-emits) and resolved. Optional.
|
|
120
|
+
*/
|
|
121
|
+
importGroups?(label: string, table: string): Promise<{
|
|
122
|
+
column: CategoricalColumnMeta;
|
|
123
|
+
matched: number;
|
|
124
|
+
}>;
|
|
125
|
+
/** Transcript totals per gene, for estimating how many markers a selection draws. */
|
|
126
|
+
getTranscriptCounts?(genes: string[]): Promise<SpatialTranscriptCounts>;
|
|
69
127
|
/**
|
|
70
128
|
* The reference volume's voxels: a uint8 scalar field, x-fastest, of exactly
|
|
71
129
|
* `width * height * depth` bytes as the dataset's {@link SpatialVolumeMeta}
|
|
@@ -63,6 +63,20 @@ export interface CategoricalColumnMeta extends SpatialColumnMetaBase {
|
|
|
63
63
|
kind: 'categorical';
|
|
64
64
|
/** Category labels; a value's label is `categories[codes[i]]`. */
|
|
65
65
|
categories: string[];
|
|
66
|
+
/**
|
|
67
|
+
* Heading the column is listed under in a group picker — e.g. "Xenium Onboard Analysis
|
|
68
|
+
* groups" for a pipeline's clusterings, "Imported groups" for ones added by a user.
|
|
69
|
+
*/
|
|
70
|
+
section?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Columns that are variants of one choice — k-means at k = 2…10 — share a family: a
|
|
73
|
+
* picker lists the family once and offers the variants beside it.
|
|
74
|
+
*/
|
|
75
|
+
family?: {
|
|
76
|
+
id: string;
|
|
77
|
+
label: string;
|
|
78
|
+
variant: string;
|
|
79
|
+
};
|
|
66
80
|
/** Authored display colours as `#rrggbb`, index-aligned with
|
|
67
81
|
* {@link categories}. Supply these to keep the viewer's palette identical to
|
|
68
82
|
* the figures the same analysis produced in R/Python; omit to let the viewer
|
|
@@ -228,6 +242,149 @@ export interface SpatialEmbedding {
|
|
|
228
242
|
y: Float32Array;
|
|
229
243
|
z?: Float32Array;
|
|
230
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* Level-of-detail geometry, served one grid tile at a time.
|
|
247
|
+
*
|
|
248
|
+
* Whole-dataset vectors stop working somewhere past 10^5 observations: 717k Xenium cell
|
|
249
|
+
* outlines are ~140 MB, and a whole-transcriptome run has ~10^9 transcripts. What scales
|
|
250
|
+
* is what map tiles do — a square grid in the observations' coordinate space, one grid
|
|
251
|
+
* per level, each level's tiles twice the size of the one below and carrying coarser
|
|
252
|
+
* geometry. The renderer asks only for the tiles on screen, at the level the zoom needs.
|
|
253
|
+
*
|
|
254
|
+
* Tile `(gx, gy)` at level `l` covers `[gx·s, (gx+1)·s) × [gy·s, (gy+1)·s)` with
|
|
255
|
+
* `s = levels[l].tileSize`, in the same units as {@link SpatialObservations.x}.
|
|
256
|
+
*/
|
|
257
|
+
export interface SpatialTileLevel {
|
|
258
|
+
/** Edge length of one tile, in observation coordinate units. */
|
|
259
|
+
tileSize: number;
|
|
260
|
+
}
|
|
261
|
+
/** Axis-aligned extent `[minX, minY, maxX, maxY]` in observation coordinates. */
|
|
262
|
+
export type SpatialBounds = [number, number, number, number];
|
|
263
|
+
/** Tiled cell/nucleus boundaries. Level 0 is the finest (every vertex). */
|
|
264
|
+
export interface SpatialPolygonTilesMeta {
|
|
265
|
+
bounds: SpatialBounds;
|
|
266
|
+
/** Boundary sets on offer — typically `cell` and `nucleus`. */
|
|
267
|
+
sets: {
|
|
268
|
+
name: string;
|
|
269
|
+
label: string;
|
|
270
|
+
}[];
|
|
271
|
+
/** The set to draw when the user has not chosen. */
|
|
272
|
+
defaultSet?: string;
|
|
273
|
+
levels: SpatialTileLevel[];
|
|
274
|
+
}
|
|
275
|
+
/** Tiled transcripts. Level 0 is one entry per transcript; coarser levels aggregate. */
|
|
276
|
+
export interface SpatialTranscriptTilesMeta {
|
|
277
|
+
bounds: SpatialBounds;
|
|
278
|
+
/** Total transcripts, for display. */
|
|
279
|
+
count?: number;
|
|
280
|
+
/** Real genes (control probes excluded). Names come from the feature search. */
|
|
281
|
+
geneCount: number;
|
|
282
|
+
hasZ: boolean;
|
|
283
|
+
levels: (SpatialTileLevel & {
|
|
284
|
+
/** True when an entry stands for several transcripts (see {@link SpatialTranscriptTile.weight}). */
|
|
285
|
+
aggregated: boolean;
|
|
286
|
+
})[];
|
|
287
|
+
/**
|
|
288
|
+
* Levels up to and including this one assign each entry to the cell it lies in
|
|
289
|
+
* exactly; above it the assignment names the cell nearest an aggregate's centroid.
|
|
290
|
+
*/
|
|
291
|
+
exactCellLevel?: number;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* The all-gene transcript grouping pyramid: square bins, four times coarser per level,
|
|
295
|
+
* each holding a count, a centroid and the cell that contributed most of its transcripts.
|
|
296
|
+
* Served per tile, in the {@link SpatialTranscriptTile} layout (one entry per bin).
|
|
297
|
+
*
|
|
298
|
+
* Tile `(tx, ty)` of level `l` covers `origin + [tx·s, (tx+1)·s) × [ty·s, (ty+1)·s)` with
|
|
299
|
+
* `s = levels[l].tileSize` — note the origin, which the per-gene tiles do not have.
|
|
300
|
+
*/
|
|
301
|
+
export interface SpatialTranscriptBinsMeta {
|
|
302
|
+
bounds: SpatialBounds;
|
|
303
|
+
origin: [number, number];
|
|
304
|
+
/** Transcripts the pyramid was built from. */
|
|
305
|
+
count: number;
|
|
306
|
+
levels: {
|
|
307
|
+
binSize: number;
|
|
308
|
+
tileSize: number;
|
|
309
|
+
}[];
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* A grouping pyramid the server is still building (it builds one itself when a dataset
|
|
313
|
+
* arrives without it). "All genes" becomes available once it is done.
|
|
314
|
+
*/
|
|
315
|
+
export interface SpatialTranscriptBinsStatus {
|
|
316
|
+
state: 'queued' | 'building' | 'failed';
|
|
317
|
+
/** Source tiles processed so far, of `total`. */
|
|
318
|
+
done?: number;
|
|
319
|
+
total?: number;
|
|
320
|
+
message?: string;
|
|
321
|
+
}
|
|
322
|
+
/** What is in an area — the hover details of a transcript marker or grouped bin. */
|
|
323
|
+
export interface SpatialTranscriptSummary {
|
|
324
|
+
/** Absent for a pure cell-id lookup. */
|
|
325
|
+
transcripts?: number;
|
|
326
|
+
/** Distinct genes among them. */
|
|
327
|
+
genes?: number;
|
|
328
|
+
topGenes?: {
|
|
329
|
+
name: string;
|
|
330
|
+
count: number;
|
|
331
|
+
}[];
|
|
332
|
+
/** Distinct cells they fall in, and how many fall in none. */
|
|
333
|
+
cells?: number;
|
|
334
|
+
unassigned?: number;
|
|
335
|
+
topCells?: {
|
|
336
|
+
index: number;
|
|
337
|
+
id?: string;
|
|
338
|
+
count: number;
|
|
339
|
+
}[];
|
|
340
|
+
/** Display ids of the cells asked for, by observation index. */
|
|
341
|
+
cellIds?: Record<number, string>;
|
|
342
|
+
}
|
|
343
|
+
/** A per-gene transcript-count raster covering the section. */
|
|
344
|
+
export interface SpatialDensityMeta {
|
|
345
|
+
/** Size of one raster cell, in observation units, `[x, y]`. */
|
|
346
|
+
gridSize: [number, number];
|
|
347
|
+
/** Observation-space position of raster cell (0, 0)'s near corner. */
|
|
348
|
+
origin: [number, number];
|
|
349
|
+
rows: number;
|
|
350
|
+
cols: number;
|
|
351
|
+
}
|
|
352
|
+
/** "No observation" in {@link SpatialTranscriptTile.observation}. */
|
|
353
|
+
export declare const NO_OBSERVATION = 4294967295;
|
|
354
|
+
/** One tile of boundaries: rings as in {@link SpatialPolygons}, plus who owns each ring. */
|
|
355
|
+
export interface SpatialPolygonTile extends SpatialPolygons {
|
|
356
|
+
/** Observation index of ring `i` — the join key to columns and features. */
|
|
357
|
+
observation: Uint32Array;
|
|
358
|
+
}
|
|
359
|
+
/** One tile of transcripts (or aggregates of them). Every array has length {@link count}. */
|
|
360
|
+
export interface SpatialTranscriptTile {
|
|
361
|
+
readonly count: number;
|
|
362
|
+
aggregated: boolean;
|
|
363
|
+
x: Float32Array;
|
|
364
|
+
y: Float32Array;
|
|
365
|
+
z: Float32Array;
|
|
366
|
+
/** Transcripts each entry stands for — 1 at an unaggregated level. */
|
|
367
|
+
weight: Uint32Array;
|
|
368
|
+
/** Observation (cell) the entry falls in, or {@link NO_OBSERVATION}. */
|
|
369
|
+
observation: Uint32Array;
|
|
370
|
+
/** Index into the `genes` list the tile was requested with. */
|
|
371
|
+
gene: Uint16Array;
|
|
372
|
+
}
|
|
373
|
+
/** Transcript totals for an estimate of how many markers a selection would draw. */
|
|
374
|
+
export interface SpatialTranscriptCounts {
|
|
375
|
+
/** Total transcripts per requested gene, over the whole dataset. */
|
|
376
|
+
counts: Record<string, number>;
|
|
377
|
+
/** Total over every gene. */
|
|
378
|
+
total: number;
|
|
379
|
+
/** Extent the totals are spread over (observation units). */
|
|
380
|
+
bounds: SpatialBounds;
|
|
381
|
+
}
|
|
382
|
+
/** A summed density raster for a list of genes; `values` is row-major `rows × cols`. */
|
|
383
|
+
export interface SpatialDensityRaster {
|
|
384
|
+
meta: SpatialDensityMeta;
|
|
385
|
+
genes: string[];
|
|
386
|
+
values: Float32Array;
|
|
387
|
+
}
|
|
231
388
|
export interface SpatialDataset {
|
|
232
389
|
/** Stable id — the key the port's lazy accessors are scoped to. */
|
|
233
390
|
id: string;
|
|
@@ -243,6 +400,16 @@ export interface SpatialDataset {
|
|
|
243
400
|
volume?: SpatialVolumeMeta;
|
|
244
401
|
/** Embeddings available for these observations, whose coordinates are fetched on demand. */
|
|
245
402
|
embeddings?: SpatialEmbeddingMeta[];
|
|
403
|
+
/** Level-of-detail boundaries, fetched per tile — see {@link SpatialPolygonTilesMeta}. */
|
|
404
|
+
polygonTiles?: SpatialPolygonTilesMeta;
|
|
405
|
+
/** Level-of-detail transcripts, fetched per tile — see {@link SpatialTranscriptTilesMeta}. */
|
|
406
|
+
transcriptTiles?: SpatialTranscriptTilesMeta;
|
|
407
|
+
/** A served per-gene transcript density raster. */
|
|
408
|
+
density?: SpatialDensityMeta;
|
|
409
|
+
/** The all-gene transcript grouping pyramid, when it has been built. */
|
|
410
|
+
transcriptBins?: SpatialTranscriptBinsMeta;
|
|
411
|
+
/** Present while the server is still building {@link transcriptBins}. */
|
|
412
|
+
transcriptBinsStatus?: SpatialTranscriptBinsStatus;
|
|
246
413
|
/**
|
|
247
414
|
* Microns per observation coordinate unit — what makes a scale bar possible.
|
|
248
415
|
*
|
|
@@ -8,7 +8,7 @@ import { ViewerCapabilities } from './capabilities.contract';
|
|
|
8
8
|
import { IRegionOverlay } from './region-overlay.contract';
|
|
9
9
|
import { IHistogram } from './channel-histogram-api.contract';
|
|
10
10
|
import { ColormapNode, IWandOptions, IBrushOptions, SpatialViewState, SpatialColorBy } from './display-types';
|
|
11
|
-
import { SpatialDataset, SpatialEmbedding } from './spatial-dataset.contract';
|
|
11
|
+
import { CategoricalColumnMeta, SpatialDataset, SpatialEmbedding, SpatialTranscriptCounts } from './spatial-dataset.contract';
|
|
12
12
|
import { SpatialSelectionMask } from '../spatial/spatial-selection';
|
|
13
13
|
import type { PlotModeViewport } from './plot-type-contribution.contract';
|
|
14
14
|
/**
|
|
@@ -334,6 +334,27 @@ export interface ISpatialControls {
|
|
|
334
334
|
getEmbedding?(name: string): Promise<SpatialEmbedding>;
|
|
335
335
|
/** Names of the categorical columns available to group by. */
|
|
336
336
|
categoricalColumns(): string[];
|
|
337
|
+
/**
|
|
338
|
+
* Import a cell grouping (CSV/TSV of `cell_id`, `group`) as a new categorical column.
|
|
339
|
+
* Optional: only data sources that can join cell ids implement it.
|
|
340
|
+
*/
|
|
341
|
+
importGroups?(label: string, table: string): Promise<{
|
|
342
|
+
column: CategoricalColumnMeta;
|
|
343
|
+
matched: number;
|
|
344
|
+
}>;
|
|
345
|
+
/** Transcript totals per gene — for the points estimate. Optional. */
|
|
346
|
+
transcriptCounts?(genes: string[]): Promise<SpatialTranscriptCounts>;
|
|
347
|
+
/** Estimated transcripts in view for the current selection, against the marker budget. */
|
|
348
|
+
getTranscriptEstimate$?(): Observable<{
|
|
349
|
+
points: number;
|
|
350
|
+
max: number;
|
|
351
|
+
} | null>;
|
|
352
|
+
/** The density map's colour window in use (auto-derived or set) and its densest bin. */
|
|
353
|
+
getDensityStats$?(): Observable<{
|
|
354
|
+
lo: number;
|
|
355
|
+
hi: number;
|
|
356
|
+
max: number;
|
|
357
|
+
} | null>;
|
|
337
358
|
/**
|
|
338
359
|
* The z positions of the sections the dataset was imaged at, ascending — or
|
|
339
360
|
* null when its z is continuous rather than sectioned (so there are no sections
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Overview navigator (minimap) for the napari-js 2D views, mirroring the OSD backend's
|
|
3
|
+
* built-in navigator: a thumbnail of the whole image in the bottom-right corner with the
|
|
4
|
+
* current viewport drawn on it. Clicking re-centres the main view on that point at the
|
|
5
|
+
* same zoom; dragging pans continuously — exactly OSD's behaviour.
|
|
6
|
+
*
|
|
7
|
+
* napari keeps layers at full-resolution pixel scale, so the camera's world units ARE
|
|
8
|
+
* image pixels and the thumbnail maps onto `[0, width) × [0, height)` directly.
|
|
9
|
+
*/
|
|
10
|
+
/** The slice of the napari 2D camera the navigator reads and drives. */
|
|
11
|
+
export interface NavigatorCamera {
|
|
12
|
+
center: readonly [number, number];
|
|
13
|
+
readonly zoom: number;
|
|
14
|
+
readonly changed: {
|
|
15
|
+
connect(listener: () => void): () => void;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** Where the thumbnail sits inside the navigator box, in CSS px. */
|
|
19
|
+
export interface NavigatorLayout {
|
|
20
|
+
/** Box size. */
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
/** CSS px per world unit (image pixel). */
|
|
24
|
+
scale: number;
|
|
25
|
+
}
|
|
26
|
+
/** Fraction of the host's width the navigator takes — OSD's `navigatorSizeRatio`. */
|
|
27
|
+
export declare const NAVIGATOR_SIZE_RATIO = 0.16;
|
|
28
|
+
/** Box size and scale for a `worldW × worldH` image in a host `hostW` wide. */
|
|
29
|
+
export declare function navigatorLayout(hostW: number, worldW: number, worldH: number, ratio?: number): NavigatorLayout | null;
|
|
30
|
+
/** The part of the world the camera shows, as `[x0, y0, x1, y1]`. */
|
|
31
|
+
export declare function viewportWorldRect(center: readonly [number, number], zoom: number, hostW: number, hostH: number): [number, number, number, number];
|
|
32
|
+
/** A point in the navigator box → world coordinates, clamped to the image. */
|
|
33
|
+
export declare function navigatorToWorld(px: number, py: number, layout: NavigatorLayout, worldW: number, worldH: number): [number, number];
|
|
34
|
+
export declare class NapariNavigator {
|
|
35
|
+
private readonly host;
|
|
36
|
+
private readonly camera;
|
|
37
|
+
private worldW;
|
|
38
|
+
private worldH;
|
|
39
|
+
/** Called when the pointer enters the navigator — e.g. to hide a hover tooltip that
|
|
40
|
+
* was describing the canvas underneath. */
|
|
41
|
+
private readonly onEnter?;
|
|
42
|
+
private readonly box;
|
|
43
|
+
private readonly canvas;
|
|
44
|
+
private readonly region;
|
|
45
|
+
private readonly disconnectCamera;
|
|
46
|
+
private readonly resizeObserver?;
|
|
47
|
+
/** Aborted by destroy() to remove every DOM listener the box holds. */
|
|
48
|
+
private readonly listeners;
|
|
49
|
+
private layout;
|
|
50
|
+
private image;
|
|
51
|
+
private dragging;
|
|
52
|
+
private visible;
|
|
53
|
+
constructor(host: HTMLElement, camera: NavigatorCamera, worldW: number, worldH: number,
|
|
54
|
+
/** Called when the pointer enters the navigator — e.g. to hide a hover tooltip that
|
|
55
|
+
* was describing the canvas underneath. */
|
|
56
|
+
onEnter?: (() => void) | undefined);
|
|
57
|
+
/** The thumbnail to draw: any image of the whole world (a coarse pyramid level). */
|
|
58
|
+
setImage(image: CanvasImageSource | null): void;
|
|
59
|
+
/** The image's world size changed (another image was loaded). */
|
|
60
|
+
setWorld(width: number, height: number): void;
|
|
61
|
+
setVisible(visible: boolean): void;
|
|
62
|
+
destroy(): void;
|
|
63
|
+
private relayout;
|
|
64
|
+
private drawImage;
|
|
65
|
+
/** Draw the viewport rectangle, clipped to the box (as OSD does when zoomed out). */
|
|
66
|
+
private updateRegion;
|
|
67
|
+
/** Re-centre the main view on the navigator point under the pointer, keeping the zoom. */
|
|
68
|
+
private panTo;
|
|
69
|
+
private readonly onDown;
|
|
70
|
+
private readonly onMove;
|
|
71
|
+
private readonly onUp;
|
|
72
|
+
}
|
|
@@ -48,6 +48,7 @@ export declare class NapariRegionOverlay implements IRegionOverlay {
|
|
|
48
48
|
private marqueeEl;
|
|
49
49
|
private draftPath;
|
|
50
50
|
private drawing;
|
|
51
|
+
private regionsVisible;
|
|
51
52
|
/** In-progress manipulation (select/move modes): dragging a body, a polygon vertex, or a
|
|
52
53
|
* rectangle corner. `anchor` is the fixed opposite corner for a rectangle resize. */
|
|
53
54
|
private edit;
|
|
@@ -124,6 +125,8 @@ export declare class NapariRegionOverlay implements IRegionOverlay {
|
|
|
124
125
|
private commitRectangle;
|
|
125
126
|
private commitPolygon;
|
|
126
127
|
private hitTest;
|
|
128
|
+
/** Show or hide the drawn regions. A region being drawn stays visible either way. */
|
|
129
|
+
setRegionsVisible(visible: boolean): void;
|
|
127
130
|
redraw(): void;
|
|
128
131
|
/** Draw a region's classification label at its top-left, when labels are enabled (matches OSD). */
|
|
129
132
|
private drawLabel;
|