Coverage Report

Created: 2026-04-01 06:20

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