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

Generated by: LCOV version 1.14