LCOV - code coverage report
Current view: top level - apps - ogrinfo_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1185 1366 86.7 %
Date: 2026-07-08 05:24:13 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         125 : void GDALVectorInfoOptionsFree(GDALVectorInfoOptions *psOptions)
      95             : {
      96         125 :     delete psOptions;
      97         125 : }
      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         766 : static void ConcatStr(CPLString &osRet, bool bStdoutOutput, const char *pszStr)
     138             : {
     139         766 :     if (bStdoutOutput)
     140         616 :         fwrite(pszStr, 1, strlen(pszStr), stdout);
     141             :     else
     142         150 :         osRet += pszStr;
     143         766 : }
     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         293 : static void GDALVectorInfoReportMetadata(CPLString &osRet, CPLJSONObject &oRoot,
     708             :                                          const GDALVectorInfoOptions *psOptions,
     709             :                                          GDALMajorObject *poMajorObject,
     710             :                                          bool bListMDD, bool bShowMetadata,
     711             :                                          CSLConstList papszExtraMDDomains)
     712             : {
     713         293 :     const char *pszIndent = "";
     714         293 :     auto hObject = GDALMajorObject::ToHandle(poMajorObject);
     715             : 
     716         293 :     const bool bJson = psOptions->eFormat == FORMAT_JSON;
     717             :     /* -------------------------------------------------------------------- */
     718             :     /*      Report list of Metadata domains                                 */
     719             :     /* -------------------------------------------------------------------- */
     720         293 :     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         293 :     if (!bShowMetadata)
     754          25 :         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, "") &&
     778          24 :                     !EQUAL(pszDomain, GDAL_MDD_SUBDATASETS))
     779             :                 {
     780          24 :                     aosExtraMDDomainsExpanded.AddString(pszDomain);
     781             :                 }
     782          98 :             }
     783             :         }
     784             :         else
     785             :         {
     786           0 :             aosExtraMDDomainsExpanded = CSLDuplicate(papszExtraMDDomains);
     787             :         }
     788             : 
     789         122 :         for (const char *pszDomain : aosExtraMDDomainsExpanded)
     790             :         {
     791             :             const std::string osDisplayedName =
     792          72 :                 std::string("Metadata (").append(pszDomain).append(")");
     793          24 :             GDALVectorInfoPrintMetadata(osRet, oMetadata, psOptions, hObject,
     794             :                                         pszDomain, osDisplayedName.c_str(),
     795             :                                         pszIndent);
     796             :         }
     797             :     }
     798         268 :     GDALVectorInfoPrintMetadata(osRet, oMetadata, psOptions, hObject,
     799             :                                 GDAL_MDD_SUBDATASETS, "Subdatasets", pszIndent);
     800             : }
     801             : 
     802             : /************************************************************************/
     803             : /*                           ReportOnLayer()                            */
     804             : /************************************************************************/
     805             : 
     806         190 : static void ReportOnLayer(CPLString &osRet, CPLJSONObject &oLayer,
     807             :                           const GDALVectorInfoOptions *psOptions,
     808             :                           OGRLayer *poLayer, bool bForceSummary,
     809             :                           bool bTakeIntoAccountWHERE,
     810             :                           bool bTakeIntoAccountSpatialFilter,
     811             :                           bool bTakeIntoAccountGeomField)
     812             : {
     813         190 :     const bool bJson = psOptions->eFormat == FORMAT_JSON;
     814         190 :     const bool bIsSummaryCli =
     815         190 :         psOptions->bIsCli && psOptions->bSummaryUserRequested;
     816         190 :     const bool bExportOgrSchema = psOptions->bExportOgrSchema;
     817         190 :     OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
     818             : 
     819         190 :     oLayer.Set("name", poLayer->GetName());
     820         190 :     if (bExportOgrSchema)
     821             :     {
     822          23 :         oLayer.Set("schemaType", "Full");
     823             :     }
     824             :     const int nGeomFieldCount =
     825         190 :         psOptions->bGeomType ? poLayer->GetLayerDefn()->GetGeomFieldCount() : 0;
     826             : 
     827             :     /* -------------------------------------------------------------------- */
     828             :     /*      Set filters if provided.                                        */
     829             :     /* -------------------------------------------------------------------- */
     830         190 :     if (bTakeIntoAccountWHERE && !psOptions->osWHERE.empty())
     831             :     {
     832           4 :         if (poLayer->SetAttributeFilter(psOptions->osWHERE.c_str()) !=
     833             :             OGRERR_NONE)
     834             :         {
     835           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     836             :                      "SetAttributeFilter(%s) failed.",
     837           0 :                      psOptions->osWHERE.c_str());
     838           0 :             return;
     839             :         }
     840             :     }
     841             : 
     842         190 :     if (bTakeIntoAccountSpatialFilter && psOptions->poSpatialFilter != nullptr)
     843             :     {
     844           2 :         if (bTakeIntoAccountGeomField && !psOptions->osGeomField.empty())
     845             :         {
     846             :             const int iGeomField =
     847           1 :                 poDefn->GetGeomFieldIndex(psOptions->osGeomField.c_str());
     848           1 :             if (iGeomField >= 0)
     849           1 :                 poLayer->SetSpatialFilter(iGeomField,
     850           1 :                                           psOptions->poSpatialFilter.get());
     851             :             else
     852           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     853             :                          "Cannot find geometry field %s.",
     854           0 :                          psOptions->osGeomField.c_str());
     855             :         }
     856             :         else
     857             :         {
     858           1 :             poLayer->SetSpatialFilter(psOptions->poSpatialFilter.get());
     859             :         }
     860             :     }
     861             : 
     862             :     /* -------------------------------------------------------------------- */
     863             :     /*      Report various overall information.                             */
     864             :     /* -------------------------------------------------------------------- */
     865         190 :     if (!bJson && !psOptions->bSuperQuiet)
     866             :     {
     867         113 :         Concat(osRet, psOptions->bStdoutOutput, "\n");
     868         113 :         Concat(osRet, psOptions->bStdoutOutput, "Layer name: %s\n",
     869         113 :                poLayer->GetName());
     870             :     }
     871             : 
     872         190 :     GDALVectorInfoReportMetadata(osRet, oLayer, psOptions, poLayer,
     873         190 :                                  !bIsSummaryCli && psOptions->bListMDD,
     874         190 :                                  !bIsSummaryCli && psOptions->bShowMetadata,
     875         190 :                                  psOptions->aosExtraMDDomains.List());
     876             : 
     877         190 :     if (psOptions->bVerbose)
     878             :     {
     879             : 
     880         378 :         CPLString osWKTFormat("FORMAT=");
     881         189 :         osWKTFormat += psOptions->osWKTFormat;
     882         189 :         const char *const apszWKTOptions[] = {osWKTFormat.c_str(),
     883         189 :                                               "MULTILINE=YES", nullptr};
     884             : 
     885         189 :         if (bJson || nGeomFieldCount > 1)
     886             :         {
     887         158 :             CPLJSONArray oGeometryFields;
     888          79 :             if (bJson)
     889          77 :                 oLayer.Add("geometryFields", oGeometryFields);
     890         146 :             for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
     891             :             {
     892             :                 const OGRGeomFieldDefn *poGFldDefn =
     893          67 :                     poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
     894          67 :                 if (bJson)
     895             :                 {
     896         126 :                     CPLJSONObject oGeometryField;
     897          63 :                     oGeometryFields.Add(oGeometryField);
     898          63 :                     oGeometryField.Set("name", poGFldDefn->GetNameRef());
     899          63 :                     oGeometryField.Set(
     900          63 :                         "type", OGRToOGCGeomType(poGFldDefn->GetType(),
     901             :                                                  /*bCamelCase=*/true,
     902             :                                                  /*bAddZm=*/true,
     903             :                                                  /*bSpaceBeforeZM=*/false));
     904          63 :                     oGeometryField.Set("nullable",
     905          63 :                                        CPL_TO_BOOL(poGFldDefn->IsNullable()));
     906          63 :                     if (psOptions->bExtent3D)
     907             :                     {
     908           3 :                         OGREnvelope3D oExt;
     909           3 :                         if (poLayer->GetExtent3D(iGeom, &oExt, TRUE) ==
     910             :                             OGRERR_NONE)
     911             :                         {
     912             :                             {
     913           3 :                                 CPLJSONArray oBbox;
     914           3 :                                 oBbox.Add(oExt.MinX);
     915           3 :                                 oBbox.Add(oExt.MinY);
     916           3 :                                 oBbox.Add(oExt.MaxX);
     917           3 :                                 oBbox.Add(oExt.MaxY);
     918           3 :                                 oGeometryField.Add("extent", oBbox);
     919             :                             }
     920             :                             {
     921           3 :                                 CPLJSONArray oBbox;
     922           3 :                                 oBbox.Add(oExt.MinX);
     923           3 :                                 oBbox.Add(oExt.MinY);
     924           3 :                                 if (std::isfinite(oExt.MinZ))
     925           2 :                                     oBbox.Add(oExt.MinZ);
     926             :                                 else
     927           1 :                                     oBbox.AddNull();
     928           3 :                                 oBbox.Add(oExt.MaxX);
     929           3 :                                 oBbox.Add(oExt.MaxY);
     930           3 :                                 if (std::isfinite(oExt.MaxZ))
     931           2 :                                     oBbox.Add(oExt.MaxZ);
     932             :                                 else
     933           1 :                                     oBbox.AddNull();
     934           3 :                                 oGeometryField.Add("extent3D", oBbox);
     935             :                             }
     936             :                         }
     937             :                     }
     938          60 :                     else if (psOptions->bExtent)
     939             :                     {
     940          38 :                         OGREnvelope oExt;
     941          38 :                         if (poLayer->GetExtent(iGeom, &oExt, TRUE) ==
     942             :                             OGRERR_NONE)
     943             :                         {
     944          28 :                             CPLJSONArray oBbox;
     945          28 :                             oBbox.Add(oExt.MinX);
     946          28 :                             oBbox.Add(oExt.MinY);
     947          28 :                             oBbox.Add(oExt.MaxX);
     948          28 :                             oBbox.Add(oExt.MaxY);
     949          28 :                             oGeometryField.Add("extent", oBbox);
     950             :                         }
     951             :                     }
     952             :                     const OGRSpatialReference *poSRS =
     953          63 :                         poGFldDefn->GetSpatialRef();
     954          63 :                     if (poSRS)
     955             :                     {
     956         102 :                         CPLJSONObject oCRS;
     957          51 :                         oGeometryField.Add("coordinateSystem", oCRS);
     958             : 
     959             :                         // When exporting the schema give priority
     960             :                         // to the compact <authority:code> form
     961          51 :                         bool authIdSet{false};
     962          51 :                         if (psOptions->bExportOgrSchema)
     963             :                         {
     964          19 :                             const char *pszAuthCode = poSRS->GetAuthorityCode();
     965          19 :                             const char *pszAuthName = poSRS->GetAuthorityName();
     966          19 :                             if (pszAuthName && pszAuthCode)
     967             :                             {
     968          19 :                                 std::string oSRS{pszAuthName};
     969          19 :                                 oSRS += ':';
     970          19 :                                 oSRS += pszAuthCode;
     971          19 :                                 oCRS.Set("authid", oSRS);
     972          19 :                                 authIdSet = true;
     973             :                             }
     974             :                         }
     975             : 
     976          51 :                         if (!authIdSet)
     977             :                         {
     978          32 :                             char *pszWKT = nullptr;
     979          32 :                             poSRS->exportToWkt(&pszWKT, apszWKTOptions);
     980          32 :                             if (pszWKT)
     981             :                             {
     982          32 :                                 oCRS.Set("wkt", pszWKT);
     983          32 :                                 CPLFree(pszWKT);
     984             :                             }
     985             : 
     986             :                             {
     987          32 :                                 char *pszProjJson = nullptr;
     988             :                                 // PROJJSON requires PROJ >= 6.2
     989             :                                 CPLErrorStateBackuper oCPLErrorHandlerPusher(
     990          64 :                                     CPLQuietErrorHandler);
     991          32 :                                 CPL_IGNORE_RET_VAL(poSRS->exportToPROJJSON(
     992             :                                     &pszProjJson, nullptr));
     993          32 :                                 if (pszProjJson)
     994             :                                 {
     995          64 :                                     CPLJSONDocument oDoc;
     996          32 :                                     if (oDoc.LoadMemory(pszProjJson))
     997             :                                     {
     998          32 :                                         oCRS.Add("projjson", oDoc.GetRoot());
     999             :                                     }
    1000          32 :                                     CPLFree(pszProjJson);
    1001             :                                 }
    1002             :                             }
    1003             : 
    1004             :                             const auto &anAxes =
    1005          32 :                                 poSRS->GetDataAxisToSRSAxisMapping();
    1006          64 :                             CPLJSONArray oAxisMapping;
    1007          97 :                             for (const auto nAxis : anAxes)
    1008             :                             {
    1009          65 :                                 oAxisMapping.Add(nAxis);
    1010             :                             }
    1011          32 :                             oCRS.Add("dataAxisToSRSAxisMapping", oAxisMapping);
    1012             : 
    1013             :                             const double dfCoordinateEpoch =
    1014          32 :                                 poSRS->GetCoordinateEpoch();
    1015          32 :                             if (dfCoordinateEpoch > 0)
    1016           2 :                                 oCRS.Set("coordinateEpoch", dfCoordinateEpoch);
    1017             :                         }
    1018             :                     }
    1019             :                     else
    1020             :                     {
    1021          12 :                         oGeometryField.SetNull("coordinateSystem");
    1022             :                     }
    1023             : 
    1024          63 :                     const auto &srsList = poLayer->GetSupportedSRSList(iGeom);
    1025          63 :                     if (!srsList.empty())
    1026             :                     {
    1027           1 :                         CPLJSONArray oSupportedSRSList;
    1028           3 :                         for (const auto &poSupportedSRS : srsList)
    1029             :                         {
    1030             :                             const char *pszAuthName =
    1031           2 :                                 poSupportedSRS->GetAuthorityName();
    1032             :                             const char *pszAuthCode =
    1033           2 :                                 poSupportedSRS->GetAuthorityCode();
    1034           4 :                             CPLJSONObject oSupportedSRS;
    1035           2 :                             if (pszAuthName && pszAuthCode)
    1036             :                             {
    1037           4 :                                 CPLJSONObject id;
    1038           2 :                                 id.Set("authority", pszAuthName);
    1039           2 :                                 id.Set("code", pszAuthCode);
    1040           2 :                                 oSupportedSRS.Add("id", id);
    1041           4 :                                 oSupportedSRSList.Add(oSupportedSRS);
    1042             :                             }
    1043             :                             else
    1044             :                             {
    1045           0 :                                 char *pszWKT = nullptr;
    1046           0 :                                 poSupportedSRS->exportToWkt(&pszWKT,
    1047             :                                                             apszWKTOptions);
    1048           0 :                                 if (pszWKT)
    1049             :                                 {
    1050           0 :                                     oSupportedSRS.Add("wkt", pszWKT);
    1051           0 :                                     oSupportedSRSList.Add(oSupportedSRS);
    1052             :                                 }
    1053           0 :                                 CPLFree(pszWKT);
    1054             :                             }
    1055             :                         }
    1056           1 :                         oGeometryField.Add("supportedSRSList",
    1057             :                                            oSupportedSRSList);
    1058             :                     }
    1059             : 
    1060             :                     const auto &oCoordPrec =
    1061          63 :                         poGFldDefn->GetCoordinatePrecision();
    1062          63 :                     if (oCoordPrec.dfXYResolution !=
    1063             :                         OGRGeomCoordinatePrecision::UNKNOWN)
    1064             :                     {
    1065           3 :                         oGeometryField.Add("xyCoordinateResolution",
    1066           3 :                                            oCoordPrec.dfXYResolution);
    1067             :                     }
    1068          63 :                     if (oCoordPrec.dfZResolution !=
    1069             :                         OGRGeomCoordinatePrecision::UNKNOWN)
    1070             :                     {
    1071           3 :                         oGeometryField.Add("zCoordinateResolution",
    1072           3 :                                            oCoordPrec.dfZResolution);
    1073             :                     }
    1074          63 :                     if (oCoordPrec.dfMResolution !=
    1075             :                         OGRGeomCoordinatePrecision::UNKNOWN)
    1076             :                     {
    1077           2 :                         oGeometryField.Add("mCoordinateResolution",
    1078           2 :                                            oCoordPrec.dfMResolution);
    1079             :                     }
    1080             : 
    1081             :                     // For example set by OpenFileGDB driver
    1082          63 :                     if (!oCoordPrec.oFormatSpecificOptions.empty())
    1083             :                     {
    1084           2 :                         CPLJSONObject oFormatSpecificOptions;
    1085           2 :                         for (const auto &formatOptionsPair :
    1086           6 :                              oCoordPrec.oFormatSpecificOptions)
    1087             :                         {
    1088           4 :                             CPLJSONObject oThisFormatSpecificOptions;
    1089          44 :                             for (const auto &[pszKey, pszValue] :
    1090             :                                  cpl::IterateNameValue(
    1091          46 :                                      formatOptionsPair.second))
    1092             :                             {
    1093             :                                 const auto eValueType =
    1094          22 :                                     CPLGetValueType(pszValue);
    1095          22 :                                 if (eValueType == CPL_VALUE_INTEGER)
    1096             :                                 {
    1097          14 :                                     oThisFormatSpecificOptions.Add(
    1098             :                                         pszKey, CPLAtoGIntBig(pszValue));
    1099             :                                 }
    1100           8 :                                 else if (eValueType == CPL_VALUE_REAL)
    1101             :                                 {
    1102           6 :                                     oThisFormatSpecificOptions.Add(
    1103             :                                         pszKey, CPLAtof(pszValue));
    1104             :                                 }
    1105             :                                 else
    1106             :                                 {
    1107           2 :                                     oThisFormatSpecificOptions.Add(pszKey,
    1108             :                                                                    pszValue);
    1109             :                                 }
    1110             :                             }
    1111           2 :                             oFormatSpecificOptions.Add(
    1112           2 :                                 formatOptionsPair.first,
    1113             :                                 oThisFormatSpecificOptions);
    1114             :                         }
    1115           2 :                         oGeometryField.Add(
    1116             :                             "coordinatePrecisionFormatSpecificOptions",
    1117             :                             oFormatSpecificOptions);
    1118             :                     }
    1119             :                 }
    1120             :                 else
    1121             :                 {
    1122           4 :                     Concat(osRet, psOptions->bStdoutOutput,
    1123             :                            "Geometry (%s): %s\n", poGFldDefn->GetNameRef(),
    1124             :                            OGRGeometryTypeToName(poGFldDefn->GetType()));
    1125             :                 }
    1126          79 :             }
    1127             :         }
    1128         110 :         else if (psOptions->bGeomType)
    1129             :         {
    1130         110 :             Concat(osRet, psOptions->bStdoutOutput, "Geometry: %s\n",
    1131         110 :                    OGRGeometryTypeToName(poLayer->GetGeomType()));
    1132             :         }
    1133             : 
    1134         189 :         if (psOptions->bFeatureCount)
    1135             :         {
    1136         164 :             if (bJson)
    1137          53 :                 oLayer.Set("featureCount", poLayer->GetFeatureCount());
    1138             :             else
    1139             :             {
    1140         111 :                 Concat(osRet, psOptions->bStdoutOutput,
    1141             :                        "Feature Count: " CPL_FRMT_GIB "\n",
    1142         111 :                        poLayer->GetFeatureCount());
    1143             :             }
    1144             :         }
    1145             : 
    1146         189 :         if (!bJson && psOptions->bExtent && nGeomFieldCount > 1)
    1147             :         {
    1148           6 :             for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
    1149             :             {
    1150           4 :                 if (psOptions->bExtent3D)
    1151             :                 {
    1152           0 :                     OGREnvelope3D oExt;
    1153           0 :                     if (poLayer->GetExtent3D(iGeom, &oExt, TRUE) == OGRERR_NONE)
    1154             :                     {
    1155             :                         OGRGeomFieldDefn *poGFldDefn =
    1156           0 :                             poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
    1157           0 :                         Concat(osRet, psOptions->bStdoutOutput,
    1158             :                                "Extent (%s): (%f, %f, %s) - (%f, %f, %s)\n",
    1159             :                                poGFldDefn->GetNameRef(), oExt.MinX, oExt.MinY,
    1160           0 :                                std::isfinite(oExt.MinZ)
    1161           0 :                                    ? CPLSPrintf("%f", oExt.MinZ)
    1162             :                                    : "none",
    1163             :                                oExt.MaxX, oExt.MaxY,
    1164           0 :                                std::isfinite(oExt.MaxZ)
    1165           0 :                                    ? CPLSPrintf("%f", oExt.MaxZ)
    1166             :                                    : "none");
    1167             :                     }
    1168             :                 }
    1169             :                 else
    1170             :                 {
    1171           4 :                     OGREnvelope oExt;
    1172           4 :                     if (poLayer->GetExtent(iGeom, &oExt, TRUE) == OGRERR_NONE)
    1173             :                     {
    1174             :                         OGRGeomFieldDefn *poGFldDefn =
    1175           4 :                             poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
    1176           4 :                         Concat(osRet, psOptions->bStdoutOutput,
    1177             :                                "Extent (%s): (%f, %f) - (%f, %f)\n",
    1178             :                                poGFldDefn->GetNameRef(), oExt.MinX, oExt.MinY,
    1179             :                                oExt.MaxX, oExt.MaxY);
    1180             :                     }
    1181             :                 }
    1182           2 :             }
    1183             :         }
    1184         187 :         else if (!bJson && psOptions->bExtent)
    1185             :         {
    1186         110 :             if (psOptions->bExtent3D)
    1187             :             {
    1188           2 :                 OGREnvelope3D oExt;
    1189           2 :                 if (poLayer->GetExtent3D(0, &oExt, TRUE) == OGRERR_NONE)
    1190             :                 {
    1191           4 :                     Concat(
    1192           2 :                         osRet, psOptions->bStdoutOutput,
    1193             :                         "Extent: (%f, %f, %s) - (%f, %f, %s)\n", oExt.MinX,
    1194             :                         oExt.MinY,
    1195           3 :                         std::isfinite(oExt.MinZ) ? CPLSPrintf("%f", oExt.MinZ)
    1196             :                                                  : "none",
    1197             :                         oExt.MaxX, oExt.MaxY,
    1198           3 :                         std::isfinite(oExt.MaxZ) ? CPLSPrintf("%f", oExt.MaxZ)
    1199             :                                                  : "none");
    1200             :                 }
    1201             :             }
    1202             :             else
    1203             :             {
    1204         108 :                 OGREnvelope oExt;
    1205         108 :                 if (poLayer->GetExtent(&oExt, TRUE) == OGRERR_NONE)
    1206             :                 {
    1207          30 :                     Concat(osRet, psOptions->bStdoutOutput,
    1208             :                            "Extent: (%f, %f) - (%f, %f)\n", oExt.MinX,
    1209             :                            oExt.MinY, oExt.MaxX, oExt.MaxY);
    1210             :                 }
    1211             :             }
    1212             :         }
    1213             : 
    1214             :         const auto DisplayExtraInfoSRS =
    1215         244 :             [&osRet, &psOptions](const OGRSpatialReference *poSRS)
    1216             :         {
    1217          40 :             const double dfCoordinateEpoch = poSRS->GetCoordinateEpoch();
    1218          40 :             if (dfCoordinateEpoch > 0)
    1219             :             {
    1220             :                 std::string osCoordinateEpoch =
    1221           4 :                     CPLSPrintf("%f", dfCoordinateEpoch);
    1222           2 :                 const size_t nDotPos = osCoordinateEpoch.find('.');
    1223           2 :                 if (nDotPos != std::string::npos)
    1224             :                 {
    1225          22 :                     while (osCoordinateEpoch.size() > nDotPos + 2 &&
    1226          10 :                            osCoordinateEpoch.back() == '0')
    1227          10 :                         osCoordinateEpoch.pop_back();
    1228             :                 }
    1229           2 :                 Concat(osRet, psOptions->bStdoutOutput,
    1230             :                        "Coordinate epoch: %s\n", osCoordinateEpoch.c_str());
    1231             :             }
    1232             : 
    1233          40 :             const auto &mapping = poSRS->GetDataAxisToSRSAxisMapping();
    1234          40 :             Concat(osRet, psOptions->bStdoutOutput,
    1235             :                    "Data axis to CRS axis mapping: ");
    1236         121 :             for (size_t i = 0; i < mapping.size(); i++)
    1237             :             {
    1238          81 :                 if (i > 0)
    1239             :                 {
    1240          41 :                     Concat(osRet, psOptions->bStdoutOutput, ",");
    1241             :                 }
    1242          81 :                 Concat(osRet, psOptions->bStdoutOutput, "%d", mapping[i]);
    1243             :             }
    1244          40 :             Concat(osRet, psOptions->bStdoutOutput, "\n");
    1245          40 :         };
    1246             : 
    1247             :         const auto DisplaySRS =
    1248         114 :             [&osRet, &psOptions, &apszWKTOptions,
    1249             :              DisplayExtraInfoSRS](const OGRSpatialReference *poSRS,
    1250         394 :                                   const OGRGeomFieldDefn *poGFldDefn)
    1251             :         {
    1252         228 :             std::string osWkt;
    1253         114 :             if (poSRS)
    1254          40 :                 osWkt = poSRS->exportToWkt(apszWKTOptions);
    1255             : 
    1256         114 :             if (psOptions->bIsCli && !poSRS)
    1257             :             {
    1258           0 :                 if (poGFldDefn)
    1259           0 :                     Concat(osRet, psOptions->bStdoutOutput,
    1260             :                            "Coordinate Reference System of field %s: none\n",
    1261             :                            poGFldDefn->GetNameRef());
    1262             :                 else
    1263           0 :                     Concat(osRet, psOptions->bStdoutOutput,
    1264             :                            "Layer Coordinate Reference System: none\n");
    1265             :             }
    1266         114 :             else if (psOptions->bIsCli)
    1267             :             {
    1268          12 :                 std::string osIntroText;
    1269          12 :                 if (poGFldDefn)
    1270             :                 {
    1271             :                     osIntroText =
    1272           0 :                         std::string("Coordinate Reference System of field ")
    1273           0 :                             .append(poGFldDefn->GetNameRef());
    1274             :                 }
    1275             :                 else
    1276             :                 {
    1277          12 :                     osIntroText = "Layer Coordinate Reference System";
    1278             :                 }
    1279             : 
    1280          12 :                 EmitTextDisplayOfCRS(poSRS, psOptions->osCRSFormat, osIntroText,
    1281         138 :                                      [&osRet, psOptions](const std::string &s)
    1282             :                                      {
    1283          69 :                                          Concat(osRet, psOptions->bStdoutOutput,
    1284             :                                                 "%s", s.c_str());
    1285          69 :                                      });
    1286             :             }
    1287             :             else
    1288             :             {
    1289         102 :                 if (osWkt.empty())
    1290          74 :                     osWkt = "(unknown)";
    1291             : 
    1292         102 :                 if (poGFldDefn)
    1293             :                 {
    1294           4 :                     Concat(osRet, psOptions->bStdoutOutput,
    1295             :                            "SRS WKT (%s):\n%s\n", poGFldDefn->GetNameRef(),
    1296             :                            osWkt.c_str());
    1297             :                 }
    1298             :                 else
    1299             :                 {
    1300          98 :                     Concat(osRet, psOptions->bStdoutOutput,
    1301             :                            "Layer SRS WKT:\n%s\n", osWkt.c_str());
    1302             :                 }
    1303             :             }
    1304             : 
    1305         114 :             if (poSRS)
    1306          40 :                 DisplayExtraInfoSRS(poSRS);
    1307         114 :         };
    1308             : 
    1309         114 :         const auto DisplaySupportedCRSList = [&](int iGeomField)
    1310             :         {
    1311         114 :             const auto &srsList = poLayer->GetSupportedSRSList(iGeomField);
    1312         114 :             if (!srsList.empty())
    1313             :             {
    1314           1 :                 Concat(osRet, psOptions->bStdoutOutput, "Supported SRS: ");
    1315           1 :                 bool bFirst = true;
    1316           3 :                 for (const auto &poSupportedSRS : srsList)
    1317             :                 {
    1318             :                     const char *pszAuthName =
    1319           2 :                         poSupportedSRS->GetAuthorityName();
    1320             :                     const char *pszAuthCode =
    1321           2 :                         poSupportedSRS->GetAuthorityCode();
    1322           2 :                     if (!bFirst)
    1323           1 :                         Concat(osRet, psOptions->bStdoutOutput, ", ");
    1324           2 :                     bFirst = false;
    1325           2 :                     if (pszAuthName && pszAuthCode)
    1326             :                     {
    1327           2 :                         Concat(osRet, psOptions->bStdoutOutput, "%s:%s",
    1328             :                                pszAuthName, pszAuthCode);
    1329             :                     }
    1330             :                     else
    1331             :                     {
    1332           0 :                         ConcatStr(osRet, psOptions->bStdoutOutput,
    1333             :                                   poSupportedSRS->GetName());
    1334             :                     }
    1335             :                 }
    1336           1 :                 Concat(osRet, psOptions->bStdoutOutput, "\n");
    1337             :             }
    1338         114 :         };
    1339             : 
    1340         189 :         if (!bJson && nGeomFieldCount > 1)
    1341             :         {
    1342             : 
    1343           6 :             for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
    1344             :             {
    1345             :                 const OGRGeomFieldDefn *poGFldDefn =
    1346           4 :                     poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
    1347           4 :                 const OGRSpatialReference *poSRS = poGFldDefn->GetSpatialRef();
    1348           4 :                 DisplaySRS(poSRS, poGFldDefn);
    1349           4 :                 DisplaySupportedCRSList(iGeom);
    1350           2 :             }
    1351             :         }
    1352         187 :         else if (!bJson)
    1353             :         {
    1354         110 :             const auto poSRS = poLayer->GetSpatialRef();
    1355         110 :             DisplaySRS(poSRS, nullptr);
    1356         110 :             DisplaySupportedCRSList(0);
    1357             :         }
    1358             : 
    1359         189 :         const char *pszFIDColumn = poLayer->GetFIDColumn();
    1360         189 :         if (pszFIDColumn[0] != '\0')
    1361             :         {
    1362          48 :             if (bJson)
    1363          33 :                 oLayer.Set("fidColumnName", pszFIDColumn);
    1364             :             else
    1365             :             {
    1366          15 :                 Concat(osRet, psOptions->bStdoutOutput, "FID Column = %s\n",
    1367             :                        pszFIDColumn);
    1368             :             }
    1369             :         }
    1370             : 
    1371         197 :         for (int iGeom = 0; !bJson && iGeom < nGeomFieldCount; iGeom++)
    1372             :         {
    1373             :             OGRGeomFieldDefn *poGFldDefn =
    1374          40 :                 poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
    1375          72 :             if (nGeomFieldCount == 1 && EQUAL(poGFldDefn->GetNameRef(), "") &&
    1376          32 :                 poGFldDefn->IsNullable())
    1377          32 :                 break;
    1378           8 :             Concat(osRet, psOptions->bStdoutOutput, "Geometry Column ");
    1379           8 :             if (nGeomFieldCount > 1)
    1380           4 :                 Concat(osRet, psOptions->bStdoutOutput, "%d ", iGeom + 1);
    1381           8 :             if (!poGFldDefn->IsNullable())
    1382           0 :                 Concat(osRet, psOptions->bStdoutOutput, "NOT NULL ");
    1383           8 :             Concat(osRet, psOptions->bStdoutOutput, "= %s\n",
    1384             :                    poGFldDefn->GetNameRef());
    1385             :         }
    1386             : 
    1387         378 :         CPLJSONArray oFields;
    1388         189 :         if (bJson)
    1389          77 :             oLayer.Add("fields", oFields);
    1390         803 :         for (int iAttr = 0; iAttr < poDefn->GetFieldCount(); iAttr++)
    1391             :         {
    1392         614 :             const OGRFieldDefn *poField = poDefn->GetFieldDefn(iAttr);
    1393         614 :             const char *pszAlias = poField->GetAlternativeNameRef();
    1394         614 :             const std::string &osDomain = poField->GetDomainName();
    1395         614 :             const std::string &osComment = poField->GetComment();
    1396         614 :             const auto eType = poField->GetType();
    1397        1228 :             std::string osTimeZone;
    1398         614 :             if (eType == OFTTime || eType == OFTDate || eType == OFTDateTime)
    1399             :             {
    1400          26 :                 const int nTZFlag = poField->GetTZFlag();
    1401          26 :                 if (nTZFlag == OGR_TZFLAG_LOCALTIME)
    1402             :                 {
    1403           1 :                     osTimeZone = "localtime";
    1404             :                 }
    1405          25 :                 else if (nTZFlag == OGR_TZFLAG_MIXED_TZ)
    1406             :                 {
    1407           1 :                     osTimeZone = "mixed timezones";
    1408             :                 }
    1409          24 :                 else if (nTZFlag == OGR_TZFLAG_UTC)
    1410             :                 {
    1411           2 :                     osTimeZone = "UTC";
    1412             :                 }
    1413          22 :                 else if (nTZFlag > 0)
    1414             :                 {
    1415             :                     char chSign;
    1416           3 :                     const int nOffset = (nTZFlag - OGR_TZFLAG_UTC) * 15;
    1417           3 :                     int nHours =
    1418             :                         static_cast<int>(nOffset / 60);  // Round towards zero.
    1419           3 :                     const int nMinutes = std::abs(nOffset - nHours * 60);
    1420             : 
    1421           3 :                     if (nOffset < 0)
    1422             :                     {
    1423           1 :                         chSign = '-';
    1424           1 :                         nHours = std::abs(nHours);
    1425             :                     }
    1426             :                     else
    1427             :                     {
    1428           2 :                         chSign = '+';
    1429             :                     }
    1430             :                     osTimeZone =
    1431           3 :                         CPLSPrintf("%c%02d:%02d", chSign, nHours, nMinutes);
    1432             :                 }
    1433             :             }
    1434             : 
    1435         614 :             if (bJson)
    1436             :             {
    1437         252 :                 CPLJSONObject oField;
    1438         126 :                 oFields.Add(oField);
    1439         126 :                 oField.Set("name", poField->GetNameRef());
    1440         126 :                 oField.Set("type", OGRFieldDefn::GetFieldTypeName(eType));
    1441         126 :                 if (poField->GetSubType() != OFSTNone)
    1442           2 :                     oField.Set("subType", OGRFieldDefn::GetFieldSubTypeName(
    1443             :                                               poField->GetSubType()));
    1444         126 :                 if (poField->GetWidth() > 0)
    1445          71 :                     oField.Set("width", poField->GetWidth());
    1446         126 :                 if (poField->GetPrecision() > 0)
    1447          12 :                     oField.Set("precision", poField->GetPrecision());
    1448         126 :                 oField.Set("nullable", CPL_TO_BOOL(poField->IsNullable()));
    1449         126 :                 oField.Set("uniqueConstraint",
    1450         126 :                            CPL_TO_BOOL(poField->IsUnique()));
    1451         126 :                 if (poField->GetDefault() != nullptr)
    1452           2 :                     oField.Set("defaultValue", poField->GetDefault());
    1453         126 :                 if (pszAlias != nullptr && pszAlias[0])
    1454           1 :                     oField.Set("alias", pszAlias);
    1455         126 :                 if (!osDomain.empty())
    1456           6 :                     oField.Set("domainName", osDomain);
    1457         126 :                 if (!osComment.empty())
    1458           1 :                     oField.Set("comment", osComment);
    1459         126 :                 if (!osTimeZone.empty())
    1460           7 :                     oField.Set("timezone", osTimeZone);
    1461             :             }
    1462             :             else
    1463             :             {
    1464             :                 const char *pszType =
    1465         488 :                     (poField->GetSubType() != OFSTNone)
    1466         488 :                         ? CPLSPrintf("%s(%s)",
    1467             :                                      OGRFieldDefn::GetFieldTypeName(
    1468             :                                          poField->GetType()),
    1469             :                                      OGRFieldDefn::GetFieldSubTypeName(
    1470             :                                          poField->GetSubType()))
    1471         466 :                         : OGRFieldDefn::GetFieldTypeName(poField->GetType());
    1472         488 :                 Concat(osRet, psOptions->bStdoutOutput, "%s: %s",
    1473             :                        poField->GetNameRef(), pszType);
    1474         488 :                 if (eType == OFTTime || eType == OFTDate ||
    1475             :                     eType == OFTDateTime)
    1476             :                 {
    1477          18 :                     if (!osTimeZone.empty())
    1478           0 :                         Concat(osRet, psOptions->bStdoutOutput, " (%s)",
    1479             :                                osTimeZone.c_str());
    1480             :                 }
    1481             :                 else
    1482             :                 {
    1483         470 :                     Concat(osRet, psOptions->bStdoutOutput, " (%d.%d)",
    1484             :                            poField->GetWidth(), poField->GetPrecision());
    1485             :                 }
    1486         488 :                 if (poField->IsUnique())
    1487           0 :                     Concat(osRet, psOptions->bStdoutOutput, " UNIQUE");
    1488         488 :                 if (!poField->IsNullable())
    1489         204 :                     Concat(osRet, psOptions->bStdoutOutput, " NOT NULL");
    1490         488 :                 if (poField->GetDefault() != nullptr)
    1491           8 :                     Concat(osRet, psOptions->bStdoutOutput, " DEFAULT %s",
    1492             :                            poField->GetDefault());
    1493         488 :                 if (pszAlias != nullptr && pszAlias[0])
    1494           0 :                     Concat(osRet, psOptions->bStdoutOutput,
    1495             :                            ", alternative name=\"%s\"", pszAlias);
    1496         488 :                 if (!osDomain.empty())
    1497           5 :                     Concat(osRet, psOptions->bStdoutOutput, ", domain name=%s",
    1498             :                            osDomain.c_str());
    1499         488 :                 if (!osComment.empty())
    1500           0 :                     Concat(osRet, psOptions->bStdoutOutput, ", comment=%s",
    1501             :                            osComment.c_str());
    1502         488 :                 Concat(osRet, psOptions->bStdoutOutput, "\n");
    1503             :             }
    1504             :         }
    1505             :     }
    1506             : 
    1507             :     /* -------------------------------------------------------------------- */
    1508             :     /*      Read, and dump features.                                        */
    1509             :     /* -------------------------------------------------------------------- */
    1510             : 
    1511         190 :     if ((psOptions->nFetchFID == OGRNullFID || bJson) && !bForceSummary &&
    1512         189 :         ((psOptions->bIsCli && psOptions->bFeaturesUserRequested) ||
    1513         184 :          (!psOptions->bIsCli && !psOptions->bSummaryOnly)))
    1514             :     {
    1515          89 :         if (!psOptions->bSuperQuiet)
    1516             :         {
    1517         178 :             CPLJSONArray oFeatures;
    1518             :             const bool bDisplayFields =
    1519          89 :                 CPLTestBool(psOptions->aosOptions.FetchNameValueDef(
    1520             :                     "DISPLAY_FIELDS", "YES"));
    1521             :             const int nFields =
    1522          89 :                 bDisplayFields ? poLayer->GetLayerDefn()->GetFieldCount() : 0;
    1523             :             const bool bDisplayGeometry =
    1524          89 :                 CPLTestBool(psOptions->aosOptions.FetchNameValueDef(
    1525             :                     "DISPLAY_GEOMETRY", "YES"));
    1526             :             const int nGeomFields =
    1527          89 :                 bDisplayGeometry ? poLayer->GetLayerDefn()->GetGeomFieldCount()
    1528          89 :                                  : 0;
    1529          89 :             if (bJson)
    1530          12 :                 oLayer.Add("features", oFeatures);
    1531             : 
    1532             :             const auto EmitFeatureJSON =
    1533          45 :                 [poLayer, nFields, nGeomFields,
    1534         290 :                  &oFeatures](const OGRFeature *poFeature)
    1535             :             {
    1536          90 :                 CPLJSONObject oFeature;
    1537          90 :                 CPLJSONObject oProperties;
    1538          45 :                 oFeatures.Add(oFeature);
    1539          45 :                 oFeature.Add("type", "Feature");
    1540          45 :                 oFeature.Add("properties", oProperties);
    1541          45 :                 oFeature.Add("fid", poFeature->GetFID());
    1542         157 :                 for (int i = 0; i < nFields; ++i)
    1543             :                 {
    1544         112 :                     const auto poFDefn = poFeature->GetFieldDefnRef(i);
    1545         112 :                     const auto eType = poFDefn->GetType();
    1546         112 :                     if (!poFeature->IsFieldSet(i))
    1547           0 :                         continue;
    1548         112 :                     if (poFeature->IsFieldNull(i))
    1549             :                     {
    1550           2 :                         oProperties.SetNull(poFDefn->GetNameRef());
    1551             :                     }
    1552         110 :                     else if (eType == OFTInteger)
    1553             :                     {
    1554           1 :                         if (poFDefn->GetSubType() == OFSTBoolean)
    1555           0 :                             oProperties.Add(
    1556             :                                 poFDefn->GetNameRef(),
    1557           0 :                                 CPL_TO_BOOL(poFeature->GetFieldAsInteger(i)));
    1558             :                         else
    1559           1 :                             oProperties.Add(poFDefn->GetNameRef(),
    1560             :                                             poFeature->GetFieldAsInteger(i));
    1561             :                     }
    1562         109 :                     else if (eType == OFTInteger64)
    1563             :                     {
    1564          34 :                         oProperties.Add(poFDefn->GetNameRef(),
    1565             :                                         poFeature->GetFieldAsInteger64(i));
    1566             :                     }
    1567          75 :                     else if (eType == OFTReal)
    1568             :                     {
    1569          34 :                         oProperties.Add(poFDefn->GetNameRef(),
    1570             :                                         poFeature->GetFieldAsDouble(i));
    1571             :                     }
    1572          41 :                     else if ((eType == OFTString &&
    1573          46 :                               poFDefn->GetSubType() != OFSTJSON) ||
    1574          82 :                              eType == OFTDate || eType == OFTTime ||
    1575             :                              eType == OFTDateTime)
    1576             :                     {
    1577          36 :                         oProperties.Add(poFDefn->GetNameRef(),
    1578             :                                         poFeature->GetFieldAsString(i));
    1579             :                     }
    1580             :                     else
    1581             :                     {
    1582             :                         char *pszSerialized =
    1583           5 :                             poFeature->GetFieldAsSerializedJSon(i);
    1584           5 :                         if (pszSerialized)
    1585             :                         {
    1586             :                             const auto eStrType =
    1587           5 :                                 CPLGetValueType(pszSerialized);
    1588           5 :                             if (eStrType == CPL_VALUE_INTEGER)
    1589             :                             {
    1590           1 :                                 oProperties.Add(poFDefn->GetNameRef(),
    1591             :                                                 CPLAtoGIntBig(pszSerialized));
    1592             :                             }
    1593           4 :                             else if (eStrType == CPL_VALUE_REAL)
    1594             :                             {
    1595           0 :                                 oProperties.Add(poFDefn->GetNameRef(),
    1596             :                                                 CPLAtof(pszSerialized));
    1597             :                             }
    1598             :                             else
    1599             :                             {
    1600           8 :                                 CPLJSONDocument oDoc;
    1601           4 :                                 if (oDoc.LoadMemory(pszSerialized))
    1602           4 :                                     oProperties.Add(poFDefn->GetNameRef(),
    1603           8 :                                                     oDoc.GetRoot());
    1604             :                             }
    1605           5 :                             CPLFree(pszSerialized);
    1606             :                         }
    1607             :                     }
    1608             :                 }
    1609             : 
    1610          86 :                 const auto GetGeoJSONOptions = [poLayer](int iGeomField)
    1611             :                 {
    1612          43 :                     CPLStringList aosGeoJSONOptions;
    1613          43 :                     const auto &oCoordPrec = poLayer->GetLayerDefn()
    1614          43 :                                                  ->GetGeomFieldDefn(iGeomField)
    1615          43 :                                                  ->GetCoordinatePrecision();
    1616          43 :                     if (oCoordPrec.dfXYResolution !=
    1617             :                         OGRGeomCoordinatePrecision::UNKNOWN)
    1618             :                     {
    1619             :                         aosGeoJSONOptions.SetNameValue(
    1620             :                             "XY_COORD_PRECISION",
    1621             :                             CPLSPrintf("%d",
    1622             :                                        OGRGeomCoordinatePrecision::
    1623             :                                            ResolutionToPrecision(
    1624           1 :                                                oCoordPrec.dfXYResolution)));
    1625             :                     }
    1626          43 :                     if (oCoordPrec.dfZResolution !=
    1627             :                         OGRGeomCoordinatePrecision::UNKNOWN)
    1628             :                     {
    1629             :                         aosGeoJSONOptions.SetNameValue(
    1630             :                             "Z_COORD_PRECISION",
    1631             :                             CPLSPrintf("%d",
    1632             :                                        OGRGeomCoordinatePrecision::
    1633             :                                            ResolutionToPrecision(
    1634           1 :                                                oCoordPrec.dfZResolution)));
    1635             :                     }
    1636          43 :                     return aosGeoJSONOptions;
    1637          45 :                 };
    1638             : 
    1639          45 :                 if (nGeomFields == 0)
    1640           2 :                     oFeature.SetNull("geometry");
    1641             :                 else
    1642             :                 {
    1643          43 :                     if (const auto poGeom = poFeature->GetGeometryRef())
    1644             :                     {
    1645             :                         char *pszSerialized =
    1646          43 :                             wkbFlatten(poGeom->getGeometryType()) <=
    1647             :                                     wkbGeometryCollection
    1648          86 :                                 ? poGeom->exportToJson(
    1649          86 :                                       GetGeoJSONOptions(0).List())
    1650          43 :                                 : nullptr;
    1651          43 :                         if (pszSerialized)
    1652             :                         {
    1653          86 :                             CPLJSONDocument oDoc;
    1654          43 :                             if (oDoc.LoadMemory(pszSerialized))
    1655          43 :                                 oFeature.Add("geometry", oDoc.GetRoot());
    1656          43 :                             CPLFree(pszSerialized);
    1657             :                         }
    1658             :                         else
    1659             :                         {
    1660           0 :                             CPLJSONObject oGeometry;
    1661           0 :                             oFeature.SetNull("geometry");
    1662           0 :                             oFeature.Add("wkt_geometry", poGeom->exportToWkt());
    1663             :                         }
    1664             :                     }
    1665             :                     else
    1666           0 :                         oFeature.SetNull("geometry");
    1667             : 
    1668          43 :                     if (nGeomFields > 1)
    1669             :                     {
    1670           0 :                         CPLJSONArray oGeometries;
    1671           0 :                         oFeature.Add("geometries", oGeometries);
    1672           0 :                         for (int i = 0; i < nGeomFields; ++i)
    1673             :                         {
    1674           0 :                             auto poGeom = poFeature->GetGeomFieldRef(i);
    1675           0 :                             if (poGeom)
    1676             :                             {
    1677             :                                 char *pszSerialized =
    1678           0 :                                     wkbFlatten(poGeom->getGeometryType()) <=
    1679             :                                             wkbGeometryCollection
    1680           0 :                                         ? poGeom->exportToJson(
    1681           0 :                                               GetGeoJSONOptions(i).List())
    1682           0 :                                         : nullptr;
    1683           0 :                                 if (pszSerialized)
    1684             :                                 {
    1685           0 :                                     CPLJSONDocument oDoc;
    1686           0 :                                     if (oDoc.LoadMemory(pszSerialized))
    1687           0 :                                         oGeometries.Add(oDoc.GetRoot());
    1688           0 :                                     CPLFree(pszSerialized);
    1689             :                                 }
    1690             :                                 else
    1691             :                                 {
    1692           0 :                                     CPLJSONObject oGeometry;
    1693           0 :                                     oGeometries.Add(poGeom->exportToWkt());
    1694             :                                 }
    1695             :                             }
    1696             :                             else
    1697           0 :                                 oGeometries.AddNull();
    1698             :                         }
    1699             :                     }
    1700             :                 }
    1701          45 :             };
    1702             : 
    1703          89 :             if (psOptions->nFetchFID != OGRNullFID)
    1704             :             {
    1705             :                 auto poFeature = std::unique_ptr<OGRFeature>(
    1706           2 :                     poLayer->GetFeature(psOptions->nFetchFID));
    1707           1 :                 if (poFeature)
    1708             :                 {
    1709           1 :                     EmitFeatureJSON(poFeature.get());
    1710             :                 }
    1711             :             }
    1712          88 :             else if (psOptions->nLimit < 0 || psOptions->nLimit > 0)
    1713             :             {
    1714          88 :                 GIntBig nFeatureCount = 0;
    1715         728 :                 for (auto &poFeature : poLayer)
    1716             :                 {
    1717         640 :                     if (bJson)
    1718             :                     {
    1719          44 :                         EmitFeatureJSON(poFeature.get());
    1720             :                     }
    1721             :                     else
    1722             :                     {
    1723         596 :                         ConcatStr(osRet, psOptions->bStdoutOutput,
    1724             :                                   poFeature
    1725        1788 :                                       ->DumpReadableAsString(
    1726         596 :                                           psOptions->aosOptions.List())
    1727             :                                       .c_str());
    1728             :                     }
    1729             : 
    1730         640 :                     ++nFeatureCount;
    1731         640 :                     if (psOptions->nLimit >= 0 &&
    1732           3 :                         nFeatureCount >= psOptions->nLimit)
    1733             :                     {
    1734           2 :                         break;
    1735             :                     }
    1736             :                 }
    1737             :             }
    1738          89 :         }
    1739             :     }
    1740         101 :     else if (!bJson && psOptions->nFetchFID != OGRNullFID)
    1741             :     {
    1742             :         auto poFeature = std::unique_ptr<OGRFeature>(
    1743           2 :             poLayer->GetFeature(psOptions->nFetchFID));
    1744           1 :         if (poFeature == nullptr)
    1745             :         {
    1746           0 :             Concat(osRet, psOptions->bStdoutOutput,
    1747             :                    "Unable to locate feature id " CPL_FRMT_GIB
    1748             :                    " on this layer.\n",
    1749           0 :                    psOptions->nFetchFID);
    1750             :         }
    1751             :         else
    1752             :         {
    1753           1 :             ConcatStr(
    1754           1 :                 osRet, psOptions->bStdoutOutput,
    1755           2 :                 poFeature->DumpReadableAsString(psOptions->aosOptions.List())
    1756             :                     .c_str());
    1757             :         }
    1758             :     }
    1759             : }
    1760             : 
    1761             : /************************************************************************/
    1762             : /*                         PrintLayerSummary()                          */
    1763             : /************************************************************************/
    1764             : 
    1765          23 : static void PrintLayerSummary(CPLString &osRet, CPLJSONObject &oLayer,
    1766             :                               const GDALVectorInfoOptions *psOptions,
    1767             :                               OGRLayer *poLayer, bool bIsPrivate)
    1768             : {
    1769          23 :     const bool bJson = psOptions->eFormat == FORMAT_JSON;
    1770          23 :     const bool bIsSummaryCli = psOptions->bIsCli && psOptions->bSummaryOnly;
    1771          23 :     if (bJson)
    1772             :     {
    1773           2 :         oLayer.Set("name", poLayer->GetName());
    1774             :     }
    1775             :     else
    1776          21 :         ConcatStr(osRet, psOptions->bStdoutOutput, poLayer->GetName());
    1777             : 
    1778          23 :     const char *pszTitle = poLayer->GetMetadataItem("TITLE");
    1779          23 :     if (pszTitle)
    1780             :     {
    1781           0 :         if (bJson)
    1782           0 :             oLayer.Set("title", pszTitle);
    1783             :         else
    1784           0 :             Concat(osRet, psOptions->bStdoutOutput, " (title: %s)", pszTitle);
    1785             :     }
    1786             : 
    1787             :     const int nGeomFieldCount =
    1788          23 :         psOptions->bGeomType ? poLayer->GetLayerDefn()->GetGeomFieldCount() : 0;
    1789             : 
    1790          23 :     if (bIsSummaryCli && bJson)
    1791             :     {
    1792           2 :         CPLJSONArray oGeometryTypes;
    1793           7 :         for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
    1794             :         {
    1795             :             OGRGeomFieldDefn *poGFldDefn =
    1796           5 :                 poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
    1797           5 :             oGeometryTypes.Add(OGRGeometryTypeToName(poGFldDefn->GetType()));
    1798             :         }
    1799           2 :         oLayer.Add("geometryType", oGeometryTypes);
    1800           2 :         return;
    1801             :     }
    1802             : 
    1803          21 :     if (bJson || nGeomFieldCount > 1)
    1804             :     {
    1805           2 :         if (!bJson)
    1806           2 :             Concat(osRet, psOptions->bStdoutOutput, " (");
    1807           4 :         CPLJSONArray oGeometryFields;
    1808           2 :         oLayer.Add("geometryFields", oGeometryFields);
    1809           8 :         for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
    1810             :         {
    1811             :             OGRGeomFieldDefn *poGFldDefn =
    1812           6 :                 poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
    1813           6 :             if (bJson)
    1814             :             {
    1815           0 :                 oGeometryFields.Add(
    1816             :                     OGRGeometryTypeToName(poGFldDefn->GetType()));
    1817             :             }
    1818             :             else
    1819             :             {
    1820           6 :                 if (iGeom > 0)
    1821           4 :                     Concat(osRet, psOptions->bStdoutOutput, ", ");
    1822           6 :                 ConcatStr(osRet, psOptions->bStdoutOutput,
    1823             :                           OGRGeometryTypeToName(poGFldDefn->GetType()));
    1824             :             }
    1825             :         }
    1826           2 :         if (!bJson)
    1827           4 :             Concat(osRet, psOptions->bStdoutOutput, ")");
    1828             :     }
    1829          19 :     else if (psOptions->bGeomType && poLayer->GetGeomType() != wkbUnknown)
    1830          11 :         Concat(osRet, psOptions->bStdoutOutput, " (%s)",
    1831          11 :                OGRGeometryTypeToName(poLayer->GetGeomType()));
    1832             : 
    1833          21 :     if (bIsPrivate)
    1834             :     {
    1835           0 :         if (bJson)
    1836           0 :             oLayer.Set("isPrivate", true);
    1837             :         else
    1838           0 :             Concat(osRet, psOptions->bStdoutOutput, " [private]");
    1839             :     }
    1840             : 
    1841          21 :     if (!bJson)
    1842          21 :         Concat(osRet, psOptions->bStdoutOutput, "\n");
    1843             : }
    1844             : 
    1845             : /************************************************************************/
    1846             : /*                      ReportHiearchicalLayers()                       */
    1847             : /************************************************************************/
    1848             : 
    1849           5 : static void ReportHiearchicalLayers(CPLString &osRet, CPLJSONObject &oRoot,
    1850             :                                     const GDALVectorInfoOptions *psOptions,
    1851             :                                     const GDALGroup *group,
    1852             :                                     const std::string &indent, bool bGeomType)
    1853             : {
    1854           5 :     const bool bJson = psOptions->eFormat == FORMAT_JSON;
    1855          10 :     const auto aosVectorLayerNames = group->GetVectorLayerNames();
    1856          10 :     CPLJSONArray oLayerNames;
    1857           5 :     oRoot.Add("layerNames", oLayerNames);
    1858          23 :     for (const auto &osVectorLayerName : aosVectorLayerNames)
    1859             :     {
    1860          18 :         OGRLayer *poLayer = group->OpenVectorLayer(osVectorLayerName);
    1861          18 :         if (poLayer)
    1862             :         {
    1863          36 :             CPLJSONObject oLayer;
    1864          18 :             if (!bJson)
    1865             :             {
    1866           4 :                 Concat(osRet, psOptions->bStdoutOutput,
    1867             :                        "%sLayer: ", indent.c_str());
    1868           4 :                 PrintLayerSummary(osRet, oLayer, psOptions, poLayer,
    1869             :                                   /* bIsPrivate=*/false);
    1870             :             }
    1871             :             else
    1872             :             {
    1873          14 :                 oLayerNames.Add(poLayer->GetName());
    1874             :             }
    1875             :         }
    1876             :     }
    1877             : 
    1878          10 :     const std::string subIndent(indent + "  ");
    1879          10 :     auto aosSubGroupNames = group->GetGroupNames();
    1880          10 :     CPLJSONArray oGroupArray;
    1881           5 :     oRoot.Add("groups", oGroupArray);
    1882           7 :     for (const auto &osSubGroupName : aosSubGroupNames)
    1883             :     {
    1884           4 :         auto poSubGroup = group->OpenGroup(osSubGroupName);
    1885           2 :         if (poSubGroup)
    1886             :         {
    1887           4 :             CPLJSONObject oGroup;
    1888           2 :             if (!bJson)
    1889             :             {
    1890           2 :                 Concat(osRet, psOptions->bStdoutOutput, "Group %s",
    1891             :                        indent.c_str());
    1892           2 :                 Concat(osRet, psOptions->bStdoutOutput, "%s:\n",
    1893             :                        osSubGroupName.c_str());
    1894             :             }
    1895             :             else
    1896             :             {
    1897           0 :                 oGroupArray.Add(oGroup);
    1898           0 :                 oGroup.Set("name", osSubGroupName);
    1899             :             }
    1900           2 :             ReportHiearchicalLayers(osRet, oGroup, psOptions, poSubGroup.get(),
    1901             :                                     subIndent, bGeomType);
    1902             :         }
    1903             :     }
    1904           5 : }
    1905             : 
    1906             : /************************************************************************/
    1907             : /*                           GDALVectorInfo()                           */
    1908             : /************************************************************************/
    1909             : 
    1910             : /**
    1911             :  * Lists various information about a GDAL supported vector dataset.
    1912             :  *
    1913             :  * This is the equivalent of the <a href="/programs/ogrinfo.html">ogrinfo</a>
    1914             :  * utility.
    1915             :  *
    1916             :  * GDALVectorInfoOptions* must be allocated and freed with
    1917             :  * GDALVectorInfoOptionsNew() and GDALVectorInfoOptionsFree() respectively.
    1918             :  *
    1919             :  * @param hDataset the dataset handle.
    1920             :  * @param psOptions the options structure returned by GDALVectorInfoOptionsNew()
    1921             :  * or NULL.
    1922             :  * @return string corresponding to the information about the raster dataset
    1923             :  * (must be freed with CPLFree()), or NULL in case of error.
    1924             :  *
    1925             :  * @since GDAL 3.7
    1926             :  */
    1927         121 : char *GDALVectorInfo(GDALDatasetH hDataset,
    1928             :                      const GDALVectorInfoOptions *psOptions)
    1929             : {
    1930         121 :     auto poDS = GDALDataset::FromHandle(hDataset);
    1931         121 :     if (poDS == nullptr)
    1932           0 :         return nullptr;
    1933             : 
    1934         242 :     const GDALVectorInfoOptions sDefaultOptions;
    1935         121 :     if (!psOptions)
    1936           0 :         psOptions = &sDefaultOptions;
    1937             : 
    1938         121 :     GDALDriver *poDriver = poDS->GetDriver();
    1939             : 
    1940         242 :     CPLString osRet;
    1941         242 :     CPLJSONObject oRoot;
    1942         242 :     const std::string osFilename(poDS->GetDescription());
    1943             : 
    1944         121 :     const bool bExportOgrSchema = psOptions->bExportOgrSchema;
    1945         121 :     const bool bJson = psOptions->eFormat == FORMAT_JSON;
    1946         121 :     const bool bIsSummaryCli =
    1947         121 :         (psOptions->bIsCli && psOptions->bSummaryUserRequested);
    1948             : 
    1949         242 :     CPLJSONArray oLayerArray;
    1950         121 :     if (bJson)
    1951             :     {
    1952          58 :         if (!bExportOgrSchema)
    1953             :         {
    1954          44 :             oRoot.Set("description", poDS->GetDescription());
    1955          44 :             if (poDriver)
    1956             :             {
    1957          44 :                 oRoot.Set("driverShortName", poDriver->GetDescription());
    1958          44 :                 oRoot.Set("driverLongName",
    1959          88 :                           poDriver->GetMetadataItem(GDAL_DMD_LONGNAME));
    1960             :             }
    1961             :         }
    1962          58 :         oRoot.Add("layers", oLayerArray);
    1963             :     }
    1964             : 
    1965             :     /* -------------------------------------------------------------------- */
    1966             :     /*      Some information messages.                                      */
    1967             :     /* -------------------------------------------------------------------- */
    1968         121 :     if (!bJson && psOptions->bVerbose)
    1969             :     {
    1970         122 :         Concat(osRet, psOptions->bStdoutOutput,
    1971             :                "INFO: Open of `%s'\n"
    1972             :                "      using driver `%s' successful.\n",
    1973             :                osFilename.c_str(),
    1974          61 :                poDriver ? poDriver->GetDescription() : "(null)");
    1975             :     }
    1976             : 
    1977         182 :     if (!bJson && psOptions->bVerbose &&
    1978          61 :         !EQUAL(osFilename.c_str(), poDS->GetDescription()))
    1979             :     {
    1980           0 :         Concat(osRet, psOptions->bStdoutOutput,
    1981             :                "INFO: Internal data source name `%s'\n"
    1982             :                "      different from user name `%s'.\n",
    1983           0 :                poDS->GetDescription(), osFilename.c_str());
    1984             :     }
    1985             : 
    1986         121 :     int nRepeatCount = psOptions->nRepeatCount;
    1987             : 
    1988         121 :     if (!bIsSummaryCli && !bExportOgrSchema)
    1989             :     {
    1990         103 :         GDALVectorInfoReportMetadata(
    1991         103 :             osRet, oRoot, psOptions, poDS, psOptions->bListMDD,
    1992         103 :             psOptions->bShowMetadata, psOptions->aosExtraMDDomains.List());
    1993             : 
    1994         103 :         CPLJSONObject oDomains;
    1995         103 :         oRoot.Add("domains", oDomains);
    1996         103 :         if (!psOptions->osFieldDomain.empty())
    1997             :         {
    1998           7 :             auto poDomain = poDS->GetFieldDomain(psOptions->osFieldDomain);
    1999           7 :             if (poDomain == nullptr)
    2000             :             {
    2001           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2002             :                          "Domain %s cannot be found.",
    2003             :                          psOptions->osFieldDomain.c_str());
    2004           0 :                 return nullptr;
    2005             :             }
    2006           7 :             if (!bJson)
    2007           7 :                 Concat(osRet, psOptions->bStdoutOutput, "\n");
    2008           7 :             ReportFieldDomain(osRet, oDomains, psOptions, poDomain);
    2009           7 :             if (!bJson)
    2010           7 :                 Concat(osRet, psOptions->bStdoutOutput, "\n");
    2011             :         }
    2012          96 :         else if (bJson)
    2013             :         {
    2014          49 :             for (const auto &osDomainName : poDS->GetFieldDomainNames())
    2015             :             {
    2016           7 :                 auto poDomain = poDS->GetFieldDomain(osDomainName);
    2017           7 :                 if (poDomain)
    2018             :                 {
    2019           7 :                     ReportFieldDomain(osRet, oDomains, psOptions, poDomain);
    2020             :                 }
    2021             :             }
    2022             :         }
    2023             : 
    2024         103 :         if (psOptions->bDatasetGetNextFeature)
    2025             :         {
    2026           1 :             nRepeatCount = 0;  // skip layer reporting.
    2027             : 
    2028             :             /* --------------------------------------------------------------------
    2029             :              */
    2030             :             /*      Set filters if provided. */
    2031             :             /* --------------------------------------------------------------------
    2032             :              */
    2033           2 :             if (!psOptions->osWHERE.empty() ||
    2034           1 :                 psOptions->poSpatialFilter != nullptr)
    2035             :             {
    2036           0 :                 for (int iLayer = 0; iLayer < poDS->GetLayerCount(); iLayer++)
    2037             :                 {
    2038           0 :                     OGRLayer *poLayer = poDS->GetLayer(iLayer);
    2039             : 
    2040           0 :                     if (poLayer == nullptr)
    2041             :                     {
    2042           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    2043             :                                  "Couldn't fetch advertised layer %d.", iLayer);
    2044           0 :                         return nullptr;
    2045             :                     }
    2046             : 
    2047           0 :                     if (!psOptions->osWHERE.empty())
    2048             :                     {
    2049           0 :                         if (poLayer->SetAttributeFilter(
    2050           0 :                                 psOptions->osWHERE.c_str()) != OGRERR_NONE)
    2051             :                         {
    2052           0 :                             CPLError(
    2053             :                                 CE_Warning, CPLE_AppDefined,
    2054             :                                 "SetAttributeFilter(%s) failed on layer %s.",
    2055           0 :                                 psOptions->osWHERE.c_str(), poLayer->GetName());
    2056             :                         }
    2057             :                     }
    2058             : 
    2059           0 :                     if (psOptions->poSpatialFilter != nullptr)
    2060             :                     {
    2061           0 :                         if (!psOptions->osGeomField.empty())
    2062             :                         {
    2063           0 :                             OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
    2064           0 :                             const int iGeomField = poDefn->GetGeomFieldIndex(
    2065           0 :                                 psOptions->osGeomField.c_str());
    2066           0 :                             if (iGeomField >= 0)
    2067           0 :                                 poLayer->SetSpatialFilter(
    2068             :                                     iGeomField,
    2069           0 :                                     psOptions->poSpatialFilter.get());
    2070             :                             else
    2071           0 :                                 CPLError(CE_Warning, CPLE_AppDefined,
    2072             :                                          "Cannot find geometry field %s.",
    2073             :                                          psOptions->osGeomField.c_str());
    2074             :                         }
    2075             :                         else
    2076             :                         {
    2077           0 :                             poLayer->SetSpatialFilter(
    2078           0 :                                 psOptions->poSpatialFilter.get());
    2079             :                         }
    2080             :                     }
    2081             :                 }
    2082             :             }
    2083             : 
    2084           1 :             std::set<OGRLayer *> oSetLayers;
    2085             :             while (true)
    2086             :             {
    2087          11 :                 OGRLayer *poLayer = nullptr;
    2088             :                 OGRFeature *poFeature =
    2089          11 :                     poDS->GetNextFeature(&poLayer, nullptr, nullptr, nullptr);
    2090          11 :                 if (poFeature == nullptr)
    2091           1 :                     break;
    2092          10 :                 if (psOptions->aosLayers.empty() || poLayer == nullptr ||
    2093           0 :                     CSLFindString(psOptions->aosLayers.List(),
    2094           0 :                                   poLayer->GetName()) >= 0)
    2095             :                 {
    2096          10 :                     if (psOptions->bVerbose && poLayer != nullptr &&
    2097          10 :                         oSetLayers.find(poLayer) == oSetLayers.end())
    2098             :                     {
    2099           0 :                         oSetLayers.insert(poLayer);
    2100           0 :                         CPLJSONObject oLayer;
    2101           0 :                         oLayerArray.Add(oLayer);
    2102           0 :                         ReportOnLayer(
    2103             :                             osRet, oLayer, psOptions, poLayer,
    2104             :                             /*bForceSummary = */ true,
    2105             :                             /*bTakeIntoAccountWHERE = */ false,
    2106             :                             /*bTakeIntoAccountSpatialFilter = */ false,
    2107             :                             /*bTakeIntoAccountGeomField = */ false);
    2108             :                     }
    2109          10 :                     if (!psOptions->bSuperQuiet && !psOptions->bSummaryOnly)
    2110          10 :                         poFeature->DumpReadable(
    2111             :                             nullptr,
    2112             :                             const_cast<char **>(psOptions->aosOptions.List()));
    2113             :                 }
    2114          10 :                 OGRFeature::DestroyFeature(poFeature);
    2115          10 :             }
    2116             :         }
    2117             : 
    2118             :         /* -------------------------------------------------------------------- */
    2119             :         /*      Special case for -sql clause.  No source layers required.       */
    2120             :         /* -------------------------------------------------------------------- */
    2121         102 :         else if (!psOptions->osSQLStatement.empty())
    2122             :         {
    2123           5 :             nRepeatCount = 0;  // skip layer reporting.
    2124             : 
    2125           5 :             if (!bJson && !psOptions->aosLayers.empty())
    2126           0 :                 Concat(osRet, psOptions->bStdoutOutput,
    2127             :                        "layer names ignored in combination with -sql.\n");
    2128             : 
    2129           5 :             CPLErrorReset();
    2130          15 :             OGRLayer *poResultSet = poDS->ExecuteSQL(
    2131             :                 psOptions->osSQLStatement.c_str(),
    2132           5 :                 psOptions->osGeomField.empty()
    2133           5 :                     ? psOptions->poSpatialFilter.get()
    2134             :                     : nullptr,
    2135           5 :                 psOptions->osDialect.empty() ? nullptr
    2136           6 :                                              : psOptions->osDialect.c_str());
    2137             : 
    2138           5 :             if (poResultSet != nullptr)
    2139             :             {
    2140           4 :                 if (!psOptions->osWHERE.empty())
    2141             :                 {
    2142           0 :                     if (poResultSet->SetAttributeFilter(
    2143           0 :                             psOptions->osWHERE.c_str()) != OGRERR_NONE)
    2144             :                     {
    2145           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    2146             :                                  "SetAttributeFilter(%s) failed.",
    2147             :                                  psOptions->osWHERE.c_str());
    2148           0 :                         return nullptr;
    2149             :                     }
    2150             :                 }
    2151             : 
    2152           8 :                 CPLJSONObject oLayer;
    2153           4 :                 oLayerArray.Add(oLayer);
    2154           4 :                 if (!psOptions->osGeomField.empty())
    2155           0 :                     ReportOnLayer(osRet, oLayer, psOptions, poResultSet,
    2156             :                                   /*bForceSummary = */ false,
    2157             :                                   /*bTakeIntoAccountWHERE = */ false,
    2158             :                                   /*bTakeIntoAccountSpatialFilter = */ true,
    2159             :                                   /*bTakeIntoAccountGeomField = */ true);
    2160             :                 else
    2161           4 :                     ReportOnLayer(osRet, oLayer, psOptions, poResultSet,
    2162             :                                   /*bForceSummary = */ false,
    2163             :                                   /*bTakeIntoAccountWHERE = */ false,
    2164             :                                   /*bTakeIntoAccountSpatialFilter = */ false,
    2165             :                                   /*bTakeIntoAccountGeomField = */ false);
    2166             : 
    2167           4 :                 poDS->ReleaseResultSet(poResultSet);
    2168             :             }
    2169           1 :             else if (CPLGetLastErrorType() != CE_None)
    2170             :             {
    2171             :                 // sqlite3 emits messages with "readonly" and GDAL with "read-only"
    2172           1 :                 if (psOptions->bIsCli &&
    2173           0 :                     (strstr(CPLGetLastErrorMsg(), "readonly") ||
    2174           0 :                      strstr(CPLGetLastErrorMsg(), "read-only")))
    2175             :                 {
    2176           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    2177             :                              "Perhaps you want to run \"gdal vector sql "
    2178             :                              "--update\" instead?");
    2179             :                 }
    2180           1 :                 return nullptr;
    2181             :             }
    2182             :         }
    2183             :     }
    2184             : 
    2185             :     // coverity[tainted_data]
    2186         120 :     auto papszLayers = psOptions->aosLayers.List();
    2187         234 :     for (int iRepeat = 0; iRepeat < nRepeatCount; iRepeat++)
    2188             :     {
    2189         115 :         if (papszLayers == nullptr || papszLayers[0] == nullptr)
    2190             :         {
    2191         104 :             const int nLayerCount = poDS->GetLayerCount();
    2192         104 :             if (iRepeat == 0)
    2193         104 :                 CPLDebug("OGR", "GetLayerCount() = %d\n", nLayerCount);
    2194             : 
    2195         104 :             bool bDone = false;
    2196         104 :             auto poRootGroup = poDS->GetRootGroup();
    2197         107 :             if ((bJson || !psOptions->bAllLayers) && poRootGroup &&
    2198         107 :                 (!poRootGroup->GetGroupNames().empty() ||
    2199         106 :                  !poRootGroup->GetVectorLayerNames().empty()))
    2200             :             {
    2201           6 :                 CPLJSONObject oGroup;
    2202           3 :                 oRoot.Add("rootGroup", oGroup);
    2203           3 :                 ReportHiearchicalLayers(osRet, oGroup, psOptions,
    2204           6 :                                         poRootGroup.get(), std::string(),
    2205           3 :                                         psOptions->bGeomType);
    2206           3 :                 if (!bJson)
    2207           1 :                     bDone = true;
    2208             :             }
    2209             : 
    2210             :             /* --------------------------------------------------------------------
    2211             :              */
    2212             :             /*      Process each data source layer. */
    2213             :             /* --------------------------------------------------------------------
    2214             :              */
    2215         298 :             for (int iLayer = 0; !bDone && iLayer < nLayerCount; iLayer++)
    2216             :             {
    2217         194 :                 OGRLayer *poLayer = poDS->GetLayer(iLayer);
    2218             : 
    2219         194 :                 if (poLayer == nullptr)
    2220             :                 {
    2221           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2222             :                              "Couldn't fetch advertised layer %d.", iLayer);
    2223           0 :                     return nullptr;
    2224             :                 }
    2225             : 
    2226         388 :                 CPLJSONObject oLayer;
    2227         194 :                 oLayerArray.Add(oLayer);
    2228         194 :                 if (!psOptions->bAllLayers || bIsSummaryCli)
    2229             :                 {
    2230          19 :                     if (!bJson)
    2231          17 :                         Concat(osRet, psOptions->bStdoutOutput,
    2232             :                                "%d: ", iLayer + 1);
    2233          19 :                     PrintLayerSummary(osRet, oLayer, psOptions, poLayer,
    2234          19 :                                       poDS->IsLayerPrivate(iLayer));
    2235             :                 }
    2236             :                 else
    2237             :                 {
    2238         175 :                     if (iRepeat != 0)
    2239           0 :                         poLayer->ResetReading();
    2240             : 
    2241         175 :                     ReportOnLayer(osRet, oLayer, psOptions, poLayer,
    2242             :                                   /*bForceSummary = */ false,
    2243             :                                   /*bTakeIntoAccountWHERE = */ true,
    2244             :                                   /*bTakeIntoAccountSpatialFilter = */ true,
    2245             :                                   /*bTakeIntoAccountGeomField = */ true);
    2246             :                 }
    2247         104 :             }
    2248             :         }
    2249             :         else
    2250             :         {
    2251             :             /* --------------------------------------------------------------------
    2252             :              */
    2253             :             /*      Process specified data source layers. */
    2254             :             /* --------------------------------------------------------------------
    2255             :              */
    2256             : 
    2257          22 :             for (const char *pszLayer : cpl::Iterate(papszLayers))
    2258             :             {
    2259          12 :                 OGRLayer *poLayer = poDS->GetLayerByName(pszLayer);
    2260             : 
    2261          12 :                 if (poLayer == nullptr)
    2262             :                 {
    2263           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2264             :                              "Couldn't fetch requested layer %s.", pszLayer);
    2265           1 :                     return nullptr;
    2266             :                 }
    2267             : 
    2268          11 :                 if (iRepeat != 0)
    2269           0 :                     poLayer->ResetReading();
    2270             : 
    2271          22 :                 CPLJSONObject oLayer;
    2272          11 :                 oLayerArray.Add(oLayer);
    2273          11 :                 ReportOnLayer(osRet, oLayer, psOptions, poLayer,
    2274             :                               /*bForceSummary = */ false,
    2275             :                               /*bTakeIntoAccountWHERE = */ true,
    2276             :                               /*bTakeIntoAccountSpatialFilter = */ true,
    2277             :                               /*bTakeIntoAccountGeomField = */ true);
    2278             :             }
    2279             :         }
    2280             :     }
    2281             : 
    2282         119 :     if (!papszLayers && !bIsSummaryCli && !bExportOgrSchema)
    2283             :     {
    2284          91 :         ReportRelationships(osRet, oRoot, psOptions, poDS);
    2285             :     }
    2286             : 
    2287         119 :     if (bJson)
    2288             :     {
    2289          57 :         osRet.clear();
    2290          57 :         ConcatStr(
    2291          57 :             osRet, psOptions->bStdoutOutput,
    2292             :             json_object_to_json_string_ext(
    2293          57 :                 static_cast<struct json_object *>(oRoot.GetInternalHandle()),
    2294             :                 JSON_C_TO_STRING_PRETTY
    2295             : #ifdef JSON_C_TO_STRING_NOSLASHESCAPE
    2296             :                     | JSON_C_TO_STRING_NOSLASHESCAPE
    2297             : #endif
    2298             :                 ));
    2299          57 :         ConcatStr(osRet, psOptions->bStdoutOutput, "\n");
    2300             :     }
    2301             : 
    2302         119 :     return VSI_STRDUP_VERBOSE(osRet);
    2303             : }
    2304             : 
    2305             : /************************************************************************/
    2306             : /*                   GDALVectorInfoOptionsGetParser()                   */
    2307             : /************************************************************************/
    2308             : 
    2309         127 : static std::unique_ptr<GDALArgumentParser> GDALVectorInfoOptionsGetParser(
    2310             :     GDALVectorInfoOptions *psOptions,
    2311             :     GDALVectorInfoOptionsForBinary *psOptionsForBinary)
    2312             : {
    2313             :     auto argParser = std::make_unique<GDALArgumentParser>(
    2314         127 :         "ogrinfo", /* bForBinary=*/psOptionsForBinary != nullptr);
    2315             : 
    2316         127 :     argParser->add_description(
    2317         127 :         _("Lists information about an OGR-supported data source."));
    2318             : 
    2319         127 :     argParser->add_epilog(
    2320         127 :         _("For more details, consult https://gdal.org/programs/ogrinfo.html"));
    2321             : 
    2322         127 :     argParser->add_argument("-json")
    2323         127 :         .flag()
    2324          44 :         .action([psOptions](const std::string &)
    2325         127 :                 { psOptions->eFormat = FORMAT_JSON; })
    2326         127 :         .help(_("Display the output in json format."));
    2327             : 
    2328             :     // Hidden argument to select OGR_SCHEMA output
    2329         127 :     argParser->add_argument("-schema")
    2330         127 :         .flag()
    2331         127 :         .hidden()
    2332          14 :         .action([psOptions](const std::string &)
    2333         127 :                 { psOptions->bExportOgrSchema = true; })
    2334         127 :         .help(_("Export the OGR_SCHEMA in json format."));
    2335             : 
    2336         127 :     argParser->add_argument("-ro")
    2337         127 :         .flag()
    2338             :         .action(
    2339          14 :             [psOptionsForBinary](const std::string &)
    2340             :             {
    2341           7 :                 if (psOptionsForBinary)
    2342           7 :                     psOptionsForBinary->bReadOnly = true;
    2343         127 :             })
    2344         127 :         .help(_("Open the data source in read-only mode."));
    2345             : 
    2346         127 :     argParser->add_argument("-update")
    2347         127 :         .flag()
    2348             :         .action(
    2349           0 :             [psOptionsForBinary](const std::string &)
    2350             :             {
    2351           0 :                 if (psOptionsForBinary)
    2352           0 :                     psOptionsForBinary->bUpdate = true;
    2353         127 :             })
    2354         127 :         .help(_("Open the data source in update mode."));
    2355             : 
    2356         127 :     argParser->add_argument("-q", "--quiet")
    2357         127 :         .flag()
    2358             :         .action(
    2359           4 :             [psOptions, psOptionsForBinary](const std::string &)
    2360             :             {
    2361           2 :                 psOptions->bVerbose = false;
    2362           2 :                 if (psOptionsForBinary)
    2363           2 :                     psOptionsForBinary->bVerbose = false;
    2364         127 :             })
    2365             :         .help(_("Quiet mode. No progress message is emitted on the standard "
    2366         127 :                 "output."));
    2367             : 
    2368             : #ifdef __AFL_HAVE_MANUAL_CONTROL
    2369             :     /* Undocumented: mainly only useful for AFL testing */
    2370             :     argParser->add_argument("-qq")
    2371             :         .flag()
    2372             :         .hidden()
    2373             :         .action(
    2374             :             [psOptions, psOptionsForBinary](const std::string &)
    2375             :             {
    2376             :                 psOptions->bVerbose = false;
    2377             :                 if (psOptionsForBinary)
    2378             :                     psOptionsForBinary->bVerbose = false;
    2379             :                 psOptions->bSuperQuiet = true;
    2380             :             })
    2381             :         .help(_("Super quiet mode."));
    2382             : #endif
    2383             : 
    2384         127 :     argParser->add_argument("-fid")
    2385         254 :         .metavar("<FID>")
    2386         127 :         .store_into(psOptions->nFetchFID)
    2387         127 :         .help(_("Only the feature with this feature id will be reported."));
    2388             : 
    2389         127 :     argParser->add_argument("-spat")
    2390         254 :         .metavar("<xmin> <ymin> <xmax> <ymax>")
    2391         127 :         .nargs(4)
    2392         127 :         .scan<'g', double>()
    2393             :         .help(_("The area of interest. Only features within the rectangle will "
    2394         127 :                 "be reported."));
    2395             : 
    2396         127 :     argParser->add_argument("-geomfield")
    2397         254 :         .metavar("<field>")
    2398         127 :         .store_into(psOptions->osGeomField)
    2399             :         .help(_("Name of the geometry field on which the spatial filter "
    2400         127 :                 "operates."));
    2401             : 
    2402         127 :     argParser->add_argument("-where")
    2403         254 :         .metavar("<restricted_where>")
    2404         127 :         .store_into(psOptions->osWHERE)
    2405             :         .help(_("An attribute query in a restricted form of the queries used "
    2406         127 :                 "in the SQL WHERE statement."));
    2407             : 
    2408             :     {
    2409         127 :         auto &group = argParser->add_mutually_exclusive_group();
    2410         127 :         group.add_argument("-sql")
    2411         254 :             .metavar("<statement|@filename>")
    2412         127 :             .store_into(psOptions->osSQLStatement)
    2413             :             .help(_(
    2414         127 :                 "Execute the indicated SQL statement and return the result."));
    2415             : 
    2416         127 :         group.add_argument("-rl")
    2417         127 :             .store_into(psOptions->bDatasetGetNextFeature)
    2418         127 :             .help(_("Enable random layer reading mode."));
    2419             :     }
    2420             : 
    2421         127 :     argParser->add_argument("-dialect")
    2422         254 :         .metavar("<dialect>")
    2423         127 :         .store_into(psOptions->osDialect)
    2424         127 :         .help(_("SQL dialect."));
    2425             : 
    2426             :     // Only for fuzzing
    2427         127 :     argParser->add_argument("-rc")
    2428         127 :         .hidden()
    2429         254 :         .metavar("<count>")
    2430         127 :         .store_into(psOptions->nRepeatCount)
    2431         127 :         .help(_("Repeat count"));
    2432             : 
    2433         127 :     argParser->add_argument("-al")
    2434         127 :         .store_into(psOptions->bAllLayers)
    2435             :         .help(_("List all layers (used instead of having to give layer names "
    2436         127 :                 "as arguments)."));
    2437             : 
    2438             :     {
    2439         127 :         auto &group = argParser->add_mutually_exclusive_group();
    2440         127 :         group.add_argument("-so", "-summary")
    2441         127 :             .store_into(psOptions->bSummaryUserRequested)
    2442             :             .help(_("Summary only: show only summary information like "
    2443         127 :                     "projection, schema, feature count and extents."));
    2444             : 
    2445         127 :         group.add_argument("-features")
    2446         127 :             .store_into(psOptions->bFeaturesUserRequested)
    2447         127 :             .help(_("Enable listing of features."));
    2448             :     }
    2449             : 
    2450         127 :     argParser->add_argument("-limit")
    2451         254 :         .metavar("<nb_features>")
    2452         127 :         .store_into(psOptions->nLimit)
    2453         127 :         .help(_("Limit the number of features per layer."));
    2454             : 
    2455         127 :     argParser->add_argument("-fields")
    2456         127 :         .choices("YES", "NO")
    2457         254 :         .metavar("YES|NO")
    2458             :         .action(
    2459           2 :             [psOptions](const std::string &s)
    2460             :             {
    2461           2 :                 psOptions->aosOptions.SetNameValue("DISPLAY_FIELDS", s.c_str());
    2462         127 :             })
    2463             :         .help(
    2464         127 :             _("If set to NO, the feature dump will not display field values."));
    2465             : 
    2466         127 :     argParser->add_argument("-geom")
    2467         127 :         .choices("YES", "NO", "SUMMARY", "WKT", "ISO_WKT")
    2468         254 :         .metavar("YES|NO|SUMMARY|WKT|ISO_WKT")
    2469             :         .action(
    2470           3 :             [psOptions](const std::string &s)
    2471             :             {
    2472             :                 psOptions->aosOptions.SetNameValue("DISPLAY_GEOMETRY",
    2473           3 :                                                    s.c_str());
    2474         127 :             })
    2475         127 :         .help(_("How to display geometries in feature dump."));
    2476             : 
    2477         127 :     argParser->add_argument("-oo")
    2478         127 :         .append()
    2479         254 :         .metavar("<NAME=VALUE>")
    2480             :         .action(
    2481          20 :             [psOptionsForBinary](const std::string &s)
    2482             :             {
    2483          10 :                 if (psOptionsForBinary)
    2484          10 :                     psOptionsForBinary->aosOpenOptions.AddString(s.c_str());
    2485         127 :             })
    2486         127 :         .help(_("Dataset open option (format-specific)."));
    2487             : 
    2488         127 :     argParser->add_argument("-nomd")
    2489         127 :         .flag()
    2490           1 :         .action([psOptions](const std::string &)
    2491         127 :                 { psOptions->bShowMetadata = false; })
    2492         127 :         .help(_("Suppress metadata printing."));
    2493             : 
    2494         127 :     argParser->add_argument("-listmdd")
    2495         127 :         .store_into(psOptions->bListMDD)
    2496         127 :         .help(_("List all metadata domains available for the dataset."));
    2497             : 
    2498         127 :     argParser->add_argument("-mdd")
    2499         127 :         .append()
    2500         254 :         .metavar("<domain>")
    2501           1 :         .action([psOptions](const std::string &s)
    2502         128 :                 { psOptions->aosExtraMDDomains.AddString(s.c_str()); })
    2503         127 :         .help(_("List metadata in the specified domain."));
    2504             : 
    2505         127 :     argParser->add_argument("-nocount")
    2506         127 :         .flag()
    2507           2 :         .action([psOptions](const std::string &)
    2508         127 :                 { psOptions->bFeatureCount = false; })
    2509         127 :         .help(_("Suppress feature count printing."));
    2510             : 
    2511         127 :     argParser->add_argument("-noextent")
    2512         127 :         .flag()
    2513           0 :         .action([psOptions](const std::string &)
    2514         127 :                 { psOptions->bExtent = false; })
    2515         127 :         .help(_("Suppress spatial extent printing."));
    2516             : 
    2517         127 :     argParser->add_argument("-extent3D")
    2518         127 :         .store_into(psOptions->bExtent3D)
    2519         127 :         .help(_("Request a 3D extent to be reported."));
    2520             : 
    2521         127 :     argParser->add_argument("-nogeomtype")
    2522         127 :         .flag()
    2523           1 :         .action([psOptions](const std::string &)
    2524         127 :                 { psOptions->bGeomType = false; })
    2525         127 :         .help(_("Suppress layer geometry type printing."));
    2526             : 
    2527         127 :     argParser->add_argument("-wkt_format")
    2528         127 :         .store_into(psOptions->osWKTFormat)
    2529         254 :         .metavar("WKT1|WKT2|WKT2_2015|WKT2_2019")
    2530         127 :         .help(_("The WKT format used to display the SRS."));
    2531             : 
    2532         127 :     argParser->add_argument("-fielddomain")
    2533         127 :         .store_into(psOptions->osFieldDomain)
    2534         254 :         .metavar("<name>")
    2535         127 :         .help(_("Display details about a field domain."));
    2536             : 
    2537         127 :     argParser->add_argument("-if")
    2538         127 :         .append()
    2539         254 :         .metavar("<format>")
    2540             :         .action(
    2541           4 :             [psOptionsForBinary](const std::string &s)
    2542             :             {
    2543           2 :                 if (psOptionsForBinary)
    2544             :                 {
    2545           2 :                     if (GDALGetDriverByName(s.c_str()) == nullptr)
    2546             :                     {
    2547           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    2548             :                                  "%s is not a recognized driver", s.c_str());
    2549             :                     }
    2550             :                     psOptionsForBinary->aosAllowInputDrivers.AddString(
    2551           2 :                         s.c_str());
    2552             :                 }
    2553         127 :             })
    2554         127 :         .help(_("Format/driver name(s) to try when opening the input file."));
    2555             : 
    2556         127 :     argParser->add_argument("-stdout")
    2557         127 :         .flag()
    2558         127 :         .store_into(psOptions->bStdoutOutput)
    2559         127 :         .hidden()
    2560         127 :         .help(_("Directly output on stdout (format=text mode only)"));
    2561             : 
    2562         127 :     argParser->add_argument("--cli")
    2563         127 :         .hidden()
    2564         127 :         .store_into(psOptions->bIsCli)
    2565             :         .help(_("Indicates that this is called from the gdal vector info CLI "
    2566         127 :                 "utility."));
    2567             : 
    2568             :     // Hidden: only for gdal vector info
    2569         127 :     argParser->add_argument("--crs-format")
    2570         127 :         .choices("AUTO", "WKT2", "PROJJSON")
    2571         127 :         .store_into(psOptions->osCRSFormat)
    2572         127 :         .hidden();
    2573             : 
    2574         127 :     auto &argFilename = argParser->add_argument("filename")
    2575             :                             .action(
    2576         134 :                                 [psOptionsForBinary](const std::string &s)
    2577             :                                 {
    2578          91 :                                     if (psOptionsForBinary)
    2579          43 :                                         psOptionsForBinary->osFilename = s;
    2580         127 :                                 })
    2581         127 :                             .help(_("The data source to open."));
    2582         127 :     if (!psOptionsForBinary)
    2583          82 :         argFilename.nargs(argparse::nargs_pattern::optional);
    2584             : 
    2585         127 :     argParser->add_argument("layer")
    2586         127 :         .remaining()
    2587         254 :         .metavar("<layer_name>")
    2588         127 :         .help(_("Layer name."));
    2589             : 
    2590         127 :     return argParser;
    2591             : }
    2592             : 
    2593             : /************************************************************************/
    2594             : /*                    GDALVectorInfoGetParserUsage()                    */
    2595             : /************************************************************************/
    2596             : 
    2597           1 : std::string GDALVectorInfoGetParserUsage()
    2598             : {
    2599             :     try
    2600             :     {
    2601           2 :         GDALVectorInfoOptions sOptions;
    2602           2 :         GDALVectorInfoOptionsForBinary sOptionsForBinary;
    2603             :         auto argParser =
    2604           2 :             GDALVectorInfoOptionsGetParser(&sOptions, &sOptionsForBinary);
    2605           1 :         return argParser->usage();
    2606             :     }
    2607           0 :     catch (const std::exception &err)
    2608             :     {
    2609           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
    2610           0 :                  err.what());
    2611           0 :         return std::string();
    2612             :     }
    2613             : }
    2614             : 
    2615             : /************************************************************************/
    2616             : /*                      GDALVectorInfoOptionsNew()                      */
    2617             : /************************************************************************/
    2618             : 
    2619             : /**
    2620             :  * Allocates a GDALVectorInfoOptions struct.
    2621             :  *
    2622             :  * Note that  when this function is used a library function, and not from the
    2623             :  * ogrinfo utility, a dataset name must be specified if any layer names(s) are
    2624             :  * specified (if no layer name is specific, passing a dataset name is not
    2625             :  * needed). That dataset name may be a dummy one, as the dataset taken into
    2626             :  * account is the hDS parameter passed to GDALVectorInfo().
    2627             :  * Similarly the -oo switch in a non-ogrinfo context will be ignored, and it
    2628             :  * is the responsibility of the user to apply them when opening the hDS parameter
    2629             :  * passed to GDALVectorInfo().
    2630             :  *
    2631             :  * @param papszArgv NULL terminated list of options (potentially including
    2632             :  * filename and open options too), or NULL. The accepted options are the ones of
    2633             :  * the <a href="/programs/ogrinfo.html">ogrinfo</a> utility.
    2634             :  * @param psOptionsForBinary (output) may be NULL (and should generally be
    2635             :  * NULL), otherwise (ogrinfo_bin.cpp use case) must be allocated with
    2636             :  * GDALVectorInfoOptionsForBinaryNew() prior to this
    2637             :  * function. Will be filled with potentially present filename, open options,
    2638             :  * subdataset number...
    2639             :  * @return pointer to the allocated GDALVectorInfoOptions struct. Must be freed
    2640             :  * with GDALVectorInfoOptionsFree().
    2641             :  *
    2642             :  * @since GDAL 3.7
    2643             :  */
    2644             : 
    2645             : GDALVectorInfoOptions *
    2646         126 : GDALVectorInfoOptionsNew(char **papszArgv,
    2647             :                          GDALVectorInfoOptionsForBinary *psOptionsForBinary)
    2648             : {
    2649         252 :     auto psOptions = std::make_unique<GDALVectorInfoOptions>();
    2650             : 
    2651             :     try
    2652             :     {
    2653             :         auto argParser =
    2654         252 :             GDALVectorInfoOptionsGetParser(psOptions.get(), psOptionsForBinary);
    2655             : 
    2656             :         /* Special pre-processing to rewrite -fields=foo as "-fields" "FOO", and
    2657             :      * same for -geom=foo. */
    2658         252 :         CPLStringList aosArgv;
    2659         587 :         for (CSLConstList papszIter = papszArgv; papszIter && *papszIter;
    2660             :              ++papszIter)
    2661             :         {
    2662         461 :             if (STARTS_WITH(*papszIter, "-fields="))
    2663             :             {
    2664           2 :                 aosArgv.AddString("-fields");
    2665             :                 aosArgv.AddString(
    2666           2 :                     CPLString(*papszIter + strlen("-fields=")).toupper());
    2667             :             }
    2668         459 :             else if (STARTS_WITH(*papszIter, "-geom="))
    2669             :             {
    2670           3 :                 aosArgv.AddString("-geom");
    2671             :                 aosArgv.AddString(
    2672           3 :                     CPLString(*papszIter + strlen("-geom=")).toupper());
    2673             :             }
    2674             :             else
    2675             :             {
    2676         456 :                 aosArgv.AddString(*papszIter);
    2677             :             }
    2678             :         }
    2679             : 
    2680         126 :         argParser->parse_args_without_binary_name(aosArgv.List());
    2681             : 
    2682         250 :         auto layers = argParser->present<std::vector<std::string>>("layer");
    2683         125 :         if (layers)
    2684             :         {
    2685          23 :             for (const auto &layer : *layers)
    2686             :             {
    2687          12 :                 psOptions->aosLayers.AddString(layer.c_str());
    2688          12 :                 psOptions->bAllLayers = false;
    2689             :             }
    2690             :         }
    2691             : 
    2692         127 :         if (auto oSpat = argParser->present<std::vector<double>>("-spat"))
    2693             :         {
    2694           2 :             const double dfMinX = (*oSpat)[0];
    2695           2 :             const double dfMinY = (*oSpat)[1];
    2696           2 :             const double dfMaxX = (*oSpat)[2];
    2697           2 :             const double dfMaxY = (*oSpat)[3];
    2698             : 
    2699             :             auto poPolygon =
    2700           4 :                 std::make_unique<OGRPolygon>(dfMinX, dfMinY, dfMaxX, dfMaxY);
    2701           2 :             psOptions->poSpatialFilter.reset(poPolygon.release());
    2702             :         }
    2703             : 
    2704         125 :         if (!psOptions->osWHERE.empty() && psOptions->osWHERE[0] == '@')
    2705             :         {
    2706           0 :             GByte *pabyRet = nullptr;
    2707           0 :             if (VSIIngestFile(nullptr, psOptions->osWHERE.substr(1).c_str(),
    2708           0 :                               &pabyRet, nullptr, 10 * 1024 * 1024))
    2709             :             {
    2710           0 :                 GDALRemoveBOM(pabyRet);
    2711           0 :                 psOptions->osWHERE = reinterpret_cast<const char *>(pabyRet);
    2712           0 :                 VSIFree(pabyRet);
    2713             :             }
    2714             :             else
    2715             :             {
    2716           0 :                 CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
    2717           0 :                          psOptions->osWHERE.substr(1).c_str());
    2718           0 :                 return nullptr;
    2719             :             }
    2720             :         }
    2721             : 
    2722         130 :         if (!psOptions->osSQLStatement.empty() &&
    2723           5 :             psOptions->osSQLStatement[0] == '@')
    2724             :         {
    2725           1 :             GByte *pabyRet = nullptr;
    2726           1 :             if (VSIIngestFile(nullptr,
    2727           2 :                               psOptions->osSQLStatement.substr(1).c_str(),
    2728           1 :                               &pabyRet, nullptr, 10 * 1024 * 1024))
    2729             :             {
    2730           1 :                 GDALRemoveBOM(pabyRet);
    2731           1 :                 char *pszSQLStatement = reinterpret_cast<char *>(pabyRet);
    2732           1 :                 psOptions->osSQLStatement =
    2733           2 :                     CPLRemoveSQLComments(pszSQLStatement);
    2734           1 :                 VSIFree(pabyRet);
    2735             :             }
    2736             :             else
    2737             :             {
    2738           0 :                 CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
    2739           0 :                          psOptions->osSQLStatement.substr(1).c_str());
    2740           0 :                 return nullptr;
    2741             :             }
    2742             :         }
    2743             : 
    2744         125 :         if (psOptionsForBinary)
    2745             :         {
    2746          43 :             psOptions->bStdoutOutput = true;
    2747          43 :             psOptionsForBinary->osSQLStatement = psOptions->osSQLStatement;
    2748             :         }
    2749             : 
    2750         125 :         if (psOptions->eFormat == FORMAT_JSON)
    2751             :         {
    2752          44 :             psOptions->bAllLayers = true;
    2753          44 :             psOptions->bSummaryOnly = true;
    2754          44 :             if (psOptions->aosExtraMDDomains.empty())
    2755          44 :                 psOptions->aosExtraMDDomains.AddString("all");
    2756          44 :             psOptions->bStdoutOutput = false;
    2757             :         }
    2758             : 
    2759         125 :         if (psOptions->bSummaryUserRequested)
    2760          17 :             psOptions->bSummaryOnly = true;
    2761         108 :         else if (psOptions->bFeaturesUserRequested)
    2762          13 :             psOptions->bSummaryOnly = false;
    2763             : 
    2764         125 :         if (!psOptions->osDialect.empty() && !psOptions->osWHERE.empty() &&
    2765           0 :             psOptions->osSQLStatement.empty())
    2766             :         {
    2767           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2768             :                      "-dialect is ignored with -where. Use -sql instead");
    2769             :         }
    2770             : 
    2771             :         // Patch options when -schema is set
    2772         125 :         if (psOptions->bExportOgrSchema)
    2773             :         {
    2774             :             // TODO: validate and raise an error if incompatible options are set?
    2775             :             //       not strictly necessary given that -schema is an hidden option
    2776          14 :             psOptions->eFormat = FORMAT_JSON;
    2777          14 :             psOptions->bAllLayers = true;
    2778          14 :             psOptions->bShowMetadata = false;
    2779          14 :             psOptions->bListMDD = false;
    2780          14 :             psOptions->bFeatureCount = false;
    2781          14 :             psOptions->bIsCli = true;
    2782          14 :             psOptions->bSummaryOnly = false;
    2783          14 :             psOptions->bExtent = false;
    2784          14 :             psOptions->bExtent3D = false;
    2785             :         }
    2786             : 
    2787         125 :         return psOptions.release();
    2788             :     }
    2789           1 :     catch (const std::exception &err)
    2790             :     {
    2791           1 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what());
    2792           1 :         return nullptr;
    2793             :     }
    2794             : }

Generated by: LCOV version 1.14