• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15143862414

20 May 2025 05:20PM UTC coverage: 70.927% (+0.006%) from 70.921%
15143862414

Pull #12392

github

web-flow
Merge 6e44293c3 into 36cf82678
Pull Request #12392: GDALOverviews: Limit external file size in GDALRegenerateOverviewsMultiBand

182 of 202 new or added lines in 3 files covered. (90.1%)

21204 existing lines in 67 files now uncovered.

567713 of 800420 relevant lines covered (70.93%)

235755.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.46
/apps/gdalmdiminfo_lib.cpp
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
                      bool bOutputObjType, bool bOutputName);
31

32
/************************************************************************/
33
/*                       GDALMultiDimInfoOptions                        */
34
/************************************************************************/
35

36
struct GDALMultiDimInfoOptions
37
{
38
    bool bStdoutOutput = false;
39
    bool bDetailed = false;
40
    bool bPretty = true;
41
    size_t nLimitValuesByDim = 0;
42
    CPLStringList aosArrayOptions{};
43
    std::string osArrayName{};
44
    bool bStats = false;
45
};
46

47
/************************************************************************/
48
/*                         HasUniqueNames()                             */
49
/************************************************************************/
50

51
static bool HasUniqueNames(const std::vector<std::string> &oNames)
55✔
52
{
53
    std::set<std::string> oSetNames;
110✔
54
    for (const auto &subgroupName : oNames)
189✔
55
    {
56
        if (oSetNames.find(subgroupName) != oSetNames.end())
134✔
57
        {
UNCOV
58
            return false;
×
59
        }
60
        oSetNames.insert(subgroupName);
134✔
61
    }
62
    return true;
55✔
63
}
64

65
/************************************************************************/
66
/*                          DumpDataType()                              */
67
/************************************************************************/
68

69
static void DumpDataType(const GDALExtendedDataType &dt,
168✔
70
                         CPLJSonStreamingWriter &serializer)
71
{
72
    switch (dt.GetClass())
168✔
73
    {
74
        case GEDTC_STRING:
50✔
75
            serializer.Add("String");
50✔
76
            break;
50✔
77

78
        case GEDTC_NUMERIC:
111✔
79
        {
80
            auto poRAT = dt.GetRAT();
111✔
81
            if (poRAT)
111✔
82
            {
83
                auto objContext(serializer.MakeObjectContext());
2✔
84
                serializer.AddObjKey("name");
1✔
85
                serializer.Add(dt.GetName());
1✔
86
                serializer.AddObjKey("type");
1✔
87
                serializer.Add(GDALGetDataTypeName(dt.GetNumericDataType()));
1✔
88
                serializer.AddObjKey("attribute_table");
1✔
89
                auto arrayContext(serializer.MakeArrayContext());
2✔
90
                const int nRows = poRAT->GetRowCount();
1✔
91
                const int nCols = poRAT->GetColumnCount();
1✔
92
                for (int iRow = 0; iRow < nRows; ++iRow)
4✔
93
                {
94
                    auto obj2Context(serializer.MakeObjectContext());
6✔
95
                    for (int iCol = 0; iCol < nCols; ++iCol)
9✔
96
                    {
97
                        serializer.AddObjKey(poRAT->GetNameOfCol(iCol));
6✔
98
                        switch (poRAT->GetTypeOfCol(iCol))
6✔
99
                        {
100
                            case GFT_Integer:
3✔
101
                                serializer.Add(
3✔
102
                                    poRAT->GetValueAsInt(iRow, iCol));
3✔
103
                                break;
3✔
UNCOV
104
                            case GFT_Real:
×
UNCOV
105
                                serializer.Add(
×
UNCOV
106
                                    poRAT->GetValueAsDouble(iRow, iCol));
×
UNCOV
107
                                break;
×
108
                            case GFT_String:
3✔
109
                                serializer.Add(
3✔
110
                                    poRAT->GetValueAsString(iRow, iCol));
3✔
111
                                break;
3✔
112
                        }
113
                    }
114
                }
115
            }
116
            else
117
            {
118
                serializer.Add(GDALGetDataTypeName(dt.GetNumericDataType()));
110✔
119
            }
120
            break;
111✔
121
        }
122

123
        case GEDTC_COMPOUND:
7✔
124
        {
125
            auto compoundContext(serializer.MakeObjectContext());
14✔
126
            serializer.AddObjKey("name");
7✔
127
            serializer.Add(dt.GetName());
7✔
128
            serializer.AddObjKey("size");
7✔
129
            serializer.Add(static_cast<unsigned>(dt.GetSize()));
7✔
130
            serializer.AddObjKey("components");
7✔
131
            const auto &components = dt.GetComponents();
7✔
132
            auto componentsContext(serializer.MakeArrayContext());
14✔
133
            for (const auto &comp : components)
26✔
134
            {
135
                auto compContext(serializer.MakeObjectContext());
38✔
136
                serializer.AddObjKey("name");
19✔
137
                serializer.Add(comp->GetName());
19✔
138
                serializer.AddObjKey("offset");
19✔
139
                serializer.Add(static_cast<unsigned>(comp->GetOffset()));
19✔
140
                serializer.AddObjKey("type");
19✔
141
                DumpDataType(comp->GetType(), serializer);
19✔
142
            }
143
            break;
7✔
144
        }
145
    }
146
}
168✔
147

148
/************************************************************************/
149
/*                           DumpValue()                                */
150
/************************************************************************/
151

152
template <typename T>
153
static void DumpValue(CPLJSonStreamingWriter &serializer, const void *bytes)
189✔
154
{
155
    T tmp;
156
    memcpy(&tmp, bytes, sizeof(T));
189✔
157
    serializer.Add(tmp);
189✔
158
}
189✔
159

160
/************************************************************************/
161
/*                         DumpComplexValue()                           */
162
/************************************************************************/
163

164
template <typename T>
UNCOV
165
static void DumpComplexValue(CPLJSonStreamingWriter &serializer,
×
166
                             const GByte *bytes)
167
{
168
    auto objectContext(serializer.MakeObjectContext());
×
UNCOV
169
    serializer.AddObjKey("real");
×
UNCOV
170
    DumpValue<T>(serializer, bytes);
×
UNCOV
171
    serializer.AddObjKey("imag");
×
UNCOV
172
    DumpValue<T>(serializer, bytes + sizeof(T));
×
UNCOV
173
}
×
174

175
/************************************************************************/
176
/*                           DumpValue()                                */
177
/************************************************************************/
178

179
static void DumpValue(CPLJSonStreamingWriter &serializer, const GByte *bytes,
189✔
180
                      const GDALDataType &eDT)
