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