@vitessce/config 2.0.3-beta.0 → 3.0.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 +15914 -1
- package/dist-tsc/VitessceAutoConfig.d.ts +2 -0
- package/dist-tsc/VitessceAutoConfig.d.ts.map +1 -0
- package/dist-tsc/VitessceAutoConfig.js +323 -0
- package/dist-tsc/VitessceAutoConfig.test.d.ts +2 -0
- package/dist-tsc/VitessceAutoConfig.test.d.ts.map +1 -0
- package/dist-tsc/VitessceAutoConfig.test.js +442 -0
- package/dist-tsc/VitessceConfig.d.ts +257 -0
- package/dist-tsc/VitessceConfig.d.ts.map +1 -0
- package/dist-tsc/VitessceConfig.test.d.ts +2 -0
- package/dist-tsc/VitessceConfig.test.d.ts.map +1 -0
- package/{dist → dist-tsc}/VitessceConfig.test.js +1 -1
- package/dist-tsc/index.d.ts +3 -0
- package/dist-tsc/index.d.ts.map +1 -0
- package/dist-tsc/index.js +2 -0
- package/dist-tsc/utils.d.ts +12 -0
- package/dist-tsc/utils.d.ts.map +1 -0
- package/package.json +15 -6
- package/src/VitessceAutoConfig.js +382 -0
- package/src/VitessceAutoConfig.test.js +465 -0
- package/src/VitessceConfig.js +502 -0
- package/src/VitessceConfig.test.js +490 -0
- package/src/index.js +2 -0
- package/src/utils.js +48 -0
- /package/{dist → dist-tsc}/VitessceConfig.js +0 -0
- /package/{dist → dist-tsc}/utils.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VitessceConfig.d.ts","sourceRoot":"","sources":["../src/VitessceConfig.js"],"names":[],"mappings":"AAqMA;;;;;GAKG;AACH,kCAJe,CAAC,kBAAkB,GAAC,yBAAyB,GAAC,yBAAyB,CAAC,KAE1E,yBAAyB,CAKrC;AAED;;;;;GAKG;AACH,kCAJe,CAAC,kBAAkB,GAAC,yBAAyB,GAAC,yBAAyB,CAAC,KAE1E,yBAAyB,CAKrC;AAtND;;GAEG;AACH;IACE;;;;;;;;OAQG;IACH,iBAPW,MAAM,YAEN,MAAM,oCACN,MAAM,WAAO,IAAI,EAW3B;IANC;;;;;MAKC;IAGH;;OAEG;IACH,UAFa,MAAM,CAIlB;CACF;AAED;;GAEG;AACH;IACE;;;;;OAKG;IACH,iBAJW,MAAM,QACN,MAAM,eACN,MAAM,EAShB;IANC;;;;;MAKC;IAGH;;;;;;;;;;OAUG;IACH;QARoC,GAAG,EAA5B,MAAM,GAAC,SAAS;QACD,QAAQ,EAAvB,MAAM;QACmB,kBAAkB,EAA3C,MAAM,GAAC,SAAS;QACe,OAAO,EAAtC,MAAM,WAAO,SAAS;wBAGpB,qBAAqB,CA6BjC;IAED;;OAEG;IACH,UAFa,MAAM,CAOlB;CACF;AAED;;GAEG;AACH;IACE;;;;;;;;;OASG;IACH,uBARW,MAAM,sBACN,MAAM,KAEN,MAAM,KACN,MAAM,KACN,MAAM,KACN,MAAM,EAWhB;IARC;;;;;;;MAOC;IAGH;;;;;OAKG;IACH,yBAJe,+BAA+B,KAEjC,kBAAkB,CAQ9B;IAED;;;;;;;QAOI;IACJ,WANY,MAAM,KACN,MAAM,KACN,MAAM,KACN,MAAM,GACJ,kBAAkB,CAS/B;IAED;;;OAGG;IACH,sBAFa,kBAAkB,CAQ9B;IAED;;OAEG;IACH,UAFa,MAAM,CAIlB;CACF;AAED;;GAEG;AACH;IACE,wBAEC;IADC,WAAkB;CAErB;AAED;;GAEG;AACH;IACE,wBAEC;IADC,WAAkB;CAErB;AAwBD;;GAEG;AACH;IACE;;;;OAIG;IACH,mBAHW,MAAM,UACN,MAAM,EAMhB;IAHC,cAAkB;IAClB,eAAoB;IACpB,YAAkB;IAGpB;;;;OAIG;IACH,iBAHW,GAAG,GACD,+BAA+B,CAK3C;CACF;AAED;;GAEG;AACH;IAuNE;;;;;;OAMG;IACH,wBAJW,MAAM,GACJ,cAAc,CAiC1B;IA3PD;;;;;;OAMG;IACH;QAJ0B,aAAa,EAA5B,MAAM;QACS,IAAI,EAAnB,MAAM;QACmB,WAAW,EAApC,MAAM,GAAC,SAAS;uBAmC1B;IATC;;;;;;;;MAQC;IAGH;;;;;;;;OAQG;IACH,kBAPW,MAAM,gBACN,MAAM;QAEU,GAAG,EAAnB,MAAM;QAEJ,qBAAqB,CAWjC;IAED;;;;;;;;;;;;;OAaG;IACH,iBAZW,qBAAqB,aAErB,MAAM;QAEU,CAAC,EAAjB,MAAM;QACU,CAAC,EAAjB,MAAM;QACU,CAAC,EAAjB,MAAM;QACU,CAAC,EAAjB,MAAM;QACU,OAAO,EAAvB,MAAM;QAEJ,kBAAkB,CAmC9B;IAED;;;;;OAKG;IACH,yBAHc,MAAM,KACP,+BAA+B,EAAE,CAmB7C;IAED;;;;;;;OAOG;IACH,iBANW,kBAAkB,EAAE,UACpB,MAAM,EAAE,YACR,GAAG,EAAE,GAEH,cAAc,CAe1B;IAED;;;;;OAKG;IACH,mBAJW,kBAAkB,GAAC,yBAAyB,GAAC,yBAAyB,GAEpE,cAAc,CA0B1B;IAED;;;OAGG;IACH,UAFa,MAAM,CAmBlB;CAwCF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VitessceConfig.test.d.ts","sourceRoot":"","sources":["../src/VitessceConfig.test.js"],"names":[],"mappings":""}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CoordinationType } from '@vitessce/constants-internal';
|
|
2
|
-
import { VitessceConfig, hconcat, vconcat, } from './VitessceConfig';
|
|
2
|
+
import { VitessceConfig, hconcat, vconcat, } from './VitessceConfig.js';
|
|
3
3
|
describe('src/api/VitessceConfig.js', () => {
|
|
4
4
|
describe('VitessceConfig', () => {
|
|
5
5
|
it('can be instantiated in the old way for backwards compatibility', () => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate a new scope name which does not
|
|
3
|
+
* conflict / overlap with a previous scope name.
|
|
4
|
+
* Really these just need to be unique within the coordination object.
|
|
5
|
+
* So in theory they could be String(Math.random()) or uuidv4() or something.
|
|
6
|
+
* However it may be good to make them more human-readable and memorable
|
|
7
|
+
* since eventually we will want to expose a UI to update the coordination.
|
|
8
|
+
* @param {string[]} prevScopes Previous scope names.
|
|
9
|
+
* @returns {string} The new scope name.
|
|
10
|
+
*/
|
|
11
|
+
export function getNextScope(prevScopes: string[]): string;
|
|
12
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.js"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,yCAHW,MAAM,EAAE,GACN,MAAM,CAuClB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitessce/config",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"author": "Gehlenborg Lab",
|
|
5
5
|
"homepage": "http://vitessce.io",
|
|
6
6
|
"repository": {
|
|
@@ -8,17 +8,26 @@
|
|
|
8
8
|
"url": "git+https://github.com/vitessce/vitessce.git"
|
|
9
9
|
},
|
|
10
10
|
"license": "MIT",
|
|
11
|
+
"type": "module",
|
|
11
12
|
"main": "dist/index.js",
|
|
12
13
|
"files": [
|
|
13
|
-
"
|
|
14
|
+
"src",
|
|
15
|
+
"dist",
|
|
16
|
+
"dist-tsc"
|
|
14
17
|
],
|
|
15
18
|
"dependencies": {
|
|
16
|
-
"@vitessce/constants-internal": "
|
|
17
|
-
"@vitessce/utils": "
|
|
19
|
+
"@vitessce/constants-internal": "3.0.0",
|
|
20
|
+
"@vitessce/utils": "3.0.0"
|
|
18
21
|
},
|
|
19
22
|
"scripts": {
|
|
20
|
-
"
|
|
21
|
-
"build": "tsc",
|
|
23
|
+
"bundle": "pnpm exec vite build -c ../../scripts/vite.config.js",
|
|
22
24
|
"test": "pnpm exec vitest --run -r ../../ --dir ."
|
|
25
|
+
},
|
|
26
|
+
"module": "dist/index.js",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist-tsc/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
}
|
|
23
32
|
}
|
|
24
33
|
}
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import { CoordinationType, FileType } from '@vitessce/constants-internal';
|
|
2
|
+
import {
|
|
3
|
+
VitessceConfig,
|
|
4
|
+
} from './VitessceConfig.js';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AbstractAutoConfig {
|
|
8
|
+
async composeViewsConfig() { /* eslint-disable-line class-methods-use-this */
|
|
9
|
+
throw new Error('The composeViewsConfig() method has not been implemented.');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async composeFileConfig() { /* eslint-disable-line class-methods-use-this */
|
|
13
|
+
throw new Error('The composeFileConfig() method has not been implemented.');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
class OmeTiffAutoConfig extends AbstractAutoConfig {
|
|
17
|
+
constructor(fileUrl) {
|
|
18
|
+
super();
|
|
19
|
+
this.fileUrl = fileUrl;
|
|
20
|
+
this.fileType = FileType.RASTER_JSON;
|
|
21
|
+
this.fileName = fileUrl.split('/').at(-1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async composeViewsConfig() { /* eslint-disable-line class-methods-use-this */
|
|
26
|
+
return [
|
|
27
|
+
['description'],
|
|
28
|
+
['spatial'],
|
|
29
|
+
['layerController'],
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async composeFileConfig() {
|
|
34
|
+
return {
|
|
35
|
+
fileType: this.fileType,
|
|
36
|
+
options: {
|
|
37
|
+
images: [
|
|
38
|
+
{
|
|
39
|
+
metadata: {
|
|
40
|
+
isBitmask: false,
|
|
41
|
+
},
|
|
42
|
+
name: this.fileName,
|
|
43
|
+
type: 'ome-tiff',
|
|
44
|
+
url: this.fileUrl,
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
schemaVersion: '0.0.2',
|
|
48
|
+
usePhysicalSizeScaling: false,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
class OmeZarrAutoConfig extends AbstractAutoConfig {
|
|
55
|
+
constructor(fileUrl) {
|
|
56
|
+
super();
|
|
57
|
+
this.fileUrl = fileUrl;
|
|
58
|
+
this.fileType = FileType.RASTER_OME_ZARR;
|
|
59
|
+
this.fileName = fileUrl.split('/').at(-1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async composeViewsConfig() { /* eslint-disable-line class-methods-use-this */
|
|
63
|
+
return [
|
|
64
|
+
['description'],
|
|
65
|
+
['spatial'],
|
|
66
|
+
['layerController'],
|
|
67
|
+
['status'],
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async composeFileConfig() {
|
|
72
|
+
return {
|
|
73
|
+
fileType: this.fileType,
|
|
74
|
+
type: 'raster',
|
|
75
|
+
url: this.fileUrl,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
class AnndataZarrAutoConfig extends AbstractAutoConfig {
|
|
81
|
+
constructor(fileUrl) {
|
|
82
|
+
super();
|
|
83
|
+
this.fileUrl = fileUrl;
|
|
84
|
+
this.fileType = FileType.ANNDATA_ZARR;
|
|
85
|
+
this.fileName = fileUrl.split('/').at(-1);
|
|
86
|
+
this.metadataSummary = {};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async composeFileConfig() {
|
|
90
|
+
this.metadataSummary = await this.setMetadataSummary();
|
|
91
|
+
|
|
92
|
+
const options = {
|
|
93
|
+
obsEmbedding: [],
|
|
94
|
+
obsFeatureMatrix: {
|
|
95
|
+
path: 'X',
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
this.metadataSummary.obsm.forEach((key) => {
|
|
100
|
+
if (key.toLowerCase().includes(('obsm/x_segmentations'))) {
|
|
101
|
+
options.obsSegmentations = { path: key };
|
|
102
|
+
}
|
|
103
|
+
if (key.toLowerCase().includes(('obsm/x_spatial'))) {
|
|
104
|
+
options.obsLocations = { path: key };
|
|
105
|
+
}
|
|
106
|
+
if (key.toLowerCase().includes('obsm/x_umap')) {
|
|
107
|
+
options.obsEmbedding.push({ path: key, embeddingType: 'UMAP' });
|
|
108
|
+
}
|
|
109
|
+
if (key.toLowerCase().includes('obsm/x_tsne')) {
|
|
110
|
+
options.obsEmbedding.push({ path: key, embeddingType: 't-SNE' });
|
|
111
|
+
}
|
|
112
|
+
if (key.toLowerCase().includes('obsm/x_pca')) {
|
|
113
|
+
options.obsEmbedding.push({ path: key, embeddingType: 'PCA' });
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const supportedObsSetsKeys = [
|
|
118
|
+
'cluster', 'subcluster', 'cell_type', 'leiden', 'louvain', 'disease', 'organism', 'self_reported_ethnicity', 'tissue', 'sex',
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
this.metadataSummary.obs.forEach((key) => {
|
|
122
|
+
supportedObsSetsKeys.forEach((supportedKey) => {
|
|
123
|
+
if (key.toLowerCase() === ['obs', supportedKey].join('/')) {
|
|
124
|
+
if (!('obsSets' in options)) {
|
|
125
|
+
options.obsSets = [
|
|
126
|
+
{
|
|
127
|
+
name: 'Cell Type',
|
|
128
|
+
path: [key],
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
} else {
|
|
132
|
+
options.obsSets[0].path.push(key);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
options,
|
|
140
|
+
fileType: this.fileType,
|
|
141
|
+
url: this.fileUrl,
|
|
142
|
+
coordinationValues: {
|
|
143
|
+
obsType: 'cell',
|
|
144
|
+
featureType: 'gene',
|
|
145
|
+
featureValueType: 'expression',
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async composeViewsConfig() {
|
|
151
|
+
this.metadataSummary = await this.setMetadataSummary();
|
|
152
|
+
|
|
153
|
+
const views = [];
|
|
154
|
+
|
|
155
|
+
const hasCellSetData = this.metadataSummary.obs
|
|
156
|
+
.filter(key => key.toLowerCase().includes('cluster') || key.toLowerCase().includes('cell_type'));
|
|
157
|
+
|
|
158
|
+
if (hasCellSetData.length > 0) {
|
|
159
|
+
views.push(['obsSets']);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
this.metadataSummary.obsm.forEach((key) => {
|
|
163
|
+
if (key.toLowerCase().includes('obsm/x_umap')) {
|
|
164
|
+
views.push(['scatterplot', { mapping: 'UMAP' }]);
|
|
165
|
+
}
|
|
166
|
+
if (key.toLowerCase().includes('obsm/x_tsne')) {
|
|
167
|
+
views.push(['scatterplot', { mapping: 't-SNE' }]);
|
|
168
|
+
}
|
|
169
|
+
if (key.toLowerCase().includes('obsm/x_pca')) {
|
|
170
|
+
views.push(['scatterplot', { mapping: 'PCA' }]);
|
|
171
|
+
}
|
|
172
|
+
if (key.toLowerCase().includes(('obsm/x_segmentations'))) {
|
|
173
|
+
views.push(['layerController']);
|
|
174
|
+
}
|
|
175
|
+
if (key.toLowerCase().includes(('obsm/x_spatial'))) {
|
|
176
|
+
views.push(['spatial']);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
if (this.metadataSummary.X) {
|
|
181
|
+
views.push(['heatmap']);
|
|
182
|
+
views.push(['featureList']);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return views;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async setMetadataSummary() {
|
|
189
|
+
if (Object.keys(this.metadataSummary).length > 0) {
|
|
190
|
+
return this.metadataSummary;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const parseMetadataFile = (metadataFile) => {
|
|
194
|
+
if (!metadataFile.metadata) {
|
|
195
|
+
throw new Error('Could not generate config: .zmetadata file is not valid.');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const obsmKeys = Object.keys(metadataFile.metadata)
|
|
199
|
+
.filter(key => key.startsWith('obsm/X_'))
|
|
200
|
+
.map(key => key.split('/.zarray')[0]);
|
|
201
|
+
|
|
202
|
+
const obsKeysArr = Object.keys(metadataFile.metadata)
|
|
203
|
+
.filter(key => key.startsWith('obs/')).map(key => key.split('/.za')[0]);
|
|
204
|
+
|
|
205
|
+
function uniq(a) {
|
|
206
|
+
return a.sort().filter((item, pos, ary) => !pos || item !== ary[pos - 1]);
|
|
207
|
+
}
|
|
208
|
+
const obsKeys = uniq(obsKeysArr);
|
|
209
|
+
|
|
210
|
+
const X = Object.keys(metadataFile.metadata).filter(key => key.startsWith('X'));
|
|
211
|
+
|
|
212
|
+
const out = {
|
|
213
|
+
obsm: obsmKeys,
|
|
214
|
+
obs: obsKeys,
|
|
215
|
+
X: X.length > 0,
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
return out;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const metadataExtension = '.zmetadata';
|
|
222
|
+
const url = [this.fileUrl, metadataExtension].join('/');
|
|
223
|
+
return fetch(url).then((response) => {
|
|
224
|
+
if (response.ok) {
|
|
225
|
+
return response.json();
|
|
226
|
+
}
|
|
227
|
+
return Promise.reject(response);
|
|
228
|
+
})
|
|
229
|
+
.then(responseJson => parseMetadataFile(responseJson))
|
|
230
|
+
.catch((error) => {
|
|
231
|
+
if (error.status === 404) {
|
|
232
|
+
const errorMssg = `Could not generate config. File ${metadataExtension} not found in supplied file URL. Check docs for more explanation.`;
|
|
233
|
+
return Promise.reject(new Error(errorMssg));
|
|
234
|
+
}
|
|
235
|
+
return Promise.reject(error);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const configClasses = [
|
|
241
|
+
{
|
|
242
|
+
extensions: ['.ome.tif', '.ome.tiff', '.ome.tf2', '.ome.tf8'],
|
|
243
|
+
class: OmeTiffAutoConfig,
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
extensions: ['.h5ad.zarr', '.adata.zarr', '.anndata.zarr'],
|
|
247
|
+
class: AnndataZarrAutoConfig,
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
extensions: ['ome.zarr'],
|
|
251
|
+
class: OmeZarrAutoConfig,
|
|
252
|
+
},
|
|
253
|
+
];
|
|
254
|
+
|
|
255
|
+
function getFileType(url) {
|
|
256
|
+
const match = configClasses.find(obj => obj.extensions.filter(
|
|
257
|
+
ext => url.endsWith(ext),
|
|
258
|
+
).length === 1);
|
|
259
|
+
if (!match) {
|
|
260
|
+
throw new Error(`Could not generate config for URL: ${url}. This file type is not supported.`);
|
|
261
|
+
}
|
|
262
|
+
return match.class;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function calculateCoordinates(viewsNumb) {
|
|
266
|
+
const rows = Math.ceil(Math.sqrt(viewsNumb));
|
|
267
|
+
const cols = Math.ceil(viewsNumb / rows);
|
|
268
|
+
const width = 12 / cols;
|
|
269
|
+
const height = 12 / rows;
|
|
270
|
+
const coords = [];
|
|
271
|
+
|
|
272
|
+
for (let i = 0; i < viewsNumb; i++) {
|
|
273
|
+
const row = Math.floor(i / cols);
|
|
274
|
+
const col = i % cols;
|
|
275
|
+
const x = col * width;
|
|
276
|
+
const y = row * height;
|
|
277
|
+
coords.push([x, y, width, height]);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return coords;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async function generateConfig(url, vc) {
|
|
284
|
+
let ConfigClassName;
|
|
285
|
+
try {
|
|
286
|
+
ConfigClassName = getFileType(url);
|
|
287
|
+
} catch (err) {
|
|
288
|
+
return Promise.reject(err);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const configInstance = new ConfigClassName(url);
|
|
292
|
+
let fileConfig;
|
|
293
|
+
let viewsConfig;
|
|
294
|
+
try {
|
|
295
|
+
fileConfig = await configInstance.composeFileConfig();
|
|
296
|
+
viewsConfig = await configInstance.composeViewsConfig();
|
|
297
|
+
} catch (error) {
|
|
298
|
+
console.error(error);
|
|
299
|
+
return Promise.reject(error);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const dataset = vc
|
|
303
|
+
.addDataset(configInstance.fileName)
|
|
304
|
+
.addFile(fileConfig);
|
|
305
|
+
|
|
306
|
+
let layerControllerView = false;
|
|
307
|
+
let spatialView = false;
|
|
308
|
+
|
|
309
|
+
const views = [];
|
|
310
|
+
|
|
311
|
+
viewsConfig.forEach((v) => {
|
|
312
|
+
const view = vc.addView(dataset, ...v);
|
|
313
|
+
if (v[0] === 'layerController') {
|
|
314
|
+
layerControllerView = view;
|
|
315
|
+
}
|
|
316
|
+
if (v[0] === 'spatial') {
|
|
317
|
+
spatialView = view;
|
|
318
|
+
}
|
|
319
|
+
// this piece of code can be removed once these props are added by default to layerController
|
|
320
|
+
// see this issue: https://github.com/vitessce/vitessce/issues/1454
|
|
321
|
+
if (v[0] === 'layerController' && configInstance instanceof OmeTiffAutoConfig) {
|
|
322
|
+
view.setProps({
|
|
323
|
+
disable3d: [],
|
|
324
|
+
disableChannelsIfRgbDetected: true,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
// transpose the heatmap by default
|
|
328
|
+
if (v[0] === 'heatmap' && configInstance instanceof AnndataZarrAutoConfig) {
|
|
329
|
+
view.setProps({ transpose: true });
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
views.push(view);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
if (layerControllerView && spatialView && configInstance instanceof AnndataZarrAutoConfig) {
|
|
336
|
+
const spatialSegmentationLayerValue = {
|
|
337
|
+
opacity: 1,
|
|
338
|
+
radius: 0,
|
|
339
|
+
visible: true,
|
|
340
|
+
stroked: false,
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
vc.linkViews(
|
|
344
|
+
[spatialView, layerControllerView],
|
|
345
|
+
[
|
|
346
|
+
CoordinationType.SPATIAL_ZOOM,
|
|
347
|
+
CoordinationType.SPATIAL_TARGET_X,
|
|
348
|
+
CoordinationType.SPATIAL_TARGET_Y,
|
|
349
|
+
CoordinationType.SPATIAL_SEGMENTATION_LAYER,
|
|
350
|
+
],
|
|
351
|
+
[-5.5, 16000, 20000, spatialSegmentationLayerValue],
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return views;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export async function generateConfigs(fileUrls) {
|
|
359
|
+
const vc = new VitessceConfig({
|
|
360
|
+
schemaVersion: '1.0.15',
|
|
361
|
+
name: 'An automatically generated config. Adjust values and add layout components if needed.',
|
|
362
|
+
description: 'Populate with text relevant to this visualisation.',
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
const allViews = [];
|
|
366
|
+
|
|
367
|
+
fileUrls.forEach((url) => {
|
|
368
|
+
allViews.push(generateConfig(url, vc));
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
return Promise.all(allViews).then((views) => {
|
|
372
|
+
const flattenedViews = views.flat();
|
|
373
|
+
|
|
374
|
+
const coord = calculateCoordinates(flattenedViews.length);
|
|
375
|
+
|
|
376
|
+
for (let i = 0; i < flattenedViews.length; i++) {
|
|
377
|
+
flattenedViews[i].setXYWH(...coord[i]);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return vc.toJSON();
|
|
381
|
+
});
|
|
382
|
+
}
|