181
{
182
    switch (eDT)
189✔
183
    {
184
        case GDT_Byte:
47✔
185
            DumpValue<GByte>(serializer, bytes);
47✔
186
            break;
47✔
187
        case GDT_Int8:
3✔
188
            DumpValue<GInt8>(serializer, bytes);
3✔
189
            break;
3✔
190
        case GDT_Int16:
9✔
191
            DumpValue<GInt16>(serializer, bytes);
9✔
192
            break;
9✔
193
        case GDT_UInt16:
25✔
194
            DumpValue<GUInt16>(serializer, bytes);
25✔
195
            break;
25✔
196
        case GDT_Int32:
13✔
197
            DumpValue<GInt32>(serializer, bytes);
13✔
198
            break;
13✔
199
        case GDT_UInt32:
2✔
200
            DumpValue<GUInt32>(serializer, bytes);
2✔
201
            break;
2✔
202
        case GDT_Int64:
7✔
203
            DumpValue<std::int64_t>(serializer, bytes);
7✔
204
            break;
7✔
205
        case GDT_UInt64:
1✔
206
            DumpValue<std::uint64_t>(serializer, bytes);
1✔
207
            break;
1✔
UNCOV
208
        case GDT_Float16:
×
UNCOV
209
            DumpValue<GFloat16>(serializer, bytes);
×
UNCOV
210
            break;
×
211
        case GDT_Float32:
14✔
212
            DumpValue<float>(serializer, bytes);
14✔
213
            break;
14✔
214
        case GDT_Float64:
68✔
215
            DumpValue<double>(serializer, bytes);
68✔
216
            break;
68✔
UNCOV
217
        case GDT_CInt16:
×
UNCOV
218
            DumpComplexValue<GInt16>(serializer, bytes);
×
UNCOV
219
            break;
×
UNCOV
220
        case GDT_CInt32:
×
UNCOV
221
            DumpComplexValue<GInt32>(serializer, bytes);
×
UNCOV
222
            break;
×
UNCOV
223
        case GDT_CFloat16:
×
UNCOV
224
            DumpComplexValue<GFloat16>(serializer, bytes);
×
UNCOV
225
            break;
×
UNCOV
226
        case GDT_CFloat32:
×
UNCOV
227
            DumpComplexValue<float>(serializer, bytes);
×
UNCOV
228
            break;
×
UNCOV
229
        case GDT_CFloat64:
×
UNCOV
230
            DumpComplexValue<double>(serializer, bytes);
×
UNCOV
231
            break;
×
UNCOV
232
        case GDT_Unknown:
×
233
        case GDT_TypeCount:
UNCOV
234
            CPLAssert(false);
×
235
            break;
236
    }
237
}
189✔
238

239
static void DumpValue(CPLJSonStreamingWriter &serializer, const GByte *values,
240
                      const GDALExtendedDataType &dt);
241

242
/************************************************************************/
243
/*                          DumpCompound()                              */
244
/************************************************************************/
245

246
static void DumpCompound(CPLJSonStreamingWriter &serializer,
21✔
247
                         const GByte *values, const GDALExtendedDataType &dt)
248
{
249
    CPLAssert(dt.GetClass() == GEDTC_COMPOUND);
21✔
250
    const auto &components = dt.GetComponents();
21✔
251
    auto objectContext(serializer.MakeObjectContext());
42✔
252
    for (const auto &comp : components)
79✔
253
    {
254
        serializer.AddObjKey(comp->GetName());
58✔
255
        DumpValue(serializer, values + comp->GetOffset(), comp->GetType());
58✔
256
    }
257
}
21✔
258

259
/************************************************************************/
260
/*                           DumpValue()                                */
261
/************************************************************************/
262

263
static void DumpValue(CPLJSonStreamingWriter &serializer, const GByte *values,
171✔
264
                      const GDALExtendedDataType &dt)
265
{
266
    switch (dt.GetClass())
171✔
267
    {
268
        case GEDTC_NUMERIC:
147✔
269
            DumpValue(serializer, values, dt.GetNumericDataType());
147✔
270
            break;
147✔
271
        case GEDTC_COMPOUND:
21✔
272
            DumpCompound(serializer, values, dt);
21✔
273
            break;
21✔
274
        case GEDTC_STRING:
3✔
275
        {
276
            const char *pszStr;
277
            // cppcheck-suppress pointerSize
278
            memcpy(&pszStr, values, sizeof(const char *));
3✔
279
            if (pszStr)
3✔
280
                serializer.Add(pszStr);
3✔
281
            else
UNCOV
282
                serializer.AddNull();
×
283
            break;
3✔
284
        }
285
    }
286
}
171✔
287

288
/************************************************************************/
289
/*                          SerializeJSON()                             */
290
/************************************************************************/
291

292
static void SerializeJSON(const CPLJSONObject &obj,
22✔
293
                          CPLJSonStreamingWriter &serializer)
294
{
295
    switch (obj.GetType())
22✔
296
    {
UNCOV
297
        case CPLJSONObject::Type::Unknown:
×
298
        {
UNCOV
299
            CPLAssert(false);
×
300
            break;
301
        }
302

UNCOV
303
        case CPLJSONObject::Type::Null:
×
304
        {
UNCOV
305
            serializer.AddNull();
×
UNCOV
306
            break;
×
307
        }
308

309
        case CPLJSONObject::Type::Object:
6✔
310
        {
311
            auto objectContext(serializer.MakeObjectContext());
12✔
312
            for (const auto &subobj : obj.GetChildren())
13✔
313
            {
314
                serializer.AddObjKey(subobj.GetName());
7✔
315
                SerializeJSON(subobj, serializer);
7✔
316
            }
317
            break;
6✔
318
        }
319

320
        case CPLJSONObject::Type::Array:
4✔
321
        {
322
            auto arrayContext(serializer.MakeArrayContext());
8✔
323
            const CPLJSONArray array = obj.ToArray();
8✔
324
            for (const auto &subobj : array)
12✔
325
            {
326
                SerializeJSON(subobj, serializer);
8✔
327
            }
328
            break;
4✔
329
        }
330

331
        case CPLJSONObject::Type::Boolean:
2✔
332
        {
333
            serializer.Add(obj.ToBool());
2✔
334
            break;
2✔
335
        }
336

337
        case CPLJSONObject::Type::String:
7✔
338
        {
339
            serializer.Add(obj.ToString());
7✔
340
            break;
7✔
341
        }
342

343
        case CPLJSONObject::Type::Integer:
2✔
344
        {
345
            serializer.Add(obj.ToInteger());
2✔
346
            break;
2✔
347
        }
348

UNCOV
349
        case CPLJSONObject::Type::Long:
×
350
        {
UNCOV
351
            serializer.Add(static_cast<int64_t>(obj.ToLong()));
×
UNCOV
352
            break;
×
353
        }
354

355
        case CPLJSONObject::Type::Double:
1✔
356
        {
357
            serializer.Add(obj.ToDouble());
1✔
358
            break;
1✔
359
        }
360
    }
361
}
22✔
362

363
/************************************************************************/
364
/*                          DumpAttrValue()                             */
365
/************************************************************************/
366

367
static void DumpAttrValue(const std::shared_ptr<GDALAttribute> &attr,
112✔
368
                          CPLJSonStreamingWriter &serializer)
