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