LCOV - code coverage report
Current view: top level - apps - ogrinfo_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1115 1294 86.2 %
Date: 2025-01-18 12:42:00 Functions: 30 32 93.8 %

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

Generated by: LCOV version 1.14