@haniffalab/cherita-react 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/components/controls/Controls.js +38 -30
  3. package/dist/cjs/components/dotplot/Dotplot.js +85 -57
  4. package/dist/cjs/components/dotplot/DotplotControls.js +103 -83
  5. package/dist/cjs/components/full-page/FullPage.js +167 -114
  6. package/dist/cjs/components/full-page/PlotAlert.js +45 -0
  7. package/dist/cjs/components/full-page/PlotTypeSelector.js +102 -0
  8. package/dist/cjs/components/heatmap/Heatmap.js +83 -53
  9. package/dist/cjs/components/heatmap/HeatmapControls.js +6 -3
  10. package/dist/cjs/components/icons/DotPlotIcon.js +64 -0
  11. package/dist/cjs/components/icons/HeatmapIcon.js +45 -0
  12. package/dist/cjs/components/icons/MatrixPlotIcon.1.js +57 -0
  13. package/dist/cjs/components/icons/MatrixPlotIcon.js +59 -0
  14. package/dist/cjs/components/icons/ScatterplotIcon.1.js +164 -0
  15. package/dist/cjs/components/icons/ScatterplotIcon.js +144 -0
  16. package/dist/cjs/components/icons/ViolinPlotIcon.js +42 -0
  17. package/dist/cjs/components/matrixplot/Matrixplot.js +83 -54
  18. package/dist/cjs/components/matrixplot/MatrixplotControls.js +8 -5
  19. package/dist/cjs/components/obs-list/ObsItem.js +305 -216
  20. package/dist/cjs/components/obs-list/ObsList.js +164 -128
  21. package/dist/cjs/components/obs-list/ObsToolbar.js +2 -3
  22. package/dist/cjs/components/obsm-list/ObsmList.js +67 -28
  23. package/dist/cjs/components/offcanvas/index.js +62 -27
  24. package/dist/cjs/components/pseudospatial/Pseudospatial.js +132 -76
  25. package/dist/cjs/components/pseudospatial/PseudospatialToolbar.js +122 -74
  26. package/dist/cjs/components/scatterplot/Scatterplot.js +223 -175
  27. package/dist/cjs/components/scatterplot/ScatterplotControls.js +45 -31
  28. package/dist/cjs/components/scatterplot/SpatialControls.js +143 -116
  29. package/dist/cjs/components/scatterplot/Toolbox.js +41 -30
  30. package/dist/cjs/components/search-bar/SearchBar.js +176 -120
  31. package/dist/cjs/components/search-bar/SearchInfo.js +79 -85
  32. package/dist/cjs/components/search-bar/SearchResults.js +93 -71
  33. package/dist/cjs/components/toolbar/Toolbar.js +111 -0
  34. package/dist/cjs/components/var-list/VarItem.js +131 -103
  35. package/dist/cjs/components/var-list/VarList.js +96 -74
  36. package/dist/cjs/components/var-list/VarListToolbar.js +59 -54
  37. package/dist/cjs/components/var-list/VarSet.js +126 -108
  38. package/dist/cjs/components/violin/Violin.js +124 -81
  39. package/dist/cjs/components/violin/ViolinControls.js +8 -5
  40. package/dist/cjs/constants/colorscales.js +19 -19
  41. package/dist/cjs/constants/constants.js +54 -39
  42. package/dist/cjs/context/DatasetContext.js +27 -17
  43. package/dist/cjs/context/FilterContext.js +11 -9
  44. package/dist/cjs/context/SettingsContext.js +339 -125
  45. package/dist/cjs/context/ZarrDataContext.js +6 -5
  46. package/dist/cjs/helpers/color-helper.js +2 -2
  47. package/dist/cjs/helpers/map-helper.js +2 -1
  48. package/dist/cjs/helpers/zarr-helper.js +3 -3
  49. package/dist/cjs/index.js +15 -21
  50. package/dist/cjs/utils/Filter.js +16 -11
  51. package/dist/cjs/utils/Histogram.js +35 -33
  52. package/dist/cjs/utils/ImageViewer.js +11 -8
  53. package/dist/cjs/utils/Legend.js +37 -30
  54. package/dist/cjs/utils/LoadingIndicators.js +15 -13
  55. package/dist/cjs/utils/Resolver.js +213 -0
  56. package/dist/cjs/utils/Skeleton.js +10 -10
  57. package/dist/cjs/utils/StyledTooltip.js +44 -0
  58. package/dist/cjs/utils/VirtualizedList.js +36 -29
  59. package/dist/cjs/utils/errors.js +15 -15
  60. package/dist/cjs/utils/requests.js +21 -9
  61. package/dist/cjs/utils/search.js +4 -4
  62. package/dist/cjs/utils/string.js +6 -6
  63. package/dist/cjs/utils/zarrData.js +20 -21
  64. package/dist/css/cherita.css +188 -65
  65. package/dist/css/cherita.css.map +1 -1
  66. package/dist/esm/components/controls/Controls.js +43 -35
  67. package/dist/esm/components/dotplot/Dotplot.js +93 -64
  68. package/dist/esm/components/dotplot/DotplotControls.js +106 -85
  69. package/dist/esm/components/full-page/FullPage.js +180 -124
  70. package/dist/esm/components/full-page/PlotAlert.js +39 -0
  71. package/dist/esm/components/full-page/PlotTypeSelector.js +95 -0
  72. package/dist/esm/components/heatmap/Heatmap.js +91 -60
  73. package/dist/esm/components/heatmap/HeatmapControls.js +8 -4
  74. package/dist/esm/components/icons/DotPlotIcon.js +58 -0
  75. package/dist/esm/components/icons/HeatmapIcon.js +39 -0
  76. package/dist/esm/components/icons/MatrixPlotIcon.1.js +51 -0
  77. package/dist/esm/components/icons/MatrixPlotIcon.js +53 -0
  78. package/dist/esm/components/icons/ScatterplotIcon.1.js +158 -0
  79. package/dist/esm/components/icons/ScatterplotIcon.js +138 -0
  80. package/dist/esm/components/icons/ViolinPlotIcon.js +36 -0
  81. package/dist/esm/components/matrixplot/Matrixplot.js +91 -61
  82. package/dist/esm/components/matrixplot/MatrixplotControls.js +10 -6
  83. package/dist/esm/components/obs-list/ObsItem.js +320 -228
  84. package/dist/esm/components/obs-list/ObsList.js +179 -142
  85. package/dist/esm/components/obs-list/ObsToolbar.js +3 -3
  86. package/dist/esm/components/obsm-list/ObsmList.js +71 -32
  87. package/dist/esm/components/offcanvas/index.js +68 -33
  88. package/dist/esm/components/pseudospatial/Pseudospatial.js +145 -88
  89. package/dist/esm/components/pseudospatial/PseudospatialToolbar.js +127 -78
  90. package/dist/esm/components/scatterplot/Scatterplot.js +243 -194
  91. package/dist/esm/components/scatterplot/ScatterplotControls.js +50 -35
  92. package/dist/esm/components/scatterplot/SpatialControls.js +155 -127
  93. package/dist/esm/components/scatterplot/Toolbox.js +44 -32
  94. package/dist/esm/components/search-bar/SearchBar.js +187 -130
  95. package/dist/esm/components/search-bar/SearchInfo.js +86 -91
  96. package/dist/esm/components/search-bar/SearchResults.js +100 -77
  97. package/dist/esm/components/toolbar/Toolbar.js +101 -0
  98. package/dist/esm/components/var-list/VarItem.js +142 -113
  99. package/dist/esm/components/var-list/VarList.js +108 -88
  100. package/dist/esm/components/var-list/VarListToolbar.js +64 -58
  101. package/dist/esm/components/var-list/VarSet.js +134 -115
  102. package/dist/esm/components/violin/Violin.js +135 -91
  103. package/dist/esm/components/violin/ViolinControls.js +10 -6
  104. package/dist/esm/constants/colorscales.js +19 -19
  105. package/dist/esm/constants/constants.js +53 -38
  106. package/dist/esm/context/DatasetContext.js +34 -23
  107. package/dist/esm/context/FilterContext.js +11 -8
  108. package/dist/esm/context/SettingsContext.js +341 -126
  109. package/dist/esm/context/ZarrDataContext.js +8 -6
  110. package/dist/esm/helpers/color-helper.js +5 -5
  111. package/dist/esm/helpers/map-helper.js +4 -3
  112. package/dist/esm/helpers/zarr-helper.js +6 -6
  113. package/dist/esm/index.js +22 -22
  114. package/dist/esm/utils/Filter.js +22 -17
  115. package/dist/esm/utils/Histogram.js +39 -37
  116. package/dist/esm/utils/ImageViewer.js +12 -8
  117. package/dist/esm/utils/Legend.js +44 -36
  118. package/dist/esm/utils/LoadingIndicators.js +16 -13
  119. package/dist/esm/utils/Resolver.js +201 -0
  120. package/dist/esm/utils/Skeleton.js +11 -10
  121. package/dist/esm/utils/StyledTooltip.js +38 -0
  122. package/dist/esm/utils/VirtualizedList.js +37 -29
  123. package/dist/esm/utils/errors.js +15 -15
  124. package/dist/esm/utils/requests.js +24 -12
  125. package/dist/esm/utils/search.js +7 -7
  126. package/dist/esm/utils/string.js +7 -7
  127. package/dist/esm/utils/zarrData.js +27 -28
  128. package/package.json +21 -8
  129. package/scss/cherita-bootstrap.scss +2 -2
  130. package/scss/cherita.scss +65 -17
  131. package/scss/components/accordions.scss +15 -2
  132. package/scss/components/layouts.scss +116 -30
  133. package/scss/components/lists.scss +16 -5
  134. package/scss/components/plotly.scss +40 -23
  135. package/scss/components/plots.scss +14 -1
  136. package/dist/cjs/components/full-page/FullPagePseudospatial.js +0 -157
  137. package/dist/esm/components/full-page/FullPagePseudospatial.js +0 -149
