Coverage Report

Created: 2026-07-25 06:50

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