LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_rename_layer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 183 188 97.3 %
Date: 2026-06-03 12:46:18 Functions: 17 17 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  "rename-layer" step of "vector pipeline"
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2026, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_vector_rename_layer.h"
      14             : 
      15             : //! @cond Doxygen_Suppress
      16             : 
      17             : #include <map>
      18             : 
      19             : #include "cpl_string.h"
      20             : 
      21             : #ifndef _
      22             : #define _(x) (x)
      23             : #endif
      24             : 
      25             : /************************************************************************/
      26             : /*   GDALVectorRenameLayerAlgorithm::GDALVectorRenameLayerAlgorithm()   */
      27             : /************************************************************************/
      28             : 
      29          97 : GDALVectorRenameLayerAlgorithm::GDALVectorRenameLayerAlgorithm(
      30          97 :     bool standaloneStep)
      31             :     : GDALVectorPipelineStepAlgorithm(
      32             :           NAME, DESCRIPTION, HELP_URL,
      33           0 :           ConstructorOptions()
      34          97 :               .SetStandaloneStep(standaloneStep)
      35          97 :               .SetAddInputLayerNameArgument(false)
      36         194 :               .SetOutputLayerNameAvailableInPipelineStep(true))
      37             : {
      38          97 :     AddLayerNameArg(&m_inputLayerName);
      39          97 :     if (!standaloneStep)
      40             :     {
      41          37 :         AddOutputLayerNameArg(/* hiddenForCLI = */ false,
      42             :                               /* shortNameOutputLayerAllowed = */ false);
      43             :     }
      44          97 :     AddArg("ascii", 0, _("Force names to ASCII character"), &m_ascii);
      45             :     AddArg("lower-case", 0,
      46             :            _("Force names to lower case (only on ASCII characters)"),
      47          97 :            &m_lowerCase);
      48             :     AddArg("filename-compatible", 0, _("Force names to be usable as filenames"),
      49          97 :            &m_filenameCompatible);
      50             :     AddArg("reserved-characters", 0, _("Reserved character(s) to be removed"),
      51          97 :            &m_reservedChars);
      52             :     AddArg("replacement-character", 0,
      53             :            _("Replacement character when ASCII conversion not possible"),
      54         194 :            &m_replacementChar)
      55          97 :         .SetMaxCharCount(1);
      56         194 :     AddArg("max-length", 0, _("Maximum length of layer names"), &m_maxLength)
      57          97 :         .SetMinValueIncluded(1);
      58             : 
      59          97 :     AddValidationAction(
      60         156 :         [this]()
      61             :         {
      62          24 :             if (!m_inputLayerName.empty() && m_outputLayerName.empty())
      63             :             {
      64           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
      65             :                             "Argument output-layer must be specified when "
      66             :                             "input-layer is specified");
      67           1 :                 return false;
      68             :             }
      69             : 
      70          23 :             if (!m_inputDataset.empty() && m_inputDataset[0].GetDatasetRef())
      71             :             {
      72          21 :                 auto poSrcDS = m_inputDataset[0].GetDatasetRef();
      73          26 :                 if (!m_inputLayerName.empty() &&
      74           5 :                     poSrcDS->GetLayerByName(m_inputLayerName.c_str()) ==
      75             :                         nullptr)
      76             :                 {
      77           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
      78             :                                 "Input layer '%s' does not exist",
      79             :                                 m_inputLayerName.c_str());
      80           1 :                     return false;
      81             :                 }
      82             : 
      83          27 :                 if (!m_outputLayerName.empty() && m_inputLayerName.empty() &&
      84           7 :                     poSrcDS->GetLayerCount() >= 2)
      85             :                 {
      86           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
      87             :                                 "Argument input-layer must be specified when "
      88             :                                 "output-layer is specified and there is more "
      89             :                                 "than one layer");
      90           1 :                     return false;
      91             :                 }
      92             :             }
      93             : 
      94          21 :             return true;
      95             :         });
      96          97 : }
      97             : 
      98             : namespace
      99             : {
     100             : 
     101             : /************************************************************************/
     102             : /*                 GDALVectorRenameLayerAlgorithmLayer                  */
     103             : /************************************************************************/
     104             : 
     105             : class GDALVectorRenameLayerAlgorithmLayer final
     106             :     : public GDALVectorPipelineOutputLayer
     107             : {
     108             :   private:
     109             :     const OGRFeatureDefnRefCountedPtr m_poFeatureDefn;
     110             : 
     111             :     CPL_DISALLOW_COPY_ASSIGN(GDALVectorRenameLayerAlgorithmLayer)
     112             : 
     113         195 :     void TranslateFeature(
     114             :         std::unique_ptr<OGRFeature> poSrcFeature,
     115             :         std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures) override
     116             :     {
     117         195 :         poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn.get());
     118         195 :         apoOutFeatures.push_back(std::move(poSrcFeature));
     119         195 :     }
     120             : 
     121             :   public:
     122          20 :     explicit GDALVectorRenameLayerAlgorithmLayer(
     123             :         OGRLayer &oSrcLayer, const std::string &osOutputLayerName)
     124          20 :         : GDALVectorPipelineOutputLayer(oSrcLayer),
     125          20 :           m_poFeatureDefn(oSrcLayer.GetLayerDefn()->Clone())
     126             :     {
     127          20 :         m_poFeatureDefn->SetName(osOutputLayerName.c_str());
     128          20 :         const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
     129          20 :         const auto poSrcLayerDefn = oSrcLayer.GetLayerDefn();
     130          39 :         for (int i = 0; i < nGeomFieldCount; ++i)
     131             :         {
     132          38 :             m_poFeatureDefn->GetGeomFieldDefn(i)->SetSpatialRef(
     133          19 :                 poSrcLayerDefn->GetGeomFieldDefn(i)->GetSpatialRef());
     134             :         }
     135          20 :         SetDescription(m_poFeatureDefn->GetName());
     136          20 :         SetMetadata(oSrcLayer.GetMetadata());
     137          20 :     }
     138             : 
     139         311 :     const OGRFeatureDefn *GetLayerDefn() const override
     140             :     {
     141         311 :         return m_poFeatureDefn.get();
     142             :     }
     143             : 
     144          13 :     GIntBig GetFeatureCount(int bForce) override
     145             :     {
     146          13 :         return m_srcLayer.GetFeatureCount(bForce);
     147             :     }
     148             : 
     149           4 :     OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
     150             :                       bool bForce) override
     151             :     {
     152           4 :         return m_srcLayer.GetExtent(iGeomField, psExtent, bForce);
     153             :     }
     154             : 
     155          14 :     OGRErr SetIgnoredFields(CSLConstList papszFields) override
     156             :     {
     157          14 :         return m_srcLayer.SetIgnoredFields(papszFields);
     158             :     }
     159             : 
     160          34 :     OGRErr SetAttributeFilter(const char *pszAttributeFilter) override
     161             :     {
     162          34 :         OGRLayer::SetAttributeFilter(pszAttributeFilter);
     163          34 :         return m_srcLayer.SetAttributeFilter(pszAttributeFilter);
     164             :     }
     165             : 
     166           1 :     OGRGeometry *GetSpatialFilter() override
     167             :     {
     168           1 :         return m_srcLayer.GetSpatialFilter();
     169             :     }
     170             : 
     171          26 :     OGRErr ISetSpatialFilter(int iGeomField, const OGRGeometry *poGeom) override
     172             :     {
     173          26 :         return m_srcLayer.SetSpatialFilter(iGeomField, poGeom);
     174             :     }
     175             : 
     176           8 :     OGRFeature *GetFeature(GIntBig nFID) override
     177             :     {
     178             :         auto poSrcFeature =
     179          16 :             std::unique_ptr<OGRFeature>(m_srcLayer.GetFeature(nFID));
     180           8 :         if (!poSrcFeature)
     181           3 :             return nullptr;
     182           5 :         poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn.get());
     183           5 :         return poSrcFeature.release();
     184             :     }
     185             : 
     186          48 :     int TestCapability(const char *pszCap) const override
     187             :     {
     188          48 :         return m_srcLayer.TestCapability(pszCap);
     189             :     }
     190             : };
     191             : 
     192             : /************************************************************************/
     193             : /*                GDALVectorRenameLayerAlgorithmDataset                 */
     194             : /************************************************************************/
     195             : 
     196             : class GDALVectorRenameLayerAlgorithmDataset final
     197             :     : public GDALVectorPipelineOutputDataset
     198             : {
     199             :   public:
     200          17 :     GDALVectorRenameLayerAlgorithmDataset(
     201             :         GDALDataset &oSrcDS, const std::vector<std::string> &aosNewLayerNames)
     202          17 :         : GDALVectorPipelineOutputDataset(oSrcDS)
     203             :     {
     204          17 :         const int nLayerCount = oSrcDS.GetLayerCount();
     205          17 :         CPLAssert(aosNewLayerNames.size() == static_cast<size_t>(nLayerCount));
     206          52 :         for (int i = 0; i < nLayerCount; ++i)
     207             :         {
     208          70 :             m_mapOldLayerNameToNew[oSrcDS.GetLayer(i)->GetName()] =
     209          70 :                 aosNewLayerNames[i];
     210             :         }
     211          17 :     }
     212             : 
     213             :     const GDALRelationship *
     214             :     GetRelationship(const std::string &name) const override;
     215             : 
     216             :   private:
     217             :     std::map<std::string, std::string> m_mapOldLayerNameToNew{};
     218             :     mutable std::map<std::string, std::unique_ptr<GDALRelationship>>
     219             :         m_relationships{};
     220             : };
     221             : 
     222             : /************************************************************************/
     223             : /*                          GetRelationship()                           */
     224             : /************************************************************************/
     225             : 
     226           6 : const GDALRelationship *GDALVectorRenameLayerAlgorithmDataset::GetRelationship(
     227             :     const std::string &name) const
     228             : {
     229           6 :     const auto oIterRelationships = m_relationships.find(name);
     230           6 :     if (oIterRelationships != m_relationships.end())
     231           1 :         return oIterRelationships->second.get();
     232             : 
     233           5 :     const GDALRelationship *poSrcRelationShip = m_srcDS.GetRelationship(name);
     234           5 :     if (!poSrcRelationShip)
     235           1 :         return nullptr;
     236             :     const auto oIterLeftTableName =
     237           4 :         m_mapOldLayerNameToNew.find(poSrcRelationShip->GetLeftTableName());
     238             :     const auto oIterRightTableName =
     239           4 :         m_mapOldLayerNameToNew.find(poSrcRelationShip->GetRightTableName());
     240             :     const auto oIterMappingTableName =
     241           4 :         m_mapOldLayerNameToNew.find(poSrcRelationShip->GetMappingTableName());
     242           4 :     if (oIterLeftTableName == m_mapOldLayerNameToNew.end() &&
     243           4 :         oIterRightTableName == m_mapOldLayerNameToNew.end() &&
     244           4 :         oIterMappingTableName == m_mapOldLayerNameToNew.end())
     245             :     {
     246           0 :         return poSrcRelationShip;
     247             :     }
     248             : 
     249             :     auto poNewRelationship =
     250           4 :         std::make_unique<GDALRelationship>(*poSrcRelationShip);
     251           4 :     if (oIterLeftTableName != m_mapOldLayerNameToNew.end())
     252           4 :         poNewRelationship->SetLeftTableName(oIterLeftTableName->second);
     253           4 :     if (oIterRightTableName != m_mapOldLayerNameToNew.end())
     254           4 :         poNewRelationship->SetRightTableName(oIterRightTableName->second);
     255           4 :     if (oIterMappingTableName != m_mapOldLayerNameToNew.end())
     256           4 :         poNewRelationship->SetMappingTableName(oIterMappingTableName->second);
     257             : 
     258           8 :     return m_relationships.insert({name, std::move(poNewRelationship)})
     259           4 :         .first->second.get();
     260             : }
     261             : 
     262             : }  // namespace
     263             : 
     264             : /************************************************************************/
     265             : /*                       TruncateUTF8ToMaxChar()                        */
     266             : /************************************************************************/
     267             : 
     268           7 : static void TruncateUTF8ToMaxChar(std::string &osStr, size_t maxCharCount)
     269             : {
     270           7 :     size_t nCharacterCount = 0;
     271          28 :     for (size_t i = 0; i < osStr.size(); ++i)
     272             :     {
     273             :         // Is it first byte of a UTF-8 character?
     274          28 :         if ((osStr[i] & 0xc0) != 0x80)
     275             :         {
     276          23 :             ++nCharacterCount;
     277          23 :             if (nCharacterCount == maxCharCount)
     278             :             {
     279           7 :                 osStr.resize(i + 1);
     280           7 :                 break;
     281             :             }
     282             :         }
     283             :     }
     284           7 : }
     285             : 
     286             : /************************************************************************/
     287             : /*              GDALVectorRenameLayerAlgorithm::RunStep()               */
     288             : /************************************************************************/
     289             : 
     290          17 : bool GDALVectorRenameLayerAlgorithm::RunStep(GDALPipelineStepRunContext &)
     291             : {
     292          17 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
     293          17 :     CPLAssert(poSrcDS);
     294             : 
     295          17 :     CPLAssert(m_outputDataset.GetName().empty());
     296          17 :     CPLAssert(!m_outputDataset.GetDatasetRef());
     297             : 
     298             :     // First pass over layer names to create new layer names matching specified
     299             :     // constraints
     300          34 :     std::vector<std::string> aosNames;
     301          34 :     std::map<std::string, int> oMapCountNames;
     302          17 :     bool bNonUniqueNames = false;
     303          17 :     const int nLayerCount = poSrcDS->GetLayerCount();
     304          52 :     for (int i = 0; i < nLayerCount; ++i)
     305             :     {
     306          35 :         const OGRLayer *poSrcLayer = poSrcDS->GetLayer(i);
     307          66 :         if ((m_inputLayerName == poSrcLayer->GetDescription() ||
     308          70 :              nLayerCount == 1) &&
     309          14 :             !m_outputLayerName.empty())
     310             :         {
     311           8 :             aosNames.push_back(m_outputLayerName);
     312             :         }
     313             :         else
     314             :         {
     315          54 :             std::string osName(poSrcLayer->GetDescription());
     316          27 :             if (!m_reservedChars.empty())
     317             :             {
     318           2 :                 std::string osNewName;
     319          11 :                 for (char c : osName)
     320             :                 {
     321          10 :                     if (m_reservedChars.find(c) != std::string::npos)
     322             :                     {
     323           1 :                         if (!m_replacementChar.empty())
     324           1 :                             osNewName += m_replacementChar;
     325             :                     }
     326             :                     else
     327             :                     {
     328           9 :                         osNewName += c;
     329             :                     }
     330             :                 }
     331           1 :                 osName = std::move(osNewName);
     332             :             }
     333          27 :             if (m_filenameCompatible)
     334             :             {
     335           1 :                 osName = CPLLaunderForFilenameSafe(
     336           1 :                     osName, m_replacementChar.c_str()[0]);
     337             :             }
     338          27 :             if (m_ascii)
     339             :             {
     340           2 :                 char *pszStr = CPLUTF8ForceToASCII(
     341           2 :                     osName.c_str(), m_replacementChar.c_str()[0]);
     342           2 :                 osName = pszStr;
     343           2 :                 CPLFree(pszStr);
     344             :             }
     345          27 :             if (m_lowerCase)
     346             :             {
     347          10 :                 for (char &c : osName)
     348             :                 {
     349           9 :                     if (c >= 'A' && c <= 'Z')
     350           9 :                         c = c - 'A' + 'a';
     351             :                 }
     352             :             }
     353          27 :             if (m_maxLength > 0)
     354             :             {
     355           5 :                 TruncateUTF8ToMaxChar(osName, m_maxLength);
     356             :             }
     357          27 :             if (++oMapCountNames[osName] > 1)
     358           3 :                 bNonUniqueNames = true;
     359          27 :             aosNames.push_back(std::move(osName));
     360             :         }
     361             :     }
     362             : 
     363             :     // Extra optional pass if some names are not unique
     364          17 :     if (bNonUniqueNames)
     365             :     {
     366           6 :         std::map<std::string, int> oMapCurCounter;
     367           3 :         bool bUniquenessPossible = true;
     368           9 :         for (auto &osName : aosNames)
     369             :         {
     370           6 :             const int nCountForName = oMapCountNames[osName];
     371           6 :             if (nCountForName > 1)
     372             :             {
     373           6 :                 const int nCounter = ++oMapCurCounter[osName];
     374          12 :                 std::string osSuffix("_");
     375           6 :                 if (nCountForName <= 9)
     376           6 :                     osSuffix += CPLSPrintf("%d", nCounter);
     377           0 :                 else if (nCountForName <= 99)
     378           0 :                     osSuffix += CPLSPrintf("%02d", nCounter);
     379             :                 else
     380           0 :                     osSuffix += CPLSPrintf("%03d", nCounter);
     381           6 :                 const size_t nNameLen = CPLStrlenUTF8Ex(osName.c_str());
     382          10 :                 if (m_maxLength > 0 && nNameLen + osSuffix.size() >
     383           4 :                                            static_cast<size_t>(m_maxLength))
     384             :                 {
     385           4 :                     if (nNameLen > osSuffix.size())
     386             :                     {
     387           2 :                         TruncateUTF8ToMaxChar(osName,
     388           2 :                                               nNameLen - osSuffix.size());
     389           2 :                         osName += osSuffix;
     390             :                     }
     391           2 :                     else if (bUniquenessPossible)
     392             :                     {
     393           1 :                         ReportError(CE_Warning, CPLE_AppDefined,
     394             :                                     "Cannot create unique name for '%s' while "
     395             :                                     "respecting %d maximum length",
     396             :                                     osName.c_str(), m_maxLength);
     397           1 :                         bUniquenessPossible = false;
     398             :                     }
     399             :                 }
     400             :                 else
     401             :                 {
     402           2 :                     osName += osSuffix;
     403             :                 }
     404             :             }
     405             :         }
     406             :     }
     407             : 
     408             :     auto outDS = std::make_unique<GDALVectorRenameLayerAlgorithmDataset>(
     409          17 :         *poSrcDS, aosNames);
     410             : 
     411             :     // Final pass to create output layers
     412          52 :     for (int i = 0; i < nLayerCount; ++i)
     413             :     {
     414          35 :         OGRLayer *poSrcLayer = poSrcDS->GetLayer(i);
     415          35 :         if (poSrcLayer->GetDescription() != aosNames[i])
     416             :         {
     417             :             auto poLayer =
     418             :                 std::make_unique<GDALVectorRenameLayerAlgorithmLayer>(
     419          20 :                     *poSrcLayer, aosNames[i]);
     420          20 :             outDS->AddLayer(*poSrcLayer, std::move(poLayer));
     421             :         }
     422             :         else
     423             :         {
     424          30 :             outDS->AddLayer(
     425             :                 *poSrcLayer,
     426          30 :                 std::make_unique<GDALVectorPipelinePassthroughLayer>(
     427             :                     *poSrcLayer));
     428             :         }
     429             :     }
     430             : 
     431          17 :     m_outputDataset.Set(std::move(outDS));
     432             : 
     433          34 :     return true;
     434             : }
     435             : 
     436             : GDALVectorRenameLayerAlgorithmStandalone::
     437             :     ~GDALVectorRenameLayerAlgorithmStandalone() = default;
     438             : 
     439             : //! @endcond

Generated by: LCOV version 1.14