@@ -3,27 +3,31 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
3
3
  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
4
4
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
5
5
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
6
- import React, { useCallback, useDeferredValue, useEffect, useMemo, useRef, useState } from "react";
7
- import { ScatterplotLayer } from "@deck.gl/layers";
8
- import { DeckGL } from "@deck.gl/react";
9
- import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons";
10
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
11
- import { ViewMode } from "@nebula.gl/edit-modes";
12
- import { EditableGeoJsonLayer } from "@nebula.gl/layers";
13
- import _ from "lodash";
14
- import { Alert } from "react-bootstrap";
15
- import { SpatialControls } from "./SpatialControls";
16
- import { Toolbox } from "./Toolbox";
17
- import { COLOR_ENCODINGS, OBS_TYPES, SELECTED_POLYGON_FILLCOLOR, UNSELECTED_POLYGON_FILLCOLOR } from "../../constants/constants";
18
- import { useFilteredData } from "../../context/FilterContext";
19
- import { useSettings, useSettingsDispatch } from "../../context/SettingsContext";
20
- import { useZarrData } from "../../context/ZarrDataContext";
21
- import { rgbToHex, useColor } from "../../helpers/color-helper";
22
- import { MapHelper } from "../../helpers/map-helper";
23
- import { Legend } from "../../utils/Legend";
24
- import { LoadingLinear, LoadingSpinner } from "../../utils/LoadingIndicators";
25
- import { formatNumerical } from "../../utils/string";
26
- import { useLabelObsData } from "../../utils/zarrData";
6
+ import { useCallback, useDeferredValue, useEffect, useMemo, useRef, useState } from 'react';
7
+ import { ScatterplotLayer } from '@deck.gl/layers';
8
+ import { DeckGL } from '@deck.gl/react';
9
+ import { faTriangleExclamation } from '@fortawesome/free-solid-svg-icons';
10
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
11
+ import { ViewMode } from '@nebula.gl/edit-modes';
12
+ import { EditableGeoJsonLayer } from '@nebula.gl/layers';
13
+ import _ from 'lodash';
14
+ import { Alert } from 'react-bootstrap';
15
+ import { SpatialControls } from './SpatialControls';
16
+ import { Toolbox } from './Toolbox';
17
+ import { COLOR_ENCODINGS, OBS_TYPES, SELECTED_POLYGON_FILLCOLOR, UNSELECTED_POLYGON_FILLCOLOR } from '../../constants/constants';
18
+ import { useDataset } from '../../context/DatasetContext';
19
+ import { useFilteredData } from '../../context/FilterContext';
20
+ import { useSettings, useSettingsDispatch } from '../../context/SettingsContext';
21
+ import { useZarrData } from '../../context/ZarrDataContext';
22
+ import { rgbToHex, useColor } from '../../helpers/color-helper';
23
+ import { MapHelper } from '../../helpers/map-helper';
24
+ import { Legend } from '../../utils/Legend';
25
+ import { LoadingLinear, LoadingSpinner } from '../../utils/LoadingIndicators';
26
+ import { useSelectedObs } from '../../utils/Resolver';
27
+ import { formatNumerical } from '../../utils/string';
28
+ import { useLabelObsData } from '../../utils/zarrData';
29
+ import { PlotAlert } from '../full-page/PlotAlert';
30
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
27
31
  window.deck.log.level = 1;
