Coverage Report

Created: 2026-02-14 06:52

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