369
{
370
    const auto &dt = attr->GetDataType();
112✔
371
    const size_t nEltCount(static_cast<size_t>(attr->GetTotalElementsCount()));
112✔
372
    switch (dt.GetClass())
112✔
373
    {
374
        case GEDTC_STRING:
82✔
375
        {
376
            if (nEltCount == 1)
82✔
377
            {
378
                const char *pszStr = attr->ReadAsString();
80✔
379
                if (pszStr)
80✔
380
                {
381
                    if (dt.GetSubType() == GEDTST_JSON)
80✔
382
                    {
383
                        CPLJSONDocument oDoc;
18✔
384
                        if (oDoc.LoadMemory(std::string(pszStr)))
9✔
385
                        {
386
                            SerializeJSON(oDoc.GetRoot(), serializer);
7✔
387
                        }
388
                        else
389
                        {
390
                            serializer.Add(pszStr);
2✔
391
                        }
392
                    }
393
                    else
394
                    {
395
                        serializer.Add(pszStr);
71✔
396
                    }
397
                }
398
            }
399
            else
400
            {
401
                CPLStringList aosValues(attr->ReadAsStringArray());
4✔
402
                {
403
                    auto arrayContextValues(
404
                        serializer.MakeArrayContext(nEltCount < 10));
4✔
405
                    for (int i = 0; i < aosValues.size(); ++i)
6✔
406
                    {
407
                        serializer.Add(aosValues[i]);
4✔
408
                    }
409
                }
410
            }
411
            break;
82✔
412
        }
413

414
        case GEDTC_NUMERIC:
30✔
415
        {
416
            auto eDT = dt.GetNumericDataType();
30✔
417
            const auto rawValues(attr->ReadAsRaw());
60✔
418
            const GByte *bytePtr = rawValues.data();
30✔
419
            if (bytePtr)
30✔
420
            {
421
                const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
30✔
422
                if (nEltCount == 1)
30✔
423
                {
424
                    serializer.SetNewline(false);
18✔
425
                    DumpValue(serializer, rawValues.data(), eDT);
18✔
426
                    serializer.SetNewline(true);
18✔
427
                }
428
                else
429
                {
430
                    auto arrayContextValues(
431
                        serializer.MakeArrayContext(nEltCount < 10));
24✔
432
                    for (size_t i = 0; i < nEltCount; i++)
36✔
433
                    {
434
                        DumpValue(serializer, bytePtr, eDT);
24✔
435
                        bytePtr += nDTSize;
24✔
436
                    }
437
                }
438
            }
439
            else
440
            {
UNCOV
441
                serializer.AddNull();
×
442
            }
443
            break;
30✔
444
        }
445

UNCOV
446
        case GEDTC_COMPOUND:
×
447
        {
UNCOV
448
            auto rawValues(attr->ReadAsRaw());
×
UNCOV
449
            const GByte *bytePtr = rawValues.data();
×
UNCOV
450
            if (bytePtr)
×
451
            {
UNCOV
452
                if (nEltCount == 1)
×
453
                {
454
                    serializer.SetNewline(false);
×
455
                    DumpCompound(serializer, bytePtr, dt);
×
UNCOV
456
                    serializer.SetNewline(true);
×
457
                }
458
                else
459
                {
460
                    auto arrayContextValues(serializer.MakeArrayContext());
×
UNCOV
461
                    for (size_t i = 0; i < nEltCount; i++)
×
462
                    {
UNCOV
463
                        DumpCompound(serializer, bytePtr, dt);
×
UNCOV
464
                        bytePtr += dt.GetSize();
×
465
                    }
466
                }
467
            }
468
            else
469
            {
UNCOV
470
                serializer.AddNull();
×
471
            }
472
            break;
×
473
        }
474
    }
475
}
112✔
476

477
/************************************************************************/
478
/*                              DumpAttr()                              */
479
/************************************************************************/
480

481
static void DumpAttr(std::shared_ptr<GDALAttribute> attr,
112✔
482
                     CPLJSonStreamingWriter &serializer,
483
                     const GDALMultiDimInfoOptions *psOptions,
484
                     bool bOutputObjType, bool bOutputName)
485
{
486
    if (!bOutputObjType && !bOutputName && !psOptions->bDetailed)
112✔
487
    {
488
        DumpAttrValue(attr, serializer);
94✔
489
        return;
94✔
490
    }
491

492
    const auto &dt = attr->GetDataType();
18✔
493
    auto objectContext(serializer.MakeObjectContext());
36✔
494
    if (bOutputObjType)
18✔
495
    {
UNCOV
496
        serializer.AddObjKey("type");
×
UNCOV
497
        serializer.Add("attribute");
×
498
    }
499
    if (bOutputName)
18✔
500
    {
UNCOV
501
        serializer.AddObjKey("name");
×
UNCOV
502
        serializer.Add(attr->GetName());
×
503
    }
504

505
    if (psOptions->bDetailed)
18✔
506
    {
507
        serializer.AddObjKey("datatype");
18✔
508
        DumpDataType(dt, serializer);
18✔
509

510
        switch (dt.GetSubType())
18✔
511
        {
512
            case GEDTST_NONE:
18✔
513
                break;
18✔
UNCOV
514
            case GEDTST_JSON:
×
515
            {
UNCOV
516
                serializer.AddObjKey("subtype");
×
UNCOV
517
                serializer.Add("JSON");
×
UNCOV
518
                break;
×
519
            }
520
        }
521

522
        serializer.AddObjKey("value");
18✔
523
    }
524

525
    DumpAttrValue(attr, serializer);
18✔
526
}
527

528
/************************************************************************/
529
/*                              DumpAttrs()                             */
530
/************************************************************************/
531

532
static void DumpAttrs(const std::vector<std::shared_ptr<GDALAttribute>> &attrs,
42✔
533
                      CPLJSonStreamingWriter &serializer,
534
                      const GDALMultiDimInfoOptions *psOptions)
535
{
536
    std::vector<std::string> attributeNames;
84✔
537
    for (const auto &poAttr : attrs)
154✔
538
        attributeNames.emplace_back(poAttr->GetName());
112✔
539
    if (HasUniqueNames(attributeNames))
42✔
540
    {
541
        auto objectContext(serializer.MakeObjectContext());
84✔
542
        for (const auto &poAttr : attrs)
154✔
543
        {
544
            serializer.AddObjKey(poAttr->GetName());
112✔
545
            DumpAttr(poAttr, serializer, psOptions, false, false);
112✔
546
        }
547
    }
548
    else
549
    {
UNCOV
550
        auto arrayContext(serializer.MakeArrayContext());
×
UNCOV
551
        for (const auto &poAttr : attrs)
×
552
        {
UNCOV
553
            DumpAttr(poAttr, serializer, psOptions, false, true);
×
554
        }
555
    }
556
}
42✔
557

558
/************************************************************************/
559
/*                            DumpArrayRec()                            */
560
/************************************************************************/
561

562
static void DumpArrayRec(std::shared_ptr<GDALMDArray> array,
32✔
563
                         CPLJSonStreamingWriter &serializer, size_t nCurDim,
564
                         const std::vector<GUInt64> &dimSizes,
565
                         std::vector<GUInt64> &startIdx,
566
                         const GDALMultiDimInfoOptions *psOptions)
