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 32 : static bool HasUniqueNames(const std::vector<std::string> &oNames)
51 : {
52 64 : std::set<std::string> oSetNames;
53 116 : for (const auto &subgroupName : oNames)
54 : {
55 84 : if (oSetNames.find(subgroupName) != oSetNames.end())
56 : {
57 0 : return false;
58 : }
59 84 : oSetNames.insert(subgroupName);
60 : }
61 32 : return true;
62 : }
63 :
64 : /************************************************************************/
65 : /* DumpDataType() */
66 : /************************************************************************/
67 :
68 100 : static void DumpDataType(const GDALExtendedDataType &dt,
69 : CPLJSonStreamingWriter &serializer)
70 : {
71 100 : switch (dt.GetClass())
72 : {
73 25 : case GEDTC_STRING:
74 25 : serializer.Add("String");
75 25 : break;
76 :
77 68 : case GEDTC_NUMERIC:
78 68 : serializer.Add(GDALGetDataTypeName(dt.GetNumericDataType()));
79 68 : 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 100 : }
105 :
106 : /************************************************************************/
107 : /* DumpValue() */
108 : /************************************************************************/
109 :
110 : template <typename T>
111 148 : static void DumpValue(CPLJSonStreamingWriter &serializer, const void *bytes)
112 : {
113 : T tmp;
114 148 : memcpy(&tmp, bytes, sizeof(T));
115 148 : serializer.Add(tmp);
116 148 : }
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 148 : static void DumpValue(CPLJSonStreamingWriter &serializer, const GByte *bytes,
138 : const GDALDataType &eDT)
139 : {
140 148 : switch (eDT)
141 : {
142 22 : case GDT_Byte:
143 22 : DumpValue<GByte>(serializer, bytes);
144 22 : 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 19 : case GDT_UInt16:
152 19 : DumpValue<GUInt16>(serializer, bytes);
153 19 : 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 58 : case GDT_Float64:
173 58 : DumpValue<double>(serializer, bytes);
174 58 : 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 148 : }
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 136 : static void DumpValue(CPLJSonStreamingWriter &serializer, const GByte *values,
222 : const GDALExtendedDataType &dt)
223 : {
224 136 : switch (dt.GetClass())
225 : {
226 112 : case GEDTC_NUMERIC:
227 112 : DumpValue(serializer, values, dt.GetNumericDataType());
228 112 : 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 136 : }
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 70 : static void DumpAttrValue(const std::shared_ptr<GDALAttribute> &attr,
326 : CPLJSonStreamingWriter &serializer)
327 : {
328 70 : const auto &dt = attr->GetDataType();
329 70 : const size_t nEltCount(static_cast<size_t>(attr->GetTotalElementsCount()));
330 70 : switch (dt.GetClass())
331 : {
332 43 : case GEDTC_STRING:
333 : {
334 43 : if (nEltCount == 1)
335 : {
336 41 : const char *pszStr = attr->ReadAsString();
337 41 : if (pszStr)
338 : {
339 41 : 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 32 : 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 43 : break;
370 : }
371 :
372 27 : case GEDTC_NUMERIC:
373 : {
374 27 : auto eDT = dt.GetNumericDataType();
375 54 : const auto rawValues(attr->ReadAsRaw());
376 27 : const GByte *bytePtr = rawValues.data();
377 27 : if (bytePtr)
378 : {
379 27 : const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
380 27 : 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 18 : serializer.MakeArrayContext(nEltCount < 10));
390 27 : for (size_t i = 0; i < nEltCount; i++)
391 : {
392 18 : DumpValue(serializer, bytePtr, eDT);
393 18 : bytePtr += nDTSize;
394 : }
395 : }
396 : }
397 : else
398 : {
399 0 : serializer.AddNull();
400 : }
401 27 : 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 70 : }
434 :
435 : /************************************************************************/
436 : /* DumpAttr() */
437 : /************************************************************************/
438 :
439 70 : static void DumpAttr(std::shared_ptr<GDALAttribute> attr,
440 : CPLJSonStreamingWriter &serializer,
441 : const GDALMultiDimInfoOptions *psOptions,
442 : bool bOutputObjType, bool bOutputName)
443 : {
444 70 : if (!bOutputObjType && !bOutputName && !psOptions->bDetailed)
445 : {
446 62 : DumpAttrValue(attr, serializer);
447 62 : return;
448 : }
449 :
450 8 : const auto &dt = attr->GetDataType();
451 16 : auto objectContext(serializer.MakeObjectContext());
452 8 : if (bOutputObjType)
453 : {
454 0 : serializer.AddObjKey("type");
455 0 : serializer.Add("attribute");
456 : }
457 8 : if (bOutputName)
458 : {
459 0 : serializer.AddObjKey("name");
460 0 : serializer.Add(attr->GetName());
461 : }
462 :
463 8 : if (psOptions->bDetailed)
464 : {
465 8 : serializer.AddObjKey("datatype");
466 8 : DumpDataType(dt, serializer);
467 :
468 8 : switch (dt.GetSubType())
469 : {
470 8 : case GEDTST_NONE:
471 8 : break;
472 0 : case GEDTST_JSON:
473 : {
474 0 : serializer.AddObjKey("subtype");
475 0 : serializer.Add("JSON");
476 0 : break;
477 : }
478 : }
479 :
480 8 : serializer.AddObjKey("value");
481 : }
482 :
483 8 : DumpAttrValue(attr, serializer);
484 : }
485 :
486 : /************************************************************************/
487 : /* DumpAttrs() */
488 : /************************************************************************/
489 :
490 23 : static void DumpAttrs(const std::vector<std::shared_ptr<GDALAttribute>> &attrs,
491 : CPLJSonStreamingWriter &serializer,
492 : const GDALMultiDimInfoOptions *psOptions)
493 : {
494 46 : std::vector<std::string> attributeNames;
495 93 : for (const auto &poAttr : attrs)
496 70 : attributeNames.emplace_back(poAttr->GetName());
497 23 : if (HasUniqueNames(attributeNames))
498 : {
499 46 : auto objectContext(serializer.MakeObjectContext());
500 93 : for (const auto &poAttr : attrs)
501 : {
502 70 : serializer.AddObjKey(poAttr->GetName());
503 70 : 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 23 : }
515 :
516 : /************************************************************************/
517 : /* DumpArrayRec() */
518 : /************************************************************************/
519 :
520 24 : 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 24 : auto arrayContext(serializer.MakeArrayContext());
529 24 : if (nCurDim + 1 == dimSizes.size())
530 : {
531 21 : const auto &dt(array->GetDataType());
532 21 : const auto nDTSize(dt.GetSize());
533 : const auto lambdaDumpValue =
534 19 : [&serializer, &dt, nDTSize](std::vector<GByte> &abyTmp,
535 198 : size_t nCount)
536 : {
537 19 : GByte *pabyPtr = &abyTmp[0];
538 85 : for (size_t i = 0; i < nCount; ++i)
539 : {
540 66 : DumpValue(serializer, pabyPtr, dt);
541 66 : dt.FreeDynamicMemory(pabyPtr);
542 66 : pabyPtr += nDTSize;
543 : }
544 40 : };
545 :
546 21 : serializer.SetNewline(false);
547 21 : std::vector<size_t> count(dimSizes.size(), 1);
548 21 : if (psOptions->nLimitValuesByDim == 0 ||
549 0 : 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 0 : nDTSize * (psOptions->nLimitValuesByDim + 1) / 2);
572 0 : startIdx.back() = 0;
573 0 : size_t nStartCount = (psOptions->nLimitValuesByDim + 1) / 2;
574 0 : count.back() = nStartCount;
575 0 : if (!array->Read(startIdx.data(), count.data(), nullptr,
576 0 : nullptr, dt, &abyTmp[0]))
577 0 : break;
578 0 : lambdaDumpValue(abyTmp, count.back());
579 0 : serializer.Add("[...]");
580 :
581 0 : count.back() = psOptions->nLimitValuesByDim / 2;
582 0 : if (count.back())
583 : {
584 0 : startIdx.back() = dimSizes.back() - count.back();
585 0 : if (!array->Read(startIdx.data(), count.data(), nullptr,
586 0 : nullptr, dt, &abyTmp[0]))
587 0 : break;
588 0 : lambdaDumpValue(abyTmp, count.back());
589 : }
590 : }
591 : }
592 : else
593 : {
594 3 : if (psOptions->nLimitValuesByDim == 0 ||
595 0 : 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 0 : size_t nStartCount = (psOptions->nLimitValuesByDim + 1) / 2;
607 0 : for (startIdx[nCurDim] = 0; startIdx[nCurDim] < nStartCount;
608 0 : ++startIdx[nCurDim])
609 : {
610 0 : DumpArrayRec(array, serializer, nCurDim + 1, dimSizes,
611 : startIdx, psOptions);
612 : }
613 0 : serializer.Add("[...]");
614 0 : size_t nEndCount = psOptions->nLimitValuesByDim / 2;
615 0 : for (startIdx[nCurDim] = dimSizes[nCurDim] - nEndCount;
616 0 : startIdx[nCurDim] < dimSizes[nCurDim]; ++startIdx[nCurDim])
617 : {
618 0 : DumpArrayRec(array, serializer, nCurDim + 1, dimSizes,
619 : startIdx, psOptions);
620 : }
621 : }
622 : }
623 : } while (false);
624 24 : serializer.SetNewline(true);
625 24 : }
626 :
627 : /************************************************************************/
628 : /* DumpDimensions() */
629 : /************************************************************************/
630 :
631 : static void
632 85 : 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 170 : auto arrayContext(serializer.MakeArrayContext());
639 214 : for (const auto &dim : dims)
640 : {
641 129 : const std::string osFullname(dim->GetFullName());
642 129 : if (alreadyDumpedDimensions.find(osFullname) !=
643 258 : alreadyDumpedDimensions.end())
644 : {
645 85 : serializer.Add(osFullname);
646 85 : continue;
647 : }
648 :
649 88 : auto dimObjectContext(serializer.MakeObjectContext());
650 44 : if (!osFullname.empty() && osFullname[0] == '/')
651 38 : alreadyDumpedDimensions.insert(osFullname);
652 :
653 44 : serializer.AddObjKey("name");
654 44 : serializer.Add(dim->GetName());
655 :
656 44 : serializer.AddObjKey("full_name");
657 44 : serializer.Add(osFullname);
658 :
659 44 : serializer.AddObjKey("size");
660 44 : serializer.Add(static_cast<std::uint64_t>(dim->GetSize()));
661 :
662 44 : const auto &type(dim->GetType());
663 44 : if (!type.empty())
664 : {
665 27 : serializer.AddObjKey("type");
666 27 : serializer.Add(type);
667 : }
668 :
669 44 : const auto &direction(dim->GetDirection());
670 44 : if (!direction.empty())
671 : {
672 17 : serializer.AddObjKey("direction");
673 17 : serializer.Add(direction);
674 : }
675 :
676 88 : auto poIndexingVariable(dim->GetIndexingVariable());
677 44 : if (poIndexingVariable)
678 : {
679 26 : serializer.AddObjKey("indexing_variable");
680 26 : if (rootGroup->OpenMDArray(poIndexingVariable->GetFullName()))
681 : {
682 0 : serializer.Add(poIndexingVariable->GetFullName());
683 : }
684 : else
685 : {
686 : std::set<std::string> alreadyDumpedDimensionsLocal(
687 52 : alreadyDumpedDimensions);
688 26 : alreadyDumpedDimensionsLocal.insert(osFullname);
689 :
690 52 : auto indexingVariableContext(serializer.MakeObjectContext());
691 26 : serializer.AddObjKey(poIndexingVariable->GetName());
692 26 : DumpArray(rootGroup, poIndexingVariable, serializer, psOptions,
693 : alreadyDumpedDimensionsLocal,
694 : /* bOutputObjType = */ false,
695 : /* bOutputName = */ false);
696 : }
697 : }
698 : }
699 85 : }
700 :
701 : /************************************************************************/
702 : /* DumpStructuralInfo() */
703 : /************************************************************************/
704 :
705 5 : static void DumpStructuralInfo(CSLConstList papszStructuralInfo,
706 : CPLJSonStreamingWriter &serializer)
707 : {
708 10 : auto objectContext(serializer.MakeObjectContext());
709 5 : int i = 1;
710 10 : for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(
711 15 : papszStructuralInfo, /* bReturnNullKeyIfNotNameValue = */ true))
712 : {
713 5 : if (pszKey)
714 : {
715 5 : serializer.AddObjKey(pszKey);
716 : }
717 : else
718 : {
719 0 : serializer.AddObjKey(CPLSPrintf("metadata_%d", i));
720 0 : ++i;
721 : }
722 5 : serializer.Add(pszValue);
723 : }
724 5 : }
725 :
726 : /************************************************************************/
727 : /* DumpArray() */
728 : /************************************************************************/
729 :
730 73 : 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 146 : auto objectContext(serializer.MakeObjectContext());
738 73 : if (bOutputObjType)
739 : {
740 1 : serializer.AddObjKey("type");
741 1 : serializer.Add("array");
742 : }
743 73 : if (bOutputName)
744 : {
745 1 : serializer.AddObjKey("name");
746 1 : serializer.Add(array->GetName());
747 : }
748 :
749 73 : serializer.AddObjKey("datatype");
750 73 : const auto &dt(array->GetDataType());
751 73 : DumpDataType(dt, serializer);
752 :
753 146 : auto dims = array->GetDimensions();
754 73 : if (!dims.empty())
755 : {
756 73 : serializer.AddObjKey("dimensions");
757 73 : DumpDimensions(rootGroup, dims, serializer, psOptions,
758 : alreadyDumpedDimensions);
759 :
760 73 : serializer.AddObjKey("dimension_size");
761 146 : auto arrayContext(serializer.MakeArrayContext());
762 173 : for (const auto &poDim : dims)
763 : {
764 100 : serializer.Add(static_cast<uint64_t>(poDim->GetSize()));
765 : }
766 : }
767 :
768 73 : bool hasNonNullBlockSize = false;
769 146 : const auto blockSize = array->GetBlockSize();
770 159 : for (auto v : blockSize)
771 : {
772 98 : if (v != 0)
773 : {
774 12 : hasNonNullBlockSize = true;
775 12 : break;
776 : }
777 : }
778 73 : if (hasNonNullBlockSize)
779 : {
780 12 : serializer.AddObjKey("block_size");
781 24 : auto arrayContext(serializer.MakeArrayContext());
782 26 : for (auto v : blockSize)
783 : {
784 14 : serializer.Add(static_cast<uint64_t>(v));
785 : }
786 : }
787 :
788 146 : CPLStringList aosOptions;
789 73 : if (psOptions->bDetailed)
790 16 : aosOptions.SetNameValue("SHOW_ALL", "YES");
791 146 : auto attrs = array->GetAttributes(aosOptions.List());
792 73 : if (!attrs.empty())
793 : {
794 13 : serializer.AddObjKey("attributes");
795 13 : DumpAttrs(attrs, serializer, psOptions);
796 : }
797 :
798 73 : const auto &unit = array->GetUnit();
799 73 : if (!unit.empty())
800 : {
801 7 : serializer.AddObjKey("unit");
802 7 : serializer.Add(unit);
803 : }
804 :
805 73 : auto nodata = array->GetRawNoDataValue();
806 73 : if (nodata)
807 : {
808 12 : serializer.AddObjKey("nodata_value");
809 12 : DumpValue(serializer, static_cast<const GByte *>(nodata), dt);
810 : }
811 :
812 73 : bool bValid = false;
813 73 : double dfOffset = array->GetOffset(&bValid);
814 73 : if (bValid)
815 : {
816 1 : serializer.AddObjKey("offset");
817 1 : serializer.Add(dfOffset);
818 : }
819 73 : double dfScale = array->GetScale(&bValid);
820 73 : if (bValid)
821 : {
822 1 : serializer.AddObjKey("scale");
823 1 : serializer.Add(dfScale);
824 : }
825 :
826 146 : auto srs = array->GetSpatialRef();
827 73 : if (srs)
828 : {
829 4 : char *pszWKT = nullptr;
830 8 : CPLStringList wktOptions;
831 4 : wktOptions.SetNameValue("FORMAT", "WKT2_2018");
832 4 : if (srs->exportToWkt(&pszWKT, wktOptions.List()) == OGRERR_NONE)
833 : {
834 4 : serializer.AddObjKey("srs");
835 : {
836 8 : auto srsContext(serializer.MakeObjectContext());
837 4 : serializer.AddObjKey("wkt");
838 4 : serializer.Add(pszWKT);
839 4 : serializer.AddObjKey("data_axis_to_srs_axis_mapping");
840 : {
841 8 : auto dataAxisContext(serializer.MakeArrayContext(true));
842 8 : auto mapping = srs->GetDataAxisToSRSAxisMapping();
843 12 : for (const auto &axisNumber : mapping)
844 8 : serializer.Add(axisNumber);
845 : }
846 : }
847 : }
848 4 : CPLFree(pszWKT);
849 : }
850 :
851 73 : auto papszStructuralInfo = array->GetStructuralInfo();
852 73 : if (papszStructuralInfo)
853 : {
854 0 : serializer.AddObjKey("structural_info");
855 0 : DumpStructuralInfo(papszStructuralInfo, serializer);
856 : }
857 :
858 73 : if (psOptions->bDetailed)
859 : {
860 16 : serializer.AddObjKey("values");
861 16 : 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 32 : std::vector<GUInt64> startIdx(dims.size());
870 16 : std::vector<GUInt64> dimSizes;
871 35 : for (const auto &dim : dims)
872 19 : dimSizes.emplace_back(dim->GetSize());
873 16 : DumpArrayRec(array, serializer, 0, dimSizes, startIdx, psOptions);
874 : }
875 : }
876 :
877 73 : if (psOptions->bStats)
878 : {
879 0 : double dfMin = 0.0;
880 0 : double dfMax = 0.0;
881 0 : double dfMean = 0.0;
882 0 : double dfStdDev = 0.0;
883 0 : GUInt64 nValidCount = 0;
884 0 : if (array->GetStatistics(false, true, &dfMin, &dfMax, &dfMean,
885 : &dfStdDev, &nValidCount, nullptr,
886 0 : nullptr) == CE_None)
887 : {
888 0 : serializer.AddObjKey("statistics");
889 0 : auto statContext(serializer.MakeObjectContext());
890 0 : if (nValidCount > 0)
891 : {
892 0 : serializer.AddObjKey("min");
893 0 : serializer.Add(dfMin);
894 :
895 0 : serializer.AddObjKey("max");
896 0 : serializer.Add(dfMax);
897 :
898 0 : serializer.AddObjKey("mean");
899 0 : serializer.Add(dfMean);
900 :
901 0 : serializer.AddObjKey("stddev");
902 0 : serializer.Add(dfStdDev);
903 : }
904 :
905 0 : serializer.AddObjKey("valid_sample_count");
906 0 : serializer.Add(static_cast<std::uint64_t>(nValidCount));
907 : }
908 : }
909 73 : }
910 :
911 : /************************************************************************/
912 : /* DumpArrays() */
913 : /************************************************************************/
914 :
915 23 : 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 46 : std::set<std::string> oSetNames;
923 46 : auto objectContext(serializer.MakeObjectContext());
924 69 : for (const auto &name : arrayNames)
925 : {
926 46 : if (oSetNames.find(name) != oSetNames.end())
927 0 : continue; // should not happen on well behaved drivers
928 46 : oSetNames.insert(name);
929 92 : auto array = group->OpenMDArray(name);
930 46 : if (array)
931 : {
932 46 : serializer.AddObjKey(array->GetName());
933 46 : DumpArray(rootGroup, array, serializer, psOptions,
934 : alreadyDumpedDimensions, false, false);
935 : }
936 : }
937 23 : }
938 :
939 : /************************************************************************/
940 : /* DumpGroup() */
941 : /************************************************************************/
942 :
943 34 : 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 68 : auto objectContext(serializer.MakeObjectContext());
952 34 : if (bOutputObjType)
953 : {
954 20 : serializer.AddObjKey("type");
955 20 : serializer.Add("group");
956 : }
957 34 : if (pszDriverName)
958 : {
959 20 : serializer.AddObjKey("driver");
960 20 : serializer.Add(pszDriverName);
961 : }
962 34 : if (bOutputName)
963 : {
964 20 : serializer.AddObjKey("name");
965 20 : serializer.Add(group->GetName());
966 :
967 : // If the root group is not actually the root, print its full path
968 20 : if (pszDriverName != nullptr && group->GetName() != "/")
969 : {
970 0 : serializer.AddObjKey("full_name");
971 0 : serializer.Add(group->GetFullName());
972 : }
973 : }
974 :
975 68 : CPLStringList aosOptionsGetAttr;
976 34 : if (psOptions->bDetailed)
977 11 : aosOptionsGetAttr.SetNameValue("SHOW_ALL", "YES");
978 68 : auto attrs = group->GetAttributes(aosOptionsGetAttr.List());
979 34 : if (!attrs.empty())
980 : {
981 10 : serializer.AddObjKey("attributes");
982 10 : DumpAttrs(attrs, serializer, psOptions);
983 : }
984 :
985 68 : auto dims = group->GetDimensions();
986 34 : if (!dims.empty())
987 : {
988 12 : serializer.AddObjKey("dimensions");
989 12 : DumpDimensions(rootGroup, dims, serializer, psOptions,
990 : alreadyDumpedDimensions);
991 : }
992 :
993 68 : CPLStringList aosOptionsGetArray(psOptions->aosArrayOptions);
994 34 : if (psOptions->bDetailed)
995 11 : aosOptionsGetArray.SetNameValue("SHOW_ALL", "YES");
996 68 : auto arrayNames = group->GetMDArrayNames(aosOptionsGetArray.List());
997 34 : if (!arrayNames.empty())
998 : {
999 23 : serializer.AddObjKey("arrays");
1000 23 : DumpArrays(rootGroup, group, arrayNames, serializer, psOptions,
1001 : alreadyDumpedDimensions);
1002 : }
1003 :
1004 34 : auto papszStructuralInfo = group->GetStructuralInfo();
1005 34 : if (papszStructuralInfo)
1006 : {
1007 5 : serializer.AddObjKey("structural_info");
1008 5 : DumpStructuralInfo(papszStructuralInfo, serializer);
1009 : }
1010 :
1011 68 : auto subgroupNames = group->GetGroupNames();
1012 34 : if (!subgroupNames.empty())
1013 : {
1014 9 : serializer.AddObjKey("groups");
1015 9 : if (HasUniqueNames(subgroupNames))
1016 : {
1017 18 : auto groupContext(serializer.MakeObjectContext());
1018 23 : for (const auto &subgroupName : subgroupNames)
1019 : {
1020 28 : auto subgroup = group->OpenGroup(subgroupName);
1021 14 : if (subgroup)
1022 : {
1023 14 : serializer.AddObjKey(subgroupName);
1024 14 : 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 34 : }
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 24 : static std::unique_ptr<GDALArgumentParser> GDALMultiDimInfoAppOptionsGetParser(
1055 : GDALMultiDimInfoOptions *psOptions,
1056 : GDALMultiDimInfoOptionsForBinary *psOptionsForBinary)
1057 : {
1058 : auto argParser = std::make_unique<GDALArgumentParser>(
1059 24 : "gdalmdiminfo", /* bForBinary=*/psOptionsForBinary != nullptr);
1060 :
1061 24 : argParser->add_description(
1062 24 : _("Lists various information about a GDAL multidimensional dataset."));
1063 :
1064 24 : argParser->add_epilog(_("For more details, consult "
1065 24 : "https://gdal.org/programs/gdalmdiminfo.html"));
1066 :
1067 24 : argParser->add_argument("-detailed")
1068 24 : .flag()
1069 24 : .store_into(psOptions->bDetailed)
1070 : .help(_("Most verbose output. Report attribute data types and array "
1071 24 : "values."));
1072 :
1073 : argParser->add_inverted_logic_flag(
1074 : "-nopretty", &psOptions->bPretty,
1075 24 : _("Outputs on a single line without any indentation."));
1076 :
1077 24 : argParser->add_argument("-array")
1078 48 : .metavar("<array_name>")
1079 24 : .store_into(psOptions->osArrayName)
1080 : .help(_("Name of the array, used to restrict the output to the "
1081 24 : "specified array."));
1082 :
1083 24 : argParser->add_argument("-limit")
1084 48 : .metavar("<number>")
1085 24 : .scan<'i', int>()
1086 24 : .store_into(psOptions->nLimitValuesByDim)
1087 : .help(_("Number of values in each dimension that is used to limit the "
1088 24 : "display of array values."));
1089 :
1090 24 : 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 24 : argParser->add_argument("-arrayoption")
1105 48 : .metavar("<NAME>=<VALUE>")
1106 24 : .append()
1107 1 : .action([psOptions](const std::string &s)
1108 25 : { psOptions->aosArrayOptions.AddString(s.c_str()); })
1109 : .help(_("Option passed to GDALGroup::GetMDArrayNames() to filter "
1110 24 : "reported arrays."));
1111 :
1112 24 : argParser->add_argument("-stats")
1113 24 : .flag()
1114 24 : .store_into(psOptions->bStats)
1115 24 : .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 24 : argParser->add_argument("-stdout").flag().hidden().store_into(
1119 24 : psOptions->bStdoutOutput);
1120 :
1121 24 : 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 22 : char *GDALMultiDimInfo(GDALDatasetH hDataset,
1171 : const GDALMultiDimInfoOptions *psOptionsIn)
1172 : {
1173 22 : if (hDataset == nullptr)
1174 0 : return nullptr;
1175 :
1176 44 : GDALMultiDimInfoOptions oOptionsDefault;
1177 22 : const GDALMultiDimInfoOptions *psOptions =
1178 22 : psOptionsIn ? psOptionsIn : &oOptionsDefault;
1179 : CPLJSonStreamingWriter serializer(
1180 44 : psOptions->bStdoutOutput ? WriteToStdout : nullptr, nullptr);
1181 22 : serializer.SetPrettyFormatting(psOptions->bPretty);
1182 22 : GDALDataset *poDS = GDALDataset::FromHandle(hDataset);
1183 44 : auto group = poDS->GetRootGroup();
1184 22 : if (!group)
1185 1 : return nullptr;
1186 :
1187 42 : std::set<std::string> alreadyDumpedDimensions;
1188 : try
1189 : {
1190 21 : if (psOptions->osArrayName.empty())
1191 : {
1192 20 : const char *pszDriverName = nullptr;
1193 20 : auto poDriver = poDS->GetDriver();
1194 20 : if (poDriver)
1195 20 : pszDriverName = poDriver->GetDescription();
1196 20 : DumpGroup(group, group, pszDriverName, serializer, psOptions,
1197 : alreadyDumpedDimensions, true, true);
1198 : }
1199 : else
1200 : {
1201 1 : auto curGroup = group;
1202 : CPLStringList aosTokens(
1203 1 : CSLTokenizeString2(psOptions->osArrayName.c_str(), "/", 0));
1204 1 : 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 1 : const char *pszArrayName = aosTokens.back();
1216 2 : auto array(curGroup->OpenMDArray(pszArrayName));
1217 1 : if (!array)
1218 : {
1219 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find array %s",
1220 : pszArrayName);
1221 0 : return nullptr;
1222 : }
1223 1 : 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 21 : if (psOptions->bStdoutOutput)
1234 : {
1235 2 : printf("\n");
1236 : }
1237 : else
1238 : {
1239 19 : 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 24 : GDALMultiDimInfoOptionsNew(char **papszArgv,
1264 : GDALMultiDimInfoOptionsForBinary *psOptionsForBinary)
1265 : {
1266 48 : auto psOptions = std::make_unique<GDALMultiDimInfoOptions>();
1267 :
1268 : /* -------------------------------------------------------------------- */
1269 : /* Parse arguments. */
1270 : /* -------------------------------------------------------------------- */
1271 :
1272 48 : CPLStringList aosArgv;
1273 :
1274 24 : if (papszArgv)
1275 : {
1276 14 : const int nArgc = CSLCount(papszArgv);
1277 38 : for (int i = 0; i < nArgc; i++)
1278 24 : aosArgv.AddString(papszArgv[i]);
1279 : }
1280 :
1281 : try
1282 : {
1283 : auto argParser = GDALMultiDimInfoAppOptionsGetParser(
1284 48 : psOptions.get(), psOptionsForBinary);
1285 24 : 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 24 : 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 23 : void GDALMultiDimInfoOptionsFree(GDALMultiDimInfoOptions *psOptions)
1310 : {
1311 23 : delete psOptions;
1312 23 : }
|