@jax-data-science/sci-image-visualizer 0.7.0 → 0.8.1
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 +1 -1
- package/esm2022/lib/contracts/plot-type-contribution.contract.mjs +1 -1
- package/esm2022/lib/contracts/ports/spatial-data.port.mjs +1 -1
- package/esm2022/lib/contracts/spatial-dataset.contract.mjs +1 -1
- package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
- package/esm2022/lib/implementations/napari-js/napari-loading-badge.mjs +73 -0
- package/esm2022/lib/implementations/napari-js/napari-spatial-tiles.mjs +418 -29
- package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +49 -6
- package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +8 -1
- package/esm2022/lib/implementations/plotly/plotly.service.mjs +7 -1
- package/esm2022/lib/implementations/spatial-data-http/spatial-data-http.service.mjs +12 -1
- package/esm2022/lib/implementations/spatial-data-http/spatial-wire.mjs +2 -1
- package/esm2022/lib/routing-visualizer.service.mjs +5 -1
- package/esm2022/lib/spatial/spatial-tiles.mjs +144 -1
- package/esm2022/lib/spatial-controls/spatial-controls.component.mjs +198 -9
- package/esm2022/lib/toolbar/toolbar.component.mjs +11 -3
- package/esm2022/lib/visualizer.component.mjs +58 -4
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +964 -39
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
- package/lib/contracts/display-types.d.ts +4 -2
- package/lib/contracts/plot-type-contribution.contract.d.ts +9 -0
- package/lib/contracts/ports/spatial-data.port.d.ts +12 -1
- package/lib/contracts/spatial-dataset.contract.d.ts +30 -0
- package/lib/contracts/visualizer.contract.d.ts +7 -1
- package/lib/implementations/napari-js/napari-loading-badge.d.ts +20 -0
- package/lib/implementations/napari-js/napari-spatial-tiles.d.ts +76 -1
- package/lib/implementations/napari-js/napari-visualizer.service.d.ts +7 -0
- package/lib/implementations/spatial-data-http/spatial-data-http.service.d.ts +3 -1
- package/lib/implementations/spatial-data-http/spatial-wire.d.ts +2 -1
- package/lib/spatial/spatial-tiles.d.ts +38 -1
- package/lib/spatial-controls/spatial-controls.component.d.ts +54 -0
- package/lib/toolbar/toolbar.component.d.ts +5 -1
- package/lib/visualizer.component.d.ts +10 -1
- package/package.json +1 -1
|
@@ -252,8 +252,10 @@ export interface SpatialViewState {
|
|
|
252
252
|
* level-of-detail — aggregated when zoomed out, one per transcript at the finest zoom.
|
|
253
253
|
*/
|
|
254
254
|
transcriptMode: 'off' | 'circles' | 'glyphs' | 'density';
|
|
255
|
-
/** Colour transcripts by the type of the cell they fall in, or by gene
|
|
256
|
-
|
|
255
|
+
/** Colour transcripts by the type of the cell they fall in, by gene, or by the gene-tree
|
|
256
|
+
* cluster their gene is in (in the colour that cluster's cells have, when a cell grouping
|
|
257
|
+
* names it). */
|
|
258
|
+
transcriptColorBy: 'cellType' | 'gene' | 'cluster';
|
|
257
259
|
/** Glyph per gene for `glyphs` mode; genes not listed take one by position. */
|
|
258
260
|
transcriptGlyphs: Record<string, TranscriptGlyphName>;
|
|
259
261
|
/** Multiplier on transcript marker size. */
|
|
@@ -102,6 +102,15 @@ export interface PlotModeViewport {
|
|
|
102
102
|
/** Same rect, emitted once the viewport settles (existing getViewportChange$ semantics).
|
|
103
103
|
* A new subscriber first receives the current rect when the viewport is ready. */
|
|
104
104
|
settled$: Observable<PlotModeRect>;
|
|
105
|
+
/**
|
|
106
|
+
* Pan and zoom so a level-0 image rect fills the view (within the viewer's zoom
|
|
107
|
+
* constraints), e.g. to take the user to a region a mode proposes. Animated unless
|
|
108
|
+
* `immediately`. A no-op while the viewport is not ready. Optional: backends without
|
|
109
|
+
* a 2D image viewport leave it out, so callers feature-detect it.
|
|
110
|
+
*/
|
|
111
|
+
fitBounds?(rect: PlotModeRect, options?: {
|
|
112
|
+
immediately?: boolean;
|
|
113
|
+
}): void;
|
|
105
114
|
}
|
|
106
115
|
/** The class a {@link PlotModeTools.armBrush} stroke paints. */
|
|
107
116
|
export interface PlotModeBrushClass {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { InjectionToken } from '@angular/core';
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
|
-
import { SpatialColumn, SpatialDataset, SpatialDensityRaster, SpatialEmbedding, SpatialPolygonTile, CategoricalColumnMeta, SpatialPolygons, SpatialTranscriptCounts, SpatialTranscriptSummary, SpatialTranscriptTile } from '../spatial-dataset.contract';
|
|
3
|
+
import { SpatialColumn, SpatialDataset, SpatialDensityRaster, SpatialEmbedding, SpatialPolygonTile, CategoricalColumnMeta, SpatialMarkerGenes, SpatialPolygons, SpatialTranscriptCounts, SpatialTranscriptSummary, SpatialTranscriptTile } from '../spatial-dataset.contract';
|
|
4
4
|
/** Options for {@link SpatialDataPort.getTranscriptTile}. */
|
|
5
5
|
export interface TranscriptTileQuery {
|
|
6
6
|
/**
|
|
@@ -97,6 +97,11 @@ export interface SpatialDataPort {
|
|
|
97
97
|
* `weight` its transcript count and `observation` its dominant cell.
|
|
98
98
|
*/
|
|
99
99
|
getTranscriptBins?(level: number, tx: number, ty: number): Promise<SpatialTranscriptTile>;
|
|
100
|
+
/**
|
|
101
|
+
* One tile of the per-gene levels for `genes`: each entry is one gene's transcripts in one bin
|
|
102
|
+
* (centroid, count, dominant cell); its `gene` is the position in `genes`. Optional.
|
|
103
|
+
*/
|
|
104
|
+
getTranscriptGeneBins?(level: number, tx: number, ty: number, genes: string[]): Promise<SpatialTranscriptTile>;
|
|
100
105
|
/**
|
|
101
106
|
* What is inside `box` (observation units): transcript count, distinct genes and cells,
|
|
102
107
|
* the most frequent of each — optionally restricted to `genes` — plus display ids for
|
|
@@ -124,6 +129,12 @@ export interface SpatialDataPort {
|
|
|
124
129
|
}>;
|
|
125
130
|
/** Transcript totals per gene, for estimating how many markers a selection draws. */
|
|
126
131
|
getTranscriptCounts?(genes: string[]): Promise<SpatialTranscriptCounts>;
|
|
132
|
+
/**
|
|
133
|
+
* The top `perGroup` marker genes of every group of categorical `column`, for building
|
|
134
|
+
* gene groups from cell clusters ("Macrophages: CD163, MRC1, …"). Optional, and possibly
|
|
135
|
+
* slow the first time: the server makes one pass over the expression matrix.
|
|
136
|
+
*/
|
|
137
|
+
getMarkerGenes?(column: string, perGroup?: number): Promise<SpatialMarkerGenes>;
|
|
127
138
|
/**
|
|
128
139
|
* The reference volume's voxels: a uint8 scalar field, x-fastest, of exactly
|
|
129
140
|
* `width * height * depth` bytes as the dataset's {@link SpatialVolumeMeta}
|
|
@@ -371,6 +371,34 @@ export interface SpatialTranscriptTile {
|
|
|
371
371
|
gene: Uint16Array;
|
|
372
372
|
}
|
|
373
373
|
/** Transcript totals for an estimate of how many markers a selection would draw. */
|
|
374
|
+
/** The per-gene levels: each level's bin and tile size, on the all-gene pyramid's grid. */
|
|
375
|
+
export interface SpatialTranscriptGeneBinsMeta {
|
|
376
|
+
origin: [number, number];
|
|
377
|
+
levels: {
|
|
378
|
+
binSize: number;
|
|
379
|
+
tileSize: number;
|
|
380
|
+
}[];
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Marker genes of each group of a categorical column: per group, the real genes most
|
|
384
|
+
* specific to it, best first. A gene's score is its mean `log1p(count)` in the group minus
|
|
385
|
+
* that in every other cell; it must be detected in at least 10% of the group's cells, and
|
|
386
|
+
* in more of them than elsewhere.
|
|
387
|
+
*/
|
|
388
|
+
export interface SpatialMarkerGenes {
|
|
389
|
+
column: string;
|
|
390
|
+
groups: {
|
|
391
|
+
name: string;
|
|
392
|
+
/** Cells in the group. */
|
|
393
|
+
cells: number;
|
|
394
|
+
genes: {
|
|
395
|
+
name: string;
|
|
396
|
+
score: number;
|
|
397
|
+
pctIn: number;
|
|
398
|
+
pctOut: number;
|
|
399
|
+
}[];
|
|
400
|
+
}[];
|
|
401
|
+
}
|
|
374
402
|
export interface SpatialTranscriptCounts {
|
|
375
403
|
/** Total transcripts per requested gene, over the whole dataset. */
|
|
376
404
|
counts: Record<string, number>;
|
|
@@ -408,6 +436,8 @@ export interface SpatialDataset {
|
|
|
408
436
|
density?: SpatialDensityMeta;
|
|
409
437
|
/** The all-gene transcript grouping pyramid, when it has been built. */
|
|
410
438
|
transcriptBins?: SpatialTranscriptBinsMeta;
|
|
439
|
+
/** Per-gene levels of the transcript pyramid (same bins): a gene selection at any zoom. */
|
|
440
|
+
transcriptGeneBins?: SpatialTranscriptGeneBinsMeta;
|
|
411
441
|
/** Present while the server is still building {@link transcriptBins}. */
|
|
412
442
|
transcriptBinsStatus?: SpatialTranscriptBinsStatus;
|
|
413
443
|
/**
|
|
@@ -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 { CategoricalColumnMeta, SpatialDataset, SpatialEmbedding, SpatialTranscriptCounts } from './spatial-dataset.contract';
|
|
11
|
+
import { CategoricalColumnMeta, SpatialDataset, SpatialEmbedding, SpatialMarkerGenes, 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
|
/**
|
|
@@ -344,6 +344,12 @@ export interface ISpatialControls {
|
|
|
344
344
|
}>;
|
|
345
345
|
/** Transcript totals per gene — for the points estimate. Optional. */
|
|
346
346
|
transcriptCounts?(genes: string[]): Promise<SpatialTranscriptCounts>;
|
|
347
|
+
/** Marker genes of each group of a categorical column. Optional (see the port). */
|
|
348
|
+
markerGenes?(column: string, perGroup?: number): Promise<SpatialMarkerGenes>;
|
|
349
|
+
/** Transcripts of each selected gene in the current view, or null when not known
|
|
350
|
+
* (all genes at once, transcripts off, or a selection zoomed out on a dataset without the
|
|
351
|
+
* pyramid's per-gene levels, where clusters are drawn from summed density grids). */
|
|
352
|
+
getGeneCountsInView$?(): Observable<Record<string, number> | null>;
|
|
347
353
|
/** Estimated transcripts in view for the current selection, against the marker budget. */
|
|
348
354
|
getTranscriptEstimate$?(): Observable<{
|
|
349
355
|
points: number;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Transcripts reloading…" at the bottom of the canvas while a layer's data is in flight: one
|
|
3
|
+
* line naming every layer still loading, on a semi-opaque backing so it reads over any tissue.
|
|
4
|
+
*
|
|
5
|
+
* Shown only once something has been loading for {@link SHOW_AFTER_MS}, so a redraw served from
|
|
6
|
+
* cache does not flash it.
|
|
7
|
+
*/
|
|
8
|
+
export declare const SHOW_AFTER_MS = 150;
|
|
9
|
+
export declare class NapariLoadingBadge {
|
|
10
|
+
private readonly host;
|
|
11
|
+
private readonly el;
|
|
12
|
+
private names;
|
|
13
|
+
private timer;
|
|
14
|
+
constructor(host: HTMLElement);
|
|
15
|
+
/** The layers loading now; empty hides the badge. */
|
|
16
|
+
set(names: readonly string[]): void;
|
|
17
|
+
/** The text currently set (shown or about to be). */
|
|
18
|
+
get text(): string;
|
|
19
|
+
destroy(): void;
|
|
20
|
+
}
|
|
@@ -2,8 +2,9 @@ import type { Layer, Viewer } from 'napari-js';
|
|
|
2
2
|
import type { Rgb } from '../../contracts/colormap-lut';
|
|
3
3
|
import { type SpatialDataPort } from '../../contracts/ports/spatial-data.port';
|
|
4
4
|
import type { SpatialViewState } from '../../contracts/display-types';
|
|
5
|
-
import { SpatialDataset, SpatialImageRef, SpatialPolygonTile, SpatialTranscriptTile } from '../../contracts/spatial-dataset.contract';
|
|
5
|
+
import { SpatialDataset, SpatialDensityRaster, SpatialImageRef, SpatialPolygonTile, SpatialTranscriptTile } from '../../contracts/spatial-dataset.contract';
|
|
6
6
|
import { SpatialSelectionMask } from '../../spatial/spatial-selection';
|
|
7
|
+
import { DataRect } from '../../spatial/spatial-tiles';
|
|
7
8
|
/**
|
|
8
9
|
* Level-of-detail cell outlines, transcripts and transcript density for the 2D spatial view.
|
|
9
10
|
*
|
|
@@ -47,6 +48,10 @@ export interface SpatialTileHost {
|
|
|
47
48
|
}): void;
|
|
48
49
|
/** Estimated transcripts in view for the current selection, against the budget. */
|
|
49
50
|
estimateChanged?(estimate: TranscriptEstimate | null): void;
|
|
51
|
+
/** Transcripts of each selected gene inside the view, or null when not known. */
|
|
52
|
+
geneCountsChanged?(counts: Record<string, number> | null): void;
|
|
53
|
+
/** The layers whose data is in flight now ("Transcripts", "Cells"…); empty when none. */
|
|
54
|
+
loadingChanged?(layers: string[]): void;
|
|
50
55
|
}
|
|
51
56
|
/** Xenium Explorer's "Estimated Transcript Points". */
|
|
52
57
|
export interface TranscriptEstimate {
|
|
@@ -73,6 +78,20 @@ interface TranscriptJob {
|
|
|
73
78
|
interface TranscriptLoad {
|
|
74
79
|
merged: SpatialTranscriptTile;
|
|
75
80
|
px: Float32Array;
|
|
81
|
+
/** For a gene selection: group it on this bin ladder once hidden genes are dropped. */
|
|
82
|
+
ladder?: {
|
|
83
|
+
baseBin: number;
|
|
84
|
+
levels: number;
|
|
85
|
+
start: number | null;
|
|
86
|
+
};
|
|
87
|
+
/** Already one marker per cluster per bin (zoomed out, from the density grids): no cells. */
|
|
88
|
+
clustered?: {
|
|
89
|
+
group: Int32Array;
|
|
90
|
+
names: string[];
|
|
91
|
+
genes: string[][];
|
|
92
|
+
bin: number;
|
|
93
|
+
origin: [number, number];
|
|
94
|
+
};
|
|
76
95
|
kind?: TranscriptJob['kind'];
|
|
77
96
|
bin?: TranscriptJob['bin'];
|
|
78
97
|
}
|
|
@@ -101,6 +120,26 @@ export declare class NapariSpatialTileLayers {
|
|
|
101
120
|
private selectionRevision;
|
|
102
121
|
constructor(port: SpatialDataPort, host: SpatialTileHost);
|
|
103
122
|
attach(viewer: Viewer): void;
|
|
123
|
+
/**
|
|
124
|
+
* Group a gene selection for the zoom: one marker per gene-tree cluster (a gene in no
|
|
125
|
+
* cluster is its own) per bin of the pyramid ladder, starting at the finest bin whose
|
|
126
|
+
* markers are 14 px apart and stepping up while the markers would exceed the budget.
|
|
127
|
+
* Null to draw every transcript: zoomed in, with the selection within the budget.
|
|
128
|
+
*/
|
|
129
|
+
private groupSelection;
|
|
130
|
+
/** µm per observation unit of the drawn dataset, for describing a grouped marker's area. */
|
|
131
|
+
private micronsPerUnit;
|
|
132
|
+
/** Loads in flight, per layer label, for the loading badge. */
|
|
133
|
+
private readonly loading;
|
|
134
|
+
/** Report `label` loading while `work` is in flight. */
|
|
135
|
+
private track;
|
|
136
|
+
/** The loaded per-gene transcripts the in-view counts are taken from. */
|
|
137
|
+
private countSource;
|
|
138
|
+
/**
|
|
139
|
+
* Transcripts of each selected gene inside `rect`: each entry counts for the transcripts it
|
|
140
|
+
* stands for (an aggregate at a coarse level holds several). Null without per-gene data.
|
|
141
|
+
*/
|
|
142
|
+
geneCountsIn(rect: DataRect): Record<string, number> | null;
|
|
104
143
|
/** Whether `layer` is one of the overlays drawn here (density, cells, transcripts). */
|
|
105
144
|
owns(layer: Layer): boolean;
|
|
106
145
|
detach(): void;
|
|
@@ -159,6 +198,28 @@ export declare class NapariSpatialTileLayers {
|
|
|
159
198
|
* view would exceed the budget.
|
|
160
199
|
*/
|
|
161
200
|
private geneJob;
|
|
201
|
+
/**
|
|
202
|
+
* A gene selection from the pyramid's per-gene levels: starting at the level whose bins are
|
|
203
|
+
* `startBin`, fetch the view's tiles for the selected genes and step to a coarser level while
|
|
204
|
+
* one marker per cluster per bin would exceed the max. Hidden genes are fetched too (their
|
|
205
|
+
* counts in view still show) and dropped before drawing; grouping by cluster happens there.
|
|
206
|
+
*/
|
|
207
|
+
private geneBinsJob;
|
|
208
|
+
/** The selection's clusters: each gene-tree group's visible genes, then each ungrouped gene. */
|
|
209
|
+
private selectionClusters;
|
|
210
|
+
/**
|
|
211
|
+
* Per-gene density grids summed for `genes`, cached (a cluster is asked for once per bin).
|
|
212
|
+
* Keyed by dataset too: this manager outlives a dataset, and another may share gene names.
|
|
213
|
+
*/
|
|
214
|
+
private densityFor;
|
|
215
|
+
private readonly densityCache;
|
|
216
|
+
/**
|
|
217
|
+
* A gene selection zoomed out: one marker per cluster per bin, from the per-gene density
|
|
218
|
+
* grids. The bin starts where markers are 14 px apart and doubles until the markers fit the
|
|
219
|
+
* max; each marker sits at the count-weighted centre of the finer grid inside it, so markers
|
|
220
|
+
* follow the tissue rather than a lattice.
|
|
221
|
+
*/
|
|
222
|
+
private clusterDensityJob;
|
|
162
223
|
/**
|
|
163
224
|
* Every gene: the transcripts themselves once they fit the budget, else bins of the
|
|
164
225
|
* all-gene pyramid sized by how many transcripts each holds.
|
|
@@ -211,6 +272,11 @@ interface DrawnTranscripts {
|
|
|
211
272
|
radius: Float32Array;
|
|
212
273
|
genes: string[];
|
|
213
274
|
ref: SpatialImageRef | null;
|
|
275
|
+
/** For a grouped gene selection: each entry's group, and the groups' names. */
|
|
276
|
+
entryGroup?: Int32Array;
|
|
277
|
+
groupNames?: string[];
|
|
278
|
+
/** Each group's genes, when the markers came from the density grids (for the hover). */
|
|
279
|
+
groupGenes?: string[][];
|
|
214
280
|
/** Lazily built spatial index: bucket → entry indices. */
|
|
215
281
|
grid: {
|
|
216
282
|
size: number;
|
|
@@ -237,4 +303,13 @@ export declare function filterTranscripts(t: SpatialTranscriptTile, px: Float32A
|
|
|
237
303
|
export declare function mergePolygonTiles(tiles: SpatialPolygonTile[]): SpatialPolygonTile;
|
|
238
304
|
/** Concatenate transcript tiles, stopping at `limit` entries. */
|
|
239
305
|
export declare function mergeTranscriptTiles(tiles: SpatialTranscriptTile[], limit?: number): SpatialTranscriptTile;
|
|
306
|
+
/**
|
|
307
|
+
* One marker per (cluster, `bin` square) from per-cluster density grids: the counts of the grid
|
|
308
|
+
* cells inside `area` summed per square, at their count-weighted centre. `slots[c]` is the gene
|
|
309
|
+
* slot whose icon cluster c's markers take.
|
|
310
|
+
*/
|
|
311
|
+
export declare function clusterMarkers(rasters: SpatialDensityRaster[], slots: number[], bin: number, area: DataRect): {
|
|
312
|
+
tile: SpatialTranscriptTile;
|
|
313
|
+
group: Int32Array;
|
|
314
|
+
};
|
|
240
315
|
export {};
|
|
@@ -168,6 +168,8 @@ export declare class NapariVisualizerService extends BaseStoreVisualizer impleme
|
|
|
168
168
|
private spatialTilesMgr;
|
|
169
169
|
/** For the panel: the transcripts-in-view estimate and the density window in use. */
|
|
170
170
|
readonly transcriptEstimate$: BehaviorSubject<TranscriptEstimate | null>;
|
|
171
|
+
/** Transcripts of each selected gene in view (see NapariSpatialTileLayers.geneCountsIn). */
|
|
172
|
+
readonly geneCountsInView$: BehaviorSubject<Record<string, number> | null>;
|
|
171
173
|
readonly densityStats$: BehaviorSubject<{
|
|
172
174
|
lo: number;
|
|
173
175
|
hi: number;
|
|
@@ -183,6 +185,10 @@ export declare class NapariVisualizerService extends BaseStoreVisualizer impleme
|
|
|
183
185
|
* round-trip, so a fast sequence of colour-by changes can resolve out of
|
|
184
186
|
* order. Only the newest rebuild is allowed to touch the layer. */
|
|
185
187
|
private spatialRebuildToken;
|
|
188
|
+
/** "x reloading…" at the bottom of the canvas: the tile layers' loads and the observations'. */
|
|
189
|
+
private loadingBadge;
|
|
190
|
+
private tileLoading;
|
|
191
|
+
private observationsLoading;
|
|
186
192
|
/** Monotonic load generation. Bumped by {@link reset} and {@link cancelLoading}; the frame-loading
|
|
187
193
|
* loops (volume assembly, surface preload) capture it and bail when it changes, so a Cancel (or a
|
|
188
194
|
* new plot) actually stops fetching frames instead of running to completion in the background. */
|
|
@@ -352,6 +358,7 @@ export declare class NapariVisualizerService extends BaseStoreVisualizer impleme
|
|
|
352
358
|
* The overview navigator for a 2D view with an image: a coarse thumbnail of the whole
|
|
353
359
|
* image with the viewport on it; click or drag to pan at the current zoom (as OSD).
|
|
354
360
|
*/
|
|
361
|
+
private showLoading;
|
|
355
362
|
private installNavigator;
|
|
356
363
|
/**
|
|
357
364
|
* Draw the navigator's thumbnail from the coarsest pyramid level. A multichannel image
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { HttpClient } from '@angular/common/http';
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
3
|
import { SpatialDataPort, TranscriptTileQuery } from '../../contracts/ports/spatial-data.port';
|
|
4
|
-
import { SpatialColumn, SpatialDataset, SpatialDensityRaster, SpatialEmbedding, SpatialPolygonTile, SpatialPolygons, CategoricalColumnMeta, SpatialTranscriptCounts, SpatialTranscriptSummary, SpatialTranscriptTile } from '../../contracts/spatial-dataset.contract';
|
|
4
|
+
import { SpatialColumn, SpatialDataset, SpatialDensityRaster, SpatialEmbedding, SpatialPolygonTile, SpatialPolygons, CategoricalColumnMeta, SpatialMarkerGenes, SpatialTranscriptCounts, SpatialTranscriptSummary, SpatialTranscriptTile } from '../../contracts/spatial-dataset.contract';
|
|
5
5
|
import { SpatialDatasetSummary, SpatialManifest } from './spatial-wire';
|
|
6
6
|
import * as i0 from "@angular/core";
|
|
7
7
|
/**
|
|
@@ -97,6 +97,7 @@ export declare class SpatialDataHttpService implements SpatialDataPort {
|
|
|
97
97
|
getPolygons(): Promise<SpatialPolygons>;
|
|
98
98
|
getPolygonTile(set: string, level: number, gx: number, gy: number): Promise<SpatialPolygonTile>;
|
|
99
99
|
getTranscriptTile(level: number, gx: number, gy: number, query: TranscriptTileQuery): Promise<SpatialTranscriptTile>;
|
|
100
|
+
getTranscriptGeneBins(level: number, tx: number, ty: number, genes: string[]): Promise<SpatialTranscriptTile>;
|
|
100
101
|
getTranscriptBins(level: number, tx: number, ty: number): Promise<SpatialTranscriptTile>;
|
|
101
102
|
/** Small JSON answers, keyed by URL — hovering back and forth must not refetch. */
|
|
102
103
|
private readonly summaryCache;
|
|
@@ -110,6 +111,7 @@ export declare class SpatialDataHttpService implements SpatialDataPort {
|
|
|
110
111
|
column: CategoricalColumnMeta;
|
|
111
112
|
matched: number;
|
|
112
113
|
}>;
|
|
114
|
+
getMarkerGenes(column: string, perGroup?: number): Promise<SpatialMarkerGenes>;
|
|
113
115
|
getTranscriptCounts(genes: string[]): Promise<SpatialTranscriptCounts>;
|
|
114
116
|
/** LRU over tile promises, so concurrent asks share one request and a failure is not kept. */
|
|
115
117
|
private cachedTile;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SpatialColumn, SpatialColumnMeta, SpatialDataset, SpatialDensityMeta, SpatialDensityRaster, SpatialEmbedding, SpatialEmbeddingMeta, SpatialFeatureMeta, SpatialImageRef, SpatialObservations, SpatialPolygonTile, SpatialPolygonTilesMeta, SpatialPolygons, SpatialTranscriptBinsMeta, SpatialTranscriptBinsStatus, SpatialTranscriptTile, SpatialTranscriptTilesMeta, SpatialVolumeMeta } from '../../contracts/spatial-dataset.contract';
|
|
1
|
+
import { SpatialColumn, SpatialColumnMeta, SpatialDataset, SpatialDensityMeta, SpatialDensityRaster, SpatialEmbedding, SpatialEmbeddingMeta, SpatialFeatureMeta, SpatialImageRef, SpatialObservations, SpatialPolygonTile, SpatialPolygonTilesMeta, SpatialPolygons, SpatialTranscriptBinsMeta, SpatialTranscriptGeneBinsMeta, SpatialTranscriptBinsStatus, SpatialTranscriptTile, SpatialTranscriptTilesMeta, SpatialVolumeMeta } from '../../contracts/spatial-dataset.contract';
|
|
2
2
|
/**
|
|
3
3
|
* The wire format the bundled example server speaks, and its decoders.
|
|
4
4
|
*
|
|
@@ -79,6 +79,7 @@ export interface SpatialManifest {
|
|
|
79
79
|
density?: SpatialDensityMeta;
|
|
80
80
|
transcriptBins?: SpatialTranscriptBinsMeta;
|
|
81
81
|
transcriptBinsStatus?: SpatialTranscriptBinsStatus;
|
|
82
|
+
transcriptGeneBins?: SpatialTranscriptGeneBinsMeta;
|
|
82
83
|
}
|
|
83
84
|
/** `GET /spatial/datasets` */
|
|
84
85
|
export interface SpatialDatasetSummary {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* `zoom · scale`. For a Xenium bundle the data unit is the micron and the world unit the
|
|
14
14
|
* morphology pixel.
|
|
15
15
|
*/
|
|
16
|
-
import type { SpatialBounds, SpatialImageRef, SpatialTileLevel } from '../contracts/spatial-dataset.contract';
|
|
16
|
+
import type { SpatialBounds, SpatialImageRef, SpatialTileLevel, SpatialTranscriptTile } from '../contracts/spatial-dataset.contract';
|
|
17
17
|
import type { SpatialViewState } from '../contracts/display-types';
|
|
18
18
|
import type { SpatialDataset } from '../contracts/spatial-dataset.contract';
|
|
19
19
|
/** A rectangle in observation coordinates. */
|
|
@@ -83,6 +83,43 @@ export declare function polygonLevelFor(pxPerUnit: number, cellDiameter: number,
|
|
|
83
83
|
* keeps the view fast.
|
|
84
84
|
*/
|
|
85
85
|
export declare function transcriptLevelFor(pxPerUnit: number, levels: readonly SpatialTileLevel[], targetTilePx?: number): number;
|
|
86
|
+
/**
|
|
87
|
+
* The bin a selection of genes is grouped into at this zoom, in observation units, or null
|
|
88
|
+
* to draw every transcript. Bins follow the all-gene pyramid's ladder (`baseBin` × 2^m) so a
|
|
89
|
+
* gene selection and "all genes" group alike: the finest bin whose markers land at least
|
|
90
|
+
* `spacingPx` apart on screen. Once the finest bin is that wide, transcripts are drawn as
|
|
91
|
+
* themselves.
|
|
92
|
+
*/
|
|
93
|
+
export declare function geneBinSize(pxPerUnit: number, baseBin: number, levels?: number, spacingPx?: number): number | null;
|
|
94
|
+
/** The entries of `t` inside `rect` (observation units). */
|
|
95
|
+
export declare function clipTranscripts(t: SpatialTranscriptTile, rect: DataRect): SpatialTranscriptTile;
|
|
96
|
+
/**
|
|
97
|
+
* Group transcripts into `bin`-wide squares of a grid anchored at the origin (so a pan does
|
|
98
|
+
* not move them), one entry per (group, square): `groupOf(gene slot)` says which group a gene
|
|
99
|
+
* is in — its cluster in the gene tree, or itself. Each entry sits at its transcripts'
|
|
100
|
+
* centroid, weighted by how many each stands for; it takes the gene and the cell holding most
|
|
101
|
+
* of them, each summed over all its transcripts in the entry. `group[i]` is entry i's group.
|
|
102
|
+
*/
|
|
103
|
+
export declare function groupTranscripts(t: SpatialTranscriptTile, bin: number, groupOf?: (geneSlot: number) => number): {
|
|
104
|
+
tile: SpatialTranscriptTile;
|
|
105
|
+
group: Int32Array;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* The colour of each gene-tree cluster of a selection, for colouring transcripts by cluster: a
|
|
109
|
+
* cluster named like a group of the cells' grouping takes that group's colour (its cells and its
|
|
110
|
+
* transcripts agree); the others take palette colours in tree order. Clusters are the tree's
|
|
111
|
+
* groups holding a selected gene, then each ungrouped selected gene on its own. Every gene of a
|
|
112
|
+
* cluster gets the same colour — the tree's swatches and the markers use this one map.
|
|
113
|
+
*/
|
|
114
|
+
export declare function clusterColorMap(genes: readonly string[], groups: readonly {
|
|
115
|
+
name: string;
|
|
116
|
+
genes: readonly string[];
|
|
117
|
+
}[], cellColors: ReadonlyMap<string, string>, palette: readonly string[]): Map<string, string>;
|
|
118
|
+
/** The cluster a selected gene is in: its first gene-tree group holding it, else itself. */
|
|
119
|
+
export declare function clusterOfGene(gene: string, groups: readonly {
|
|
120
|
+
name: string;
|
|
121
|
+
genes: readonly string[];
|
|
122
|
+
}[]): string;
|
|
86
123
|
/** Smallest and largest transcript marker, in canvas pixels. */
|
|
87
124
|
export declare const TRANSCRIPT_MIN_PX = 4;
|
|
88
125
|
export declare const TRANSCRIPT_MAX_PX = 32;
|
|
@@ -390,6 +390,21 @@ export declare class SpatialControlsComponent implements OnInit, OnDestroy {
|
|
|
390
390
|
/** The eye toggle: hide or show genes without removing them from the selection. */
|
|
391
391
|
onGenesShown(genes: string[], on: boolean): void;
|
|
392
392
|
onGeneColor(gene: string, hex: string): void;
|
|
393
|
+
/** Swatches in the picker: the categorical palette genes are coloured from by default. */
|
|
394
|
+
readonly colorPresets: string[];
|
|
395
|
+
/** The gene the open picker edits. */
|
|
396
|
+
styleGene: string | null;
|
|
397
|
+
openGeneStyle(event: Event, gene: string, panel: {
|
|
398
|
+
toggle(e: Event): void;
|
|
399
|
+
hide(): void;
|
|
400
|
+
}): void;
|
|
401
|
+
/** The icon the open picker shows as chosen. */
|
|
402
|
+
get styleGlyph(): TranscriptGlyphName | null;
|
|
403
|
+
glyphPoints(glyph: TranscriptGlyphName): string;
|
|
404
|
+
/** A typed hex colour, accepted as `#rrggbb` or `rrggbb`; anything else is ignored. */
|
|
405
|
+
onGeneHex(gene: string, text: string): void;
|
|
406
|
+
/** Back to the gene's default icon and colour (by its position in the list). */
|
|
407
|
+
resetGeneStyle(gene: string): void;
|
|
393
408
|
/** '±': every gene, or back to the chosen list. */
|
|
394
409
|
onToggleAllGenes(): void;
|
|
395
410
|
/** '⋮' menu. */
|
|
@@ -407,6 +422,39 @@ export declare class SpatialControlsComponent implements OnInit, OnDestroy {
|
|
|
407
422
|
get cellOpacityPercent(): number;
|
|
408
423
|
get densityOpacityPercent(): number;
|
|
409
424
|
geneGroupError: string | null;
|
|
425
|
+
/** Transcripts of each selected gene in the current view, from the renderer. */
|
|
426
|
+
geneCounts: Record<string, number> | null;
|
|
427
|
+
geneCountOf(gene: string): number | null;
|
|
428
|
+
markersOpen: boolean;
|
|
429
|
+
markerColumn: string | null;
|
|
430
|
+
markerPerGroup: number;
|
|
431
|
+
readonly markerPerGroupOptions: {
|
|
432
|
+
label: string;
|
|
433
|
+
value: number;
|
|
434
|
+
}[];
|
|
435
|
+
markerClusters: string[];
|
|
436
|
+
markerClusterOptions: {
|
|
437
|
+
label: string;
|
|
438
|
+
value: string;
|
|
439
|
+
}[];
|
|
440
|
+
markerLoading: boolean;
|
|
441
|
+
markerError: string | null;
|
|
442
|
+
get canAddMarkers(): boolean;
|
|
443
|
+
/** Every categorical column but the segmentation method, which says nothing about genes. */
|
|
444
|
+
get markerColumnOptions(): {
|
|
445
|
+
label: string;
|
|
446
|
+
value: string;
|
|
447
|
+
}[];
|
|
448
|
+
openMarkers(): void;
|
|
449
|
+
onMarkerColumn(column: string): void;
|
|
450
|
+
/** The clusters of the chosen column, all picked to start with. */
|
|
451
|
+
private refreshMarkerClusters;
|
|
452
|
+
/**
|
|
453
|
+
* Add each picked cluster's top marker genes as a gene group named after it, and select
|
|
454
|
+
* them. A gene that marks several clusters goes to the one it is most specific to, so the
|
|
455
|
+
* tree lists it once.
|
|
456
|
+
*/
|
|
457
|
+
addMarkerGenes(): Promise<void>;
|
|
410
458
|
onNewGeneGroup(): void;
|
|
411
459
|
/**
|
|
412
460
|
* Import gene groups: CSV/TSV with a group and a gene column (header optional), e.g.
|
|
@@ -451,6 +499,12 @@ export declare class SpatialControlsComponent implements OnInit, OnDestroy {
|
|
|
451
499
|
onGlyph(gene: string, glyph: TranscriptGlyphName): void;
|
|
452
500
|
/** The colour gene `slot` is drawn in when transcripts are coloured by gene. */
|
|
453
501
|
geneColor(slot: number): string;
|
|
502
|
+
/** Colours of the cells' groups, by name — what a cluster of the same name is drawn in. */
|
|
503
|
+
private cellGroupColors;
|
|
504
|
+
private cellGroupColorsFor;
|
|
505
|
+
private refreshCellGroupColors;
|
|
506
|
+
/** In Cluster colouring, a gene's swatch is its cluster's colour, as its markers are. */
|
|
507
|
+
geneSwatchOf(gene: string): string;
|
|
454
508
|
geneColorOf(gene: string): string;
|
|
455
509
|
reset(): void;
|
|
456
510
|
/**
|
|
@@ -21,6 +21,10 @@ import * as i0 from "@angular/core";
|
|
|
21
21
|
export declare class ToolbarComponent implements OnChanges {
|
|
22
22
|
/** Current image (gates which control groups are shown). */
|
|
23
23
|
imageInfo: IImageInfo | undefined;
|
|
24
|
+
/** A spatial dataset is on offer: its plot modes are a choice even with no image loaded. */
|
|
25
|
+
hasSpatialDataset: boolean;
|
|
26
|
+
/** Whether the plot-type dropdown shows: for an image, or a spatial dataset with none. */
|
|
27
|
+
get showPlotTypes(): boolean;
|
|
24
28
|
/** Plot types the active backend advertises, then any contributed modes. */
|
|
25
29
|
plotTypeOptions: PlotTypeOption[];
|
|
26
30
|
/** The selector's value: a built-in type or a contributed mode's id. */
|
|
@@ -250,5 +254,5 @@ export declare class ToolbarComponent implements OnChanges {
|
|
|
250
254
|
get isIntensityCapable(): boolean;
|
|
251
255
|
showHelp(): void;
|
|
252
256
|
static ɵfac: i0.ɵɵFactoryDeclaration<ToolbarComponent, never>;
|
|
253
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<ToolbarComponent, "plotting-toolbar", never, { "imageInfo": { "alias": "imageInfo"; "required": false; }; "plotTypeOptions": { "alias": "plotTypeOptions"; "required": false; }; "selectedPlotType": { "alias": "selectedPlotType"; "required": false; }; "basePlotType": { "alias": "basePlotType"; "required": false; }; "isoRange": { "alias": "isoRange"; "required": false; }; "maxIndex": { "alias": "maxIndex"; "required": false; }; "zIndex": { "alias": "zIndex"; "required": false; }; "isHeatmap": { "alias": "isHeatmap"; "required": false; }; "is3dRegions": { "alias": "is3dRegions"; "required": false; }; "activeDragMode": { "alias": "activeDragMode"; "required": false; }; "activeSurface3dMode": { "alias": "activeSurface3dMode"; "required": false; }; "axesVisible": { "alias": "axesVisible"; "required": false; }; "wireframeActive": { "alias": "wireframeActive"; "required": false; }; "resolutionScale": { "alias": "resolutionScale"; "required": false; }; "wandSensitivity": { "alias": "wandSensitivity"; "required": false; }; "brushSize": { "alias": "brushSize"; "required": false; }; "vertexEraserRadius": { "alias": "vertexEraserRadius"; "required": false; }; "canUndo": { "alias": "canUndo"; "required": false; }; "canRedo": { "alias": "canRedo"; "required": false; }; "stackOptions": { "alias": "stackOptions"; "required": false; }; "tools": { "alias": "tools"; "required": false; }; "imageSmoothingEnabled": { "alias": "imageSmoothingEnabled"; "required": false; }; "samModels": { "alias": "samModels"; "required": false; }; "samModelId": { "alias": "samModelId"; "required": false; }; "contributedTools": { "alias": "contributedTools"; "required": false; }; "dialogTools": { "alias": "dialogTools"; "required": false; }; "openDialogToolId": { "alias": "openDialogToolId"; "required": false; }; "toolModelIds": { "alias": "toolModelIds"; "required": false; }; }, { "selectPlotType": "selectPlotType"; "addProfileLine": "addProfileLine"; "toggleImageSmoothing": "toggleImageSmoothing"; "isoRangeChange": "isoRangeChange"; "openChannelHistogram": "openChannelHistogram"; "openSpatialControls": "openSpatialControls"; "openRegionEditor": "openRegionEditor"; "selectStackOption": "selectStackOption"; "zScrub": "zScrub"; "zSlide": "zSlide"; "zIndexInput": "zIndexInput"; "reloadAndPlot": "reloadAndPlot"; "downloadImage": "downloadImage"; "autoscaleImage": "autoscaleImage"; "toggleDragMode": "toggleDragMode"; "zoomIn": "zoomIn"; "zoomOut": "zoomOut"; "toggleSurface3dMode": "toggleSurface3dMode"; "resetSurfaceCamera": "resetSurfaceCamera"; "toggleAxes": "toggleAxes"; "toggleWireframe": "toggleWireframe"; "selectResolution": "selectResolution"; "deleteRegion": "deleteRegion"; "undoRegion": "undoRegion"; "redoRegion": "redoRegion"; "segmentRegions": "segmentRegions"; "segmentCellpose": "segmentCellpose"; "runTool": "runTool"; "toggleDialogTool": "toggleDialogTool"; "toolModelChange": "toolModelChange"; "openToolParams": "openToolParams"; "samModelChange": "samModelChange"; "wandSensitivityChange": "wandSensitivityChange"; "brushSizeChange": "brushSizeChange"; "vertexEraserRadiusChange": "vertexEraserRadiusChange"; }, never, ["*"], false, never>;
|
|
257
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ToolbarComponent, "plotting-toolbar", never, { "imageInfo": { "alias": "imageInfo"; "required": false; }; "hasSpatialDataset": { "alias": "hasSpatialDataset"; "required": false; }; "plotTypeOptions": { "alias": "plotTypeOptions"; "required": false; }; "selectedPlotType": { "alias": "selectedPlotType"; "required": false; }; "basePlotType": { "alias": "basePlotType"; "required": false; }; "isoRange": { "alias": "isoRange"; "required": false; }; "maxIndex": { "alias": "maxIndex"; "required": false; }; "zIndex": { "alias": "zIndex"; "required": false; }; "isHeatmap": { "alias": "isHeatmap"; "required": false; }; "is3dRegions": { "alias": "is3dRegions"; "required": false; }; "activeDragMode": { "alias": "activeDragMode"; "required": false; }; "activeSurface3dMode": { "alias": "activeSurface3dMode"; "required": false; }; "axesVisible": { "alias": "axesVisible"; "required": false; }; "wireframeActive": { "alias": "wireframeActive"; "required": false; }; "resolutionScale": { "alias": "resolutionScale"; "required": false; }; "wandSensitivity": { "alias": "wandSensitivity"; "required": false; }; "brushSize": { "alias": "brushSize"; "required": false; }; "vertexEraserRadius": { "alias": "vertexEraserRadius"; "required": false; }; "canUndo": { "alias": "canUndo"; "required": false; }; "canRedo": { "alias": "canRedo"; "required": false; }; "stackOptions": { "alias": "stackOptions"; "required": false; }; "tools": { "alias": "tools"; "required": false; }; "imageSmoothingEnabled": { "alias": "imageSmoothingEnabled"; "required": false; }; "samModels": { "alias": "samModels"; "required": false; }; "samModelId": { "alias": "samModelId"; "required": false; }; "contributedTools": { "alias": "contributedTools"; "required": false; }; "dialogTools": { "alias": "dialogTools"; "required": false; }; "openDialogToolId": { "alias": "openDialogToolId"; "required": false; }; "toolModelIds": { "alias": "toolModelIds"; "required": false; }; }, { "selectPlotType": "selectPlotType"; "addProfileLine": "addProfileLine"; "toggleImageSmoothing": "toggleImageSmoothing"; "isoRangeChange": "isoRangeChange"; "openChannelHistogram": "openChannelHistogram"; "openSpatialControls": "openSpatialControls"; "openRegionEditor": "openRegionEditor"; "selectStackOption": "selectStackOption"; "zScrub": "zScrub"; "zSlide": "zSlide"; "zIndexInput": "zIndexInput"; "reloadAndPlot": "reloadAndPlot"; "downloadImage": "downloadImage"; "autoscaleImage": "autoscaleImage"; "toggleDragMode": "toggleDragMode"; "zoomIn": "zoomIn"; "zoomOut": "zoomOut"; "toggleSurface3dMode": "toggleSurface3dMode"; "resetSurfaceCamera": "resetSurfaceCamera"; "toggleAxes": "toggleAxes"; "toggleWireframe": "toggleWireframe"; "selectResolution": "selectResolution"; "deleteRegion": "deleteRegion"; "undoRegion": "undoRegion"; "redoRegion": "redoRegion"; "segmentRegions": "segmentRegions"; "segmentCellpose": "segmentCellpose"; "runTool": "runTool"; "toggleDialogTool": "toggleDialogTool"; "toolModelChange": "toolModelChange"; "openToolParams": "openToolParams"; "samModelChange": "samModelChange"; "wandSensitivityChange": "wandSensitivityChange"; "brushSizeChange": "brushSizeChange"; "vertexEraserRadiusChange": "vertexEraserRadiusChange"; }, never, ["*"], false, never>;
|
|
254
258
|
}
|
|
@@ -309,7 +309,10 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
|
|
|
309
309
|
isoRange: number[];
|
|
310
310
|
/** Whether a spatial-omics dataset is currently published on
|
|
311
311
|
* `SPATIAL_DATA_PORT` — gates the spatial plot types in the selector. */
|
|
312
|
-
|
|
312
|
+
/** Bound by the toolbar, which offers the plot modes for a dataset with no image too. */
|
|
313
|
+
hasSpatialDataset: boolean;
|
|
314
|
+
/** The spatial dataset on offer, for drawing one that brings no image (see reloadAndPlot). */
|
|
315
|
+
private spatialDataset;
|
|
313
316
|
/** Whether that dataset's observations carry a z, gating the 3D spatial mode. */
|
|
314
317
|
private hasSpatial3dDataset;
|
|
315
318
|
/** Whether it carries a registered volume. Change detection only: a volume
|
|
@@ -465,6 +468,12 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
|
|
|
465
468
|
/** Move the displayed slice by `delta`, clamped to the stack bounds. */
|
|
466
469
|
stepSlice(delta: number): void;
|
|
467
470
|
reloadAndPlot(): void;
|
|
471
|
+
/**
|
|
472
|
+
* Draw a spatial mode with no image behind it: the observations alone, framed on their own
|
|
473
|
+
* extent, as the seqFISH example shows them. The image info is a placeholder naming the
|
|
474
|
+
* dataset, so regions drawn here are kept per dataset like any image's.
|
|
475
|
+
*/
|
|
476
|
+
private plotSpatialWithoutImage;
|
|
468
477
|
cancelLoading(): void;
|
|
469
478
|
downloadImage(): void;
|
|
470
479
|
autoscaleImage(): void;
|