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

Generated by: LCOV version 1.14