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

Generated by: LCOV version 1.14