Coverage Report

Created: 2026-02-14 09:00

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