Coverage Report

Created: 2026-07-25 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_vector_index.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  gdal "vector index" subcommand
5
 * Author:   Even Rouault <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "gdalalg_vector_index.h"
14
15
#include "cpl_conv.h"
16
#include "gdal_priv.h"
17
#include "gdal_utils_priv.h"
18
#include "ogrsf_frmts.h"
19
#include "commonutils.h"
20
21
#include <algorithm>
22
#include <cassert>
23
#include <utility>
24
25
//! @cond Doxygen_Suppress
26
27
#ifndef _
28
0
#define _(x) (x)
29
#endif
30
31
/************************************************************************/
32
/*         GDALVectorIndexAlgorithm::GDALVectorIndexAlgorithm()         */
33
/************************************************************************/
34
35
GDALVectorIndexAlgorithm::GDALVectorIndexAlgorithm()
36
0
    : GDALVectorOutputAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL)
37
0
{
38
0
    AddProgressArg();
39
0
    AddInputDatasetArg(&m_inputDatasets, GDAL_OF_VECTOR)
40
0
        .SetAutoOpenDataset(false)
41
0
        .SetDatasetInputFlags(GADV_NAME);
42
0
    GDALVectorOutputAbstractAlgorithm::AddAllOutputArgs();
43
44
0
    AddArg("recursive", 0,
45
0
           _("Whether input directories should be explored recursively."),
46
0
           &m_recursive);
47
0
    AddArg("filename-filter", 0,
48
0
           _("Pattern that the filenames in input directories should follow "
49
0
             "('*' and '?' wildcard)"),
50
0
           &m_filenameFilter);
51
0
    AddArg("location-name", 0, _("Name of the field with the vector path"),
52
0
           &m_locationName)
53
0
        .SetDefault(m_locationName)
54
0
        .SetMinCharCount(1);
55
0
    AddAbsolutePathArg(
56
0
        &m_writeAbsolutePaths,
57
0
        _("Whether the path to the input datasets should be stored as an "
58
0
          "absolute path"));
59
0
    AddArg(GDAL_ARG_NAME_OUTPUT_CRS, 0, _("Output CRS"), &m_crs)
60
0
        .SetIsCRSArg()
61
0
        .AddHiddenAlias("dst-crs")
62
0
        .AddHiddenAlias("t_srs");
63
64
0
    {
65
0
        auto &arg =
66
0
            AddArg("metadata", 0, _("Add dataset metadata item"), &m_metadata)
67
0
                .SetMetaVar("<KEY>=<VALUE>")
68
0
                .SetPackedValuesAllowed(false);
69
0
        arg.AddValidationAction([this, &arg]()
70
0
                                { return ParseAndValidateKeyValue(arg); });
71
0
        arg.AddHiddenAlias("mo");
72
0
    }
73
0
    AddArg("source-crs-field-name", 0,
74
0
           _("Name of the field to store the CRS of each dataset"),
75
0
           &m_sourceCrsName)
76
0
        .SetMinCharCount(1);
77
0
    auto &sourceCRSFormatArg =
78
0
        AddArg("source-crs-format", 0,
79
0
               _("Format in which the CRS of each dataset must be written"),
80
0
               &m_sourceCrsFormat)
81
0
            .SetMinCharCount(1)
82
0
            .SetDefault(m_sourceCrsFormat)
83
0
            .SetChoices("auto", "WKT", "EPSG", "PROJ");
84
0
    AddArg("source-layer-name", 0,
85
0
           _("Add layer of specified name from each source file in the tile "
86
0
             "index"),
87
0
           &m_layerNames);
88
0
    AddArg("source-layer-index", 0,
89
0
           _("Add layer of specified index (0-based) from each source file in "
90
0
             "the tile index"),
91
0
           &m_layerIndices);
92
0
    AddArg("accept-different-crs", 0,
93
0
           _("Whether layers with different CRS are accepted"),
94
0
           &m_acceptDifferentCRS);
95
0
    AddArg("accept-different-schemas", 0,
96
0
           _("Whether layers with different schemas are accepted"),
97
0
           &m_acceptDifferentSchemas);
98
0
    AddArg("dataset-name-only", 0,
99
0
           _("Whether to write the dataset name only, instead of suffixed with "
100
0
             "the layer index"),
101
0
           &m_datasetNameOnly);
102
103
    // Hidden
104
0
    AddArg("called-from-ogrtindex", 0,
105
0
           _("Whether we are called from ogrtindex"), &m_calledFromOgrTIndex)
106
0
        .SetHidden();
107
    // Hidden. For compatibility with ogrtindex
108
0
    AddArg("skip-different-crs", 0,
109
0
           _("Skip layers that are not in the same CRS as the first layer"),
110
0
           &m_skipDifferentCRS)
111
0
        .SetHidden();
112
113
0
    AddValidationAction(
114
0
        [this, &sourceCRSFormatArg]()
115
0
        {
116
0
            if (m_acceptDifferentCRS && m_skipDifferentCRS)
117
0
            {
118
0
                ReportError(CE_Failure, CPLE_IllegalArg,
119
0
                            "Options 'accept-different-crs' and "
120
0
                            "'skip-different-crs' are mutually exclusive");
121
0
                return false;
122
0
            }
123
124
0
            if (sourceCRSFormatArg.IsExplicitlySet() && m_sourceCrsName.empty())
125
0
            {
126
0
                ReportError(CE_Failure, CPLE_IllegalArg,
127
0
                            "Option 'source-crs-name' must be specified when "
128
0
                            "'source-crs-format' is specified");
129
0
                return false;
130
0
            }
131
132
0
            if (!m_crs.empty() && m_skipDifferentCRS)
133
0
            {
134
0
                ReportError(
135
0
                    CE_Warning, CPLE_AppDefined,
136
0
                    "--skip-different-crs ignored when --output-crs specified");
137
0
            }
138
139
0
            return true;
140
0
        });
141
0
}
142
143
/************************************************************************/
144
/*                      GDALVectorDatasetIterator                       */
145
/************************************************************************/
146
147
struct GDALVectorDatasetIterator
148
{
149
    const std::vector<GDALArgDatasetValue> &inputs;
150
    const bool bRecursive;
151
    const std::vector<std::string> &filenameFilters;
152
    const std::vector<std::string> &aosLayerNamesOfInterest;
153
    const std::vector<int> &aosLayerIndicesOfInterest;
154
    std::string osCurDir{};
155
    size_t iCurSrc = 0;
156
    VSIDIR *psDir = nullptr;
157
158
    CPL_DISALLOW_COPY_ASSIGN(GDALVectorDatasetIterator)
159
160
    GDALVectorDatasetIterator(
161
        const std::vector<GDALArgDatasetValue> &inputsIn, bool bRecursiveIn,
162
        const std::vector<std::string> &filenameFiltersIn,
163
        const std::vector<std::string> &aosLayerNamesOfInterestIn,
164
        const std::vector<int> &aosLayerIndicesOfInterestIn)
165
0
        : inputs(inputsIn), bRecursive(bRecursiveIn),
166
0
          filenameFilters(filenameFiltersIn),
167
0
          aosLayerNamesOfInterest(aosLayerNamesOfInterestIn),
168
0
          aosLayerIndicesOfInterest(aosLayerIndicesOfInterestIn)
169
0
    {
170
0
    }
171
172
    void reset()
173
0
    {
174
0
        if (psDir)
175
0
            VSICloseDir(psDir);
176
0
        psDir = nullptr;
177
0
        iCurSrc = 0;
178
0
    }
179
180
    std::vector<int> GetLayerIndices(GDALDataset *poDS) const
181
0
    {
182
0
        std::vector<int> ret;
183
0
        const int nLayerCount = poDS->GetLayerCount();
184
0
        for (int i = 0; i < nLayerCount; ++i)
185
0
        {
186
0
            auto poLayer = poDS->GetLayer(i);
187
0
            if ((aosLayerNamesOfInterest.empty() &&
188
0
                 aosLayerIndicesOfInterest.empty()) ||
189
0
                (!aosLayerNamesOfInterest.empty() &&
190
0
                 std::find(aosLayerNamesOfInterest.begin(),
191
0
                           aosLayerNamesOfInterest.end(),
192
0
                           poLayer->GetDescription()) !=
193
0
                     aosLayerNamesOfInterest.end()) ||
194
0
                (!aosLayerIndicesOfInterest.empty() &&
195
0
                 std::find(aosLayerIndicesOfInterest.begin(),
196
0
                           aosLayerIndicesOfInterest.end(),
197
0
                           i) != aosLayerIndicesOfInterest.end()))
198
0
            {
199
0
                ret.push_back(i);
200
0
            }
201
0
        }
202
0
        return ret;
203
0
    }
204
205
    bool MatchPattern(const std::string &filename) const
206
0
    {
207
0
        for (const auto &osFilter : filenameFilters)
208
0
        {
209
0
            if (GDALPatternMatch(filename.c_str(), osFilter.c_str()))
210
0
            {
211
0
                return true;
212
0
            }
213
0
        }
214
0
        return filenameFilters.empty();
215
0
    }
216
217
    std::pair<std::unique_ptr<GDALDataset>, std::vector<int>> next()
218
0
    {
219
0
        std::pair<std::unique_ptr<GDALDataset>, std::vector<int>> emptyRet;
220
221
0
        while (true)
222
0
        {
223
0
            if (!psDir)
224
0
            {
225
0
                if (iCurSrc == inputs.size())
226
0
                {
227
0
                    break;
228
0
                }
229
230
0
                VSIStatBufL sStatBuf;
231
0
                const std::string &osCurName = inputs[iCurSrc++].GetName();
232
0
                if (MatchPattern(osCurName))
233
0
                {
234
0
                    auto poSrcDS = std::unique_ptr<GDALDataset>(
235
0
                        GDALDataset::Open(osCurName.c_str(), GDAL_OF_VECTOR,
236
0
                                          nullptr, nullptr, nullptr));
237
0
                    if (poSrcDS)
238
0
                    {
239
0
                        auto anLayerIndices = GetLayerIndices(poSrcDS.get());
240
0
                        if (!anLayerIndices.empty())
241
0
                        {
242
0
                            return {std::move(poSrcDS),
243
0
                                    std::move(anLayerIndices)};
244
0
                        }
245
0
                    }
246
0
                }
247
248
0
                if (VSIStatL(osCurName.c_str(), &sStatBuf) == 0 &&
249
0
                    VSI_ISDIR(sStatBuf.st_mode) &&
250
0
                    !cpl::ends_with(osCurName, ".gdb"))
251
0
                {
252
0
                    osCurDir = osCurName;
253
0
                    psDir = VSIOpenDir(osCurDir.c_str(),
254
0
                                       /*nDepth=*/bRecursive ? -1 : 0, nullptr);
255
0
                    if (!psDir)
256
0
                    {
257
0
                        CPLError(CE_Failure, CPLE_AppDefined,
258
0
                                 "Cannot open directory %s", osCurDir.c_str());
259
0
                        return emptyRet;
260
0
                    }
261
0
                }
262
0
                else
263
0
                {
264
0
                    return emptyRet;
265
0
                }
266
0
            }
267
268
0
            auto psEntry = VSIGetNextDirEntry(psDir);
269
0
            if (!psEntry)
270
0
            {
271
0
                VSICloseDir(psDir);
272
0
                psDir = nullptr;
273
0
                continue;
274
0
            }
275
276
0
            if (!MatchPattern(CPLGetFilename(psEntry->pszName)))
277
0
            {
278
0
                continue;
279
0
            }
280
281
0
            const std::string osFilename = CPLFormFilenameSafe(
282
0
                osCurDir.c_str(), psEntry->pszName, nullptr);
283
0
            auto poSrcDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
284
0
                osFilename.c_str(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr));
285
0
            if (poSrcDS)
286
0
            {
287
0
                auto anLayerIndices = GetLayerIndices(poSrcDS.get());
288
0
                if (!anLayerIndices.empty())
289
0
                {
290
0
                    return {std::move(poSrcDS), std::move(anLayerIndices)};
291
0
                }
292
0
            }
293
0
        }
294
0
        return emptyRet;
295
0
    }
