11/*
2- * Copyright (c) 2021 University Corporation for Atmospheric Research/Unidata
2+ * Copyright (c) 2021-2025 University Corporation for Atmospheric Research/Unidata
33 * See LICENSE for license information.
44 */
55
66package ucar .nc2 .iosp .zarr ;
77
8+ import static ucar .nc2 .constants .CDM .ARRAYDIMENSIONS ;
9+
810import com .fasterxml .jackson .databind .ObjectMapper ;
911
1012import ucar .ma2 .ArrayObject ;
@@ -34,6 +36,7 @@ public class ZarrHeader {
3436 private final RandomAccessDirectory rootRaf ;
3537 private final Group .Builder rootGroup ;
3638 private final String rootLocation ;
39+
3740 private static final ObjectMapper objectMapper = new ObjectMapper ();
3841
3942 public ZarrHeader (RandomAccessDirectory raf , Group .Builder rootGroup ) {
@@ -219,20 +222,25 @@ private void makeVariable(RandomAccessDirectoryItem item, long dataOffset, ZArra
219222
220223 for (Attribute attr : attrs ) {
221224 final String attrName = attr .getName ();
222- if ("_ARRAY_DIMENSIONS" .equals (attrName )) {
225+ if (ARRAYDIMENSIONS .equals (attrName )) {
223226 try {
224- final ArrayObject .D1 aod1 = (ArrayObject .D1 ) attr .getValues ();
225-
226- // getSize returns a long
227- final int aodSize = (int ) aod1 .getSize ();
228- dimNames = new String [aodSize ];
229-
230- for (int i = 0 ; i < aodSize ; ++i ) {
231- dimNames [i ] = (String ) aod1 .get (i );
227+ if (attr .getLength () == 1 && attr .getStringValue ().equals ("" )) {
228+ // scalar array without a named dimension
229+ logger .debug (" {} is a scalar array without a named dimension" , vname );
230+ } else {
231+ final ArrayObject .D1 aod1 = (ArrayObject .D1 ) attr .getValues ();
232+
233+ // getSize returns a long
234+ final int aodSize = (int ) aod1 .getSize ();
235+ dimNames = new String [aodSize ];
236+
237+ for (int i = 0 ; i < aodSize ; ++i ) {
238+ dimNames [i ] = (String ) aod1 .get (i );
239+ }
240+ hasNamedDimensions = true ;
232241 }
233- hasNamedDimensions = true ;
234242 } catch (final Exception exc ) {
235- logger .debug (" Could not extract _ARRAY_DIMENSIONS for {}, {}" , vname , exc .getMessage ());
243+ logger .debug (" Could not extract {} for {}, {}" , ARRAYDIMENSIONS , vname , exc .getMessage ());
236244 }
237245 }
238246 }
@@ -260,7 +268,8 @@ private void makeVariable(RandomAccessDirectoryItem item, long dataOffset, ZArra
260268 final Dimension .Builder dim = Dimension .builder (dname , shape [i ]);
261269 dim .setIsVariableLength (false );
262270 dim .setIsUnlimited (false );
263- dim .setIsShared (false );
271+ // if using named dimensions from _ARRAY_DIMENSIONS, mark the dimension as shared
272+ dim .setIsShared (hasNamedDimensions );
264273
265274 final Dimension dd = dim .build ();
266275
@@ -275,6 +284,9 @@ private void makeVariable(RandomAccessDirectoryItem item, long dataOffset, ZArra
275284 if (dd .getLength () != prevd .getLength ()) {
276285 throw new ZarrFormatException ("Named dimension " + dname + " seen with inconsistent lengths." );
277286 }
287+ // replace newly created dimension with the previously added dimension
288+ dims .remove (dd );
289+ dims .add (prevd );
278290 } else {
279291 logger .trace ("adding {} to group as a shared dimension" , dname );
280292 parentGroup .addDimension (dd );
@@ -327,7 +339,13 @@ private List<Attribute> makeAttributes(RandomAccessDirectoryItem item) {
327339 Attribute .Builder attr = Attribute .builder (key );
328340 Object val = attrMap .get (key );
329341 if (val instanceof Collection <?>) {
330- attr .setValues (Arrays .asList (((Collection ) val ).toArray ()), false );
342+ Collection <?> collection = (Collection <?>) val ;
343+ if (collection .isEmpty () && key .equals (ARRAYDIMENSIONS )) {
344+ // scalar array
345+ attr .setValues (Collections .singletonList ("" ), false );
346+ } else {
347+ attr .setValues (Arrays .asList (collection .toArray ()), false );
348+ }
331349 } else if (val instanceof Number ) {
332350 attr .setNumericValue ((Number ) val , false );
333351 } else {
@@ -354,7 +372,8 @@ private static int getChunkIndex(RandomAccessDirectoryItem item, ZArray zarray)
354372
355373 int nDims = zarray .getShape ().length ;
356374 // verify is data file, else return -1
357- String pattern = String .format ("([0-9]+%c){%d}[0-9]+" , zarray .getSeparator ().charAt (0 ), nDims - 1 );
375+ String pattern = String .format ("([0-9]+%c){%d}[0-9]+" , zarray .getSeparator ().charAt (0 ), nDims == 0 ? 0 : nDims - 1 );
376+
358377 if (!fileName .matches (pattern )) {
359378 return -1 ;
360379 }
@@ -363,14 +382,20 @@ private static int getChunkIndex(RandomAccessDirectoryItem item, ZArray zarray)
363382 String [] dims = fileName .split (String .format ("\\ %c" , zarray .getSeparator ().charAt (0 )));
364383 int [] subs = Arrays .stream (dims ).mapToInt (dim -> Integer .parseInt (dim )).toArray ();
365384
366- // get number of chunks in each dimension
367- int [] nChunks = new int [nDims ];
368- int [] shape = zarray .getShape ();
369- int [] chunkSize = zarray .getChunks ();
370- for (int i = 0 ; i < nDims ; i ++) {
371- nChunks [i ] = (int ) Math .ceil (shape [i ] / chunkSize [i ]);
385+ // find chunk number as a flat index
386+ if (nDims != 0 ) {
387+ // get number of chunks in each dimension
388+ int [] nChunks = new int [nDims ];
389+ int [] shape = zarray .getShape ();
390+ int [] chunkSize = zarray .getChunks ();
391+ for (int i = 0 ; i < nDims ; i ++) {
392+ nChunks [i ] = (int ) Math .ceil (shape [i ] / chunkSize [i ]);
393+ }
394+ return ZarrUtils .subscriptsToIndex (subs , nChunks );
395+ } else {
396+ // scalar array
397+ return 0 ;
372398 }
373- return ZarrUtils .subscriptsToIndex (subs , nChunks );
374399 }
375400
376401 /**
0 commit comments