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

Generated by: LCOV version 1.14