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