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