LCOV - code coverage report
Current view: top level - apps - gdalmdiminfo_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 556 684 81.3 %
Date: 2025-10-21 22:35:35 Functions: 32 39 82.1 %

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

Generated by: LCOV version 1.14