Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL Utilities
4 : * Purpose: Command line application to list info about a multidimensional
5 : *raster Author: Even Rouault,<even.rouault at spatialys.com>
6 : *
7 : * ****************************************************************************
8 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "gdal_utils.h"
15 : #include "gdal_utils_priv.h"
16 :
17 : #include "cpl_json.h"
18 : #include "cpl_json_streaming_writer.h"
19 : #include "gdal_priv.h"
20 : #include "gdal_rat.h"
21 : #include "gdalargumentparser.h"
22 : #include <limits>
23 : #include <set>
24 :
25 : static void DumpArray(const std::shared_ptr<GDALGroup> &rootGroup,
26 : const std::shared_ptr<GDALMDArray> &array,
27 : CPLJSonStreamingWriter &serializer,
28 : const GDALMultiDimInfoOptions *psOptions,
29 : std::set<std::string> &alreadyDumpedDimensions,
30 : std::set<std::string> &alreadyDumpedArrays,
31 : bool bOutputObjType, bool bOutputName,
32 : bool bOutputOverviews);
33 :
34 : /************************************************************************/
35 : /* GDALMultiDimInfoOptions */
36 : /************************************************************************/
37 :
38 : struct GDALMultiDimInfoOptions
39 : {
40 : bool bStdoutOutput = false;
41 : bool bDetailed = false;
42 : bool bPretty = true;
43 : size_t nLimitValuesByDim = 0;
44 : CPLStringList aosArrayOptions{};
45 : std::string osArrayName{};
46 : bool bStats = false;
47 : };
48 :
49 : /************************************************************************/
50 : /* HasUniqueNames() */
51 : /************************************************************************/
52 :
53 64 : static bool HasUniqueNames(const std::vector<std::string> &oNames)
54 : {
55 128 : std::set<std::string> oSetNames;
56 218 : for (const auto &subgroupName : oNames)
57 : {
58 154 : if (oSetNames.find(subgroupName) != oSetNames.end())
59 : {
60 0 : return false;
61 : }
62 154 : oSetNames.insert(subgroupName);
63 : }
64 64 : return true;
65 : }
66 :
67 : /************************************************************************/
68 : /* DumpDataType() */
69 : /************************************************************************/
70 :
71 190 : static void DumpDataType(const GDALExtendedDataType &dt,
72 : CPLJSonStreamingWriter &serializer)
73 : {
74 190 : switch (dt.GetClass())
75 : {
76 58 : case GEDTC_STRING:
77 58 : serializer.Add("String");
78 58 : break;
79 :
80 124 : case GEDTC_NUMERIC:
81 : {
82 124 : auto poRAT = dt.GetRAT();
83 124 : if (poRAT)
84 : {
85 2 : auto objContext(serializer.MakeObjectContext());
86 1 : serializer.AddObjKey("name");
87 1 : serializer.Add(dt.GetName());
88 1 : serializer.AddObjKey("type");
89 1 : serializer.Add(GDALGetDataTypeName(dt.GetNumericDataType()));
90 1 : serializer.AddObjKey("attribute_table");
91 2 : auto arrayContext(serializer.MakeArrayContext());
92 1 : const int nRows = poRAT->GetRowCount();
93 1 : const int nCols = poRAT->GetColumnCount();
94 4 : for (int iRow = 0; iRow < nRows; ++iRow)
95 : {
96 6 : auto obj2Context(serializer.MakeObjectContext());
97 9 : for (int iCol = 0; iCol < nCols; ++iCol)
98 : {
99 6 : serializer.AddObjKey(poRAT->GetNameOfCol(iCol));
100 6 : switch (poRAT->GetTypeOfCol(iCol))
101 : {
102 3 : case GFT_Integer:
103 3 : serializer.Add(
104 3 : poRAT->GetValueAsInt(iRow, iCol));
105 3 : break;
106 0 : case GFT_Real:
107 0 : serializer.Add(
108 0 : poRAT->GetValueAsDouble(iRow, iCol));
109 0 : break;
110 3 : case GFT_String:
111 3 : serializer.Add(
112 3 : poRAT->GetValueAsString(iRow, iCol));
113 3 : break;
114 0 : case GFT_Boolean:
115 0 : serializer.Add(
116 0 : poRAT->GetValueAsBoolean(iRow, iCol));
117 0 : break;
118 0 : case GFT_DateTime:
119 : {
120 : const auto sDateTime =
121 0 : poRAT->GetValueAsDateTime(iRow, iCol);
122 0 : serializer.Add(
123 0 : GDALRasterAttributeTable::DateTimeToString(
124 : sDateTime));
125 0 : break;
126 : }
127 0 : case GFT_WKBGeometry:
128 : {
129 0 : size_t nWKBSize = 0;
130 : const GByte *pabyWKB =
131 0 : poRAT->GetValueAsWKBGeometry(iRow, iCol,
132 0 : nWKBSize);
133 : std::string osWKT =
134 : GDALRasterAttributeTable::WKBGeometryToWKT(
135 0 : pabyWKB, nWKBSize);
136 0 : if (osWKT.empty())
137 0 : serializer.AddNull();
138 : else
139 0 : serializer.Add(osWKT);
140 0 : break;
141 : }
142 : }
143 : }
144 : }
145 : }
146 : else
147 : {
148 123 : serializer.Add(GDALGetDataTypeName(dt.GetNumericDataType()));
149 : }
150 124 : break;
151 : }
152 :
153 8 : case GEDTC_COMPOUND:
154 : {
155 16 : auto compoundContext(serializer.MakeObjectContext());
156 8 : serializer.AddObjKey("name");
157 8 : serializer.Add(dt.GetName());
158 8 : serializer.AddObjKey("size");
159 8 : serializer.Add(static_cast<unsigned>(dt.GetSize()));
160 8 : serializer.AddObjKey("components");
161 8 : const auto &components = dt.GetComponents();
162 16 : auto componentsContext(serializer.MakeArrayContext());
163 35 : for (const auto &comp : components)
164 : {
165 54 : auto compContext(serializer.MakeObjectContext());
166 27 : serializer.AddObjKey("name");
167 27 : serializer.Add(comp->GetName());
168 27 : serializer.AddObjKey("offset");
169 27 : serializer.Add(static_cast<unsigned>(comp->GetOffset()));
170 27 : serializer.AddObjKey("type");
171 27 : DumpDataType(comp->GetType(), serializer);
172 : }
173 8 : break;
174 : }
175 : }
176 190 : }
177 :
178 : /************************************************************************/
179 : /* DumpValue() */
180 : /************************************************************************/
181 :
182 : template <typename T>
183 199 : static void DumpValue(CPLJSonStreamingWriter &serializer, const void *bytes)
184 : {
185 : T tmp;
186 199 : memcpy(&tmp, bytes, sizeof(T));
187 199 : serializer.Add(tmp);
188 199 : }
189 :
190 : /************************************************************************/
191 : /* DumpComplexValue() */
192 : /************************************************************************/
193 :
194 : template <typename T>
195 0 : static void DumpComplexValue(CPLJSonStreamingWriter &serializer,
196 : const GByte *bytes)
197 : {
198 0 : auto objectContext(serializer.MakeObjectContext());
199 0 : serializer.AddObjKey("real");
200 0 : DumpValue<T>(serializer, bytes);
201 0 : serializer.AddObjKey("imag");
202 0 : DumpValue<T>(serializer, bytes + sizeof(T));
203 0 : }
204 :
205 : /************************************************************************/
206 : /* DumpValue() */
207 : /************************************************************************/
208 :
209 199 : static void DumpValue(CPLJSonStreamingWriter &serializer, const GByte *bytes,
210 : const GDALDataType &eDT)
211 : {
212 199 : switch (eDT)
213 : {
214 47 : case GDT_UInt8:
215 47 : DumpValue<GByte>(serializer, bytes);
216 47 : break;
217 3 : case GDT_Int8:
218 3 : DumpValue<GInt8>(serializer, bytes);
219 3 : break;
220 9 : case GDT_Int16:
221 9 : DumpValue<GInt16>(serializer, bytes);
222 9 : break;
223 27 : case GDT_UInt16:
224 27 : DumpValue<GUInt16>(serializer, bytes);
225 27 : break;
226 13 : case GDT_Int32:
227 13 : DumpValue<GInt32>(serializer, bytes);
228 13 : break;
229 2 : case GDT_UInt32:
230 2 : DumpValue<GUInt32>(serializer, bytes);
231 2 : break;
232 7 : case GDT_Int64:
233 7 : DumpValue<std::int64_t>(serializer, bytes);
234 7 : break;
235 1 : case GDT_UInt64:
236 1 : DumpValue<std::uint64_t>(serializer, bytes);
237 1 : break;
238 0 : case GDT_Float16:
239 0 : DumpValue<GFloat16>(serializer, bytes);
240 0 : break;
241 22 : case GDT_Float32:
242 22 : DumpValue<float>(serializer, bytes);
243 22 : break;
244 68 : case GDT_Float64:
245 68 : DumpValue<double>(serializer, bytes);
246 68 : break;
247 0 : case GDT_CInt16:
248 0 : DumpComplexValue<GInt16>(serializer, bytes);
249 0 : break;
250 0 : case GDT_CInt32:
251 0 : DumpComplexValue<GInt32>(serializer, bytes);
252 0 : break;
253 0 : case GDT_CFloat16:
254 0 : DumpComplexValue<GFloat16>(serializer, bytes);
255 0 : break;
256 0 : case GDT_CFloat32:
257 0 : DumpComplexValue<float>(serializer, bytes);
258 0 : break;
259 0 : case GDT_CFloat64:
260 0 : DumpComplexValue<double>(serializer, bytes);
261 0 : break;
262 0 : case GDT_Unknown:
263 : case GDT_TypeCount:
264 0 : CPLAssert(false);
265 : break;
266 : }
267 199 : }
268 :
269 : static void DumpValue(CPLJSonStreamingWriter &serializer, const GByte *values,
270 : const GDALExtendedDataType &dt);
271 :
272 : /************************************************************************/
273 : /* DumpCompound() */
274 : /************************************************************************/
275 :
276 21 : static void DumpCompound(CPLJSonStreamingWriter &serializer,
277 : const GByte *values, const GDALExtendedDataType &dt)
278 : {
279 21 : CPLAssert(dt.GetClass() == GEDTC_COMPOUND);
280 21 : const auto &components = dt.GetComponents();
281 42 : auto objectContext(serializer.MakeObjectContext());
282 79 : for (const auto &comp : components)
283 : {
284 58 : serializer.AddObjKey(comp->GetName());
285 58 : DumpValue(serializer, values + comp->GetOffset(), comp->GetType());
286 : }
287 21 : }
288 :
289 : /************************************************************************/
290 : /* DumpValue() */
291 : /************************************************************************/
292 :
293 179 : static void DumpValue(CPLJSonStreamingWriter &serializer, const GByte *values,
294 : const GDALExtendedDataType &dt)
295 : {
296 179 : switch (dt.GetClass())
297 : {
298 155 : case GEDTC_NUMERIC:
299 155 : DumpValue(serializer, values, dt.GetNumericDataType());
300 155 : break;
301 21 : case GEDTC_COMPOUND:
302 21 : DumpCompound(serializer, values, dt);
303 21 : break;
304 3 : case GEDTC_STRING:
305 : {
306 : const char *pszStr;
307 : // cppcheck-suppress pointerSize
308 3 : memcpy(&pszStr, values, sizeof(const char *));
309 3 : if (pszStr)
310 3 : serializer.Add(pszStr);
311 : else
312 0 : serializer.AddNull();
313 3 : break;
314 : }
315 : }
316 179 : }
317 :
318 : /************************************************************************/
319 : /* SerializeJSON() */
320 : /************************************************************************/
321 :
322 45 : static void SerializeJSON(const CPLJSONObject &obj,
323 : CPLJSonStreamingWriter &serializer)
324 : {
325 45 : switch (obj.GetType())
326 : {
327 0 : case CPLJSONObject::Type::Unknown:
328 : {
329 0 : CPLAssert(false);
330 : break;
331 : }
332 :
333 0 : case CPLJSONObject::Type::Null:
334 : {
335 0 : serializer.AddNull();
336 0 : break;
337 : }
338 :
339 12 : case CPLJSONObject::Type::Object:
340 : {
341 24 : auto objectContext(serializer.MakeObjectContext());
342 32 : for (const auto &subobj : obj.GetChildren())
343 : {
344 20 : serializer.AddObjKey(subobj.GetName());
345 20 : SerializeJSON(subobj, serializer);
346 : }
347 12 : break;
348 : }
349 :
350 8 : case CPLJSONObject::Type::Array:
351 : {
352 16 : auto arrayContext(serializer.MakeArrayContext());
353 16 : const CPLJSONArray array = obj.ToArray();
354 24 : for (const auto &subobj : array)
355 : {
356 16 : SerializeJSON(subobj, serializer);
357 : }
358 8 : break;
359 : }
360 :
361 2 : case CPLJSONObject::Type::Boolean:
362 : {
363 2 : serializer.Add(obj.ToBool());
364 2 : break;
365 : }
366 :
367 16 : case CPLJSONObject::Type::String:
368 : {
369 16 : serializer.Add(obj.ToString());
370 16 : break;
371 : }
372 :
373 2 : case CPLJSONObject::Type::Integer:
374 : {
375 2 : serializer.Add(obj.ToInteger());
376 2 : break;
377 : }
378 :
379 0 : case CPLJSONObject::Type::Long:
380 : {
381 0 : serializer.Add(static_cast<int64_t>(obj.ToLong()));
382 0 : break;
383 : }
384 :
385 5 : case CPLJSONObject::Type::Double:
386 : {
387 5 : serializer.Add(obj.ToDouble());
388 5 : break;
389 : }
390 : }
391 45 : }
392 :
393 : /************************************************************************/
394 : /* DumpAttrValue() */
395 : /************************************************************************/
396 :
397 129 : static void DumpAttrValue(const std::shared_ptr<GDALAttribute> &attr,
398 : CPLJSonStreamingWriter &serializer)
399 : {
400 129 : const auto &dt = attr->GetDataType();
401 129 : const size_t nEltCount(static_cast<size_t>(attr->GetTotalElementsCount()));
402 129 : switch (dt.GetClass())
403 : {
404 98 : case GEDTC_STRING:
405 : {
406 98 : if (nEltCount == 1)
407 : {
408 96 : const char *pszStr = attr->ReadAsString();
409 96 : if (pszStr)
410 : {
411 95 : if (dt.GetSubType() == GEDTST_JSON)
412 : {
413 22 : CPLJSONDocument oDoc;
414 11 : if (oDoc.LoadMemory(std::string(pszStr)))
415 : {
416 9 : SerializeJSON(oDoc.GetRoot(), serializer);
417 : }
418 : else
419 : {
420 2 : serializer.Add(pszStr);
421 : }
422 : }
423 : else
424 : {
425 84 : serializer.Add(pszStr);
426 : }
427 : }
428 : else
429 : {
430 1 : serializer.AddNull();
431 : }
432 : }
433 : else
434 : {
435 4 : CPLStringList aosValues(attr->ReadAsStringArray());
436 : {
437 : auto arrayContextValues(
438 4 : serializer.MakeArrayContext(nEltCount < 10));
439 6 : for (int i = 0; i < aosValues.size(); ++i)
440 : {
441 4 : serializer.Add(aosValues[i]);
442 : }
443 : }
444 : }
445 98 : break;
446 : }
447 :
448 31 : case GEDTC_NUMERIC:
449 : {
450 31 : auto eDT = dt.GetNumericDataType();
451 62 : const auto rawValues(attr->ReadAsRaw());
452 31 : const GByte *bytePtr = rawValues.data();
453 31 : if (bytePtr)
454 : {
455 31 : const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
456 31 : if (nEltCount == 1)
457 : {
458 18 : serializer.SetNewline(false);
459 18 : DumpValue(serializer, rawValues.data(), eDT);
460 18 : serializer.SetNewline(true);
461 : }
462 : else
463 : {
464 : auto arrayContextValues(
465 26 : serializer.MakeArrayContext(nEltCount < 10));
466 39 : for (size_t i = 0; i < nEltCount; i++)
467 : {
468 26 : DumpValue(serializer, bytePtr, eDT);
469 26 : bytePtr += nDTSize;
470 : }
471 : }
472 : }
473 : else
474 : {
475 0 : serializer.AddNull();
476 : }
477 31 : break;
478 : }
479 :
480 0 : case GEDTC_COMPOUND:
481 : {
482 0 : auto rawValues(attr->ReadAsRaw());
483 0 : const GByte *bytePtr = rawValues.data();
484 0 : if (bytePtr)
485 : {
486 0 : if (nEltCount == 1)
487 : {
488 0 : serializer.SetNewline(false);
489 0 : DumpCompound(serializer, bytePtr, dt);
490 0 : serializer.SetNewline(true);
491 : }
492 : else
493 : {
494 0 : auto arrayContextValues(serializer.MakeArrayContext());
495 0 : for (size_t i = 0; i < nEltCount; i++)
496 : {
497 0 : DumpCompound(serializer, bytePtr, dt);
498 0 : bytePtr += dt.GetSize();
499 : }
500 : }
501 : }
502 : else
503 : {
504 0 : serializer.AddNull();
505 : }
506 0 : break;
507 : }
508 : }
509 129 : }
510 :
511 : /************************************************************************/
512 : /* DumpAttr() */
513 : /************************************************************************/
514 :
515 129 : static void DumpAttr(std::shared_ptr<GDALAttribute> attr,
516 : CPLJSonStreamingWriter &serializer,
517 : const GDALMultiDimInfoOptions *psOptions,
518 : bool bOutputObjType, bool bOutputName)
519 : {
520 129 : if (!bOutputObjType && !bOutputName && !psOptions->bDetailed)
521 : {
522 111 : DumpAttrValue(attr, serializer);
523 111 : return;
524 : }
525 :
526 18 : const auto &dt = attr->GetDataType();
527 36 : auto objectContext(serializer.MakeObjectContext());
528 18 : if (bOutputObjType)
529 : {
530 0 : serializer.AddObjKey("type");
531 0 : serializer.Add("attribute");
532 : }
533 18 : if (bOutputName)
534 : {
535 0 : serializer.AddObjKey("name");
536 0 : serializer.Add(attr->GetName());
537 : }
538 :
539 18 : if (psOptions->bDetailed)
540 : {
541 18 : serializer.AddObjKey("datatype");
542 18 : DumpDataType(dt, serializer);
543 :
544 18 : switch (dt.GetSubType())
545 : {
546 18 : case GEDTST_NONE:
547 18 : break;
548 0 : case GEDTST_JSON:
549 : {
550 0 : serializer.AddObjKey("subtype");
551 0 : serializer.Add("JSON");
552 0 : break;
553 : }
554 : }
555 :
556 18 : serializer.AddObjKey("value");
557 : }
558 :
559 18 : DumpAttrValue(attr, serializer);
560 : }
561 :
562 : /************************************************************************/
563 : /* DumpAttrs() */
564 : /************************************************************************/
565 :
566 50 : static void DumpAttrs(const std::vector<std::shared_ptr<GDALAttribute>> &attrs,
567 : CPLJSonStreamingWriter &serializer,
568 : const GDALMultiDimInfoOptions *psOptions)
569 : {
570 100 : std::vector<std::string> attributeNames;
571 179 : for (const auto &poAttr : attrs)
572 129 : attributeNames.emplace_back(poAttr->GetName());
573 50 : if (HasUniqueNames(attributeNames))
574 : {
575 100 : auto objectContext(serializer.MakeObjectContext());
576 179 : for (const auto &poAttr : attrs)
577 : {
578 129 : serializer.AddObjKey(poAttr->GetName());
579 129 : DumpAttr(poAttr, serializer, psOptions, false, false);
580 : }
581 : }
582 : else
583 : {
584 0 : auto arrayContext(serializer.MakeArrayContext());
585 0 : for (const auto &poAttr : attrs)
586 : {
587 0 : DumpAttr(poAttr, serializer, psOptions, false, true);
588 : }
589 : }
590 50 : }
591 :
592 : /************************************************************************/
593 : /* DumpArrayRec() */
594 : /************************************************************************/
595 :
596 32 : static void DumpArrayRec(std::shared_ptr<GDALMDArray> array,
597 : CPLJSonStreamingWriter &serializer, size_t nCurDim,
598 : const std::vector<GUInt64> &dimSizes,
599 : std::vector<GUInt64> &startIdx,
600 : const GDALMultiDimInfoOptions *psOptions)
601 : {
602 : do
603 : {
604 32 : auto arrayContext(serializer.MakeArrayContext());
605 32 : if (nCurDim + 1 == dimSizes.size())
606 : {
607 28 : const auto &dt(array->GetDataType());
608 28 : const auto nDTSize(dt.GetSize());
609 : const auto lambdaDumpValue =
610 33 : [&serializer, &dt, nDTSize](std::vector<GByte> &abyTmp,
611 303 : size_t nCount)
612 : {
613 33 : GByte *pabyPtr = &abyTmp[0];
614 134 : for (size_t i = 0; i < nCount; ++i)
615 : {
616 101 : DumpValue(serializer, pabyPtr, dt);
617 101 : dt.FreeDynamicMemory(pabyPtr);
618 101 : pabyPtr += nDTSize;
619 : }
620 61 : };
621 :
622 28 : serializer.SetNewline(false);
623 28 : std::vector<size_t> count(dimSizes.size(), 1);
624 35 : if (psOptions->nLimitValuesByDim == 0 ||
625 7 : dimSizes.back() <= psOptions->nLimitValuesByDim)
626 : {
627 21 : const size_t nCount = static_cast<size_t>(dimSizes.back());
628 21 : if (nCount > 0)
629 : {
630 38 : if (nCount != dimSizes.back() ||
631 19 : nDTSize > std::numeric_limits<size_t>::max() / nCount)
632 : {
633 0 : serializer.Add("[too many values]");
634 0 : break;
635 : }
636 19 : std::vector<GByte> abyTmp(nDTSize * nCount);
637 19 : count.back() = nCount;
638 38 : if (!array->Read(startIdx.data(), count.data(), nullptr,
639 19 : nullptr, dt, &abyTmp[0]))
640 0 : break;
641 19 : lambdaDumpValue(abyTmp, count.back());
642 : }
643 : }
644 : else
645 : {
646 : std::vector<GByte> abyTmp(
647 7 : nDTSize * (psOptions->nLimitValuesByDim + 1) / 2);
648 7 : startIdx.back() = 0;
649 7 : size_t nStartCount = (psOptions->nLimitValuesByDim + 1) / 2;
650 7 : count.back() = nStartCount;
651 14 : if (!array->Read(startIdx.data(), count.data(), nullptr,
652 7 : nullptr, dt, &abyTmp[0]))
653 0 : break;
654 7 : lambdaDumpValue(abyTmp, count.back());
655 7 : serializer.Add("[...]");
656 :
657 7 : count.back() = psOptions->nLimitValuesByDim / 2;
658 7 : if (count.back())
659 : {
660 7 : startIdx.back() = dimSizes.back() - count.back();
661 14 : if (!array->Read(startIdx.data(), count.data(), nullptr,
662 7 : nullptr, dt, &abyTmp[0]))
663 0 : break;
664 7 : lambdaDumpValue(abyTmp, count.back());
665 : }
666 : }
667 : }
668 : else
669 : {
670 5 : if (psOptions->nLimitValuesByDim == 0 ||
671 1 : dimSizes[nCurDim] <= psOptions->nLimitValuesByDim)
672 : {
673 11 : for (startIdx[nCurDim] = 0;
674 11 : startIdx[nCurDim] < dimSizes[nCurDim]; ++startIdx[nCurDim])
675 : {
676 8 : DumpArrayRec(array, serializer, nCurDim + 1, dimSizes,
677 : startIdx, psOptions);
678 : }
679 : }
680 : else
681 : {
682 1 : size_t nStartCount = (psOptions->nLimitValuesByDim + 1) / 2;
683 4 : for (startIdx[nCurDim] = 0; startIdx[nCurDim] < nStartCount;
684 3 : ++startIdx[nCurDim])
685 : {
686 3 : DumpArrayRec(array, serializer, nCurDim + 1, dimSizes,
687 : startIdx, psOptions);
688 : }
689 1 : serializer.Add("[...]");
690 1 : size_t nEndCount = psOptions->nLimitValuesByDim / 2;
691 3 : for (startIdx[nCurDim] = dimSizes[nCurDim] - nEndCount;
692 3 : startIdx[nCurDim] < dimSizes[nCurDim]; ++startIdx[nCurDim])
693 : {
694 2 : DumpArrayRec(array, serializer, nCurDim + 1, dimSizes,
695 : startIdx, psOptions);
696 : }
697 : }
698 : }
699 : } while (false);
700 32 : serializer.SetNewline(true);
701 32 : }
702 :
703 : /************************************************************************/
704 : /* DumpDimensions() */
705 : /************************************************************************/
706 :
707 : static void
708 166 : DumpDimensions(const std::shared_ptr<GDALGroup> &rootGroup,
709 : const std::vector<std::shared_ptr<GDALDimension>> &dims,
710 : CPLJSonStreamingWriter &serializer,
711 : const GDALMultiDimInfoOptions *psOptions,
712 : std::set<std::string> &alreadyDumpedDimensions)
713 : {
714 332 : auto arrayContext(serializer.MakeArrayContext());
715 422 : for (const auto &dim : dims)
716 : {
717 256 : std::string osFullname(dim->GetFullName());
718 256 : if (alreadyDumpedDimensions.find(osFullname) !=
719 512 : alreadyDumpedDimensions.end())
720 : {
721 181 : serializer.Add(osFullname);
722 181 : continue;
723 : }
724 :
725 150 : auto dimObjectContext(serializer.MakeObjectContext());
726 75 : if (!osFullname.empty() && osFullname[0] == '/')
727 68 : alreadyDumpedDimensions.insert(osFullname);
728 :
729 75 : serializer.AddObjKey("name");
730 75 : serializer.Add(dim->GetName());
731 :
732 75 : serializer.AddObjKey("full_name");
733 75 : serializer.Add(osFullname);
734 :
735 75 : serializer.AddObjKey("size");
736 75 : serializer.Add(static_cast<std::uint64_t>(dim->GetSize()));
737 :
738 75 : const auto &type(dim->GetType());
739 75 : if (!type.empty())
740 : {
741 51 : serializer.AddObjKey("type");
742 51 : serializer.Add(type);
743 : }
744 :
745 75 : const auto &direction(dim->GetDirection());
746 75 : if (!direction.empty())
747 : {
748 25 : serializer.AddObjKey("direction");
749 25 : serializer.Add(direction);
750 : }
751 :
752 150 : auto poIndexingVariable(dim->GetIndexingVariable());
753 75 : if (poIndexingVariable)
754 : {
755 52 : serializer.AddObjKey("indexing_variable");
756 52 : if (rootGroup->OpenMDArray(poIndexingVariable->GetFullName()))
757 : {
758 0 : serializer.Add(poIndexingVariable->GetFullName());
759 : }
760 : else
761 : {
762 : std::set<std::string> alreadyDumpedDimensionsLocal(
763 104 : alreadyDumpedDimensions);
764 52 : alreadyDumpedDimensionsLocal.insert(std::move(osFullname));
765 104 : std::set<std::string> alreadyDumpedArrays;
766 :
767 104 : auto indexingVariableContext(serializer.MakeObjectContext());
768 52 : serializer.AddObjKey(poIndexingVariable->GetName());
769 52 : DumpArray(rootGroup, poIndexingVariable, serializer, psOptions,
770 : alreadyDumpedDimensionsLocal, alreadyDumpedArrays,
771 : /* bOutputObjType = */ false,
772 : /* bOutputName = */ false,
773 : /* bOutputOverviews = */ false);
774 : }
775 : }
776 : }
777 166 : }
778 :
779 : /************************************************************************/
780 : /* DumpStructuralInfo() */
781 : /************************************************************************/
782 :
783 10 : static void DumpStructuralInfo(CSLConstList papszStructuralInfo,
784 : CPLJSonStreamingWriter &serializer)
785 : {
786 20 : auto objectContext(serializer.MakeObjectContext());
787 10 : int i = 1;
788 20 : for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(
789 30 : papszStructuralInfo, /* bReturnNullKeyIfNotNameValue = */ true))
790 : {
791 10 : if (pszKey)
792 : {
793 10 : serializer.AddObjKey(pszKey);
794 : }
795 : else
796 : {
797 0 : serializer.AddObjKey(CPLSPrintf("metadata_%d", i));
798 0 : ++i;
799 : }
800 10 : serializer.Add(pszValue);
801 : }
802 10 : }
803 :
804 : /************************************************************************/
805 : /* DumpArray() */
806 : /************************************************************************/
807 :
808 144 : static void DumpArray(const std::shared_ptr<GDALGroup> &rootGroup,
809 : const std::shared_ptr<GDALMDArray> &array,
810 : CPLJSonStreamingWriter &serializer,
811 : const GDALMultiDimInfoOptions *psOptions,
812 : std::set<std::string> &alreadyDumpedDimensions,
813 : std::set<std::string> &alreadyDumpedArrays,
814 : bool bOutputObjType, bool bOutputName,
815 : bool bOutputOverviews)
816 : {
817 : // Protection against infinite recursion
818 144 : if (cpl::contains(alreadyDumpedArrays, array->GetFullName()))
819 : {
820 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s already visited",
821 0 : array->GetFullName().c_str());
822 0 : return;
823 : }
824 144 : alreadyDumpedArrays.insert(array->GetFullName());
825 :
826 288 : auto objectContext(serializer.MakeObjectContext());
827 144 : if (bOutputObjType)
828 : {
829 2 : serializer.AddObjKey("type");
830 2 : serializer.Add("array");
831 : }
832 144 : if (bOutputName)
833 : {
834 2 : serializer.AddObjKey("name");
835 2 : serializer.Add(array->GetName());
836 : }
837 :
838 144 : serializer.AddObjKey("datatype");
839 144 : const auto &dt(array->GetDataType());
840 144 : DumpDataType(dt, serializer);
841 :
842 288 : auto dims = array->GetDimensions();
843 144 : if (!dims.empty())
844 : {
845 144 : serializer.AddObjKey("dimensions");
846 144 : DumpDimensions(rootGroup, dims, serializer, psOptions,
847 : alreadyDumpedDimensions);
848 :
849 144 : serializer.AddObjKey("dimension_size");
850 288 : auto arrayContext(serializer.MakeArrayContext());
851 343 : for (const auto &poDim : dims)
852 : {
853 199 : serializer.Add(static_cast<uint64_t>(poDim->GetSize()));
854 : }
855 : }
856 :
857 144 : bool hasNonNullBlockSize = false;
858 288 : const auto blockSize = array->GetBlockSize();
859 315 : for (auto v : blockSize)
860 : {
861 192 : if (v != 0)
862 : {
863 21 : hasNonNullBlockSize = true;
864 21 : break;
865 : }
866 : }
867 144 : if (hasNonNullBlockSize)
868 : {
869 21 : serializer.AddObjKey("block_size");
870 42 : auto arrayContext(serializer.MakeArrayContext());
871 49 : for (auto v : blockSize)
872 : {
873 28 : serializer.Add(static_cast<uint64_t>(v));
874 : }
875 : }
876 :
877 288 : CPLStringList aosOptions;
878 144 : if (psOptions->bDetailed)
879 19 : aosOptions.SetNameValue("SHOW_ALL", "YES");
880 288 : auto attrs = array->GetAttributes(aosOptions.List());
881 144 : if (!attrs.empty())
882 : {
883 31 : serializer.AddObjKey("attributes");
884 31 : DumpAttrs(attrs, serializer, psOptions);
885 : }
886 :
887 144 : const auto &unit = array->GetUnit();
888 144 : if (!unit.empty())
889 : {
890 21 : serializer.AddObjKey("unit");
891 21 : serializer.Add(unit);
892 : }
893 :
894 144 : auto nodata = array->GetRawNoDataValue();
895 144 : if (nodata)
896 : {
897 20 : serializer.AddObjKey("nodata_value");
898 20 : DumpValue(serializer, static_cast<const GByte *>(nodata), dt);
899 : }
900 :
901 144 : bool bValid = false;
902 144 : double dfOffset = array->GetOffset(&bValid);
903 144 : if (bValid)
904 : {
905 1 : serializer.AddObjKey("offset");
906 1 : serializer.Add(dfOffset);
907 : }
908 144 : double dfScale = array->GetScale(&bValid);
909 144 : if (bValid)
910 : {
911 1 : serializer.AddObjKey("scale");
912 1 : serializer.Add(dfScale);
913 : }
914 :
915 288 : auto srs = array->GetSpatialRef();
916 144 : if (srs)
917 : {
918 8 : char *pszWKT = nullptr;
919 16 : CPLStringList wktOptions;
920 8 : wktOptions.SetNameValue("FORMAT", "WKT2_2018");
921 8 : if (srs->exportToWkt(&pszWKT, wktOptions.List()) == OGRERR_NONE)
922 : {
923 8 : serializer.AddObjKey("srs");
924 : {
925 16 : auto srsContext(serializer.MakeObjectContext());
926 8 : serializer.AddObjKey("wkt");
927 8 : serializer.Add(pszWKT);
928 8 : serializer.AddObjKey("data_axis_to_srs_axis_mapping");
929 : {
930 16 : auto dataAxisContext(serializer.MakeArrayContext(true));
931 16 : auto mapping = srs->GetDataAxisToSRSAxisMapping();
932 24 : for (const auto &axisNumber : mapping)
933 16 : serializer.Add(axisNumber);
934 : }
935 : }
936 : }
937 8 : CPLFree(pszWKT);
938 : }
939 :
940 144 : auto papszStructuralInfo = array->GetStructuralInfo();
941 144 : if (papszStructuralInfo)
942 : {
943 1 : serializer.AddObjKey("structural_info");
944 1 : DumpStructuralInfo(papszStructuralInfo, serializer);
945 : }
946 :
947 144 : if (psOptions->bDetailed)
948 : {
949 19 : serializer.AddObjKey("values");
950 19 : if (dims.empty())
951 : {
952 0 : std::vector<GByte> abyTmp(dt.GetSize());
953 0 : array->Read(nullptr, nullptr, nullptr, nullptr, dt, &abyTmp[0]);
954 0 : DumpValue(serializer, &abyTmp[0], dt);
955 : }
956 : else
957 : {
958 38 : std::vector<GUInt64> startIdx(dims.size());
959 19 : std::vector<GUInt64> dimSizes;
960 42 : for (const auto &dim : dims)
961 23 : dimSizes.emplace_back(dim->GetSize());
962 19 : DumpArrayRec(array, serializer, 0, dimSizes, startIdx, psOptions);
963 : }
964 : }
965 :
966 144 : if (psOptions->bStats)
967 : {
968 3 : double dfMin = 0.0;
969 3 : double dfMax = 0.0;
970 3 : double dfMean = 0.0;
971 3 : double dfStdDev = 0.0;
972 3 : GUInt64 nValidCount = 0;
973 3 : if (array->GetStatistics(false, true, &dfMin, &dfMax, &dfMean,
974 : &dfStdDev, &nValidCount, nullptr,
975 3 : nullptr) == CE_None)
976 : {
977 3 : serializer.AddObjKey("statistics");
978 6 : auto statContext(serializer.MakeObjectContext());
979 3 : if (nValidCount > 0)
980 : {
981 3 : serializer.AddObjKey("min");
982 3 : serializer.Add(dfMin);
983 :
984 3 : serializer.AddObjKey("max");
985 3 : serializer.Add(dfMax);
986 :
987 3 : serializer.AddObjKey("mean");
988 3 : serializer.Add(dfMean);
989 :
990 3 : serializer.AddObjKey("stddev");
991 3 : serializer.Add(dfStdDev);
992 : }
993 :
994 3 : serializer.AddObjKey("valid_sample_count");
995 3 : serializer.Add(static_cast<std::uint64_t>(nValidCount));
996 : }
997 : }
998 :
999 144 : if (bOutputOverviews)
1000 : {
1001 92 : const int nOverviews = array->GetOverviewCount();
1002 92 : if (nOverviews > 0)
1003 : {
1004 3 : serializer.AddObjKey("overviews");
1005 6 : auto overviewsContext(serializer.MakeArrayContext());
1006 7 : for (int i = 0; i < nOverviews; ++i)
1007 : {
1008 8 : if (auto poOvrArray = array->GetOverview(i))
1009 : {
1010 4 : bool bIsStandalone = false;
1011 : {
1012 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1013 : bIsStandalone =
1014 12 : rootGroup->OpenMDArrayFromFullname(
1015 8 : poOvrArray->GetFullName()) == nullptr;
1016 : }
1017 4 : if (bIsStandalone)
1018 : {
1019 0 : DumpArray(rootGroup, poOvrArray, serializer, psOptions,
1020 : alreadyDumpedDimensions, alreadyDumpedArrays,
1021 : bOutputObjType, bOutputName,
1022 : bOutputOverviews);
1023 : }
1024 : else
1025 : {
1026 4 : serializer.Add(poOvrArray->GetFullName());
1027 : }
1028 : }
1029 : }
1030 : }
1031 : }
1032 : }
1033 :
1034 : /************************************************************************/
1035 : /* DumpArrays() */
1036 : /************************************************************************/
1037 :
1038 38 : static void DumpArrays(const std::shared_ptr<GDALGroup> &rootGroup,
1039 : const std::shared_ptr<GDALGroup> &group,
1040 : const std::vector<std::string> &arrayNames,
1041 : CPLJSonStreamingWriter &serializer,
1042 : const GDALMultiDimInfoOptions *psOptions,
1043 : std::set<std::string> &alreadyDumpedDimensions,
1044 : std::set<std::string> &alreadyDumpedArrays)
1045 : {
1046 76 : std::set<std::string> oSetNames;
1047 76 : auto objectContext(serializer.MakeObjectContext());
1048 128 : for (const auto &name : arrayNames)
1049 : {
1050 90 : if (oSetNames.find(name) != oSetNames.end())
1051 0 : continue; // should not happen on well behaved drivers
1052 90 : oSetNames.insert(name);
1053 180 : auto array = group->OpenMDArray(name);
1054 90 : if (array)
1055 : {
1056 90 : serializer.AddObjKey(array->GetName());
1057 90 : DumpArray(rootGroup, array, serializer, psOptions,
1058 : alreadyDumpedDimensions, alreadyDumpedArrays, false,
1059 : false, /* bOutputOverviews = */ true);
1060 : }
1061 : }
1062 38 : }
1063 :
1064 : /************************************************************************/
1065 : /* DumpGroup() */
1066 : /************************************************************************/
1067 :
1068 55 : static void DumpGroup(const std::shared_ptr<GDALGroup> &rootGroup,
1069 : const std::shared_ptr<GDALGroup> &group,
1070 : const char *pszDriverName,
1071 : CPLJSonStreamingWriter &serializer,
1072 : const GDALMultiDimInfoOptions *psOptions,
1073 : std::set<std::string> &alreadyDumpedDimensions,
1074 : std::set<std::string> &alreadyDumpedArrays,
1075 : bool bOutputObjType, bool bOutputName)
1076 : {
1077 110 : auto objectContext(serializer.MakeObjectContext());
1078 55 : if (bOutputObjType)
1079 : {
1080 30 : serializer.AddObjKey("type");
1081 30 : serializer.Add("group");
1082 : }
1083 55 : if (pszDriverName)
1084 : {
1085 30 : serializer.AddObjKey("driver");
1086 30 : serializer.Add(pszDriverName);
1087 : }
1088 55 : if (bOutputName)
1089 : {
1090 30 : serializer.AddObjKey("name");
1091 30 : serializer.Add(group->GetName());
1092 :
1093 : // If the root group is not actually the root, print its full path
1094 30 : if (pszDriverName != nullptr && group->GetName() != "/")
1095 : {
1096 0 : serializer.AddObjKey("full_name");
1097 0 : serializer.Add(group->GetFullName());
1098 : }
1099 : }
1100 :
1101 110 : CPLStringList aosOptionsGetAttr;
1102 55 : if (psOptions->bDetailed)
1103 11 : aosOptionsGetAttr.SetNameValue("SHOW_ALL", "YES");
1104 110 : auto attrs = group->GetAttributes(aosOptionsGetAttr.List());
1105 55 : if (!attrs.empty())
1106 : {
1107 19 : serializer.AddObjKey("attributes");
1108 19 : DumpAttrs(attrs, serializer, psOptions);
1109 : }
1110 :
1111 110 : auto dims = group->GetDimensions();
1112 55 : if (!dims.empty())
1113 : {
1114 22 : serializer.AddObjKey("dimensions");
1115 22 : DumpDimensions(rootGroup, dims, serializer, psOptions,
1116 : alreadyDumpedDimensions);
1117 : }
1118 :
1119 55 : const auto &types = group->GetDataTypes();
1120 55 : if (!types.empty())
1121 : {
1122 1 : serializer.AddObjKey("datatypes");
1123 2 : auto arrayContext(serializer.MakeArrayContext());
1124 2 : for (const auto &dt : types)
1125 : {
1126 1 : DumpDataType(*(dt.get()), serializer);
1127 : }
1128 : }
1129 :
1130 110 : CPLStringList aosOptionsGetArray(psOptions->aosArrayOptions);
1131 55 : if (psOptions->bDetailed)
1132 11 : aosOptionsGetArray.SetNameValue("SHOW_ALL", "YES");
1133 110 : auto arrayNames = group->GetMDArrayNames(aosOptionsGetArray.List());
1134 55 : if (!arrayNames.empty())
1135 : {
1136 38 : serializer.AddObjKey("arrays");
1137 38 : DumpArrays(rootGroup, group, arrayNames, serializer, psOptions,
1138 : alreadyDumpedDimensions, alreadyDumpedArrays);
1139 : }
1140 :
1141 55 : auto papszStructuralInfo = group->GetStructuralInfo();
1142 55 : if (papszStructuralInfo)
1143 : {
1144 9 : serializer.AddObjKey("structural_info");
1145 9 : DumpStructuralInfo(papszStructuralInfo, serializer);
1146 : }
1147 :
1148 110 : auto subgroupNames = group->GetGroupNames();
1149 55 : if (!subgroupNames.empty())
1150 : {
1151 14 : serializer.AddObjKey("groups");
1152 14 : if (HasUniqueNames(subgroupNames))
1153 : {
1154 28 : auto groupContext(serializer.MakeObjectContext());
1155 39 : for (const auto &subgroupName : subgroupNames)
1156 : {
1157 50 : auto subgroup = group->OpenGroup(subgroupName);
1158 25 : if (subgroup)
1159 : {
1160 25 : serializer.AddObjKey(subgroupName);
1161 25 : DumpGroup(rootGroup, subgroup, nullptr, serializer,
1162 : psOptions, alreadyDumpedDimensions,
1163 : alreadyDumpedArrays, false, false);
1164 : }
1165 : }
1166 : }
1167 : else
1168 : {
1169 0 : auto arrayContext(serializer.MakeArrayContext());
1170 0 : for (const auto &subgroupName : subgroupNames)
1171 : {
1172 0 : auto subgroup = group->OpenGroup(subgroupName);
1173 0 : if (subgroup)
1174 : {
1175 0 : DumpGroup(rootGroup, subgroup, nullptr, serializer,
1176 : psOptions, alreadyDumpedDimensions,
1177 : alreadyDumpedArrays, false, true);
1178 : }
1179 : }
1180 : }
1181 : }
1182 55 : }
1183 :
1184 : /************************************************************************/
1185 : /* WriteToStdout() */
1186 : /************************************************************************/
1187 :
1188 1921 : static void WriteToStdout(const char *pszText, void *)
1189 : {
1190 1921 : printf("%s", pszText);
1191 1921 : }
1192 :
1193 35 : static std::unique_ptr<GDALArgumentParser> GDALMultiDimInfoAppOptionsGetParser(
1194 : GDALMultiDimInfoOptions *psOptions,
1195 : GDALMultiDimInfoOptionsForBinary *psOptionsForBinary)
1196 : {
1197 : auto argParser = std::make_unique<GDALArgumentParser>(
1198 35 : "gdalmdiminfo", /* bForBinary=*/psOptionsForBinary != nullptr);
1199 :
1200 35 : argParser->add_description(
1201 35 : _("Lists various information about a GDAL multidimensional dataset."));
1202 :
1203 35 : argParser->add_epilog(_("For more details, consult "
1204 35 : "https://gdal.org/programs/gdalmdiminfo.html"));
1205 :
1206 35 : argParser->add_argument("-detailed")
1207 35 : .flag()
1208 35 : .store_into(psOptions->bDetailed)
1209 : .help(_("Most verbose output. Report attribute data types and array "
1210 35 : "values."));
1211 :
1212 : argParser->add_inverted_logic_flag(
1213 : "-nopretty", &psOptions->bPretty,
1214 35 : _("Outputs on a single line without any indentation."));
1215 :
1216 35 : argParser->add_argument("-array")
1217 70 : .metavar("<array_name>")
1218 35 : .store_into(psOptions->osArrayName)
1219 : .help(_("Name of the array, used to restrict the output to the "
1220 35 : "specified array."));
1221 :
1222 35 : argParser->add_argument("-limit")
1223 70 : .metavar("<number>")
1224 35 : .scan<'i', int>()
1225 35 : .store_into(psOptions->nLimitValuesByDim)
1226 : .help(_("Number of values in each dimension that is used to limit the "
1227 35 : "display of array values."));
1228 :
1229 35 : if (psOptionsForBinary)
1230 : {
1231 : argParser->add_open_options_argument(
1232 3 : psOptionsForBinary->aosOpenOptions);
1233 :
1234 : argParser->add_input_format_argument(
1235 3 : &psOptionsForBinary->aosAllowInputDrivers);
1236 :
1237 3 : argParser->add_argument("dataset_name")
1238 6 : .metavar("<dataset_name>")
1239 3 : .store_into(psOptionsForBinary->osFilename)
1240 3 : .help("Input dataset.");
1241 : }
1242 :
1243 35 : argParser->add_argument("-arrayoption")
1244 70 : .metavar("<NAME>=<VALUE>")
1245 35 : .append()
1246 2 : .action([psOptions](const std::string &s)
1247 37 : { psOptions->aosArrayOptions.AddString(s.c_str()); })
1248 : .help(_("Option passed to GDALGroup::GetMDArrayNames() to filter "
1249 35 : "reported arrays."));
1250 :
1251 35 : argParser->add_argument("-stats")
1252 35 : .flag()
1253 35 : .store_into(psOptions->bStats)
1254 35 : .help(_("Read and display image statistics."));
1255 :
1256 : // Only used by gdalmdiminfo binary to write output to stdout instead of in a string, in JSON mode
1257 35 : argParser->add_argument("-stdout").flag().hidden().store_into(
1258 35 : psOptions->bStdoutOutput);
1259 :
1260 35 : return argParser;
1261 : }
1262 :
1263 : /************************************************************************/
1264 : /* GDALMultiDimInfoAppGetParserUsage() */
1265 : /************************************************************************/
1266 :
1267 0 : std::string GDALMultiDimInfoAppGetParserUsage()
1268 : {
1269 : try
1270 : {
1271 0 : GDALMultiDimInfoOptions sOptions;
1272 0 : GDALMultiDimInfoOptionsForBinary sOptionsForBinary;
1273 : auto argParser =
1274 0 : GDALMultiDimInfoAppOptionsGetParser(&sOptions, &sOptionsForBinary);
1275 0 : return argParser->usage();
1276 : }
1277 0 : catch (const std::exception &err)
1278 : {
1279 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
1280 0 : err.what());
1281 0 : return std::string();
1282 : }
1283 : }
1284 :
1285 : /************************************************************************/
1286 : /* GDALMultiDimInfo() */
1287 : /************************************************************************/
1288 :
1289 : /* clang-format off */
1290 : /**
1291 : * Lists various information about a GDAL multidimensional dataset.
1292 : *
1293 : * This is the equivalent of the
1294 : * <a href="/programs/gdalmdiminfo.html">gdalmdiminfo</a>utility.
1295 : *
1296 : * GDALMultiDimInfoOptions* must be allocated and freed with
1297 : * GDALMultiDimInfoOptionsNew() and GDALMultiDimInfoOptionsFree() respectively.
1298 : *
1299 : * @param hDataset the dataset handle.
1300 : * @param psOptionsIn the options structure returned by
1301 : * GDALMultiDimInfoOptionsNew() or NULL.
1302 : * @return string corresponding to the information about the raster dataset
1303 : * (must be freed with CPLFree()), or NULL in case of error.
1304 : *
1305 : * @since GDAL 3.1
1306 : */
1307 : /* clang-format on */
1308 :
1309 33 : char *GDALMultiDimInfo(GDALDatasetH hDataset,
1310 : const GDALMultiDimInfoOptions *psOptionsIn)
1311 : {
1312 33 : if (hDataset == nullptr)
1313 0 : return nullptr;
1314 :
1315 66 : GDALMultiDimInfoOptions oOptionsDefault;
1316 33 : const GDALMultiDimInfoOptions *psOptions =
1317 33 : psOptionsIn ? psOptionsIn : &oOptionsDefault;
1318 : CPLJSonStreamingWriter serializer(
1319 66 : psOptions->bStdoutOutput ? WriteToStdout : nullptr, nullptr);
1320 33 : serializer.SetPrettyFormatting(psOptions->bPretty);
1321 33 : GDALDataset *poDS = GDALDataset::FromHandle(hDataset);
1322 66 : auto group = poDS->GetRootGroup();
1323 33 : if (!group)
1324 1 : return nullptr;
1325 :
1326 64 : std::set<std::string> alreadyDumpedDimensions;
1327 64 : std::set<std::string> alreadyDumpedArrays;
1328 : try
1329 : {
1330 32 : if (psOptions->osArrayName.empty())
1331 : {
1332 30 : const char *pszDriverName = nullptr;
1333 30 : auto poDriver = poDS->GetDriver();
1334 30 : if (poDriver)
1335 30 : pszDriverName = poDriver->GetDescription();
1336 30 : DumpGroup(group, group, pszDriverName, serializer, psOptions,
1337 : alreadyDumpedDimensions, alreadyDumpedArrays, true, true);
1338 : }
1339 : else
1340 : {
1341 2 : auto curGroup = group;
1342 : CPLStringList aosTokens(
1343 2 : CSLTokenizeString2(psOptions->osArrayName.c_str(), "/", 0));
1344 2 : for (int i = 0; i < aosTokens.size() - 1; i++)
1345 : {
1346 0 : auto curGroupNew = curGroup->OpenGroup(aosTokens[i]);
1347 0 : if (!curGroupNew)
1348 : {
1349 0 : CPLError(CE_Failure, CPLE_AppDefined,
1350 : "Cannot find group %s", aosTokens[i]);
1351 0 : return nullptr;
1352 : }
1353 0 : curGroup = std::move(curGroupNew);
1354 : }
1355 2 : const char *pszArrayName = aosTokens.back();
1356 4 : auto array(curGroup->OpenMDArray(pszArrayName));
1357 2 : if (!array)
1358 : {
1359 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find array %s",
1360 : pszArrayName);
1361 0 : return nullptr;
1362 : }
1363 2 : DumpArray(group, array, serializer, psOptions,
1364 : alreadyDumpedDimensions, alreadyDumpedArrays, true, true,
1365 : true);
1366 : }
1367 : }
1368 0 : catch (const std::exception &e)
1369 : {
1370 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
1371 0 : return nullptr;
1372 : }
1373 :
1374 32 : if (psOptions->bStdoutOutput)
1375 : {
1376 3 : printf("\n");
1377 3 : return VSIStrdup("ok");
1378 : }
1379 : else
1380 : {
1381 29 : return VSIStrdup(serializer.GetString().c_str());
1382 : }
1383 : }
1384 :
1385 : /************************************************************************/
1386 : /* GDALMultiDimInfoOptionsNew() */
1387 : /************************************************************************/
1388 :
1389 : /**
1390 : * Allocates a GDALMultiDimInfo struct.
1391 : *
1392 : * @param papszArgv NULL terminated list of options (potentially including
1393 : * filename and open options too), or NULL. The accepted options are the ones of
1394 : * the <a href="/programs/gdalmdiminfo.html">gdalmdiminfo</a> utility.
1395 : * @param psOptionsForBinary should be nullptr, unless called from
1396 : * gdalmultidiminfo_bin.cpp
1397 : * @return pointer to the allocated GDALMultiDimInfoOptions struct. Must be
1398 : * freed with GDALMultiDimInfoOptionsFree().
1399 : *
1400 : * @since GDAL 3.1
1401 : */
1402 :
1403 : GDALMultiDimInfoOptions *
1404 35 : GDALMultiDimInfoOptionsNew(char **papszArgv,
1405 : GDALMultiDimInfoOptionsForBinary *psOptionsForBinary)
1406 : {
1407 70 : auto psOptions = std::make_unique<GDALMultiDimInfoOptions>();
1408 :
1409 : /* -------------------------------------------------------------------- */
1410 : /* Parse arguments. */
1411 : /* -------------------------------------------------------------------- */
1412 :
1413 70 : CPLStringList aosArgv;
1414 :
1415 35 : if (papszArgv)
1416 : {
1417 16 : const int nArgc = CSLCount(papszArgv);
1418 49 : for (int i = 0; i < nArgc; i++)
1419 33 : aosArgv.AddString(papszArgv[i]);
1420 : }
1421 :
1422 : try
1423 : {
1424 : auto argParser = GDALMultiDimInfoAppOptionsGetParser(
1425 70 : psOptions.get(), psOptionsForBinary);
1426 35 : argParser->parse_args_without_binary_name(aosArgv);
1427 : }
1428 0 : catch (const std::exception &err)
1429 : {
1430 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
1431 0 : err.what());
1432 0 : return nullptr;
1433 : }
1434 :
1435 35 : return psOptions.release();
1436 : }
1437 :
1438 : /************************************************************************/
1439 : /* GDALMultiDimInfoOptionsFree() */
1440 : /************************************************************************/
1441 :
1442 : /**
1443 : * Frees the GDALMultiDimInfoOptions struct.
1444 : *
1445 : * @param psOptions the options struct for GDALMultiDimInfo().
1446 : *
1447 : * @since GDAL 3.1
1448 : */
1449 :
1450 34 : void GDALMultiDimInfoOptionsFree(GDALMultiDimInfoOptions *psOptions)
1451 : {
1452 34 : delete psOptions;
1453 34 : }
|