@vitessce/gl 3.1.3 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{deflate-c9746f58.js → deflate-69c280fb.js} +1 -1
- package/dist/{index-72755d2f.js → index-2a7f1ae0.js} +21935 -5890
- package/dist/index.js +14 -13
- package/dist/{jpeg-cb74431a.js → jpeg-95f78026.js} +1 -1
- package/dist/{lerc-5dffa929.js → lerc-5320d7fa.js} +1 -1
- package/dist/{lzw-85fa08a9.js → lzw-8d3691e0.js} +1 -1
- package/dist/{packbits-6b8314bf.js → packbits-2846ed6b.js} +1 -1
- package/dist/{raw-f12c17bf.js → raw-b0ec4406.js} +1 -1
- package/dist/{webimage-99c1b585.js → webimage-a17e81f9.js} +1 -1
- package/dist-tsc/BitmaskLayerBeta.d.ts +152 -0
- package/dist-tsc/BitmaskLayerBeta.d.ts.map +1 -0
- package/dist-tsc/BitmaskLayerBeta.js +253 -0
- package/dist-tsc/SelectionLayer.d.ts +1 -0
- package/dist-tsc/SelectionLayer.d.ts.map +1 -1
- package/dist-tsc/SelectionLayer.js +45 -31
- package/dist-tsc/bitmask-layer-beta-shaders.d.ts +3 -0
- package/dist-tsc/bitmask-layer-beta-shaders.d.ts.map +1 -0
- package/dist-tsc/bitmask-layer-beta-shaders.js +212 -0
- package/dist-tsc/bitmask-utils.d.ts +12 -0
- package/dist-tsc/bitmask-utils.d.ts.map +1 -0
- package/dist-tsc/bitmask-utils.js +89 -0
- package/dist-tsc/bitmask-utils.test.d.ts +2 -0
- package/dist-tsc/bitmask-utils.test.d.ts.map +1 -0
- package/dist-tsc/bitmask-utils.test.js +72 -0
- package/dist-tsc/index.d.ts +2 -1
- package/dist-tsc/index.js +2 -1
- package/dist-tsc/selection-utils.d.ts +3 -3
- package/dist-tsc/selection-utils.d.ts.map +1 -1
- package/dist-tsc/selection-utils.js +26 -28
- package/dist-tsc/viv.d.ts +1 -1
- package/dist-tsc/viv.js +1 -1
- package/package.json +3 -2
- package/src/BitmaskLayerBeta.js +360 -0
- package/src/SelectionLayer.js +60 -42
- package/src/bitmask-layer-beta-shaders.js +214 -0
- package/src/bitmask-utils.js +99 -0
- package/src/bitmask-utils.test.js +91 -0
- package/src/index.js +3 -1
- package/src/selection-utils.js +13 -17
- package/src/viv.js +1 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
import GL from '@luma.gl/constants'; // eslint-disable-line import/no-extraneous-dependencies
|
|
3
|
+
import { project32, picking } from '@deck.gl/core'; // eslint-disable-line import/no-extraneous-dependencies
|
|
4
|
+
import { Texture2D, isWebGL2 } from '@luma.gl/core';
|
|
5
|
+
import { XRLayer } from '@hms-dbmi/viv';
|
|
6
|
+
import { fromEntries } from '@vitessce/utils';
|
|
7
|
+
import { range } from 'lodash-es';
|
|
8
|
+
import { fs, vs } from './bitmask-layer-beta-shaders.js';
|
|
9
|
+
import {
|
|
10
|
+
GLSL_COLORMAPS,
|
|
11
|
+
GLSL_COLORMAP_DEFAULT,
|
|
12
|
+
COLORMAP_SHADER_PLACEHOLDER,
|
|
13
|
+
} from './constants.js';
|
|
14
|
+
import { multiSetsToTextureData } from './bitmask-utils.js';
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
const MAX_CHANNELS = 7;
|
|
18
|
+
const MULTI_FEATURE_TEX_SIZE = 2048;
|
|
19
|
+
|
|
20
|
+
function padWithDefault(arr, defaultValue, padWidth) {
|
|
21
|
+
const newArr = [...arr];
|
|
22
|
+
for (let i = 0; i < padWidth; i += 1) {
|
|
23
|
+
newArr.push(defaultValue);
|
|
24
|
+
}
|
|
25
|
+
return newArr;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getColor(arr) {
|
|
29
|
+
return arr ? arr.map(v => v / 255) : [0, 0, 0];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
const defaultProps = {
|
|
34
|
+
channelStrokeWidths: { type: 'array', value: null, compare: true },
|
|
35
|
+
channelsFilled: { type: 'array', value: null, compare: true },
|
|
36
|
+
channelOpacities: { type: 'array', value: null, compare: true },
|
|
37
|
+
channelColors: { type: 'array', value: null, compare: true },
|
|
38
|
+
hoveredCell: { type: 'number', value: null, compare: true },
|
|
39
|
+
colormap: { type: 'string', value: GLSL_COLORMAP_DEFAULT, compare: true },
|
|
40
|
+
expressionData: { type: 'object', value: null, compare: true },
|
|
41
|
+
multiFeatureValues: { type: 'array', value: null, compare: true },
|
|
42
|
+
setColorValues: { type: 'array', value: null, compare: true },
|
|
43
|
+
channelFeatureValueColormaps: { type: 'array', value: null, compare: true },
|
|
44
|
+
channelFeatureValueColormapRanges: { type: 'array', value: null, compare: true },
|
|
45
|
+
channelIsStaticColorMode: { type: 'array', value: null, compare: true },
|
|
46
|
+
channelIsSetColorMode: { type: 'array', value: null, compare: true },
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A BitmapLayer that performs aggregation in the fragment shader,
|
|
51
|
+
* and renders its texture from a Uint8Array rather than an ImageData.
|
|
52
|
+
*/
|
|
53
|
+
export default class BitmaskLayer extends XRLayer {
|
|
54
|
+
// eslint-disable-next-line class-methods-use-this
|
|
55
|
+
getShaders() {
|
|
56
|
+
const { colormap } = this.props;
|
|
57
|
+
return {
|
|
58
|
+
fs,
|
|
59
|
+
vs,
|
|
60
|
+
modules: [project32, picking],
|
|
61
|
+
defines: {
|
|
62
|
+
[COLORMAP_SHADER_PLACEHOLDER]: GLSL_COLORMAPS.includes(colormap)
|
|
63
|
+
? colormap
|
|
64
|
+
: GLSL_COLORMAP_DEFAULT,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Override the parent loadChannelTextures to enable
|
|
71
|
+
* up to eight channels (rather than six).
|
|
72
|
+
* Reference: https://github.com/hms-dbmi/viv/blob/v0.13.3/packages/layers/src/xr-layer/xr-layer.js#L316
|
|
73
|
+
*/
|
|
74
|
+
loadChannelTextures(channelData) {
|
|
75
|
+
const textures = {
|
|
76
|
+
channel0: null,
|
|
77
|
+
channel1: null,
|
|
78
|
+
channel2: null,
|
|
79
|
+
channel3: null,
|
|
80
|
+
channel4: null,
|
|
81
|
+
channel5: null,
|
|
82
|
+
channel6: null,
|
|
83
|
+
};
|
|
84
|
+
if (this.state.textures) {
|
|
85
|
+
Object.values(this.state.textures).forEach(tex => tex && tex.delete());
|
|
86
|
+
}
|
|
87
|
+
if (channelData
|
|
88
|
+
&& Object.keys(channelData).length > 0
|
|
89
|
+
&& channelData.data
|
|
90
|
+
) {
|
|
91
|
+
channelData.data.forEach((d, i) => {
|
|
92
|
+
textures[`channel${i}`] = this.dataToTexture(
|
|
93
|
+
d,
|
|
94
|
+
channelData.width,
|
|
95
|
+
channelData.height,
|
|
96
|
+
);
|
|
97
|
+
}, this);
|
|
98
|
+
this.setState({ textures });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
updateState({ props, oldProps, changeFlags }) {
|
|
103
|
+
super.updateState({ props, oldProps, changeFlags });
|
|
104
|
+
if (
|
|
105
|
+
props.multiFeatureValues !== oldProps.multiFeatureValues
|
|
106
|
+
|| props.setColorValues !== oldProps.setColorValues
|
|
107
|
+
|| props.channelIsSetColorMode !== oldProps.channelIsSetColorMode
|
|
108
|
+
) {
|
|
109
|
+
const { multiFeatureValues, setColorValues, channelIsSetColorMode } = this.props;
|
|
110
|
+
// Use one expressionTex for all channels,
|
|
111
|
+
// using an offset mechanism.
|
|
112
|
+
const [
|
|
113
|
+
valueTex,
|
|
114
|
+
colorTex,
|
|
115
|
+
valueTexOffsets,
|
|
116
|
+
colorTexOffsets,
|
|
117
|
+
valueTexHeight,
|
|
118
|
+
colorTexHeight,
|
|
119
|
+
] = this.multiSetsToTexture(
|
|
120
|
+
multiFeatureValues,
|
|
121
|
+
setColorValues,
|
|
122
|
+
channelIsSetColorMode,
|
|
123
|
+
);
|
|
124
|
+
this.setState({
|
|
125
|
+
valueTex,
|
|
126
|
+
colorTex,
|
|
127
|
+
valueTexOffsets,
|
|
128
|
+
colorTexOffsets,
|
|
129
|
+
valueTexHeight,
|
|
130
|
+
colorTexHeight,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
if (props.colormap !== oldProps.colormap) {
|
|
134
|
+
const { gl } = this.context;
|
|
135
|
+
if (this.state.model) {
|
|
136
|
+
this.state.model.delete();
|
|
137
|
+
}
|
|
138
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
139
|
+
this.setState({ model: this._getModel(gl) });
|
|
140
|
+
|
|
141
|
+
this.getAttributeManager().invalidateAll();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
draw(opts) {
|
|
146
|
+
const { uniforms } = opts;
|
|
147
|
+
const {
|
|
148
|
+
channelStrokeWidths,
|
|
149
|
+
channelsFilled,
|
|
150
|
+
channelOpacities,
|
|
151
|
+
channelColors,
|
|
152
|
+
channelsVisible,
|
|
153
|
+
// TODO: use `channelFeatureValueColormaps` in shader,
|
|
154
|
+
// figure out how to call multiple GLSL colormap functions
|
|
155
|
+
channelFeatureValueColormaps,
|
|
156
|
+
channelFeatureValueColormapRanges,
|
|
157
|
+
channelIsStaticColorMode,
|
|
158
|
+
channelIsSetColorMode,
|
|
159
|
+
hoveredCell,
|
|
160
|
+
colorScaleLo,
|
|
161
|
+
colorScaleHi,
|
|
162
|
+
isExpressionMode,
|
|
163
|
+
zoom,
|
|
164
|
+
minZoom,
|
|
165
|
+
maxZoom,
|
|
166
|
+
zoomOffset, // TODO: figure out if this needs to be used or not
|
|
167
|
+
} = this.props;
|
|
168
|
+
const {
|
|
169
|
+
textures,
|
|
170
|
+
model,
|
|
171
|
+
// Expression and set index (and colors) textures with offsets
|
|
172
|
+
valueTex,
|
|
173
|
+
colorTex,
|
|
174
|
+
valueTexOffsets,
|
|
175
|
+
colorTexOffsets,
|
|
176
|
+
valueTexHeight,
|
|
177
|
+
colorTexHeight,
|
|
178
|
+
} = this.state;
|
|
179
|
+
// Render the image
|
|
180
|
+
if (textures && model) {
|
|
181
|
+
const scaleFactor = 1 / (2 ** (maxZoom - zoom));
|
|
182
|
+
const colors = fromEntries(range(MAX_CHANNELS).map(i => ([`color${i}`, getColor(channelColors[i])])));
|
|
183
|
+
model
|
|
184
|
+
.setUniforms(
|
|
185
|
+
Object.assign({}, uniforms, {
|
|
186
|
+
...colors,
|
|
187
|
+
// Bitmask image channel data textures
|
|
188
|
+
...textures,
|
|
189
|
+
multiFeatureTexSize: MULTI_FEATURE_TEX_SIZE,
|
|
190
|
+
// Expression textures with offsets
|
|
191
|
+
valueTex,
|
|
192
|
+
valueTexOffsets: padWithDefault(
|
|
193
|
+
valueTexOffsets,
|
|
194
|
+
0,
|
|
195
|
+
MAX_CHANNELS - valueTexOffsets.length,
|
|
196
|
+
),
|
|
197
|
+
valueTexHeight,
|
|
198
|
+
// Set indices and colors textures with offsets
|
|
199
|
+
colorTex,
|
|
200
|
+
colorTexOffsets: padWithDefault(
|
|
201
|
+
colorTexOffsets,
|
|
202
|
+
0,
|
|
203
|
+
MAX_CHANNELS - colorTexOffsets.length,
|
|
204
|
+
),
|
|
205
|
+
colorTexHeight,
|
|
206
|
+
// Visualization properties
|
|
207
|
+
channelsFilled: padWithDefault(
|
|
208
|
+
channelsFilled,
|
|
209
|
+
true,
|
|
210
|
+
// There are six texture entries on the shaders
|
|
211
|
+
MAX_CHANNELS - channelsFilled.length,
|
|
212
|
+
),
|
|
213
|
+
channelOpacities: padWithDefault(
|
|
214
|
+
channelOpacities,
|
|
215
|
+
0.0,
|
|
216
|
+
// There are six texture entries on the shaders
|
|
217
|
+
MAX_CHANNELS - channelOpacities.length,
|
|
218
|
+
),
|
|
219
|
+
channelStrokeWidths: padWithDefault(
|
|
220
|
+
channelStrokeWidths,
|
|
221
|
+
1.0,
|
|
222
|
+
// There are six texture entries on the shaders
|
|
223
|
+
MAX_CHANNELS - channelStrokeWidths.length,
|
|
224
|
+
),
|
|
225
|
+
channelColormapRangeStarts: padWithDefault(
|
|
226
|
+
channelFeatureValueColormapRanges.map(r => r?.[0] || 0.0),
|
|
227
|
+
0.0,
|
|
228
|
+
// There are six texture entries on the shaders
|
|
229
|
+
MAX_CHANNELS - channelFeatureValueColormapRanges.length,
|
|
230
|
+
),
|
|
231
|
+
channelColormapRangeEnds: padWithDefault(
|
|
232
|
+
channelFeatureValueColormapRanges.map(r => r?.[1] || 1.0),
|
|
233
|
+
1.0,
|
|
234
|
+
// There are six texture entries on the shaders
|
|
235
|
+
MAX_CHANNELS - channelFeatureValueColormapRanges.length,
|
|
236
|
+
),
|
|
237
|
+
channelIsStaticColorMode: padWithDefault(
|
|
238
|
+
channelIsStaticColorMode,
|
|
239
|
+
true,
|
|
240
|
+
// There are six texture entries on the shaders
|
|
241
|
+
MAX_CHANNELS - channelIsStaticColorMode.length,
|
|
242
|
+
),
|
|
243
|
+
channelIsSetColorMode: padWithDefault(
|
|
244
|
+
channelIsSetColorMode,
|
|
245
|
+
false,
|
|
246
|
+
// There are six texture entries on the shaders
|
|
247
|
+
MAX_CHANNELS - channelIsSetColorMode.length,
|
|
248
|
+
),
|
|
249
|
+
hovered: hoveredCell || 0,
|
|
250
|
+
channelsVisible: padWithDefault(
|
|
251
|
+
channelsVisible,
|
|
252
|
+
false,
|
|
253
|
+
// There are six texture entries on the shaders
|
|
254
|
+
MAX_CHANNELS - channelsVisible.length,
|
|
255
|
+
),
|
|
256
|
+
// uColorScaleRange: [colorScaleLo, colorScaleHi],
|
|
257
|
+
// uIsExpressionMode: isExpressionMode,
|
|
258
|
+
// uIsOutlined: false,
|
|
259
|
+
scaleFactor,
|
|
260
|
+
}),
|
|
261
|
+
)
|
|
262
|
+
.draw();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* This function creates textures from the data
|
|
268
|
+
*/
|
|
269
|
+
dataToTexture(data, width, height) {
|
|
270
|
+
const isWebGL2On = isWebGL2(this.context.gl);
|
|
271
|
+
return new Texture2D(this.context.gl, {
|
|
272
|
+
width,
|
|
273
|
+
height,
|
|
274
|
+
// Only use Float32 so we don't have to write two shaders
|
|
275
|
+
data: new Float32Array(data),
|
|
276
|
+
// we don't want or need mimaps
|
|
277
|
+
mipmaps: false,
|
|
278
|
+
parameters: {
|
|
279
|
+
// NEAREST for integer data
|
|
280
|
+
[GL.TEXTURE_MIN_FILTER]: GL.NEAREST,
|
|
281
|
+
[GL.TEXTURE_MAG_FILTER]: GL.NEAREST,
|
|
282
|
+
// CLAMP_TO_EDGE to remove tile artifacts
|
|
283
|
+
[GL.TEXTURE_WRAP_S]: GL.CLAMP_TO_EDGE,
|
|
284
|
+
[GL.TEXTURE_WRAP_T]: GL.CLAMP_TO_EDGE,
|
|
285
|
+
},
|
|
286
|
+
format: isWebGL2On ? GL.R32F : GL.LUMINANCE,
|
|
287
|
+
dataFormat: isWebGL2On ? GL.RED : GL.LUMINANCE,
|
|
288
|
+
type: GL.FLOAT,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
multiSetsToTexture(multiFeatureValues, setColorValues, channelIsSetColorMode) {
|
|
293
|
+
const isWebGL2On = isWebGL2(this.context.gl);
|
|
294
|
+
|
|
295
|
+
const [
|
|
296
|
+
totalData,
|
|
297
|
+
valueTexHeight,
|
|
298
|
+
indicesOffsets,
|
|
299
|
+
totalColors,
|
|
300
|
+
colorTexHeight,
|
|
301
|
+
colorsOffsets,
|
|
302
|
+
] = multiSetsToTextureData(
|
|
303
|
+
multiFeatureValues,
|
|
304
|
+
setColorValues,
|
|
305
|
+
channelIsSetColorMode,
|
|
306
|
+
MULTI_FEATURE_TEX_SIZE,
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
return [
|
|
310
|
+
// Color indices texture
|
|
311
|
+
new Texture2D(this.context.gl, {
|
|
312
|
+
width: MULTI_FEATURE_TEX_SIZE,
|
|
313
|
+
height: valueTexHeight,
|
|
314
|
+
// Only use Float32 so we don't have to write two shaders
|
|
315
|
+
data: new Float32Array(totalData),
|
|
316
|
+
// we don't want or need mimaps
|
|
317
|
+
mipmaps: false,
|
|
318
|
+
parameters: {
|
|
319
|
+
// NEAREST for integer data
|
|
320
|
+
[GL.TEXTURE_MIN_FILTER]: GL.NEAREST,
|
|
321
|
+
[GL.TEXTURE_MAG_FILTER]: GL.NEAREST,
|
|
322
|
+
// CLAMP_TO_EDGE to remove tile artifacts
|
|
323
|
+
[GL.TEXTURE_WRAP_S]: GL.CLAMP_TO_EDGE,
|
|
324
|
+
[GL.TEXTURE_WRAP_T]: GL.CLAMP_TO_EDGE,
|
|
325
|
+
},
|
|
326
|
+
format: isWebGL2On ? GL.R32F : GL.LUMINANCE,
|
|
327
|
+
dataFormat: isWebGL2On ? GL.RED : GL.LUMINANCE,
|
|
328
|
+
type: GL.FLOAT,
|
|
329
|
+
}),
|
|
330
|
+
// Colors texture
|
|
331
|
+
new Texture2D(this.context.gl, {
|
|
332
|
+
width: MULTI_FEATURE_TEX_SIZE,
|
|
333
|
+
height: colorTexHeight,
|
|
334
|
+
// Only use Float32 so we don't have to write two shaders
|
|
335
|
+
data: new Float32Array(totalColors),
|
|
336
|
+
// we don't want or need mimaps
|
|
337
|
+
mipmaps: false,
|
|
338
|
+
parameters: {
|
|
339
|
+
// NEAREST for integer data
|
|
340
|
+
[GL.TEXTURE_MIN_FILTER]: GL.NEAREST,
|
|
341
|
+
[GL.TEXTURE_MAG_FILTER]: GL.NEAREST,
|
|
342
|
+
// CLAMP_TO_EDGE to remove tile artifacts
|
|
343
|
+
[GL.TEXTURE_WRAP_S]: GL.CLAMP_TO_EDGE,
|
|
344
|
+
[GL.TEXTURE_WRAP_T]: GL.CLAMP_TO_EDGE,
|
|
345
|
+
},
|
|
346
|
+
format: isWebGL2On ? GL.R32F : GL.LUMINANCE,
|
|
347
|
+
dataFormat: isWebGL2On ? GL.RED : GL.LUMINANCE,
|
|
348
|
+
type: GL.FLOAT,
|
|
349
|
+
}),
|
|
350
|
+
// Offsets
|
|
351
|
+
indicesOffsets,
|
|
352
|
+
colorsOffsets,
|
|
353
|
+
// Texture heights
|
|
354
|
+
valueTexHeight,
|
|
355
|
+
colorTexHeight,
|
|
356
|
+
];
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
BitmaskLayer.layerName = 'BitmaskLayer';
|
|
360
|
+
BitmaskLayer.defaultProps = defaultProps;
|
package/src/SelectionLayer.js
CHANGED
|
@@ -70,10 +70,8 @@ const PASS_THROUGH_PROPS = [
|
|
|
70
70
|
export default class SelectionLayer extends CompositeLayer {
|
|
71
71
|
_selectPolygonObjects(coordinates) {
|
|
72
72
|
const {
|
|
73
|
-
onSelect,
|
|
74
|
-
getCellCoords,
|
|
75
|
-
cellsQuadTree,
|
|
76
73
|
flipY,
|
|
74
|
+
obsLayers,
|
|
77
75
|
} = this.props;
|
|
78
76
|
|
|
79
77
|
const flippedCoordinates = (flipY
|
|
@@ -83,53 +81,73 @@ export default class SelectionLayer extends CompositeLayer {
|
|
|
83
81
|
// Convert the selection to a turf polygon object.
|
|
84
82
|
const selectedPolygon = turfPolygon(flippedCoordinates);
|
|
85
83
|
|
|
86
|
-
// Create an array to store the results.
|
|
87
|
-
const pickingInfos = [];
|
|
88
|
-
|
|
89
84
|
// quadtree.visit() takes a callback that returns a boolean:
|
|
90
85
|
// If true returned, then the children of the node are _not_ visited.
|
|
91
86
|
// If false returned, then the children of the node are visited.
|
|
92
87
|
// Reference: https://github.com/d3/d3-quadtree#quadtree_visit
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
88
|
+
obsLayers.forEach((obsLayer) => {
|
|
89
|
+
const {
|
|
90
|
+
getObsCoords,
|
|
91
|
+
obsQuadTree,
|
|
92
|
+
obsIndex,
|
|
93
|
+
onSelect: layerOnSelect,
|
|
94
|
+
} = obsLayer;
|
|
95
|
+
|
|
96
|
+
// Create an array to store the results.
|
|
97
|
+
// Clear the array before checking each new layer.
|
|
98
|
+
const pickingInfos = [];
|
|
99
|
+
|
|
100
|
+
// It is possible for a layer to not have an obsQuadTree,
|
|
101
|
+
// for example if the layer is a segmentation bitmask without associated
|
|
102
|
+
// obsLocations.
|
|
103
|
+
obsQuadTree?.visit((node, x0, y0, x1, y1) => {
|
|
104
|
+
const nodePoints = [[[x0, y0], [x1, y0], [x1, y1], [x0, y1], [x0, y0]]];
|
|
105
|
+
const nodePolygon = turfPolygon(nodePoints);
|
|
106
|
+
|
|
107
|
+
const nodePolygonContainsSelectedPolygon = booleanContains(nodePolygon, selectedPolygon);
|
|
108
|
+
const nodePolygonWithinSelectedPolygon = booleanWithin(nodePolygon, selectedPolygon);
|
|
109
|
+
const nodePolygonOverlapsSelectedPolgyon = booleanOverlap(nodePolygon, selectedPolygon);
|
|
110
|
+
|
|
111
|
+
if (!nodePolygonContainsSelectedPolygon
|
|
112
|
+
&& !nodePolygonWithinSelectedPolygon
|
|
113
|
+
&& !nodePolygonOverlapsSelectedPolgyon) {
|
|
114
|
+
// We are not interested in anything below this node,
|
|
115
|
+
// so return true because we are done with this node.
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// This node made it past the above return statement, so it must either
|
|
120
|
+
// contain, be within, or overlap with the selected polygon.
|
|
121
|
+
|
|
122
|
+
// Check if this is a leaf node.
|
|
123
|
+
if (node.data
|
|
124
|
+
&& booleanPointInPolygon(
|
|
125
|
+
turfPoint([].slice.call(getObsCoords(node.data))), selectedPolygon,
|
|
126
|
+
)
|
|
127
|
+
) {
|
|
128
|
+
// This node has data, so it is a leaf node representing one data point,
|
|
129
|
+
// and we have verified that the point is in the selected polygon.
|
|
130
|
+
pickingInfos.push(node.data);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Return false because we are not done.
|
|
134
|
+
// We want to visit the children of this node.
|
|
135
|
+
return false;
|
|
136
|
+
});
|
|
137
|
+
const pickingIds = pickingInfos.map(obsI => obsIndex[obsI]);
|
|
138
|
+
layerOnSelect(pickingIds);
|
|
126
139
|
});
|
|
140
|
+
}
|
|
127
141
|
|
|
128
|
-
|
|
142
|
+
_selectEmpty() {
|
|
143
|
+
const { obsLayers } = this.props;
|
|
144
|
+
obsLayers.forEach((obsLayer) => {
|
|
145
|
+
const { onSelect: layerOnSelect } = obsLayer;
|
|
146
|
+
layerOnSelect([]);
|
|
147
|
+
});
|
|
129
148
|
}
|
|
130
149
|
|
|
131
150
|
renderLayers() {
|
|
132
|
-
const { onSelect } = this.props;
|
|
133
151
|
const mode = MODE_MAP[this.props.selectionType] || ViewMode;
|
|
134
152
|
|
|
135
153
|
const inheritedProps = {};
|
|
@@ -153,7 +171,7 @@ export default class SelectionLayer extends CompositeLayer {
|
|
|
153
171
|
this._selectPolygonObjects(coordinates);
|
|
154
172
|
} else if (editType === EDIT_TYPE_CLEAR) {
|
|
155
173
|
// We want to select an empty array to clear any previous selection.
|
|
156
|
-
|
|
174
|
+
this._selectEmpty();
|
|
157
175
|
}
|
|
158
176
|
},
|
|
159
177
|
_subLayerProps: {
|