296
};
297
298
/************************************************************************/
299
/*                 GDALVectorIndexAlgorithm::RunImpl()                  */
300
/************************************************************************/
301
302
bool GDALVectorIndexAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
303
                                       void *pProgressData)
304
0
{
305
0
    CPLStringList aosSources;
306
0
    for (auto &srcDS : m_inputDatasets)
307
0
    {
308
0
        CPLAssert(!srcDS.GetDatasetRef());
309
0
        aosSources.push_back(srcDS.GetName());
310
0
    }
311
312
0
    std::string osCWD;
313
0
    if (m_writeAbsolutePaths)
314
0
    {
315
0
        char *pszCurrentPath = CPLGetCurrentDir();
316
0
        if (pszCurrentPath == nullptr)
317
0
        {
318
0
            ReportError(
319
0
                CE_Failure, CPLE_AppDefined,
320
0
                "This system does not support the CPLGetCurrentDir call.");
321
0
            return false;
322
0
        }
323
0
        osCWD = pszCurrentPath;
324
0
        CPLFree(pszCurrentPath);
325
0
    }
326
327
0
    auto setupRet = SetupOutputDataset();
328
0
    if (!setupRet.outDS)
329
0
        return false;
330
331
0
    const auto poOutDrv = setupRet.outDS->GetDriver();
332
333
0
    GDALVectorDatasetIterator oIterator(m_inputDatasets, m_recursive,
334
0
                                        m_filenameFilter, m_layerNames,
335
0
                                        m_layerIndices);
336
337
0
    if (m_outputLayerName.empty())
338
0
        m_outputLayerName = "tileindex";
339
340
0
    OGRSpatialReferenceRefCountedPtr poTargetCRS;
341
0
    if (!m_crs.empty())
342
0
    {
343
0
        poTargetCRS = OGRSpatialReferenceRefCountedPtr::makeInstance();
344
0
        poTargetCRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
345
        // already checked by GDALAlgorithmArg framework
346
0
        CPL_IGNORE_RET_VAL(poTargetCRS->SetFromUserInput(m_crs.c_str()));
347
0
    }
348
349
0
    std::set<std::string> setAlreadyReferencedLayers;
350
351
0
    const size_t nMaxFieldSize = [poOutDrv]()
352
0
    {
353
0
        const char *pszVal =
354
0
            poOutDrv ? poOutDrv->GetMetadataItem(GDAL_DMD_MAX_STRING_LENGTH)
355
0
                     : nullptr;
356
0
        return pszVal ? atoi(pszVal) : 0;
357
0
    }();
358
359
0
    OGRLayer *poDstLayer = setupRet.layer;
360
0
    int nLocationFieldIdx = -1;
361
0
    int nSourceCRSFieldIdx = -1;
362
363
0
    OGRFeatureDefnRefCountedPtr poRefFeatureDefn;
364
0
    if (poDstLayer)
365
0
    {
366
0
        nLocationFieldIdx =
367
0
            poDstLayer->GetLayerDefn()->GetFieldIndex(m_locationName.c_str());
368
0
        if (nLocationFieldIdx < 0)
369
0
        {
370
0
            ReportError(CE_Failure, CPLE_AppDefined,
371
0
                        "Unable to find field '%s' in output layer.",
372
0
                        m_locationName.c_str());
373
0
            return false;
374
0
        }
375
376
0
        if (!m_sourceCrsName.empty())
377
0
        {
378
0
            nSourceCRSFieldIdx = poDstLayer->GetLayerDefn()->GetFieldIndex(
379
0
                m_sourceCrsName.c_str());
380
0
            if (nSourceCRSFieldIdx < 0)
381
0
            {
382
0
                ReportError(CE_Failure, CPLE_AppDefined,
383
0
                            "Unable to find field '%s' in output layer.",
384
0
                            m_sourceCrsName.c_str());
385
0
                return false;
386
0
            }
387
0
        }
388
389
0
        if (!poTargetCRS)
390
0
        {
391
0
            const auto poSrcCRS = poDstLayer->GetSpatialRef();
392
0
            if (poSrcCRS)
393
0
                poTargetCRS =
394
0
                    OGRSpatialReferenceRefCountedPtr::makeClone(poSrcCRS);
395
0
        }
396
397
0
        for (auto &&poFeature : poDstLayer)
398
0
        {
399
0
            std::string osLocation =
400
0
                poFeature->GetFieldAsString(nLocationFieldIdx);
401
402
0
            if (!poRefFeatureDefn)
403
0
            {
404
0
                const auto nCommaPos = osLocation.rfind(',');
405
0
                if (nCommaPos != std::string::npos)
406
0
                {
407
0
                    auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
408
0
                        osLocation.substr(0, nCommaPos).c_str(),
409
0
                        GDAL_OF_VECTOR));
410
0
                    if (poDS)
411
0
                    {
412
0
                        if (auto poLayer = poDS->GetLayer(
413
0
                                atoi(osLocation.substr(nCommaPos + 1).c_str())))
414
0
                        {
415
0
                            poRefFeatureDefn.reset(
416
0
                                poLayer->GetLayerDefn()->Clone());
417
0
                        }
418
0
                    }
419
0
                }
420
0
            }
421
422
0
            setAlreadyReferencedLayers.insert(std::move(osLocation));
423
0
        }
