LCOV - code coverage report
Current view: top level - apps - gdalmdiminfo_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 583 716 81.4 %
Date: 2026-02-21 16:21:44 Functions: 32 39 82.1 %

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

Generated by: LCOV version 1.14