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