LCOV - code coverage report
Current view: top level - apps - ogrinfo_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1105 1284 86.1 %
Date: 2024-11-21 22:18:42 Functions: 30 32 93.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Simple client for viewing OGR driver data.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "cpl_json.h"
      16             : #include "ogrlibjsonutils.h"
      17             : #include "cpl_string.h"
      18             : #include "gdal_utils.h"
      19             : #include "gdal_utils_priv.h"
      20             : #include "gdal_priv.h"
      21             : #include "ogr_feature.h"
      22             : #include "ogrsf_frmts.h"
      23             : #include "ogr_geometry.h"
      24             : #include "commonutils.h"
      25             : #include "gdalargumentparser.h"
      26             : 
      27             : #include <cmath>
      28             : #include <set>
      29             : 
      30             : /*! output format */
      31             : typedef enum
      32             : {
      33             :     /*! output in text format */ FORMAT_TEXT = 0,
      34             :     /*! output in json format */ FORMAT_JSON = 1
      35             : } GDALVectorInfoFormat;
      36             : 
      37             : struct GDALVectorInfoOptions
      38             : {
      39             :     GDALVectorInfoFormat eFormat = FORMAT_TEXT;
      40             :     std::string osWHERE{};
      41             :     CPLStringList aosLayers{};
      42             :     std::unique_ptr<OGRGeometry> poSpatialFilter{};
      43             :     bool bAllLayers = false;
      44             :     std::string osSQLStatement{};
      45             :     std::string osDialect{};
      46             :     std::string osGeomField{};
      47             :     CPLStringList aosExtraMDDomains{};
      48             :     bool bListMDD = false;
      49             :     bool bShowMetadata = true;
      50             :     bool bFeatureCount = true;
      51             :     bool bExtent = true;
      52             :     bool bExtent3D = false;
      53             :     bool bGeomType = true;
      54             :     bool bDatasetGetNextFeature = false;
      55             :     bool bVerbose = true;
      56             :     bool bSuperQuiet = false;
      57             :     bool bSummaryOnly = false;
      58             :     GIntBig nFetchFID = OGRNullFID;
      59             :     std::string osWKTFormat = "WKT2";
      60             :     std::string osFieldDomain{};
      61             :     CPLStringList aosOptions{};
      62             :     bool bStdoutOutput = false;  // only set by ogrinfo_bin
      63             :     int nRepeatCount = 1;
      64             : 
      65             :     /*! Maximum number of features, or -1 if no limit. */
      66             :     GIntBig nLimit = -1;
      67             : 
      68             :     // Only used during argument parsing
      69             :     bool bSummaryParser = false;
      70             :     bool bFeaturesParser = false;
      71             : };
      72             : 
      73             : /************************************************************************/
      74             : /*                     GDALVectorInfoOptionsFree()                      */
      75             : /************************************************************************/
      76             : 
      77             : /**
      78             :  * Frees the GDALVectorInfoOptions struct.
      79             :  *
      80             :  * @param psOptions the options struct for GDALVectorInfo().
      81             :  *
      82             :  * @since GDAL 3.7
      83             :  */
      84             : 
      85          80 : void GDALVectorInfoOptionsFree(GDALVectorInfoOptions *psOptions)
      86             : {
      87          80 :     delete psOptions;
      88          80 : }
      89             : 
      90             : /************************************************************************/
      91             : /*                            Concat()                                  */
      92             : /************************************************************************/
      93             : 
      94             : #ifndef Concat_defined
      95             : #define Concat_defined
      96             : static void Concat(CPLString &osRet, bool bStdoutOutput, const char *pszFormat,
      97             :                    ...) CPL_PRINT_FUNC_FORMAT(3, 4);
      98             : 
      99        2642 : static void Concat(CPLString &osRet, bool bStdoutOutput, const char *pszFormat,
     100             :                    ...)
     101             : {
     102             :     va_list args;
     103        2642 :     va_start(args, pszFormat);
     104             : 
     105        2642 :     if (bStdoutOutput)
     106             :     {
     107        2147 :         vfprintf(stdout, pszFormat, args);
     108             :     }
     109             :     else
     110             :     {
     111             :         try
     112             :         {
     113         990 :             CPLString osTarget;
     114         495 :             osTarget.vPrintf(pszFormat, args);
     115             : 
     116         495 :             osRet += osTarget;
     117             :         }
     118           0 :         catch (const std::bad_alloc &)
     119             :         {
     120           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
     121             :         }
     122             :     }
     123             : 
     124        2642 :     va_end(args);
     125        2642 : }
     126             : #endif
     127             : 
     128         697 : static void ConcatStr(CPLString &osRet, bool bStdoutOutput, const char *pszStr)
     129             : {
     130         697 :     if (bStdoutOutput)
     131         616 :         fwrite(pszStr, 1, strlen(pszStr), stdout);
     132             :     else
     133          81 :         osRet += pszStr;
     134         697 : }
     135             : 
     136             : /************************************************************************/
     137             : /*                        ReportFieldDomain()                           */
     138             : /************************************************************************/
     139             : 
     140          14 : static void ReportFieldDomain(CPLString &osRet, CPLJSONObject &oDomains,
     141             :                               const GDALVectorInfoOptions *psOptions,
     142             :                               const OGRFieldDomain *poDomain)
     143             : {
     144          14 :     const bool bJson = psOptions->eFormat == FORMAT_JSON;
     145          28 :     CPLJSONObject oDomain;
     146          14 :     oDomains.Add(poDomain->GetName(), oDomain);
     147          14 :     Concat(osRet, psOptions->bStdoutOutput, "Domain %s:\n",
     148          14 :            poDomain->GetName().c_str());
     149          14 :     const std::string &osDesc = poDomain->GetDescription();
     150          14 :     if (!osDesc.empty())
     151             :     {
     152           2 :         if (bJson)
     153           1 :             oDomain.Set("description", osDesc);
     154             :         else
     155           1 :             Concat(osRet, psOptions->bStdoutOutput, "  Description: %s\n",
     156             :                    osDesc.c_str());
     157             :     }
     158          14 :     const char *pszType = "";
     159          14 :     switch (poDomain->GetDomainType())
     160             :     {
     161           2 :         case OFDT_CODED:
     162           2 :             pszType = "coded";
     163           2 :             break;
     164          10 :         case OFDT_RANGE:
     165          10 :             pszType = "range";
     166          10 :             break;
     167           2 :         case OFDT_GLOB:
     168           2 :             pszType = "glob";
     169           2 :             break;
     170             :     }
     171          14 :     if (bJson)
     172             :     {
     173           7 :         oDomain.Set("type", pszType);
     174             :     }
     175             :     else
     176             :     {
     177           7 :         Concat(osRet, psOptions->bStdoutOutput, "  Type: %s\n", pszType);
     178             :     }
     179             :     const char *pszFieldType =
     180          14 :         OGRFieldDefn::GetFieldTypeName(poDomain->GetFieldType());
     181             :     const char *pszFieldSubType =
     182          14 :         OGRFieldDefn::GetFieldSubTypeName(poDomain->GetFieldSubType());
     183          14 :     if (bJson)
     184             :     {
     185           7 :         oDomain.Set("fieldType", pszFieldType);
     186           7 :         if (poDomain->GetFieldSubType() != OFSTNone)
     187           0 :             oDomain.Set("fieldSubType", pszFieldSubType);
     188             :     }
     189             :     else
     190             :     {
     191             :         const char *pszFieldTypeDisplay =
     192           7 :             (poDomain->GetFieldSubType() != OFSTNone)
     193           7 :                 ? CPLSPrintf("%s(%s)", pszFieldType, pszFieldSubType)
     194           7 :                 : pszFieldType;
     195           7 :         Concat(osRet, psOptions->bStdoutOutput, "  Field type: %s\n",
     196             :                pszFieldTypeDisplay);
     197             :     }
     198             : 
     199          14 :     const char *pszSplitPolicy = "";
     200          14 :     switch (poDomain->GetSplitPolicy())
     201             :     {
     202          14 :         case OFDSP_DEFAULT_VALUE:
     203          14 :             pszSplitPolicy = "default value";
     204          14 :             break;
     205           0 :         case OFDSP_DUPLICATE:
     206           0 :             pszSplitPolicy = "duplicate";
     207           0 :             break;
     208           0 :         case OFDSP_GEOMETRY_RATIO:
     209           0 :             pszSplitPolicy = "geometry ratio";
     210           0 :             break;
     211             :     }
     212          14 :     if (bJson)
     213             :     {
     214           7 :         oDomain.Set("splitPolicy", pszSplitPolicy);
     215             :     }
     216             :     else
     217             :     {
     218           7 :         Concat(osRet, psOptions->bStdoutOutput, "  Split policy: %s\n",
     219             :                pszSplitPolicy);
     220             :     }
     221             : 
     222          14 :     const char *pszMergePolicy = "";
     223          14 :     switch (poDomain->GetMergePolicy())
     224             :     {
     225          14 :         case OFDMP_DEFAULT_VALUE:
     226          14 :             pszMergePolicy = "default value";
     227          14 :             break;
     228           0 :         case OFDMP_SUM:
     229           0 :             pszMergePolicy = "sum";
     230           0 :             break;
     231           0 :         case OFDMP_GEOMETRY_WEIGHTED:
     232           0 :             pszMergePolicy = "geometry weighted";
     233           0 :             break;
     234             :     }
     235          14 :     if (bJson)
     236             :     {
     237           7 :         oDomain.Set("mergePolicy", pszMergePolicy);
     238             :     }
     239             :     else
     240             :     {
     241           7 :         Concat(osRet, psOptions->bStdoutOutput, "  Merge policy: %s\n",
     242             :                pszMergePolicy);
     243             :     }
     244             : 
     245          14 :     switch (poDomain->GetDomainType())
     246             :     {
     247           2 :         case OFDT_CODED:
     248             :         {
     249             :             const auto poCodedFieldDomain =
     250           2 :                 cpl::down_cast<const OGRCodedFieldDomain *>(poDomain);
     251             :             const OGRCodedValue *enumeration =
     252           2 :                 poCodedFieldDomain->GetEnumeration();
     253           2 :             if (!bJson)
     254           1 :                 Concat(osRet, psOptions->bStdoutOutput, "  Coded values:\n");
     255           4 :             CPLJSONObject oCodedValues;
     256           2 :             oDomain.Add("codedValues", oCodedValues);
     257           6 :             for (int i = 0; enumeration[i].pszCode != nullptr; ++i)
     258             :             {
     259           4 :                 if (enumeration[i].pszValue)
     260             :                 {
     261           2 :                     if (bJson)
     262             :                     {
     263           1 :                         oCodedValues.Set(enumeration[i].pszCode,
     264           1 :                                          enumeration[i].pszValue);
     265             :                     }
     266             :                     else
     267             :                     {
     268           1 :                         Concat(osRet, psOptions->bStdoutOutput, "    %s: %s\n",
     269           1 :                                enumeration[i].pszCode, enumeration[i].pszValue);
     270             :                     }
     271             :                 }
     272             :                 else
     273             :                 {
     274           2 :                     if (bJson)
     275             :                     {
     276           1 :                         oCodedValues.SetNull(enumeration[i].pszCode);
     277             :                     }
     278             :                     else
     279             :                     {
     280           1 :                         Concat(osRet, psOptions->bStdoutOutput, "    %s\n",
     281           1 :                                enumeration[i].pszCode);
     282             :                     }
     283             :                 }
     284             :             }
     285           2 :             break;
     286             :         }
     287             : 
     288          10 :         case OFDT_RANGE:
     289             :         {
     290             :             const auto poRangeFieldDomain =
     291          10 :                 cpl::down_cast<const OGRRangeFieldDomain *>(poDomain);
     292          10 :             bool bMinIsIncluded = false;
     293          10 :             const OGRField &sMin = poRangeFieldDomain->GetMin(bMinIsIncluded);
     294          10 :             bool bMaxIsIncluded = false;
     295          10 :             const OGRField &sMax = poRangeFieldDomain->GetMax(bMaxIsIncluded);
     296          10 :             if (poDomain->GetFieldType() == OFTInteger)
     297             :             {
     298           2 :                 if (!OGR_RawField_IsUnset(&sMin))
     299             :                 {
     300           2 :                     if (bJson)
     301             :                     {
     302           1 :                         oDomain.Set("minValue", sMin.Integer);
     303           1 :                         oDomain.Set("minValueIncluded", bMinIsIncluded);
     304             :                     }
     305             :                     else
     306             :                     {
     307           1 :                         Concat(osRet, psOptions->bStdoutOutput,
     308           1 :                                "  Minimum value: %d%s\n", sMin.Integer,
     309             :                                bMinIsIncluded ? "" : " (excluded)");
     310             :                     }
     311             :                 }
     312           2 :                 if (!OGR_RawField_IsUnset(&sMax))
     313             :                 {
     314           2 :                     if (bJson)
     315             :                     {
     316           1 :                         oDomain.Set("maxValue", sMax.Integer);
     317           1 :                         oDomain.Set("maxValueIncluded", bMaxIsIncluded);
     318             :                     }
     319             :                     else
     320             :                     {
     321           1 :                         Concat(osRet, psOptions->bStdoutOutput,
     322           1 :                                "  Maximum value: %d%s\n", sMax.Integer,
     323             :                                bMaxIsIncluded ? "" : " (excluded)");
     324             :                     }
     325             :                 }
     326             :             }
     327           8 :             else if (poDomain->GetFieldType() == OFTInteger64)
     328             :             {
     329           2 :                 if (!OGR_RawField_IsUnset(&sMin))
     330             :                 {
     331           2 :                     if (bJson)
     332             :                     {
     333           1 :                         oDomain.Set("minValue", sMin.Integer64);
     334           1 :                         oDomain.Set("minValueIncluded", bMinIsIncluded);
     335             :                     }
     336             :                     else
     337             :                     {
     338           1 :                         Concat(osRet, psOptions->bStdoutOutput,
     339             :                                "  Minimum value: " CPL_FRMT_GIB "%s\n",
     340           1 :                                sMin.Integer64,
     341             :                                bMinIsIncluded ? "" : " (excluded)");
     342             :                     }
     343             :                 }
     344           2 :                 if (!OGR_RawField_IsUnset(&sMax))
     345             :                 {
     346           2 :                     if (bJson)
     347             :                     {
     348           1 :                         oDomain.Set("maxValue", sMax.Integer64);
     349           1 :                         oDomain.Set("maxValueIncluded", bMaxIsIncluded);
     350             :                     }
     351             :                     else
     352             :                     {
     353           1 :                         Concat(osRet, psOptions->bStdoutOutput,
     354             :                                "  Maximum value: " CPL_FRMT_GIB "%s\n",
     355           1 :                                sMax.Integer64,
     356             :                                bMaxIsIncluded ? "" : " (excluded)");
     357             :                     }
     358             :                 }
     359             :             }
     360           6 :             else if (poDomain->GetFieldType() == OFTReal)
     361             :             {
     362           4 :                 if (!OGR_RawField_IsUnset(&sMin))
     363             :                 {
     364           2 :                     if (bJson)
     365             :                     {
     366           1 :                         oDomain.Set("minValue", sMin.Real);
     367           1 :                         oDomain.Set("minValueIncluded", bMinIsIncluded);
     368             :                     }
     369             :                     else
     370             :                     {
     371           1 :                         Concat(osRet, psOptions->bStdoutOutput,
     372           1 :                                "  Minimum value: %g%s\n", sMin.Real,
     373             :                                bMinIsIncluded ? "" : " (excluded)");
     374             :                     }
     375             :                 }
     376           4 :                 if (!OGR_RawField_IsUnset(&sMax))
     377             :                 {
     378           2 :                     if (bJson)
     379             :                     {
     380           1 :                         oDomain.Set("maxValue", sMax.Real);
     381           1 :                         oDomain.Set("maxValueIncluded", bMaxIsIncluded);
     382             :                     }
     383             :                     else
     384             :                     {
     385           1 :                         Concat(osRet, psOptions->bStdoutOutput,
     386           1 :                                "  Maximum value: %g%s\n", sMax.Real,
     387             :                                bMaxIsIncluded ? "" : " (excluded)");
     388             :                     }
     389             :                 }
     390             :             }
     391           2 :             else if (poDomain->GetFieldType() == OFTDateTime)
     392             :             {
     393           2 :                 if (!OGR_RawField_IsUnset(&sMin))
     394             :                 {
     395           4 :                     const char *pszVal = CPLSPrintf(
     396           2 :                         "%04d-%02d-%02dT%02d:%02d:%02d", sMin.Date.Year,
     397           2 :                         sMin.Date.Month, sMin.Date.Day, sMin.Date.Hour,
     398           2 :                         sMin.Date.Minute,
     399           2 :                         static_cast<int>(sMin.Date.Second + 0.5));
     400           2 :                     if (bJson)
     401             :                     {
     402           1 :                         oDomain.Set("minValue", pszVal);
     403           1 :                         oDomain.Set("minValueIncluded", bMinIsIncluded);
     404             :                     }
     405             :                     else
     406             :                     {
     407           1 :                         Concat(osRet, psOptions->bStdoutOutput,
     408             :                                "  Minimum value: %s%s\n", pszVal,
     409             :                                bMinIsIncluded ? "" : " (excluded)");
     410             :                     }
     411             :                 }
     412           2 :                 if (!OGR_RawField_IsUnset(&sMax))
     413             :                 {
     414           4 :                     const char *pszVal = CPLSPrintf(
     415           2 :                         "%04d-%02d-%02dT%02d:%02d:%02d", sMax.Date.Year,
     416           2 :                         sMax.Date.Month, sMax.Date.Day, sMax.Date.Hour,
     417           2 :                         sMax.Date.Minute,
     418           2 :                         static_cast<int>(sMax.Date.Second + 0.5));
     419           2 :                     if (bJson)
     420             :                     {
     421           1 :                         oDomain.Set("maxValue", pszVal);
     422           1 :                         oDomain.Set("maxValueIncluded", bMaxIsIncluded);
     423             :                     }
     424             :                     else
     425             :                     {
     426           1 :                         Concat(osRet, psOptions->bStdoutOutput,
     427             :                                "  Maximum value: %s%s\n", pszVal,
     428             :                                bMaxIsIncluded ? "" : " (excluded)");
     429             :                     }
     430             :                 }
     431             :             }
     432          10 :             break;
     433             :         }
     434             : 
     435           2 :         case OFDT_GLOB:
     436             :         {
     437             :             const auto poGlobFieldDomain =
     438           2 :                 cpl::down_cast<const OGRGlobFieldDomain *>(poDomain);
     439           2 :             if (bJson)
     440           1 :                 oDomain.Set("glob", poGlobFieldDomain->GetGlob());
     441             :             else
     442           1 :                 Concat(osRet, psOptions->bStdoutOutput, "  Glob: %s\n",
     443           1 :                        poGlobFieldDomain->GetGlob().c_str());
     444           2 :             break;
     445             :         }
     446             :     }
     447          14 : }
     448             : 
     449             : /************************************************************************/
     450             : /*                       ReportRelationships()                          */
     451             : /************************************************************************/
     452             : 
     453          65 : static void ReportRelationships(CPLString &osRet, CPLJSONObject &oRoot,
     454             :                                 const GDALVectorInfoOptions *psOptions,
     455             :                                 const GDALDataset *poDS)
     456             : {
     457          65 :     const bool bJson = psOptions->eFormat == FORMAT_JSON;
     458         130 :     CPLJSONObject oRelationships;
     459          65 :     if (bJson)
     460          24 :         oRoot.Add("relationships", oRelationships);
     461             : 
     462         130 :     const auto aosRelationshipNames = poDS->GetRelationshipNames();
     463          87 :     for (const std::string &osRelationshipName : aosRelationshipNames)
     464             :     {
     465          22 :         const auto poRelationship = poDS->GetRelationship(osRelationshipName);
     466          22 :         if (!poRelationship)
     467           0 :             continue;
     468             : 
     469          22 :         const char *pszType = "";
     470          22 :         switch (poRelationship->GetType())
     471             :         {
     472           8 :             case GRT_COMPOSITE:
     473           8 :                 pszType = "Composite";
     474           8 :                 break;
     475          14 :             case GRT_ASSOCIATION:
     476          14 :                 pszType = "Association";
     477          14 :                 break;
     478           0 :             case GRT_AGGREGATION:
     479           0 :                 pszType = "Aggregation";
     480           0 :                 break;
     481             :         }
     482             : 
     483          22 :         const char *pszCardinality = "";
     484          22 :         switch (poRelationship->GetCardinality())
     485             :         {
     486          12 :             case GRC_ONE_TO_ONE:
     487          12 :                 pszCardinality = "OneToOne";
     488          12 :                 break;
     489           6 :             case GRC_ONE_TO_MANY:
     490           6 :                 pszCardinality = "OneToMany";
     491           6 :                 break;
     492           0 :             case GRC_MANY_TO_ONE:
     493           0 :                 pszCardinality = "ManyToOne";
     494           0 :                 break;
     495           4 :             case GRC_MANY_TO_MANY:
     496           4 :                 pszCardinality = "ManyToMany";
     497           4 :                 break;
     498             :         }
     499             : 
     500          22 :         const auto &aosLeftTableFields = poRelationship->GetLeftTableFields();
     501          22 :         const auto &aosRightTableFields = poRelationship->GetRightTableFields();
     502          22 :         const auto &osMappingTableName = poRelationship->GetMappingTableName();
     503             :         const auto &aosLeftMappingTableFields =
     504          22 :             poRelationship->GetLeftMappingTableFields();
     505             :         const auto &aosRightMappingTableFields =
     506          22 :             poRelationship->GetRightMappingTableFields();
     507             : 
     508          22 :         if (bJson)
     509             :         {
     510          22 :             CPLJSONObject oRelationship;
     511          11 :             oRelationships.Add(osRelationshipName, oRelationship);
     512             : 
     513          11 :             oRelationship.Add("type", pszType);
     514          11 :             oRelationship.Add("related_table_type",
     515             :                               poRelationship->GetRelatedTableType());
     516          11 :             oRelationship.Add("cardinality", pszCardinality);
     517          11 :             oRelationship.Add("left_table_name",
     518             :                               poRelationship->GetLeftTableName());
     519          11 :             oRelationship.Add("right_table_name",
     520             :                               poRelationship->GetRightTableName());
     521             : 
     522          22 :             CPLJSONArray oLeftTableFields;
     523          11 :             oRelationship.Add("left_table_fields", oLeftTableFields);
     524          22 :             for (const auto &osName : aosLeftTableFields)
     525          11 :                 oLeftTableFields.Add(osName);
     526             : 
     527          11 :             CPLJSONArray oRightTableFields;
     528          11 :             oRelationship.Add("right_table_fields", oRightTableFields);
     529          23 :             for (const auto &osName : aosRightTableFields)
     530          12 :                 oRightTableFields.Add(osName);
     531             : 
     532          11 :             if (!osMappingTableName.empty())
     533             :             {
     534           2 :                 oRelationship.Add("mapping_table_name", osMappingTableName);
     535             : 
     536           4 :                 CPLJSONArray oLeftMappingTableFields;
     537           2 :                 oRelationship.Add("left_mapping_table_fields",
     538             :                                   oLeftMappingTableFields);
     539           4 :                 for (const auto &osName : aosLeftMappingTableFields)
     540           2 :                     oLeftMappingTableFields.Add(osName);
     541             : 
     542           4 :                 CPLJSONArray oRightMappingTableFields;
     543           2 :                 oRelationship.Add("right_mapping_table_fields",
     544             :                                   oRightMappingTableFields);
     545           4 :                 for (const auto &osName : aosRightMappingTableFields)
     546           2 :                     oRightMappingTableFields.Add(osName);
     547             :             }
     548             : 
     549          11 :             oRelationship.Add("forward_path_label",
     550             :                               poRelationship->GetForwardPathLabel());
     551          11 :             oRelationship.Add("backward_path_label",
     552             :                               poRelationship->GetBackwardPathLabel());
     553             :         }
     554             :         else
     555             :         {
     556             :             const auto ConcatStringList =
     557          80 :                 [&osRet, psOptions](const std::vector<std::string> &aosList)
     558             :             {
     559          26 :                 bool bFirstName = true;
     560          53 :                 for (const auto &osName : aosList)
     561             :                 {
     562          27 :                     if (!bFirstName)
     563           1 :                         ConcatStr(osRet, psOptions->bStdoutOutput, ", ");
     564          27 :                     bFirstName = false;
     565          27 :                     ConcatStr(osRet, psOptions->bStdoutOutput, osName.c_str());
     566             :                 }
     567          26 :                 Concat(osRet, psOptions->bStdoutOutput, "\n");
     568          26 :             };
     569             : 
     570          11 :             if (!psOptions->bAllLayers)
     571             :             {
     572           0 :                 Concat(osRet, psOptions->bStdoutOutput,
     573             :                        "Relationship: %s (%s, %s, %s)\n",
     574             :                        osRelationshipName.c_str(), pszType,
     575           0 :                        poRelationship->GetLeftTableName().c_str(),
     576           0 :                        poRelationship->GetRightTableName().c_str());
     577           0 :                 continue;
     578             :             }
     579          11 :             Concat(osRet, psOptions->bStdoutOutput, "\nRelationship: %s\n",
     580             :                    osRelationshipName.c_str());
     581          11 :             Concat(osRet, psOptions->bStdoutOutput, "  Type: %s\n", pszType);
     582          11 :             Concat(osRet, psOptions->bStdoutOutput,
     583             :                    "  Related table type: %s\n",
     584          11 :                    poRelationship->GetRelatedTableType().c_str());
     585          11 :             Concat(osRet, psOptions->bStdoutOutput, "  Cardinality: %s\n",
     586             :                    pszCardinality);
     587          11 :             Concat(osRet, psOptions->bStdoutOutput, "  Left table name: %s\n",
     588          11 :                    poRelationship->GetLeftTableName().c_str());
     589          11 :             Concat(osRet, psOptions->bStdoutOutput, "  Right table name: %s\n",
     590          11 :                    poRelationship->GetRightTableName().c_str());
     591          11 :             Concat(osRet, psOptions->bStdoutOutput, "  Left table fields: ");
     592          11 :             ConcatStringList(aosLeftTableFields);
     593          11 :             Concat(osRet, psOptions->bStdoutOutput, "  Right table fields: ");
     594          11 :             ConcatStringList(aosRightTableFields);
     595             : 
     596          11 :             if (!osMappingTableName.empty())
     597             :             {
     598           2 :                 Concat(osRet, psOptions->bStdoutOutput,
     599             :                        "  Mapping table name: %s\n",
     600             :                        osMappingTableName.c_str());
     601             : 
     602           2 :                 Concat(osRet, psOptions->bStdoutOutput,
     603             :                        "  Left mapping table fields: ");
     604           2 :                 ConcatStringList(aosLeftMappingTableFields);
     605             : 
     606           2 :                 Concat(osRet, psOptions->bStdoutOutput,
     607             :                        "  Right mapping table fields: ");
     608           2 :                 ConcatStringList(aosRightMappingTableFields);
     609             :             }
     610             : 
     611          11 :             Concat(osRet, psOptions->bStdoutOutput,
     612             :                    "  Forward path label: %s\n",
     613          11 :                    poRelationship->GetForwardPathLabel().c_str());
     614          11 :             Concat(osRet, psOptions->bStdoutOutput,
     615             :                    "  Backward path label: %s\n",
     616          11 :                    poRelationship->GetBackwardPathLabel().c_str());
     617             :         }
     618             :     }
     619          65 : }
     620             : 
     621             : /************************************************************************/
     622             : /*                     GDALVectorInfoPrintMetadata()                    */
     623             : /************************************************************************/
     624             : 
     625             : static void
     626         437 : GDALVectorInfoPrintMetadata(CPLString &osRet, CPLJSONObject &oMetadata,
     627             :                             const GDALVectorInfoOptions *psOptions,
     628             :                             GDALMajorObjectH hObject, const char *pszDomain,
     629             :                             const char *pszDisplayedname, const char *pszIndent)
     630             : {
     631         437 :     const bool bJsonOutput = psOptions->eFormat == FORMAT_JSON;
     632         437 :     bool bIsxml = false;
     633         437 :     bool bMDIsJson = false;
     634             : 
     635         437 :     if (pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "xml:"))
     636           0 :         bIsxml = true;
     637         437 :     else if (pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "json:"))
     638           1 :         bMDIsJson = true;
     639             : 
     640         437 :     CSLConstList papszMetadata = GDALGetMetadata(hObject, pszDomain);
     641         437 :     if (CSLCount(papszMetadata) > 0)
     642             :     {
     643          31 :         CPLJSONObject oMetadataDomain;
     644          31 :         if (!bJsonOutput)
     645          17 :             Concat(osRet, psOptions->bStdoutOutput, "%s%s:\n", pszIndent,
     646             :                    pszDisplayedname);
     647          61 :         for (int i = 0; papszMetadata[i] != nullptr; i++)
     648             :         {
     649          31 :             if (bJsonOutput)
     650             :             {
     651          14 :                 if (bIsxml)
     652             :                 {
     653           0 :                     oMetadata.Add(pszDomain, papszMetadata[i]);
     654           0 :                     return;
     655             :                 }
     656          14 :                 else if (bMDIsJson)
     657             :                 {
     658           1 :                     CPLJSONDocument oDoc;
     659           1 :                     if (oDoc.LoadMemory(papszMetadata[i]))
     660           1 :                         oMetadata.Add(pszDomain, oDoc.GetRoot());
     661           1 :                     return;
     662             :                 }
     663             :                 else
     664             :                 {
     665          13 :                     char *pszKey = nullptr;
     666             :                     const char *pszValue =
     667          13 :                         CPLParseNameValue(papszMetadata[i], &pszKey);
     668          13 :                     if (pszKey)
     669             :                     {
     670          13 :                         oMetadataDomain.Add(pszKey, pszValue);
     671          13 :                         CPLFree(pszKey);
     672             :                     }
     673             :                 }
     674             :             }
     675          17 :             else if (bIsxml)
     676           0 :                 Concat(osRet, psOptions->bStdoutOutput, "%s%s\n", pszIndent,
     677           0 :                        papszMetadata[i]);
     678             :             else
     679          17 :                 Concat(osRet, psOptions->bStdoutOutput, "%s  %s\n", pszIndent,
     680          17 :                        papszMetadata[i]);
     681             :         }
     682          30 :         if (bJsonOutput)
     683             :         {
     684          13 :             oMetadata.Add(pszDomain ? pszDomain : "", oMetadataDomain);
     685             :         }
     686             :     }
     687             : }
     688             : 
     689             : /************************************************************************/
     690             : /*                    GDALVectorInfoReportMetadata()                    */
     691             : /************************************************************************/
     692             : 
     693         216 : static void GDALVectorInfoReportMetadata(CPLString &osRet, CPLJSONObject &oRoot,
     694             :                                          const GDALVectorInfoOptions *psOptions,
     695             :                                          GDALMajorObject *poMajorObject,
     696             :                                          bool bListMDD, bool bShowMetadata,
     697             :                                          CSLConstList papszExtraMDDomains)
     698             : {
     699         216 :     const char *pszIndent = "";
     700         216 :     auto hObject = GDALMajorObject::ToHandle(poMajorObject);
     701             : 
     702         216 :     const bool bJson = psOptions->eFormat == FORMAT_JSON;
     703             :     /* -------------------------------------------------------------------- */
     704             :     /*      Report list of Metadata domains                                 */
     705             :     /* -------------------------------------------------------------------- */
     706         216 :     if (bListMDD)
     707             :     {
     708           0 :         const CPLStringList aosMDDList(GDALGetMetadataDomainList(hObject));
     709             : 
     710           0 :         CPLJSONArray metadataDomains;
     711             : 
     712           0 :         if (!aosMDDList.empty() && !bJson)
     713           0 :             Concat(osRet, psOptions->bStdoutOutput, "%sMetadata domains:\n",
     714             :                    pszIndent);
     715           0 :         for (const char *pszDomain : aosMDDList)
     716             :         {
     717           0 :             if (EQUAL(pszDomain, ""))
     718             :             {
     719           0 :                 if (bJson)
     720           0 :                     metadataDomains.Add("");
     721             :                 else
     722           0 :                     Concat(osRet, psOptions->bStdoutOutput, "%s  (default)\n",
     723             :                            pszIndent);
     724             :             }
     725             :             else
     726             :             {
     727           0 :                 if (bJson)
     728           0 :                     metadataDomains.Add(pszDomain);
     729             :                 else
     730           0 :                     Concat(osRet, psOptions->bStdoutOutput, "%s  %s\n",
     731             :                            pszIndent, pszDomain);
     732             :             }
     733             :         }
     734             : 
     735           0 :         if (bJson)
     736           0 :             oRoot.Add("metadataDomains", metadataDomains);
     737             :     }
     738             : 
     739         216 :     if (!bShowMetadata)
     740           2 :         return;
     741             : 
     742             :     /* -------------------------------------------------------------------- */
     743             :     /*      Report default Metadata domain.                                 */
     744             :     /* -------------------------------------------------------------------- */
     745         428 :     CPLJSONObject oMetadata;
     746         214 :     oRoot.Add("metadata", oMetadata);
     747         214 :     GDALVectorInfoPrintMetadata(osRet, oMetadata, psOptions, hObject, nullptr,
     748             :                                 "Metadata", pszIndent);
     749             : 
     750             :     /* -------------------------------------------------------------------- */
     751             :     /*      Report extra Metadata domains                                   */
     752             :     /* -------------------------------------------------------------------- */
     753         214 :     if (papszExtraMDDomains != nullptr)
     754             :     {
     755         136 :         CPLStringList aosExtraMDDomainsExpanded;
     756             : 
     757          68 :         if (EQUAL(papszExtraMDDomains[0], "all") &&
     758          68 :             papszExtraMDDomains[1] == nullptr)
     759             :         {
     760         136 :             const CPLStringList aosMDDList(GDALGetMetadataDomainList(hObject));
     761          85 :             for (const char *pszDomain : aosMDDList)
     762             :             {
     763          17 :                 if (!EQUAL(pszDomain, "") && !EQUAL(pszDomain, "SUBDATASETS"))
     764             :                 {
     765           9 :                     aosExtraMDDomainsExpanded.AddString(pszDomain);
     766             :                 }
     767          68 :             }
     768             :         }
     769             :         else
     770             :         {
     771           0 :             aosExtraMDDomainsExpanded = CSLDuplicate(papszExtraMDDomains);
     772             :         }
     773             : 
     774          77 :         for (const char *pszDomain : aosExtraMDDomainsExpanded)
     775             :         {
     776             :             const std::string osDisplayedName =
     777          27 :                 std::string("Metadata (").append(pszDomain).append(")");
     778           9 :             GDALVectorInfoPrintMetadata(osRet, oMetadata, psOptions, hObject,
     779             :                                         pszDomain, osDisplayedName.c_str(),
     780             :                                         pszIndent);
     781             :         }
     782             :     }
     783         214 :     GDALVectorInfoPrintMetadata(osRet, oMetadata, psOptions, hObject,
     784             :                                 "SUBDATASETS", "Subdatasets", pszIndent);
     785             : }
     786             : 
     787             : /************************************************************************/
     788             : /*                           ReportOnLayer()                            */
     789             : /************************************************************************/
     790             : 
     791         140 : static void ReportOnLayer(CPLString &osRet, CPLJSONObject &oLayer,
     792             :                           const GDALVectorInfoOptions *psOptions,
     793             :                           OGRLayer *poLayer, bool bForceSummary,
     794             :                           bool bTakeIntoAccountWHERE,
     795             :                           bool bTakeIntoAccountSpatialFilter,
     796             :                           bool bTakeIntoAccountGeomField)
     797             : {
     798         140 :     const bool bJson = psOptions->eFormat == FORMAT_JSON;
     799         140 :     OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
     800             : 
     801         140 :     oLayer.Set("name", poLayer->GetName());
     802             : 
     803             :     /* -------------------------------------------------------------------- */
     804             :     /*      Set filters if provided.                                        */
     805             :     /* -------------------------------------------------------------------- */
     806         140 :     if (bTakeIntoAccountWHERE && !psOptions->osWHERE.empty())
     807             :     {
     808           2 :         if (poLayer->SetAttributeFilter(psOptions->osWHERE.c_str()) !=
     809             :             OGRERR_NONE)
     810             :         {
     811           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     812             :                      "SetAttributeFilter(%s) failed.",
     813           0 :                      psOptions->osWHERE.c_str());
     814           0 :             return;
     815             :         }
     816             :     }
     817             : 
     818         140 :     if (bTakeIntoAccountSpatialFilter && psOptions->poSpatialFilter != nullptr)
     819             :     {
     820           2 :         if (bTakeIntoAccountGeomField && !psOptions->osGeomField.empty())
     821             :         {
     822             :             const int iGeomField =
     823           1 :                 poDefn->GetGeomFieldIndex(psOptions->osGeomField.c_str());
     824           1 :             if (iGeomField >= 0)
     825           1 :                 poLayer->SetSpatialFilter(iGeomField,
     826           1 :                                           psOptions->poSpatialFilter.get());
     827             :             else
     828           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     829             :                          "Cannot find geometry field %s.",
     830           0 :                          psOptions->osGeomField.c_str());
     831             :         }
     832             :         else
     833             :         {
     834           1 :             poLayer->SetSpatialFilter(psOptions->poSpatialFilter.get());
     835             :         }
     836             :     }
     837             : 
     838             :     /* -------------------------------------------------------------------- */
     839             :     /*      Report various overall information.                             */
     840             :     /* -------------------------------------------------------------------- */
     841         140 :     if (!bJson && !psOptions->bSuperQuiet)
     842             :     {
     843         101 :         Concat(osRet, psOptions->bStdoutOutput, "\n");
     844         101 :         Concat(osRet, psOptions->bStdoutOutput, "Layer name: %s\n",
     845         101 :                poLayer->GetName());
     846             :     }
     847             : 
     848         280 :     GDALVectorInfoReportMetadata(osRet, oLayer, psOptions, poLayer,
     849         140 :                                  psOptions->bListMDD, psOptions->bShowMetadata,
     850         140 :                                  psOptions->aosExtraMDDomains.List());
     851             : 
     852         140 :     if (psOptions->bVerbose)
     853             :     {
     854             :         const int nGeomFieldCount =
     855         139 :             psOptions->bGeomType ? poLayer->GetLayerDefn()->GetGeomFieldCount()
     856         139 :                                  : 0;
     857             : 
     858         278 :         CPLString osWKTFormat("FORMAT=");
     859         139 :         osWKTFormat += psOptions->osWKTFormat;
     860         139 :         const char *const apszWKTOptions[] = {osWKTFormat.c_str(),
     861         139 :                                               "MULTILINE=YES", nullptr};
     862             : 
     863         139 :         if (bJson || nGeomFieldCount > 1)
     864             :         {
     865          82 :             CPLJSONArray oGeometryFields;
     866          41 :             if (bJson)
     867          39 :                 oLayer.Add("geometryFields", oGeometryFields);
     868          72 :             for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
     869             :             {
     870             :                 const OGRGeomFieldDefn *poGFldDefn =
     871          31 :                     poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
     872          31 :                 if (bJson)
     873             :                 {
     874          54 :                     CPLJSONObject oGeometryField;
     875          27 :                     oGeometryFields.Add(oGeometryField);
     876          27 :                     oGeometryField.Set("name", poGFldDefn->GetNameRef());
     877          27 :                     oGeometryField.Set(
     878             :                         "type", OGRToOGCGeomType(poGFldDefn->GetType(),
     879             :                                                  /*bCamelCase=*/true,
     880             :                                                  /*bAddZm=*/true,
     881             :                                                  /*bSpaceBeforeZM=*/false));
     882          27 :                     oGeometryField.Set("nullable",
     883          27 :                                        CPL_TO_BOOL(poGFldDefn->IsNullable()));
     884          27 :                     if (psOptions->bExtent3D)
     885             :                     {
     886           3 :                         OGREnvelope3D oExt;
     887           3 :                         if (poLayer->GetExtent3D(iGeom, &oExt, TRUE) ==
     888             :                             OGRERR_NONE)
     889             :                         {
     890             :                             {
     891           3 :                                 CPLJSONArray oBbox;
     892           3 :                                 oBbox.Add(oExt.MinX);
     893           3 :                                 oBbox.Add(oExt.MinY);
     894           3 :                                 oBbox.Add(oExt.MaxX);
     895           3 :                                 oBbox.Add(oExt.MaxY);
     896           3 :                                 oGeometryField.Add("extent", oBbox);
     897             :                             }
     898             :                             {
     899           3 :                                 CPLJSONArray oBbox;
     900           3 :                                 oBbox.Add(oExt.MinX);
     901           3 :                                 oBbox.Add(oExt.MinY);
     902           3 :                                 if (std::isfinite(oExt.MinZ))
     903           2 :                                     oBbox.Add(oExt.MinZ);
     904             :                                 else
     905           1 :                                     oBbox.AddNull();
     906           3 :                                 oBbox.Add(oExt.MaxX);
     907           3 :                                 oBbox.Add(oExt.MaxY);
     908           3 :                                 if (std::isfinite(oExt.MaxZ))
     909           2 :                                     oBbox.Add(oExt.MaxZ);
     910             :                                 else
     911           1 :                                     oBbox.AddNull();
     912           3 :                                 oGeometryField.Add("extent3D", oBbox);
     913             :                             }
     914             :                         }
     915             :                     }
     916          24 :                     else if (psOptions->bExtent)
     917             :                     {
     918          24 :                         OGREnvelope oExt;
     919          24 :                         if (poLayer->GetExtent(iGeom, &oExt, TRUE) ==
     920             :                             OGRERR_NONE)
     921             :                         {
     922          14 :                             CPLJSONArray oBbox;
     923          14 :                             oBbox.Add(oExt.MinX);
     924          14 :                             oBbox.Add(oExt.MinY);
     925          14 :                             oBbox.Add(oExt.MaxX);
     926          14 :                             oBbox.Add(oExt.MaxY);
     927          14 :                             oGeometryField.Add("extent", oBbox);
     928             :                         }
     929             :                     }
     930             :                     const OGRSpatialReference *poSRS =
     931          27 :                         poGFldDefn->GetSpatialRef();
     932          27 :                     if (poSRS)
     933             :                     {
     934          36 :                         CPLJSONObject oCRS;
     935          18 :                         oGeometryField.Add("coordinateSystem", oCRS);
     936          18 :                         char *pszWKT = nullptr;
     937          18 :                         poSRS->exportToWkt(&pszWKT, apszWKTOptions);
     938          18 :                         if (pszWKT)
     939             :                         {
     940          18 :                             oCRS.Set("wkt", pszWKT);
     941          18 :                             CPLFree(pszWKT);
     942             :                         }
     943             : 
     944             :                         {
     945          18 :                             char *pszProjJson = nullptr;
     946             :                             // PROJJSON requires PROJ >= 6.2
     947             :                             CPLErrorStateBackuper oCPLErrorHandlerPusher(
     948          36 :                                 CPLQuietErrorHandler);
     949          18 :                             CPL_IGNORE_RET_VAL(
     950          18 :                                 poSRS->exportToPROJJSON(&pszProjJson, nullptr));
     951          18 :                             if (pszProjJson)
     952             :                             {
     953          36 :                                 CPLJSONDocument oDoc;
     954          18 :                                 if (oDoc.LoadMemory(pszProjJson))
     955             :                                 {
     956          18 :                                     oCRS.Add("projjson", oDoc.GetRoot());
     957             :                                 }
     958          18 :                                 CPLFree(pszProjJson);
     959             :                             }
     960             :                         }
     961             : 
     962             :                         const auto &anAxes =
     963          18 :                             poSRS->GetDataAxisToSRSAxisMapping();
     964          36 :                         CPLJSONArray oAxisMapping;
     965          55 :                         for (const auto nAxis : anAxes)
     966             :                         {
     967          37 :                             oAxisMapping.Add(nAxis);
     968             :                         }
     969          18 :                         oCRS.Add("dataAxisToSRSAxisMapping", oAxisMapping);
     970             : 
     971             :                         const double dfCoordinateEpoch =
     972          18 :                             poSRS->GetCoordinateEpoch();
     973          18 :                         if (dfCoordinateEpoch > 0)
     974           2 :                             oCRS.Set("coordinateEpoch", dfCoordinateEpoch);
     975             :                     }
     976             :                     else
     977             :                     {
     978           9 :                         oGeometryField.SetNull("coordinateSystem");
     979             :                     }
     980             : 
     981          27 :                     const auto &srsList = poLayer->GetSupportedSRSList(iGeom);
     982          27 :                     if (!srsList.empty())
     983             :                     {
     984           1 :                         CPLJSONArray oSupportedSRSList;
     985           3 :                         for (const auto &poSupportedSRS : srsList)
     986             :                         {
     987             :                             const char *pszAuthName =
     988           2 :                                 poSupportedSRS->GetAuthorityName(nullptr);
     989             :                             const char *pszAuthCode =
     990           2 :                                 poSupportedSRS->GetAuthorityCode(nullptr);
     991           4 :                             CPLJSONObject oSupportedSRS;
     992           2 :                             if (pszAuthName && pszAuthCode)
     993             :                             {
     994           4 :                                 CPLJSONObject id;
     995           2 :                                 id.Set("authority", pszAuthName);
     996           2 :                                 id.Set("code", pszAuthCode);
     997           2 :                                 oSupportedSRS.Add("id", id);
     998           4 :                                 oSupportedSRSList.Add(oSupportedSRS);
     999             :                             }
    1000             :                             else
    1001             :                             {
    1002           0 :                                 char *pszWKT = nullptr;
    1003           0 :                                 poSupportedSRS->exportToWkt(&pszWKT,
    1004             :                                                             apszWKTOptions);
    1005           0 :                                 if (pszWKT)
    1006             :                                 {
    1007           0 :                                     oSupportedSRS.Add("wkt", pszWKT);
    1008           0 :                                     oSupportedSRSList.Add(oSupportedSRS);
    1009             :                                 }
    1010           0 :                                 CPLFree(pszWKT);
    1011             :                             }
    1012             :                         }
    1013           1 :                         oGeometryField.Add("supportedSRSList",
    1014             :                                            oSupportedSRSList);
    1015             :                     }
    1016             : 
    1017             :                     const auto &oCoordPrec =
    1018          27 :                         poGFldDefn->GetCoordinatePrecision();
    1019          27 :                     if (oCoordPrec.dfXYResolution !=
    1020             :                         OGRGeomCoordinatePrecision::UNKNOWN)
    1021             :                     {
    1022           4 :                         oGeometryField.Add("xyCoordinateResolution",
    1023           4 :                                            oCoordPrec.dfXYResolution);
    1024             :                     }
    1025          27 :                     if (oCoordPrec.dfZResolution !=
    1026             :                         OGRGeomCoordinatePrecision::UNKNOWN)
    1027             :                     {
    1028           4 :                         oGeometryField.Add("zCoordinateResolution",
    1029           4 :                                            oCoordPrec.dfZResolution);
    1030             :                     }
    1031          27 :                     if (oCoordPrec.dfMResolution !=
    1032             :                         OGRGeomCoordinatePrecision::UNKNOWN)
    1033             :                     {
    1034           3 :                         oGeometryField.Add("mCoordinateResolution",
    1035           3 :                                            oCoordPrec.dfMResolution);
    1036             :                     }
    1037             : 
    1038             :                     // For example set by OpenFileGDB driver
    1039          27 :                     if (!oCoordPrec.oFormatSpecificOptions.empty())
    1040             :                     {
    1041           3 :                         CPLJSONObject oFormatSpecificOptions;
    1042           3 :                         for (const auto &formatOptionsPair :
    1043           9 :                              oCoordPrec.oFormatSpecificOptions)
    1044             :                         {
    1045           6 :                             CPLJSONObject oThisFormatSpecificOptions;
    1046          66 :                             for (const auto &[pszKey, pszValue] :
    1047             :                                  cpl::IterateNameValue(
    1048          69 :                                      formatOptionsPair.second))
    1049             :                             {
    1050             :                                 const auto eValueType =
    1051          33 :                                     CPLGetValueType(pszValue);
    1052          33 :                                 if (eValueType == CPL_VALUE_INTEGER)
    1053             :                                 {
    1054          21 :                                     oThisFormatSpecificOptions.Add(
    1055             :                                         pszKey, CPLAtoGIntBig(pszValue));
    1056             :                                 }
    1057          12 :                                 else if (eValueType == CPL_VALUE_REAL)
    1058             :                                 {
    1059           9 :                                     oThisFormatSpecificOptions.Add(
    1060             :                                         pszKey, CPLAtof(pszValue));
    1061             :                                 }
    1062             :                                 else
    1063             :                                 {
    1064           3 :                                     oThisFormatSpecificOptions.Add(pszKey,
    1065             :                                                                    pszValue);
    1066             :                                 }
    1067             :                             }
    1068           3 :                             oFormatSpecificOptions.Add(
    1069           3 :                                 formatOptionsPair.first,
    1070             :                                 oThisFormatSpecificOptions);
    1071             :                         }
    1072           3 :                         oGeometryField.Add(
    1073             :                             "coordinatePrecisionFormatSpecificOptions",
    1074             :                             oFormatSpecificOptions);
    1075             :                     }
    1076             :                 }
    1077             :                 else
    1078             :                 {
    1079           4 :                     Concat(osRet, psOptions->bStdoutOutput,
    1080             :                            "Geometry (%s): %s\n", poGFldDefn->GetNameRef(),
    1081             :                            OGRGeometryTypeToName(poGFldDefn->GetType()));
    1082             :                 }
    1083          41 :             }
    1084             :         }
    1085          98 :         else if (psOptions->bGeomType)
    1086             :         {
    1087          98 :             Concat(osRet, psOptions->bStdoutOutput, "Geometry: %s\n",
    1088          98 :                    OGRGeometryTypeToName(poLayer->GetGeomType()));
    1089             :         }
    1090             : 
    1091         139 :         if (psOptions->bFeatureCount)
    1092             :         {
    1093         137 :             if (bJson)
    1094          38 :                 oLayer.Set("featureCount", poLayer->GetFeatureCount());
    1095             :             else
    1096             :             {
    1097          99 :                 Concat(osRet, psOptions->bStdoutOutput,
    1098             :                        "Feature Count: " CPL_FRMT_GIB "\n",
    1099          99 :                        poLayer->GetFeatureCount());
    1100             :             }
    1101             :         }
    1102             : 
    1103         139 :         if (!bJson && psOptions->bExtent && nGeomFieldCount > 1)
    1104             :         {
    1105           6 :             for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
    1106             :             {
    1107           4 :                 if (psOptions->bExtent3D)
    1108             :                 {
    1109           0 :                     OGREnvelope3D oExt;
    1110           0 :                     if (poLayer->GetExtent3D(iGeom, &oExt, TRUE) == OGRERR_NONE)
    1111             :                     {
    1112             :                         OGRGeomFieldDefn *poGFldDefn =
    1113           0 :                             poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
    1114           0 :                         Concat(osRet, psOptions->bStdoutOutput,
    1115             :                                "Extent (%s): (%f, %f, %s) - (%f, %f, %s)\n",
    1116             :                                poGFldDefn->GetNameRef(), oExt.MinX, oExt.MinY,
    1117           0 :                                std::isfinite(oExt.MinZ)
    1118           0 :                                    ? CPLSPrintf("%f", oExt.MinZ)
    1119             :                                    : "none",
    1120             :                                oExt.MaxX, oExt.MaxY,
    1121           0 :                                std::isfinite(oExt.MaxZ)
    1122           0 :                                    ? CPLSPrintf("%f", oExt.MaxZ)
    1123             :                                    : "none");
    1124             :                     }
    1125             :                 }
    1126             :                 else
    1127             :                 {
    1128           4 :                     OGREnvelope oExt;
    1129           4 :                     if (poLayer->GetExtent(iGeom, &oExt, TRUE) == OGRERR_NONE)
    1130             :                     {
    1131             :                         OGRGeomFieldDefn *poGFldDefn =
    1132           4 :                             poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
    1133           4 :                         Concat(osRet, psOptions->bStdoutOutput,
    1134             :                                "Extent (%s): (%f, %f) - (%f, %f)\n",
    1135             :                                poGFldDefn->GetNameRef(), oExt.MinX, oExt.MinY,
    1136             :                                oExt.MaxX, oExt.MaxY);
    1137             :                     }
    1138             :                 }
    1139           2 :             }
    1140             :         }
    1141         137 :         else if (!bJson && psOptions->bExtent)
    1142             :         {
    1143          98 :             if (psOptions->bExtent3D)
    1144             :             {
    1145           2 :                 OGREnvelope3D oExt;
    1146           2 :                 if (poLayer->GetExtent3D(0, &oExt, TRUE) == OGRERR_NONE)
    1147             :                 {
    1148           4 :                     Concat(
    1149           2 :                         osRet, psOptions->bStdoutOutput,
    1150             :                         "Extent: (%f, %f, %s) - (%f, %f, %s)\n", oExt.MinX,
    1151             :                         oExt.MinY,
    1152           3 :                         std::isfinite(oExt.MinZ) ? CPLSPrintf("%f", oExt.MinZ)
    1153             :                                                  : "none",
    1154             :                         oExt.MaxX, oExt.MaxY,
    1155           3 :                         std::isfinite(oExt.MaxZ) ? CPLSPrintf("%f", oExt.MaxZ)
    1156             :                                                  : "none");
    1157             :                 }
    1158             :             }
    1159             :             else
    1160             :             {
    1161          96 :                 OGREnvelope oExt;
    1162          96 :                 if (poLayer->GetExtent(&oExt, TRUE) == OGRERR_NONE)
    1163             :                 {
    1164          18 :                     Concat(osRet, psOptions->bStdoutOutput,
    1165             :                            "Extent: (%f, %f) - (%f, %f)\n", oExt.MinX,
    1166             :                            oExt.MinY, oExt.MaxX, oExt.MaxY);
    1167             :                 }
    1168             :             }
    1169             :         }
    1170             : 
    1171             :         const auto displayExtraInfoSRS =
    1172         172 :             [&osRet, &psOptions](const OGRSpatialReference *poSRS)
    1173             :         {
    1174          28 :             const double dfCoordinateEpoch = poSRS->GetCoordinateEpoch();
    1175          28 :             if (dfCoordinateEpoch > 0)
    1176             :             {
    1177             :                 std::string osCoordinateEpoch =
    1178           4 :                     CPLSPrintf("%f", dfCoordinateEpoch);
    1179           2 :                 const size_t nDotPos = osCoordinateEpoch.find('.');
    1180           2 :                 if (nDotPos != std::string::npos)
    1181             :                 {
    1182          22 :                     while (osCoordinateEpoch.size() > nDotPos + 2 &&
    1183          10 :                            osCoordinateEpoch.back() == '0')
    1184          10 :                         osCoordinateEpoch.pop_back();
    1185             :                 }
    1186           2 :                 Concat(osRet, psOptions->bStdoutOutput,
    1187             :                        "Coordinate epoch: %s\n", osCoordinateEpoch.c_str());
    1188             :             }
    1189             : 
    1190          28 :             const auto &mapping = poSRS->GetDataAxisToSRSAxisMapping();
    1191          28 :             Concat(osRet, psOptions->bStdoutOutput,
    1192             :                    "Data axis to CRS axis mapping: ");
    1193          85 :             for (size_t i = 0; i < mapping.size(); i++)
    1194             :             {
    1195          57 :                 if (i > 0)
    1196             :                 {
    1197          29 :                     Concat(osRet, psOptions->bStdoutOutput, ",");
    1198             :                 }
    1199          57 :                 Concat(osRet, psOptions->bStdoutOutput, "%d", mapping[i]);
    1200             :             }
    1201          28 :             Concat(osRet, psOptions->bStdoutOutput, "\n");
    1202          28 :         };
    1203             : 
    1204         102 :         const auto DisplaySupportedCRSList = [&](int iGeomField)
    1205             :         {
    1206         102 :             const auto &srsList = poLayer->GetSupportedSRSList(iGeomField);
    1207         102 :             if (!srsList.empty())
    1208             :             {
    1209           1 :                 Concat(osRet, psOptions->bStdoutOutput, "Supported SRS: ");
    1210           1 :                 bool bFirst = true;
    1211           3 :                 for (const auto &poSupportedSRS : srsList)
    1212             :                 {
    1213             :                     const char *pszAuthName =
    1214           2 :                         poSupportedSRS->GetAuthorityName(nullptr);
    1215             :                     const char *pszAuthCode =
    1216           2 :                         poSupportedSRS->GetAuthorityCode(nullptr);
    1217           2 :                     if (!bFirst)
    1218           1 :                         Concat(osRet, psOptions->bStdoutOutput, ", ");
    1219           2 :                     bFirst = false;
    1220           2 :                     if (pszAuthName && pszAuthCode)
    1221             :                     {
    1222           2 :                         Concat(osRet, psOptions->bStdoutOutput, "%s:%s",
    1223             :                                pszAuthName, pszAuthCode);
    1224             :                     }
    1225             :                     else
    1226             :                     {
    1227           0 :                         ConcatStr(osRet, psOptions->bStdoutOutput,
    1228             :                                   poSupportedSRS->GetName());
    1229             :                     }
    1230             :                 }
    1231           1 :                 Concat(osRet, psOptions->bStdoutOutput, "\n");
    1232             :             }
    1233         102 :         };
    1234             : 
    1235         139 :         if (!bJson && nGeomFieldCount > 1)
    1236             :         {
    1237             : 
    1238           6 :             for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
    1239             :             {
    1240             :                 OGRGeomFieldDefn *poGFldDefn =
    1241           4 :                     poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
    1242           4 :                 const OGRSpatialReference *poSRS = poGFldDefn->GetSpatialRef();
    1243           4 :                 char *pszWKT = nullptr;
    1244           4 :                 if (poSRS == nullptr)
    1245             :                 {
    1246           0 :                     pszWKT = CPLStrdup("(unknown)");
    1247             :                 }
    1248             :                 else
    1249             :                 {
    1250           4 :                     poSRS->exportToWkt(&pszWKT, apszWKTOptions);
    1251             :                 }
    1252             : 
    1253           4 :                 Concat(osRet, psOptions->bStdoutOutput, "SRS WKT (%s):\n%s\n",
    1254             :                        poGFldDefn->GetNameRef(), pszWKT);
    1255           4 :                 CPLFree(pszWKT);
    1256           4 :                 if (poSRS)
    1257             :                 {
    1258           4 :                     displayExtraInfoSRS(poSRS);
    1259             :                 }
    1260           4 :                 DisplaySupportedCRSList(iGeom);
    1261           2 :             }
    1262             :         }
    1263         137 :         else if (!bJson)
    1264             :         {
    1265          98 :             char *pszWKT = nullptr;
    1266          98 :             auto poSRS = poLayer->GetSpatialRef();
    1267          98 :             if (poSRS == nullptr)
    1268             :             {
    1269          74 :                 pszWKT = CPLStrdup("(unknown)");
    1270             :             }
    1271             :             else
    1272             :             {
    1273          24 :                 poSRS->exportToWkt(&pszWKT, apszWKTOptions);
    1274             :             }
    1275             : 
    1276          98 :             Concat(osRet, psOptions->bStdoutOutput, "Layer SRS WKT:\n%s\n",
    1277             :                    pszWKT);
    1278          98 :             CPLFree(pszWKT);
    1279          98 :             if (poSRS)
    1280             :             {
    1281          24 :                 displayExtraInfoSRS(poSRS);
    1282             :             }
    1283          98 :             DisplaySupportedCRSList(0);
    1284             :         }
    1285             : 
    1286         139 :         const char *pszFIDColumn = poLayer->GetFIDColumn();
    1287         139 :         if (pszFIDColumn[0] != '\0')
    1288             :         {
    1289          33 :             if (bJson)
    1290          18 :                 oLayer.Set("fidColumnName", pszFIDColumn);
    1291             :             else
    1292             :             {
    1293          15 :                 Concat(osRet, psOptions->bStdoutOutput, "FID Column = %s\n",
    1294             :                        pszFIDColumn);
    1295             :             }
    1296             :         }
    1297             : 
    1298         147 :         for (int iGeom = 0; !bJson && iGeom < nGeomFieldCount; iGeom++)
    1299             :         {
    1300             :             OGRGeomFieldDefn *poGFldDefn =
    1301          28 :                 poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
    1302          48 :             if (nGeomFieldCount == 1 && EQUAL(poGFldDefn->GetNameRef(), "") &&
    1303          20 :                 poGFldDefn->IsNullable())
    1304          20 :                 break;
    1305           8 :             Concat(osRet, psOptions->bStdoutOutput, "Geometry Column ");
    1306           8 :             if (nGeomFieldCount > 1)
    1307           4 :                 Concat(osRet, psOptions->bStdoutOutput, "%d ", iGeom + 1);
    1308           8 :             if (!poGFldDefn->IsNullable())
    1309           0 :                 Concat(osRet, psOptions->bStdoutOutput, "NOT NULL ");
    1310           8 :             Concat(osRet, psOptions->bStdoutOutput, "= %s\n",
    1311             :                    poGFldDefn->GetNameRef());
    1312             :         }
    1313             : 
    1314         278 :         CPLJSONArray oFields;
    1315         139 :         if (bJson)
    1316          39 :             oLayer.Add("fields", oFields);
    1317         663 :         for (int iAttr = 0; iAttr < poDefn->GetFieldCount(); iAttr++)
    1318             :         {
    1319         524 :             const OGRFieldDefn *poField = poDefn->GetFieldDefn(iAttr);
    1320         524 :             const char *pszAlias = poField->GetAlternativeNameRef();
    1321         524 :             const std::string &osDomain = poField->GetDomainName();
    1322         524 :             const std::string &osComment = poField->GetComment();
    1323         524 :             const auto eType = poField->GetType();
    1324        1048 :             std::string osTimeZone;
    1325         524 :             if (eType == OFTTime || eType == OFTDate || eType == OFTDateTime)
    1326             :             {
    1327          24 :                 const int nTZFlag = poField->GetTZFlag();
    1328          24 :                 if (nTZFlag == OGR_TZFLAG_LOCALTIME)
    1329             :                 {
    1330           1 :                     osTimeZone = "localtime";
    1331             :                 }
    1332          23 :                 else if (nTZFlag == OGR_TZFLAG_MIXED_TZ)
    1333             :                 {
    1334           1 :                     osTimeZone = "mixed timezones";
    1335             :                 }
    1336          22 :                 else if (nTZFlag == OGR_TZFLAG_UTC)
    1337             :                 {
    1338           1 :                     osTimeZone = "UTC";
    1339             :                 }
    1340          21 :                 else if (nTZFlag > 0)
    1341             :                 {
    1342             :                     char chSign;
    1343           2 :                     const int nOffset = (nTZFlag - OGR_TZFLAG_UTC) * 15;
    1344           2 :                     int nHours =
    1345             :                         static_cast<int>(nOffset / 60);  // Round towards zero.
    1346           2 :                     const int nMinutes = std::abs(nOffset - nHours * 60);
    1347             : 
    1348           2 :                     if (nOffset < 0)
    1349             :                     {
    1350           1 :                         chSign = '-';
    1351           1 :                         nHours = std::abs(nHours);
    1352             :                     }
    1353             :                     else
    1354             :                     {
    1355           1 :                         chSign = '+';
    1356             :                     }
    1357             :                     osTimeZone =
    1358           2 :                         CPLSPrintf("%c%02d:%02d", chSign, nHours, nMinutes);
    1359             :                 }
    1360             :             }
    1361             : 
    1362         524 :             if (bJson)
    1363             :             {
    1364         126 :                 CPLJSONObject oField;
    1365          63 :                 oFields.Add(oField);
    1366          63 :                 oField.Set("name", poField->GetNameRef());
    1367          63 :                 oField.Set("type", OGRFieldDefn::GetFieldTypeName(eType));
    1368          63 :                 if (poField->GetSubType() != OFSTNone)
    1369           1 :                     oField.Set("subType", OGRFieldDefn::GetFieldSubTypeName(
    1370             :                                               poField->GetSubType()));
    1371          63 :                 if (poField->GetWidth() > 0)
    1372          29 :                     oField.Set("width", poField->GetWidth());
    1373          63 :                 if (poField->GetPrecision() > 0)
    1374           6 :                     oField.Set("precision", poField->GetPrecision());
    1375          63 :                 oField.Set("nullable", CPL_TO_BOOL(poField->IsNullable()));
    1376          63 :                 oField.Set("uniqueConstraint",
    1377          63 :                            CPL_TO_BOOL(poField->IsUnique()));
    1378          63 :                 if (poField->GetDefault() != nullptr)
    1379           0 :                     oField.Set("defaultValue", poField->GetDefault());
    1380          63 :                 if (pszAlias != nullptr && pszAlias[0])
    1381           0 :                     oField.Set("alias", pszAlias);
    1382          63 :                 if (!osDomain.empty())
    1383           5 :                     oField.Set("domainName", osDomain);
    1384          63 :                 if (!osComment.empty())
    1385           0 :                     oField.Set("comment", osComment);
    1386          63 :                 if (!osTimeZone.empty())
    1387           5 :                     oField.Set("timezone", osTimeZone);
    1388             :             }
    1389             :             else
    1390             :             {
    1391             :                 const char *pszType =
    1392         461 :                     (poField->GetSubType() != OFSTNone)
    1393         461 :                         ? CPLSPrintf("%s(%s)",
    1394             :                                      OGRFieldDefn::GetFieldTypeName(
    1395             :                                          poField->GetType()),
    1396             :                                      OGRFieldDefn::GetFieldSubTypeName(
    1397             :                                          poField->GetSubType()))
    1398         439 :                         : OGRFieldDefn::GetFieldTypeName(poField->GetType());
    1399         461 :                 Concat(osRet, psOptions->bStdoutOutput, "%s: %s",
    1400             :                        poField->GetNameRef(), pszType);
    1401         461 :                 if (eType == OFTTime || eType == OFTDate ||
    1402             :                     eType == OFTDateTime)
    1403             :                 {
    1404          18 :                     if (!osTimeZone.empty())
    1405           0 :                         Concat(osRet, psOptions->bStdoutOutput, " (%s)",
    1406             :                                osTimeZone.c_str());
    1407             :                 }
    1408             :                 else
    1409             :                 {
    1410         443 :                     Concat(osRet, psOptions->bStdoutOutput, " (%d.%d)",
    1411             :                            poField->GetWidth(), poField->GetPrecision());
    1412             :                 }
    1413         461 :                 if (poField->IsUnique())
    1414           0 :                     Concat(osRet, psOptions->bStdoutOutput, " UNIQUE");
    1415         461 :                 if (!poField->IsNullable())
    1416         204 :                     Concat(osRet, psOptions->bStdoutOutput, " NOT NULL");
    1417         461 :                 if (poField->GetDefault() != nullptr)
    1418           8 :                     Concat(osRet, psOptions->bStdoutOutput, " DEFAULT %s",
    1419             :                            poField->GetDefault());
    1420         461 :                 if (pszAlias != nullptr && pszAlias[0])
    1421           0 :                     Concat(osRet, psOptions->bStdoutOutput,
    1422             :                            ", alternative name=\"%s\"", pszAlias);
    1423         461 :                 if (!osDomain.empty())
    1424           5 :                     Concat(osRet, psOptions->bStdoutOutput, ", domain name=%s",
    1425             :                            osDomain.c_str());
    1426         461 :                 if (!osComment.empty())
    1427           0 :                     Concat(osRet, psOptions->bStdoutOutput, ", comment=%s",
    1428             :                            osComment.c_str());
    1429         461 :                 Concat(osRet, psOptions->bStdoutOutput, "\n");
    1430             :             }
    1431             :         }
    1432             :     }
    1433             : 
    1434             :     /* -------------------------------------------------------------------- */
    1435             :     /*      Read, and dump features.                                        */
    1436             :     /* -------------------------------------------------------------------- */
    1437             : 
    1438         140 :     if (psOptions->nFetchFID == OGRNullFID && !bForceSummary &&
    1439         139 :         !psOptions->bSummaryOnly)
    1440             :     {
    1441          84 :         if (!psOptions->bSuperQuiet)
    1442             :         {
    1443         168 :             CPLJSONArray oFeatures;
    1444             :             const bool bDisplayFields =
    1445          84 :                 CPLTestBool(psOptions->aosOptions.FetchNameValueDef(
    1446             :                     "DISPLAY_FIELDS", "YES"));
    1447             :             const int nFields =
    1448          84 :                 bDisplayFields ? poLayer->GetLayerDefn()->GetFieldCount() : 0;
    1449             :             const bool bDisplayGeometry =
    1450          84 :                 CPLTestBool(psOptions->aosOptions.FetchNameValueDef(
    1451             :                     "DISPLAY_GEOMETRY", "YES"));
    1452             :             const int nGeomFields =
    1453          84 :                 bDisplayGeometry ? poLayer->GetLayerDefn()->GetGeomFieldCount()
    1454          84 :                                  : 0;
    1455          84 :             if (bJson)
    1456           8 :                 oLayer.Add("features", oFeatures);
    1457          84 :             GIntBig nFeatureCount = 0;
    1458         720 :             for (auto &poFeature : poLayer)
    1459             :             {
    1460         636 :                 if (psOptions->nLimit >= 0 &&
    1461           2 :                     nFeatureCount >= psOptions->nLimit)
    1462             :                 {
    1463           1 :                     break;
    1464             :                 }
    1465         635 :                 ++nFeatureCount;
    1466             : 
    1467         635 :                 if (bJson)
    1468             :                 {
    1469          80 :                     CPLJSONObject oFeature;
    1470          80 :                     CPLJSONObject oProperties;
    1471          40 :                     oFeatures.Add(oFeature);
    1472          40 :                     oFeature.Add("type", "Feature");
    1473          40 :                     oFeature.Add("properties", oProperties);
    1474          40 :                     oFeature.Add("fid", poFeature->GetFID());
    1475         140 :                     for (int i = 0; i < nFields; ++i)
    1476             :                     {
    1477         100 :                         const auto poFDefn = poFeature->GetFieldDefnRef(i);
    1478         100 :                         const auto eType = poFDefn->GetType();
    1479         100 :                         if (!poFeature->IsFieldSet(i))
    1480           0 :                             continue;
    1481         100 :                         if (poFeature->IsFieldNull(i))
    1482             :                         {
    1483           1 :                             oProperties.SetNull(poFDefn->GetNameRef());
    1484             :                         }
    1485          99 :                         else if (eType == OFTInteger)
    1486             :                         {
    1487           1 :                             if (poFDefn->GetSubType() == OFSTBoolean)
    1488           0 :                                 oProperties.Add(
    1489             :                                     poFDefn->GetNameRef(),
    1490           0 :                                     CPL_TO_BOOL(
    1491             :                                         poFeature->GetFieldAsInteger(i)));
    1492             :                             else
    1493           1 :                                 oProperties.Add(
    1494             :                                     poFDefn->GetNameRef(),
    1495             :                                     poFeature->GetFieldAsInteger(i));
    1496             :                         }
    1497          98 :                         else if (eType == OFTInteger64)
    1498             :                         {
    1499          31 :                             oProperties.Add(poFDefn->GetNameRef(),
    1500             :                                             poFeature->GetFieldAsInteger64(i));
    1501             :                         }
    1502          67 :                         else if (eType == OFTReal)
    1503             :                         {
    1504          31 :                             oProperties.Add(poFDefn->GetNameRef(),
    1505             :                                             poFeature->GetFieldAsDouble(i));
    1506             :                         }
    1507          36 :                         else if ((eType == OFTString &&
    1508          41 :                                   poFDefn->GetSubType() != OFSTJSON) ||
    1509          72 :                                  eType == OFTDate || eType == OFTTime ||
    1510             :                                  eType == OFTDateTime)
    1511             :                         {
    1512          31 :                             oProperties.Add(poFDefn->GetNameRef(),
    1513             :                                             poFeature->GetFieldAsString(i));
    1514             :                         }
    1515             :                         else
    1516             :                         {
    1517             :                             char *pszSerialized =
    1518           5 :                                 poFeature->GetFieldAsSerializedJSon(i);
    1519           5 :                             if (pszSerialized)
    1520             :                             {
    1521             :                                 const auto eStrType =
    1522           5 :                                     CPLGetValueType(pszSerialized);
    1523           5 :                                 if (eStrType == CPL_VALUE_INTEGER)
    1524             :                                 {
    1525           1 :                                     oProperties.Add(
    1526             :                                         poFDefn->GetNameRef(),
    1527             :                                         CPLAtoGIntBig(pszSerialized));
    1528             :                                 }
    1529           4 :                                 else if (eStrType == CPL_VALUE_REAL)
    1530             :                                 {
    1531           0 :                                     oProperties.Add(poFDefn->GetNameRef(),
    1532             :                                                     CPLAtof(pszSerialized));
    1533             :                                 }
    1534             :                                 else
    1535             :                                 {
    1536           8 :                                     CPLJSONDocument oDoc;
    1537           4 :                                     if (oDoc.LoadMemory(pszSerialized))
    1538           4 :                                         oProperties.Add(poFDefn->GetNameRef(),
    1539           8 :                                                         oDoc.GetRoot());
    1540             :                                 }
    1541           5 :                                 CPLFree(pszSerialized);
    1542             :                             }
    1543             :                         }
    1544             :                     }
    1545             : 
    1546          78 :                     const auto GetGeoJSONOptions = [poLayer](int iGeomField)
    1547             :                     {
    1548          39 :                         CPLStringList aosGeoJSONOptions;
    1549             :                         const auto &oCoordPrec =
    1550          39 :                             poLayer->GetLayerDefn()
    1551          39 :                                 ->GetGeomFieldDefn(iGeomField)
    1552          39 :                                 ->GetCoordinatePrecision();
    1553          39 :                         if (oCoordPrec.dfXYResolution !=
    1554             :                             OGRGeomCoordinatePrecision::UNKNOWN)
    1555             :                         {
    1556             :                             aosGeoJSONOptions.SetNameValue(
    1557             :                                 "XY_COORD_PRECISION",
    1558             :                                 CPLSPrintf("%d",
    1559             :                                            OGRGeomCoordinatePrecision::
    1560             :                                                ResolutionToPrecision(
    1561           1 :                                                    oCoordPrec.dfXYResolution)));
    1562             :                         }
    1563          39 :                         if (oCoordPrec.dfZResolution !=
    1564             :                             OGRGeomCoordinatePrecision::UNKNOWN)
    1565             :                         {
    1566             :                             aosGeoJSONOptions.SetNameValue(
    1567             :                                 "Z_COORD_PRECISION",
    1568             :                                 CPLSPrintf("%d",
    1569             :                                            OGRGeomCoordinatePrecision::
    1570             :                                                ResolutionToPrecision(
    1571           1 :                                                    oCoordPrec.dfZResolution)));
    1572             :                         }
    1573          39 :                         return aosGeoJSONOptions;
    1574          40 :                     };
    1575             : 
    1576          40 :                     if (nGeomFields == 0)
    1577           1 :                         oFeature.SetNull("geometry");
    1578             :                     else
    1579             :                     {
    1580          39 :                         if (const auto poGeom = poFeature->GetGeometryRef())
    1581             :                         {
    1582             :                             char *pszSerialized =
    1583          39 :                                 wkbFlatten(poGeom->getGeometryType()) <=
    1584             :                                         wkbGeometryCollection
    1585          78 :                                     ? poGeom->exportToJson(
    1586          78 :                                           GetGeoJSONOptions(0).List())
    1587          39 :                                     : nullptr;
    1588          39 :                             if (pszSerialized)
    1589             :                             {
    1590          78 :                                 CPLJSONDocument oDoc;
    1591          39 :                                 if (oDoc.LoadMemory(pszSerialized))
    1592          39 :                                     oFeature.Add("geometry", oDoc.GetRoot());
    1593          39 :                                 CPLFree(pszSerialized);
    1594             :                             }
    1595             :                             else
    1596             :                             {
    1597           0 :                                 CPLJSONObject oGeometry;
    1598           0 :                                 oFeature.SetNull("geometry");
    1599           0 :                                 oFeature.Add("wkt_geometry",
    1600           0 :                                              poGeom->exportToWkt());
    1601             :                             }
    1602             :                         }
    1603             :                         else
    1604           0 :                             oFeature.SetNull("geometry");
    1605             : 
    1606          39 :                         if (nGeomFields > 1)
    1607             :                         {
    1608           0 :                             CPLJSONArray oGeometries;
    1609           0 :                             oFeature.Add("geometries", oGeometries);
    1610           0 :                             for (int i = 0; i < nGeomFields; ++i)
    1611             :                             {
    1612           0 :                                 auto poGeom = poFeature->GetGeomFieldRef(i);
    1613           0 :                                 if (poGeom)
    1614             :                                 {
    1615             :                                     char *pszSerialized =
    1616           0 :                                         wkbFlatten(poGeom->getGeometryType()) <=
    1617             :                                                 wkbGeometryCollection
    1618           0 :                                             ? poGeom->exportToJson(
    1619           0 :                                                   GetGeoJSONOptions(i).List())
    1620           0 :                                             : nullptr;
    1621           0 :                                     if (pszSerialized)
    1622             :                                     {
    1623           0 :                                         CPLJSONDocument oDoc;
    1624           0 :                                         if (oDoc.LoadMemory(pszSerialized))
    1625           0 :                                             oGeometries.Add(oDoc.GetRoot());
    1626           0 :                                         CPLFree(pszSerialized);
    1627             :                                     }
    1628             :                                     else
    1629             :                                     {
    1630           0 :                                         CPLJSONObject oGeometry;
    1631           0 :                                         oGeometries.Add(poGeom->exportToWkt());
    1632             :                                     }
    1633             :                                 }
    1634             :                                 else
    1635           0 :                                     oGeometries.AddNull();
    1636             :                             }
    1637             :                         }
    1638             :                     }
    1639             :                 }
    1640             :                 else
    1641             :                 {
    1642         595 :                     ConcatStr(
    1643         595 :                         osRet, psOptions->bStdoutOutput,
    1644             :                         poFeature
    1645        1190 :                             ->DumpReadableAsString(psOptions->aosOptions.List())
    1646             :                             .c_str());
    1647             :                 }
    1648             :             }
    1649          84 :         }
    1650             :     }
    1651          56 :     else if (!bJson && psOptions->nFetchFID != OGRNullFID)
    1652             :     {
    1653           1 :         OGRFeature *poFeature = poLayer->GetFeature(psOptions->nFetchFID);
    1654           1 :         if (poFeature == nullptr)
    1655             :         {
    1656           0 :             Concat(osRet, psOptions->bStdoutOutput,
    1657             :                    "Unable to locate feature id " CPL_FRMT_GIB
    1658             :                    " on this layer.\n",
    1659           0 :                    psOptions->nFetchFID);
    1660             :         }
    1661             :         else
    1662             :         {
    1663           1 :             ConcatStr(
    1664           1 :                 osRet, psOptions->bStdoutOutput,
    1665           2 :                 poFeature->DumpReadableAsString(psOptions->aosOptions.List())
    1666             :                     .c_str());
    1667           1 :             OGRFeature::DestroyFeature(poFeature);
    1668             :         }
    1669             :     }
    1670             : }
    1671             : 
    1672             : /************************************************************************/
    1673             : /*                           PrintLayerSummary()                        */
    1674             : /************************************************************************/
    1675             : 
    1676          19 : static void PrintLayerSummary(CPLString &osRet, CPLJSONObject &oLayer,
    1677             :                               const GDALVectorInfoOptions *psOptions,
    1678             :                               OGRLayer *poLayer, bool bIsPrivate)
    1679             : {
    1680          19 :     const bool bJson = psOptions->eFormat == FORMAT_JSON;
    1681          19 :     if (bJson)
    1682           0 :         oLayer.Set("name", poLayer->GetName());
    1683             :     else
    1684          19 :         ConcatStr(osRet, psOptions->bStdoutOutput, poLayer->GetName());
    1685             : 
    1686          19 :     const char *pszTitle = poLayer->GetMetadataItem("TITLE");
    1687          19 :     if (pszTitle)
    1688             :     {
    1689           0 :         if (bJson)
    1690           0 :             oLayer.Set("title", pszTitle);
    1691             :         else
    1692           0 :             Concat(osRet, psOptions->bStdoutOutput, " (title: %s)", pszTitle);
    1693             :     }
    1694             : 
    1695             :     const int nGeomFieldCount =
    1696          19 :         psOptions->bGeomType ? poLayer->GetLayerDefn()->GetGeomFieldCount() : 0;
    1697          19 :     if (bJson || nGeomFieldCount > 1)
    1698             :     {
    1699           1 :         if (!bJson)
    1700           1 :             Concat(osRet, psOptions->bStdoutOutput, " (");
    1701           2 :         CPLJSONArray oGeometryFields;
    1702           1 :         oLayer.Add("geometryFields", oGeometryFields);
    1703           3 :         for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
    1704             :         {
    1705             :             OGRGeomFieldDefn *poGFldDefn =
    1706           2 :                 poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
    1707           2 :             if (bJson)
    1708             :             {
    1709           0 :                 oGeometryFields.Add(
    1710             :                     OGRGeometryTypeToName(poGFldDefn->GetType()));
    1711             :             }
    1712             :             else
    1713             :             {
    1714           2 :                 if (iGeom > 0)
    1715           1 :                     Concat(osRet, psOptions->bStdoutOutput, ", ");
    1716           2 :                 ConcatStr(osRet, psOptions->bStdoutOutput,
    1717             :                           OGRGeometryTypeToName(poGFldDefn->GetType()));
    1718             :             }
    1719             :         }
    1720           1 :         if (!bJson)
    1721           2 :             Concat(osRet, psOptions->bStdoutOutput, ")");
    1722             :     }
    1723          18 :     else if (psOptions->bGeomType && poLayer->GetGeomType() != wkbUnknown)
    1724          10 :         Concat(osRet, psOptions->bStdoutOutput, " (%s)",
    1725          10 :                OGRGeometryTypeToName(poLayer->GetGeomType()));
    1726             : 
    1727          19 :     if (bIsPrivate)
    1728             :     {
    1729           0 :         if (bJson)
    1730           0 :             oLayer.Set("isPrivate", true);
    1731             :         else
    1732           0 :             Concat(osRet, psOptions->bStdoutOutput, " [private]");
    1733             :     }
    1734             : 
    1735          19 :     if (!bJson)
    1736          19 :         Concat(osRet, psOptions->bStdoutOutput, "\n");
    1737          19 : }
    1738             : 
    1739             : /************************************************************************/
    1740             : /*                       ReportHiearchicalLayers()                      */
    1741             : /************************************************************************/
    1742             : 
    1743           6 : static void ReportHiearchicalLayers(CPLString &osRet, CPLJSONObject &oRoot,
    1744             :                                     const GDALVectorInfoOptions *psOptions,
    1745             :                                     const GDALGroup *group,
    1746             :                                     const std::string &indent, bool bGeomType)
    1747             : {
    1748           6 :     const bool bJson = psOptions->eFormat == FORMAT_JSON;
    1749          12 :     const auto aosVectorLayerNames = group->GetVectorLayerNames();
    1750          12 :     CPLJSONArray oLayerNames;
    1751           6 :     oRoot.Add("layerNames", oLayerNames);
    1752          25 :     for (const auto &osVectorLayerName : aosVectorLayerNames)
    1753             :     {
    1754          19 :         OGRLayer *poLayer = group->OpenVectorLayer(osVectorLayerName);
    1755          19 :         if (poLayer)
    1756             :         {
    1757          38 :             CPLJSONObject oLayer;
    1758          19 :             if (!bJson)
    1759             :             {
    1760           4 :                 Concat(osRet, psOptions->bStdoutOutput,
    1761             :                        "%sLayer: ", indent.c_str());
    1762           4 :                 PrintLayerSummary(osRet, oLayer, psOptions, poLayer,
    1763             :                                   /* bIsPrivate=*/false);
    1764             :             }
    1765             :             else
    1766             :             {
    1767          15 :                 oLayerNames.Add(poLayer->GetName());
    1768             :             }
    1769             :         }
    1770             :     }
    1771             : 
    1772          12 :     const std::string subIndent(indent + "  ");
    1773          12 :     auto aosSubGroupNames = group->GetGroupNames();
    1774          12 :     CPLJSONArray oGroupArray;
    1775           6 :     oRoot.Add("groups", oGroupArray);
    1776           8 :     for (const auto &osSubGroupName : aosSubGroupNames)
    1777             :     {
    1778           4 :         auto poSubGroup = group->OpenGroup(osSubGroupName);
    1779           2 :         if (poSubGroup)
    1780             :         {
    1781           4 :             CPLJSONObject oGroup;
    1782           2 :             if (!bJson)
    1783             :             {
    1784           2 :                 Concat(osRet, psOptions->bStdoutOutput, "Group %s",
    1785             :                        indent.c_str());
    1786           2 :                 Concat(osRet, psOptions->bStdoutOutput, "%s:\n",
    1787             :                        osSubGroupName.c_str());
    1788             :             }
    1789             :             else
    1790             :             {
    1791           0 :                 oGroupArray.Add(oGroup);
    1792           0 :                 oGroup.Set("name", osSubGroupName);
    1793             :             }
    1794           2 :             ReportHiearchicalLayers(osRet, oGroup, psOptions, poSubGroup.get(),
    1795             :                                     subIndent, bGeomType);
    1796             :         }
    1797             :     }
    1798           6 : }
    1799             : 
    1800             : /************************************************************************/
    1801             : /*                           GDALVectorInfo()                           */
    1802             : /************************************************************************/
    1803             : 
    1804             : /**
    1805             :  * Lists various information about a GDAL supported vector dataset.
    1806             :  *
    1807             :  * This is the equivalent of the <a href="/programs/ogrinfo.html">ogrinfo</a>
    1808             :  * utility.
    1809             :  *
    1810             :  * GDALVectorInfoOptions* must be allocated and freed with
    1811             :  * GDALVectorInfoOptionsNew() and GDALVectorInfoOptionsFree() respectively.
    1812             :  *
    1813             :  * @param hDataset the dataset handle.
    1814             :  * @param psOptions the options structure returned by GDALVectorInfoOptionsNew()
    1815             :  * or NULL.
    1816             :  * @return string corresponding to the information about the raster dataset
    1817             :  * (must be freed with CPLFree()), or NULL in case of error.
    1818             :  *
    1819             :  * @since GDAL 3.7
    1820             :  */
    1821          76 : char *GDALVectorInfo(GDALDatasetH hDataset,
    1822             :                      const GDALVectorInfoOptions *psOptions)
    1823             : {
    1824          76 :     auto poDS = GDALDataset::FromHandle(hDataset);
    1825          76 :     if (poDS == nullptr)
    1826           0 :         return nullptr;
    1827             : 
    1828         152 :     const GDALVectorInfoOptions sDefaultOptions;
    1829          76 :     if (!psOptions)
    1830           0 :         psOptions = &sDefaultOptions;
    1831             : 
    1832          76 :     GDALDriver *poDriver = poDS->GetDriver();
    1833             : 
    1834         152 :     CPLString osRet;
    1835         152 :     CPLJSONObject oRoot;
    1836         152 :     const std::string osFilename(poDS->GetDescription());
    1837             : 
    1838          76 :     const bool bJson = psOptions->eFormat == FORMAT_JSON;
    1839         152 :     CPLJSONArray oLayerArray;
    1840          76 :     if (bJson)
    1841             :     {
    1842          27 :         oRoot.Set("description", poDS->GetDescription());
    1843          27 :         if (poDriver)
    1844             :         {
    1845          27 :             oRoot.Set("driverShortName", poDriver->GetDescription());
    1846          27 :             oRoot.Set("driverLongName",
    1847          27 :                       poDriver->GetMetadataItem(GDAL_DMD_LONGNAME));
    1848             :         }
    1849          27 :         oRoot.Add("layers", oLayerArray);
    1850             :     }
    1851             : 
    1852             :     /* -------------------------------------------------------------------- */
    1853             :     /*      Some information messages.                                      */
    1854             :     /* -------------------------------------------------------------------- */
    1855          76 :     if (!bJson && psOptions->bVerbose)
    1856             :     {
    1857          94 :         Concat(osRet, psOptions->bStdoutOutput,
    1858             :                "INFO: Open of `%s'\n"
    1859             :                "      using driver `%s' successful.\n",
    1860             :                osFilename.c_str(),
    1861          47 :                poDriver ? poDriver->GetDescription() : "(null)");
    1862             :     }
    1863             : 
    1864         123 :     if (!bJson && psOptions->bVerbose &&
    1865          47 :         !EQUAL(osFilename.c_str(), poDS->GetDescription()))
    1866             :     {
    1867           0 :         Concat(osRet, psOptions->bStdoutOutput,
    1868             :                "INFO: Internal data source name `%s'\n"
    1869             :                "      different from user name `%s'.\n",
    1870           0 :                poDS->GetDescription(), osFilename.c_str());
    1871             :     }
    1872             : 
    1873          76 :     GDALVectorInfoReportMetadata(osRet, oRoot, psOptions, poDS,
    1874          76 :                                  psOptions->bListMDD, psOptions->bShowMetadata,
    1875             :                                  psOptions->aosExtraMDDomains.List());
    1876             : 
    1877         152 :     CPLJSONObject oDomains;
    1878          76 :     oRoot.Add("domains", oDomains);
    1879          76 :     if (!psOptions->osFieldDomain.empty())
    1880             :     {
    1881           7 :         auto poDomain = poDS->GetFieldDomain(psOptions->osFieldDomain);
    1882           7 :         if (poDomain == nullptr)
    1883             :         {
    1884           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Domain %s cannot be found.",
    1885             :                      psOptions->osFieldDomain.c_str());
    1886           0 :             return nullptr;
    1887             :         }
    1888           7 :         if (!bJson)
    1889           7 :             Concat(osRet, psOptions->bStdoutOutput, "\n");
    1890           7 :         ReportFieldDomain(osRet, oDomains, psOptions, poDomain);
    1891           7 :         if (!bJson)
    1892           7 :             Concat(osRet, psOptions->bStdoutOutput, "\n");
    1893             :     }
    1894          69 :     else if (bJson)
    1895             :     {
    1896          34 :         for (const auto &osDomainName : poDS->GetFieldDomainNames())
    1897             :         {
    1898           7 :             auto poDomain = poDS->GetFieldDomain(osDomainName);
    1899           7 :             if (poDomain)
    1900             :             {
    1901           7 :                 ReportFieldDomain(osRet, oDomains, psOptions, poDomain);
    1902             :             }
    1903             :         }
    1904             :     }
    1905             : 
    1906          76 :     int nRepeatCount = psOptions->nRepeatCount;
    1907          76 :     if (psOptions->bDatasetGetNextFeature)
    1908             :     {
    1909           1 :         nRepeatCount = 0;  // skip layer reporting.
    1910             : 
    1911             :         /* --------------------------------------------------------------------
    1912             :          */
    1913             :         /*      Set filters if provided. */
    1914             :         /* --------------------------------------------------------------------
    1915             :          */
    1916           2 :         if (!psOptions->osWHERE.empty() ||
    1917           1 :             psOptions->poSpatialFilter != nullptr)
    1918             :         {
    1919           0 :             for (int iLayer = 0; iLayer < poDS->GetLayerCount(); iLayer++)
    1920             :             {
    1921           0 :                 OGRLayer *poLayer = poDS->GetLayer(iLayer);
    1922             : 
    1923           0 :                 if (poLayer == nullptr)
    1924             :                 {
    1925           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1926             :                              "Couldn't fetch advertised layer %d.", iLayer);
    1927           0 :                     return nullptr;
    1928             :                 }
    1929             : 
    1930           0 :                 if (!psOptions->osWHERE.empty())
    1931             :                 {
    1932           0 :                     if (poLayer->SetAttributeFilter(
    1933           0 :                             psOptions->osWHERE.c_str()) != OGRERR_NONE)
    1934             :                     {
    1935           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1936             :                                  "SetAttributeFilter(%s) failed on layer %s.",
    1937             :                                  psOptions->osWHERE.c_str(),
    1938           0 :                                  poLayer->GetName());
    1939             :                     }
    1940             :                 }
    1941             : 
    1942           0 :                 if (psOptions->poSpatialFilter != nullptr)
    1943             :                 {
    1944           0 :                     if (!psOptions->osGeomField.empty())
    1945             :                     {
    1946           0 :                         OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
    1947           0 :                         const int iGeomField = poDefn->GetGeomFieldIndex(
    1948           0 :                             psOptions->osGeomField.c_str());
    1949           0 :                         if (iGeomField >= 0)
    1950           0 :                             poLayer->SetSpatialFilter(
    1951           0 :                                 iGeomField, psOptions->poSpatialFilter.get());
    1952             :                         else
    1953           0 :                             CPLError(CE_Warning, CPLE_AppDefined,
    1954             :                                      "Cannot find geometry field %s.",
    1955             :                                      psOptions->osGeomField.c_str());
    1956             :                     }
    1957             :                     else
    1958             :                     {
    1959           0 :                         poLayer->SetSpatialFilter(
    1960           0 :                             psOptions->poSpatialFilter.get());
    1961             :                     }
    1962             :                 }
    1963             :             }
    1964             :         }
    1965             : 
    1966           1 :         std::set<OGRLayer *> oSetLayers;
    1967             :         while (true)
    1968             :         {
    1969          11 :             OGRLayer *poLayer = nullptr;
    1970             :             OGRFeature *poFeature =
    1971          11 :                 poDS->GetNextFeature(&poLayer, nullptr, nullptr, nullptr);
    1972          11 :             if (poFeature == nullptr)
    1973           1 :                 break;
    1974          10 :             if (psOptions->aosLayers.empty() || poLayer == nullptr ||
    1975           0 :                 CSLFindString(psOptions->aosLayers.List(),
    1976           0 :                               poLayer->GetName()) >= 0)
    1977             :             {
    1978          10 :                 if (psOptions->bVerbose && poLayer != nullptr &&
    1979          10 :                     oSetLayers.find(poLayer) == oSetLayers.end())
    1980             :                 {
    1981           0 :                     oSetLayers.insert(poLayer);
    1982           0 :                     CPLJSONObject oLayer;
    1983           0 :                     oLayerArray.Add(oLayer);
    1984           0 :                     ReportOnLayer(osRet, oLayer, psOptions, poLayer,
    1985             :                                   /*bForceSummary = */ true,
    1986             :                                   /*bTakeIntoAccountWHERE = */ false,
    1987             :                                   /*bTakeIntoAccountSpatialFilter = */ false,
    1988             :                                   /*bTakeIntoAccountGeomField = */ false);
    1989             :                 }
    1990          10 :                 if (!psOptions->bSuperQuiet && !psOptions->bSummaryOnly)
    1991          10 :                     poFeature->DumpReadable(
    1992             :                         nullptr,
    1993             :                         const_cast<char **>(psOptions->aosOptions.List()));
    1994             :             }
    1995          10 :             OGRFeature::DestroyFeature(poFeature);
    1996          10 :         }
    1997             :     }
    1998             : 
    1999             :     /* -------------------------------------------------------------------- */
    2000             :     /*      Special case for -sql clause.  No source layers required.       */
    2001             :     /* -------------------------------------------------------------------- */
    2002          75 :     else if (!psOptions->osSQLStatement.empty())
    2003             :     {
    2004           3 :         nRepeatCount = 0;  // skip layer reporting.
    2005             : 
    2006           3 :         if (!bJson && !psOptions->aosLayers.empty())
    2007           0 :             Concat(osRet, psOptions->bStdoutOutput,
    2008             :                    "layer names ignored in combination with -sql.\n");
    2009             : 
    2010           3 :         CPLErrorReset();
    2011           9 :         OGRLayer *poResultSet = poDS->ExecuteSQL(
    2012             :             psOptions->osSQLStatement.c_str(),
    2013           6 :             psOptions->osGeomField.empty() ? psOptions->poSpatialFilter.get()
    2014             :                                            : nullptr,
    2015           3 :             psOptions->osDialect.empty() ? nullptr
    2016           3 :                                          : psOptions->osDialect.c_str());
    2017             : 
    2018           3 :         if (poResultSet != nullptr)
    2019             :         {
    2020           2 :             if (!psOptions->osWHERE.empty())
    2021             :             {
    2022           0 :                 if (poResultSet->SetAttributeFilter(
    2023           0 :                         psOptions->osWHERE.c_str()) != OGRERR_NONE)
    2024             :                 {
    2025           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2026             :                              "SetAttributeFilter(%s) failed.",
    2027             :                              psOptions->osWHERE.c_str());
    2028           0 :                     return nullptr;
    2029             :                 }
    2030             :             }
    2031             : 
    2032           4 :             CPLJSONObject oLayer;
    2033           2 :             oLayerArray.Add(oLayer);
    2034           2 :             if (!psOptions->osGeomField.empty())
    2035           0 :                 ReportOnLayer(osRet, oLayer, psOptions, poResultSet,
    2036             :                               /*bForceSummary = */ false,
    2037             :                               /*bTakeIntoAccountWHERE = */ false,
    2038             :                               /*bTakeIntoAccountSpatialFilter = */ true,
    2039             :                               /*bTakeIntoAccountGeomField = */ true);
    2040             :             else
    2041           2 :                 ReportOnLayer(osRet, oLayer, psOptions, poResultSet,
    2042             :                               /*bForceSummary = */ false,
    2043             :                               /*bTakeIntoAccountWHERE = */ false,
    2044             :                               /*bTakeIntoAccountSpatialFilter = */ false,
    2045             :                               /*bTakeIntoAccountGeomField = */ false);
    2046             : 
    2047           2 :             poDS->ReleaseResultSet(poResultSet);
    2048             :         }
    2049           1 :         else if (CPLGetLastErrorType() != CE_None)
    2050             :         {
    2051           1 :             return nullptr;
    2052             :         }
    2053             :     }
    2054             : 
    2055             :     // coverity[tainted_data]
    2056          75 :     auto papszLayers = psOptions->aosLayers.List();
    2057         146 :     for (int iRepeat = 0; iRepeat < nRepeatCount; iRepeat++)
    2058             :     {
    2059          72 :         if (papszLayers == nullptr || papszLayers[0] == nullptr)
    2060             :         {
    2061          62 :             const int nLayerCount = poDS->GetLayerCount();
    2062          62 :             if (iRepeat == 0)
    2063          62 :                 CPLDebug("OGR", "GetLayerCount() = %d\n", nLayerCount);
    2064             : 
    2065          62 :             bool bDone = false;
    2066          62 :             auto poRootGroup = poDS->GetRootGroup();
    2067          66 :             if ((bJson || !psOptions->bAllLayers) && poRootGroup &&
    2068          66 :                 (!poRootGroup->GetGroupNames().empty() ||
    2069          65 :                  !poRootGroup->GetVectorLayerNames().empty()))
    2070             :             {
    2071           8 :                 CPLJSONObject oGroup;
    2072           4 :                 oRoot.Add("rootGroup", oGroup);
    2073           4 :                 ReportHiearchicalLayers(osRet, oGroup, psOptions,
    2074           8 :                                         poRootGroup.get(), std::string(),
    2075           4 :                                         psOptions->bGeomType);
    2076           4 :                 if (!bJson)
    2077           1 :                     bDone = true;
    2078             :             }
    2079             : 
    2080             :             /* --------------------------------------------------------------------
    2081             :              */
    2082             :             /*      Process each data source layer. */
    2083             :             /* --------------------------------------------------------------------
    2084             :              */
    2085         205 :             for (int iLayer = 0; !bDone && iLayer < nLayerCount; iLayer++)
    2086             :             {
    2087         143 :                 OGRLayer *poLayer = poDS->GetLayer(iLayer);
    2088             : 
    2089         143 :                 if (poLayer == nullptr)
    2090             :                 {
    2091           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2092             :                              "Couldn't fetch advertised layer %d.", iLayer);
    2093           0 :                     return nullptr;
    2094             :                 }
    2095             : 
    2096         286 :                 CPLJSONObject oLayer;
    2097         143 :                 oLayerArray.Add(oLayer);
    2098         143 :                 if (!psOptions->bAllLayers)
    2099             :                 {
    2100          15 :                     if (!bJson)
    2101          15 :                         Concat(osRet, psOptions->bStdoutOutput,
    2102             :                                "%d: ", iLayer + 1);
    2103          15 :                     PrintLayerSummary(osRet, oLayer, psOptions, poLayer,
    2104          15 :                                       poDS->IsLayerPrivate(iLayer));
    2105             :                 }
    2106             :                 else
    2107             :                 {
    2108         128 :                     if (iRepeat != 0)
    2109           0 :                         poLayer->ResetReading();
    2110             : 
    2111         128 :                     ReportOnLayer(osRet, oLayer, psOptions, poLayer,
    2112             :                                   /*bForceSummary = */ false,
    2113             :                                   /*bTakeIntoAccountWHERE = */ true,
    2114             :                                   /*bTakeIntoAccountSpatialFilter = */ true,
    2115             :                                   /*bTakeIntoAccountGeomField = */ true);
    2116             :                 }
    2117          62 :             }
    2118             :         }
    2119             :         else
    2120             :         {
    2121             :             /* --------------------------------------------------------------------
    2122             :              */
    2123             :             /*      Process specified data source layers. */
    2124             :             /* --------------------------------------------------------------------
    2125             :              */
    2126             : 
    2127          20 :             for (const char *pszLayer : cpl::Iterate(papszLayers))
    2128             :             {
    2129          11 :                 OGRLayer *poLayer = poDS->GetLayerByName(pszLayer);
    2130             : 
    2131          11 :                 if (poLayer == nullptr)
    2132             :                 {
    2133           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2134             :                              "Couldn't fetch requested layer %s.", pszLayer);
    2135           1 :                     return nullptr;
    2136             :                 }
    2137             : 
    2138          10 :                 if (iRepeat != 0)
    2139           0 :                     poLayer->ResetReading();
    2140             : 
    2141          20 :                 CPLJSONObject oLayer;
    2142          10 :                 oLayerArray.Add(oLayer);
    2143          10 :                 ReportOnLayer(osRet, oLayer, psOptions, poLayer,
    2144             :                               /*bForceSummary = */ false,
    2145             :                               /*bTakeIntoAccountWHERE = */ true,
    2146             :                               /*bTakeIntoAccountSpatialFilter = */ true,
    2147             :                               /*bTakeIntoAccountGeomField = */ true);
    2148             :             }
    2149             :         }
    2150             :     }
    2151             : 
    2152          74 :     if (!papszLayers)
    2153             :     {
    2154          65 :         ReportRelationships(osRet, oRoot, psOptions, poDS);
    2155             :     }
    2156             : 
    2157          74 :     if (bJson)
    2158             :     {
    2159          26 :         osRet.clear();
    2160          26 :         ConcatStr(
    2161          26 :             osRet, psOptions->bStdoutOutput,
    2162             :             json_object_to_json_string_ext(
    2163          26 :                 static_cast<struct json_object *>(oRoot.GetInternalHandle()),
    2164             :                 JSON_C_TO_STRING_PRETTY
    2165             : #ifdef JSON_C_TO_STRING_NOSLASHESCAPE
    2166             :                     | JSON_C_TO_STRING_NOSLASHESCAPE
    2167             : #endif
    2168             :                 ));
    2169          26 :         ConcatStr(osRet, psOptions->bStdoutOutput, "\n");
    2170             :     }
    2171             : 
    2172          74 :     return VSI_STRDUP_VERBOSE(osRet);
    2173             : }
    2174             : 
    2175             : /************************************************************************/
    2176             : /*                    GDALVectorInfoOptionsGetParser()                  */
    2177             : /************************************************************************/
    2178             : 
    2179          85 : static std::unique_ptr<GDALArgumentParser> GDALVectorInfoOptionsGetParser(
    2180             :     GDALVectorInfoOptions *psOptions,
    2181             :     GDALVectorInfoOptionsForBinary *psOptionsForBinary)
    2182             : {
    2183             :     auto argParser = std::make_unique<GDALArgumentParser>(
    2184          85 :         "ogrinfo", /* bForBinary=*/psOptionsForBinary != nullptr);
    2185             : 
    2186          85 :     argParser->add_description(
    2187          85 :         _("Lists information about an OGR-supported data source."));
    2188             : 
    2189          85 :     argParser->add_epilog(
    2190          85 :         _("For more details, consult https://gdal.org/programs/ogrinfo.html"));
    2191             : 
    2192          85 :     argParser->add_argument("-json")
    2193          85 :         .flag()
    2194             :         .action(
    2195          27 :             [psOptions](const std::string &)
    2196             :             {
    2197          27 :                 psOptions->eFormat = FORMAT_JSON;
    2198          27 :                 psOptions->bAllLayers = true;
    2199          27 :                 psOptions->bSummaryOnly = true;
    2200          85 :             })
    2201          85 :         .help(_("Display the output in json format."));
    2202             : 
    2203          85 :     argParser->add_argument("-ro")
    2204          85 :         .flag()
    2205             :         .action(
    2206          14 :             [psOptionsForBinary](const std::string &)
    2207             :             {
    2208           7 :                 if (psOptionsForBinary)
    2209           7 :                     psOptionsForBinary->bReadOnly = true;
    2210          85 :             })
    2211          85 :         .help(_("Open the data source in read-only mode."));
    2212             : 
    2213          85 :     argParser->add_argument("-update")
    2214          85 :         .flag()
    2215             :         .action(
    2216           0 :             [psOptionsForBinary](const std::string &)
    2217             :             {
    2218           0 :                 if (psOptionsForBinary)
    2219           0 :                     psOptionsForBinary->bUpdate = true;
    2220          85 :             })
    2221          85 :         .help(_("Open the data source in update mode."));
    2222             : 
    2223          85 :     argParser->add_argument("-q", "--quiet")
    2224          85 :         .flag()
    2225             :         .action(
    2226           4 :             [psOptions, psOptionsForBinary](const std::string &)
    2227             :             {
    2228           2 :                 psOptions->bVerbose = false;
    2229           2 :                 if (psOptionsForBinary)
    2230           2 :                     psOptionsForBinary->bVerbose = false;
    2231          85 :             })
    2232             :         .help(_("Quiet mode. No progress message is emitted on the standard "
    2233          85 :                 "output."));
    2234             : 
    2235             : #ifdef __AFL_HAVE_MANUAL_CONTROL
    2236             :     /* Undocumented: mainly only useful for AFL testing */
    2237             :     argParser->add_argument("-qq")
    2238             :         .flag()
    2239             :         .hidden()
    2240             :         .action(
    2241             :             [psOptions, psOptionsForBinary](const std::string &)
    2242             :             {
    2243             :                 psOptions->bVerbose = false;
    2244             :                 if (psOptionsForBinary)
    2245             :                     psOptionsForBinary->bVerbose = false;
    2246             :                 psOptions->bSuperQuiet = true;
    2247             :             })
    2248             :         .help(_("Super quiet mode."));
    2249             : #endif
    2250             : 
    2251          85 :     argParser->add_argument("-fid")
    2252         170 :         .metavar("<FID>")
    2253          85 :         .store_into(psOptions->nFetchFID)
    2254          85 :         .help(_("Only the feature with this feature id will be reported."));
    2255             : 
    2256          85 :     argParser->add_argument("-spat")
    2257         170 :         .metavar("<xmin> <ymin> <xmax> <ymax>")
    2258          85 :         .nargs(4)
    2259          85 :         .scan<'g', double>()
    2260             :         .help(_("The area of interest. Only features within the rectangle will "
    2261          85 :                 "be reported."));
    2262             : 
    2263          85 :     argParser->add_argument("-geomfield")
    2264         170 :         .metavar("<field>")
    2265          85 :         .store_into(psOptions->osGeomField)
    2266             :         .help(_("Name of the geometry field on which the spatial filter "
    2267          85 :                 "operates."));
    2268             : 
    2269          85 :     argParser->add_argument("-where")
    2270         170 :         .metavar("<restricted_where>")
    2271          85 :         .store_into(psOptions->osWHERE)
    2272             :         .help(_("An attribute query in a restricted form of the queries used "
    2273          85 :                 "in the SQL WHERE statement."));
    2274             : 
    2275             :     {
    2276          85 :         auto &group = argParser->add_mutually_exclusive_group();
    2277          85 :         group.add_argument("-sql")
    2278         170 :             .metavar("<statement|@filename>")
    2279          85 :             .store_into(psOptions->osSQLStatement)
    2280             :             .help(_(
    2281          85 :                 "Execute the indicated SQL statement and return the result."));
    2282             : 
    2283          85 :         group.add_argument("-rl")
    2284          85 :             .store_into(psOptions->bDatasetGetNextFeature)
    2285          85 :             .help(_("Enable random layer reading mode."));
    2286             :     }
    2287             : 
    2288          85 :     argParser->add_argument("-dialect")
    2289         170 :         .metavar("<dialect>")
    2290          85 :         .store_into(psOptions->osDialect)
    2291          85 :         .help(_("SQL dialect."));
    2292             : 
    2293             :     // Only for fuzzing
    2294          85 :     argParser->add_argument("-rc")
    2295          85 :         .hidden()
    2296         170 :         .metavar("<count>")
    2297          85 :         .store_into(psOptions->nRepeatCount)
    2298          85 :         .help(_("Repeat count"));
    2299             : 
    2300          85 :     argParser->add_argument("-al")
    2301          85 :         .store_into(psOptions->bAllLayers)
    2302             :         .help(_("List all layers (used instead of having to give layer names "
    2303          85 :                 "as arguments)"));
    2304             : 
    2305             :     {
    2306          85 :         auto &group = argParser->add_mutually_exclusive_group();
    2307          85 :         group.add_argument("-so", "-summary")
    2308          85 :             .store_into(psOptions->bSummaryParser)
    2309             :             .help(_("Summary only: list all layers (used instead of having to "
    2310          85 :                     "give layer names as arguments)"));
    2311             : 
    2312          85 :         group.add_argument("-features")
    2313          85 :             .store_into(psOptions->bFeaturesParser)
    2314          85 :             .help(_("Enable listing of features"));
    2315             :     }
    2316             : 
    2317          85 :     argParser->add_argument("-limit")
    2318         170 :         .metavar("<nb_features>")
    2319          85 :         .store_into(psOptions->nLimit)
    2320          85 :         .help(_("Limit the number of features per layer."));
    2321             : 
    2322          85 :     argParser->add_argument("-fields")
    2323          85 :         .choices("YES", "NO")
    2324         170 :         .metavar("YES|NO")
    2325             :         .action(
    2326           2 :             [psOptions](const std::string &s) {
    2327           2 :                 psOptions->aosOptions.SetNameValue("DISPLAY_FIELDS", s.c_str());
    2328          85 :             })
    2329             :         .help(
    2330          85 :             _("If set to NO, the feature dump will not display field values."));
    2331             : 
    2332          85 :     argParser->add_argument("-geom")
    2333          85 :         .choices("YES", "NO", "SUMMARY", "WKT", "ISO_WKT")
    2334         170 :         .metavar("YES|NO|SUMMARY|WKT|ISO_WKT")
    2335             :         .action(
    2336           3 :             [psOptions](const std::string &s) {
    2337             :                 psOptions->aosOptions.SetNameValue("DISPLAY_GEOMETRY",
    2338           3 :                                                    s.c_str());
    2339          85 :             })
    2340          85 :         .help(_("How to display geometries in feature dump."));
    2341             : 
    2342          85 :     argParser->add_argument("-oo")
    2343          85 :         .append()
    2344         170 :         .metavar("<NAME=VALUE>")
    2345             :         .action(
    2346          20 :             [psOptionsForBinary](const std::string &s)
    2347             :             {
    2348          10 :                 if (psOptionsForBinary)
    2349          10 :                     psOptionsForBinary->aosOpenOptions.AddString(s.c_str());
    2350          85 :             })
    2351          85 :         .help(_("Dataset open option (format-specific)"));
    2352             : 
    2353          85 :     argParser->add_argument("-nomd")
    2354          85 :         .flag()
    2355           1 :         .action([psOptions](const std::string &)
    2356          85 :                 { psOptions->bShowMetadata = false; })
    2357          85 :         .help(_("Suppress metadata printing"));
    2358             : 
    2359          85 :     argParser->add_argument("-listmdd")
    2360          85 :         .store_into(psOptions->bListMDD)
    2361          85 :         .help(_("List all metadata domains available for the dataset."));
    2362             : 
    2363          85 :     argParser->add_argument("-mdd")
    2364          85 :         .append()
    2365         170 :         .metavar("<domain>")
    2366           1 :         .action([psOptions](const std::string &s)
    2367          86 :                 { psOptions->aosExtraMDDomains.AddString(s.c_str()); })
    2368          85 :         .help(_("List metadata in the specified domain."));
    2369             : 
    2370          85 :     argParser->add_argument("-nocount")
    2371          85 :         .flag()
    2372           2 :         .action([psOptions](const std::string &)
    2373          85 :                 { psOptions->bFeatureCount = false; })
    2374          85 :         .help(_("Suppress feature count printing."));
    2375             : 
    2376          85 :     argParser->add_argument("-noextent")
    2377          85 :         .flag()
    2378           0 :         .action([psOptions](const std::string &)
    2379          85 :                 { psOptions->bExtent = false; })
    2380          85 :         .help(_("Suppress spatial extent printing."));
    2381             : 
    2382          85 :     argParser->add_argument("-extent3D")
    2383          85 :         .store_into(psOptions->bExtent3D)
    2384          85 :         .help(_("Request a 3D extent to be reported."));
    2385             : 
    2386          85 :     argParser->add_argument("-nogeomtype")
    2387          85 :         .flag()
    2388           1 :         .action([psOptions](const std::string &)
    2389          85 :                 { psOptions->bGeomType = false; })
    2390          85 :         .help(_("Suppress layer geometry type printing."));
    2391             : 
    2392          85 :     argParser->add_argument("-wkt_format")
    2393          85 :         .store_into(psOptions->osWKTFormat)
    2394         170 :         .metavar("WKT1|WKT2|WKT2_2015|WKT2_2019")
    2395          85 :         .help(_("The WKT format used to display the SRS."));
    2396             : 
    2397          85 :     argParser->add_argument("-fielddomain")
    2398          85 :         .store_into(psOptions->osFieldDomain)
    2399         170 :         .metavar("<name>")
    2400          85 :         .help(_("Display details about a field domain."));
    2401             : 
    2402          85 :     argParser->add_argument("-if")
    2403          85 :         .append()
    2404         170 :         .metavar("<format>")
    2405             :         .action(
    2406           4 :             [psOptionsForBinary](const std::string &s)
    2407             :             {
    2408           2 :                 if (psOptionsForBinary)
    2409             :                 {
    2410           2 :                     if (GDALGetDriverByName(s.c_str()) == nullptr)
    2411             :                     {
    2412           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    2413             :                                  "%s is not a recognized driver", s.c_str());
    2414             :                     }
    2415             :                     psOptionsForBinary->aosAllowInputDrivers.AddString(
    2416           2 :                         s.c_str());
    2417             :                 }
    2418          85 :             })
    2419          85 :         .help(_("Format/driver name(s) to try when opening the input file."));
    2420             : 
    2421          85 :     auto &argFilename = argParser->add_argument("filename")
    2422             :                             .action(
    2423          89 :                                 [psOptionsForBinary](const std::string &s)
    2424             :                                 {
    2425          46 :                                     if (psOptionsForBinary)
    2426          43 :                                         psOptionsForBinary->osFilename = s;
    2427          85 :                                 })
    2428          85 :                             .help(_("The data source to open."));
    2429          85 :     if (!psOptionsForBinary)
    2430          37 :         argFilename.nargs(argparse::nargs_pattern::optional);
    2431             : 
    2432          85 :     argParser->add_argument("layer")
    2433          85 :         .remaining()
    2434         170 :         .metavar("<layer_name>")
    2435          85 :         .help(_("Layer name."));
    2436             : 
    2437          85 :     return argParser;
    2438             : }
    2439             : 
    2440             : /************************************************************************/
    2441             : /*                       GDALVectorInfoGetParserUsage()                 */
    2442             : /************************************************************************/
    2443             : 
    2444           1 : std::string GDALVectorInfoGetParserUsage()
    2445             : {
    2446             :     try
    2447             :     {
    2448           2 :         GDALVectorInfoOptions sOptions;
    2449           2 :         GDALVectorInfoOptionsForBinary sOptionsForBinary;
    2450             :         auto argParser =
    2451           2 :             GDALVectorInfoOptionsGetParser(&sOptions, &sOptionsForBinary);
    2452           1 :         return argParser->usage();
    2453             :     }
    2454           0 :     catch (const std::exception &err)
    2455             :     {
    2456           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
    2457           0 :                  err.what());
    2458           0 :         return std::string();
    2459             :     }
    2460             : }
    2461             : 
    2462             : /************************************************************************/
    2463             : /*                      GDALVectorInfoOptionsNew()                      */
    2464             : /************************************************************************/
    2465             : 
    2466             : /**
    2467             :  * Allocates a GDALVectorInfoOptions struct.
    2468             :  *
    2469             :  * Note that  when this function is used a library function, and not from the
    2470             :  * ogrinfo utility, a dataset name must be specified if any layer names(s) are
    2471             :  * specified (if no layer name is specific, passing a dataset name is not
    2472             :  * needed). That dataset name may be a dummy one, as the dataset taken into
    2473             :  * account is the hDS parameter passed to GDALVectorInfo().
    2474             :  * Similarly the -oo switch in a non-ogrinfo context will be ignored, and it
    2475             :  * is the responsibility of the user to apply them when opening the hDS parameter
    2476             :  * passed to GDALVectorInfo().
    2477             :  *
    2478             :  * @param papszArgv NULL terminated list of options (potentially including
    2479             :  * filename and open options too), or NULL. The accepted options are the ones of
    2480             :  * the <a href="/programs/ogrinfo.html">ogrinfo</a> utility.
    2481             :  * @param psOptionsForBinary (output) may be NULL (and should generally be
    2482             :  * NULL), otherwise (ogrinfo_bin.cpp use case) must be allocated with
    2483             :  * GDALVectorInfoOptionsForBinaryNew() prior to this
    2484             :  * function. Will be filled with potentially present filename, open options,
    2485             :  * subdataset number...
    2486             :  * @return pointer to the allocated GDALVectorInfoOptions struct. Must be freed
    2487             :  * with GDALVectorInfoOptionsFree().
    2488             :  *
    2489             :  * @since GDAL 3.7
    2490             :  */
    2491             : 
    2492             : GDALVectorInfoOptions *
    2493          84 : GDALVectorInfoOptionsNew(char **papszArgv,
    2494             :                          GDALVectorInfoOptionsForBinary *psOptionsForBinary)
    2495             : {
    2496         165 :     auto psOptions = std::make_unique<GDALVectorInfoOptions>();
    2497             : 
    2498             :     try
    2499             :     {
    2500             :         auto argParser =
    2501         165 :             GDALVectorInfoOptionsGetParser(psOptions.get(), psOptionsForBinary);
    2502             : 
    2503             :         /* Special pre-processing to rewrite -fields=foo as "-fields" "FOO", and
    2504             :      * same for -geom=foo. */
    2505         165 :         CPLStringList aosArgv;
    2506         326 :         for (CSLConstList papszIter = papszArgv; papszIter && *papszIter;
    2507             :              ++papszIter)
    2508             :         {
    2509         242 :             if (STARTS_WITH(*papszIter, "-fields="))
    2510             :             {
    2511           2 :                 aosArgv.AddString("-fields");
    2512             :                 aosArgv.AddString(
    2513           2 :                     CPLString(*papszIter + strlen("-fields=")).toupper());
    2514             :             }
    2515         240 :             else if (STARTS_WITH(*papszIter, "-geom="))
    2516             :             {
    2517           3 :                 aosArgv.AddString("-geom");
    2518             :                 aosArgv.AddString(
    2519           3 :                     CPLString(*papszIter + strlen("-geom=")).toupper());
    2520             :             }
    2521             :             else
    2522             :             {
    2523         237 :                 aosArgv.AddString(*papszIter);
    2524             :             }
    2525             :         }
    2526             : 
    2527          84 :         argParser->parse_args_without_binary_name(aosArgv.List());
    2528             : 
    2529         160 :         auto layers = argParser->present<std::vector<std::string>>("layer");
    2530          80 :         if (layers)
    2531             :         {
    2532          21 :             for (const auto &layer : *layers)
    2533             :             {
    2534          11 :                 psOptions->aosLayers.AddString(layer.c_str());
    2535          11 :                 psOptions->bAllLayers = false;
    2536             :             }
    2537             :         }
    2538             : 
    2539          82 :         if (auto oSpat = argParser->present<std::vector<double>>("-spat"))
    2540             :         {
    2541           4 :             OGRLinearRing oRing;
    2542           2 :             const double dfMinX = (*oSpat)[0];
    2543           2 :             const double dfMinY = (*oSpat)[1];
    2544           2 :             const double dfMaxX = (*oSpat)[2];
    2545           2 :             const double dfMaxY = (*oSpat)[3];
    2546             : 
    2547           2 :             oRing.addPoint(dfMinX, dfMinY);
    2548           2 :             oRing.addPoint(dfMinX, dfMaxY);
    2549           2 :             oRing.addPoint(dfMaxX, dfMaxY);
    2550           2 :             oRing.addPoint(dfMaxX, dfMinY);
    2551           2 :             oRing.addPoint(dfMinX, dfMinY);
    2552             : 
    2553           4 :             auto poPolygon = std::make_unique<OGRPolygon>();
    2554           2 :             poPolygon->addRing(&oRing);
    2555           2 :             psOptions->poSpatialFilter.reset(poPolygon.release());
    2556             :         }
    2557             : 
    2558          80 :         if (!psOptions->osWHERE.empty() && psOptions->osWHERE[0] == '@')
    2559             :         {
    2560           0 :             GByte *pabyRet = nullptr;
    2561           0 :             if (VSIIngestFile(nullptr, psOptions->osWHERE.substr(1).c_str(),
    2562           0 :                               &pabyRet, nullptr, 1024 * 1024))
    2563             :             {
    2564           0 :                 GDALRemoveBOM(pabyRet);
    2565           0 :                 psOptions->osWHERE = reinterpret_cast<const char *>(pabyRet);
    2566           0 :                 VSIFree(pabyRet);
    2567             :             }
    2568             :             else
    2569             :             {
    2570           0 :                 CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
    2571           0 :                          psOptions->osWHERE.substr(1).c_str());
    2572           0 :                 return nullptr;
    2573             :             }
    2574             :         }
    2575             : 
    2576          83 :         if (!psOptions->osSQLStatement.empty() &&
    2577           3 :             psOptions->osSQLStatement[0] == '@')
    2578             :         {
    2579           1 :             GByte *pabyRet = nullptr;
    2580           1 :             if (VSIIngestFile(nullptr,
    2581           2 :                               psOptions->osSQLStatement.substr(1).c_str(),
    2582           1 :                               &pabyRet, nullptr, 1024 * 1024))
    2583             :             {
    2584           1 :                 GDALRemoveBOM(pabyRet);
    2585           1 :                 char *pszSQLStatement = reinterpret_cast<char *>(pabyRet);
    2586           1 :                 psOptions->osSQLStatement =
    2587           2 :                     GDALRemoveSQLComments(pszSQLStatement);
    2588           1 :                 VSIFree(pabyRet);
    2589             :             }
    2590             :             else
    2591             :             {
    2592           0 :                 CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
    2593           0 :                          psOptions->osSQLStatement.substr(1).c_str());
    2594           0 :                 return nullptr;
    2595             :             }
    2596             :         }
    2597             : 
    2598          80 :         if (psOptionsForBinary)
    2599             :         {
    2600          43 :             psOptions->bStdoutOutput = true;
    2601          43 :             psOptionsForBinary->osSQLStatement = psOptions->osSQLStatement;
    2602             :         }
    2603             : 
    2604          80 :         if (psOptions->bSummaryParser)
    2605          13 :             psOptions->bSummaryOnly = true;
    2606          67 :         else if (psOptions->bFeaturesParser)
    2607           8 :             psOptions->bSummaryOnly = false;
    2608             : 
    2609          80 :         if (!psOptions->osDialect.empty() && !psOptions->osWHERE.empty() &&
    2610           0 :             psOptions->osSQLStatement.empty())
    2611             :         {
    2612           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2613             :                      "-dialect is ignored with -where. Use -sql instead");
    2614             :         }
    2615             : 
    2616          80 :         if (psOptions->eFormat == FORMAT_JSON)
    2617             :         {
    2618          27 :             if (psOptions->aosExtraMDDomains.empty())
    2619          27 :                 psOptions->aosExtraMDDomains.AddString("all");
    2620          27 :             psOptions->bStdoutOutput = false;
    2621             :         }
    2622             : 
    2623          80 :         return psOptions.release();
    2624             :     }
    2625           1 :     catch (const std::exception &err)
    2626             :     {
    2627           1 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what());
    2628           1 :         return nullptr;
    2629             :     }
    2630             : }

Generated by: LCOV version 1.14