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

Generated by: LCOV version 1.14