@vitessce/config 3.0.1 → 3.1.0
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/index.js +515 -59
- package/dist-tsc/VitessceAutoConfig.d.ts +13 -1
- package/dist-tsc/VitessceAutoConfig.d.ts.map +1 -1
- package/dist-tsc/VitessceAutoConfig.js +233 -57
- package/dist-tsc/VitessceAutoConfig.test.js +533 -76
- package/dist-tsc/VitessceConfig.d.ts +46 -0
- package/dist-tsc/VitessceConfig.d.ts.map +1 -1
- package/dist-tsc/VitessceConfig.js +353 -0
- package/dist-tsc/VitessceConfig.test.js +328 -1
- package/dist-tsc/constants.d.ts +247 -0
- package/dist-tsc/constants.d.ts.map +1 -0
- package/dist-tsc/constants.js +87 -0
- package/dist-tsc/index.d.ts +2 -1
- package/dist-tsc/index.js +2 -1
- package/package.json +3 -3
- package/src/VitessceAutoConfig.js +271 -65
- package/src/VitessceAutoConfig.test.js +561 -78
- package/src/VitessceConfig.js +393 -0
- package/src/VitessceConfig.test.js +338 -0
- package/src/constants.js +94 -0
- package/src/index.js +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitessce/config",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"author": "Gehlenborg Lab",
|
|
5
5
|
"homepage": "http://vitessce.io",
|
|
6
6
|
"repository": {
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"dist-tsc"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@vitessce/constants-internal": "3.0
|
|
20
|
-
"@vitessce/utils": "3.0
|
|
19
|
+
"@vitessce/constants-internal": "3.1.0",
|
|
20
|
+
"@vitessce/utils": "3.1.0"
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
23
|
"bundle": "pnpm exec vite build -c ../../scripts/vite.config.js",
|
|
@@ -1,8 +1,39 @@
|
|
|
1
|
-
import { CoordinationType, FileType } from '@vitessce/constants-internal';
|
|
1
|
+
import { CoordinationType as ct, FileType } from '@vitessce/constants-internal';
|
|
2
2
|
import {
|
|
3
3
|
VitessceConfig,
|
|
4
4
|
} from './VitessceConfig.js';
|
|
5
5
|
|
|
6
|
+
import { HINTS_CONFIG, HINT_TYPE_TO_FILE_TYPE_MAP } from './constants.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {Object} hintsConfig. The hints config for the given dataset.
|
|
10
|
+
*
|
|
11
|
+
* @param {Array} possibleViews. All views for a given file type, supported by the dataset.
|
|
12
|
+
* The array contains arrays of strings, each of size >= 1, where the first element of each string
|
|
13
|
+
* is a VitessceConfig view name ('description', 'spatial', 'layerController', etc.)
|
|
14
|
+
*
|
|
15
|
+
* @returns {Array of strings} the intersection of VitessceConfig view names in
|
|
16
|
+
* possibleViews and requiredViews.
|
|
17
|
+
*/
|
|
18
|
+
const filterViews = (hintsConfig, possibleViews) => {
|
|
19
|
+
const requiredViews = Object.keys(hintsConfig.views);
|
|
20
|
+
|
|
21
|
+
if (requiredViews.length === 0) {
|
|
22
|
+
return possibleViews;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const resultViews = [];
|
|
26
|
+
|
|
27
|
+
requiredViews.forEach((requiredView) => {
|
|
28
|
+
const match = possibleViews.find(possibleView => possibleView[0] === requiredView);
|
|
29
|
+
if (match) resultViews.push(match);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (resultViews.length === 0) {
|
|
33
|
+
throw new Error('No views found that are compatible with the supplied dataset URLs and hint.');
|
|
34
|
+
}
|
|
35
|
+
return resultViews;
|
|
36
|
+
};
|
|
6
37
|
|
|
7
38
|
class AbstractAutoConfig {
|
|
8
39
|
async composeViewsConfig() { /* eslint-disable-line class-methods-use-this */
|
|
@@ -13,6 +44,7 @@ class AbstractAutoConfig {
|
|
|
13
44
|
throw new Error('The composeFileConfig() method has not been implemented.');
|
|
14
45
|
}
|
|
15
46
|
}
|
|
47
|
+
|
|
16
48
|
class OmeTiffAutoConfig extends AbstractAutoConfig {
|
|
17
49
|
constructor(fileUrl) {
|
|
18
50
|
super();
|
|
@@ -21,13 +53,11 @@ class OmeTiffAutoConfig extends AbstractAutoConfig {
|
|
|
21
53
|
this.fileName = fileUrl.split('/').at(-1);
|
|
22
54
|
}
|
|
23
55
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
['description'],
|
|
28
|
-
|
|
29
|
-
['layerController'],
|
|
30
|
-
];
|
|
56
|
+
async composeViewsConfig(hintsConfig) { /* eslint-disable-line class-methods-use-this */
|
|
57
|
+
return filterViews(
|
|
58
|
+
hintsConfig,
|
|
59
|
+
[['description'], ['spatial'], ['layerController']],
|
|
60
|
+
);
|
|
31
61
|
}
|
|
32
62
|
|
|
33
63
|
async composeFileConfig() {
|
|
@@ -59,13 +89,11 @@ class OmeZarrAutoConfig extends AbstractAutoConfig {
|
|
|
59
89
|
this.fileName = fileUrl.split('/').at(-1);
|
|
60
90
|
}
|
|
61
91
|
|
|
62
|
-
async composeViewsConfig() { /* eslint-disable-line class-methods-use-this */
|
|
63
|
-
return
|
|
64
|
-
|
|
65
|
-
['spatial'],
|
|
66
|
-
|
|
67
|
-
['status'],
|
|
68
|
-
];
|
|
92
|
+
async composeViewsConfig(hintsConfig) { /* eslint-disable-line class-methods-use-this */
|
|
93
|
+
return filterViews(
|
|
94
|
+
hintsConfig,
|
|
95
|
+
[['description'], ['spatial'], ['layerController']],
|
|
96
|
+
);
|
|
69
97
|
}
|
|
70
98
|
|
|
71
99
|
async composeFileConfig() {
|
|
@@ -115,7 +143,7 @@ class AnndataZarrAutoConfig extends AbstractAutoConfig {
|
|
|
115
143
|
});
|
|
116
144
|
|
|
117
145
|
const supportedObsSetsKeys = [
|
|
118
|
-
'cluster', 'subcluster', 'cell_type', 'leiden', 'louvain', 'disease', 'organism', 'self_reported_ethnicity', 'tissue', 'sex',
|
|
146
|
+
'cluster', 'clusters', 'subcluster', 'cell_type', 'celltype', 'leiden', 'louvain', 'disease', 'organism', 'self_reported_ethnicity', 'tissue', 'sex',
|
|
119
147
|
];
|
|
120
148
|
|
|
121
149
|
this.metadataSummary.obs.forEach((key) => {
|
|
@@ -135,6 +163,19 @@ class AnndataZarrAutoConfig extends AbstractAutoConfig {
|
|
|
135
163
|
});
|
|
136
164
|
});
|
|
137
165
|
|
|
166
|
+
// if length of path is 1, storing the value as an array doesn't work
|
|
167
|
+
// es-lint-disable-next-line max-len
|
|
168
|
+
// Example: https://s3.amazonaws.com/vitessce-data/0.0.33/main/human-lymph-node-10x-visium/human_lymph_node_10x_visium.h5ad.zarr
|
|
169
|
+
options.obsSets = options.obsSets?.map((obsSet) => {
|
|
170
|
+
if (obsSet.path.length === 1) {
|
|
171
|
+
return {
|
|
172
|
+
...obsSet,
|
|
173
|
+
path: obsSet.path[0],
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return obsSet;
|
|
177
|
+
});
|
|
178
|
+
|
|
138
179
|
return {
|
|
139
180
|
options,
|
|
140
181
|
fileType: this.fileType,
|
|
@@ -147,41 +188,46 @@ class AnndataZarrAutoConfig extends AbstractAutoConfig {
|
|
|
147
188
|
};
|
|
148
189
|
}
|
|
149
190
|
|
|
150
|
-
async composeViewsConfig() {
|
|
191
|
+
async composeViewsConfig(hintsConfig) {
|
|
151
192
|
this.metadataSummary = await this.setMetadataSummary();
|
|
152
|
-
|
|
153
|
-
const views = [];
|
|
193
|
+
const possibleViews = [];
|
|
154
194
|
|
|
155
195
|
const hasCellSetData = this.metadataSummary.obs
|
|
156
|
-
.filter(key => key.toLowerCase().includes('cluster')
|
|
196
|
+
.filter(key => key.toLowerCase().includes('cluster')
|
|
197
|
+
|| key.toLowerCase().includes('cell_type')
|
|
198
|
+
|| key.toLowerCase().includes('celltype'));
|
|
157
199
|
|
|
158
200
|
if (hasCellSetData.length > 0) {
|
|
159
|
-
|
|
201
|
+
possibleViews.push(['obsSets']);
|
|
160
202
|
}
|
|
161
203
|
|
|
162
204
|
this.metadataSummary.obsm.forEach((key) => {
|
|
163
205
|
if (key.toLowerCase().includes('obsm/x_umap')) {
|
|
164
|
-
|
|
206
|
+
possibleViews.push(['scatterplot', { mapping: 'UMAP' }]);
|
|
165
207
|
}
|
|
166
208
|
if (key.toLowerCase().includes('obsm/x_tsne')) {
|
|
167
|
-
|
|
209
|
+
possibleViews.push(['scatterplot', { mapping: 't-SNE' }]);
|
|
168
210
|
}
|
|
169
211
|
if (key.toLowerCase().includes('obsm/x_pca')) {
|
|
170
|
-
|
|
212
|
+
possibleViews.push(['scatterplot', { mapping: 'PCA' }]);
|
|
171
213
|
}
|
|
172
214
|
if (key.toLowerCase().includes(('obsm/x_segmentations'))) {
|
|
173
|
-
|
|
215
|
+
possibleViews.push(['layerController']);
|
|
174
216
|
}
|
|
175
217
|
if (key.toLowerCase().includes(('obsm/x_spatial'))) {
|
|
176
|
-
|
|
218
|
+
possibleViews.push(['spatial']);
|
|
177
219
|
}
|
|
178
220
|
});
|
|
179
221
|
|
|
222
|
+
possibleViews.push(['obsSetSizes']);
|
|
223
|
+
possibleViews.push(['obsSetFeatureValueDistribution']);
|
|
224
|
+
|
|
180
225
|
if (this.metadataSummary.X) {
|
|
181
|
-
|
|
182
|
-
|
|
226
|
+
possibleViews.push(['heatmap']);
|
|
227
|
+
possibleViews.push(['featureList']);
|
|
183
228
|
}
|
|
184
229
|
|
|
230
|
+
const views = filterViews(hintsConfig, possibleViews);
|
|
185
231
|
return views;
|
|
186
232
|
}
|
|
187
233
|
|
|
@@ -224,6 +270,7 @@ class AnndataZarrAutoConfig extends AbstractAutoConfig {
|
|
|
224
270
|
'/obsm/X_segmentations/.zarray',
|
|
225
271
|
'/obs/.zattrs',
|
|
226
272
|
'/X/.zarray',
|
|
273
|
+
'/X/data/.zarray', // for https://s3.amazonaws.com/vitessce-data/0.0.33/main/human-lymph-node-10x-visium/human_lymph_node_10x_visium.h5ad.zarr
|
|
227
274
|
];
|
|
228
275
|
|
|
229
276
|
const getObsmKey = (url) => {
|
|
@@ -296,6 +343,8 @@ class AnndataZarrAutoConfig extends AbstractAutoConfig {
|
|
|
296
343
|
return this.setMetadataSummaryWithoutZmetadata();
|
|
297
344
|
}
|
|
298
345
|
throw new Error(`Could not generate config: ${response.statusText}`);
|
|
346
|
+
}).catch((error) => {
|
|
347
|
+
throw new Error(`Could not generate config for URL ${this.fileUrl}: ${error}`);
|
|
299
348
|
});
|
|
300
349
|
}
|
|
301
350
|
}
|
|
@@ -304,27 +353,20 @@ const configClasses = [
|
|
|
304
353
|
{
|
|
305
354
|
extensions: ['.ome.tif', '.ome.tiff', '.ome.tf2', '.ome.tf8'],
|
|
306
355
|
class: OmeTiffAutoConfig,
|
|
356
|
+
name: 'OME-TIFF',
|
|
307
357
|
},
|
|
308
358
|
{
|
|
309
359
|
extensions: ['.h5ad.zarr', '.adata.zarr', '.anndata.zarr'],
|
|
310
360
|
class: AnndataZarrAutoConfig,
|
|
361
|
+
name: 'AnnData-Zarr',
|
|
311
362
|
},
|
|
312
363
|
{
|
|
313
364
|
extensions: ['ome.zarr'],
|
|
314
365
|
class: OmeZarrAutoConfig,
|
|
366
|
+
name: 'OME-Zarr',
|
|
315
367
|
},
|
|
316
368
|
];
|
|
317
369
|
|
|
318
|
-
function getFileType(url) {
|
|
319
|
-
const match = configClasses.find(obj => obj.extensions.filter(
|
|
320
|
-
ext => url.endsWith(ext),
|
|
321
|
-
).length === 1);
|
|
322
|
-
if (!match) {
|
|
323
|
-
throw new Error(`Could not generate config for URL: ${url}. This file type is not supported.`);
|
|
324
|
-
}
|
|
325
|
-
return match.class;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
370
|
function calculateCoordinates(viewsNumb) {
|
|
329
371
|
const rows = Math.ceil(Math.sqrt(viewsNumb));
|
|
330
372
|
const cols = Math.ceil(viewsNumb / rows);
|
|
@@ -337,16 +379,142 @@ function calculateCoordinates(viewsNumb) {
|
|
|
337
379
|
const col = i % cols;
|
|
338
380
|
const x = col * width;
|
|
339
381
|
const y = row * height;
|
|
340
|
-
|
|
382
|
+
|
|
383
|
+
// The coordinates have to be integer values:
|
|
384
|
+
coords.push([
|
|
385
|
+
Math.floor(x),
|
|
386
|
+
Math.floor(y),
|
|
387
|
+
// Ensure width/height is at least 1.
|
|
388
|
+
Math.max(1, Math.floor(width)),
|
|
389
|
+
Math.max(1, Math.floor(height)),
|
|
390
|
+
]);
|
|
341
391
|
}
|
|
342
392
|
|
|
343
393
|
return coords;
|
|
344
394
|
}
|
|
345
395
|
|
|
346
|
-
|
|
396
|
+
const spatialSegmentationLayerValue = {
|
|
397
|
+
radius: 65,
|
|
398
|
+
stroked: true,
|
|
399
|
+
visible: true,
|
|
400
|
+
opacity: 1,
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Inserts the spatial coordination space into the VitessceConfig views.
|
|
405
|
+
* @param {Array of VitessceConfigView} of views
|
|
406
|
+
* @param {VitessceConfig} vc instance
|
|
407
|
+
*/
|
|
408
|
+
function insertCoordinationSpaceForSpatial(views, vc) {
|
|
409
|
+
const [
|
|
410
|
+
spatialSegmentationLayer,
|
|
411
|
+
spatialImageLayer,
|
|
412
|
+
spatialZoom,
|
|
413
|
+
spatialTargetX,
|
|
414
|
+
spatialTargetY,
|
|
415
|
+
] = vc.addCoordination(
|
|
416
|
+
ct.SPATIAL_SEGMENTATION_LAYER,
|
|
417
|
+
ct.SPATIAL_IMAGE_LAYER,
|
|
418
|
+
ct.SPATIAL_ZOOM,
|
|
419
|
+
ct.SPATIAL_TARGET_X,
|
|
420
|
+
ct.SPATIAL_TARGET_Y,
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
// Note: this always assumes the segmentation is polygon-based.
|
|
424
|
+
// In the future, we may want to support both polygon-based and bitmask-based.
|
|
425
|
+
spatialSegmentationLayer.setValue(spatialSegmentationLayerValue);
|
|
426
|
+
// Note: this always assumes the image is RGB.
|
|
427
|
+
// In the future, we may want to support both RGB and multi-channel.
|
|
428
|
+
spatialImageLayer.setValue([
|
|
429
|
+
{
|
|
430
|
+
type: 'raster',
|
|
431
|
+
index: 0,
|
|
432
|
+
colormap: null,
|
|
433
|
+
transparentColor: null,
|
|
434
|
+
opacity: 1,
|
|
435
|
+
domainType: 'Min/Max',
|
|
436
|
+
channels: [
|
|
437
|
+
{
|
|
438
|
+
selection: {
|
|
439
|
+
c: 0,
|
|
440
|
+
},
|
|
441
|
+
color: [
|
|
442
|
+
255,
|
|
443
|
+
0,
|
|
444
|
+
0,
|
|
445
|
+
],
|
|
446
|
+
visible: true,
|
|
447
|
+
slider: [
|
|
448
|
+
0,
|
|
449
|
+
255,
|
|
450
|
+
],
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
selection: {
|
|
454
|
+
c: 1,
|
|
455
|
+
},
|
|
456
|
+
color: [
|
|
457
|
+
0,
|
|
458
|
+
255,
|
|
459
|
+
0,
|
|
460
|
+
],
|
|
461
|
+
visible: true,
|
|
462
|
+
slider: [
|
|
463
|
+
0,
|
|
464
|
+
255,
|
|
465
|
+
],
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
selection: {
|
|
469
|
+
c: 2,
|
|
470
|
+
},
|
|
471
|
+
color: [
|
|
472
|
+
0,
|
|
473
|
+
0,
|
|
474
|
+
255,
|
|
475
|
+
],
|
|
476
|
+
visible: true,
|
|
477
|
+
slider: [
|
|
478
|
+
0,
|
|
479
|
+
255,
|
|
480
|
+
],
|
|
481
|
+
},
|
|
482
|
+
],
|
|
483
|
+
},
|
|
484
|
+
]);
|
|
485
|
+
|
|
486
|
+
views.forEach((view) => {
|
|
487
|
+
if (view.view.component === 'spatial' || view.view.component === 'layerController') {
|
|
488
|
+
view.useCoordination(spatialImageLayer);
|
|
489
|
+
view.useCoordination(spatialSegmentationLayer);
|
|
490
|
+
view.useCoordination(spatialZoom);
|
|
491
|
+
view.useCoordination(spatialTargetX);
|
|
492
|
+
view.useCoordination(spatialTargetY);
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Returns the type of the file, based on the file extension.
|
|
499
|
+
* @param {string} url of the file.
|
|
500
|
+
* @returns {object} An element from the `configClasses` array, which will be an object with
|
|
501
|
+
* the properties `extensions: string[]`, `class` (an AutoConfig class definition) and
|
|
502
|
+
* `name`: string.
|
|
503
|
+
*/
|
|
504
|
+
function getFileType(url) {
|
|
505
|
+
const match = configClasses.find(obj => obj.extensions.filter(
|
|
506
|
+
ext => url.endsWith(ext),
|
|
507
|
+
).length === 1);
|
|
508
|
+
if (!match) {
|
|
509
|
+
throw new Error('One or more of the URLs provided point to unsupported file types.');
|
|
510
|
+
}
|
|
511
|
+
return match;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
async function generateViewDefinition(url, vc, dataset, hintsConfig) {
|
|
347
515
|
let ConfigClassName;
|
|
348
516
|
try {
|
|
349
|
-
ConfigClassName = getFileType(url);
|
|
517
|
+
ConfigClassName = getFileType(url).class;
|
|
350
518
|
} catch (err) {
|
|
351
519
|
return Promise.reject(err);
|
|
352
520
|
}
|
|
@@ -356,19 +524,16 @@ async function generateConfig(url, vc) {
|
|
|
356
524
|
let viewsConfig;
|
|
357
525
|
try {
|
|
358
526
|
fileConfig = await configInstance.composeFileConfig();
|
|
359
|
-
viewsConfig = await configInstance.composeViewsConfig();
|
|
527
|
+
viewsConfig = await configInstance.composeViewsConfig(hintsConfig);
|
|
360
528
|
} catch (error) {
|
|
361
529
|
console.error(error);
|
|
362
530
|
return Promise.reject(error);
|
|
363
531
|
}
|
|
364
532
|
|
|
365
|
-
|
|
366
|
-
.addDataset(configInstance.fileName)
|
|
367
|
-
.addFile(fileConfig);
|
|
533
|
+
dataset.addFile(fileConfig);
|
|
368
534
|
|
|
369
535
|
let layerControllerView = false;
|
|
370
536
|
let spatialView = false;
|
|
371
|
-
|
|
372
537
|
const views = [];
|
|
373
538
|
|
|
374
539
|
viewsConfig.forEach((v) => {
|
|
@@ -381,7 +546,7 @@ async function generateConfig(url, vc) {
|
|
|
381
546
|
}
|
|
382
547
|
// this piece of code can be removed once these props are added by default to layerController
|
|
383
548
|
// see this issue: https://github.com/vitessce/vitessce/issues/1454
|
|
384
|
-
if (v[0] === 'layerController'
|
|
549
|
+
if (v[0] === 'layerController') {
|
|
385
550
|
view.setProps({
|
|
386
551
|
disable3d: [],
|
|
387
552
|
disableChannelsIfRgbDetected: true,
|
|
@@ -396,29 +561,48 @@ async function generateConfig(url, vc) {
|
|
|
396
561
|
});
|
|
397
562
|
|
|
398
563
|
if (layerControllerView && spatialView && configInstance instanceof AnndataZarrAutoConfig) {
|
|
399
|
-
const spatialSegmentationLayerValue = {
|
|
400
|
-
opacity: 1,
|
|
401
|
-
radius: 0,
|
|
402
|
-
visible: true,
|
|
403
|
-
stroked: false,
|
|
404
|
-
};
|
|
405
|
-
|
|
406
564
|
vc.linkViews(
|
|
407
565
|
[spatialView, layerControllerView],
|
|
408
566
|
[
|
|
409
|
-
|
|
410
|
-
CoordinationType.SPATIAL_TARGET_X,
|
|
411
|
-
CoordinationType.SPATIAL_TARGET_Y,
|
|
412
|
-
CoordinationType.SPATIAL_SEGMENTATION_LAYER,
|
|
567
|
+
ct.SPATIAL_SEGMENTATION_LAYER,
|
|
413
568
|
],
|
|
414
|
-
[
|
|
569
|
+
[spatialSegmentationLayerValue],
|
|
415
570
|
);
|
|
416
571
|
}
|
|
417
572
|
|
|
418
573
|
return views;
|
|
419
574
|
}
|
|
420
575
|
|
|
421
|
-
|
|
576
|
+
/**
|
|
577
|
+
* Returns the hints that are available for the given file URLs, depending on their types.
|
|
578
|
+
* @param {Array} fileUrls containing urls of files to be loaded into Vitessce
|
|
579
|
+
* @returns the hints available for these file URLs
|
|
580
|
+
*/
|
|
581
|
+
export function getHintOptions(fileUrls) {
|
|
582
|
+
const fileTypes = {};
|
|
583
|
+
|
|
584
|
+
fileUrls.forEach((url) => {
|
|
585
|
+
const match = getFileType(url);
|
|
586
|
+
// hints config only has OME-TIFF, since settings are the same for both OME-TIFF and OME-Zarr
|
|
587
|
+
if (match.name === 'OME-Zarr') {
|
|
588
|
+
fileTypes['OME-TIFF'] = true;
|
|
589
|
+
} else {
|
|
590
|
+
fileTypes[match.name] = true;
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
const datasetType = Object.keys(fileTypes).sort().join(',');
|
|
595
|
+
|
|
596
|
+
return HINT_TYPE_TO_FILE_TYPE_MAP?.[datasetType] || [];
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
*
|
|
601
|
+
* @param {Array} fileUrls containing urls of files to be loaded into Vitessce
|
|
602
|
+
* @param {String} the hints config to be used for the dataset. Null by default
|
|
603
|
+
* @returns ViewConfig as JSON
|
|
604
|
+
*/
|
|
605
|
+
export async function generateConfig(fileUrls, hintTitle = null) {
|
|
422
606
|
const vc = new VitessceConfig({
|
|
423
607
|
schemaVersion: '1.0.15',
|
|
424
608
|
name: 'An automatically generated config. Adjust values and add layout components if needed.',
|
|
@@ -427,19 +611,41 @@ export async function generateConfigs(fileUrls) {
|
|
|
427
611
|
|
|
428
612
|
const allViews = [];
|
|
429
613
|
|
|
614
|
+
const dataset = vc.addDataset('An automatically generated view config for dataset. Adjust values and add layout components if needed.');
|
|
615
|
+
|
|
616
|
+
const hintsConfig = !hintTitle ? { views: {} } : HINTS_CONFIG?.[hintTitle];
|
|
617
|
+
if (!hintsConfig) {
|
|
618
|
+
throw new Error(`Hints config not found for the supplied hint: ${hintTitle}.`);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const useHints = Object.keys(hintsConfig?.views)?.length > 0;
|
|
622
|
+
|
|
430
623
|
fileUrls.forEach((url) => {
|
|
431
|
-
allViews.push(
|
|
624
|
+
allViews.push(generateViewDefinition(url, vc, dataset, hintsConfig));
|
|
432
625
|
});
|
|
433
626
|
|
|
434
627
|
return Promise.all(allViews).then((views) => {
|
|
435
628
|
const flattenedViews = views.flat();
|
|
436
629
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
630
|
+
// If coordinationSpaceRequired field is set to true in the HINTS_CONFIG,
|
|
631
|
+
// then insert coordination space.
|
|
632
|
+
// NOTE: the user needs to manually add the coordination values for the image and
|
|
633
|
+
// segmentation layers which will be visualized in the spatial/layer controller views.
|
|
634
|
+
if (hintsConfig?.coordinationSpaceRequired) {
|
|
635
|
+
insertCoordinationSpaceForSpatial(flattenedViews, vc);
|
|
441
636
|
}
|
|
442
637
|
|
|
638
|
+
if (!useHints) {
|
|
639
|
+
const coord = calculateCoordinates(flattenedViews.length);
|
|
640
|
+
for (let i = 0; i < flattenedViews.length; i++) {
|
|
641
|
+
flattenedViews[i].setXYWH(...coord[i]);
|
|
642
|
+
}
|
|
643
|
+
} else {
|
|
644
|
+
flattenedViews.forEach((vitessceConfigView) => {
|
|
645
|
+
const coordinates = Object.values(hintsConfig.views[vitessceConfigView.view.component]);
|
|
646
|
+
vitessceConfigView.setXYWH(...coordinates);
|
|
647
|
+
});
|
|
648
|
+
}
|
|
443
649
|
return vc.toJSON();
|
|
444
650
|
});
|
|
445
651
|
}
|