567
{
568
    do
569
    {
570
        auto arrayContext(serializer.MakeArrayContext());
32✔
571
        if (nCurDim + 1 == dimSizes.size())
32✔
572
        {
573
            const auto &dt(array->GetDataType());
28✔
574
            const auto nDTSize(dt.GetSize());
28✔
575
            const auto lambdaDumpValue =
576
                [&serializer, &dt, nDTSize](std::vector<GByte> &abyTmp,
33✔
577
                                            size_t nCount)
303✔
578
            {
579
                GByte *pabyPtr = &abyTmp[0];
33✔
580
                for (size_t i = 0; i < nCount; ++i)
134✔
581
                {
582
                    DumpValue(serializer, pabyPtr, dt);
101✔
583
                    dt.FreeDynamicMemory(pabyPtr);
101✔
584
                    pabyPtr += nDTSize;
101✔
585
                }
586
            };
61✔
587

588
            serializer.SetNewline(false);
28✔
589
            std::vector<size_t> count(dimSizes.size(), 1);
28✔
590
            if (psOptions->nLimitValuesByDim == 0 ||
35✔
591
                dimSizes.back() <= psOptions->nLimitValuesByDim)
7✔
592
            {
593
                const size_t nCount = static_cast<size_t>(dimSizes.back());
21✔
594
                if (nCount > 0)
21✔
595
                {
596
                    if (nCount != dimSizes.back() ||
38✔
597
                        nDTSize > std::numeric_limits<size_t>::max() / nCount)
19✔
598
                    {
UNCOV
599
                        serializer.Add("[too many values]");
×
UNCOV
600
                        break;
×
601
                    }
602
                    std::vector<GByte> abyTmp(nDTSize * nCount);
19✔
603
                    count.back() = nCount;
19✔
604
                    if (!array->Read(startIdx.data(), count.data(), nullptr,
38✔
605
                                     nullptr, dt, &abyTmp[0]))
19✔
UNCOV
606
                        break;
×
607
                    lambdaDumpValue(abyTmp, count.back());
19✔
608
                }
609
            }
610
            else
611
            {
612
                std::vector<GByte> abyTmp(
613
                    nDTSize * (psOptions->nLimitValuesByDim + 1) / 2);
7✔
614
                startIdx.back() = 0;
7✔
615
                size_t nStartCount = (psOptions->nLimitValuesByDim + 1) / 2;
7✔
616
                count.back() = nStartCount;
7✔
617
                if (!array->Read(startIdx.data(), count.data(), nullptr,
14✔
618
                                 nullptr, dt, &abyTmp[0]))
7✔
UNCOV
619
                    break;
×
620
                lambdaDumpValue(abyTmp, count.back());
7✔
621
                serializer.Add("[...]");
7✔
622

623
                count.back() = psOptions->nLimitValuesByDim / 2;
7✔
624
                if (count.back())
7✔
625
                {
626
                    startIdx.back() = dimSizes.back() - count.back();
7✔
627
                    if (!array->Read(startIdx.data(), count.data(), nullptr,
14✔
628
                                     nullptr, dt, &abyTmp[0]))
7✔
UNCOV
629
                        break;
×
630
                    lambdaDumpValue(abyTmp, count.back());
7✔
631
                }
632
            }
633
        }
634
        else
635
        {
636
            if (psOptions->nLimitValuesByDim == 0 ||
5✔
637
                dimSizes[nCurDim] <= psOptions->nLimitValuesByDim)
1✔
638
            {
639
                for (startIdx[nCurDim] = 0;
11✔
640
                     startIdx[nCurDim] < dimSizes[nCurDim]; ++startIdx[nCurDim])
11✔
641
                {
642
                    DumpArrayRec(array, serializer, nCurDim + 1, dimSizes,
8✔
643
                                 startIdx, psOptions);
644
                }
645
            }
646
            else
647
            {
648
                size_t nStartCount = (psOptions->nLimitValuesByDim + 1) / 2;
1✔
649
                for (startIdx[nCurDim] = 0; startIdx[nCurDim] < nStartCount;
4✔
650
                     ++startIdx[nCurDim])
3✔
651
                {
652
                    DumpArrayRec(array, serializer, nCurDim + 1, dimSizes,
3✔
653
                                 startIdx, psOptions);
654
                }
655
                serializer.Add("[...]");
1✔
656
                size_t nEndCount = psOptions->nLimitValuesByDim / 2;
1✔
657
                for (startIdx[nCurDim] = dimSizes[nCurDim] - nEndCount;
3✔
658
                     startIdx[nCurDim] < dimSizes[nCurDim]; ++startIdx[nCurDim])
3✔
659
                {
660
                    DumpArrayRec(array, serializer, nCurDim + 1, dimSizes,
2✔
661
                                 startIdx, psOptions);
662
                }
663
            }
664
        }
665
    } while (false);
666
    serializer.SetNewline(true);
32✔
667
}
32✔
668

669
/************************************************************************/
670
/*                        DumpDimensions()                               */
671
/************************************************************************/
672

673
static void
674
DumpDimensions(const std::shared_ptr<GDALGroup> &rootGroup,
148✔
675
               const std::vector<std::shared_ptr<GDALDimension>> &dims,
676
               CPLJSonStreamingWriter &serializer,
677
               const GDALMultiDimInfoOptions *psOptions,
678
               std::set<std::string> &alreadyDumpedDimensions)
679
{
680
    auto arrayContext(serializer.MakeArrayContext());
296✔
681
    for (const auto &dim : dims)
377✔
682
    {
683
        const std::string osFullname(dim->GetFullName());
229✔
684
        if (alreadyDumpedDimensions.find(osFullname) !=
229✔
685
            alreadyDumpedDimensions.end())
458✔
686
        {
687
            serializer.Add(osFullname);
163✔
688
            continue;
163✔
689
        }
690

691
        auto dimObjectContext(serializer.MakeObjectContext());
132✔
692
        if (!osFullname.empty() && osFullname[0] == '/')
66✔
693
            alreadyDumpedDimensions.insert(osFullname);
60✔
694

695
        serializer.AddObjKey("name");
66✔
696
        serializer.Add(dim->GetName());
66✔
697

698
        serializer.AddObjKey("full_name");
66✔
699
        serializer.Add(osFullname);
66✔
700

701
        serializer.AddObjKey("size");
66✔
702
        serializer.Add(static_cast<std::uint64_t>(dim->GetSize()));
66✔
703

704
        const auto &type(dim->GetType());
66✔
705
        if (!type.empty())
66✔
706
        {
707
            serializer.AddObjKey("type");
49✔
708
            serializer.Add(type);
49✔
709
        }
710

711
        const auto &direction(dim->GetDirection());
66✔
712
        if (!direction.empty())
66✔
713
        {
714
            serializer.AddObjKey("direction");
25✔
715
            serializer.Add(direction);
25✔
716
        }
717

718
        auto poIndexingVariable(dim->GetIndexingVariable());
132✔
719
        if (poIndexingVariable)
66✔
720
        {
721
            serializer.AddObjKey("indexing_variable");
48✔
722
            if (rootGroup->OpenMDArray(poIndexingVariable->GetFullName()))
48✔
723
            {
UNCOV
724
                serializer.Add(poIndexingVariable->GetFullName());
×
725
            }
726
            else
727
            {
728
                std::set<std::string> alreadyDumpedDimensionsLocal(
729
                    alreadyDumpedDimensions);
96✔
730
                alreadyDumpedDimensionsLocal.insert(osFullname);
48✔
731

732
                auto indexingVariableContext(serializer.MakeObjectContext());
96✔
733
                serializer.AddObjKey(poIndexingVariable->GetName());
48✔
734
                DumpArray(rootGroup, poIndexingVariable, serializer, psOptions,
48✔
735
                          alreadyDumpedDimensionsLocal,
736
                          /* bOutputObjType = */ false,
737
                          /* bOutputName = */ false);
738
            }
739
        }
740
    }
741
}
148✔
742

