Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: Zarr driver
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2021, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "zarr.h"
14 :
15 : #include <algorithm>
16 : #include <cassert>
17 : #include <limits>
18 : #include <map>
19 : #include <set>
20 :
21 : /************************************************************************/
22 : /* ZarrV3Group::Create() */
23 : /************************************************************************/
24 :
25 : std::shared_ptr<ZarrV3Group>
26 403 : ZarrV3Group::Create(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
27 : const std::string &osParentName, const std::string &osName,
28 : const std::string &osRootDirectoryName)
29 : {
30 : auto poGroup = std::shared_ptr<ZarrV3Group>(new ZarrV3Group(
31 403 : poSharedResource, osParentName, osName, osRootDirectoryName));
32 403 : poGroup->SetSelf(poGroup);
33 403 : return poGroup;
34 : }
35 :
36 : /************************************************************************/
37 : /* OpenZarrArray() */
38 : /************************************************************************/
39 :
40 162 : std::shared_ptr<ZarrArray> ZarrV3Group::OpenZarrArray(const std::string &osName,
41 : CSLConstList) const
42 : {
43 162 : if (!CheckValidAndErrorOutIfNot())
44 0 : return nullptr;
45 :
46 162 : auto oIter = m_oMapMDArrays.find(osName);
47 162 : if (oIter != m_oMapMDArrays.end())
48 28 : return oIter->second;
49 :
50 : const std::string osSubDir =
51 268 : CPLFormFilename(m_osDirectoryName.c_str(), osName.c_str(), nullptr);
52 : const std::string osZarrayFilename =
53 268 : CPLFormFilename(osSubDir.c_str(), "zarr.json", nullptr);
54 :
55 : VSIStatBufL sStat;
56 134 : if (VSIStatL(osZarrayFilename.c_str(), &sStat) == 0)
57 : {
58 256 : CPLJSONDocument oDoc;
59 128 : if (!oDoc.Load(osZarrayFilename))
60 0 : return nullptr;
61 256 : const auto oRoot = oDoc.GetRoot();
62 128 : return LoadArray(osName, osZarrayFilename, oRoot);
63 : }
64 :
65 6 : return nullptr;
66 : }
67 :
68 : /************************************************************************/
69 : /* ZarrV3Group::LoadAttributes() */
70 : /************************************************************************/
71 :
72 60 : void ZarrV3Group::LoadAttributes() const
73 : {
74 60 : if (m_bAttributesLoaded)
75 33 : return;
76 27 : m_bAttributesLoaded = true;
77 :
78 : const std::string osFilename =
79 27 : CPLFormFilename(m_osDirectoryName.c_str(), "zarr.json", nullptr);
80 :
81 : VSIStatBufL sStat;
82 27 : if (VSIStatL(osFilename.c_str(), &sStat) == 0)
83 : {
84 27 : CPLJSONDocument oDoc;
85 27 : if (!oDoc.Load(osFilename))
86 0 : return;
87 27 : auto oRoot = oDoc.GetRoot();
88 27 : m_oAttrGroup.Init(oRoot["attributes"], m_bUpdatable);
89 : }
90 : }
91 :
92 : /************************************************************************/
93 : /* ExploreDirectory() */
94 : /************************************************************************/
95 :
96 55 : void ZarrV3Group::ExploreDirectory() const
97 : {
98 55 : if (m_bDirectoryExplored)
99 0 : return;
100 55 : m_bDirectoryExplored = true;
101 :
102 55 : auto psDir = VSIOpenDir(m_osDirectoryName.c_str(), 0, nullptr);
103 55 : if (!psDir)
104 0 : return;
105 194 : while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
106 : {
107 139 : if (VSI_ISDIR(psEntry->nMode))
108 : {
109 : const std::string osSubDir = CPLFormFilename(
110 84 : m_osDirectoryName.c_str(), psEntry->pszName, nullptr);
111 : VSIStatBufL sStat;
112 : std::string osZarrJsonFilename =
113 84 : CPLFormFilename(osSubDir.c_str(), "zarr.json", nullptr);
114 84 : if (VSIStatL(osZarrJsonFilename.c_str(), &sStat) == 0)
115 : {
116 78 : CPLJSONDocument oDoc;
117 78 : if (oDoc.Load(osZarrJsonFilename.c_str()))
118 : {
119 78 : const auto oRoot = oDoc.GetRoot();
120 78 : if (oRoot.GetInteger("zarr_format") != 3)
121 : {
122 0 : CPLError(CE_Warning, CPLE_AppDefined,
123 : "Unhandled zarr_format value");
124 0 : continue;
125 : }
126 156 : const std::string osNodeType = oRoot.GetString("node_type");
127 78 : if (osNodeType == "array")
128 : {
129 50 : if (std::find(m_aosArrays.begin(), m_aosArrays.end(),
130 50 : psEntry->pszName) == m_aosArrays.end())
131 : {
132 49 : m_aosArrays.emplace_back(psEntry->pszName);
133 : }
134 : }
135 28 : else if (osNodeType == "group")
136 : {
137 28 : if (std::find(m_aosGroups.begin(), m_aosGroups.end(),
138 28 : psEntry->pszName) == m_aosGroups.end())
139 : {
140 28 : m_aosGroups.emplace_back(psEntry->pszName);
141 : }
142 : }
143 : else
144 : {
145 0 : CPLError(CE_Warning, CPLE_AppDefined,
146 : "Unhandled node_type value");
147 0 : continue;
148 : }
149 : }
150 : }
151 : else
152 : {
153 : // Implicit group
154 6 : if (std::find(m_aosGroups.begin(), m_aosGroups.end(),
155 6 : psEntry->pszName) == m_aosGroups.end())
156 : {
157 6 : m_aosGroups.emplace_back(psEntry->pszName);
158 : }
159 : }
160 : }
161 139 : }
162 55 : VSICloseDir(psDir);
163 : }
164 :
165 : /************************************************************************/
166 : /* ZarrV3Group::ZarrV3Group() */
167 : /************************************************************************/
168 :
169 403 : ZarrV3Group::ZarrV3Group(
170 : const std::shared_ptr<ZarrSharedResource> &poSharedResource,
171 : const std::string &osParentName, const std::string &osName,
172 403 : const std::string &osDirectoryName)
173 403 : : ZarrGroupBase(poSharedResource, osParentName, osName)
174 : {
175 403 : m_osDirectoryName = osDirectoryName;
176 403 : }
177 :
178 : /************************************************************************/
179 : /* ZarrV3Group::~ZarrV3Group() */
180 : /************************************************************************/
181 :
182 806 : ZarrV3Group::~ZarrV3Group()
183 : {
184 403 : if (m_bValid && m_oAttrGroup.IsModified())
185 : {
186 26 : CPLJSONDocument oDoc;
187 26 : auto oRoot = oDoc.GetRoot();
188 13 : oRoot.Add("zarr_format", 3);
189 13 : oRoot.Add("node_type", "group");
190 13 : oRoot.Add("attributes", m_oAttrGroup.Serialize());
191 : const std::string osZarrJsonFilename =
192 26 : CPLFormFilename(m_osDirectoryName.c_str(), "zarr.json", nullptr);
193 13 : oDoc.Save(osZarrJsonFilename);
194 : }
195 806 : }
196 :
197 : /************************************************************************/
198 : /* OpenZarrGroup() */
199 : /************************************************************************/
200 :
201 : std::shared_ptr<ZarrGroupBase>
202 41 : ZarrV3Group::OpenZarrGroup(const std::string &osName, CSLConstList) const
203 : {
204 41 : if (!CheckValidAndErrorOutIfNot())
205 0 : return nullptr;
206 :
207 41 : auto oIter = m_oMapGroups.find(osName);
208 41 : if (oIter != m_oMapGroups.end())
209 3 : return oIter->second;
210 :
211 : const std::string osSubDir =
212 76 : CPLFormFilename(m_osDirectoryName.c_str(), osName.c_str(), nullptr);
213 : const std::string osSubDirZarrJsonFilename =
214 76 : CPLFormFilename(osSubDir.c_str(), "zarr.json", nullptr);
215 :
216 : VSIStatBufL sStat;
217 : // Explicit group
218 38 : if (VSIStatL(osSubDirZarrJsonFilename.c_str(), &sStat) == 0)
219 : {
220 62 : CPLJSONDocument oDoc;
221 31 : if (oDoc.Load(osSubDirZarrJsonFilename.c_str()))
222 : {
223 62 : const auto oRoot = oDoc.GetRoot();
224 31 : if (oRoot.GetInteger("zarr_format") != 3)
225 : {
226 0 : CPLError(CE_Failure, CPLE_AppDefined,
227 : "Unhandled zarr_format value");
228 0 : return nullptr;
229 : }
230 93 : const std::string osNodeType = oRoot.GetString("node_type");
231 31 : if (osNodeType != "group")
232 : {
233 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s is a %s, not a group",
234 : osName.c_str(), osNodeType.c_str());
235 0 : return nullptr;
236 : }
237 : auto poSubGroup = ZarrV3Group::Create(
238 62 : m_poSharedResource, GetFullName(), osName, osSubDir);
239 31 : poSubGroup->m_poParent =
240 62 : std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock());
241 31 : poSubGroup->SetUpdatable(m_bUpdatable);
242 31 : m_oMapGroups[osName] = poSubGroup;
243 31 : return poSubGroup;
244 : }
245 0 : return nullptr;
246 : }
247 :
248 : // Implicit group
249 7 : if (VSIStatL(osSubDir.c_str(), &sStat) == 0 && VSI_ISDIR(sStat.st_mode))
250 : {
251 3 : auto poSubGroup = ZarrV3Group::Create(m_poSharedResource, GetFullName(),
252 6 : osName, osSubDir);
253 3 : poSubGroup->m_poParent =
254 6 : std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock());
255 3 : poSubGroup->SetUpdatable(m_bUpdatable);
256 3 : m_oMapGroups[osName] = poSubGroup;
257 3 : return poSubGroup;
258 : }
259 :
260 4 : return nullptr;
261 : }
262 :
263 : /************************************************************************/
264 : /* ZarrV3Group::CreateOnDisk() */
265 : /************************************************************************/
266 :
267 161 : std::shared_ptr<ZarrV3Group> ZarrV3Group::CreateOnDisk(
268 : const std::shared_ptr<ZarrSharedResource> &poSharedResource,
269 : const std::string &osParentFullName, const std::string &osName,
270 : const std::string &osDirectoryName)
271 : {
272 161 : if (VSIMkdir(osDirectoryName.c_str(), 0755) != 0)
273 : {
274 : VSIStatBufL sStat;
275 1 : if (VSIStatL(osDirectoryName.c_str(), &sStat) == 0)
276 : {
277 1 : CPLError(CE_Failure, CPLE_FileIO, "Directory %s already exists.",
278 : osDirectoryName.c_str());
279 : }
280 : else
281 : {
282 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create directory %s.",
283 : osDirectoryName.c_str());
284 : }
285 1 : return nullptr;
286 : }
287 :
288 : const std::string osZarrJsonFilename(
289 320 : CPLFormFilename(osDirectoryName.c_str(), "zarr.json", nullptr));
290 160 : VSILFILE *fp = VSIFOpenL(osZarrJsonFilename.c_str(), "wb");
291 160 : if (!fp)
292 : {
293 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create file %s.",
294 : osZarrJsonFilename.c_str());
295 0 : return nullptr;
296 : }
297 160 : VSIFPrintfL(fp, "{\n"
298 : " \"zarr_format\": 3,\n"
299 : " \"node_type\": \"group\",\n"
300 : " \"attributes\": {}\n"
301 : "}\n");
302 160 : VSIFCloseL(fp);
303 :
304 : auto poGroup = ZarrV3Group::Create(poSharedResource, osParentFullName,
305 320 : osName, osDirectoryName);
306 160 : poGroup->SetUpdatable(true);
307 160 : poGroup->m_bDirectoryExplored = true;
308 160 : return poGroup;
309 : }
310 :
311 : /************************************************************************/
312 : /* ZarrV3Group::CreateGroup() */
313 : /************************************************************************/
314 :
315 : std::shared_ptr<GDALGroup>
316 51 : ZarrV3Group::CreateGroup(const std::string &osName,
317 : CSLConstList /* papszOptions */)
318 : {
319 51 : if (!CheckValidAndErrorOutIfNot())
320 0 : return nullptr;
321 :
322 51 : if (!m_bUpdatable)
323 : {
324 1 : CPLError(CE_Failure, CPLE_NotSupported,
325 : "Dataset not open in update mode");
326 1 : return nullptr;
327 : }
328 50 : if (!IsValidObjectName(osName))
329 : {
330 14 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid group name");
331 14 : return nullptr;
332 : }
333 :
334 36 : GetGroupNames();
335 :
336 36 : if (std::find(m_aosGroups.begin(), m_aosGroups.end(), osName) !=
337 72 : m_aosGroups.end())
338 : {
339 3 : CPLError(CE_Failure, CPLE_AppDefined,
340 : "A group with same name already exists");
341 3 : return nullptr;
342 : }
343 :
344 : const std::string osDirectoryName =
345 66 : CPLFormFilename(m_osDirectoryName.c_str(), osName.c_str(), nullptr);
346 33 : auto poGroup = CreateOnDisk(m_poSharedResource, GetFullName(), osName,
347 66 : osDirectoryName);
348 33 : if (!poGroup)
349 1 : return nullptr;
350 32 : poGroup->m_poParent =
351 64 : std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock());
352 32 : m_oMapGroups[osName] = poGroup;
353 32 : m_aosGroups.emplace_back(osName);
354 32 : return poGroup;
355 : }
356 :
357 : /************************************************************************/
358 : /* FillDTypeElts() */
359 : /************************************************************************/
360 :
361 126 : static CPLJSONObject FillDTypeElts(const GDALExtendedDataType &oDataType,
362 : std::vector<DtypeElt> &aoDtypeElts)
363 : {
364 126 : CPLJSONObject dtype;
365 252 : const std::string dummy("dummy");
366 :
367 126 : const auto eDT = oDataType.GetNumericDataType();
368 252 : DtypeElt elt;
369 126 : bool bUnsupported = false;
370 126 : switch (eDT)
371 : {
372 56 : case GDT_Byte:
373 : {
374 56 : elt.nativeType = DtypeElt::NativeType::UNSIGNED_INT;
375 56 : dtype.Set(dummy, "uint8");
376 56 : break;
377 : }
378 6 : case GDT_Int8:
379 : {
380 6 : elt.nativeType = DtypeElt::NativeType::SIGNED_INT;
381 6 : dtype.Set(dummy, "int8");
382 6 : break;
383 : }
384 7 : case GDT_UInt16:
385 : {
386 7 : elt.nativeType = DtypeElt::NativeType::UNSIGNED_INT;
387 7 : dtype.Set(dummy, "uint16");
388 7 : break;
389 : }
390 9 : case GDT_Int16:
391 : {
392 9 : elt.nativeType = DtypeElt::NativeType::SIGNED_INT;
393 9 : dtype.Set(dummy, "int16");
394 9 : break;
395 : }
396 7 : case GDT_UInt32:
397 : {
398 7 : elt.nativeType = DtypeElt::NativeType::UNSIGNED_INT;
399 7 : dtype.Set(dummy, "uint32");
400 7 : break;
401 : }
402 7 : case GDT_Int32:
403 : {
404 7 : elt.nativeType = DtypeElt::NativeType::SIGNED_INT;
405 7 : dtype.Set(dummy, "int32");
406 7 : break;
407 : }
408 6 : case GDT_UInt64:
409 : {
410 6 : elt.nativeType = DtypeElt::NativeType::UNSIGNED_INT;
411 6 : dtype.Set(dummy, "uint64");
412 6 : break;
413 : }
414 6 : case GDT_Int64:
415 : {
416 6 : elt.nativeType = DtypeElt::NativeType::SIGNED_INT;
417 6 : dtype.Set(dummy, "int64");
418 6 : break;
419 : }
420 7 : case GDT_Float32:
421 : {
422 7 : elt.nativeType = DtypeElt::NativeType::IEEEFP;
423 7 : dtype.Set(dummy, "float32");
424 7 : break;
425 : }
426 11 : case GDT_Float64:
427 : {
428 11 : elt.nativeType = DtypeElt::NativeType::IEEEFP;
429 11 : dtype.Set(dummy, "float64");
430 11 : break;
431 : }
432 2 : case GDT_Unknown:
433 : case GDT_CInt16:
434 : case GDT_CInt32:
435 : {
436 2 : bUnsupported = true;
437 2 : break;
438 : }
439 1 : case GDT_CFloat32:
440 : {
441 1 : elt.nativeType = DtypeElt::NativeType::COMPLEX_IEEEFP;
442 1 : dtype.Set(dummy, "complex64");
443 1 : break;
444 : }
445 1 : case GDT_CFloat64:
446 : {
447 1 : elt.nativeType = DtypeElt::NativeType::COMPLEX_IEEEFP;
448 1 : dtype.Set(dummy, "complex128");
449 1 : break;
450 : }
451 0 : case GDT_TypeCount:
452 : {
453 : static_assert(GDT_TypeCount == GDT_Int8 + 1,
454 : "GDT_TypeCount == GDT_Int8 + 1");
455 0 : break;
456 : }
457 : }
458 126 : if (bUnsupported)
459 : {
460 2 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported data type: %s",
461 : GDALGetDataTypeName(eDT));
462 2 : dtype = CPLJSONObject();
463 2 : dtype.Deinit();
464 2 : return dtype;
465 : }
466 124 : elt.nativeOffset = 0;
467 124 : elt.nativeSize = GDALGetDataTypeSizeBytes(eDT);
468 124 : elt.gdalOffset = 0;
469 124 : elt.gdalSize = elt.nativeSize;
470 : #ifdef CPL_MSB
471 : elt.needByteSwapping = elt.nativeSize > 1;
472 : #endif
473 124 : aoDtypeElts.emplace_back(elt);
474 :
475 124 : return dtype;
476 : }
477 :
478 : /************************************************************************/
479 : /* ZarrV3Group::CreateMDArray() */
480 : /************************************************************************/
481 :
482 144 : std::shared_ptr<GDALMDArray> ZarrV3Group::CreateMDArray(
483 : const std::string &osName,
484 : const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
485 : const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
486 : {
487 144 : if (!CheckValidAndErrorOutIfNot())
488 0 : return nullptr;
489 :
490 144 : if (!m_bUpdatable)
491 : {
492 0 : CPLError(CE_Failure, CPLE_NotSupported,
493 : "Dataset not open in update mode");
494 0 : return nullptr;
495 : }
496 144 : if (!IsValidObjectName(osName))
497 : {
498 14 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid array name");
499 14 : return nullptr;
500 : }
501 :
502 130 : if (oDataType.GetClass() != GEDTC_NUMERIC)
503 : {
504 4 : CPLError(CE_Failure, CPLE_AppDefined,
505 : "Unsupported data type with Zarr V3");
506 4 : return nullptr;
507 : }
508 :
509 126 : if (!EQUAL(CSLFetchNameValueDef(papszOptions, "FILTER", "NONE"), "NONE"))
510 : {
511 0 : CPLError(CE_Failure, CPLE_AppDefined,
512 : "FILTER option not supported with Zarr V3");
513 0 : return nullptr;
514 : }
515 :
516 252 : std::vector<DtypeElt> aoDtypeElts;
517 378 : const auto dtype = FillDTypeElts(oDataType, aoDtypeElts)["dummy"];
518 126 : if (!dtype.IsValid() || aoDtypeElts.empty())
519 2 : return nullptr;
520 :
521 124 : GetMDArrayNames();
522 :
523 124 : if (std::find(m_aosArrays.begin(), m_aosArrays.end(), osName) !=
524 248 : m_aosArrays.end())
525 : {
526 2 : CPLError(CE_Failure, CPLE_AppDefined,
527 : "An array with same name already exists");
528 2 : return nullptr;
529 : }
530 :
531 244 : std::vector<GUInt64> anBlockSize;
532 122 : if (!ZarrArray::FillBlockSize(aoDimensions, oDataType, anBlockSize,
533 : papszOptions))
534 1 : return nullptr;
535 :
536 : const char *pszDimSeparator =
537 121 : CSLFetchNameValueDef(papszOptions, "DIM_SEPARATOR", "/");
538 :
539 : const std::string osArrayDirectory =
540 242 : CPLFormFilename(m_osDirectoryName.c_str(), osName.c_str(), nullptr);
541 121 : if (VSIMkdir(osArrayDirectory.c_str(), 0755) != 0)
542 : {
543 : VSIStatBufL sStat;
544 2 : if (VSIStatL(osArrayDirectory.c_str(), &sStat) == 0)
545 : {
546 2 : CPLError(CE_Failure, CPLE_FileIO, "Directory %s already exists.",
547 : osArrayDirectory.c_str());
548 : }
549 : else
550 : {
551 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create directory %s.",
552 : osArrayDirectory.c_str());
553 : }
554 2 : return nullptr;
555 : }
556 :
557 119 : std::unique_ptr<ZarrV3CodecSequence> poCodecs;
558 238 : CPLJSONArray oCodecs;
559 :
560 119 : const bool bFortranOrder = EQUAL(
561 : CSLFetchNameValueDef(papszOptions, "CHUNK_MEMORY_LAYOUT", "C"), "F");
562 119 : if (bFortranOrder)
563 : {
564 80 : CPLJSONObject oCodec;
565 40 : oCodec.Add("name", "transpose");
566 40 : oCodec.Add("configuration",
567 80 : ZarrV3CodecTranspose::GetConfiguration("F"));
568 40 : oCodecs.Add(oCodec);
569 : }
570 :
571 : // Not documented
572 119 : const char *pszEndian = CSLFetchNameValue(papszOptions, "@ENDIAN");
573 119 : if (pszEndian)
574 : {
575 100 : CPLJSONObject oCodec;
576 50 : oCodec.Add("name", "endian");
577 50 : oCodec.Add("configuration", ZarrV3CodecEndian::GetConfiguration(
578 50 : EQUAL(pszEndian, "little")));
579 50 : oCodecs.Add(oCodec);
580 : }
581 :
582 : const char *pszCompressor =
583 119 : CSLFetchNameValueDef(papszOptions, "COMPRESS", "NONE");
584 119 : if (EQUAL(pszCompressor, "GZIP"))
585 : {
586 46 : CPLJSONObject oCodec;
587 23 : oCodec.Add("name", "gzip");
588 : const char *pszLevel =
589 23 : CSLFetchNameValueDef(papszOptions, "GZIP_LEVEL", "6");
590 23 : oCodec.Add("configuration",
591 46 : ZarrV3CodecGZip::GetConfiguration(atoi(pszLevel)));
592 23 : oCodecs.Add(oCodec);
593 : }
594 96 : else if (EQUAL(pszCompressor, "BLOSC"))
595 : {
596 2 : const auto psCompressor = CPLGetCompressor("blosc");
597 2 : if (!psCompressor)
598 0 : return nullptr;
599 : const char *pszOptions =
600 2 : CSLFetchNameValueDef(psCompressor->papszMetadata, "OPTIONS", "");
601 2 : CPLXMLTreeCloser oTreeCompressor(CPLParseXMLString(pszOptions));
602 : const auto psRoot =
603 2 : oTreeCompressor.get()
604 2 : ? CPLGetXMLNode(oTreeCompressor.get(), "=Options")
605 2 : : nullptr;
606 2 : if (!psRoot)
607 0 : return nullptr;
608 :
609 2 : const char *cname = "zlib";
610 14 : for (const CPLXMLNode *psNode = psRoot->psChild; psNode != nullptr;
611 12 : psNode = psNode->psNext)
612 : {
613 12 : if (psNode->eType == CXT_Element)
614 : {
615 12 : const char *pszName = CPLGetXMLValue(psNode, "name", "");
616 12 : if (EQUAL(pszName, "CNAME"))
617 : {
618 2 : cname = CPLGetXMLValue(psNode, "default", cname);
619 : }
620 : }
621 : }
622 :
623 4 : CPLJSONObject oCodec;
624 2 : oCodec.Add("name", "blosc");
625 2 : cname = CSLFetchNameValueDef(papszOptions, "BLOSC_CNAME", cname);
626 : const int clevel =
627 2 : atoi(CSLFetchNameValueDef(papszOptions, "BLOSC_CLEVEL", "5"));
628 : const char *shuffle =
629 2 : CSLFetchNameValueDef(papszOptions, "BLOSC_SHUFFLE", "BYTE");
630 3 : shuffle = (EQUAL(shuffle, "0") || EQUAL(shuffle, "NONE")) ? "noshuffle"
631 1 : : (EQUAL(shuffle, "1") || EQUAL(shuffle, "BYTE")) ? "shuffle"
632 0 : : (EQUAL(shuffle, "2") || EQUAL(shuffle, "BIT"))
633 0 : ? "bitshuffle"
634 : : "invalid";
635 2 : const int typesize = atoi(CSLFetchNameValueDef(
636 : papszOptions, "BLOSC_TYPESIZE",
637 : CPLSPrintf("%d", GDALGetDataTypeSizeBytes(GDALGetNonComplexDataType(
638 : oDataType.GetNumericDataType())))));
639 : const int blocksize =
640 2 : atoi(CSLFetchNameValueDef(papszOptions, "BLOSC_BLOCKSIZE", "0"));
641 2 : oCodec.Add("configuration",
642 4 : ZarrV3CodecBlosc::GetConfiguration(cname, clevel, shuffle,
643 : typesize, blocksize));
644 2 : oCodecs.Add(oCodec);
645 : }
646 94 : else if (!EQUAL(pszCompressor, "NONE"))
647 : {
648 1 : CPLError(CE_Failure, CPLE_AppDefined,
649 : "COMPRESS = %s not implemented with Zarr V3", pszCompressor);
650 1 : return nullptr;
651 : }
652 :
653 118 : if (oCodecs.Size() > 0)
654 : {
655 : // Byte swapping will be done by the codec chain
656 65 : aoDtypeElts.back().needByteSwapping = false;
657 :
658 65 : ZarrArrayMetadata oInputArrayMetadata;
659 151 : for (auto &nSize : anBlockSize)
660 86 : oInputArrayMetadata.anBlockSizes.push_back(
661 86 : static_cast<size_t>(nSize));
662 65 : oInputArrayMetadata.oElt = aoDtypeElts.back();
663 65 : poCodecs = std::make_unique<ZarrV3CodecSequence>(oInputArrayMetadata);
664 65 : if (!poCodecs->InitFromJson(oCodecs))
665 0 : return nullptr;
666 : }
667 :
668 : auto poArray =
669 118 : ZarrV3Array::Create(m_poSharedResource, GetFullName(), osName,
670 236 : aoDimensions, oDataType, aoDtypeElts, anBlockSize);
671 :
672 118 : if (!poArray)
673 0 : return nullptr;
674 118 : poArray->SetNew(true);
675 : std::string osFilename =
676 236 : CPLFormFilename(osArrayDirectory.c_str(), "zarr.json", nullptr);
677 118 : poArray->SetFilename(osFilename);
678 118 : poArray->SetDimSeparator(pszDimSeparator);
679 118 : poArray->SetDtype(dtype);
680 118 : if (poCodecs)
681 65 : poArray->SetCodecs(std::move(poCodecs));
682 118 : poArray->SetUpdatable(true);
683 118 : poArray->SetDefinitionModified(true);
684 118 : poArray->Flush();
685 118 : RegisterArray(poArray);
686 :
687 118 : return poArray;
688 : }
|