@vitessce/scatterplot-embedding 3.5.10 → 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.
@@ -1,5 +1,5 @@
1
1
  import { i as inflate_1 } from "./pako.esm-68f84e2a.js";
2
- import { B as BaseDecoder } from "./index-a37f30c8.js";
2
+ import { B as BaseDecoder } from "./index-ab73546f.js";
3
3
  import "react";
4
4
  import "@vitessce/vit-s";
5
5
  import "react-dom";
@@ -11717,6 +11717,7 @@ const CoordinationType$1 = {
11717
11717
  FEATURE_VALUE_COLORMAP: "featureValueColormap",
11718
11718
  FEATURE_VALUE_TRANSFORM: "featureValueTransform",
11719
11719
  FEATURE_VALUE_COLORMAP_RANGE: "featureValueColormapRange",
11720
+ FEATURE_AGGREGATION_STRATEGY: "featureAggregationStrategy",
11720
11721
  OBS_COLOR_ENCODING: "obsColorEncoding",
11721
11722
  SPATIAL_IMAGE_LAYER: "spatialImageLayer",
11722
11723
  SPATIAL_SEGMENTATION_LAYER: "spatialSegmentationLayer",
@@ -11859,6 +11860,7 @@ const COMPONENT_COORDINATION_TYPES = {
11859
11860
  CoordinationType$1.FEATURE_SELECTION,
11860
11861
  CoordinationType$1.FEATURE_VALUE_COLORMAP,
11861
11862
  CoordinationType$1.FEATURE_VALUE_COLORMAP_RANGE,
11863
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
11862
11864
  CoordinationType$1.OBS_COLOR_ENCODING,
11863
11865
  CoordinationType$1.ADDITIONAL_OBS_SETS,
11864
11866
  CoordinationType$1.TOOLTIPS_VISIBLE,
@@ -11902,6 +11904,7 @@ const COMPONENT_COORDINATION_TYPES = {
11902
11904
  CoordinationType$1.FEATURE_SELECTION,
11903
11905
  CoordinationType$1.FEATURE_VALUE_COLORMAP,
11904
11906
  CoordinationType$1.FEATURE_VALUE_COLORMAP_RANGE,
11907
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
11905
11908
  CoordinationType$1.OBS_COLOR_ENCODING,
11906
11909
  CoordinationType$1.ADDITIONAL_OBS_SETS,
11907
11910
  CoordinationType$1.TOOLTIPS_VISIBLE,
@@ -11981,6 +11984,7 @@ const COMPONENT_COORDINATION_TYPES = {
11981
11984
  CoordinationType$1.FEATURE_SELECTION,
11982
11985
  CoordinationType$1.FEATURE_VALUE_COLORMAP,
11983
11986
  CoordinationType$1.FEATURE_VALUE_COLORMAP_RANGE,
11987
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
11984
11988
  CoordinationType$1.OBS_COLOR_ENCODING,
11985
11989
  CoordinationType$1.ADDITIONAL_OBS_SETS,
11986
11990
  CoordinationType$1.MOLECULE_HIGHLIGHT,
@@ -12018,6 +12022,7 @@ const COMPONENT_COORDINATION_TYPES = {
12018
12022
  CoordinationType$1.FEATURE_SELECTION,
12019
12023
  CoordinationType$1.FEATURE_VALUE_COLORMAP,
12020
12024
  CoordinationType$1.FEATURE_VALUE_COLORMAP_RANGE,
12025
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
12021
12026
  CoordinationType$1.OBS_COLOR_ENCODING,
12022
12027
  CoordinationType$1.ADDITIONAL_OBS_SETS,
12023
12028
  CoordinationType$1.MOLECULE_HIGHLIGHT,
@@ -12131,6 +12136,7 @@ const COMPONENT_COORDINATION_TYPES = {
12131
12136
  CoordinationType$1.FEATURE_SELECTION,
12132
12137
  CoordinationType$1.FEATURE_VALUE_TRANSFORM,
12133
12138
  CoordinationType$1.FEATURE_VALUE_TRANSFORM_COEFFICIENT,
12139
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
12134
12140
  CoordinationType$1.OBS_SET_SELECTION,
12135
12141
  CoordinationType$1.OBS_SET_FILTER,
12136
12142
  CoordinationType$1.OBS_SET_HIGHLIGHT,
@@ -12146,6 +12152,7 @@ const COMPONENT_COORDINATION_TYPES = {
12146
12152
  CoordinationType$1.FEATURE_TYPE,
12147
12153
  CoordinationType$1.FEATURE_VALUE_TYPE,
12148
12154
  CoordinationType$1.FEATURE_SELECTION,
12155
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
12149
12156
  CoordinationType$1.ADDITIONAL_OBS_SETS,
12150
12157
  CoordinationType$1.OBS_SET_COLOR,
12151
12158
  CoordinationType$1.OBS_COLOR_ENCODING,
@@ -12224,6 +12231,7 @@ const COMPONENT_COORDINATION_TYPES = {
12224
12231
  CoordinationType$1.FEATURE_VALUE_COLORMAP,
12225
12232
  CoordinationType$1.FEATURE_VALUE_COLORMAP_RANGE,
12226
12233
  CoordinationType$1.FEATURE_SELECTION,
12234
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
12227
12235
  CoordinationType$1.TOOLTIPS_VISIBLE,
12228
12236
  CoordinationType$1.TOOLTIP_CROSSHAIRS_VISIBLE,
12229
12237
  CoordinationType$1.LEGEND_VISIBLE,
@@ -12264,6 +12272,7 @@ const COMPONENT_COORDINATION_TYPES = {
12264
12272
  CoordinationType$1.FEATURE_VALUE_TRANSFORM_COEFFICIENT,
12265
12273
  CoordinationType$1.FEATURE_VALUE_POSITIVITY_THRESHOLD,
12266
12274
  CoordinationType$1.FEATURE_VALUE_COLORMAP,
12275
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
12267
12276
  CoordinationType$1.OBS_SET_SELECTION,
12268
12277
  CoordinationType$1.OBS_SET_FILTER,
12269
12278
  CoordinationType$1.OBS_SET_HIGHLIGHT,
@@ -12292,6 +12301,7 @@ const COMPONENT_COORDINATION_TYPES = {
12292
12301
  CoordinationType$1.FEATURE_SELECTION,
12293
12302
  CoordinationType$1.FEATURE_VALUE_TRANSFORM,
12294
12303
  CoordinationType$1.FEATURE_VALUE_TRANSFORM_COEFFICIENT,
12304
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
12295
12305
  CoordinationType$1.OBS_SET_SELECTION,
12296
12306
  CoordinationType$1.OBS_SET_HIGHLIGHT,
12297
12307
  CoordinationType$1.OBS_HIGHLIGHT,
@@ -12396,6 +12406,7 @@ const COMPONENT_COORDINATION_TYPES = {
12396
12406
  CoordinationType$1.OBS_TYPE,
12397
12407
  CoordinationType$1.SAMPLE_TYPE,
12398
12408
  CoordinationType$1.FEATURE_SELECTION,
12409
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
12399
12410
  CoordinationType$1.SAMPLE_SET_SELECTION,
12400
12411
  CoordinationType$1.SAMPLE_SET_FILTER,
12401
12412
  CoordinationType$1.OBS_SET_SELECTION,
@@ -12703,6 +12714,7 @@ z.object({
12703
12714
  termColumn: z.string().optional(),
12704
12715
  pValueColumn: z.string(),
12705
12716
  pValueAdjusted: z.boolean().optional(),
12717
+ featureSetLibrary: z.string().optional().describe("Optionally, provide a feature set library name. By default, Reactome_2022."),
12706
12718
  analysisType: z.string().optional().describe("Optionally, provide an analysis_type name. By default, pertpy_hypergeometric.")
12707
12719
  });
12708
12720
  z.object({
@@ -12713,7 +12725,6 @@ z.object({
12713
12725
  foldChangeColumn: z.string().describe("The log-fold change is then calculated between this expected sample and the expected sample with no active covariates from the intercept section."),
12714
12726
  foldChangeTransformation: z.enum(["log2"]).optional(),
12715
12727
  isCredibleEffectColumn: z.string().describe("Column which annotates effects as being credible or not (boolean)."),
12716
- covariateColumn: z.string().describe("Column which defines the covariate used in the analysis."),
12717
12728
  analysisType: z.string().optional().describe("Optionally, provide an analysis_type name. By default, sccoda_df.")
12718
12729
  });
12719
12730
  const annDataObsLabels = annDataObs;
@@ -19717,7 +19728,7 @@ function getCellColors(params) {
19717
19728
  }
19718
19729
  return /* @__PURE__ */ new Map();
19719
19730
  }
19720
- function stratifyArrays(sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetSelection, obsIndex, mergedCellSets, cellSetSelection, arraysToStratify) {
19731
+ function stratifyArrays(sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetSelection, obsIndex, mergedCellSets, cellSetSelection, arraysToStratify, featureAggregationStrategy) {
19721
19732
  const result = new InternMap([], JSON.stringify);
19722
19733
  const hasSampleSetSelection = Array.isArray(sampleSetSelection) && sampleSetSelection.length > 0;
19723
19734
  const hasCellSetSelection = Array.isArray(cellSetSelection) && cellSetSelection.length > 0;
@@ -19727,9 +19738,13 @@ function stratifyArrays(sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetS
19727
19738
  if (arrKeys.includes("obsIndex") || arrKeys.includes("i")) {
19728
19739
  throw new Error('The keys "obsIndex" and "i" are reserved for internal use.');
19729
19740
  }
19730
- if (Object.values(arraysToStratify).some((arr) => arr.length !== obsIndex.length)) {
19731
- throw new Error("All arrays must have the same length as the obsIndex.");
19732
- }
19741
+ if (Object.entries(arraysToStratify).some(([arrKey, arr]) => {
19742
+ if (arrKey === "featureValue") {
19743
+ return arr.some((a2) => a2.length !== obsIndex.length);
19744
+ }
19745
+ return arr.length !== obsIndex.length;
19746
+ }))
19747
+ ;
19733
19748
  const sampleSetInfo = sampleSets && sampleSetSelection ? treeToObsIdsBySetNames(sampleSets, sampleSetSelection) : null;
19734
19749
  const cellSetInfo = mergedCellSets && cellSetSelection ? treeToObsIdsBySetNames(mergedCellSets, cellSetSelection) : null;
19735
19750
  cellSetKeys.forEach((cellSetKey) => {
@@ -19759,6 +19774,7 @@ function stratifyArrays(sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetS
19759
19774
  });
19760
19775
  const sampleIdToSetMap = sampleSets && sampleSetSelection ? treeToSelectedSetMap(sampleSets, sampleSetSelection) : null;
19761
19776
  const cellIdToSetMap = mergedCellSets && cellSetSelection ? treeToSelectedSetMap(mergedCellSets, cellSetSelection) : null;
19777
+ let cellCount = 0;
19762
19778
  for (let i2 = 0; i2 < obsIndex.length; i2 += 1) {
19763
19779
  const obsId = obsIndex[i2];
19764
19780
  const cellSet = (cellIdToSetMap == null ? void 0 : cellIdToSetMap.get(obsId)) || null;
@@ -19769,10 +19785,28 @@ function stratifyArrays(sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetS
19769
19785
  }
19770
19786
  const insertionIndex = result.get(cellSet).get(sampleSet).get("i");
19771
19787
  arrKeys.forEach((arrKey) => {
19772
- const value = arraysToStratify[arrKey][i2];
19788
+ let value;
19789
+ if (arrKey === "featureValue") {
19790
+ if (featureAggregationStrategy === "first") {
19791
+ value = arraysToStratify[arrKey][0][i2];
19792
+ } else if (featureAggregationStrategy === "last") {
19793
+ value = arraysToStratify[arrKey].at(-1)[i2];
19794
+ } else if (typeof featureAggregationStrategy === "number") {
19795
+ const j = featureAggregationStrategy;
19796
+ value = arraysToStratify[arrKey][j][i2];
19797
+ } else if (featureAggregationStrategy === "sum" || featureAggregationStrategy === "mean") {
19798
+ value = arraysToStratify[arrKey].reduce((a2, h) => a2 + h[i2], 0);
19799
+ if (featureAggregationStrategy === "mean") {
19800
+ value /= arraysToStratify[arrKey].length;
19801
+ }
19802
+ }
19803
+ } else {
19804
+ value = arraysToStratify[arrKey][i2];
19805
+ }
19773
19806
  result.get(cellSet).get(sampleSet).get(arrKey)[insertionIndex] = value;
19774
19807
  });
19775
19808
  result.get(cellSet).get(sampleSet).set("i", insertionIndex + 1);
19809
+ cellCount += 1;
19776
19810
  }
19777
19811
  cellSetKeys.forEach((cellSetKey) => {
19778
19812
  sampleSetKeys.forEach((sampleSetKey) => {
@@ -19783,7 +19817,7 @@ function stratifyArrays(sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetS
19783
19817
  result.get(cellSetKey).get(sampleSetKey).delete("i");
19784
19818
  });
19785
19819
  });
19786
- return result;
19820
+ return [result, cellCount];
19787
19821
  }
19788
19822
  function tree_add(d) {
19789
19823
  var x2 = +this._x.call(null, d), y2 = +this._y.call(null, d);
@@ -127455,8 +127489,15 @@ class SelectionLayer extends CompositeLayer {
127455
127489
  if (!nodePolygonContainsSelectedPolygon && !nodePolygonWithinSelectedPolygon && !nodePolygonOverlapsSelectedPolgyon) {
127456
127490
  return true;
127457
127491
  }
127458
- if (node.data && booleanPointInPolygon$1(point$1([].slice.call(getObsCoords(node.data))), selectedPolygon)) {
127459
- pickingInfos.push(node.data);
127492
+ if (node.data) {
127493
+ let current = node;
127494
+ while (current) {
127495
+ const pointCoords = [].slice.call(getObsCoords(current.data));
127496
+ if (booleanPointInPolygon$1(point$1(pointCoords), selectedPolygon)) {
127497
+ pickingInfos.push(current.data);
127498
+ }
127499
+ current = current.next;
127500
+ }
127460
127501
  }
127461
127502
  return false;
127462
127503
  });
@@ -128668,16 +128709,16 @@ function addDecoder(cases, importFn) {
128668
128709
  }
128669
128710
  cases.forEach((c2) => registry$1.set(c2, importFn));
128670
128711
  }
128671
- addDecoder([void 0, 1], () => import("./raw-b648d255.js").then((m2) => m2.default));
128672
- addDecoder(5, () => import("./lzw-ce001eff.js").then((m2) => m2.default));
128712
+ addDecoder([void 0, 1], () => import("./raw-140846a5.js").then((m2) => m2.default));
128713
+ addDecoder(5, () => import("./lzw-ec516fe1.js").then((m2) => m2.default));
128673
128714
  addDecoder(6, () => {
128674
128715
  throw new Error("old style JPEG compression is not supported.");
128675
128716
  });
128676
- addDecoder(7, () => import("./jpeg-5927a952.js").then((m2) => m2.default));
128677
- addDecoder([8, 32946], () => import("./deflate-f550f6c6.js").then((m2) => m2.default));
128678
- addDecoder(32773, () => import("./packbits-075c055a.js").then((m2) => m2.default));
128679
- addDecoder(34887, () => import("./lerc-7a4fca51.js").then((m2) => m2.default));
128680
- addDecoder(50001, () => import("./webimage-dec5407e.js").then((m2) => m2.default));
128717
+ addDecoder(7, () => import("./jpeg-6b5f0200.js").then((m2) => m2.default));
128718
+ addDecoder([8, 32946], () => import("./deflate-7f3bb131.js").then((m2) => m2.default));
128719
+ addDecoder(32773, () => import("./packbits-28d4e412.js").then((m2) => m2.default));
128720
+ addDecoder(34887, () => import("./lerc-6a84a90d.js").then((m2) => m2.default));
128721
+ addDecoder(50001, () => import("./webimage-5e317232.js").then((m2) => m2.default));
128681
128722
  function decodeRowAcc(row, stride) {
128682
128723
  let length2 = row.length - stride;
128683
128724
  let offset5 = 0;
@@ -149712,7 +149753,10 @@ const getPosition = (object2, { index: index2, data, target }) => {
149712
149753
  target[2] = POINT_LAYER_Z_INDEX;
149713
149754
  return target;
149714
149755
  };
149715
- const contourGetWeight = (object2, { index: index2, data }) => data.src.featureValues[index2];
149756
+ const contourGetWeight = (object2, { index: index2, data }) => {
149757
+ var _a3;
149758
+ return (_a3 = data.src.featureValues) == null ? void 0 : _a3[index2];
149759
+ };
149716
149760
  const contourGetPosition = (object2, { index: index2, data, target }) => {
149717
149761
  target[0] = data.src.embeddingX[index2];
149718
149762
  target[1] = -data.src.embeddingY[index2];
@@ -150009,6 +150053,16 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
150009
150053
  const { obsEmbeddingIndex, obsEmbedding } = this.props;
150010
150054
  super.viewInfoDidUpdate(obsEmbeddingIndex, obsEmbedding, makeFlippedGetObsCoords);
150011
150055
  }
150056
+ componentWillUnmount() {
150057
+ delete this.cellsQuadTree;
150058
+ delete this.cellsLayer;
150059
+ delete this.cellsData;
150060
+ delete this.stratifiedData;
150061
+ delete this.cellSetsForceSimulation;
150062
+ delete this.cellSetsLabelPrevZoom;
150063
+ delete this.cellSetsLayers;
150064
+ delete this.contourLayers;
150065
+ }
150012
150066
  /**
150013
150067
  * Here, asynchronously check whether props have
150014
150068
  * updated which require re-computing memoized variables,
@@ -150215,8 +150269,9 @@ if (typeof document !== "undefined") {
150215
150269
  else
150216
150270
  document.addEventListener("DOMContentLoaded", $bbed8b41f857bcc0$var$setupGlobalEvents);
150217
150271
  }
150272
+ const FEATURE_AGGREGATION_STRATEGIES = ["first", "last", "sum", "mean"];
150218
150273
  function ScatterplotOptions(props) {
150219
- const { children: children2, observationsLabel, cellRadius, setCellRadius, cellRadiusMode, setCellRadiusMode, cellOpacity, setCellOpacity, cellOpacityMode, setCellOpacityMode, cellSetLabelsVisible, setCellSetLabelsVisible, tooltipsVisible, setTooltipsVisible, cellSetLabelSize, setCellSetLabelSize, cellSetPolygonsVisible, setCellSetPolygonsVisible, cellColorEncoding, setCellColorEncoding, geneExpressionColormap, setGeneExpressionColormap, geneExpressionColormapRange, setGeneExpressionColormapRange, embeddingPointsVisible, setEmbeddingPointsVisible, embeddingContoursVisible, setEmbeddingContoursVisible, embeddingContoursFilled, setEmbeddingContoursFilled, contourPercentiles, setContourPercentiles, defaultContourPercentiles, contourColorEncoding, setContourColorEncoding } = props;
150274
+ const { children: children2, observationsLabel, cellRadius, setCellRadius, cellRadiusMode, setCellRadiusMode, cellOpacity, setCellOpacity, cellOpacityMode, setCellOpacityMode, cellSetLabelsVisible, setCellSetLabelsVisible, tooltipsVisible, setTooltipsVisible, cellSetLabelSize, setCellSetLabelSize, cellSetPolygonsVisible, setCellSetPolygonsVisible, cellColorEncoding, setCellColorEncoding, geneExpressionColormap, setGeneExpressionColormap, geneExpressionColormapRange, setGeneExpressionColormapRange, embeddingPointsVisible, setEmbeddingPointsVisible, embeddingContoursVisible, setEmbeddingContoursVisible, embeddingContoursFilled, setEmbeddingContoursFilled, contourPercentiles, setContourPercentiles, defaultContourPercentiles, contourColorEncoding, setContourColorEncoding, featureAggregationStrategy, setFeatureAggregationStrategy } = props;
150220
150275
  const scatterplotOptionsId = $bdb11010cef70236$export$f680877a34711e37();
150221
150276
  const observationsLabelNice = capitalize$1(observationsLabel);
150222
150277
  const classes = usePlotOptionsStyles();
@@ -150267,6 +150322,9 @@ function ScatterplotOptions(props) {
150267
150322
  setContourPercentiles(values2);
150268
150323
  }
150269
150324
  const handlePercentilesChangeDebounced = useCallback(debounce$2(handlePercentilesChange, 5, { trailing: true }), [handlePercentilesChange]);
150325
+ function handleFeatureAggregationStrategyChange(event) {
150326
+ setFeatureAggregationStrategy(event.target.value);
150327
+ }
150270
150328
  return jsxRuntimeExports.jsxs(OptionsContainer, { children: [children2, jsxRuntimeExports.jsx(CellColorEncodingOption, { observationsLabel, cellColorEncoding, setCellColorEncoding }), jsxRuntimeExports.jsxs(TableRow$1, { children: [jsxRuntimeExports.jsx(TableCell$1, { className: classes.labelCell, variant: "head", scope: "row", children: jsxRuntimeExports.jsxs("label", { htmlFor: `scatterplot-set-labels-visible-${scatterplotOptionsId}`, children: [observationsLabelNice, " Set Labels Visible"] }) }), jsxRuntimeExports.jsx(TableCell$1, { className: classes.inputCell, variant: "body", children: jsxRuntimeExports.jsx(Checkbox$1, { className: classes.checkbox, checked: cellSetLabelsVisible, onChange: handleLabelVisibilityChange, name: "scatterplot-option-cell-set-labels", color: "default", inputProps: {
150271
150329
  "aria-label": "Show or hide set labels",
150272
150330
  id: `scatterplot-set-labels-visible-${scatterplotOptionsId}`
@@ -150308,7 +150366,9 @@ function ScatterplotOptions(props) {
150308
150366
  id: `scatterplot-contours-filled-${scatterplotOptionsId}`
150309
150367
  } }) })] }), jsxRuntimeExports.jsxs(TableRow$1, { children: [jsxRuntimeExports.jsx(TableCell$1, { className: classes.labelCell, variant: "head", scope: "row", children: jsxRuntimeExports.jsx("label", { htmlFor: `scatterplot-contour-color-encoding-${scatterplotOptionsId}`, children: "Contour Color Encoding" }) }), jsxRuntimeExports.jsx(TableCell$1, { className: classes.inputCell, variant: "body", children: jsxRuntimeExports.jsxs(OptionSelect, { className: classes.select, value: contourColorEncoding, onChange: handleContourColorEncodingChange, inputProps: {
150310
150368
  id: `scatterplot-contour-color-encoding-${scatterplotOptionsId}`
150311
- }, children: [jsxRuntimeExports.jsx("option", { value: "sampleSetSelection", children: "Sample Sets" }), jsxRuntimeExports.jsxs("option", { value: "cellSetSelection", children: [observationsLabelNice, " Sets"] }), jsxRuntimeExports.jsx("option", { value: "staticColor", children: "Static Color" })] }) })] }), jsxRuntimeExports.jsxs(TableRow$1, { children: [jsxRuntimeExports.jsx(TableCell$1, { className: classes.labelCell, variant: "head", scope: "row", children: jsxRuntimeExports.jsx("label", { htmlFor: `scatterplot-contour-percentiles-${scatterplotOptionsId}`, children: "Contour Percentiles" }) }), jsxRuntimeExports.jsx(TableCell$1, { className: classes.inputCell, variant: "body", children: jsxRuntimeExports.jsx(Slider$1, { classes: { root: classes.slider, valueLabel: classes.sliderValueLabel }, value: contourPercentiles || defaultContourPercentiles, onChange: handlePercentilesChangeDebounced, "aria-label": "Scatterplot sliders for contour percentile thresholds", id: `scatterplot-contour-percentiles-${scatterplotOptionsId}`, valueLabelDisplay: "auto", step: 5e-3, min: 9e-3, max: 0.999 }) })] })] });
150369
+ }, children: [jsxRuntimeExports.jsx("option", { value: "sampleSetSelection", children: "Sample Sets" }), jsxRuntimeExports.jsxs("option", { value: "cellSetSelection", children: [observationsLabelNice, " Sets"] }), jsxRuntimeExports.jsx("option", { value: "staticColor", children: "Static Color" })] }) })] }), jsxRuntimeExports.jsxs(TableRow$1, { children: [jsxRuntimeExports.jsx(TableCell$1, { className: classes.labelCell, variant: "head", scope: "row", children: jsxRuntimeExports.jsx("label", { htmlFor: `scatterplot-contour-percentiles-${scatterplotOptionsId}`, children: "Contour Percentiles" }) }), jsxRuntimeExports.jsx(TableCell$1, { className: classes.inputCell, variant: "body", children: jsxRuntimeExports.jsx(Slider$1, { classes: { root: classes.slider, valueLabel: classes.sliderValueLabel }, value: contourPercentiles || defaultContourPercentiles, onChange: handlePercentilesChangeDebounced, "aria-label": "Scatterplot sliders for contour percentile thresholds", id: `scatterplot-contour-percentiles-${scatterplotOptionsId}`, valueLabelDisplay: "auto", step: 5e-3, min: 9e-3, max: 0.999 }) })] }), setFeatureAggregationStrategy ? jsxRuntimeExports.jsxs(TableRow$1, { children: [jsxRuntimeExports.jsx(TableCell$1, { className: classes.labelCell, variant: "head", scope: "row", children: jsxRuntimeExports.jsx("label", { htmlFor: `feature-aggregation-strategy-${scatterplotOptionsId}`, children: "Feature Aggregation Strategy" }) }), jsxRuntimeExports.jsx(TableCell$1, { className: classes.inputCell, variant: "body", children: jsxRuntimeExports.jsx(OptionSelect, { className: classes.select, value: featureAggregationStrategy ?? "first", onChange: handleFeatureAggregationStrategyChange, inputProps: {
150370
+ id: `feature-aggregation-strategy-${scatterplotOptionsId}`
150371
+ }, children: FEATURE_AGGREGATION_STRATEGIES.map((opt) => jsxRuntimeExports.jsx("option", { value: opt, children: capitalize$1(opt) }, opt)) }) })] }) : null] });
150312
150372
  }
150313
150373
  const styles10 = makeStyles((theme) => ({
150314
150374
  tooltipAnchor: {
@@ -152819,8 +152879,50 @@ const titleHeight = 10;
152819
152879
  const rectHeight = 8;
152820
152880
  const rectMarginY = 2;
152821
152881
  const rectMarginX = 2;
152882
+ function combineExtents(extents, featureAggregationStrategy) {
152883
+ if (Array.isArray(extents)) {
152884
+ if (featureAggregationStrategy === "first") {
152885
+ return extents[0];
152886
+ }
152887
+ if (featureAggregationStrategy === "last") {
152888
+ return extents.at(-1);
152889
+ }
152890
+ if (typeof featureAggregationStrategy === "number") {
152891
+ const i2 = featureAggregationStrategy;
152892
+ return extents[i2];
152893
+ }
152894
+ if (featureAggregationStrategy === "sum") {
152895
+ return extents.reduce((a2, h) => [a2[0] + h[0], a2[1] + h[1]]);
152896
+ }
152897
+ if (featureAggregationStrategy === "mean") {
152898
+ return extents.reduce((a2, h) => [a2[0] + h[0], a2[1] + h[1]]).map((v) => v / extents.length);
152899
+ }
152900
+ }
152901
+ return null;
152902
+ }
152903
+ function combineMissings(missings, featureAggregationStrategy) {
152904
+ if (Array.isArray(missings)) {
152905
+ if (featureAggregationStrategy === "first") {
152906
+ return missings[0];
152907
+ }
152908
+ if (featureAggregationStrategy === "last") {
152909
+ return missings.at(-1);
152910
+ }
152911
+ if (typeof featureAggregationStrategy === "number") {
152912
+ const i2 = featureAggregationStrategy;
152913
+ return missings[i2];
152914
+ }
152915
+ if (featureAggregationStrategy === "sum") {
152916
+ return missings.reduce((a2, h) => a2 + h, 0);
152917
+ }
152918
+ if (featureAggregationStrategy === "mean") {
152919
+ return missings.reduce((a2, h) => a2 + h / missings.length, 0);
152920
+ }
152921
+ }
152922
+ return null;
152923
+ }
152822
152924
  function Legend(props) {
152823
- const { visible: visibleProp, positionRelative = false, highContrast = false, obsType, featureValueType, considerSelections = true, obsColorEncoding, featureSelection, featureLabelsMap, featureValueColormap, featureValueColormapRange, spatialChannelColor, spatialLayerColor, obsSetSelection, obsSetColor, extent: extent2, missing, width = 100, height = 36, theme, showObsLabel = false, pointsVisible = true, contoursVisible = false, contoursFilled, contourPercentiles, contourThresholds } = props;
152925
+ const { visible: visibleProp, positionRelative = false, highContrast = false, obsType, featureValueType, considerSelections = true, obsColorEncoding, featureSelection, featureLabelsMap, featureValueColormap, featureValueColormapRange, spatialChannelColor, spatialLayerColor, obsSetSelection, obsSetColor, featureAggregationStrategy, extent: extent2, missing, width = 100, height = 36, theme, showObsLabel = false, pointsVisible = true, contoursVisible = false, contoursFilled, contourPercentiles, contourThresholds } = props;
152824
152926
  const svgRef = useRef();
152825
152927
  const classes = useStyles();
152826
152928
  const isDarkTheme = theme === "dark";
@@ -152829,9 +152931,9 @@ function Legend(props) {
152829
152931
  const layerColor = Array.isArray(spatialLayerColor) && spatialLayerColor.length === 3 ? spatialLayerColor : getDefaultColor(theme);
152830
152932
  const channelColor = Array.isArray(spatialChannelColor) && spatialChannelColor.length === 3 ? spatialChannelColor : getDefaultColor(theme);
152831
152933
  const staticColor = obsColorEncoding === "spatialChannelColor" ? channelColor : layerColor;
152832
- const visible = visibleProp && (!considerSelections || obsColorEncoding === "geneSelection" && featureSelection && Array.isArray(featureSelection) && featureSelection.length === 1 || isSetColor && (obsSetSelection == null ? void 0 : obsSetSelection.length) > 0 && (obsSetColor == null ? void 0 : obsSetColor.length) > 0 || isStaticColor);
152934
+ const visible = visibleProp && (!considerSelections || obsColorEncoding === "geneSelection" && featureSelection && Array.isArray(featureSelection) && featureSelection.length >= 1 || isSetColor && (obsSetSelection == null ? void 0 : obsSetSelection.length) > 0 && (obsSetColor == null ? void 0 : obsSetColor.length) > 0 || isStaticColor);
152833
152935
  const levelZeroNames = useMemo(() => Array.from(new Set((obsSetSelection == null ? void 0 : obsSetSelection.map((setPath) => setPath[0])) || [])), [obsSetSelection]);
152834
- const dynamicHeight = isSetColor ? levelZeroNames.length * titleHeight + (obsSetSelection == null ? void 0 : obsSetSelection.length) * (rectHeight + rectMarginY) : height + (!pointsVisible && contoursVisible ? 25 : 0);
152936
+ const dynamicHeight = isSetColor && obsSetSelection ? levelZeroNames.length * titleHeight + (obsSetSelection == null ? void 0 : obsSetSelection.length) * (rectHeight + rectMarginY) : height + (!pointsVisible && contoursVisible ? 25 : 0);
152835
152937
  useEffect(() => {
152836
152938
  const domElement = svgRef.current;
152837
152939
  const foregroundColor = highContrast ? "black" : isDarkTheme ? "white" : "black";
@@ -152840,7 +152942,7 @@ function Legend(props) {
152840
152942
  svg.attr("width", width).attr("height", dynamicHeight);
152841
152943
  const g2 = svg.append("g").attr("width", width).attr("height", dynamicHeight);
152842
152944
  if (!considerSelections || obsColorEncoding === "geneSelection") {
152843
- const [xMin, xMax] = extent2 || [0, 1];
152945
+ const [xMin, xMax] = combineExtents(extent2, featureAggregationStrategy) || [0, 1];
152844
152946
  if (featureValueColormap && pointsVisible) {
152845
152947
  const xlinkHref = getXlinkHref(featureValueColormap);
152846
152948
  g2.append("image").attr("x", 0).attr("y", titleHeight).attr("width", width).attr("height", rectHeight).attr("preserveAspectRatio", "none").attr("href", xlinkHref);
@@ -152904,8 +153006,19 @@ function Legend(props) {
152904
153006
  });
152905
153007
  });
152906
153008
  }
152907
- const featureSelectionLabelRaw = featureSelection && featureSelection.length >= 1 && !isStaticColor ? (featureLabelsMap == null ? void 0 : featureLabelsMap.get(featureSelection[0])) || (featureLabelsMap == null ? void 0 : featureLabelsMap.get(cleanFeatureId(featureSelection[0]))) || featureSelection[0] : null;
152908
- const featureSelectionLabel = missing ? `${featureSelectionLabelRaw} (${Math.round(missing * 100)}% NaN)` : featureSelectionLabelRaw;
153009
+ const featureSelectionLabelRaw = featureSelection && featureSelection.length >= 1 && !isStaticColor ? featureSelection.map((geneName) => (featureLabelsMap == null ? void 0 : featureLabelsMap.get(geneName)) || (featureLabelsMap == null ? void 0 : featureLabelsMap.get(cleanFeatureId(geneName))) || geneName) : null;
153010
+ let featureSelectionLabelRawStr = "";
153011
+ if (featureAggregationStrategy === "first") {
153012
+ featureSelectionLabelRawStr = featureSelectionLabelRaw == null ? void 0 : featureSelectionLabelRaw[0];
153013
+ } else if (featureAggregationStrategy === "last") {
153014
+ featureSelectionLabelRawStr = featureSelectionLabelRaw == null ? void 0 : featureSelectionLabelRaw.at(-1);
153015
+ } else if (featureAggregationStrategy === "sum") {
153016
+ featureSelectionLabelRawStr = "Sum of features";
153017
+ } else if (featureAggregationStrategy === "mean") {
153018
+ featureSelectionLabelRawStr = "Mean of features";
153019
+ }
153020
+ const combinedMissing = combineMissings(missing, featureAggregationStrategy);
153021
+ const featureSelectionLabel = combinedMissing ? `${featureSelectionLabelRawStr} (${Math.round(combinedMissing * 100)}% NaN)` : featureSelectionLabelRawStr;
152909
153022
  const obsLabel = capitalize$1(obsType);
152910
153023
  const featureLabel = considerSelections ? featureSelectionLabel || capitalize$1(featureValueType) : capitalize$1(featureValueType);
152911
153024
  const mainLabel = showObsLabel ? obsLabel : featureLabel;
@@ -152939,7 +153052,8 @@ function Legend(props) {
152939
153052
  contourThresholds,
152940
153053
  contoursFilled,
152941
153054
  contoursVisible,
152942
- pointsVisible
153055
+ pointsVisible,
153056
+ featureAggregationStrategy
152943
153057
  ]);
152944
153058
  return jsxRuntimeExports.jsx("div", { className: clsx(classes.legend, {
152945
153059
  [classes.legendRelative]: positionRelative,
@@ -152981,6 +153095,7 @@ makeStyles(() => ({
152981
153095
  }
152982
153096
  }));
152983
153097
  const DEFAULT_CONTOUR_PERCENTILES = [0.09, 0.9, 0.99];
153098
+ const DEFAULT_FEATURE_AGGREGATION_STRATEGY = "first";
152984
153099
  function EmbeddingScatterplotSubscriber(props) {
152985
153100
  const {
152986
153101
  uuid,
@@ -153035,7 +153150,8 @@ function EmbeddingScatterplotSubscriber(props) {
153035
153150
  embeddingContoursFilled,
153036
153151
  embeddingContourPercentiles: contourPercentiles,
153037
153152
  contourColorEncoding,
153038
- contourColor
153153
+ contourColor,
153154
+ featureAggregationStrategy
153039
153155
  }, {
153040
153156
  setEmbeddingZoom: setZoom,
153041
153157
  setEmbeddingTargetX: setTargetX,
@@ -153061,7 +153177,8 @@ function EmbeddingScatterplotSubscriber(props) {
153061
153177
  setEmbeddingContoursVisible,
153062
153178
  setEmbeddingContoursFilled,
153063
153179
  setEmbeddingContourPercentiles: setContourPercentiles,
153064
- setContourColorEncoding
153180
+ setContourColorEncoding,
153181
+ setFeatureAggregationStrategy
153065
153182
  }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType$1.SCATTERPLOT], coordinationScopes);
153066
153183
  const {
153067
153184
  embeddingZoom: initialZoom,
@@ -153073,6 +153190,7 @@ function EmbeddingScatterplotSubscriber(props) {
153073
153190
  );
153074
153191
  const observationsLabel = observationsLabelOverride || obsType;
153075
153192
  const sampleSetSelection = sampleSetSelectionFromProps || sampleSetSelectionFromCoordination;
153193
+ const featureAggregationStrategyToUse = featureAggregationStrategy ?? DEFAULT_FEATURE_AGGREGATION_STRATEGY;
153076
153194
  const [width, height, deckRef] = useDeckCanvasSize();
153077
153195
  const title = titleOverride || `Scatterplot (${mapping})`;
153078
153196
  const [obsLabelsTypes, obsLabelsData] = useMultiObsLabels(
@@ -153249,7 +153367,7 @@ function EmbeddingScatterplotSubscriber(props) {
153249
153367
  return [null, null, null, null, null];
153250
153368
  }, [obsEmbedding]);
153251
153369
  useEffect(() => {
153252
- if (xRange && yRange) {
153370
+ if (xRange && yRange && width && height) {
153253
153371
  const pointSizeDevicePixels = getPointSizeDevicePixels(
153254
153372
  window.devicePixelRatio,
153255
153373
  zoom,
@@ -153342,6 +153460,9 @@ function EmbeddingScatterplotSubscriber(props) {
153342
153460
  originalViewState.target[1]
153343
153461
  ];
153344
153462
  const scaleFactor = 2 ** originalViewState.zoom;
153463
+ if (!(typeof scaleFactor === "number" && typeof center2[0] === "number" && typeof center2[1] === "number") || Number.isNaN(scaleFactor)) {
153464
+ return null;
153465
+ }
153345
153466
  const radius = Math.min(width, height) / 2 / scaleFactor;
153346
153467
  const numPoints = 96;
153347
153468
  const options = { steps: numPoints, units: "degrees" };
@@ -153386,9 +153507,9 @@ function EmbeddingScatterplotSubscriber(props) {
153386
153507
  }
153387
153508
  return null;
153388
153509
  }, [sampleEdges]);
153389
- const stratifiedData = useMemo(() => {
153510
+ const [stratifiedData, stratifiedDataCount] = useMemo(() => {
153390
153511
  if (alignedEmbeddingData == null ? void 0 : alignedEmbeddingData.data) {
153391
- const result = stratifyArrays(
153512
+ const [result, cellCountResult] = stratifyArrays(
153392
153513
  sampleEdges,
153393
153514
  sampleIdToObsIdsMap,
153394
153515
  sampleSets,
@@ -153399,13 +153520,13 @@ function EmbeddingScatterplotSubscriber(props) {
153399
153520
  {
153400
153521
  obsEmbeddingX: alignedEmbeddingData.data[0],
153401
153522
  obsEmbeddingY: alignedEmbeddingData.data[1],
153402
- // TODO: aggregate and transform expression data if needed prior to passing here
153403
- ...(uint8ExpressionData == null ? void 0 : uint8ExpressionData[0]) ? { featureValue: uint8ExpressionData == null ? void 0 : uint8ExpressionData[0] } : {}
153404
- }
153523
+ ...(uint8ExpressionData == null ? void 0 : uint8ExpressionData[0]) ? { featureValue: uint8ExpressionData } : {}
153524
+ },
153525
+ featureAggregationStrategyToUse
153405
153526
  );
153406
- return result;
153527
+ return [result, cellCountResult];
153407
153528
  }
153408
- return null;
153529
+ return [null, null];
153409
153530
  }, [
153410
153531
  alignedEmbeddingIndex,
153411
153532
  alignedEmbeddingData,
@@ -153415,7 +153536,8 @@ function EmbeddingScatterplotSubscriber(props) {
153415
153536
  sampleSets,
153416
153537
  sampleSetSelection,
153417
153538
  cellSetSelection,
153418
- mergedCellSets
153539
+ mergedCellSets,
153540
+ featureAggregationStrategyToUse
153419
153541
  ]);
153420
153542
  const setViewState = ({ zoom: newZoom, target }) => {
153421
153543
  setZoom(newZoom);
@@ -153423,11 +153545,12 @@ function EmbeddingScatterplotSubscriber(props) {
153423
153545
  setTargetY(target[1]);
153424
153546
  setTargetZ(target[2] || 0);
153425
153547
  };
153548
+ const cellCountToUse = embeddingPointsVisible ? cellsCount : stratifiedDataCount ?? cellsCount;
153426
153549
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
153427
153550
  TitleInfo,
153428
153551
  {
153429
153552
  title,
153430
- info: `${commaNumber(cellsCount)} ${pluralize(observationsLabel, cellsCount)}`,
153553
+ info: `${commaNumber(cellCountToUse)} ${pluralize(observationsLabel, cellCountToUse)}`,
153431
153554
  closeButtonVisible,
153432
153555
  downloadButtonVisible,
153433
153556
  removeGridComponent,
@@ -153471,7 +153594,9 @@ function EmbeddingScatterplotSubscriber(props) {
153471
153594
  setContourPercentiles,
153472
153595
  defaultContourPercentiles: DEFAULT_CONTOUR_PERCENTILES,
153473
153596
  contourColorEncoding,
153474
- setContourColorEncoding
153597
+ setContourColorEncoding,
153598
+ featureAggregationStrategy,
153599
+ setFeatureAggregationStrategy
153475
153600
  }
153476
153601
  ),
153477
153602
  children: [
@@ -153523,7 +153648,7 @@ function EmbeddingScatterplotSubscriber(props) {
153523
153648
  featureSelection: geneSelection
153524
153649
  }
153525
153650
  ),
153526
- tooltipsVisible && /* @__PURE__ */ jsxRuntimeExports.jsx(
153651
+ tooltipsVisible && width && height ? /* @__PURE__ */ jsxRuntimeExports.jsx(
153527
153652
  ScatterplotTooltipSubscriber,
153528
153653
  {
153529
153654
  parentUuid: uuid,
@@ -153534,7 +153659,7 @@ function EmbeddingScatterplotSubscriber(props) {
153534
153659
  featureType,
153535
153660
  featureLabelsMap
153536
153661
  }
153537
- ),
153662
+ ) : null,
153538
153663
  /* @__PURE__ */ jsxRuntimeExports.jsx(
153539
153664
  Legend,
153540
153665
  {
@@ -153548,13 +153673,14 @@ function EmbeddingScatterplotSubscriber(props) {
153548
153673
  featureValueColormap: geneExpressionColormap,
153549
153674
  featureValueColormapRange: geneExpressionColormapRange,
153550
153675
  obsSetSelection: cellSetSelection,
153551
- extent: expressionExtents == null ? void 0 : expressionExtents[0],
153552
- missing: expressionMissing == null ? void 0 : expressionMissing[0],
153676
+ extent: expressionExtents,
153677
+ missing: expressionMissing,
153553
153678
  pointsVisible: embeddingPointsVisible,
153554
153679
  contoursVisible: embeddingContoursVisible,
153555
153680
  contoursFilled: embeddingContoursFilled,
153556
153681
  contourPercentiles: contourPercentiles || DEFAULT_CONTOUR_PERCENTILES,
153557
- contourThresholds
153682
+ contourThresholds,
153683
+ featureAggregationStrategy: featureAggregationStrategyToUse
153558
153684
  }
153559
153685
  )
153560
153686
  ]
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { D, E } from "./index-a37f30c8.js";
1
+ import { D, E } from "./index-ab73546f.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-a37f30c8.js";
1
+ import { B as BaseDecoder } from "./index-ab73546f.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -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-a37f30c8.js";
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,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-a37f30c8.js";
1
+ import { B as BaseDecoder } from "./index-ab73546f.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-a37f30c8.js";
1
+ import { B as BaseDecoder } from "./index-ab73546f.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-a37f30c8.js";
1
+ import { B as BaseDecoder } from "./index-ab73546f.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1,4 +1,4 @@
1
- import { B as BaseDecoder } from "./index-a37f30c8.js";
1
+ import { B as BaseDecoder } from "./index-ab73546f.js";
2
2
  import "react";
3
3
  import "@vitessce/vit-s";
4
4
  import "react-dom";
@@ -1 +1 @@
1
- {"version":3,"file":"EmbeddingScatterplotSubscriber.d.ts","sourceRoot":"","sources":["../src/EmbeddingScatterplotSubscriber.js"],"names":[],"mappings":"AA2CA;;;;;;;;;;;;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,eAmjBA"}
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"}
@@ -10,6 +10,7 @@ import { Scatterplot, ScatterplotTooltipSubscriber, ScatterplotOptions, getPoint
10
10
  import { Legend } from '@vitessce/legend';
11
11
  import { ViewType, COMPONENT_COORDINATION_TYPES, ViewHelpMapping } from '@vitessce/constants-internal';
12
12
  import { DEFAULT_CONTOUR_PERCENTILES } from './constants.js';
13
+ const DEFAULT_FEATURE_AGGREGATION_STRATEGY = 'first';
13
14
  /**
14
15
  * A subscriber component for the scatterplot.
15
16
  * @param {object} props
@@ -33,11 +34,13 @@ export function EmbeddingScatterplotSubscriber(props) {
33
34
  const setComponentHover = useSetComponentHover();
34
35
  const setComponentViewInfo = useSetComponentViewInfo(uuid);
35
36
  // Get "props" from the coordination space.
36
- 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);
37
38
  const { embeddingZoom: initialZoom, embeddingTargetX: initialTargetX, embeddingTargetY: initialTargetY, } = useInitialCoordination(COMPONENT_COORDINATION_TYPES[ViewType.SCATTERPLOT], coordinationScopes);
38
39
  const observationsLabel = observationsLabelOverride || obsType;
39
40
  const sampleSetSelection = (sampleSetSelectionFromProps
40
41
  || sampleSetSelectionFromCoordination);
42
+ const featureAggregationStrategyToUse = featureAggregationStrategy
43
+ ?? DEFAULT_FEATURE_AGGREGATION_STRATEGY;
41
44
  const [width, height, deckRef] = useDeckCanvasSize();
42
45
  const title = titleOverride || `Scatterplot (${mapping})`;
43
46
  const [obsLabelsTypes, obsLabelsData] = useMultiObsLabels(coordinationScopes, obsType, loaders, dataset);
@@ -129,7 +132,7 @@ export function EmbeddingScatterplotSubscriber(props) {
129
132
  // compute the cell radius scale based on the
130
133
  // extents of the cell coordinates on the x/y axes.
131
134
  useEffect(() => {
132
- if (xRange && yRange) {
135
+ if (xRange && yRange && width && height) {
133
136
  const pointSizeDevicePixels = getPointSizeDevicePixels(window.devicePixelRatio, zoom, xRange, yRange, width, height);
134
137
  setDynamicCellRadius(pointSizeDevicePixels);
135
138
  const nextCellOpacityScale = getPointOpacity(zoom, xRange, yRange, width, height, numCells, averageFillDensity);
@@ -204,6 +207,9 @@ export function EmbeddingScatterplotSubscriber(props) {
204
207
  originalViewState.target[1],
205
208
  ];
206
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
+ }
207
213
  const radius = Math.min(width, height) / 2 / scaleFactor;
208
214
  const numPoints = 96;
209
215
  const options = { steps: numPoints, units: 'degrees' };
@@ -263,20 +269,19 @@ export function EmbeddingScatterplotSubscriber(props) {
263
269
  return null;
264
270
  }, [sampleEdges]);
265
271
  // Stratify multiple arrays: per-cellSet and per-sampleSet.
266
- const stratifiedData = useMemo(() => {
272
+ const [stratifiedData, stratifiedDataCount] = useMemo(() => {
267
273
  if (alignedEmbeddingData?.data) {
268
- const result = stratifyArrays(sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetSelection, alignedEmbeddingIndex, mergedCellSets, cellSetSelection, {
274
+ const [result, cellCountResult] = stratifyArrays(sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetSelection, alignedEmbeddingIndex, mergedCellSets, cellSetSelection, {
269
275
  obsEmbeddingX: alignedEmbeddingData.data[0],
270
276
  obsEmbeddingY: alignedEmbeddingData.data[1],
271
- // TODO: aggregate and transform expression data if needed prior to passing here
272
- ...(uint8ExpressionData?.[0] ? { featureValue: uint8ExpressionData?.[0] } : {}),
273
- });
274
- return result;
277
+ ...(uint8ExpressionData?.[0] ? { featureValue: uint8ExpressionData } : {}),
278
+ }, featureAggregationStrategyToUse);
279
+ return [result, cellCountResult];
275
280
  }
276
- return null;
281
+ return [null, null];
277
282
  }, [alignedEmbeddingIndex, alignedEmbeddingData, uint8ExpressionData,
278
283
  sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetSelection,
279
- cellSetSelection, mergedCellSets,
284
+ cellSetSelection, mergedCellSets, featureAggregationStrategyToUse,
280
285
  ]);
281
286
  const setViewState = ({ zoom: newZoom, target }) => {
282
287
  setZoom(newZoom);
@@ -284,12 +289,16 @@ export function EmbeddingScatterplotSubscriber(props) {
284
289
  setTargetY(target[1]);
285
290
  setTargetZ(target[2] || 0);
286
291
  };
287
- return (_jsxs(TitleInfo, { title: title, info: `${commaNumber(cellsCount)} ${plur(observationsLabel, cellsCount)}`, 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 })), 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: () => {
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: () => {
288
297
  setComponentHover(uuid);
289
298
  }, updateViewInfo: setComponentViewInfo, getExpressionValue: getExpressionValue, getCellIsSelected: getCellIsSelected, obsSetSelection: cellSetSelection, sampleSetSelection: sampleSetSelection,
290
299
  // InternMap data structures where keys are
291
300
  // obsSet -> sampleSet -> arrayKey -> [].
292
- stratifiedData: stratifiedData, obsSetColor: cellSetColor, sampleSetColor: sampleSetColor, contourThresholds: contourThresholds, contourColorEncoding: contourColorEncoding, contourColor: contourColor, contoursFilled: embeddingContoursFilled, embeddingPointsVisible: embeddingPointsVisible, embeddingContoursVisible: embeddingContoursVisible, circleInfo: circleInfo, featureSelection: geneSelection }), 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?.[0], missing: expressionMissing?.[0],
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,
293
302
  // Contour percentile legend
294
- 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 })] }));
295
304
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitessce/scatterplot-embedding",
3
- "version": "3.5.10",
3
+ "version": "3.5.11",
4
4
  "author": "HIDIVE Lab at HMS",
5
5
  "homepage": "http://vitessce.io",
6
6
  "repository": {
@@ -21,12 +21,12 @@
21
21
  "lodash-es": "^4.17.21",
22
22
  "react-aria": "^3.28.0",
23
23
  "@turf/circle": "^7.2.0",
24
- "@vitessce/constants-internal": "3.5.10",
25
- "@vitessce/legend": "3.5.10",
26
- "@vitessce/scatterplot": "3.5.10",
27
- "@vitessce/sets-utils": "3.5.10",
28
- "@vitessce/utils": "3.5.10",
29
- "@vitessce/vit-s": "3.5.10"
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"
30
30
  },
31
31
  "devDependencies": {
32
32
  "react": "^18.0.0",
@@ -40,6 +40,7 @@ import { Legend } from '@vitessce/legend';
40
40
  import { ViewType, COMPONENT_COORDINATION_TYPES, ViewHelpMapping } from '@vitessce/constants-internal';
41
41
  import { DEFAULT_CONTOUR_PERCENTILES } from './constants.js';
42
42
 
43
+ const DEFAULT_FEATURE_AGGREGATION_STRATEGY = 'first';
43
44
 
44
45
  /**
45
46
  * A subscriber component for the scatterplot.
@@ -113,6 +114,7 @@ export function EmbeddingScatterplotSubscriber(props) {
113
114
  embeddingContourPercentiles: contourPercentiles,
114
115
  contourColorEncoding,
115
116
  contourColor,
117
+ featureAggregationStrategy,
116
118
  }, {
117
119
  setEmbeddingZoom: setZoom,
118
120
  setEmbeddingTargetX: setTargetX,
@@ -139,6 +141,7 @@ export function EmbeddingScatterplotSubscriber(props) {
139
141
  setEmbeddingContoursFilled,
140
142
  setEmbeddingContourPercentiles: setContourPercentiles,
141
143
  setContourColorEncoding,
144
+ setFeatureAggregationStrategy,
142
145
  }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.SCATTERPLOT], coordinationScopes);
143
146
 
144
147
  const {
@@ -155,6 +158,9 @@ export function EmbeddingScatterplotSubscriber(props) {
155
158
  || sampleSetSelectionFromCoordination
156
159
  );
157
160
 
161
+ const featureAggregationStrategyToUse = featureAggregationStrategy
162
+ ?? DEFAULT_FEATURE_AGGREGATION_STRATEGY;
163
+
158
164
  const [width, height, deckRef] = useDeckCanvasSize();
159
165
 
160
166
  const title = titleOverride || `Scatterplot (${mapping})`;
@@ -299,7 +305,7 @@ export function EmbeddingScatterplotSubscriber(props) {
299
305
  // compute the cell radius scale based on the
300
306
  // extents of the cell coordinates on the x/y axes.
301
307
  useEffect(() => {
302
- if (xRange && yRange) {
308
+ if (xRange && yRange && width && height) {
303
309
  const pointSizeDevicePixels = getPointSizeDevicePixels(
304
310
  window.devicePixelRatio, zoom, xRange, yRange, width, height,
305
311
  );
@@ -395,6 +401,9 @@ export function EmbeddingScatterplotSubscriber(props) {
395
401
  originalViewState.target[1],
396
402
  ];
397
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
+ }
398
407
  const radius = Math.min(width, height) / 2 / scaleFactor;
399
408
  const numPoints = 96;
400
409
  const options = { steps: numPoints, units: 'degrees' };
@@ -457,24 +466,23 @@ export function EmbeddingScatterplotSubscriber(props) {
457
466
  }, [sampleEdges]);
458
467
 
459
468
  // Stratify multiple arrays: per-cellSet and per-sampleSet.
460
- const stratifiedData = useMemo(() => {
469
+ const [stratifiedData, stratifiedDataCount] = useMemo(() => {
461
470
  if (alignedEmbeddingData?.data) {
462
- const result = stratifyArrays(
471
+ const [result, cellCountResult] = stratifyArrays(
463
472
  sampleEdges, sampleIdToObsIdsMap,
464
473
  sampleSets, sampleSetSelection,
465
474
  alignedEmbeddingIndex, mergedCellSets, cellSetSelection, {
466
475
  obsEmbeddingX: alignedEmbeddingData.data[0],
467
476
  obsEmbeddingY: alignedEmbeddingData.data[1],
468
- // TODO: aggregate and transform expression data if needed prior to passing here
469
- ...(uint8ExpressionData?.[0] ? { featureValue: uint8ExpressionData?.[0] } : {}),
470
- },
477
+ ...(uint8ExpressionData?.[0] ? { featureValue: uint8ExpressionData } : {}),
478
+ }, featureAggregationStrategyToUse,
471
479
  );
472
- return result;
480
+ return [result, cellCountResult];
473
481
  }
474
- return null;
482
+ return [null, null];
475
483
  }, [alignedEmbeddingIndex, alignedEmbeddingData, uint8ExpressionData,
476
484
  sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetSelection,
477
- cellSetSelection, mergedCellSets,
485
+ cellSetSelection, mergedCellSets, featureAggregationStrategyToUse,
478
486
  ]);
479
487
 
480
488
  const setViewState = ({ zoom: newZoom, target }) => {
@@ -484,10 +492,15 @@ export function EmbeddingScatterplotSubscriber(props) {
484
492
  setTargetZ(target[2] || 0);
485
493
  };
486
494
 
495
+ // TODO: Update this once the rendered points reflects the selection/filtering.
496
+ const cellCountToUse = embeddingPointsVisible
497
+ ? cellsCount
498
+ : (stratifiedDataCount ?? cellsCount);
499
+
487
500
  return (
488
501
  <TitleInfo
489
502
  title={title}
490
- info={`${commaNumber(cellsCount)} ${plur(observationsLabel, cellsCount)}`}
503
+ info={`${commaNumber(cellCountToUse)} ${plur(observationsLabel, cellCountToUse)}`}
491
504
  closeButtonVisible={closeButtonVisible}
492
505
  downloadButtonVisible={downloadButtonVisible}
493
506
  removeGridComponent={removeGridComponent}
@@ -531,6 +544,8 @@ export function EmbeddingScatterplotSubscriber(props) {
531
544
  defaultContourPercentiles={DEFAULT_CONTOUR_PERCENTILES}
532
545
  contourColorEncoding={contourColorEncoding}
533
546
  setContourColorEncoding={setContourColorEncoding}
547
+ featureAggregationStrategy={featureAggregationStrategy}
548
+ setFeatureAggregationStrategy={setFeatureAggregationStrategy}
534
549
  />
535
550
  )}
536
551
  >
@@ -583,17 +598,17 @@ export function EmbeddingScatterplotSubscriber(props) {
583
598
  circleInfo={circleInfo}
584
599
  featureSelection={geneSelection}
585
600
  />
586
- {tooltipsVisible && (
587
- <ScatterplotTooltipSubscriber
588
- parentUuid={uuid}
589
- obsHighlight={cellHighlight}
590
- width={width}
591
- height={height}
592
- getObsInfo={getObsInfo}
593
- featureType={featureType}
594
- featureLabelsMap={featureLabelsMap}
595
- />
596
- )}
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}
597
612
  <Legend
598
613
  visible
599
614
  theme={theme}
@@ -605,14 +620,15 @@ export function EmbeddingScatterplotSubscriber(props) {
605
620
  featureValueColormap={geneExpressionColormap}
606
621
  featureValueColormapRange={geneExpressionColormapRange}
607
622
  obsSetSelection={cellSetSelection}
608
- extent={expressionExtents?.[0]}
609
- missing={expressionMissing?.[0]}
623
+ extent={expressionExtents}
624
+ missing={expressionMissing}
610
625
  // Contour percentile legend
611
626
  pointsVisible={embeddingPointsVisible}
612
627
  contoursVisible={embeddingContoursVisible}
613
628
  contoursFilled={embeddingContoursFilled}
614
629
  contourPercentiles={contourPercentiles || DEFAULT_CONTOUR_PERCENTILES}
615
630
  contourThresholds={contourThresholds}
631
+ featureAggregationStrategy={featureAggregationStrategyToUse}
616
632
  />
617
633
  </TitleInfo>
618
634
  );