LCOV - code coverage report
Current view: top level - apps - ogrinfo_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1184 1365 86.7 %
Date: 2026-04-23 19:47:11 Functions: 34 36 94.4 %

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

Generated by: LCOV version 1.14