743
/************************************************************************/
744
/*                        DumpStructuralInfo()                          */
745
/************************************************************************/
746

747
static void DumpStructuralInfo(CSLConstList papszStructuralInfo,
9✔
748
                               CPLJSonStreamingWriter &serializer)
749
{
750
    auto objectContext(serializer.MakeObjectContext());
18✔
751
    int i = 1;
9✔
752
    for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(
18✔
753
             papszStructuralInfo, /* bReturnNullKeyIfNotNameValue = */ true))
27✔
754
    {
755
        if (pszKey)
9✔
756
        {
757
            serializer.AddObjKey(pszKey);
9✔
758
        }
759
        else
760
        {
UNCOV
761
            serializer.AddObjKey(CPLSPrintf("metadata_%d", i));
×
UNCOV
762
            ++i;
×
763
        }
764
        serializer.Add(pszValue);
9✔
765
    }
766
}
9✔
767

768
/************************************************************************/
769
/*                             DumpArray()                              */
770
/************************************************************************/
771

772
static void DumpArray(const std::shared_ptr<GDALGroup> &rootGroup,
130✔
773
                      const std::shared_ptr<GDALMDArray> &array,
774
                      CPLJSonStreamingWriter &serializer,
775
                      const GDALMultiDimInfoOptions *psOptions,
776
                      std::set<std::string> &alreadyDumpedDimensions,
777
                      bool bOutputObjType, bool bOutputName)
778
{
779
    auto objectContext(serializer.MakeObjectContext());
260✔
780
    if (bOutputObjType)
130✔
781
    {
782
        serializer.AddObjKey("type");
2✔
783
        serializer.Add("array");
2✔
784
    }
785
    if (bOutputName)
130✔
786
    {
787
        serializer.AddObjKey("name");
2✔
788
        serializer.Add(array->GetName());
2✔
789
    }
790

791
    serializer.AddObjKey("datatype");
130✔
792
    const auto &dt(array->GetDataType());
130✔
793
    DumpDataType(dt, serializer);
130✔
794

795
    auto dims = array->GetDimensions();
260✔
796
    if (!dims.empty())
130✔
797
    {
798
        serializer.AddObjKey("dimensions");
130✔
799
        DumpDimensions(rootGroup, dims, serializer, psOptions,
130✔
800
                       alreadyDumpedDimensions);
801

802
        serializer.AddObjKey("dimension_size");
130✔
803
        auto arrayContext(serializer.MakeArrayContext());
260✔
804
        for (const auto &poDim : dims)
310✔
805
        {
806
            serializer.Add(static_cast<uint64_t>(poDim->GetSize()));
180✔
807
        }
808
    }
809

810
    bool hasNonNullBlockSize = false;
130✔
811
    const auto blockSize = array->GetBlockSize();
260✔
812
    for (auto v : blockSize)
294✔
813
    {
814
        if (v != 0)
177✔
815
        {
816
            hasNonNullBlockSize = true;
13✔
817
            break;
13✔
818
        }
819
    }
820
    if (hasNonNullBlockSize)
130✔
821
    {
822
        serializer.AddObjKey("block_size");
13✔
823
        auto arrayContext(serializer.MakeArrayContext());
26✔
824
        for (auto v : blockSize)
29✔
825
        {
826
            serializer.Add(static_cast<uint64_t>(v));
16✔
827
        }
828
    }
829

830
    CPLStringList aosOptions;
260✔
831
    if (psOptions->bDetailed)
130✔
832
        aosOptions.SetNameValue("SHOW_ALL", "YES");
19✔
833
    auto attrs = array->GetAttributes(aosOptions.List());
260✔
834
    if (!attrs.empty())
130✔
835
    {
836
        serializer.AddObjKey("attributes");
26✔
837
        DumpAttrs(attrs, serializer, psOptions);
26✔
838
    }
839

840
    const auto &unit = array->GetUnit();
130✔
841
    if (!unit.empty())
130✔
842
    {
843
        serializer.AddObjKey("unit");
17✔
844
        serializer.Add(unit);
17✔
845
    }
846

847
    auto nodata = array->GetRawNoDataValue();
130✔
848
    if (nodata)
130✔
849
    {
850
        serializer.AddObjKey("nodata_value");
12✔
851
        DumpValue(serializer, static_cast<const GByte *>(nodata), dt);
12✔
852
    }
853

854
    bool bValid = false;
130✔
855
    double dfOffset = array->GetOffset(&bValid);
130✔
856
    if (bValid)
130✔
857
    {
858
        serializer.AddObjKey("offset");
1✔
859
        serializer.Add(dfOffset);
1✔
860
    }
861
    double dfScale = array->GetScale(&bValid);
130✔
862
    if (bValid)
130✔
863
    {
864
        serializer.AddObjKey("scale");
1✔
865
        serializer.Add(dfScale);
1✔
866
    }
867

868
    auto srs = array->GetSpatialRef();
260✔
869
    if (srs)
130✔
870
    {
871
        char *pszWKT = nullptr;
7✔
872
        CPLStringList wktOptions;
14✔
873
        wktOptions.SetNameValue("FORMAT", "WKT2_2018");
7✔
874
        if (srs->exportToWkt(&pszWKT, wktOptions.List()) == OGRERR_NONE)
7✔
875
        {
876
            serializer.AddObjKey("srs");
7✔
877
            {
878
                auto srsContext(serializer.MakeObjectContext());
14✔
879
                serializer.AddObjKey("wkt");
7✔
880
                serializer.Add(pszWKT);
7✔
881
                serializer.AddObjKey("data_axis_to_srs_axis_mapping");
7✔
882
                {
883
                    auto dataAxisContext(serializer.MakeArrayContext(true));
14✔
884
                    auto mapping = srs->GetDataAxisToSRSAxisMapping();
14✔
885
                    for (const auto &axisNumber : mapping)
21✔
886
                        serializer.Add(axisNumber);
14✔
887
                }
888
            }
889
        }
890
        CPLFree(pszWKT);
7✔
891
    }
892

893
    auto papszStructuralInfo = array->GetStructuralInfo();
130✔
894
    if (papszStructuralInfo)
130✔
895
    {
896
        serializer.AddObjKey("structural_info");
1✔
897
        DumpStructuralInfo(papszStructuralInfo, serializer);
1✔
898
    }
899

900
    if (psOptions->bDetailed)
130✔
901
    {
902
        serializer.AddObjKey("values");
19✔
903
        if (dims.empty())
19✔
904
        {
UNCOV
905
            std::vector<GByte> abyTmp(dt.GetSize());
×
UNCOV
906
            array->Read(nullptr, nullptr, nullptr, nullptr, dt, &abyTmp[0]);
×
UNCOV
907
            DumpValue(serializer, &abyTmp[0], dt);
×
908
        }
909
        else
910
        {
911
            std::vector<GUInt64> startIdx(dims.size());
38✔
912
            std::vector<GUInt64> dimSizes;
19✔
913
            for (const auto &dim : dims)
42✔
914
                dimSizes.emplace_back(dim->GetSize());
23✔
915
            DumpArrayRec(array, serializer, 0, dimSizes, startIdx, psOptions);
19✔
916
        }
917
    }
918

919
    if (psOptions->bStats)
130✔
920
    {
921
        double dfMin = 0.0;
3✔
922
        double dfMax = 0.0;
3✔
923
        double dfMean = 0.0;
3✔
924
        double dfStdDev = 0.0;
3✔
925
        GUInt64 nValidCount = 0;
3✔
926
        if (array->GetStatistics(false, true, &dfMin, &dfMax, &dfMean,
3✔
927
                                 &dfStdDev, &nValidCount, nullptr,
928
                                 nullptr) == CE_None)
3✔
929
        {
930
            serializer.AddObjKey("statistics");
3✔
931
            auto statContext(serializer.MakeObjectContext());
6✔
932
            if (nValidCount > 0)
3✔
933
            {
934
                serializer.AddObjKey("min");
3✔
935
                serializer.Add(dfMin);
3✔
936

937
                serializer.AddObjKey("max");
3✔
938
                serializer.Add(dfMax);
3✔
939

940
                serializer.AddObjKey("mean");
3✔
941
                serializer.Add(dfMean);
3✔
942

943
                serializer.AddObjKey("stddev");
3✔
944
                serializer.Add(dfStdDev);
3✔
945
            }
946

947
            serializer.AddObjKey("valid_sample_count");
3✔
948
            serializer.Add(static_cast<std::uint64_t>(nValidCount));
3✔
949
        }
950
    }
951
}
130✔
952

