LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_index.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 318 340 93.5 %
Date: 2026-04-23 19:47:11 Functions: 11 11 100.0 %

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

Generated by: LCOV version 1.14