@vitessce/config 3.6.3 → 3.6.4
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-tsc/generate-config-anndata.d.ts +8 -0
- package/dist-tsc/generate-config-anndata.d.ts.map +1 -0
- package/dist-tsc/generate-config-anndata.js +66 -0
- package/dist-tsc/generate-config-helpers.d.ts +9 -0
- package/dist-tsc/generate-config-helpers.d.ts.map +1 -0
- package/dist-tsc/generate-config-helpers.js +17 -0
- package/dist-tsc/generate-config-ome.d.ts +4 -0
- package/dist-tsc/generate-config-ome.d.ts.map +1 -0
- package/dist-tsc/generate-config-ome.js +19 -0
- package/dist-tsc/generate-config-spatialdata.d.ts +5 -0
- package/dist-tsc/generate-config-spatialdata.d.ts.map +1 -0
- package/dist-tsc/generate-config-spatialdata.js +87 -0
- package/dist-tsc/generate-config.d.ts +41 -0
- package/dist-tsc/generate-config.d.ts.map +1 -0
- package/dist-tsc/generate-config.js +241 -0
- package/dist-tsc/generate-config.test.d.ts +2 -0
- package/dist-tsc/generate-config.test.d.ts.map +1 -0
- package/dist-tsc/generate-config.test.js +258 -0
- package/dist-tsc/json-fixtures/mouse_liver.anndata.json +274 -0
- package/dist-tsc/json-fixtures/mouse_liver.labels.ome.json +50 -0
- package/dist-tsc/json-fixtures/mouse_liver.ome.json +50 -0
- package/dist-tsc/json-fixtures/mouse_liver.spatialdata.json +374 -0
- package/package.json +6 -4
- package/src/generate-config-anndata.js +75 -0
- package/src/generate-config-helpers.js +19 -0
- package/src/generate-config-ome.js +21 -0
- package/src/generate-config-spatialdata.js +101 -0
- package/src/generate-config.js +265 -0
- package/src/generate-config.test.js +274 -0
- package/src/json-fixtures/mouse_liver.anndata.json +274 -0
- package/src/json-fixtures/mouse_liver.labels.ome.json +50 -0
- package/src/json-fixtures/mouse_liver.ome.json +50 -0
- package/src/json-fixtures/mouse_liver.spatialdata.json +374 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-config-anndata.d.ts","sourceRoot":"","sources":["../src/generate-config-anndata.js"],"names":[],"mappings":"AAGA;IACE;;;MAsDC;CAgBF;mCAzEkC,8BAA8B"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
import { AbstractAutoConfig } from './generate-config-helpers.js';
|
|
3
|
+
export class AnnDataAutoConfig extends AbstractAutoConfig {
|
|
4
|
+
getOptions() {
|
|
5
|
+
const { zmetadata } = this;
|
|
6
|
+
const options = {
|
|
7
|
+
obsEmbedding: [],
|
|
8
|
+
obsSets: [],
|
|
9
|
+
};
|
|
10
|
+
zmetadata.forEach(({ path, attrs }) => {
|
|
11
|
+
const lowerPath = path.toLowerCase();
|
|
12
|
+
const relPath = path.substring(1);
|
|
13
|
+
// Gene expression matrix.
|
|
14
|
+
if (['/x'].includes(lowerPath)) {
|
|
15
|
+
options.obsFeatureMatrix = {
|
|
16
|
+
path: relPath,
|
|
17
|
+
// TODO: Also check the shape of X.
|
|
18
|
+
// If X is very large, try to initialize initial-filtering properties
|
|
19
|
+
// (will require that /var contains a boolean column however.)
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
// Spatial coordinates.
|
|
23
|
+
if (['/obsm/x_spatial', '/obsm/spatial'].includes(lowerPath)) {
|
|
24
|
+
// TODO: use obsSpots instead of obsLocations here?
|
|
25
|
+
options.obsLocations = {
|
|
26
|
+
path: relPath,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// Embedding arrays.
|
|
30
|
+
if (['/obsm/x_umap', '/obsm/umap'].includes(lowerPath)) {
|
|
31
|
+
options.obsEmbedding.push({ path: relPath, embeddingType: 'UMAP' });
|
|
32
|
+
}
|
|
33
|
+
if (['/obsm/x_tsne', '/obsm/tsne'].includes(lowerPath)) {
|
|
34
|
+
options.obsEmbedding.push({ path: relPath, embeddingType: 't-SNE' });
|
|
35
|
+
}
|
|
36
|
+
if (['/obsm/x_pca', '/obsm/pca'].includes(lowerPath)) {
|
|
37
|
+
options.obsEmbedding.push({ path: relPath, embeddingType: 'PCA' });
|
|
38
|
+
}
|
|
39
|
+
// Cell set columns.
|
|
40
|
+
// TODO: use all categorical/string columns of obs instead of this fixed set?
|
|
41
|
+
const supportedObsSetsPaths = [
|
|
42
|
+
'cluster', 'clusters', 'subcluster', 'cell_type', 'celltype',
|
|
43
|
+
'leiden', 'louvain', 'disease', 'organism', 'self_reported_ethnicity',
|
|
44
|
+
'tissue', 'sex',
|
|
45
|
+
].map(colname => `/obs/${colname}`);
|
|
46
|
+
if (supportedObsSetsPaths.includes(lowerPath)) {
|
|
47
|
+
const name = relPath.split('/').at(-1);
|
|
48
|
+
options.obsSets.push({ path: relPath, name });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
return options;
|
|
52
|
+
}
|
|
53
|
+
addFiles(vc, dataset) {
|
|
54
|
+
const { url, fileType } = this;
|
|
55
|
+
dataset.addFile({
|
|
56
|
+
url,
|
|
57
|
+
fileType,
|
|
58
|
+
options: this.getOptions(),
|
|
59
|
+
// TODO: coordination values?
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
// eslint-disable-next-line class-methods-use-this
|
|
63
|
+
addViews(vc, layoutOption) {
|
|
64
|
+
// TODO
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-config-helpers.d.ts","sourceRoot":"","sources":["../src/generate-config-helpers.js"],"names":[],"mappings":"AACA;IACE,8BAKC;IAHC,SAAc;IACd,cAAwB;IACxB,eAA0B;IAI5B,sCAEC;IAGD,2CAEC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
export class AbstractAutoConfig {
|
|
3
|
+
constructor(parsedStore) {
|
|
4
|
+
const { url, fileType, zmetadata } = parsedStore;
|
|
5
|
+
this.url = url;
|
|
6
|
+
this.fileType = fileType;
|
|
7
|
+
this.zmetadata = zmetadata;
|
|
8
|
+
}
|
|
9
|
+
// eslint-disable-next-line class-methods-use-this
|
|
10
|
+
addFiles(vc, dataset) {
|
|
11
|
+
throw new Error('The addFiles() method has not been implemented.');
|
|
12
|
+
}
|
|
13
|
+
// eslint-disable-next-line class-methods-use-this
|
|
14
|
+
addViews(vc, layoutOption) {
|
|
15
|
+
throw new Error('The addViews() method has not been implemented.');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-config-ome.d.ts","sourceRoot":"","sources":["../src/generate-config-ome.js"],"names":[],"mappings":"AAKA;CAeC;mCAnBkC,8BAA8B"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
import { AbstractAutoConfig } from './generate-config-helpers.js';
|
|
3
|
+
// TODO: split into separate classes for OME-TIFF and OME-Zarr?
|
|
4
|
+
// TODO: split into separate classes for image and obsSegmentations?
|
|
5
|
+
export class OmeAutoConfig extends AbstractAutoConfig {
|
|
6
|
+
addFiles(vc, dataset) {
|
|
7
|
+
const { url, fileType } = this;
|
|
8
|
+
dataset.addFile({
|
|
9
|
+
url,
|
|
10
|
+
fileType,
|
|
11
|
+
// TODO: options?
|
|
12
|
+
// TODO: coordination values?
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
// eslint-disable-next-line class-methods-use-this
|
|
16
|
+
addViews(vc, layoutOption) {
|
|
17
|
+
// TODO
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-config-spatialdata.d.ts","sourceRoot":"","sources":["../src/generate-config-spatialdata.js"],"names":[],"mappings":"AAGA;IACE,iBAgFC;CAgBF;mCAnGkC,8BAA8B"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
import { AbstractAutoConfig } from './generate-config-helpers.js';
|
|
3
|
+
export class SpatialDataAutoConfig extends AbstractAutoConfig {
|
|
4
|
+
getOptions() {
|
|
5
|
+
const { zmetadata } = this;
|
|
6
|
+
const options = {};
|
|
7
|
+
const availableElements = zmetadata.filter(({ path }) => {
|
|
8
|
+
const relPath = path.substring(1);
|
|
9
|
+
return relPath.match(/^(tables|table|images|labels|shapes|points)\/([^/]*)$/);
|
|
10
|
+
});
|
|
11
|
+
availableElements.forEach(({ path, attrs }) => {
|
|
12
|
+
const relPath = path.substring(1);
|
|
13
|
+
const firstCoordinateSystem = attrs
|
|
14
|
+
?.multiscales?.[0]
|
|
15
|
+
?.coordinateTransformations?.[0]
|
|
16
|
+
?.output?.name;
|
|
17
|
+
// Handle image elements.
|
|
18
|
+
if (relPath.match(/^(images)\/([^/]*)$/)) {
|
|
19
|
+
options.image = {
|
|
20
|
+
path: relPath,
|
|
21
|
+
coordinateSystem: firstCoordinateSystem,
|
|
22
|
+
// TODO: support a fileUid property in the schema?
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// Handle labels elements.
|
|
26
|
+
if (relPath.match(/^(labels)\/([^/]*)$/)) {
|
|
27
|
+
options.labels = {
|
|
28
|
+
path: relPath,
|
|
29
|
+
coordinateSystem: firstCoordinateSystem,
|
|
30
|
+
// TODO: support a fileUid property in the schema?
|
|
31
|
+
};
|
|
32
|
+
// TODO: check which table annotates these labels.
|
|
33
|
+
}
|
|
34
|
+
// Handle shapes elements.
|
|
35
|
+
if (relPath.match(/^(shapes)\/([^/]*)$/)) {
|
|
36
|
+
// TODO: check if shapes are circles or polygons
|
|
37
|
+
// to determine which Vitessce data type to use.
|
|
38
|
+
options.obsSpots = {
|
|
39
|
+
path: relPath,
|
|
40
|
+
coordinateSystem: firstCoordinateSystem,
|
|
41
|
+
};
|
|
42
|
+
// TODO: check which table annotates these shapes.
|
|
43
|
+
}
|
|
44
|
+
// Handle table elements.
|
|
45
|
+
if (relPath.match(/^(tables|table)\/([^/]*)$/)) {
|
|
46
|
+
// Identify all sub-paths within this table element.
|
|
47
|
+
const tableEls = zmetadata.filter(({ path: subpath }) => subpath.startsWith(path));
|
|
48
|
+
// Check if the table contains an X array.
|
|
49
|
+
const hasX = tableEls.find(el => el.path === `${path}/X`);
|
|
50
|
+
if (hasX) {
|
|
51
|
+
options.obsFeatureMatrix = {
|
|
52
|
+
path: hasX.path.substring(1),
|
|
53
|
+
// region: null,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
// Check if the table contains an obs dataframe.
|
|
57
|
+
const hasObs = tableEls.find(el => el.path === `${path}/obs`);
|
|
58
|
+
if (hasObs) {
|
|
59
|
+
const columnOrder = hasObs.attrs?.['column-order'];
|
|
60
|
+
// Use the columns of this dataframe to configure the obsSets.
|
|
61
|
+
options.obsSets = {
|
|
62
|
+
// region: null,
|
|
63
|
+
tablePath: relPath,
|
|
64
|
+
obsSets: columnOrder.map(c => ({
|
|
65
|
+
path: `${hasObs.path.substring(1)}/${c}`,
|
|
66
|
+
name: c,
|
|
67
|
+
})),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return options;
|
|
73
|
+
}
|
|
74
|
+
addFiles(vc, dataset) {
|
|
75
|
+
const { url, fileType } = this;
|
|
76
|
+
dataset.addFile({
|
|
77
|
+
url,
|
|
78
|
+
fileType,
|
|
79
|
+
options: this.getOptions(),
|
|
80
|
+
// TODO: coordination values?
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// eslint-disable-next-line class-methods-use-this
|
|
84
|
+
addViews(vc, layoutOption) {
|
|
85
|
+
// TODO
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @param {string} s A single string, like this
|
|
4
|
+
* `http://example.com/my_zarr.zarr#anndata.zarr;
|
|
5
|
+
* http://example.com/my_tiff.ome.tif`
|
|
6
|
+
* @returns {{ url: string, fileType: string}[]} The URLs with file types.
|
|
7
|
+
*/
|
|
8
|
+
export function parseUrls(s: string): {
|
|
9
|
+
url: string;
|
|
10
|
+
fileType: string;
|
|
11
|
+
}[];
|
|
12
|
+
export function parsedUrlToZmetadata(parsedUrl: any): Promise<{
|
|
13
|
+
path: string;
|
|
14
|
+
kind: string;
|
|
15
|
+
attrs: import("zarrita").Attributes;
|
|
16
|
+
}[] | null>;
|
|
17
|
+
/**
|
|
18
|
+
*
|
|
19
|
+
* @param {{ url, fileType, store }[]} parsedUrls
|
|
20
|
+
* @return {string[]} The layoutOptions.
|
|
21
|
+
*/
|
|
22
|
+
export function parsedUrlsToLayoutOptions(parsedUrls: {
|
|
23
|
+
url: any;
|
|
24
|
+
fileType: any;
|
|
25
|
+
store: any;
|
|
26
|
+
}[]): string[];
|
|
27
|
+
/**
|
|
28
|
+
*
|
|
29
|
+
* @param {{ url, fileType, store }[]} parsedUrls
|
|
30
|
+
* @param {string|null} layoutOption
|
|
31
|
+
*/
|
|
32
|
+
export function generateConfig(parsedUrls: {
|
|
33
|
+
url: any;
|
|
34
|
+
fileType: any;
|
|
35
|
+
store: any;
|
|
36
|
+
}[], layoutOption?: string | null): Promise<{
|
|
37
|
+
config: VitessceConfig;
|
|
38
|
+
stores: any;
|
|
39
|
+
}>;
|
|
40
|
+
import { VitessceConfig } from './VitessceConfig.js';
|
|
41
|
+
//# sourceMappingURL=generate-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-config.d.ts","sourceRoot":"","sources":["../src/generate-config.js"],"names":[],"mappings":"AAgGA;;;;;;GAMG;AACH,6BALW,MAAM,GAGJ;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAC,EAAE,CAqB9C;AAGD;;;;YAsEC;AAED;;;;GAIG;AACH,sDAHW;IAAE,GAAG,MAAC;IAAC,QAAQ,MAAC;IAAC,KAAK,MAAA;CAAE,EAAE,GACzB,MAAM,EAAE,CAOnB;AAED;;;;GAIG;AACH,2CAHW;IAAE,GAAG,MAAC;IAAC,QAAQ,MAAC;IAAC,KAAK,MAAA;CAAE,EAAE,iBAC1B,MAAM,GAAC,IAAI;;;GAoDrB;+BArQ8B,qBAAqB"}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
// TODO: ts-check
|
|
2
|
+
import { FileType } from '@vitessce/constants-internal';
|
|
3
|
+
import { withConsolidated, FetchStore, ZipFileStore, open as zarrOpen, root as zarrRoot } from 'zarrita';
|
|
4
|
+
import { VitessceConfig } from './VitessceConfig.js';
|
|
5
|
+
// Classes for different types of objects
|
|
6
|
+
import { AnnDataAutoConfig } from './generate-config-anndata.js';
|
|
7
|
+
import { SpatialDataAutoConfig } from './generate-config-spatialdata.js';
|
|
8
|
+
import { OmeAutoConfig } from './generate-config-ome.js';
|
|
9
|
+
const fileTypeToExtensions = {
|
|
10
|
+
[FileType.IMAGE_OME_TIFF]: ['.ome.tif', '.ome.tiff', '.ome.tf2', '.ome.tf8'],
|
|
11
|
+
[FileType.IMAGE_OME_ZARR]: ['.ome.zarr'],
|
|
12
|
+
[FileType.IMAGE_OME_ZARR_ZIP]: ['.ome.zarr.zip'],
|
|
13
|
+
[FileType.ANNDATA_ZARR]: ['.ad.zarr', '.h5ad.zarr', '.adata.zarr', '.anndata.zarr'],
|
|
14
|
+
[FileType.ANNDATA_ZARR_ZIP]: ['.ad.zarr.zip', '.h5ad.zarr.zip', '.adata.zarr.zip', '.anndata.zarr.zip'],
|
|
15
|
+
// TODO: how to handle h5ad-based AnnData (since needs reference JSON file).
|
|
16
|
+
// Perhaps just assume one H5AD+one JSON (or .ref.json) file correspond to each other?
|
|
17
|
+
[FileType.SPATIALDATA_ZARR]: ['.sd.zarr', '.sdata.zarr', '.spatialdata.zarr'],
|
|
18
|
+
[FileType.SPATIALDATA_ZARR_ZIP]: ['.sd.zarr.zip', '.sdata.zarr.zip', '.spatialdata.zarr.zip'],
|
|
19
|
+
};
|
|
20
|
+
const fileTypeToClass = {
|
|
21
|
+
// OME-TIFF
|
|
22
|
+
[FileType.IMAGE_OME_TIFF]: OmeAutoConfig,
|
|
23
|
+
[FileType.OBS_SEGMENTATIONS_OME_TIFF]: OmeAutoConfig,
|
|
24
|
+
// OME-Zarr
|
|
25
|
+
[FileType.IMAGE_OME_ZARR]: OmeAutoConfig,
|
|
26
|
+
[FileType.IMAGE_OME_ZARR_ZIP]: OmeAutoConfig,
|
|
27
|
+
[FileType.OBS_SEGMENTATIONS_OME_ZARR]: OmeAutoConfig,
|
|
28
|
+
[FileType.OBS_SEGMENTATIONS_OME_ZARR_ZIP]: OmeAutoConfig,
|
|
29
|
+
// AnnData
|
|
30
|
+
[FileType.ANNDATA_ZARR]: AnnDataAutoConfig,
|
|
31
|
+
[FileType.ANNDATA_ZARR_ZIP]: AnnDataAutoConfig,
|
|
32
|
+
// SpatialData
|
|
33
|
+
[FileType.SPATIALDATA_ZARR]: SpatialDataAutoConfig,
|
|
34
|
+
[FileType.SPATIALDATA_ZARR_ZIP]: SpatialDataAutoConfig,
|
|
35
|
+
};
|
|
36
|
+
// This list contains file types that can be mapped to a regular Zarr store
|
|
37
|
+
// (e.g., FetchStore or ZipStore).
|
|
38
|
+
const ZARR_FILETYPES = [
|
|
39
|
+
FileType.ANNDATA_ZARR,
|
|
40
|
+
FileType.ANNDATA_ZARR_ZIP,
|
|
41
|
+
FileType.SPATIALDATA_ZARR,
|
|
42
|
+
FileType.SPATIALDATA_ZARR_ZIP,
|
|
43
|
+
FileType.IMAGE_OME_ZARR,
|
|
44
|
+
FileType.IMAGE_OME_ZARR_ZIP,
|
|
45
|
+
FileType.OBS_SEGMENTATIONS_OME_ZARR,
|
|
46
|
+
FileType.OBS_SEGMENTATIONS_OME_ZARR_ZIP,
|
|
47
|
+
];
|
|
48
|
+
function urlToFileType(url) {
|
|
49
|
+
const match = Object.entries(fileTypeToExtensions).find(
|
|
50
|
+
// eslint-disable-next-line no-unused-vars
|
|
51
|
+
([fileType, extensions]) => extensions.some(ext => url.endsWith(ext)));
|
|
52
|
+
if (match) {
|
|
53
|
+
return match[0];
|
|
54
|
+
}
|
|
55
|
+
throw new Error('The file extension contained in the URL did not map to a supported fileType.');
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
*
|
|
59
|
+
* @param {{ fileType, url }} parsedUrl
|
|
60
|
+
* @returns {Readable}
|
|
61
|
+
*/
|
|
62
|
+
function getStore(parsedUrl) {
|
|
63
|
+
const { fileType, url } = parsedUrl;
|
|
64
|
+
if (!ZARR_FILETYPES.includes(fileType)) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return fileType.endsWith('.zip')
|
|
68
|
+
? ZipFileStore.fromUrl(url)
|
|
69
|
+
: new FetchStore(url);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Ensure that each object { url, fileType, [store] }
|
|
73
|
+
* contains a `store`.
|
|
74
|
+
* @param {object[]} parsedUrls
|
|
75
|
+
* @returns {object[]}
|
|
76
|
+
*/
|
|
77
|
+
function ensureStores(parsedUrls) {
|
|
78
|
+
return parsedUrls.map((parsedUrl) => {
|
|
79
|
+
if (parsedUrl.store) {
|
|
80
|
+
return parsedUrl;
|
|
81
|
+
}
|
|
82
|
+
const store = getStore(parsedUrl);
|
|
83
|
+
return {
|
|
84
|
+
...parsedUrl,
|
|
85
|
+
store,
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
*
|
|
91
|
+
* @param {string} s A single string, like this
|
|
92
|
+
* `http://example.com/my_zarr.zarr#anndata.zarr;
|
|
93
|
+
* http://example.com/my_tiff.ome.tif`
|
|
94
|
+
* @returns {{ url: string, fileType: string}[]} The URLs with file types.
|
|
95
|
+
*/
|
|
96
|
+
export function parseUrls(s) {
|
|
97
|
+
const urlsWithHashes = s.split(';');
|
|
98
|
+
return urlsWithHashes.map((urlWithHash) => {
|
|
99
|
+
const parts = urlWithHash.split('#');
|
|
100
|
+
if (parts.length === 1) {
|
|
101
|
+
const [url] = parts;
|
|
102
|
+
return {
|
|
103
|
+
url,
|
|
104
|
+
fileType: urlToFileType(url),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (parts.length === 2) {
|
|
108
|
+
const [url, fileType] = parts;
|
|
109
|
+
return {
|
|
110
|
+
url,
|
|
111
|
+
fileType,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
throw new Error('Only expected zero or one # character per URL, but received more.');
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
export async function parsedUrlToZmetadata(parsedUrl) {
|
|
118
|
+
const { store: initialStore } = parsedUrl;
|
|
119
|
+
if (!initialStore) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
let store;
|
|
123
|
+
let promises = [];
|
|
124
|
+
try {
|
|
125
|
+
try {
|
|
126
|
+
store = await withConsolidated(initialStore);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// Try again with `zmetadata` rather than `.zmetadata`.
|
|
130
|
+
// Reference: https://github.com/zarr-developers/zarr-python/issues/1121
|
|
131
|
+
store = await withConsolidated(initialStore, { metadataKey: 'zmetadata' });
|
|
132
|
+
}
|
|
133
|
+
// Is consolidated.
|
|
134
|
+
const contents = store.contents();
|
|
135
|
+
promises = contents.map(async (value) => {
|
|
136
|
+
const item = await zarrOpen(zarrRoot(store).resolve(value.path));
|
|
137
|
+
return {
|
|
138
|
+
...value,
|
|
139
|
+
attrs: item.attrs,
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
store = initialStore;
|
|
145
|
+
// Is not consolidated.
|
|
146
|
+
const keysToTry = [
|
|
147
|
+
// Note: OME-NGFF metadata is stored in the root attrs.
|
|
148
|
+
'/',
|
|
149
|
+
// AnnData keys
|
|
150
|
+
'/X',
|
|
151
|
+
'/layers',
|
|
152
|
+
'/obs',
|
|
153
|
+
'/var',
|
|
154
|
+
'/obsm',
|
|
155
|
+
'/obsm/spatial',
|
|
156
|
+
'/obsm/X_spatial',
|
|
157
|
+
'/obsm/pca',
|
|
158
|
+
'/obsm/X_pca',
|
|
159
|
+
'/obsm/tsne',
|
|
160
|
+
'/obsm/X_tsne',
|
|
161
|
+
'/obsm/umap',
|
|
162
|
+
'/obsm/X_umap',
|
|
163
|
+
// TODO: second round of getting metadata for
|
|
164
|
+
// columns listed in /obs and /var .attrs['column-order'] ?
|
|
165
|
+
// SpatialData keys
|
|
166
|
+
// Note: For spatialData, we assume the store is always consolidated.
|
|
167
|
+
// TODO: throw error if spatialdata + not consolidated?
|
|
168
|
+
];
|
|
169
|
+
promises = keysToTry.map(async (k) => {
|
|
170
|
+
try {
|
|
171
|
+
const item = await zarrOpen(zarrRoot(store).resolve(k));
|
|
172
|
+
return {
|
|
173
|
+
path: k,
|
|
174
|
+
kind: item.kind,
|
|
175
|
+
attrs: item.attrs,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
return (await Promise.all(promises))
|
|
184
|
+
.filter(entry => entry !== null);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
*
|
|
188
|
+
* @param {{ url, fileType, store }[]} parsedUrls
|
|
189
|
+
* @return {string[]} The layoutOptions.
|
|
190
|
+
*/
|
|
191
|
+
export function parsedUrlsToLayoutOptions(parsedUrls) {
|
|
192
|
+
// eslint-disable-next-line no-unused-vars
|
|
193
|
+
const parsedStores = ensureStores(parsedUrls);
|
|
194
|
+
// TODO: implement
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
*
|
|
198
|
+
* @param {{ url, fileType, store }[]} parsedUrls
|
|
199
|
+
* @param {string|null} layoutOption
|
|
200
|
+
*/
|
|
201
|
+
export async function generateConfig(parsedUrls, layoutOption = null) {
|
|
202
|
+
// Map each URL to a Zarr store.
|
|
203
|
+
const parsedStores = ensureStores(parsedUrls);
|
|
204
|
+
// Obtain Zarr consolidated_metadata for each store.
|
|
205
|
+
const zmetadataStores = await Promise.all(parsedStores.map(async (parsedStore) => ({
|
|
206
|
+
...parsedStore,
|
|
207
|
+
zmetadata: await parsedUrlToZmetadata(parsedStore),
|
|
208
|
+
})));
|
|
209
|
+
// Create configuration instance.
|
|
210
|
+
const vc = new VitessceConfig({
|
|
211
|
+
schemaVersion: '1.0.17',
|
|
212
|
+
name: 'Automatically-generated configuration.',
|
|
213
|
+
// TODO: write a description based on what is known
|
|
214
|
+
// (fileType(s) and maybe layoutOption).
|
|
215
|
+
description: 'Populate with a description of this visualization.',
|
|
216
|
+
});
|
|
217
|
+
// Create datasets.
|
|
218
|
+
// TODO: cases in which more than one dataset should be created?
|
|
219
|
+
const dataset = vc.addDataset('Main dataset');
|
|
220
|
+
zmetadataStores.forEach((parsedStore) => {
|
|
221
|
+
const { fileType } = parsedStore;
|
|
222
|
+
const AutoConfigClass = fileTypeToClass[fileType];
|
|
223
|
+
const autoConfig = new AutoConfigClass(parsedStore);
|
|
224
|
+
autoConfig.addFiles(vc, dataset);
|
|
225
|
+
// TODO: add all files, then add all views (in two separate loops)?
|
|
226
|
+
autoConfig.addViews(vc, layoutOption);
|
|
227
|
+
});
|
|
228
|
+
const stores = Object.fromEntries(
|
|
229
|
+
// Here, we use `parsedUrls` rather than `parsedStores`
|
|
230
|
+
// so that we do not provide more stores than intended
|
|
231
|
+
// (i.e., we do not provide stores which were solely created
|
|
232
|
+
// to obtain zmetadata).
|
|
233
|
+
parsedUrls
|
|
234
|
+
.filter(d => d.store)
|
|
235
|
+
.map(d => ([d.url, d.store])));
|
|
236
|
+
// Return both the config and the `stores` url-to-store dict.
|
|
237
|
+
return {
|
|
238
|
+
config: vc,
|
|
239
|
+
stores,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-config.test.d.ts","sourceRoot":"","sources":["../src/generate-config.test.js"],"names":[],"mappings":""}
|