953
/************************************************************************/
954
/*                            DumpArrays()                              */
955
/************************************************************************/
956

957
static void DumpArrays(const std::shared_ptr<GDALGroup> &rootGroup,
33✔
958
                       const std::shared_ptr<GDALGroup> &group,
959
                       const std::vector<std::string> &arrayNames,
960
                       CPLJSonStreamingWriter &serializer,
961
                       const GDALMultiDimInfoOptions *psOptions,
962
                       std::set<std::string> &alreadyDumpedDimensions)
963
{
964
    std::set<std::string> oSetNames;
66✔
965
    auto objectContext(serializer.MakeObjectContext());
66✔
966
    for (const auto &name : arrayNames)
113✔
967
    {
968
        if (oSetNames.find(name) != oSetNames.end())
80✔
UNCOV
969
            continue;  // should not happen on well behaved drivers
×
970
        oSetNames.insert(name);
80✔
971
        auto array = group->OpenMDArray(name);
160✔
972
        if (array)
80✔
973
        {
974
            serializer.AddObjKey(array->GetName());
80✔
975
            DumpArray(rootGroup, array, serializer, psOptions,
80✔
976
                      alreadyDumpedDimensions, false, false);
977
        }
978
    }
979
}
33✔
980

981
/************************************************************************/
982
/*                             DumpGroup()                              */
983
/************************************************************************/
984

985
static void DumpGroup(const std::shared_ptr<GDALGroup> &rootGroup,
49✔
986
                      const std::shared_ptr<GDALGroup> &group,
987
                      const char *pszDriverName,
988
                      CPLJSonStreamingWriter &serializer,
989
                      const GDALMultiDimInfoOptions *psOptions,
990
                      std::set<std::string> &alreadyDumpedDimensions,
991
                      bool bOutputObjType, bool bOutputName)
992
{
993
    auto objectContext(serializer.MakeObjectContext());
98✔
994
    if (bOutputObjType)
49✔
995
    {
996
        serializer.AddObjKey("type");
27✔
997
        serializer.Add("group");
27✔
998
    }
999
    if (pszDriverName)
49✔
1000
    {
1001
        serializer.AddObjKey("driver");
27✔
1002
        serializer.Add(pszDriverName);
27✔
1003
    }
1004
    if (bOutputName)
49✔
1005
    {
1006
        serializer.AddObjKey("name");
27✔
1007
        serializer.Add(group->GetName());
27✔
1008

1009
        // If the root group is not actually the root, print its full path
1010
        if (pszDriverName != nullptr && group->GetName() != "/")
27✔
1011
        {
UNCOV
1012
            serializer.AddObjKey("full_name");
×
UNCOV
1013
            serializer.Add(group->GetFullName());
×
1014
        }
1015
    }
1016

1017
    CPLStringList aosOptionsGetAttr;
98✔
1018
    if (psOptions->bDetailed)
49✔
1019
        aosOptionsGetAttr.SetNameValue("SHOW_ALL", "YES");
11✔
1020
    auto attrs = group->GetAttributes(aosOptionsGetAttr.List());
98✔
1021
    if (!attrs.empty())
49✔
1022
    {
1023
        serializer.AddObjKey("attributes");
16✔
1024
        DumpAttrs(attrs, serializer, psOptions);
16✔
1025
    }
1026

1027
    auto dims = group->GetDimensions();
98✔
1028
    if (!dims.empty())
49✔
1029
    {
1030
        serializer.AddObjKey("dimensions");
18✔
1031
        DumpDimensions(rootGroup, dims, serializer, psOptions,
18✔
1032
                       alreadyDumpedDimensions);
1033
    }
1034

1035
    const auto &types = group->GetDataTypes();
49✔
1036
    if (!types.empty())
49✔
1037
    {
1038
        serializer.AddObjKey("datatypes");
1✔
1039
        auto arrayContext(serializer.MakeArrayContext());
2✔
1040
        for (const auto &dt : types)
2✔
1041
        {
1042
            DumpDataType(*(dt.get()), serializer);
1✔
1043
        }
1044
    }
1045

1046
    CPLStringList aosOptionsGetArray(psOptions->aosArrayOptions);
98✔
1047
    if (psOptions->bDetailed)
49✔
1048
        aosOptionsGetArray.SetNameValue("SHOW_ALL", "YES");
11✔
1049
    auto arrayNames = group->GetMDArrayNames(aosOptionsGetArray.List());
98✔
1050
    if (!arrayNames.empty())
49✔
1051
    {
1052
        serializer.AddObjKey("arrays");
33✔
1053
        DumpArrays(rootGroup, group, arrayNames, serializer, psOptions,
33✔
1054
                   alreadyDumpedDimensions);
1055
    }
1056

1057
    auto papszStructuralInfo = group->GetStructuralInfo();
49✔
1058
    if (papszStructuralInfo)
49✔
1059
    {
1060
        serializer.AddObjKey("structural_info");
8✔
1061
        DumpStructuralInfo(papszStructuralInfo, serializer);
8✔
1062
    }
1063

1064
    auto subgroupNames = group->GetGroupNames();
98✔
1065
    if (!subgroupNames.empty())
49✔
1066
    {
1067
        serializer.AddObjKey("groups");
13✔
1068
        if (HasUniqueNames(subgroupNames))
13✔
1069
        {
1070
            auto groupContext(serializer.MakeObjectContext());
26✔
1071
            for (const auto &subgroupName : subgroupNames)
35✔
1072
            {
1073
                auto subgroup = group->OpenGroup(subgroupName);
44✔
1074
                if (subgroup)
22✔
1075
                {
1076
                    serializer.AddObjKey(subgroupName);
22✔
1077
                    DumpGroup(rootGroup, subgroup, nullptr, serializer,
22✔
1078
                              psOptions, alreadyDumpedDimensions, false, false);
1079
                }
1080
            }
1081
        }
1082
        else
1083
        {
UNCOV
1084
            auto arrayContext(serializer.MakeArrayContext());
×
UNCOV
1085
            for (const auto &subgroupName : subgroupNames)
×
1086
            {
UNCOV
1087
                auto subgroup = group->OpenGroup(subgroupName);
×
UNCOV
1088
                if (subgroup)
×
1089
                {
UNCOV
1090
                    DumpGroup(rootGroup, subgroup, nullptr, serializer,
×
1091
                              psOptions, alreadyDumpedDimensions, false, true);
1092
                }
1093
            }
1094
        }
1095
    }
1096
}
49✔
1097

