LCOV - code coverage report
Current view: top level - apps - gdalmdiminfo_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 460 609 75.5 %
Date: 2024-05-03 15:49:35 Functions: 30 34 88.2 %

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

Generated by: LCOV version 1.14