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

Generated by: LCOV version 1.14