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