Skip to content

Commit 726a919

Browse files
authored
Merge pull request #35 from will-moore/bioformats2raw_all_images
show all images for bioformats2raw
2 parents 1178401 + 2afbe66 commit 726a919

File tree

1 file changed

+105
-96
lines changed

1 file changed

+105
-96
lines changed

viewer/src/hooks.js

Lines changed: 105 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { useEffect, useState } from 'react';
33
import { createSourceData } from '@hms-dbmi/vizarr/src/io';
44
import {
55
isBioformats2rawlayout,
6-
guessZarrVersion,
7-
isOmePlate,
86
isMultiscales,
97
coordinateTransformationsToMatrix,
8+
guessZarrVersion,
9+
isOmePlate,
1010
} from '@hms-dbmi/vizarr/src/utils';
1111
import { FetchStore, open } from 'zarrita';
1212

@@ -21,6 +21,27 @@ import {
2121
resolveOmeLabelsFromMultiscales,
2222
} from './utils';
2323

24+
const getPhysicalSizes = (attrs) => {
25+
if (isMultiscales(attrs)) {
26+
const axes = getNgffAxes(attrs.multiscales);
27+
const ct = coordinateTransformationsToMatrix(attrs.multiscales);
28+
const matrixIndices = {
29+
x: 0,
30+
y: 5,
31+
z: 10,
32+
};
33+
const physicalSizes = axes
34+
.filter((a) => a.type === 'space')
35+
.reduce((acc, a) => {
36+
acc[a.name] = { size: ct[matrixIndices[a.name]], unit: a.unit };
37+
return acc;
38+
}, {});
39+
// @TODO: get t size from multiscales.coordinateTransformations if axis is present
40+
return physicalSizes;
41+
}
42+
return null;
43+
};
44+
2445
const fetchSourceData = async (config) => {
2546
try {
2647
const base = config.source;
@@ -37,13 +58,13 @@ const fetchSourceData = async (config) => {
3758
const zarrJson = zarrVersion === 3 ? await getZarrJson(base) : null;
3859
let ome = zarrJson?.attributes?.ome || node.attrs?.OME || null;
3960

40-
let sourceData;
4161
if (
4262
!isBioformats2rawlayout(ome || node.attrs) ||
4363
isOmePlate(ome || node.attrs) // if plate is present it takes precedence (https://ngff.openmicroscopy.org/0.4/#bf2raw-attributes)
4464
) {
4565
// use Vizarr's createSourceData with source as is
4666

67+
let sourceData;
4768
if (ome?.version === '0.5') {
4869
sourceData = await createSourceData(config);
4970
const labels = await resolveOmeLabelsFromMultiscales(node);
@@ -53,107 +74,95 @@ const fetchSourceData = async (config) => {
5374
} else {
5475
sourceData = await createSourceData(config);
5576
}
56-
} else {
57-
// load bioformats2raw.layout
58-
// https://ngff.openmicroscopy.org/0.4/#bf2raw
59-
60-
// get b2f metadata from ome key in metadata or node attributes
61-
const b2fl =
62-
ome?.['bioformats2raw.layout'] || node.attrs?.['bioformats2raw.layout'];
63-
if (b2fl !== 3) {
64-
throw new Error('Unsupported bioformats2raw layout');
77+
const physicalSizes = getPhysicalSizes(ome || node.attrs);
78+
if (physicalSizes) {
79+
sourceData.loader[0].meta = {
80+
...sourceData.loader[0].meta,
81+
physicalSizes,
82+
};
6583
}
84+
return [sourceData];
85+
}
86+
// load bioformats2raw.layout
87+
// https://ngff.openmicroscopy.org/0.4/#bf2raw
88+
89+
// get b2f metadata from ome key in metadata or node attributes
90+
const b2fl =
91+
ome?.['bioformats2raw.layout'] || node.attrs?.['bioformats2raw.layout'];
92+
if (b2fl !== 3) {
93+
throw new Error('Unsupported bioformats2raw layout');
94+
}
6695

67-
// Try to load .zmetadata if present
68-
const metadata = await getZarrMetadata(base);
96+
// Try to load .zmetadata if present
97+
const metadata = await getZarrMetadata(base);
6998

70-
// Try to load OME group at root if present and not in v3 zarr metadata
71-
if (!ome) {
72-
try {
73-
ome = await open(node.resolve('OME'), { kind: 'group' });
74-
} catch {}
75-
}
99+
// Try to load OME group at root if present and not in v3 zarr metadata
100+
if (!ome) {
101+
try {
102+
ome = await open(node.resolve('OME'), { kind: 'group' });
103+
} catch {}
104+
}
76105

77-
// Try to load OME XML file if present
78-
const omeXmlDom = await getXmlDom(base);
79-
let omeXml = omeXmlDom ? parseXml(omeXmlDom) : null;
106+
// Try to load OME XML file if present
107+
const omeXmlDom = await getXmlDom(base);
108+
let omeXml = omeXmlDom ? parseXml(omeXmlDom) : null;
80109

81-
let series;
82-
if (ome?.series) {
83-
series = ome.series;
84-
} else if (ome?.attrs?.series) {
85-
series = ome.attrs.series;
110+
let series;
111+
if (ome?.series) {
112+
series = ome.series;
113+
} else if (ome?.attrs?.series) {
114+
series = ome.attrs.series;
115+
} else {
116+
// https://ngff.openmicroscopy.org/0.4/#bf2raw-details
117+
if (metadata) {
118+
const multiscaleKeys = Object.keys(metadata).filter(
119+
(key) => key.endsWith('/.zattrs') && 'multiscales' in metadata[key],
120+
);
121+
series = multiscaleKeys.map((key) => key.split('/')[0]);
122+
} else if (omeXml) {
123+
series = omeXml.images.map((image) => image.path);
86124
} else {
87-
// https://ngff.openmicroscopy.org/0.4/#bf2raw-details
88-
if (metadata) {
89-
const multiscaleKeys = Object.keys(metadata).filter(
90-
(key) => key.endsWith('/.zattrs') && 'multiscales' in metadata[key],
91-
);
92-
series = multiscaleKeys.map((key) => key.split('/')[0]);
93-
} else if (omeXml) {
94-
series = omeXml.images.map((image) => image.path);
95-
} else {
96-
console.warn(
97-
'No OME group, .zmetadata or xml file. Attempting to find series.',
98-
);
99-
series = await findSeries(base, node, zarrVersion);
100-
}
125+
console.warn(
126+
'No OME group, .zmetadata or xml file. Attempting to find series.',
127+
);
128+
series = await findSeries(base, node, zarrVersion);
101129
}
102-
103-
const seriesMd = await Promise.all(
104-
series?.map(async (s, index) => {
105-
const seriesNode = await open(node.resolve(s), {
106-
kind: 'group',
107-
});
108-
if (!seriesNode.attrs.multiscales?.[0].axes && omeXml) {
109-
// get axes from xml if not in metadata
110-
// "The specified dimension order is then reversed when creating Zarr arrays, e.g. XYCZT would become TZCYX in Zarr." (https://github.com/glencoesoftware/bioformats2raw/blob/85ef84db26ce1239dd71ef482b4f38f67e605491/README.md?plain=1#L293)
111-
// though multiscales metadata MUST have axes (https://ngff.openmicroscopy.org/0.4/#multiscale-md)
112-
const dimensionOrder = omeXml.images[index].dimensionOrder;
113-
return dimensionOrder
114-
? {
115-
channel_axis:
116-
dimensionOrder?.length - dimensionOrder?.indexOf('C') - 1,
117-
}
118-
: {};
119-
}
120-
return {};
121-
}),
122-
);
123-
124-
// @TODO: return all series
125-
const sIndex = 0;
126-
127-
const seriesUrl = `${base.replace(/\/?$/, '/')}${series?.[sIndex] || ''}`;
128-
sourceData = await createSourceData({
129-
...config,
130-
source: seriesUrl,
131-
...seriesMd[sIndex],
132-
});
133130
}
134131

135-
// @TODO: implement this in createSourceData
136-
// Get physical sizes and add them to loader.meta (or as another prop?)
137-
const attrs = ome || node.attrs;
138-
if (isMultiscales(attrs)) {
139-
const axes = getNgffAxes(attrs.multiscales);
140-
const ct = coordinateTransformationsToMatrix(attrs.multiscales);
141-
const matrixIndices = {
142-
x: 0,
143-
y: 5,
144-
z: 10,
145-
};
146-
const physicalSizes = axes
147-
.filter((a) => a.type === 'space')
148-
.reduce((acc, a) => {
149-
acc[a.name] = { size: ct[matrixIndices[a.name]], unit: a.unit };
150-
return acc;
151-
}, {});
152-
// @TODO: get t size from multiscales.coordinateTransformations if axis is present
153-
sourceData.loader[0].meta = { physicalSizes };
154-
}
155-
156-
return sourceData;
132+
// @TODO: get physicalSizes
133+
const seriesMd = await Promise.all(
134+
series?.map(async (s, index) => {
135+
const seriesNode = await open(node.resolve(s), {
136+
kind: 'group',
137+
});
138+
if (!seriesNode.attrs.multiscales?.[0].axes && omeXml) {
139+
// get axes from xml if not in metadata
140+
// "The specified dimension order is then reversed when creating Zarr arrays, e.g. XYCZT would become TZCYX in Zarr." (https://github.com/glencoesoftware/bioformats2raw/blob/85ef84db26ce1239dd71ef482b4f38f67e605491/README.md?plain=1#L293)
141+
// though multiscales metadata MUST have axes (https://ngff.openmicroscopy.org/0.4/#multiscale-md)
142+
const dimensionOrder = omeXml.images[index].dimensionOrder;
143+
return dimensionOrder
144+
? {
145+
channel_axis:
146+
dimensionOrder?.length - dimensionOrder?.indexOf('C') - 1,
147+
}
148+
: {};
149+
}
150+
return {};
151+
}),
152+
);
153+
154+
// return all series
155+
const seriesData = await Promise.all(
156+
series.map((s, sIndex) => {
157+
const seriesUrl = `${base.replace(/\/?$/, '/')}${series?.[sIndex] || ''}`;
158+
return createSourceData({
159+
...config,
160+
source: seriesUrl,
161+
...seriesMd[sIndex],
162+
});
163+
}),
164+
);
165+
return seriesData;
157166
} catch (err) {
158167
throw err;
159168
}
@@ -175,7 +184,7 @@ export const useSourceData = (configs) => {
175184

176185
results.forEach((res) => {
177186
if (res.status === 'fulfilled') {
178-
data.push(res.value);
187+
res.value.forEach((d) => data.push(d));
179188
errors.push(null);
180189
} else {
181190
data.push(null);

0 commit comments

Comments
 (0)