LCOV - code coverage report
Current view: top level - apps - gdalmdiminfo_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 555 665 83.5 %
Date: 2025-06-19 12:30:01 Functions: 32 39 82.1 %

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

Generated by: LCOV version 1.14