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