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

Generated by: LCOV version 1.14