28
32
  const INITIAL_VIEW_STATE = {
29
33
  longitude: 0,
@@ -34,13 +38,18 @@ const INITIAL_VIEW_STATE = {
34
38
  bearing: 0
35
39
  };
36
40
  export function Scatterplot(_ref) {
37
- var _settings$selectedObs3, _features$features2, _obsmData$serverError, _xData$serverError, _obsData$serverError, _labelObsData$serverE, _settings$selectedVar, _settings$selectedObs4, _obsmData$data;
41
+ var _features$features2, _obsmData$serverError, _labelObsData$serverE, _settings$selectedVar, _data$positions;
38
42
  let {
39
- radius = 30,
43
+ radius = null,
40
44
  setShowObs,
41
45
  setShowVars,
46
+ plotType,
47
+ setPlotType,
42
48
  isFullscreen = false
43
49
  } = _ref;
50
+ const {
51
+ useUnsColors
52
+ } = useDataset();
44
53
  const settings = useSettings();
45
54
  const {
46
55
  obsIndices,
@@ -55,17 +64,21 @@ export function Scatterplot(_ref) {
55
64
  const deckRef = useRef(null);
56
65
  const [viewState, setViewState] = useState(INITIAL_VIEW_STATE);
57
66
  const [isRendering, setIsRendering] = useState(true);
67
+ const [radiusScale, setRadiusScale] = useState(radius || 1);
68
+ const [isPending, setIsPending] = useState(false);
58
69
  const [data, setData] = useState({
59
- ids: [],
60
70
  positions: [],
61
- values: [],
62
- sliceValues: []
71
+ values: []
63
72
  });
73
+ const [coordsError, setCoordsError] = useState(null);
74
+ const [hasObsm, setHasObsm] = useState(true);
75
+ const [dataError, setDataError] = useState(null);
76
+ const selectedObs = useSelectedObs();
64
77
 
65
78
  // EditableGeoJsonLayer
66
79
  const [mode, setMode] = useState(() => ViewMode);
67
80
  const [features, setFeatures] = useState({
68
- type: "FeatureCollection",
81
+ type: 'FeatureCollection',
69
82
  features: settings.polygons[settings.selectedObsm] || []
70
83
  });
71
84
  const [selectedFeatureIndexes, setSelectedFeatureIndexes] = useState([]);
@@ -77,24 +90,74 @@ export function Scatterplot(_ref) {
77
90
  const labelObsData = useLabelObsData();
78
91
  // @TODO: assert length of obsmData, xData, obsData is equal
79
92
 
93
+ const getRadiusScale = useCallback(bounds => {
94
+ if (!!radius) return radius;
95
+ // From 28 degrees ~= 30km -> 30m radius
96
+ const lonDim = bounds[1][0] - bounds[0][0];
97
+ const latDim = bounds[1][1] - bounds[0][1];
98
+ const minDim = Math.min(lonDim, latDim);
99
+ const rs = 0.01 / minDim * 111111;
100
+ return rs;
101
+ }, [radius]);
80
102
  useEffect(() => {
81
- if (!obsmData.isPending && !obsmData.serverError) {
82
- var _deckRef$current, _deckRef$current2;
83
- setIsRendering(true);
103
+ if (obsmData.isPending || settings.colorEncoding === COLOR_ENCODINGS.VAR && xData.isPending || settings.colorEncoding === COLOR_ENCODINGS.OBS && obsData.isPending) {
104
+ setIsPending(true);
105
+ } else {
106
+ setIsPending(false);
84
107
  setData(d => {
85
- return _objectSpread(_objectSpread({}, d), {}, {
86
- positions: obsmData.data
87
- });
108
+ let values = d.values;
109
+ if (settings.colorEncoding === COLOR_ENCODINGS.VAR) {
110
+ if (!xData.serverError) {
111
+ values = xData.data;
112
+ setDataError(null);
113
+ } else {
114
+ values = [];
115
+ setDataError(xData.serverError);
116
+ }
117
+ } else if (settings.colorEncoding === COLOR_ENCODINGS.OBS) {
118
+ if (!obsData.serverError) {
119
+ values = obsData.data;
120
+ setDataError(null);
121
+ } else {
122
+ values = [];
123
+ setDataError(obsData.serverError);
124
+ }
125
+ }
126
+ if (!obsmData.serverError && obsmData.data) {
127
+ if (obsmData.data[0].length !== 2) {
128
+ setCoordsError('Invalid coordinates. Expected 2D coordinates');
129
+ return {
130
+ positions: [],
131
+ values: []
132
+ };
133
+ }
134
+ setCoordsError(null);
135
+ return {
136
+ positions: obsmData.data,
137
+ values: values
138
+ };
139
+ }
140
+ return {
141
+ positions: d.positions,
142
+ values: values
143
+ };
88
144
  });
145
+ }
146
+ }, [obsData.data, obsData.isPending, obsData.serverError, obsmData.data, obsmData.isPending, obsmData.serverError, settings.colorEncoding, xData.data, xData.isPending, xData.serverError]);
147
+ useEffect(() => {
148
+ if (data.positions && !!data.positions.length) {
149
+ var _deckRef$current, _deckRef$current2;
89
150
  const mapHelper = new MapHelper();
90
151
  const {
91
152
  latitude,
92
153
  longitude,
93
- zoom
94
- } = mapHelper.fitBounds(obsmData.data, {
154
+ zoom,
155
+ bounds
156
+ } = mapHelper.fitBounds(data.positions, {
95
157
  width: deckRef === null || deckRef === void 0 || (_deckRef$current = deckRef.current) === null || _deckRef$current === void 0 || (_deckRef$current = _deckRef$current.deck) === null || _deckRef$current === void 0 ? void 0 : _deckRef$current.width,
96
158
  height: deckRef === null || deckRef === void 0 || (_deckRef$current2 = deckRef.current) === null || _deckRef$current2 === void 0 || (_deckRef$current2 = _deckRef$current2.deck) === null || _deckRef$current2 === void 0 ? void 0 : _deckRef$current2.height
97
159
  });
160
+ setRadiusScale(getRadiusScale(bounds));
98
161
  setViewState(v => {
99
162
  return _objectSpread(_objectSpread({}, v), {}, {
100
163
  longitude: longitude,
@@ -102,15 +165,8 @@ export function Scatterplot(_ref) {
102
165
  zoom: zoom
103
166
  });
104
167
  });
105
- } else if (!obsmData.isPending && obsmData.serverError) {
106
- setIsRendering(true);
107
- setData(d => {
108
- return _objectSpread(_objectSpread({}, d), {}, {
109
- positions: []
110
- });
111
- });
112
168
  }
113
- }, [settings.selectedObsm, obsmData.data, obsmData.isPending, obsmData.serverError]);
169
+ }, [getRadiusScale, obsmData.data, obsmData.isPending, obsmData.serverError, data.positions]);
114
170
  const getBounds = useCallback(() => {
115
171
  var _deckRef$current3, _deckRef$current4;
116
172
  const {
@@ -127,71 +183,45 @@ export function Scatterplot(_ref) {
127
183
  zoom
128
184
  };
129
185
  }, [data.positions]);
130
- useEffect(() => {
131
- if (settings.colorEncoding === COLOR_ENCODINGS.VAR) {
132
- setIsRendering(true);
133
- if (!xData.isPending && !xData.serverError) {
134
- // @TODO: add condition to check obs slicing
135
- setData(d => {
136
- return _objectSpread(_objectSpread({}, d), {}, {
137
- values: xData.data
138
- });
139
- });
140
- } else if (!xData.isPending && xData.serverError) {
141
- setData(d => {
142
- return _objectSpread(_objectSpread({}, d), {}, {
143
- values: []
144
- });
145
- });
146
- }
147
- }
148
- }, [settings.colorEncoding, xData.data, xData.isPending, xData.serverError, getColor]);
149
- useEffect(() => {
150
- if (settings.colorEncoding === COLOR_ENCODINGS.OBS) {
151
- setIsRendering(true);
152
- if (!obsData.isPending && !obsData.serverError) {
153
- setData(d => {
154
- return _objectSpread(_objectSpread({}, d), {}, {
155
- values: obsData.data
156
- });
157
- });
158
- } else if (!obsData.isPending && obsData.serverError) {
159
- setData(d => {
160
- return _objectSpread(_objectSpread({}, d), {}, {
161
- values: []
162
- });
163
- });
164
- }
165
- } else if (settings.colorEncoding === COLOR_ENCODINGS.VAR && settings.sliceBy.obs) {
166
- if (!obsData.isPending && !obsData.serverError) {
167
- setData(d => {
168
- return _objectSpread(_objectSpread({}, d), {}, {
169
- sliceValues: obsData.data
170
- });
171
- });
172
- } else if (!obsData.isPending && obsData.serverError) {
173
- setData(d => {
174
- return _objectSpread(_objectSpread({}, d), {}, {
175
- sliceValues: []
176
- });
177
- });
178
- }
186
+
187
+ // Make stable references for getOriginalIndex and sortedIndexMap
188
+ const identityGetOriginalIndex = useCallback(i => i, []);
189
+ const identitySortedIndexMap = useMemo(() => ({
190
+ get: key => key
191
+ }), []);
192
+ const {
193
+ sortedData,
194
+ getOriginalIndex,
195
+ sortedIndexMap
196
+ } = useMemo(() => {
197
+ if ((settings.colorEncoding === COLOR_ENCODINGS.VAR || settings.colorEncoding === COLOR_ENCODINGS.OBS && (selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.type) === OBS_TYPES.CONTINUOUS) && data.positions && data.values && data.positions.length === data.values.length) {
198
+ const sortedIndices = _.map(data.values, (_v, i) => i).sort((a, b) => data.values[a] - data.values[b]);
199
+ const sortedIndexMap = new Map(_.map(sortedIndices, (originalIndex, sortedIndex) => [originalIndex, sortedIndex]));
200
+ return {
201
+ sortedData: _.mapValues(data, (v, _k) => {
202
+ return v ? _.at(v, sortedIndices) : v;
203
+ }),
204
+ getOriginalIndex: i => sortedIndices[i],
205
+ sortedIndexMap: sortedIndexMap
206
+ };
179
207
  }
180
- }, [settings.colorEncoding, obsData.data, obsData.isPending, obsData.serverError, settings.sliceBy.obs]);
208
+ return {
209
+ sortedData: data,
210
+ getOriginalIndex: identityGetOriginalIndex,
211
+ // return original index
212
+ sortedIndexMap: identitySortedIndexMap // return original index
213
+ };
214
+ }, [data, identityGetOriginalIndex, identitySortedIndexMap, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.type, settings.colorEncoding]);
215
+ const sortedObsIndices = useMemo(() => {
216
+ return obsIndices ? new Set(Array.from(obsIndices, i => sortedIndexMap.get(i))) : obsIndices;
217
+ }, [obsIndices, sortedIndexMap]);
181
218
  const isCategorical = useMemo(() => {
182
219
  if (settings.colorEncoding === COLOR_ENCODINGS.OBS) {
183
- var _settings$selectedObs, _settings$selectedObs2;
184
- return ((_settings$selectedObs = settings.selectedObs) === null || _settings$selectedObs === void 0 ? void 0 : _settings$selectedObs.type) === OBS_TYPES.CATEGORICAL || ((_settings$selectedObs2 = settings.selectedObs) === null || _settings$selectedObs2 === void 0 ? void 0 : _settings$selectedObs2.type) === OBS_TYPES.BOOLEAN;
220
+ return (selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.type) === OBS_TYPES.CATEGORICAL || (selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.type) === OBS_TYPES.BOOLEAN;
185
221
  } else {
186
222
  return false;
187
223
  }
188
- }, [settings.colorEncoding, (_settings$selectedObs3 = settings.selectedObs) === null || _settings$selectedObs3 === void 0 ? void 0 : _settings$selectedObs3.type]);
189
- useEffect(() => {
190
- dispatch({
191
- type: "set.controls.valueRange",
192
- valueRange: [valueMin, valueMax]
193
- });
194
- }, [dispatch, valueMax, valueMin]);
224
+ }, [settings.colorEncoding, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.type]);
195
225
  const {
196
226
  min,
197
227
  max
@@ -203,28 +233,30 @@ export function Scatterplot(_ref) {
203
233
  let {
204
234
  index
205
235
  } = _ref2;
206
- const grayOut = obsIndices && !obsIndices.has(index);
207
- return getColor({
208
- value: (data.values[index] - min) / (max - min),
236
+ const grayOut = isPending || sortedObsIndices && !sortedObsIndices.has(index);
237
+ return getColor(_objectSpread({
238
+ value: (sortedData.values[index] - min) / (max - min),
209
239
  categorical: isCategorical,
210
240
  grayOut: grayOut
211
- }) || [0, 0, 0, 100];
212
- }, [data.values, obsIndices, getColor, isCategorical, max, min]);
241
+ }, useUnsColors && settings.colorEncoding === COLOR_ENCODINGS.OBS && selectedObs !== null && selectedObs !== void 0 && selectedObs.colors ? {
242
+ colorscale: selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.colors
243
+ } : {})) || [0, 0, 0, 100];
244
+ }, [isPending, sortedObsIndices, getColor, sortedData.values, min, max, isCategorical, useUnsColors, settings.colorEncoding, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.colors]);
213
245
 
214
246
  // @TODO: add support for pseudospatial hover to reflect in radius
215
247
  const getRadius = useCallback((_d, _ref3) => {
216
248
  let {
217
249
  index
218
250
  } = _ref3;
219
- const grayOut = obsIndices && !obsIndices.has(index);
251
+ const grayOut = sortedObsIndices && !sortedObsIndices.has(index);
220
252
  return grayOut ? 1 : 3;
221
- }, [obsIndices]);
253
+ }, [sortedObsIndices]);
222
254
  const memoizedLayers = useMemo(() => {
223
255
  return [new ScatterplotLayer({
224
- id: "cherita-layer-scatterplot",
256
+ id: 'cherita-layer-scatterplot',
225
257
  pickable: true,
226
- data: data.positions,
227
- radiusScale: radius,
258
+ data: sortedData.positions,
259
+ radiusScale: radiusScale,
228
260
  radiusMinPixels: 1,
229
261
  getPosition: d => d,
230
262
  getFillColor: getFillColor,
@@ -234,7 +266,7 @@ export function Scatterplot(_ref) {
234
266
  getRadius: getRadius
235
267
  }
236
268
  }), new EditableGeoJsonLayer({
237
- id: "cherita-layer-draw",
269
+ id: 'cherita-layer-draw',
238
270
  data: features,
239
271
  mode: mode,
240
272
  selectedFeatureIndexes,
@@ -246,7 +278,7 @@ export function Scatterplot(_ref) {
246
278
  } = _ref4;
247
279
  setFeatures(updatedData);
248
280
  let updatedSelectedFeatureIndexes = selectedFeatureIndexes;
249
- if (editType === "addFeature") {
281
+ if (editType === 'addFeature') {
250
282
  const {
251
283
  featureIndexes
252
284
  } = editContext;
@@ -267,20 +299,20 @@ export function Scatterplot(_ref) {
267
299
  }
268
300
  }
269
301
  })];
270
- }, [data.positions, features, getFillColor, getRadius, mode, radius, selectedFeatureIndexes]);
302
+ }, [sortedData.positions, features, getFillColor, getRadius, mode, radiusScale, selectedFeatureIndexes]);
271
303
  const layers = useDeferredValue(mode === ViewMode ? memoizedLayers.reverse() : memoizedLayers); // draw scatterplot on top of polygons when in ViewMode
272
304
 
273
305
  useEffect(() => {
274
306
  var _features$features;
275
307
  if (!(features !== null && features !== void 0 && (_features$features = features.features) !== null && _features$features !== void 0 && _features$features.length)) {
276
308
  dispatch({
277
- type: "disable.slice.polygons"
309
+ type: 'disable.slice.polygons'
278
310
  });
279
311
  }
280
312
  }, [dispatch, features === null || features === void 0 || (_features$features2 = features.features) === null || _features$features2 === void 0 ? void 0 : _features$features2.length]);
281
313
  useEffect(() => {
282
314
  dispatch({
283
- type: "set.polygons",
315
+ type: 'set.polygons',
284
316
  obsm: settings.selectedObsm,
285
317
  polygons: (features === null || features === void 0 ? void 0 : features.features) || []
286
318
  });
@@ -290,7 +322,7 @@ export function Scatterplot(_ref) {
290
322
  // don't change selection while editing
291
323
  return;
292
324
  }
293
- setSelectedFeatureIndexes(f => info.object ? info.layer.id === "cherita-layer-draw" ? [info.index] : f : []);
325
+ setSelectedFeatureIndexes(f => info.object ? info.layer.id === 'cherita-layer-draw' ? [info.index] : f : []);
294
326
  }
295
327
  const getLabel = function (o, v) {
296
328
  let isVar = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
@@ -305,97 +337,114 @@ export function Scatterplot(_ref) {
305
337
  object,
306
338
  index
307
339
  } = _ref5;
308
- if (!object || (object === null || object === void 0 ? void 0 : object.type) === "Feature") return;
340
+ if (!object || (object === null || object === void 0 ? void 0 : object.type) === 'Feature') return;
309
341
  const text = [];
310
- if (settings.colorEncoding === COLOR_ENCODINGS.OBS && settings.selectedObs && !_.some(settings.labelObs, {
311
- name: settings.selectedObs.name
312
- })) {
313
- var _obsData$data;
314
- text.push(getLabel(settings.selectedObs, (_obsData$data = obsData.data) === null || _obsData$data === void 0 ? void 0 : _obsData$data[index]));
342
+ if (settings.colorEncoding === COLOR_ENCODINGS.OBS && selectedObs && !_.includes(settings.labelObs, selectedObs.name)) {
343
+ var _data$values;
344
+ text.push(getLabel(selectedObs, (_data$values = data.values) === null || _data$values === void 0 ? void 0 : _data$values[getOriginalIndex(index)]));
315
345
  }
316
346
  if (settings.colorEncoding === COLOR_ENCODINGS.VAR && settings.selectedVar) {
317
- var _xData$data;
318
- text.push(getLabel(settings.selectedVar, (_xData$data = xData.data) === null || _xData$data === void 0 ? void 0 : _xData$data[index], true));
347
+ var _data$values2;
348
+ text.push(getLabel(settings.selectedVar, (_data$values2 = data.values) === null || _data$values2 === void 0 ? void 0 : _data$values2[getOriginalIndex(index)], true));
319
349
  }
320
350
  if (settings.labelObs.length) {
321
351
  text.push(..._.map(labelObsData.data, (v, k) => {
322
- const labelObs = _.find(settings.labelObs, o => o.name === k);
323
- return getLabel(labelObs, v[index]);
352
+ const labelObs = settings.data.obs[k];
353
+ return getLabel(labelObs, v[getOriginalIndex(index)]);
324
354
  }));
325
355
  }
326
356
  if (!text.length) return;
327
- const grayOut = obsIndices && !obsIndices.has(index);
357
+ const grayOut = sortedObsIndices && !sortedObsIndices.has(index);
328
358
  return {
329
- text: text.length ? _.compact(text).join("\n") : null,
330
- className: grayOut ? "tooltip-grayout" : "deck-tooltip",
359
+ text: text.length ? _.compact(text).join('\n') : null,
360
+ className: grayOut ? 'tooltip-grayout' : 'deck-tooltip',
331
361
  style: !grayOut ? {
332
- "border-left": "3px solid ".concat(rgbToHex(getFillColor(null, {
362
+ 'border-left': "3px solid ".concat(rgbToHex(getFillColor(null, {
333
363
  index
334
364
  })))
335
365
  } : {
336
- "border-left": "none"
366
+ 'border-left': 'none'
337
367
  }
338
368
  };
339
369
  };
340
- const isPending = (isRendering || xData.isPending || obsmData.isPending) && !obsmData.isPending;
341
- const error = settings.selectedObsm && ((_obsmData$serverError = obsmData.serverError) === null || _obsmData$serverError === void 0 ? void 0 : _obsmData$serverError.length) || settings.colorEncoding === COLOR_ENCODINGS.VAR && ((_xData$serverError = xData.serverError) === null || _xData$serverError === void 0 ? void 0 : _xData$serverError.length) || settings.colorEncoding === COLOR_ENCODINGS.OBS && ((_obsData$serverError = obsData.serverError) === null || _obsData$serverError === void 0 ? void 0 : _obsData$serverError.length) || settings.labelObs.lengh && ((_labelObsData$serverE = labelObsData.serverError) === null || _labelObsData$serverE === void 0 ? void 0 : _labelObsData$serverE.length);
342
- return /*#__PURE__*/React.createElement("div", {
343
- className: "cherita-container-scatterplot"
344
- }, /*#__PURE__*/React.createElement("div", {
345
- className: "cherita-scatterplot"
346
- }, obsmData.isPending && /*#__PURE__*/React.createElement(LoadingSpinner, {
347
- disableShrink: true
348
- }), isPending && /*#__PURE__*/React.createElement(LoadingLinear, null), /*#__PURE__*/React.createElement(DeckGL, {
349
- viewState: viewState,
350
- onViewStateChange: e => setViewState(e.viewState),
351
- controller: {
352
- doubleClickZoom: mode === ViewMode
353
- },
354
- layers: layers,
355
- onClick: onLayerClick,
356
- getTooltip: getTooltip,
357
- onAfterRender: () => {
358
- setIsRendering(false);
359
- },
360
- useDevicePixels: false,
361
- getCursor: _ref6 => {
362
- let {
363
- isDragging
364
- } = _ref6;
365
- return mode !== ViewMode ? "crosshair" : isDragging ? "grabbing" : "grab";
366
- },
367
- ref: deckRef
368
- }), /*#__PURE__*/React.createElement(SpatialControls, {
369
- mode: mode,
370
- setMode: setMode,
371
- features: features,
372
- setFeatures: setFeatures,
373
- selectedFeatureIndexes: selectedFeatureIndexes,
374
- resetBounds: () => setViewState(getBounds()),
375
- increaseZoom: () => setViewState(v => _objectSpread(_objectSpread({}, v), {}, {
376
- zoom: v.zoom + 1
377
- })),
378
- decreaseZoom: () => setViewState(v => _objectSpread(_objectSpread({}, v), {}, {
379
- zoom: v.zoom - 1
380
- })),
381
- setShowObs: setShowObs,
382
- setShowVars: setShowVars,
383
- isFullscreen: isFullscreen
384
- }), /*#__PURE__*/React.createElement("div", {
385
- className: "cherita-spatial-footer"
386
- }, /*#__PURE__*/React.createElement("div", {
387
- className: "cherita-toolbox-footer"
388
- }, error && !isPending && /*#__PURE__*/React.createElement(Alert, {
389
- variant: "danger"
390
- }, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
391
- icon: faTriangleExclamation
392
- }), "\xA0Error loading data"), /*#__PURE__*/React.createElement(Toolbox, {
393
- mode: settings.colorEncoding === COLOR_ENCODINGS.VAR ? (_settings$selectedVar = settings.selectedVar) === null || _settings$selectedVar === void 0 ? void 0 : _settings$selectedVar.name : settings.colorEncoding === COLOR_ENCODINGS.OBS ? (_settings$selectedObs4 = settings.selectedObs) === null || _settings$selectedObs4 === void 0 ? void 0 : _settings$selectedObs4.name : null,
394
- obsLength: parseInt((_obsmData$data = obsmData.data) === null || _obsmData$data === void 0 ? void 0 : _obsmData$data.length),
395
- slicedLength: parseInt(slicedLength)
396
- })), /*#__PURE__*/React.createElement(Legend, {
397
- isCategorical: isCategorical,
398
- min: min,
399
- max: max
400
- }))));
370
+ const error = settings.selectedObsm && ((_obsmData$serverError = obsmData.serverError) === null || _obsmData$serverError === void 0 ? void 0 : _obsmData$serverError.length) || dataError || settings.labelObs.length && ((_labelObsData$serverE = labelObsData.serverError) === null || _labelObsData$serverE === void 0 ? void 0 : _labelObsData$serverE.length) || coordsError;
371
+ if (!hasObsm) {
372
+ return /*#__PURE__*/_jsx(PlotAlert, {
373
+ variant: "info",
374
+ heading: "Scatterplot unavailable for this dataset",
375
+ plotType: plotType,
376
+ setPlotType: setPlotType,
377
+ children: "This dataset does not include any embeddings, so a scatterplot cannot be displayed. Please choose a different plot type to explore the data."
378
+ });
379
+ }
380
+ return /*#__PURE__*/_jsx("div", {
381
+ className: "cherita-container-scatterplot",
382
+ children: /*#__PURE__*/_jsxs("div", {
383
+ className: "cherita-scatterplot",
384
+ children: [obsmData.isPending && /*#__PURE__*/_jsx(LoadingSpinner, {
385
+ disableShrink: true
386
+ }), isPending && /*#__PURE__*/_jsx(LoadingLinear, {}), /*#__PURE__*/_jsx(DeckGL, {
387
+ viewState: viewState,
388
+ onViewStateChange: e => setViewState(e.viewState),
389
+ controller: {
390
+ doubleClickZoom: mode === ViewMode
391
+ },
392
+ layers: layers,
393
+ onClick: onLayerClick,
394
+ getTooltip: getTooltip,
395
+ onAfterRender: () => {
396
+ setIsRendering(false);
397
+ },
398
+ useDevicePixels: false,
399
+ getCursor: _ref6 => {
400
+ let {
401
+ isDragging
402
+ } = _ref6;
403
+ return mode !== ViewMode ? 'crosshair' : isDragging ? 'grabbing' : 'grab';
404
+ },
405
+ ref: deckRef
406
+ }), /*#__PURE__*/_jsx(SpatialControls, {
407
+ mode: mode,
408
+ setMode: setMode,
409
+ features: features,
410
+ setFeatures: setFeatures,
411
+ selectedFeatureIndexes: selectedFeatureIndexes,
412
+ resetBounds: () => setViewState(getBounds()),
413
+ increaseZoom: () => setViewState(v => _objectSpread(_objectSpread({}, v), {}, {
414
+ zoom: v.zoom + 1
415
+ })),
416
+ decreaseZoom: () => setViewState(v => _objectSpread(_objectSpread({}, v), {}, {
417
+ zoom: v.zoom - 1
418
+ })),
419
+ setShowObs: setShowObs,
420
+ setShowVars: setShowVars,
421
+ isFullscreen: isFullscreen
422
+ }), /*#__PURE__*/_jsxs("div", {
423
+ className: "cherita-spatial-footer",
424
+ children: [/*#__PURE__*/_jsxs("div", {
425
+ className: "cherita-toolbox-footer",
426
+ children: [!!error && !isRendering && /*#__PURE__*/_jsxs(Alert, {
427
+ variant: "danger",
428
+ children: [/*#__PURE__*/_jsxs(Alert.Heading, {
429
+ children: [/*#__PURE__*/_jsx(FontAwesomeIcon, {
430
+ icon: faTriangleExclamation
431
+ }), "\xA0Error loading data"]
432
+ }), /*#__PURE__*/_jsx("p", {
433
+ className: "mb-0",
434
+ children: error.message
435
+ })]
436
+ }), /*#__PURE__*/_jsx(Toolbox, {
437
+ mode: settings.colorEncoding === COLOR_ENCODINGS.VAR ? (_settings$selectedVar = settings.selectedVar) === null || _settings$selectedVar === void 0 ? void 0 : _settings$selectedVar.name : settings.colorEncoding === COLOR_ENCODINGS.OBS ? selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.name : null,
438
+ obsLength: parseInt((_data$positions = data.positions) === null || _data$positions === void 0 ? void 0 : _data$positions.length),
439
+ slicedLength: parseInt(slicedLength),
440
+ setHasObsm: setHasObsm
441
+ })]
442
+ }), !error && /*#__PURE__*/_jsx(Legend, {
443
+ isCategorical: isCategorical,
444
+ min: min,
445
+ max: max
446
+ })]
447
+ })]
448
+ })
449
+ });
401
450
  }