LCOV - code coverage report
Current view: top level - apps - gdalmdiminfo_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 478 625 76.5 %
Date: 2024-11-21 22:18:42 Functions: 32 37 86.5 %

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

Generated by: LCOV version 1.14