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