LCOV - code coverage report
Current view: top level - apps - ogrinfo_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1086 1278 85.0 %
Date: 2024-05-03 15:49:35 Functions: 29 32 90.6 %

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

Generated by: LCOV version 1.14