1098
/************************************************************************/
1099
/*                           WriteToStdout()                            */
1100
/************************************************************************/
1101

1102
static void WriteToStdout(const char *pszText, void *)
1,410✔
1103
{
1104
    printf("%s", pszText);
1,410✔
1105
}
1,410✔
1106

1107
static std::unique_ptr<GDALArgumentParser> GDALMultiDimInfoAppOptionsGetParser(
32✔
1108
    GDALMultiDimInfoOptions *psOptions,
1109
    GDALMultiDimInfoOptionsForBinary *psOptionsForBinary)
1110
{
1111
    auto argParser = std::make_unique<GDALArgumentParser>(
1112
        "gdalmdiminfo", /* bForBinary=*/psOptionsForBinary != nullptr);
32✔
1113

1114
    argParser->add_description(
32✔
1115
        _("Lists various information about a GDAL multidimensional dataset."));
32✔
1116

1117
    argParser->add_epilog(_("For more details, consult "
32✔
1118
                            "https://gdal.org/programs/gdalmdiminfo.html"));
32✔
1119

1120
    argParser->add_argument("-detailed")
32✔
1121
        .flag()
32✔
1122
        .store_into(psOptions->bDetailed)
32✔
1123
        .help(_("Most verbose output. Report attribute data types and array "
1124
                "values."));
32✔
1125

1126
    argParser->add_inverted_logic_flag(
1127
        "-nopretty", &psOptions->bPretty,
1128
        _("Outputs on a single line without any indentation."));
32✔
1129

1130
    argParser->add_argument("-array")
32✔
1131
        .metavar("<array_name>")
64✔
1132
        .store_into(psOptions->osArrayName)
32✔
1133
        .help(_("Name of the array, used to restrict the output to the "
1134
                "specified array."));
32✔
1135

1136
    argParser->add_argument("-limit")
32✔
1137
        .metavar("<number>")
64✔
1138
        .scan<'i', int>()
32✔
1139
        .store_into(psOptions->nLimitValuesByDim)
32✔
1140
        .help(_("Number of values in each dimension that is used to limit the "
1141
                "display of array values."));
32✔
1142

1143
    if (psOptionsForBinary)
32✔
1144
    {
1145
        argParser->add_open_options_argument(
1146
            psOptionsForBinary->aosOpenOptions);
3✔
1147

1148
        argParser->add_input_format_argument(
1149
            &psOptionsForBinary->aosAllowInputDrivers);
3✔
1150

1151
        argParser->add_argument("dataset_name")
3✔
1152
            .metavar("<dataset_name>")
6✔
1153
            .store_into(psOptionsForBinary->osFilename)
3✔
1154
            .help("Input dataset.");
3✔
1155
    }
1156

1157
    argParser->add_argument("-arrayoption")
32✔
1158
        .metavar("<NAME>=<VALUE>")
64✔
1159
        .append()
32✔
1160
        .action([psOptions](const std::string &s)
2✔
1161
                { psOptions->aosArrayOptions.AddString(s.c_str()); })
34✔
1162
        .help(_("Option passed to GDALGroup::GetMDArrayNames() to filter "
1163
                "reported arrays."));
32✔
1164

1165
    argParser->add_argument("-stats")
32✔
1166
        .flag()
32✔
1167
        .store_into(psOptions->bStats)
32✔
1168
        .help(_("Read and display image statistics."));
32✔
1169

1170
    // Only used by gdalmdiminfo binary to write output to stdout instead of in a string, in JSON mode
1171
    argParser->add_argument("-stdout").flag().hidden().store_into(
32✔
1172
        psOptions->bStdoutOutput);
32✔
1173

1174
    return argParser;
32✔
1175
}
1176

1177
/************************************************************************/
1178
/*                  GDALMultiDimInfoAppGetParserUsage()                 */
1179
/************************************************************************/
1180

UNCOV
1181
std::string GDALMultiDimInfoAppGetParserUsage()
×
1182
{
1183
    try
1184
    {
UNCOV
1185
        GDALMultiDimInfoOptions sOptions;
×
UNCOV
1186
        GDALMultiDimInfoOptionsForBinary sOptionsForBinary;
×
1187
        auto argParser =
UNCOV
1188
            GDALMultiDimInfoAppOptionsGetParser(&sOptions, &sOptionsForBinary);
×
UNCOV
1189
        return argParser->usage();
×
1190
    }
UNCOV
1191
    catch (const std::exception &err)
×
1192
    {
UNCOV
1193
        CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
×
UNCOV
1194
                 err.what());
×
UNCOV
1195
        return std::string();
×
1196
    }
1197
}
1198

1199
/************************************************************************/
1200
/*                         GDALMultiDimInfo()                           */
1201
/************************************************************************/
1202

