LCOV - code coverage report
Current view: top level - apps - ogrinfo_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1160 1334 87.0 %
Date: 2026-04-03 14:38:35 Functions: 32 34 94.1 %

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

Generated by: LCOV version 1.14