@vitessce/scatterplot-embedding 3.5.9 → 3.5.11
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/dist/{deflate-38080dfa.js → deflate-7f3bb131.js} +1 -1
- package/dist/{index-5909753b.js → index-ab73546f.js} +10370 -5695
- package/dist/index.js +1 -1
- package/dist/{jpeg-8c087dbd.js → jpeg-6b5f0200.js} +1 -1
- package/dist/{lerc-6f97422d.js → lerc-6a84a90d.js} +1 -1
- package/dist/{lzw-551d533e.js → lzw-ec516fe1.js} +1 -1
- package/dist/{packbits-da285c36.js → packbits-28d4e412.js} +1 -1
- package/dist/{raw-44cfcc64.js → raw-140846a5.js} +1 -1
- package/dist/{webimage-201fd4a3.js → webimage-5e317232.js} +1 -1
- package/dist-tsc/DualEmbeddingScatterplotSubscriber.d.ts.map +1 -1
- package/dist-tsc/DualEmbeddingScatterplotSubscriber.js +4 -1
- package/dist-tsc/EmbeddingScatterplotSubscriber.d.ts.map +1 -1
- package/dist-tsc/EmbeddingScatterplotSubscriber.js +47 -14
- package/package.json +8 -7
- package/src/DualEmbeddingScatterplotSubscriber.js +17 -10
- package/src/EmbeddingScatterplotSubscriber.js +68 -24
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as inflate_1 } from "./pako.esm-68f84e2a.js";
|
|
2
|
-
import { g as getDefaultExportFromCjs, B as BaseDecoder } from "./index-
|
|
2
|
+
import { g as getDefaultExportFromCjs, B as BaseDecoder } from "./index-ab73546f.js";
|
|
3
3
|
import "react";
|
|
4
4
|
import "@vitessce/vit-s";
|
|
5
5
|
import "react-dom";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DualEmbeddingScatterplotSubscriber.d.ts","sourceRoot":"","sources":["../src/DualEmbeddingScatterplotSubscriber.js"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;GAeG;AACH,0DAVG;IAAsB,IAAI,EAAlB,MAAM;IACQ,KAAK,EAAnB,MAAM;IACQ,kBAAkB,EAAhC,MAAM;IAEU,mBAAmB;IAErB,KAAK,EAAnB,MAAM;IACQ,kBAAkB,EAAhC,MAAM;CAEhB,
|
|
1
|
+
{"version":3,"file":"DualEmbeddingScatterplotSubscriber.d.ts","sourceRoot":"","sources":["../src/DualEmbeddingScatterplotSubscriber.js"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;GAeG;AACH,0DAVG;IAAsB,IAAI,EAAlB,MAAM;IACQ,KAAK,EAAnB,MAAM;IACQ,kBAAkB,EAAhC,MAAM;IAEU,mBAAmB;IAErB,KAAK,EAAnB,MAAM;IACQ,kBAAkB,EAAhC,MAAM;CAEhB,eAmDA"}
|
|
@@ -23,11 +23,14 @@ export function DualEmbeddingScatterplotSubscriber(props) {
|
|
|
23
23
|
const { uuid, coordinationScopes, } = props;
|
|
24
24
|
// Get "props" from the coordination space.
|
|
25
25
|
const [{ embeddingType, sampleSetSelection, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.DUAL_SCATTERPLOT], coordinationScopes);
|
|
26
|
+
const isCaseCtrl = Array.isArray(sampleSetSelection) && sampleSetSelection.length === 2;
|
|
26
27
|
const caseSampleSetSelection = useMemo(() => (sampleSetSelection?.[0]
|
|
27
28
|
? [sampleSetSelection[0]]
|
|
28
29
|
: null), [sampleSetSelection]);
|
|
29
30
|
const ctrlSampleSetSelection = useMemo(() => (sampleSetSelection?.[1]
|
|
30
31
|
? [sampleSetSelection[1]]
|
|
31
32
|
: null), [sampleSetSelection]);
|
|
32
|
-
return (_jsxs("div", { style: { width: '100%', height: '100%', display: 'flex', flexDirection: 'row' }, children: [_jsx("div", { style: { width: '50%'
|
|
33
|
+
return (_jsxs("div", { style: { width: '100%', height: '100%', display: 'flex', flexDirection: 'row' }, children: [_jsx("div", { style: { width: isCaseCtrl ? '50%' : '100%', display: 'flex', flexDirection: 'column' }, children: _jsx(EmbeddingScatterplotSubscriber, { ...props, uuid: `${uuid}-case`, title: (isCaseCtrl
|
|
34
|
+
? `Scatterplot (${embeddingType}), ${caseSampleSetSelection?.[0]?.at(-1)}`
|
|
35
|
+
: null), sampleSetSelection: caseSampleSetSelection }) }), isCaseCtrl ? (_jsx("div", { style: { width: '50%', display: 'flex', flexDirection: 'column' }, children: _jsx(EmbeddingScatterplotSubscriber, { ...props, uuid: `${uuid}-ctrl`, title: `Scatterplot (${embeddingType}), ${ctrlSampleSetSelection?.[0]?.at(-1)}`, sampleSetSelection: ctrlSampleSetSelection }) })) : null] }));
|
|
33
36
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmbeddingScatterplotSubscriber.d.ts","sourceRoot":"","sources":["../src/EmbeddingScatterplotSubscriber.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"EmbeddingScatterplotSubscriber.d.ts","sourceRoot":"","sources":["../src/EmbeddingScatterplotSubscriber.js"],"names":[],"mappings":"AA4CA;;;;;;;;;;;;GAYG;AACH,sDAVG;IAAsB,IAAI,EAAlB,MAAM;IACQ,KAAK,EAAnB,MAAM;IACQ,kBAAkB,EAAhC,MAAM;IAEU,mBAAmB;IAErB,KAAK,EAAnB,MAAM;IACQ,kBAAkB,EAAhC,MAAM;CAEhB,eAkkBA"}
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import React, { useState, useEffect, useCallback, useMemo, } from 'react';
|
|
3
3
|
import { extent, quantileSorted } from 'd3-array';
|
|
4
4
|
import { isEqual } from 'lodash-es';
|
|
5
|
+
import { circle } from '@turf/circle';
|
|
5
6
|
import { TitleInfo, useReady, useUrls, useDeckCanvasSize, useUint8FeatureSelection, useExpressionValueGetter, useGetObsInfo, useObsEmbeddingData, useObsSetsData, useFeatureSelection, useObsFeatureMatrixIndices, useFeatureLabelsData, useMultiObsLabels, useSampleSetsData, useSampleEdgesData, useCoordination, useLoaders, useSetComponentHover, useSetComponentViewInfo, useInitialCoordination, useExpandedFeatureLabelsMap, } from '@vitessce/vit-s';
|
|
6
7
|
import { setObsSelection, mergeObsSets, getCellSetPolygons, getCellColors, stratifyArrays, } from '@vitessce/sets-utils';
|
|
7
8
|
import { pluralize as plur, commaNumber } from '@vitessce/utils';
|
|
@@ -9,6 +10,7 @@ import { Scatterplot, ScatterplotTooltipSubscriber, ScatterplotOptions, getPoint
|
|
|
9
10
|
import { Legend } from '@vitessce/legend';
|
|
10
11
|
import { ViewType, COMPONENT_COORDINATION_TYPES, ViewHelpMapping } from '@vitessce/constants-internal';
|
|
11
12
|
import { DEFAULT_CONTOUR_PERCENTILES } from './constants.js';
|
|
13
|
+
const DEFAULT_FEATURE_AGGREGATION_STRATEGY = 'first';
|
|
12
14
|
/**
|
|
13
15
|
* A subscriber component for the scatterplot.
|
|
14
16
|
* @param {object} props
|
|
@@ -32,11 +34,13 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
32
34
|
const setComponentHover = useSetComponentHover();
|
|
33
35
|
const setComponentViewInfo = useSetComponentViewInfo(uuid);
|
|
34
36
|
// Get "props" from the coordination space.
|
|
35
|
-
const [{ dataset, obsType, featureType, featureValueType, sampleType, embeddingZoom: zoom, embeddingTargetX: targetX, embeddingTargetY: targetY, embeddingTargetZ: targetZ, embeddingType: mapping, obsFilter: cellFilter, obsHighlight: cellHighlight, featureSelection: geneSelection, obsSetSelection: cellSetSelection, obsSetColor: cellSetColor, obsColorEncoding: cellColorEncoding, additionalObsSets: additionalCellSets, embeddingObsSetPolygonsVisible: cellSetPolygonsVisible, embeddingObsSetLabelsVisible: cellSetLabelsVisible, embeddingObsSetLabelSize: cellSetLabelSize, embeddingObsRadius: cellRadiusFixed, embeddingObsRadiusMode: cellRadiusMode, embeddingObsOpacity: cellOpacityFixed, embeddingObsOpacityMode: cellOpacityMode, featureValueColormap: geneExpressionColormap, featureValueColormapRange: geneExpressionColormapRange, tooltipsVisible, sampleSetSelection: sampleSetSelectionFromCoordination, sampleSetColor, embeddingPointsVisible, embeddingContoursVisible, embeddingContoursFilled, embeddingContourPercentiles: contourPercentiles, contourColorEncoding, contourColor, }, { setEmbeddingZoom: setZoom, setEmbeddingTargetX: setTargetX, setEmbeddingTargetY: setTargetY, setEmbeddingTargetZ: setTargetZ, setObsFilter: setCellFilter, setObsSetSelection: setCellSetSelection, setObsHighlight: setCellHighlight, setObsSetColor: setCellSetColor, setObsColorEncoding: setCellColorEncoding, setAdditionalObsSets: setAdditionalCellSets, setEmbeddingObsSetPolygonsVisible: setCellSetPolygonsVisible, setEmbeddingObsSetLabelsVisible: setCellSetLabelsVisible, setEmbeddingObsSetLabelSize: setCellSetLabelSize, setEmbeddingObsRadius: setCellRadiusFixed, setEmbeddingObsRadiusMode: setCellRadiusMode, setEmbeddingObsOpacity: setCellOpacityFixed, setEmbeddingObsOpacityMode: setCellOpacityMode, setFeatureValueColormap: setGeneExpressionColormap, setFeatureValueColormapRange: setGeneExpressionColormapRange, setTooltipsVisible, setEmbeddingPointsVisible, setEmbeddingContoursVisible, setEmbeddingContoursFilled, setEmbeddingContourPercentiles: setContourPercentiles, setContourColorEncoding, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.SCATTERPLOT], coordinationScopes);
|
|
37
|
+
const [{ dataset, obsType, featureType, featureValueType, sampleType, embeddingZoom: zoom, embeddingTargetX: targetX, embeddingTargetY: targetY, embeddingTargetZ: targetZ, embeddingType: mapping, obsFilter: cellFilter, obsHighlight: cellHighlight, featureSelection: geneSelection, obsSetSelection: cellSetSelection, obsSetColor: cellSetColor, obsColorEncoding: cellColorEncoding, additionalObsSets: additionalCellSets, embeddingObsSetPolygonsVisible: cellSetPolygonsVisible, embeddingObsSetLabelsVisible: cellSetLabelsVisible, embeddingObsSetLabelSize: cellSetLabelSize, embeddingObsRadius: cellRadiusFixed, embeddingObsRadiusMode: cellRadiusMode, embeddingObsOpacity: cellOpacityFixed, embeddingObsOpacityMode: cellOpacityMode, featureValueColormap: geneExpressionColormap, featureValueColormapRange: geneExpressionColormapRange, tooltipsVisible, sampleSetSelection: sampleSetSelectionFromCoordination, sampleSetColor, embeddingPointsVisible, embeddingContoursVisible, embeddingContoursFilled, embeddingContourPercentiles: contourPercentiles, contourColorEncoding, contourColor, featureAggregationStrategy, }, { setEmbeddingZoom: setZoom, setEmbeddingTargetX: setTargetX, setEmbeddingTargetY: setTargetY, setEmbeddingTargetZ: setTargetZ, setObsFilter: setCellFilter, setObsSetSelection: setCellSetSelection, setObsHighlight: setCellHighlight, setObsSetColor: setCellSetColor, setObsColorEncoding: setCellColorEncoding, setAdditionalObsSets: setAdditionalCellSets, setEmbeddingObsSetPolygonsVisible: setCellSetPolygonsVisible, setEmbeddingObsSetLabelsVisible: setCellSetLabelsVisible, setEmbeddingObsSetLabelSize: setCellSetLabelSize, setEmbeddingObsRadius: setCellRadiusFixed, setEmbeddingObsRadiusMode: setCellRadiusMode, setEmbeddingObsOpacity: setCellOpacityFixed, setEmbeddingObsOpacityMode: setCellOpacityMode, setFeatureValueColormap: setGeneExpressionColormap, setFeatureValueColormapRange: setGeneExpressionColormapRange, setTooltipsVisible, setEmbeddingPointsVisible, setEmbeddingContoursVisible, setEmbeddingContoursFilled, setEmbeddingContourPercentiles: setContourPercentiles, setContourColorEncoding, setFeatureAggregationStrategy, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.SCATTERPLOT], coordinationScopes);
|
|
36
38
|
const { embeddingZoom: initialZoom, embeddingTargetX: initialTargetX, embeddingTargetY: initialTargetY, } = useInitialCoordination(COMPONENT_COORDINATION_TYPES[ViewType.SCATTERPLOT], coordinationScopes);
|
|
37
39
|
const observationsLabel = observationsLabelOverride || obsType;
|
|
38
40
|
const sampleSetSelection = (sampleSetSelectionFromProps
|
|
39
41
|
|| sampleSetSelectionFromCoordination);
|
|
42
|
+
const featureAggregationStrategyToUse = featureAggregationStrategy
|
|
43
|
+
?? DEFAULT_FEATURE_AGGREGATION_STRATEGY;
|
|
40
44
|
const [width, height, deckRef] = useDeckCanvasSize();
|
|
41
45
|
const title = titleOverride || `Scatterplot (${mapping})`;
|
|
42
46
|
const [obsLabelsTypes, obsLabelsData] = useMultiObsLabels(coordinationScopes, obsType, loaders, dataset);
|
|
@@ -128,7 +132,7 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
128
132
|
// compute the cell radius scale based on the
|
|
129
133
|
// extents of the cell coordinates on the x/y axes.
|
|
130
134
|
useEffect(() => {
|
|
131
|
-
if (xRange && yRange) {
|
|
135
|
+
if (xRange && yRange && width && height) {
|
|
132
136
|
const pointSizeDevicePixels = getPointSizeDevicePixels(window.devicePixelRatio, zoom, xRange, yRange, width, height);
|
|
133
137
|
setDynamicCellRadius(pointSizeDevicePixels);
|
|
134
138
|
const nextCellOpacityScale = getPointOpacity(zoom, xRange, yRange, width, height, numCells, averageFillDensity);
|
|
@@ -189,8 +193,34 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
189
193
|
.map(t => Math.max(t, 1.0));
|
|
190
194
|
return thresholds;
|
|
191
195
|
}
|
|
192
|
-
return
|
|
196
|
+
return [1, 10, 100];
|
|
193
197
|
}, [contourPercentiles, sortedWeights]);
|
|
198
|
+
// Construct a circle polygon using Turf's circle function,
|
|
199
|
+
// which surrounds all points in the scatterplot,
|
|
200
|
+
// which we can use to position text labels along.
|
|
201
|
+
const circleInfo = useMemo(() => {
|
|
202
|
+
if (!originalViewState || !width || !height) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
const center = [
|
|
206
|
+
originalViewState.target[0],
|
|
207
|
+
originalViewState.target[1],
|
|
208
|
+
];
|
|
209
|
+
const scaleFactor = (2 ** originalViewState.zoom);
|
|
210
|
+
if (!(typeof scaleFactor === 'number' && typeof center[0] === 'number' && typeof center[1] === 'number') || Number.isNaN(scaleFactor)) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
const radius = Math.min(width, height) / 2 / scaleFactor;
|
|
214
|
+
const numPoints = 96;
|
|
215
|
+
const options = { steps: numPoints, units: 'degrees' };
|
|
216
|
+
const circlePolygon = circle(center, radius, options);
|
|
217
|
+
return {
|
|
218
|
+
center,
|
|
219
|
+
radius,
|
|
220
|
+
polygon: circlePolygon,
|
|
221
|
+
steps: numPoints,
|
|
222
|
+
};
|
|
223
|
+
}, [originalViewState, width, height]);
|
|
194
224
|
// It is possible for the embedding index+data to be out of order
|
|
195
225
|
// with respect to the matrix index+data. Here, we align the embedding
|
|
196
226
|
// data so that the rows are ordered the same as the matrix rows.
|
|
@@ -239,20 +269,19 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
239
269
|
return null;
|
|
240
270
|
}, [sampleEdges]);
|
|
241
271
|
// Stratify multiple arrays: per-cellSet and per-sampleSet.
|
|
242
|
-
const stratifiedData = useMemo(() => {
|
|
272
|
+
const [stratifiedData, stratifiedDataCount] = useMemo(() => {
|
|
243
273
|
if (alignedEmbeddingData?.data) {
|
|
244
|
-
const result = stratifyArrays(sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetSelection, alignedEmbeddingIndex, mergedCellSets, cellSetSelection, {
|
|
274
|
+
const [result, cellCountResult] = stratifyArrays(sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetSelection, alignedEmbeddingIndex, mergedCellSets, cellSetSelection, {
|
|
245
275
|
obsEmbeddingX: alignedEmbeddingData.data[0],
|
|
246
276
|
obsEmbeddingY: alignedEmbeddingData.data[1],
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
return result;
|
|
277
|
+
...(uint8ExpressionData?.[0] ? { featureValue: uint8ExpressionData } : {}),
|
|
278
|
+
}, featureAggregationStrategyToUse);
|
|
279
|
+
return [result, cellCountResult];
|
|
251
280
|
}
|
|
252
|
-
return null;
|
|
281
|
+
return [null, null];
|
|
253
282
|
}, [alignedEmbeddingIndex, alignedEmbeddingData, uint8ExpressionData,
|
|
254
283
|
sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetSelection,
|
|
255
|
-
cellSetSelection, mergedCellSets,
|
|
284
|
+
cellSetSelection, mergedCellSets, featureAggregationStrategyToUse,
|
|
256
285
|
]);
|
|
257
286
|
const setViewState = ({ zoom: newZoom, target }) => {
|
|
258
287
|
setZoom(newZoom);
|
|
@@ -260,12 +289,16 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
260
289
|
setTargetY(target[1]);
|
|
261
290
|
setTargetZ(target[2] || 0);
|
|
262
291
|
};
|
|
263
|
-
|
|
292
|
+
// TODO: Update this once the rendered points reflects the selection/filtering.
|
|
293
|
+
const cellCountToUse = embeddingPointsVisible
|
|
294
|
+
? cellsCount
|
|
295
|
+
: (stratifiedDataCount ?? cellsCount);
|
|
296
|
+
return (_jsxs(TitleInfo, { title: title, info: `${commaNumber(cellCountToUse)} ${plur(observationsLabel, cellCountToUse)}`, closeButtonVisible: closeButtonVisible, downloadButtonVisible: downloadButtonVisible, removeGridComponent: removeGridComponent, urls: urls, theme: theme, isReady: isReady, helpText: helpText, options: (_jsx(ScatterplotOptions, { observationsLabel: observationsLabel, cellRadius: cellRadiusFixed, setCellRadius: setCellRadiusFixed, cellRadiusMode: cellRadiusMode, setCellRadiusMode: setCellRadiusMode, cellOpacity: cellOpacityFixed, setCellOpacity: setCellOpacityFixed, cellOpacityMode: cellOpacityMode, setCellOpacityMode: setCellOpacityMode, cellSetLabelsVisible: cellSetLabelsVisible, setCellSetLabelsVisible: setCellSetLabelsVisible, tooltipsVisible: tooltipsVisible, setTooltipsVisible: setTooltipsVisible, cellSetLabelSize: cellSetLabelSize, setCellSetLabelSize: setCellSetLabelSize, cellSetPolygonsVisible: cellSetPolygonsVisible, setCellSetPolygonsVisible: setCellSetPolygonsVisible, cellColorEncoding: cellColorEncoding, setCellColorEncoding: setCellColorEncoding, geneExpressionColormap: geneExpressionColormap, setGeneExpressionColormap: setGeneExpressionColormap, geneExpressionColormapRange: geneExpressionColormapRange, setGeneExpressionColormapRange: setGeneExpressionColormapRange, embeddingPointsVisible: embeddingPointsVisible, setEmbeddingPointsVisible: setEmbeddingPointsVisible, embeddingContoursVisible: embeddingContoursVisible, setEmbeddingContoursVisible: setEmbeddingContoursVisible, embeddingContoursFilled: embeddingContoursFilled, setEmbeddingContoursFilled: setEmbeddingContoursFilled, contourPercentiles: contourPercentiles, setContourPercentiles: setContourPercentiles, defaultContourPercentiles: DEFAULT_CONTOUR_PERCENTILES, contourColorEncoding: contourColorEncoding, setContourColorEncoding: setContourColorEncoding, featureAggregationStrategy: featureAggregationStrategy, setFeatureAggregationStrategy: setFeatureAggregationStrategy })), children: [_jsx(Scatterplot, { ref: deckRef, uuid: uuid, theme: theme, viewState: { zoom, target: [targetX, targetY, targetZ] }, setViewState: setViewState, originalViewState: originalViewState, obsEmbeddingIndex: obsEmbeddingIndex, obsEmbedding: obsEmbedding, cellFilter: cellFilter, cellSelection: cellSelection, cellHighlight: cellHighlight, cellColors: cellColors, cellSetPolygons: cellSetPolygons, cellSetLabelSize: cellSetLabelSize, cellSetLabelsVisible: cellSetLabelsVisible, cellSetPolygonsVisible: cellSetPolygonsVisible, setCellFilter: setCellFilter, setCellSelection: setCellSelectionProp, setCellHighlight: setCellHighlight, cellRadius: cellRadius, cellOpacity: cellOpacity, cellColorEncoding: cellColorEncoding, geneExpressionColormap: geneExpressionColormap, geneExpressionColormapRange: geneExpressionColormapRange, setComponentHover: () => {
|
|
264
297
|
setComponentHover(uuid);
|
|
265
298
|
}, updateViewInfo: setComponentViewInfo, getExpressionValue: getExpressionValue, getCellIsSelected: getCellIsSelected, obsSetSelection: cellSetSelection, sampleSetSelection: sampleSetSelection,
|
|
266
299
|
// InternMap data structures where keys are
|
|
267
300
|
// obsSet -> sampleSet -> arrayKey -> [].
|
|
268
|
-
stratifiedData: stratifiedData, obsSetColor: cellSetColor, sampleSetColor: sampleSetColor, contourThresholds: contourThresholds, contourColorEncoding: contourColorEncoding, contourColor: contourColor, contoursFilled: embeddingContoursFilled, embeddingPointsVisible: embeddingPointsVisible, embeddingContoursVisible: embeddingContoursVisible }), tooltipsVisible && (_jsx(ScatterplotTooltipSubscriber, { parentUuid: uuid, obsHighlight: cellHighlight, width: width, height: height, getObsInfo: getObsInfo, featureType: featureType, featureLabelsMap: featureLabelsMap })), _jsx(Legend, { visible: true, theme: theme, featureType: featureType, featureValueType: featureValueType, obsColorEncoding: cellColorEncoding, featureSelection: geneSelection, featureLabelsMap: featureLabelsMap, featureValueColormap: geneExpressionColormap, featureValueColormapRange: geneExpressionColormapRange, obsSetSelection: cellSetSelection, extent: expressionExtents
|
|
301
|
+
stratifiedData: stratifiedData, obsSetColor: cellSetColor, sampleSetColor: sampleSetColor, contourThresholds: contourThresholds, contourColorEncoding: contourColorEncoding, contourColor: contourColor, contoursFilled: embeddingContoursFilled, embeddingPointsVisible: embeddingPointsVisible, embeddingContoursVisible: embeddingContoursVisible, circleInfo: circleInfo, featureSelection: geneSelection }), tooltipsVisible && width && height ? (_jsx(ScatterplotTooltipSubscriber, { parentUuid: uuid, obsHighlight: cellHighlight, width: width, height: height, getObsInfo: getObsInfo, featureType: featureType, featureLabelsMap: featureLabelsMap })) : null, _jsx(Legend, { visible: true, theme: theme, featureType: featureType, featureValueType: featureValueType, obsColorEncoding: cellColorEncoding, featureSelection: geneSelection, featureLabelsMap: featureLabelsMap, featureValueColormap: geneExpressionColormap, featureValueColormapRange: geneExpressionColormapRange, obsSetSelection: cellSetSelection, extent: expressionExtents, missing: expressionMissing,
|
|
269
302
|
// Contour percentile legend
|
|
270
|
-
pointsVisible: embeddingPointsVisible, contoursVisible: embeddingContoursVisible, contoursFilled: embeddingContoursFilled, contourPercentiles: contourPercentiles || DEFAULT_CONTOUR_PERCENTILES, contourThresholds: contourThresholds })] }));
|
|
303
|
+
pointsVisible: embeddingPointsVisible, contoursVisible: embeddingContoursVisible, contoursFilled: embeddingContoursFilled, contourPercentiles: contourPercentiles || DEFAULT_CONTOUR_PERCENTILES, contourThresholds: contourThresholds, featureAggregationStrategy: featureAggregationStrategyToUse })] }));
|
|
271
304
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitessce/scatterplot-embedding",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.11",
|
|
4
4
|
"author": "HIDIVE Lab at HMS",
|
|
5
5
|
"homepage": "http://vitessce.io",
|
|
6
6
|
"repository": {
|
|
@@ -20,12 +20,13 @@
|
|
|
20
20
|
"d3-array": "^2.4.0",
|
|
21
21
|
"lodash-es": "^4.17.21",
|
|
22
22
|
"react-aria": "^3.28.0",
|
|
23
|
-
"@
|
|
24
|
-
"@vitessce/
|
|
25
|
-
"@vitessce/
|
|
26
|
-
"@vitessce/
|
|
27
|
-
"@vitessce/utils": "3.5.
|
|
28
|
-
"@vitessce/
|
|
23
|
+
"@turf/circle": "^7.2.0",
|
|
24
|
+
"@vitessce/constants-internal": "3.5.11",
|
|
25
|
+
"@vitessce/legend": "3.5.11",
|
|
26
|
+
"@vitessce/scatterplot": "3.5.11",
|
|
27
|
+
"@vitessce/sets-utils": "3.5.11",
|
|
28
|
+
"@vitessce/utils": "3.5.11",
|
|
29
|
+
"@vitessce/vit-s": "3.5.11"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"react": "^18.0.0",
|
|
@@ -34,6 +34,8 @@ export function DualEmbeddingScatterplotSubscriber(props) {
|
|
|
34
34
|
sampleSetSelection,
|
|
35
35
|
}] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.DUAL_SCATTERPLOT], coordinationScopes);
|
|
36
36
|
|
|
37
|
+
const isCaseCtrl = Array.isArray(sampleSetSelection) && sampleSetSelection.length === 2;
|
|
38
|
+
|
|
37
39
|
const caseSampleSetSelection = useMemo(() => (
|
|
38
40
|
sampleSetSelection?.[0]
|
|
39
41
|
? [sampleSetSelection[0]]
|
|
@@ -47,22 +49,27 @@ export function DualEmbeddingScatterplotSubscriber(props) {
|
|
|
47
49
|
|
|
48
50
|
return (
|
|
49
51
|
<div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'row' }}>
|
|
50
|
-
<div style={{ width: '50%', display: 'flex', flexDirection: 'column' }}>
|
|
52
|
+
<div style={{ width: isCaseCtrl ? '50%' : '100%', display: 'flex', flexDirection: 'column' }}>
|
|
51
53
|
<EmbeddingScatterplotSubscriber
|
|
52
54
|
{...props}
|
|
53
55
|
uuid={`${uuid}-case`}
|
|
54
|
-
title={
|
|
56
|
+
title={(isCaseCtrl
|
|
57
|
+
? `Scatterplot (${embeddingType}), ${caseSampleSetSelection?.[0]?.at(-1)}`
|
|
58
|
+
: null
|
|
59
|
+
)}
|
|
55
60
|
sampleSetSelection={caseSampleSetSelection}
|
|
56
61
|
/>
|
|
57
62
|
</div>
|
|
58
|
-
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
{isCaseCtrl ? (
|
|
64
|
+
<div style={{ width: '50%', display: 'flex', flexDirection: 'column' }}>
|
|
65
|
+
<EmbeddingScatterplotSubscriber
|
|
66
|
+
{...props}
|
|
67
|
+
uuid={`${uuid}-ctrl`}
|
|
68
|
+
title={`Scatterplot (${embeddingType}), ${ctrlSampleSetSelection?.[0]?.at(-1)}`}
|
|
69
|
+
sampleSetSelection={ctrlSampleSetSelection}
|
|
70
|
+
/>
|
|
71
|
+
</div>
|
|
72
|
+
) : null}
|
|
66
73
|
</div>
|
|
67
74
|
);
|
|
68
75
|
}
|
|
@@ -3,6 +3,7 @@ import React, {
|
|
|
3
3
|
} from 'react';
|
|
4
4
|
import { extent, quantileSorted } from 'd3-array';
|
|
5
5
|
import { isEqual } from 'lodash-es';
|
|
6
|
+
import { circle } from '@turf/circle';
|
|
6
7
|
import {
|
|
7
8
|
TitleInfo,
|
|
8
9
|
useReady, useUrls,
|
|
@@ -39,6 +40,7 @@ import { Legend } from '@vitessce/legend';
|
|
|
39
40
|
import { ViewType, COMPONENT_COORDINATION_TYPES, ViewHelpMapping } from '@vitessce/constants-internal';
|
|
40
41
|
import { DEFAULT_CONTOUR_PERCENTILES } from './constants.js';
|
|
41
42
|
|
|
43
|
+
const DEFAULT_FEATURE_AGGREGATION_STRATEGY = 'first';
|
|
42
44
|
|
|
43
45
|
/**
|
|
44
46
|
* A subscriber component for the scatterplot.
|
|
@@ -112,6 +114,7 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
112
114
|
embeddingContourPercentiles: contourPercentiles,
|
|
113
115
|
contourColorEncoding,
|
|
114
116
|
contourColor,
|
|
117
|
+
featureAggregationStrategy,
|
|
115
118
|
}, {
|
|
116
119
|
setEmbeddingZoom: setZoom,
|
|
117
120
|
setEmbeddingTargetX: setTargetX,
|
|
@@ -138,6 +141,7 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
138
141
|
setEmbeddingContoursFilled,
|
|
139
142
|
setEmbeddingContourPercentiles: setContourPercentiles,
|
|
140
143
|
setContourColorEncoding,
|
|
144
|
+
setFeatureAggregationStrategy,
|
|
141
145
|
}] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.SCATTERPLOT], coordinationScopes);
|
|
142
146
|
|
|
143
147
|
const {
|
|
@@ -154,6 +158,9 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
154
158
|
|| sampleSetSelectionFromCoordination
|
|
155
159
|
);
|
|
156
160
|
|
|
161
|
+
const featureAggregationStrategyToUse = featureAggregationStrategy
|
|
162
|
+
?? DEFAULT_FEATURE_AGGREGATION_STRATEGY;
|
|
163
|
+
|
|
157
164
|
const [width, height, deckRef] = useDeckCanvasSize();
|
|
158
165
|
|
|
159
166
|
const title = titleOverride || `Scatterplot (${mapping})`;
|
|
@@ -298,7 +305,7 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
298
305
|
// compute the cell radius scale based on the
|
|
299
306
|
// extents of the cell coordinates on the x/y axes.
|
|
300
307
|
useEffect(() => {
|
|
301
|
-
if (xRange && yRange) {
|
|
308
|
+
if (xRange && yRange && width && height) {
|
|
302
309
|
const pointSizeDevicePixels = getPointSizeDevicePixels(
|
|
303
310
|
window.devicePixelRatio, zoom, xRange, yRange, width, height,
|
|
304
311
|
);
|
|
@@ -379,9 +386,36 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
379
386
|
.map(t => Math.max(t, 1.0));
|
|
380
387
|
return thresholds;
|
|
381
388
|
}
|
|
382
|
-
return
|
|
389
|
+
return [1, 10, 100];
|
|
383
390
|
}, [contourPercentiles, sortedWeights]);
|
|
384
391
|
|
|
392
|
+
// Construct a circle polygon using Turf's circle function,
|
|
393
|
+
// which surrounds all points in the scatterplot,
|
|
394
|
+
// which we can use to position text labels along.
|
|
395
|
+
const circleInfo = useMemo(() => {
|
|
396
|
+
if (!originalViewState || !width || !height) {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
const center = [
|
|
400
|
+
originalViewState.target[0],
|
|
401
|
+
originalViewState.target[1],
|
|
402
|
+
];
|
|
403
|
+
const scaleFactor = (2 ** originalViewState.zoom);
|
|
404
|
+
if (!(typeof scaleFactor === 'number' && typeof center[0] === 'number' && typeof center[1] === 'number') || Number.isNaN(scaleFactor)) {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
const radius = Math.min(width, height) / 2 / scaleFactor;
|
|
408
|
+
const numPoints = 96;
|
|
409
|
+
const options = { steps: numPoints, units: 'degrees' };
|
|
410
|
+
const circlePolygon = circle(center, radius, options);
|
|
411
|
+
return {
|
|
412
|
+
center,
|
|
413
|
+
radius,
|
|
414
|
+
polygon: circlePolygon,
|
|
415
|
+
steps: numPoints,
|
|
416
|
+
};
|
|
417
|
+
}, [originalViewState, width, height]);
|
|
418
|
+
|
|
385
419
|
// It is possible for the embedding index+data to be out of order
|
|
386
420
|
// with respect to the matrix index+data. Here, we align the embedding
|
|
387
421
|
// data so that the rows are ordered the same as the matrix rows.
|
|
@@ -432,24 +466,23 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
432
466
|
}, [sampleEdges]);
|
|
433
467
|
|
|
434
468
|
// Stratify multiple arrays: per-cellSet and per-sampleSet.
|
|
435
|
-
const stratifiedData = useMemo(() => {
|
|
469
|
+
const [stratifiedData, stratifiedDataCount] = useMemo(() => {
|
|
436
470
|
if (alignedEmbeddingData?.data) {
|
|
437
|
-
const result = stratifyArrays(
|
|
471
|
+
const [result, cellCountResult] = stratifyArrays(
|
|
438
472
|
sampleEdges, sampleIdToObsIdsMap,
|
|
439
473
|
sampleSets, sampleSetSelection,
|
|
440
474
|
alignedEmbeddingIndex, mergedCellSets, cellSetSelection, {
|
|
441
475
|
obsEmbeddingX: alignedEmbeddingData.data[0],
|
|
442
476
|
obsEmbeddingY: alignedEmbeddingData.data[1],
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
},
|
|
477
|
+
...(uint8ExpressionData?.[0] ? { featureValue: uint8ExpressionData } : {}),
|
|
478
|
+
}, featureAggregationStrategyToUse,
|
|
446
479
|
);
|
|
447
|
-
return result;
|
|
480
|
+
return [result, cellCountResult];
|
|
448
481
|
}
|
|
449
|
-
return null;
|
|
482
|
+
return [null, null];
|
|
450
483
|
}, [alignedEmbeddingIndex, alignedEmbeddingData, uint8ExpressionData,
|
|
451
484
|
sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetSelection,
|
|
452
|
-
cellSetSelection, mergedCellSets,
|
|
485
|
+
cellSetSelection, mergedCellSets, featureAggregationStrategyToUse,
|
|
453
486
|
]);
|
|
454
487
|
|
|
455
488
|
const setViewState = ({ zoom: newZoom, target }) => {
|
|
@@ -459,10 +492,15 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
459
492
|
setTargetZ(target[2] || 0);
|
|
460
493
|
};
|
|
461
494
|
|
|
495
|
+
// TODO: Update this once the rendered points reflects the selection/filtering.
|
|
496
|
+
const cellCountToUse = embeddingPointsVisible
|
|
497
|
+
? cellsCount
|
|
498
|
+
: (stratifiedDataCount ?? cellsCount);
|
|
499
|
+
|
|
462
500
|
return (
|
|
463
501
|
<TitleInfo
|
|
464
502
|
title={title}
|
|
465
|
-
info={`${commaNumber(
|
|
503
|
+
info={`${commaNumber(cellCountToUse)} ${plur(observationsLabel, cellCountToUse)}`}
|
|
466
504
|
closeButtonVisible={closeButtonVisible}
|
|
467
505
|
downloadButtonVisible={downloadButtonVisible}
|
|
468
506
|
removeGridComponent={removeGridComponent}
|
|
@@ -506,6 +544,8 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
506
544
|
defaultContourPercentiles={DEFAULT_CONTOUR_PERCENTILES}
|
|
507
545
|
contourColorEncoding={contourColorEncoding}
|
|
508
546
|
setContourColorEncoding={setContourColorEncoding}
|
|
547
|
+
featureAggregationStrategy={featureAggregationStrategy}
|
|
548
|
+
setFeatureAggregationStrategy={setFeatureAggregationStrategy}
|
|
509
549
|
/>
|
|
510
550
|
)}
|
|
511
551
|
>
|
|
@@ -554,18 +594,21 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
554
594
|
contoursFilled={embeddingContoursFilled}
|
|
555
595
|
embeddingPointsVisible={embeddingPointsVisible}
|
|
556
596
|
embeddingContoursVisible={embeddingContoursVisible}
|
|
597
|
+
|
|
598
|
+
circleInfo={circleInfo}
|
|
599
|
+
featureSelection={geneSelection}
|
|
557
600
|
/>
|
|
558
|
-
{tooltipsVisible && (
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
)}
|
|
601
|
+
{tooltipsVisible && width && height ? (
|
|
602
|
+
<ScatterplotTooltipSubscriber
|
|
603
|
+
parentUuid={uuid}
|
|
604
|
+
obsHighlight={cellHighlight}
|
|
605
|
+
width={width}
|
|
606
|
+
height={height}
|
|
607
|
+
getObsInfo={getObsInfo}
|
|
608
|
+
featureType={featureType}
|
|
609
|
+
featureLabelsMap={featureLabelsMap}
|
|
610
|
+
/>
|
|
611
|
+
) : null}
|
|
569
612
|
<Legend
|
|
570
613
|
visible
|
|
571
614
|
theme={theme}
|
|
@@ -577,14 +620,15 @@ export function EmbeddingScatterplotSubscriber(props) {
|
|
|
577
620
|
featureValueColormap={geneExpressionColormap}
|
|
578
621
|
featureValueColormapRange={geneExpressionColormapRange}
|
|
579
622
|
obsSetSelection={cellSetSelection}
|
|
580
|
-
extent={expressionExtents
|
|
581
|
-
missing={expressionMissing
|
|
623
|
+
extent={expressionExtents}
|
|
624
|
+
missing={expressionMissing}
|
|
582
625
|
// Contour percentile legend
|
|
583
626
|
pointsVisible={embeddingPointsVisible}
|
|
584
627
|
contoursVisible={embeddingContoursVisible}
|
|
585
628
|
contoursFilled={embeddingContoursFilled}
|
|
586
629
|
contourPercentiles={contourPercentiles || DEFAULT_CONTOUR_PERCENTILES}
|
|
587
630
|
contourThresholds={contourThresholds}
|
|
631
|
+
featureAggregationStrategy={featureAggregationStrategyToUse}
|
|
588
632
|
/>
|
|
589
633
|
</TitleInfo>
|
|
590
634
|
);
|