424
0
    }
425
0
    else
426
0
    {
427
0
        auto [poSrcDS, anLayerIndices] = oIterator.next();
428
0
        oIterator.reset();
429
0
        if (!poSrcDS)
430
0
        {
431
0
            ReportError(CE_Failure, CPLE_AppDefined, "No layer to index");
432
0
            return false;
433
0
        }
434
435
0
        if (!poTargetCRS)
436
0
        {
437
0
            const auto poSrcCRS =
438
0
                poSrcDS->GetLayer(anLayerIndices[0])->GetSpatialRef();
439
0
            if (poSrcCRS)
440
0
                poTargetCRS =
441
0
                    OGRSpatialReferenceRefCountedPtr::makeClone(poSrcCRS);
442
0
        }
443
444
0
        poDstLayer = setupRet.outDS->CreateLayer(m_outputLayerName.c_str(),
445
0
                                                 poTargetCRS.get(), wkbPolygon);
446
0
        if (!poDstLayer)
447
0
            return false;
448
449
0
        OGRFieldDefn oLocation(m_locationName.c_str(), OFTString);
450
0
        oLocation.SetWidth(static_cast<int>(nMaxFieldSize));
451
0
        if (poDstLayer->CreateField(&oLocation) != OGRERR_NONE)
452
0
            return false;
453
0
        nLocationFieldIdx = poDstLayer->GetLayerDefn()->GetFieldCount() - 1;
454
455
0
        if (!m_sourceCrsName.empty())
456
0
        {
457
0
            OGRFieldDefn oSrcSRSNameField(m_sourceCrsName.c_str(), OFTString);
458
0
            if (poDstLayer->CreateField(&oSrcSRSNameField) != OGRERR_NONE)
459
0
                return false;
460
0
            nSourceCRSFieldIdx =
461
0
                poDstLayer->GetLayerDefn()->GetFieldCount() - 1;
462
0
        }
463
464
0
        if (!m_metadata.empty())
465
0
        {
466
0
            poDstLayer->SetMetadata(CPLStringList(m_metadata).List());
467
0
        }
468
0
    }
