@vitessce/scatterplot-embedding 3.5.10 → 3.5.12

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-de0a204e.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,10 +11984,12 @@ 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,
11987
- CoordinationType$1.TOOLTIPS_VISIBLE
11991
+ CoordinationType$1.TOOLTIPS_VISIBLE,
11992
+ CoordinationType$1.PHOTOMETRIC_INTERPRETATION
11988
11993
  ],
11989
11994
  [ViewType$1.SPATIAL_BETA]: [
11990
11995
  CoordinationType$1.META_COORDINATION_SCOPES,
@@ -12018,6 +12023,7 @@ const COMPONENT_COORDINATION_TYPES = {
12018
12023
  CoordinationType$1.FEATURE_SELECTION,
12019
12024
  CoordinationType$1.FEATURE_VALUE_COLORMAP,
12020
12025
  CoordinationType$1.FEATURE_VALUE_COLORMAP_RANGE,
12026
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
12021
12027
  CoordinationType$1.OBS_COLOR_ENCODING,
12022
12028
  CoordinationType$1.ADDITIONAL_OBS_SETS,
12023
12029
  CoordinationType$1.MOLECULE_HIGHLIGHT,
@@ -12056,7 +12062,8 @@ const COMPONENT_COORDINATION_TYPES = {
12056
12062
  CoordinationType$1.LEGEND_VISIBLE,
12057
12063
  CoordinationType$1.SPATIAL_CHANNEL_LABELS_VISIBLE,
12058
12064
  CoordinationType$1.SPATIAL_CHANNEL_LABELS_ORIENTATION,
12059
- CoordinationType$1.SPATIAL_CHANNEL_LABEL_SIZE
12065
+ CoordinationType$1.SPATIAL_CHANNEL_LABEL_SIZE,
12066
+ CoordinationType$1.PHOTOMETRIC_INTERPRETATION
12060
12067
  ],
12061
12068
  [ViewType$1.HEATMAP]: [
12062
12069
  CoordinationType$1.DATASET,
@@ -12131,6 +12138,7 @@ const COMPONENT_COORDINATION_TYPES = {
12131
12138
  CoordinationType$1.FEATURE_SELECTION,
12132
12139
  CoordinationType$1.FEATURE_VALUE_TRANSFORM,
12133
12140
  CoordinationType$1.FEATURE_VALUE_TRANSFORM_COEFFICIENT,
12141
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
12134
12142
  CoordinationType$1.OBS_SET_SELECTION,
12135
12143
  CoordinationType$1.OBS_SET_FILTER,
12136
12144
  CoordinationType$1.OBS_SET_HIGHLIGHT,
@@ -12146,6 +12154,7 @@ const COMPONENT_COORDINATION_TYPES = {
12146
12154
  CoordinationType$1.FEATURE_TYPE,
12147
12155
  CoordinationType$1.FEATURE_VALUE_TYPE,
12148
12156
  CoordinationType$1.FEATURE_SELECTION,
12157
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
12149
12158
  CoordinationType$1.ADDITIONAL_OBS_SETS,
12150
12159
  CoordinationType$1.OBS_SET_COLOR,
12151
12160
  CoordinationType$1.OBS_COLOR_ENCODING,
@@ -12169,7 +12178,8 @@ const COMPONENT_COORDINATION_TYPES = {
12169
12178
  CoordinationType$1.SPATIAL_ROTATION_Y,
12170
12179
  CoordinationType$1.SPATIAL_ROTATION_Z,
12171
12180
  CoordinationType$1.SPATIAL_ROTATION_ORBIT,
12172
- CoordinationType$1.SPATIAL_ORBIT_AXIS
12181
+ CoordinationType$1.SPATIAL_ORBIT_AXIS,
12182
+ CoordinationType$1.PHOTOMETRIC_INTERPRETATION
12173
12183
  ],
12174
12184
  [ViewType$1.LAYER_CONTROLLER_BETA]: [
12175
12185
  CoordinationType$1.META_COORDINATION_SCOPES,
@@ -12224,6 +12234,7 @@ const COMPONENT_COORDINATION_TYPES = {
12224
12234
  CoordinationType$1.FEATURE_VALUE_COLORMAP,
12225
12235
  CoordinationType$1.FEATURE_VALUE_COLORMAP_RANGE,
12226
12236
  CoordinationType$1.FEATURE_SELECTION,
12237
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
12227
12238
  CoordinationType$1.TOOLTIPS_VISIBLE,
12228
12239
  CoordinationType$1.TOOLTIP_CROSSHAIRS_VISIBLE,
12229
12240
  CoordinationType$1.LEGEND_VISIBLE,
@@ -12264,6 +12275,7 @@ const COMPONENT_COORDINATION_TYPES = {
12264
12275
  CoordinationType$1.FEATURE_VALUE_TRANSFORM_COEFFICIENT,
12265
12276
  CoordinationType$1.FEATURE_VALUE_POSITIVITY_THRESHOLD,
12266
12277
  CoordinationType$1.FEATURE_VALUE_COLORMAP,
12278
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
12267
12279
  CoordinationType$1.OBS_SET_SELECTION,
12268
12280
  CoordinationType$1.OBS_SET_FILTER,
12269
12281
  CoordinationType$1.OBS_SET_HIGHLIGHT,
@@ -12292,6 +12304,7 @@ const COMPONENT_COORDINATION_TYPES = {
12292
12304
  CoordinationType$1.FEATURE_SELECTION,
12293
12305
  CoordinationType$1.FEATURE_VALUE_TRANSFORM,
12294
12306
  CoordinationType$1.FEATURE_VALUE_TRANSFORM_COEFFICIENT,
12307
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
12295
12308
  CoordinationType$1.OBS_SET_SELECTION,
12296
12309
  CoordinationType$1.OBS_SET_HIGHLIGHT,
12297
12310
  CoordinationType$1.OBS_HIGHLIGHT,
@@ -12396,6 +12409,7 @@ const COMPONENT_COORDINATION_TYPES = {
12396
12409
  CoordinationType$1.OBS_TYPE,
12397
12410
  CoordinationType$1.SAMPLE_TYPE,
12398
12411
  CoordinationType$1.FEATURE_SELECTION,
12412
+ CoordinationType$1.FEATURE_AGGREGATION_STRATEGY,
12399
12413
  CoordinationType$1.SAMPLE_SET_SELECTION,
12400
12414
  CoordinationType$1.SAMPLE_SET_FILTER,
12401
12415
  CoordinationType$1.OBS_SET_SELECTION,
@@ -12703,6 +12717,7 @@ z.object({
12703
12717
  termColumn: z.string().optional(),
12704
12718
  pValueColumn: z.string(),
12705
12719
  pValueAdjusted: z.boolean().optional(),
12720
+ featureSetLibrary: z.string().optional().describe("Optionally, provide a feature set library name. By default, Reactome_2022."),
12706
12721
  analysisType: z.string().optional().describe("Optionally, provide an analysis_type name. By default, pertpy_hypergeometric.")
12707
12722
  });
12708
12723
  z.object({
@@ -12713,7 +12728,6 @@ z.object({
12713
12728
  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
12729
  foldChangeTransformation: z.enum(["log2"]).optional(),
12715
12730
  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
12731
  analysisType: z.string().optional().describe("Optionally, provide an analysis_type name. By default, sccoda_df.")
12718
12732
  });
12719
12733
  const annDataObsLabels = annDataObs;
@@ -19717,7 +19731,7 @@ function getCellColors(params) {
19717
19731
  }
19718
19732
  return /* @__PURE__ */ new Map();
19719
19733
  }
19720
- function stratifyArrays(sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetSelection, obsIndex, mergedCellSets, cellSetSelection, arraysToStratify) {
19734
+ function stratifyArrays(sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetSelection, obsIndex, mergedCellSets, cellSetSelection, arraysToStratify, featureAggregationStrategy) {
19721
19735
  const result = new InternMap([], JSON.stringify);
19722
19736
  const hasSampleSetSelection = Array.isArray(sampleSetSelection) && sampleSetSelection.length > 0;
19723
19737
  const hasCellSetSelection = Array.isArray(cellSetSelection) && cellSetSelection.length > 0;
@@ -19727,9 +19741,13 @@ function stratifyArrays(sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetS
19727
19741
  if (arrKeys.includes("obsIndex") || arrKeys.includes("i")) {
19728
19742
  throw new Error('The keys "obsIndex" and "i" are reserved for internal use.');
19729
19743
  }
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
- }
19744
+ if (Object.entries(arraysToStratify).some(([arrKey, arr]) => {
19745
+ if (arrKey === "featureValue") {
19746
+ return arr.some((a2) => a2.length !== obsIndex.length);
19747
+ }
19748
+ return arr.length !== obsIndex.length;
19749
+ }))
19750
+ ;
19733
19751
  const sampleSetInfo = sampleSets && sampleSetSelection ? treeToObsIdsBySetNames(sampleSets, sampleSetSelection) : null;
19734
19752
  const cellSetInfo = mergedCellSets && cellSetSelection ? treeToObsIdsBySetNames(mergedCellSets, cellSetSelection) : null;
19735
19753
  cellSetKeys.forEach((cellSetKey) => {
@@ -19759,6 +19777,7 @@ function stratifyArrays(sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetS
19759
19777
  });
19760
19778
  const sampleIdToSetMap = sampleSets && sampleSetSelection ? treeToSelectedSetMap(sampleSets, sampleSetSelection) : null;
19761
19779
  const cellIdToSetMap = mergedCellSets && cellSetSelection ? treeToSelectedSetMap(mergedCellSets, cellSetSelection) : null;
19780
+ let cellCount = 0;
19762
19781
  for (let i2 = 0; i2 < obsIndex.length; i2 += 1) {
19763
19782
  const obsId = obsIndex[i2];
19764
19783
  const cellSet = (cellIdToSetMap == null ? void 0 : cellIdToSetMap.get(obsId)) || null;
@@ -19769,10 +19788,28 @@ function stratifyArrays(sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetS
19769
19788
  }
19770
19789
  const insertionIndex = result.get(cellSet).get(sampleSet).get("i");
19771
19790
  arrKeys.forEach((arrKey) => {
19772
- const value = arraysToStratify[arrKey][i2];
19791
+ let value;
19792
+ if (arrKey === "featureValue") {
19793
+ if (featureAggregationStrategy === "first") {
19794
+ value = arraysToStratify[arrKey][0][i2];
19795
+ } else if (featureAggregationStrategy === "last") {
19796
+ value = arraysToStratify[arrKey].at(-1)[i2];
19797
+ } else if (typeof featureAggregationStrategy === "number") {
19798
+ const j = featureAggregationStrategy;
19799
+ value = arraysToStratify[arrKey][j][i2];
19800
+ } else if (featureAggregationStrategy === "sum" || featureAggregationStrategy === "mean") {
19801
+ value = arraysToStratify[arrKey].reduce((a2, h) => a2 + h[i2], 0);
19802
+ if (featureAggregationStrategy === "mean") {
19803
+ value /= arraysToStratify[arrKey].length;
19804
+ }
19805
+ }
19806
+ } else {
19807
+ value = arraysToStratify[arrKey][i2];
19808
+ }
19773
19809
  result.get(cellSet).get(sampleSet).get(arrKey)[insertionIndex] = value;
19774
19810
  });
19775
19811
  result.get(cellSet).get(sampleSet).set("i", insertionIndex + 1);
19812
+ cellCount += 1;
19776
19813
  }
19777
19814
  cellSetKeys.forEach((cellSetKey) => {
19778
19815
  sampleSetKeys.forEach((sampleSetKey) => {
@@ -19783,7 +19820,7 @@ function stratifyArrays(sampleEdges, sampleIdToObsIdsMap, sampleSets, sampleSetS
19783
19820
  result.get(cellSetKey).get(sampleSetKey).delete("i");
19784
19821
  });
19785
19822
  });
19786
- return result;
19823
+ return [result, cellCount];
19787
19824
  }
19788
19825
  function tree_add(d) {
19789
19826
  var x2 = +this._x.call(null, d), y2 = +this._y.call(null, d);
@@ -127455,8 +127492,15 @@ class SelectionLayer extends CompositeLayer {
127455
127492
  if (!nodePolygonContainsSelectedPolygon && !nodePolygonWithinSelectedPolygon && !nodePolygonOverlapsSelectedPolgyon) {
127456
127493
  return true;
127457
127494
  }
127458
- if (node.data && booleanPointInPolygon$1(point$1([].slice.call(getObsCoords(node.data))), selectedPolygon)) {
127459
- pickingInfos.push(node.data);
127495
+ if (node.data) {
127496
+ let current = node;
127497
+ while (current) {
127498
+ const pointCoords = [].slice.call(getObsCoords(current.data));
127499
+ if (booleanPointInPolygon$1(point$1(pointCoords), selectedPolygon)) {
127500
+ pickingInfos.push(current.data);
127501
+ }
127502
+ current = current.next;
127503
+ }
127460
127504
  }
127461
127505
  return false;
127462
127506
  });
@@ -128668,16 +128712,16 @@ function addDecoder(cases, importFn) {
128668
128712
  }
128669
128713
  cases.forEach((c2) => registry$1.set(c2, importFn));
128670
128714
  }
128671
- addDecoder([void 0, 1], () => import("./raw-b648d255.js").then((m2) => m2.default));
128672
- addDecoder(5, () => import("./lzw-ce001eff.js").then((m2) => m2.default));
128715
+ addDecoder([void 0, 1], () => import("./raw-5d0a1d41.js").then((m2) => m2.default));
128716
+ addDecoder(5, () => import("./lzw-cecbc3f4.js").then((m2) => m2.default));
128673
128717
  addDecoder(6, () => {
128674
128718
  throw new Error("old style JPEG compression is not supported.");
128675
128719
  });
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));
128720
+ addDecoder(7, () => import("./jpeg-ea5d6166.js").then((m2) => m2.default));
128721
+ addDecoder([8, 32946], () => import("./deflate-94dfdd6c.js").then((m2) => m2.default));
128722
+ addDecoder(32773, () => import("./packbits-04a500da.js").then((m2) => m2.default));
128723
+ addDecoder(34887, () => import("./lerc-7e5d53ac.js").then((m2) => m2.default));
128724
+ addDecoder(50001, () => import("./webimage-67a3c5bf.js").then((m2) => m2.default));
128681
128725
  function decodeRowAcc(row, stride) {
128682
128726
  let length2 = row.length - stride;
128683
128727
  let offset5 = 0;
@@ -149712,7 +149756,10 @@ const getPosition = (object2, { index: index2, data, target }) => {
149712
149756
  target[2] = POINT_LAYER_Z_INDEX;
149713
149757
  return target;
149714
149758
  };
149715
- const contourGetWeight = (object2, { index: index2, data }) => data.src.featureValues[index2];
149759
+ const contourGetWeight = (object2, { index: index2, data }) => {
149760
+ var _a3;
149761
+ return (_a3 = data.src.featureValues) == null ? void 0 : _a3[index2];
149762
+ };
149716
149763
  const contourGetPosition = (object2, { index: index2, data, target }) => {
149717
149764
  target[0] = data.src.embeddingX[index2];
149718
149765
  target[1] = -data.src.embeddingY[index2];
@@ -150009,6 +150056,16 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
150009
150056
  const { obsEmbeddingIndex, obsEmbedding } = this.props;
150010
150057
  super.viewInfoDidUpdate(obsEmbeddingIndex, obsEmbedding, makeFlippedGetObsCoords);
150011
150058
  }
150059
+ componentWillUnmount() {
150060
+ delete this.cellsQuadTree;
150061
+ delete this.cellsLayer;
150062
+ delete this.cellsData;
150063
+ delete this.stratifiedData;
150064
+ delete this.cellSetsForceSimulation;
150065
+ delete this.cellSetsLabelPrevZoom;
150066
+ delete this.cellSetsLayers;
150067
+ delete this.contourLayers;
150068
+ }
150012
150069
  /**
150013
150070
  * Here, asynchronously check whether props have
150014
150071
  * updated which require re-computing memoized variables,
@@ -150215,8 +150272,9 @@ if (typeof document !== "undefined") {
150215
150272
  else
150216
150273
  document.addEventListener("DOMContentLoaded", $bbed8b41f857bcc0$var$setupGlobalEvents);
150217
150274
  }
150275
+ const FEATURE_AGGREGATION_STRATEGIES = ["first", "last", "sum", "mean"];
150218
150276
  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;
150277
+ 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
150278
  const scatterplotOptionsId = $bdb11010cef70236$export$f680877a34711e37();
150221
150279
  const observationsLabelNice = capitalize$1(observationsLabel);
150222
150280
  const classes = usePlotOptionsStyles();
@@ -150267,6 +150325,9 @@ function ScatterplotOptions(props) {
150267
150325
  setContourPercentiles(values2);
150268
150326
  }
150269
150327
  const handlePercentilesChangeDebounced = useCallback(debounce$2(handlePercentilesChange, 5, { trailing: true }), [handlePercentilesChange]);
150328
+ function handleFeatureAggregationStrategyChange(event) {
150329
+ setFeatureAggregationStrategy(event.target.value);
150330
+ }
150270
150331
  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
150332
  "aria-label": "Show or hide set labels",
150272
150333
  id: `scatterplot-set-labels-visible-${scatterplotOptionsId}`
@@ -150308,7 +150369,9 @@ function ScatterplotOptions(props) {
150308
150369
  id: `scatterplot-contours-filled-${scatterplotOptionsId}`
150309
150370
  } }) })] }), 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
150371
  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 }) })] })] });
150372
+ }, 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: {
150373
+ id: `feature-aggregation-strategy-${scatterplotOptionsId}`
150374
+ }, children: FEATURE_AGGREGATION_STRATEGIES.map((opt) => jsxRuntimeExports.jsx("option", { value: opt, children: capitalize$1(opt) }, opt)) }) })] }) : null] });
150312
150375
  }
150313
150376
  const styles10 = makeStyles((theme) => ({
150314
150377
  tooltipAnchor: {
@@ -152819,8 +152882,50 @@ const titleHeight = 10;
152819
152882
  const rectHeight = 8;
152820
152883
  const rectMarginY = 2;
152821
152884
  const rectMarginX = 2;
152885
+ function combineExtents(extents, featureAggregationStrategy) {
152886
+ if (Array.isArray(extents)) {
152887
+ if (featureAggregationStrategy === "first") {
152888
+ return extents[0];
152889
+ }
152890
+ if (featureAggregationStrategy === "last") {
152891
+ return extents.at(-1);
152892
+ }
152893
+ if (typeof featureAggregationStrategy === "number") {
152894
+ const i2 = featureAggregationStrategy;
152895
+ return extents[i2];
152896
+ }
152897
+ if (featureAggregationStrategy === "sum") {
152898
+ return extents.reduce((a2, h) => [a2[0] + h[0], a2[1] + h[1]]);
152899
+ }
152900
+ if (featureAggregationStrategy === "mean") {
152901
+ return extents.reduce((a2, h) => [a2[0] + h[0], a2[1] + h[1]]).map((v) => v / extents.length);
152902
+ }
152903
+ }
152904
+ return null;
152905
+ }
152906
+ function combineMissings(missings, featureAggregationStrategy) {
152907
+ if (Array.isArray(missings)) {
152908
+ if (featureAggregationStrategy === "first") {
152909
+ return missings[0];
152910
+ }
152911
+ if (featureAggregationStrategy === "last") {
152912
+ return missings.at(-1);
152913
+ }
152914
+ if (typeof featureAggregationStrategy === "number") {
152915
+ const i2 = featureAggregationStrategy;
152916
+ return missings[i2];
152917
+ }
152918
+ if (featureAggregationStrategy === "sum") {
152919
+ return missings.reduce((a2, h) => a2 + h, 0);
152920
+ }
152921
+ if (featureAggregationStrategy === "mean") {
152922
+ return missings.reduce((a2, h) => a2 + h / missings.length, 0);
152923
+ }
152924
+ }
152925
+ return null;
152926
+ }
152822
152927
  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;
152928
+ 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
152929
  const svgRef = useRef();
152825
152930
  const classes = useStyles();
152826
152931
  const isDarkTheme = theme === "dark";
@@ -152829,9 +152934,9 @@ function Legend(props) {
152829
152934
  const layerColor = Array.isArray(spatialLayerColor) && spatialLayerColor.length === 3 ? spatialLayerColor : getDefaultColor(theme);
152830
152935
  const channelColor = Array.isArray(spatialChannelColor) && spatialChannelColor.length === 3 ? spatialChannelColor : getDefaultColor(theme);
152831
152936
  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);
152937
+ 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
152938
  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);
152939
+ const dynamicHeight = isSetColor && obsSetSelection ? levelZeroNames.length * titleHeight + (obsSetSelection == null ? void 0 : obsSetSelection.length) * (rectHeight + rectMarginY) : height + (!pointsVisible && contoursVisible ? 25 : 0);
152835
152940
  useEffect(() => {
152836
152941
  const domElement = svgRef.current;
152837
152942
  const foregroundColor = highContrast ? "black" : isDarkTheme ? "white" : "black";
@@ -152840,7 +152945,7 @@ function Legend(props) {
152840
152945
  svg.attr("width", width).attr("height", dynamicHeight);
152841
152946
  const g2 = svg.append("g").attr("width", width).attr("height", dynamicHeight);
152842
152947
  if (!considerSelections || obsColorEncoding === "geneSelection") {
152843
- const [xMin, xMax] = extent2 || [0, 1];
152948
+ const [xMin, xMax] = combineExtents(extent2, featureAggregationStrategy) || [0, 1];
152844
152949
  if (featureValueColormap && pointsVisible) {
152845
152950
  const xlinkHref = getXlinkHref(featureValueColormap);
152846
152951
  g2.append("image").attr("x", 0).attr("y", titleHeight).attr("width", width).attr("height", rectHeight).attr("preserveAspectRatio", "none").attr("href", xlinkHref);
@@ -152904,8 +153009,19 @@ function Legend(props) {
152904
153009
  });
152905
153010
  });
152906
153011
  }
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;
153012
+ 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;
153013
+ let featureSelectionLabelRawStr = "";
153014
+ if (featureAggregationStrategy === "first") {
153015
+ featureSelectionLabelRawStr = featureSelectionLabelRaw == null ? void 0 : featureSelectionLabelRaw[0];
153016
+ } else if (featureAggregationStrategy === "last") {
153017
+ featureSelectionLabelRawStr = featureSelectionLabelRaw == null ? void 0 : featureSelectionLabelRaw.at(-1);
153018
+ } else if (featureAggregationStrategy === "sum") {
153019
+ featureSelectionLabelRawStr = "Sum of features";
153020
+ } else if (featureAggregationStrategy === "mean") {
153021
+ featureSelectionLabelRawStr = "Mean of features";
153022
+ }
153023
+ const combinedMissing = combineMissings(missing, featureAggregationStrategy);
153024
+ const featureSelectionLabel = combinedMissing ? `${featureSelectionLabelRawStr} (${Math.round(combinedMissing * 100)}% NaN)` : featureSelectionLabelRawStr;
152909
153025
  const obsLabel = capitalize$1(obsType);
152910
153026
  const featureLabel = considerSelections ? featureSelectionLabel || capitalize$1(featureValueType) : capitalize$1(featureValueType);
152911
153027
  const mainLabel = showObsLabel ? obsLabel : featureLabel;
@@ -152939,7 +153055,8 @@ function Legend(props) {
152939
153055
  contourThresholds,
152940
153056
  contoursFilled,
152941
153057
  contoursVisible,
152942
- pointsVisible
153058
+ pointsVisible,
153059
+ featureAggregationStrategy
152943
153060
  ]);
152944
153061
  return jsxRuntimeExports.jsx("div", { className: clsx(classes.legend, {
152945
153062
  [classes.legendRelative]: positionRelative,
@@ -152981,6 +153098,7 @@ makeStyles(() => ({
152981
153098
  }
152982
153099
  }));
152983
153100
  const DEFAULT_CONTOUR_PERCENTILES = [0.09, 0.9, 0.99];
153101
+ const DEFAULT_FEATURE_AGGREGATION_STRATEGY = "first";
152984
153102
  function EmbeddingScatterplotSubscriber(props) {
152985
153103
  const {
152986
153104
  uuid,
@@ -153035,7 +153153,8 @@ function EmbeddingScatterplotSubscriber(props) {
153035
153153
  embeddingContoursFilled,
153036
153154
  embeddingContourPercentiles: contourPercentiles,
153037
153155
  contourColorEncoding,
153038
- contourColor
153156
+ contourColor,
153157
+ featureAggregationStrategy
153039
153158
  }, {
153040
153159
  setEmbeddingZoom: setZoom,
153041
153160
  setEmbeddingTargetX: setTargetX,
@@ -153061,7 +153180,8 @@ function EmbeddingScatterplotSubscriber(props) {
153061
153180
  setEmbeddingContoursVisible,
153062
153181
  setEmbeddingContoursFilled,
153063
153182
  setEmbeddingContourPercentiles: setContourPercentiles,
153064
- setContourColorEncoding
153183
+ setContourColorEncoding,
153184
+ setFeatureAggregationStrategy
153065
153185
  }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType$1.SCATTERPLOT], coordinationScopes);
153066
153186
  const {
153067
153187
  embeddingZoom: initialZoom,
@@ -153073,6 +153193,7 @@ function EmbeddingScatterplotSubscriber(props) {
153073
153193
  );
153074
153194
  const observationsLabel = observationsLabelOverride || obsType;
153075
153195
  const sampleSetSelection = sampleSetSelectionFromProps || sampleSetSelectionFromCoordination;
153196
+ const featureAggregationStrategyToUse = featureAggregationStrategy ?? DEFAULT_FEATURE_AGGREGATION_STRATEGY;
153076
153197
  const [width, height, deckRef] = useDeckCanvasSize();
153077
153198
  const title = titleOverride || `Scatterplot (${mapping})`;
153078
153199
  const [obsLabelsTypes, obsLabelsData] = useMultiObsLabels(
@@ -153249,7 +153370,7 @@ function EmbeddingScatterplotSubscriber(props) {
153249
153370
  return [null, null, null, null, null];
153250
153371
  }, [obsEmbedding]);
153251
153372
  useEffect(() => {
153252
- if (xRange && yRange) {
153373
+ if (xRange && yRange && width && height) {
153253
153374
  const pointSizeDevicePixels = getPointSizeDevicePixels(
153254
153375
  window.devicePixelRatio,
153255
153376
  zoom,
@@ -153342,6 +153463,9 @@ function EmbeddingScatterplotSubscriber(props) {
153342
153463
  originalViewState.target[1]
153343
153464
  ];
153344
153465
  const scaleFactor = 2 ** originalViewState.zoom;
153466
+ if (!(typeof scaleFactor === "number" && typeof center2[0] === "number" && typeof center2[1] === "number") || Number.isNaN(scaleFactor)) {
153467
+ return null;
153468
+ }
153345
153469
  const radius = Math.min(width, height) / 2 / scaleFactor;
153346
153470
  const numPoints = 96;
153347
153471
  const options = { steps: numPoints, units: "degrees" };
@@ -153386,9 +153510,9 @@ function EmbeddingScatterplotSubscriber(props) {
153386
153510
  }
153387
153511
  return null;
153388
153512
  }, [sampleEdges]);
153389
- const stratifiedData = useMemo(() => {
153513
+ const [stratifiedData, stratifiedDataCount] = useMemo(() => {
153390
153514
  if (alignedEmbeddingData == null ? void 0 : alignedEmbeddingData.data) {
153391
- const result = stratifyArrays(
153515
+ const [result, cellCountResult] = stratifyArrays(
153392
153516
  sampleEdges,
153393
153517
  sampleIdToObsIdsMap,
153394
153518
  sampleSets,
@@ -153399,13 +153523,13 @@ function EmbeddingScatterplotSubscriber(props) {
153399
153523
  {
153400
153524
  obsEmbeddingX: alignedEmbeddingData.data[0],
153401
153525
  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
- }
153526
+ ...(uint8ExpressionData == null ? void 0 : uint8ExpressionData[0]) ? { featureValue: uint8ExpressionData } : {}
153527
+ },
153528
+ featureAggregationStrategyToUse
153405
153529
  );
153406
- return result;
153530
+ return [result, cellCountResult];
153407
153531
  }
153408
- return null;
153532
+ return [null, null];
153409
153533
  }, [
153410
153534
  alignedEmbeddingIndex,
153411
153535
  alignedEmbeddingData,
@@ -153415,7 +153539,8 @@ function EmbeddingScatterplotSubscriber(props) {
153415
153539
  sampleSets,
153416
153540
  sampleSetSelection,
153417
153541
  cellSetSelection,
153418
- mergedCellSets
153542
+ mergedCellSets,
153543
+ featureAggregationStrategyToUse
153419
153544
  ]);
153420
153545
  const setViewState = ({ zoom: newZoom, target }) => {
153421
153546
  setZoom(newZoom);
@@ -153423,11 +153548,12 @@ function EmbeddingScatterplotSubscriber(props) {
153423
153548
  setTargetY(target[1]);
153424
153549
  setTargetZ(target[2] || 0);
153425
153550
  };
153551
+ const cellCountToUse = embeddingPointsVisible ? cellsCount : stratifiedDataCount ?? cellsCount;
153426
153552
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
153427
153553
  TitleInfo,
153428
153554
  {
153429
153555
  title,
153430
- info: `${commaNumber(cellsCount)} ${pluralize(observationsLabel, cellsCount)}`,
153556
+ info: `${commaNumber(cellCountToUse)} ${pluralize(observationsLabel, cellCountToUse)}`,
153431
153557
  closeButtonVisible,
153432
153558
  downloadButtonVisible,
153433
153559
  removeGridComponent,
@@ -153471,7 +153597,9 @@ function EmbeddingScatterplotSubscriber(props) {
153471
153597
  setContourPercentiles,
153472
153598
  defaultContourPercentiles: DEFAULT_CONTOUR_PERCENTILES,
153473
153599
  contourColorEncoding,
153474
- setContourColorEncoding
153600
+ setContourColorEncoding,
153601
+ featureAggregationStrategy,
153602
+ setFeatureAggregationStrategy
153475
153603
  }
153476
153604
  ),
153477
153605
  children: [
@@ -153523,7 +153651,7 @@ function EmbeddingScatterplotSubscriber(props) {
153523
153651
  featureSelection: geneSelection
153524
153652
  }
153525
153653
  ),
153526
- tooltipsVisible && /* @__PURE__ */ jsxRuntimeExports.jsx(
153654
+ tooltipsVisible && width && height ? /* @__PURE__ */ jsxRuntimeExports.jsx(
153527
153655
  ScatterplotTooltipSubscriber,
153528
153656
  {
153529
153657
  parentUuid: uuid,
@@ -153534,7 +153662,7 @@ function EmbeddingScatterplotSubscriber(props) {
153534
153662
  featureType,
153535
153663
  featureLabelsMap
153536
153664
  }
153537
- ),
153665
+ ) : null,
153538
153666
  /* @__PURE__ */ jsxRuntimeExports.jsx(
153539
153667
  Legend,
153540
153668
  {
@@ -153548,13 +153676,14 @@ function EmbeddingScatterplotSubscriber(props) {
153548
153676
  featureValueColormap: geneExpressionColormap,
153549
153677
  featureValueColormapRange: geneExpressionColormapRange,
153550
153678
  obsSetSelection: cellSetSelection,
153551
- extent: expressionExtents == null ? void 0 : expressionExtents[0],
153552
- missing: expressionMissing == null ? void 0 : expressionMissing[0],
153679
+ extent: expressionExtents,
153680
+ missing: expressionMissing,
153553
153681
  pointsVisible: embeddingPointsVisible,
153554
153682
  contoursVisible: embeddingContoursVisible,
153555
153683
  contoursFilled: embeddingContoursFilled,
153556
153684
  contourPercentiles: contourPercentiles || DEFAULT_CONTOUR_PERCENTILES,
153557
- contourThresholds
153685
+ contourThresholds,
153686
+ featureAggregationStrategy: featureAggregationStrategyToUse
153558
153687
  }
153559
153688
  )
153560
153689
  ]
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { D, E } from "./index-a37f30c8.js";
1
+ import { D, E } from "./index-de0a204e.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-de0a204e.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-de0a204e.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-de0a204e.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-de0a204e.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-de0a204e.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-de0a204e.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.12",
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.12",
25
+ "@vitessce/legend": "3.5.12",
26
+ "@vitessce/scatterplot": "3.5.12",
27
+ "@vitessce/sets-utils": "3.5.12",
28
+ "@vitessce/utils": "3.5.12",
29
+ "@vitessce/vit-s": "3.5.12"
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
  );