@@ -3,10 +3,10 @@ import { useEffect, useState } from 'react';
33import { createSourceData } from '@hms-dbmi/vizarr/src/io' ;
44import {
55 isBioformats2rawlayout ,
6- guessZarrVersion ,
7- isOmePlate ,
86 isMultiscales ,
97 coordinateTransformationsToMatrix ,
8+ guessZarrVersion ,
9+ isOmePlate ,
1010} from '@hms-dbmi/vizarr/src/utils' ;
1111import { 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+
2445const 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