469
470
0
    double dfPct = 0;
471
0
    double dfIncrement = 0.1;
472
0
    int nRemainingIters = 5;
473
474
0
    bool bOK = true;
475
0
    bool bFirstWarningForNonMatchingAttributes = false;
476
0
    while (bOK)
477
0
    {
478
0
        auto [poSrcDS, anLayerIndices] = oIterator.next();
479
0
        if (!poSrcDS)
480
0
            break;
481
482
0
        dfPct += dfIncrement;
483
0
        if (pfnProgress && !pfnProgress(dfPct, "", pProgressData))
484
0
        {
485
0
            bOK = false;
486
0
            break;
487
0
        }
488
0
        --nRemainingIters;
489
0
        if (nRemainingIters == 0)
490
0
        {
491
0
            dfIncrement /= 2;
492
0
            nRemainingIters = 5;
493
0
        }
494
495
0
        std::string osFilename = poSrcDS->GetDescription();
496
0
        VSIStatBufL sStatBuf;
497
0
        if (m_writeAbsolutePaths && CPLIsFilenameRelative(osFilename.c_str()) &&
498
0
            VSIStatL(osFilename.c_str(), &sStatBuf) == 0)
499
0
        {
500
0
            osFilename =
501
0
                CPLFormFilenameSafe(osCWD.c_str(), osFilename.c_str(), nullptr)
502
0
                    .c_str();
503
0
        }
504
505
0
        for (int iLayer : anLayerIndices)
506
0
        {
507
0
            auto poSrcLayer = poSrcDS->GetLayer(iLayer);
508
509
0
            const std::string osLocation =
510
0
                m_datasetNameOnly
511
0
                    ? osFilename
512
0
                    : CPLOPrintf("%s,%d", osFilename.c_str(), iLayer);
513
0
            if (cpl::contains(setAlreadyReferencedLayers, osLocation))
514
0
            {
515
0
                ReportError(CE_Warning, CPLE_AppDefined,
516
0
                            "'%s' already referenced in tile index",
517
0
                            osLocation.c_str());
518
0
                continue;
519
0
            }
520
521
0
            const OGRSpatialReference *poSrcCRS = poSrcLayer->GetSpatialRef();
522
            // If not set target srs, test that the current file uses same
523
            // projection as others.
524
0
            if (m_crs.empty())
525
0
            {
526
0
                if ((poTargetCRS && poSrcCRS &&
527
0
                     !poTargetCRS->IsSame(poSrcCRS)) ||
528
0
                    ((poTargetCRS != nullptr) != (poSrcCRS != nullptr)))
529
0
                {
530
0
                    ReportError(
531
0
                        CE_Warning, CPLE_AppDefined,
532
0
                        "Warning: layer %s of %s is not using the same "
533
0
                        "CRS as other files in the "
534
0
                        "tileindex. This may cause problems when using it "
535
0
                        "in MapServer for example%s",
536
0
                        poSrcLayer->GetDescription(), poSrcDS->GetDescription(),
537
0
                        m_skipDifferentCRS || !m_acceptDifferentCRS
538
0
                            ? ". Skipping it"
539
0
                        : m_calledFromOgrTIndex
540
0
                            ? ". You may specify -skip_differerence_srs to "
541
0
                              "skip it"
542
0
                            : "");
543
0
                    if (m_skipDifferentCRS || !m_acceptDifferentCRS)
544
0
                        continue;
545
0
                }
546
0
            }
547
548
0
            OGRFeature oFeat(poDstLayer->GetLayerDefn());
549
0
            oFeat.SetField(nLocationFieldIdx, osLocation.c_str());
550
551
0
            if (nSourceCRSFieldIdx >= 0 && poSrcCRS)
552
0
            {
553
0
                const char *pszAuthorityCode = poSrcCRS->GetAuthorityCode();
554
0
                const char *pszAuthorityName = poSrcCRS->GetAuthorityName();
555
0
                const std::string osWKT = poSrcCRS->exportToWkt();
556
0
                if (m_sourceCrsFormat == "auto")
557
0
                {
558
0
                    if (pszAuthorityName != nullptr &&
559
0
                        pszAuthorityCode != nullptr)
560
0
                    {
561
0
                        oFeat.SetField(nSourceCRSFieldIdx,
562
0
                                       CPLSPrintf("%s:%s", pszAuthorityName,
563
0
                                                  pszAuthorityCode));
564
0
                    }
565
0
                    else if (nMaxFieldSize == 0 ||
566
0
                             osWKT.size() <= nMaxFieldSize)
567
0
                    {
568
0
                        oFeat.SetField(nSourceCRSFieldIdx, osWKT.c_str());
569
0
                    }
570
0
                    else
571
0
                    {
572
0
                        char *pszProj4 = nullptr;
573
0
                        if (poSrcCRS->exportToProj4(&pszProj4) == OGRERR_NONE)
574
0
                        {
575
0
                            oFeat.SetField(nSourceCRSFieldIdx, pszProj4);
576
0
                        }
577
0
                        else
578
0
                        {
579
0
                            oFeat.SetField(nSourceCRSFieldIdx, osWKT.c_str());
580
0
                        }
581
0
                        CPLFree(pszProj4);
582
0
                    }
583
0
                }
584
0
                else if (m_sourceCrsFormat == "WKT")
585
0
                {
586
0
                    if (nMaxFieldSize == 0 || osWKT.size() <= nMaxFieldSize)
587
0
                    {
588
0
                        oFeat.SetField(nSourceCRSFieldIdx, osWKT.c_str());
589
0
                    }
590
0
                    else
591
0
                    {
592
0
                        ReportError(
593
0
                            CE_Warning, CPLE_AppDefined,
594
0
                            "Cannot write WKT for file %s as it is too long",
595
0
                            osFilename.c_str());
596
0
                    }
597
0
                }
598
0
                else if (m_sourceCrsFormat == "PROJ")
599
0
                {
600
0
                    char *pszProj4 = nullptr;
601
0
                    if (poSrcCRS->exportToProj4(&pszProj4) == OGRERR_NONE)
602
0
                    {
603
0
                        oFeat.SetField(nSourceCRSFieldIdx, pszProj4);
604
0
                    }
605
0
                    CPLFree(pszProj4);
606
0
                }
607
0
                else
608
0
                {
609
0
                    CPLAssert(m_sourceCrsFormat == "EPSG");
610
0
                    if (pszAuthorityName != nullptr &&
611
0
                        pszAuthorityCode != nullptr)
612
0
                    {
613
0
                        oFeat.SetField(nSourceCRSFieldIdx,
614
0
                                       CPLSPrintf("%s:%s", pszAuthorityName,
615
0
                                                  pszAuthorityCode));
616
0
                    }
617
0
                }
618
0
            }
619
620
            // Check if all layers in dataset have the same attributes schema
621
0
            if (poRefFeatureDefn == nullptr)
622
0
            {
623
0
                poRefFeatureDefn.reset(poSrcLayer->GetLayerDefn()->Clone());
624
0
            }
625
0
            else if (!m_acceptDifferentSchemas)
626
0
            {
627
0
                const OGRFeatureDefn *poFeatureDefnCur =
628
0
                    poSrcLayer->GetLayerDefn();
629
0
                assert(nullptr != poFeatureDefnCur);
630
631
0
                const auto EmitHint =
632
0
                    [this, &bFirstWarningForNonMatchingAttributes]()
633
0
                {
634
0
                    if (bFirstWarningForNonMatchingAttributes)
635
0
                    {
636
0
                        ReportError(
637
0
                            CE_Warning, CPLE_AppDefined,
638
0
                            "Note : you can override this "
639
0
                            "behavior with %s option, "
640
0
                            "but this may result in a tileindex incompatible "
641
0
                            "with MapServer",
642
0
                            m_calledFromOgrTIndex
643
0
                                ? "-accept_different_schemas"
644
0
                                : "--accept-different-schemas");
645
0
                        bFirstWarningForNonMatchingAttributes = false;
646
0
                    }
647
0
                };
648
649
0
                const int fieldCount = poFeatureDefnCur->GetFieldCount();
650
0
                if (fieldCount != poRefFeatureDefn->GetFieldCount())
651
0
                {
652
0
                    ReportError(CE_Warning, CPLE_AppDefined,
653
0
                                "Number of attributes of layer %s of %s "
654
0
                                "does not match. Skipping it.",
655
0
                                poSrcLayer->GetDescription(),
656
0
                                poSrcDS->GetDescription());
657
0
                    EmitHint();
658
0
                    continue;
659
0
                }
660
661
0
                bool bSkip = false;
662
0
                for (int fn = 0; fn < fieldCount; fn++)
663
0
                {
664
0
                    const OGRFieldDefn *poFieldThis =
665
0
                        poFeatureDefnCur->GetFieldDefn(fn);
666
0
                    const OGRFieldDefn *poFieldRef =
667
0
                        poRefFeatureDefn->GetFieldDefn(fn);
668
0
                    if (!(poFieldThis->GetType() == poFieldRef->GetType() &&
669
0
                          poFieldThis->GetWidth() == poFieldRef->GetWidth() &&
670
0
                          poFieldThis->GetPrecision() ==
671
0
                              poFieldRef->GetPrecision() &&
672
0
                          strcmp(poFieldThis->GetNameRef(),
673
0
                                 poFieldRef->GetNameRef()) == 0))
674
0
                    {
675
0
                        ReportError(CE_Warning, CPLE_AppDefined,
676
0
                                    "Schema of attributes of layer %s of %s "
677
0
                                    "does not match. Skipping it.",
678
0
                                    poSrcLayer->GetDescription(),
679
0
                                    poSrcDS->GetDescription());
680
0
                        EmitHint();
681
0
                        bSkip = true;
682
0
                        break;
683
0
                    }
684
0
                }
685
686
0
                if (bSkip)
687
0
                    continue;
688
0
            }
689
690
            // Get layer extents, and create a corresponding polygon.
691
0
            OGREnvelope sExtents;
692
0
            if (poSrcLayer->GetExtent(&sExtents, TRUE) != OGRERR_NONE)
693
0
            {
694
0
                ReportError(CE_Warning, CPLE_AppDefined,
695
0
                            "GetExtent() failed on layer %s of %s, skipping.",
696
0
                            poSrcLayer->GetDescription(),
697
0
                            poSrcDS->GetDescription());
698
0
                continue;
699
0
            }
700
701
0
            OGRPolygon oExtentGeom(sExtents);
702
703
            // If set target srs, do the forward transformation of all points.
704
0
            if (!m_crs.empty() && poSrcCRS && poTargetCRS &&
705
0
                !poSrcCRS->IsSame(poTargetCRS.get()))
706
0
            {
707
0
                auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
708
0
                    OGRCreateCoordinateTransformation(poSrcCRS,
709
0
                                                      poTargetCRS.get()));
710
0
                if (poCT == nullptr ||
711
0
                    oExtentGeom.transform(poCT.get()) == OGRERR_FAILURE)
712
0
                {
713
0
                    ReportError(CE_Warning, CPLE_AppDefined,
714
0
                                "Cannot reproject extent of layer %s of %s to "
715
0
                                "the target CRS, skipping.",
716
0
                                poSrcLayer->GetDescription(),
717
0
                                poSrcDS->GetDescription());
718
0
                    continue;
719
0
                }
720
0
            }
721
722
0
            oFeat.SetGeometry(&oExtentGeom);
723
724
0
            bOK = bOK && (poDstLayer->CreateFeature(&oFeat) == OGRERR_NONE);
725
0
        }
726
0
    }
727
728
0
    if (bOK && pfnProgress)
729
0
        pfnProgress(1.0, "", pProgressData);
730
731
0
    if (bOK && setupRet.newDS && !m_outputDataset.GetDatasetRef())
732
0
    {
733
0
        m_outputDataset.Set(std::move(setupRet.newDS));
734
0
    }
735
736
0
    return bOK;
737
0
}
738
739
//! @endcond