1203
/* clang-format off */
1204
/**
1205
 * Lists various information about a GDAL multidimensional dataset.
1206
 *
1207
 * This is the equivalent of the
1208
 * <a href="/programs/gdalmdiminfo.html">gdalmdiminfo</a>utility.
1209
 *
1210
 * GDALMultiDimInfoOptions* must be allocated and freed with
1211
 * GDALMultiDimInfoOptionsNew() and GDALMultiDimInfoOptionsFree() respectively.
1212
 *
1213
 * @param hDataset the dataset handle.
1214
 * @param psOptionsIn the options structure returned by
1215
 * GDALMultiDimInfoOptionsNew() or NULL.
1216
 * @return string corresponding to the information about the raster dataset
1217
 * (must be freed with CPLFree()), or NULL in case of error.
1218
 *
1219
 * @since GDAL 3.1
1220
 */
1221
/* clang-format on */
1222

1223
char *GDALMultiDimInfo(GDALDatasetH hDataset,
30✔
1224
                       const GDALMultiDimInfoOptions *psOptionsIn)
1225
{
1226
    if (hDataset == nullptr)
30✔
1227
        return nullptr;
×
1228

1229
    GDALMultiDimInfoOptions oOptionsDefault;
60✔
1230
    const GDALMultiDimInfoOptions *psOptions =
30✔
1231
        psOptionsIn ? psOptionsIn : &oOptionsDefault;
30✔
1232
    CPLJSonStreamingWriter serializer(
1233
        psOptions->bStdoutOutput ? WriteToStdout : nullptr, nullptr);
60✔
1234
    serializer.SetPrettyFormatting(psOptions->bPretty);
30✔
1235
    GDALDataset *poDS = GDALDataset::FromHandle(hDataset);
30✔
1236
    auto group = poDS->GetRootGroup();
60✔
1237
    if (!group)
30✔
1238
        return nullptr;
1✔
1239

1240
    std::set<std::string> alreadyDumpedDimensions;
58✔
1241
    try
1242
    {
1243
        if (psOptions->osArrayName.empty())
29✔
1244
        {
1245
            const char *pszDriverName = nullptr;
27✔
1246
            auto poDriver = poDS->GetDriver();
27✔
1247
            if (poDriver)
27✔
1248
                pszDriverName = poDriver->GetDescription();
27✔
1249
            DumpGroup(group, group, pszDriverName, serializer, psOptions,
27✔
1250
                      alreadyDumpedDimensions, true, true);
1251
        }
1252
        else
1253
        {
1254
            auto curGroup = group;
2✔
1255
            CPLStringList aosTokens(
1256
                CSLTokenizeString2(psOptions->osArrayName.c_str(), "/", 0));
2✔
1257
            for (int i = 0; i < aosTokens.size() - 1; i++)
2✔
1258
            {
UNCOV
1259
                auto curGroupNew = curGroup->OpenGroup(aosTokens[i]);
×
UNCOV
1260
                if (!curGroupNew)
×
1261
                {
UNCOV
1262
                    CPLError(CE_Failure, CPLE_AppDefined,
×
1263
                             "Cannot find group %s", aosTokens[i]);
UNCOV
1264
                    return nullptr;
×
1265
                }
UNCOV
1266
                curGroup = std::move(curGroupNew);
×
1267
            }
1268
            const char *pszArrayName = aosTokens.back();
2✔
1269
            auto array(curGroup->OpenMDArray(pszArrayName));
4✔
1270
            if (!array)
2✔
1271
            {
UNCOV
1272
                CPLError(CE_Failure, CPLE_AppDefined, "Cannot find array %s",
×
1273
                         pszArrayName);
UNCOV
1274
                return nullptr;
×
1275
            }
1276
            DumpArray(group, array, serializer, psOptions,
2✔
1277
                      alreadyDumpedDimensions, true, true);
1278
        }
1279
    }
UNCOV
1280
    catch (const std::exception &e)
×
1281
    {
UNCOV
1282
        CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
×
UNCOV
1283
        return nullptr;
×
1284
    }
1285

1286
    if (psOptions->bStdoutOutput)
29✔
1287
    {
1288
        printf("\n");
2✔
1289
    }
1290
    else
1291
    {
1292
        return VSIStrdup(serializer.GetString().c_str());
27✔
1293
    }
1294
    return nullptr;
2✔
1295
}
1296

1297
/************************************************************************/
1298
/*                       GDALMultiDimInfoOptionsNew()                   */
1299
/************************************************************************/
1300

1301
/**
1302
 * Allocates a GDALMultiDimInfo struct.
1303
 *
1304
 * @param papszArgv NULL terminated list of options (potentially including
1305
 * filename and open options too), or NULL. The accepted options are the ones of
1306
 * the <a href="/programs/gdalmdiminfo.html">gdalmdiminfo</a> utility.
1307
 * @param psOptionsForBinary should be nullptr, unless called from
1308
 * gdalmultidiminfo_bin.cpp
1309
 * @return pointer to the allocated GDALMultiDimInfoOptions struct. Must be
1310
 * freed with GDALMultiDimInfoOptionsFree().
1311
 *
1312
 * @since GDAL 3.1
1313
 */
1314

1315
GDALMultiDimInfoOptions *
1316
GDALMultiDimInfoOptionsNew(char **papszArgv,
32✔
1317
                           GDALMultiDimInfoOptionsForBinary *psOptionsForBinary)
1318
{
1319
    auto psOptions = std::make_unique<GDALMultiDimInfoOptions>();
64✔
1320

1321
    /* -------------------------------------------------------------------- */
1322
    /*      Parse arguments.                                                */
1323
    /* -------------------------------------------------------------------- */
1324

1325
    CPLStringList aosArgv;
64✔
1326

1327
    if (papszArgv)
32✔
1328
    {
1329
        const int nArgc = CSLCount(papszArgv);
15✔
1330
        for (int i = 0; i < nArgc; i++)
47✔
1331
            aosArgv.AddString(papszArgv[i]);
32✔
1332
    }
1333

1334
    try
1335
    {
1336
        auto argParser = GDALMultiDimInfoAppOptionsGetParser(
1337
            psOptions.get(), psOptionsForBinary);
64✔
1338
        argParser->parse_args_without_binary_name(aosArgv);
32✔
1339
    }
UNCOV
1340
    catch (const std::exception &err)
×
1341
    {
UNCOV
1342
        CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
×
UNCOV
1343
                 err.what());
×
UNCOV
1344
        return nullptr;
×
1345
    }
1346

1347
    return psOptions.release();
32✔
1348
}
1349

1350
/************************************************************************/
1351
/*                         GDALMultiDimInfoOptionsFree()                */
1352
/************************************************************************/
1353

1354
/**
1355
 * Frees the GDALMultiDimInfoOptions struct.
1356
 *
1357
 * @param psOptions the options struct for GDALMultiDimInfo().
1358
 *
1359
 * @since GDAL 3.1
1360
 */
1361

1362
void GDALMultiDimInfoOptionsFree(GDALMultiDimInfoOptions *psOptions)
31✔
1363
{
1364
    delete psOptions;
31✔
1365
}
31✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc