LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_edit.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 128 140 91.4 %
Date: 2026-10-22 01:45:52 Functions: 12 13 92.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  "edit" step of "vector pipeline"
       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_edit.h"
      14             : 
      15             : #include "gdal_priv.h"
      16             : #include "gdal_utils.h"
      17             : 
      18             : //! @cond Doxygen_Suppress
      19             : 
      20             : #ifndef _
      21             : #define _(x) (x)
      22             : #endif
      23             : 
      24             : /************************************************************************/
      25             : /*          GDALVectorEditAlgorithm::GDALVectorEditAlgorithm()          */
      26             : /************************************************************************/
      27             : 
      28         122 : GDALVectorEditAlgorithm::GDALVectorEditAlgorithm(bool standaloneStep)
      29             :     : GDALVectorPipelineStepAlgorithm(
      30             :           NAME, DESCRIPTION, HELP_URL,
      31           0 :           ConstructorOptions()
      32         122 :               .SetStandaloneStep(standaloneStep)
      33         244 :               .SetOutputLayerNameAvailableInPipelineStep(true))
      34             : {
      35         122 :     AddActiveLayerArg(&m_activeLayer);
      36         122 :     if (!standaloneStep)
      37             :     {
      38          66 :         AddOutputLayerNameArg(/* hiddenForCLI = */ false,
      39             :                               /* shortNameOutputLayerAllowed = */ false);
      40             :     }
      41             : 
      42         122 :     AddGeometryTypeArg(&m_geometryType, _("Layer geometry type"));
      43             : 
      44         244 :     AddArg("crs", 0, _("Override CRS (without reprojection)"), &m_overrideCrs)
      45         244 :         .AddHiddenAlias("a_srs")
      46         122 :         .SetIsCRSArg(/*noneAllowed=*/true);
      47             : 
      48             :     {
      49             :         auto &arg = AddArg("metadata", 0, _("Add/update dataset metadata item"),
      50         244 :                            &m_metadata)
      51         244 :                         .SetMetaVar("<KEY>=<VALUE>")
      52         122 :                         .SetPackedValuesAllowed(false);
      53           9 :         arg.AddValidationAction([this, &arg]()
      54         131 :                                 { return ParseAndValidateKeyValue(arg); });
      55         122 :         arg.AddHiddenAlias("mo");
      56             :     }
      57             : 
      58             :     AddArg("unset-metadata", 0, _("Remove dataset metadata item"),
      59         244 :            &m_unsetMetadata)
      60         122 :         .SetMetaVar("<KEY>");
      61             : 
      62             :     {
      63             :         auto &arg =
      64             :             AddArg("layer-metadata", 0, _("Add/update layer metadata item"),
      65         244 :                    &m_layerMetadata)
      66         244 :                 .SetMetaVar("<KEY>=<VALUE>")
      67         122 :                 .SetPackedValuesAllowed(false);
      68           6 :         arg.AddValidationAction([this, &arg]()
      69         128 :                                 { return ParseAndValidateKeyValue(arg); });
      70             :     }
      71             : 
      72             :     AddArg("unset-layer-metadata", 0, _("Remove layer metadata item"),
      73         244 :            &m_unsetLayerMetadata)
      74         122 :         .SetMetaVar("<KEY>");
      75             : 
      76             :     AddArg("unset-fid", 0,
      77             :            _("Unset the identifier of each feature and the FID column name"),
      78         122 :            &m_unsetFID);
      79             : 
      80         122 :     AddValidationAction(
      81          62 :         [this]()
      82             :         {
      83          53 :             if (!m_outputLayerName.empty() && m_activeLayer.empty() &&
      84           4 :                 m_inputDataset.size() == 1)
      85             :             {
      86           2 :                 auto poSrcDS = m_inputDataset[0].GetDatasetRef();
      87           2 :                 if (poSrcDS && poSrcDS->GetLayerCount() > 1)
      88             :                 {
      89           1 :                     ReportError(CE_Failure, CPLE_IllegalArg,
      90             :                                 "Argument 'output-layer' cannot be used when "
      91             :                                 "the input dataset has multiple layers, unless "
      92             :                                 "argument 'active-layer' is specified");
      93           1 :                     return false;
      94             :                 }
      95             :             }
      96          48 :             return true;
      97             :         });
      98         122 : }
      99             : 
     100             : namespace
     101             : {
     102             : 
     103             : /************************************************************************/
     104             : /*                     GDALVectorEditAlgorithmLayer                     */
     105             : /************************************************************************/
     106             : 
     107             : class GDALVectorEditAlgorithmLayer final : public GDALVectorPipelineOutputLayer
     108             : {
     109             :   public:
     110          23 :     GDALVectorEditAlgorithmLayer(
     111             :         OGRLayer &oSrcLayer, const std::string &activeLayer,
     112             :         const std::string &outputLayerName, bool bChangeGeomType,
     113             :         OGRwkbGeometryType eType, const std::string &overrideCrs,
     114             :         const std::vector<std::string> &layerMetadata,
     115             :         const std::vector<std::string> &unsetLayerMetadata, bool unsetFID)
     116          23 :         : GDALVectorPipelineOutputLayer(oSrcLayer),
     117          46 :           m_bOverrideCrs(!overrideCrs.empty()), m_unsetFID(unsetFID),
     118          23 :           m_poFeatureDefn(oSrcLayer.GetLayerDefn()->Clone())
     119             :     {
     120          23 :         SetMetadata(oSrcLayer.GetMetadata());
     121             : 
     122          23 :         if (activeLayer.empty() || activeLayer == oSrcLayer.GetDescription())
     123             :         {
     124          21 :             if (!outputLayerName.empty())
     125             :             {
     126           2 :                 m_poFeatureDefn->SetName(outputLayerName.c_str());
     127             :             }
     128             : 
     129          42 :             const CPLStringList aosMD(layerMetadata);
     130          23 :             for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
     131             :             {
     132           2 :                 if (SetMetadataItem(key, value) != CE_None)
     133             :                 {
     134           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     135             :                              "SetMetadataItem('%s', '%s') failed", key, value);
     136             :                 }
     137             :             }
     138             : 
     139          22 :             for (const std::string &key : unsetLayerMetadata)
     140             :             {
     141           1 :                 if (SetMetadataItem(key.c_str(), nullptr) != CE_None)
     142             :                 {
     143           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     144             :                              "SetMetadataItem('%s', NULL) failed", key.c_str());
     145             :                 }
     146             :             }
     147             : 
     148          21 :             if (bChangeGeomType)
     149             :             {
     150           6 :                 for (auto *poGeomFieldDefns : m_poFeatureDefn->GetGeomFields())
     151             :                 {
     152           3 :                     poGeomFieldDefns->SetType(eType);
     153             :                 }
     154             :             }
     155             : 
     156          21 :             if (!overrideCrs.empty())
     157             :             {
     158           4 :                 if (!EQUAL(overrideCrs.c_str(), "null") &&
     159           2 :                     !EQUAL(overrideCrs.c_str(), "none"))
     160             :                 {
     161           1 :                     m_poSRS = OGRSpatialReferenceRefCountedPtr::makeInstance();
     162           1 :                     m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     163             :                     // already checked by GDALAlgorithmArg framework
     164           1 :                     CPL_IGNORE_RET_VAL(
     165           1 :                         m_poSRS->SetFromUserInput(overrideCrs.c_str()));
     166             :                 }
     167           4 :                 for (auto *poGeomFieldDefns : m_poFeatureDefn->GetGeomFields())
     168             :                 {
     169           2 :                     poGeomFieldDefns->SetSpatialRef(m_poSRS.get());
     170             :                 }
     171             :             }
     172             :         }
     173             : 
     174          23 :         SetDescription(m_poFeatureDefn->GetName());
     175          23 :     }
     176             : 
     177          25 :     const char *GetFIDColumn() const override
     178             :     {
     179          25 :         if (m_unsetFID)
     180           2 :             return "";
     181          23 :         return m_srcLayer.GetFIDColumn();
     182             :     }
     183             : 
     184          96 :     const OGRFeatureDefn *GetLayerDefn() const override
     185             :     {
     186          96 :         return m_poFeatureDefn.get();
     187             :     }
     188             : 
     189         312 :     bool TranslateFeature(
     190             :         std::unique_ptr<OGRFeature> poSrcFeature,
     191             :         std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures) override
     192             :     {
     193         312 :         poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn.get());
     194         312 :         if (m_bOverrideCrs)
     195             :         {
     196           4 :             for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
     197             :             {
     198           2 :                 auto poGeom = poSrcFeature->GetGeomFieldRef(i);
     199           2 :                 if (poGeom)
     200           2 :                     poGeom->assignSpatialReference(m_poSRS.get());
     201             :             }
     202             :         }
     203         312 :         if (m_unsetFID)
     204           1 :             poSrcFeature->SetFID(OGRNullFID);
     205         312 :         if (PassesFilters(poSrcFeature.get()))
     206         310 :             apoOutFeatures.push_back(std::move(poSrcFeature));
     207             : 
     208         312 :         return true;
     209             :     }
     210             : 
     211          13 :     bool TestCapability(const char *pszCap) const override
     212             :     {
     213          13 :         if (EQUAL(pszCap, OLCStringsAsUTF8) ||
     214          12 :             EQUAL(pszCap, OLCCurveGeometries) || EQUAL(pszCap, OLCZGeometries))
     215           1 :             return m_srcLayer.TestCapability(pszCap);
     216          12 :         return false;
     217             :     }
     218             : 
     219             :   private:
     220             :     const bool m_bOverrideCrs;
     221             :     const bool m_unsetFID;
     222             :     const OGRFeatureDefnRefCountedPtr m_poFeatureDefn;
     223             :     OGRSpatialReferenceRefCountedPtr m_poSRS{};
     224             : 
     225             :     CPL_DISALLOW_COPY_ASSIGN(GDALVectorEditAlgorithmLayer)
     226             : };
     227             : 
     228             : /************************************************************************/
     229             : /*                     GDALVectorEditOutputDataset                      */
     230             : /************************************************************************/
     231             : 
     232             : class GDALVectorEditOutputDataset final : public GDALVectorPipelineOutputDataset
     233             : {
     234             :   public:
     235          22 :     explicit GDALVectorEditOutputDataset(GDALDataset &oSrcDS)
     236          22 :         : GDALVectorPipelineOutputDataset(oSrcDS)
     237             :     {
     238          22 :     }
     239             : 
     240             :     CSLConstList GetMetadata(const char *pszDomain) override;
     241             : 
     242           0 :     const char *GetMetadataItem(const char *pszName,
     243             :                                 const char *pszDomain) override
     244             :     {
     245           0 :         if (!pszDomain || pszDomain[0] == 0)
     246           0 :             return GDALDataset::GetMetadataItem(pszName, pszDomain);
     247           0 :         return m_srcDS.GetMetadataItem(pszName, pszDomain);
     248             :     }
     249             : };
     250             : 
     251          11 : CSLConstList GDALVectorEditOutputDataset::GetMetadata(const char *pszDomain)
     252             : {
     253          11 :     if (!pszDomain || pszDomain[0] == 0)
     254          11 :         return GDALDataset::GetMetadata(pszDomain);
     255           0 :     return m_srcDS.GetMetadata(pszDomain);
     256             : }
     257             : 
     258             : }  // namespace
     259             : 
     260             : /************************************************************************/
     261             : /*                  GDALVectorEditAlgorithm::RunStep()                  */
     262             : /************************************************************************/
     263             : 
     264          22 : bool GDALVectorEditAlgorithm::RunStep(GDALPipelineStepRunContext &)
     265             : {
     266          22 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
     267          22 :     CPLAssert(poSrcDS);
     268             : 
     269          22 :     CPLAssert(m_outputDataset.GetName().empty());
     270          22 :     CPLAssert(!m_outputDataset.GetDatasetRef());
     271             : 
     272          22 :     const int nLayerCount = poSrcDS->GetLayerCount();
     273             : 
     274          22 :     bool bChangeGeomType = false;
     275          22 :     OGRwkbGeometryType eType = wkbUnknown;
     276          22 :     if (!m_geometryType.empty())
     277             :     {
     278           3 :         eType = OGRFromOGCGeomType(m_geometryType.c_str());
     279           3 :         bChangeGeomType = true;
     280             :     }
     281             : 
     282          44 :     auto outDS = std::make_unique<GDALVectorEditOutputDataset>(*poSrcDS);
     283             : 
     284          22 :     outDS->SetMetadata(poSrcDS->GetMetadata());
     285             : 
     286          44 :     const CPLStringList aosMD(m_metadata);
     287          25 :     for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
     288             :     {
     289           3 :         if (outDS->SetMetadataItem(key, value) != CE_None)
     290             :         {
     291           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     292             :                         "SetMetadataItem('%s', '%s') failed", key, value);
     293           0 :             return false;
     294             :         }
     295             :     }
     296             : 
     297          23 :     for (const std::string &key : m_unsetMetadata)
     298             :     {
     299           1 :         if (outDS->SetMetadataItem(key.c_str(), nullptr) != CE_None)
     300             :         {
     301           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     302             :                         "SetMetadataItem('%s', NULL) failed", key.c_str());
     303           0 :             return false;
     304             :         }
     305             :     }
     306             : 
     307          22 :     bool ret = true;
     308          46 :     for (int i = 0; ret && i < nLayerCount; ++i)
     309             :     {
     310          24 :         auto poSrcLayer = poSrcDS->GetLayer(i);
     311          24 :         ret = (poSrcLayer != nullptr);
     312          24 :         if (ret)
     313             :         {
     314             :             // Capture errors from VRT layers such as WFS
     315             :             // (see issue GH # https://github.com/OSGeo/gdal/issues/14826)
     316          24 :             const auto errorCount{CPLGetErrorCounter()};
     317             :             // Force layer definition to be read to trigger errors
     318          24 :             poSrcLayer->GetLayerDefn();
     319          24 :             if (CPLGetErrorCounter() != errorCount)
     320             :             {
     321           1 :                 ret = false;
     322             :             }
     323             :             else
     324             :             {
     325          46 :                 outDS->AddLayer(*poSrcLayer,
     326          23 :                                 std::make_unique<GDALVectorEditAlgorithmLayer>(
     327          23 :                                     *poSrcLayer, m_activeLayer,
     328          23 :                                     m_outputLayerName, bChangeGeomType, eType,
     329          23 :                                     m_overrideCrs, m_layerMetadata,
     330          23 :                                     m_unsetLayerMetadata, m_unsetFID));
     331             :             }
     332             :         }
     333             :     }
     334             : 
     335          22 :     if (ret)
     336          21 :         m_outputDataset.Set(std::move(outDS));
     337             : 
     338          22 :     return ret;
     339             : }
     340             : 
     341             : GDALVectorEditAlgorithmStandalone::~GDALVectorEditAlgorithmStandalone() =
     342             :     default;
     343             : 
     344             : //! @endcond

Generated by: LCOV version 1.14