LCOV - code coverage report
Current view: top level - apps - gdalmdiminfo_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 556 666 83.5 %
Date: 2025-08-01 10:10:57 Functions: 32 39 82.1 %

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

Generated by: LCOV version 1.14