Coverage Report

Created: 2026-04-10 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_vector_edit.cpp
Line
Count
Source
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
0
#define _(x) (x)
22
#endif
23
24
/************************************************************************/
25
/*          GDALVectorEditAlgorithm::GDALVectorEditAlgorithm()          */
26
/************************************************************************/
27
28
GDALVectorEditAlgorithm::GDALVectorEditAlgorithm(bool standaloneStep)
29
0
    : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
30
0
                                      standaloneStep)
31
0
{
32
0
    AddActiveLayerArg(&m_activeLayer);
33
0
    AddGeometryTypeArg(&m_geometryType, _("Layer geometry type"));
34
35
0
    AddArg("crs", 0, _("Override CRS (without reprojection)"), &m_overrideCrs)
36
0
        .AddHiddenAlias("a_srs")
37
0
        .SetIsCRSArg(/*noneAllowed=*/true);
38
39
0
    {
40
0
        auto &arg = AddArg("metadata", 0, _("Add/update dataset metadata item"),
41
0
                           &m_metadata)
42
0
                        .SetMetaVar("<KEY>=<VALUE>")
43
0
                        .SetPackedValuesAllowed(false);
44
0
        arg.AddValidationAction([this, &arg]()
45
0
                                { return ParseAndValidateKeyValue(arg); });
46
0
        arg.AddHiddenAlias("mo");
47
0
    }
48
49
0
    AddArg("unset-metadata", 0, _("Remove dataset metadata item"),
50
0
           &m_unsetMetadata)
51
0
        .SetMetaVar("<KEY>");
52
53
0
    {
54
0
        auto &arg =
55
0
            AddArg("layer-metadata", 0, _("Add/update layer metadata item"),
56
0
                   &m_layerMetadata)
57
0
                .SetMetaVar("<KEY>=<VALUE>")
58
0
                .SetPackedValuesAllowed(false);
59
0
        arg.AddValidationAction([this, &arg]()
60
0
                                { return ParseAndValidateKeyValue(arg); });
61
0
    }
62
63
0
    AddArg("unset-layer-metadata", 0, _("Remove layer metadata item"),
64
0
           &m_unsetLayerMetadata)
65
0
        .SetMetaVar("<KEY>");
66
67
0
    AddArg("unset-fid", 0,
68
0
           _("Unset the identifier of each feature and the FID column name"),
69
0
           &m_unsetFID);
70
0
}
71
72
namespace
73
{
74
75
/************************************************************************/
76
/*                     GDALVectorEditAlgorithmLayer                     */
77
/************************************************************************/
78
79
class GDALVectorEditAlgorithmLayer final : public GDALVectorPipelineOutputLayer
80
{
81
  public:
82
    GDALVectorEditAlgorithmLayer(
83
        OGRLayer &oSrcLayer, const std::string &activeLayer,
84
        bool bChangeGeomType, OGRwkbGeometryType eType,
85
        const std::string &overrideCrs,
86
        const std::vector<std::string> &layerMetadata,
87
        const std::vector<std::string> &unsetLayerMetadata, bool unsetFID)
88
0
        : GDALVectorPipelineOutputLayer(oSrcLayer),
89
0
          m_bOverrideCrs(!overrideCrs.empty()), m_unsetFID(unsetFID),
90
0
          m_poFeatureDefn(oSrcLayer.GetLayerDefn()->Clone())
91
0
    {
92
0
        SetDescription(oSrcLayer.GetDescription());
93
0
        SetMetadata(oSrcLayer.GetMetadata());
94
95
0
        if (activeLayer.empty() || activeLayer == GetDescription())
96
0
        {
97
0
            const CPLStringList aosMD(layerMetadata);
98
0
            for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
99
0
            {
100
0
                if (SetMetadataItem(key, value) != CE_None)
101
0
                {
102
0
                    CPLError(CE_Warning, CPLE_AppDefined,
103
0
                             "SetMetadataItem('%s', '%s') failed", key, value);
104
0
                }
105
0
            }
106
107
0
            for (const std::string &key : unsetLayerMetadata)
108
0
            {
109
0
                if (SetMetadataItem(key.c_str(), nullptr) != CE_None)
110
0
                {
111
0
                    CPLError(CE_Warning, CPLE_AppDefined,
112
0
                             "SetMetadataItem('%s', NULL) failed", key.c_str());
113
0
                }
114
0
            }
115
116
0
            if (bChangeGeomType)
117
0
            {
118
0
                for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
119
0
                {
120
0
                    m_poFeatureDefn->GetGeomFieldDefn(i)->SetType(eType);
121
0
                }
122
0
            }
123
124
0
            if (!overrideCrs.empty())
125
0
            {
126
0
                if (!EQUAL(overrideCrs.c_str(), "null") &&
127
0
                    !EQUAL(overrideCrs.c_str(), "none"))
128
0
                {
129
0
                    m_poSRS = OGRSpatialReferenceRefCountedPtr::makeInstance();
130
0
                    m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
131
                    // already checked by GDALAlgorithmArg framework
132
0
                    CPL_IGNORE_RET_VAL(
133
0
                        m_poSRS->SetFromUserInput(overrideCrs.c_str()));
134
0
                }
135
0
                for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
136
0
                {
137
0
                    m_poFeatureDefn->GetGeomFieldDefn(i)->SetSpatialRef(
138
0
                        m_poSRS.get());
139
0
                }
140
0
            }
141
0
        }
142
0
    }
143
144
    const char *GetFIDColumn() const override
145
0
    {
146
0
        if (m_unsetFID)
147
0
            return "";
148
0
        return m_srcLayer.GetFIDColumn();
149
0
    }
150
151
    const OGRFeatureDefn *GetLayerDefn() const override
152
0
    {
153
0
        return m_poFeatureDefn.get();
154
0
    }
155
156
    void TranslateFeature(
157
        std::unique_ptr<OGRFeature> poSrcFeature,
158
        std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures) override
159
0
    {
160
0
        poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn.get());
161
0
        if (m_bOverrideCrs)
162
0
        {
163
0
            for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
164
0
            {
165
0
                auto poGeom = poSrcFeature->GetGeomFieldRef(i);
166
0
                if (poGeom)
167
0
                    poGeom->assignSpatialReference(m_poSRS.get());
168
0
            }
169
0
        }
170
0
        if (m_unsetFID)
171
0
            poSrcFeature->SetFID(OGRNullFID);
172
0
        apoOutFeatures.push_back(std::move(poSrcFeature));
173
0
    }
174
175
    int TestCapability(const char *pszCap) const override
176
0
    {
177
0
        if (EQUAL(pszCap, OLCStringsAsUTF8) ||
178
0
            EQUAL(pszCap, OLCCurveGeometries) || EQUAL(pszCap, OLCZGeometries))
179
0
            return m_srcLayer.TestCapability(pszCap);
180
0
        return false;
181
0
    }
182
183
  private:
184
    const bool m_bOverrideCrs;
185
    const bool m_unsetFID;
186
    const OGRFeatureDefnRefCountedPtr m_poFeatureDefn;
187
    OGRSpatialReferenceRefCountedPtr m_poSRS{};
188
189
    CPL_DISALLOW_COPY_ASSIGN(GDALVectorEditAlgorithmLayer)
190
};
191
192
/************************************************************************/
193
/*                     GDALVectorEditOutputDataset                      */
194
/************************************************************************/
195
196
class GDALVectorEditOutputDataset final : public GDALVectorPipelineOutputDataset
197
{
198
  public:
199
    explicit GDALVectorEditOutputDataset(GDALDataset &oSrcDS)
200
0
        : GDALVectorPipelineOutputDataset(oSrcDS)
201
0
    {
202
0
    }
203
204
    CSLConstList GetMetadata(const char *pszDomain) override;
205
206
    const char *GetMetadataItem(const char *pszName,
207
                                const char *pszDomain) override
208
0
    {
209
0
        if (!pszDomain || pszDomain[0] == 0)
210
0
            return GDALDataset::GetMetadataItem(pszName, pszDomain);
211
0
        return m_srcDS.GetMetadataItem(pszName, pszDomain);
212
0
    }
213
};
214
215
CSLConstList GDALVectorEditOutputDataset::GetMetadata(const char *pszDomain)
216
0
{
217
0
    if (!pszDomain || pszDomain[0] == 0)
218
0
        return GDALDataset::GetMetadata(pszDomain);
219
0
    return m_srcDS.GetMetadata(pszDomain);
220
0
}
221
222
}  // namespace
223
224
/************************************************************************/
225
/*                  GDALVectorEditAlgorithm::RunStep()                  */
226
/************************************************************************/
227
228
bool GDALVectorEditAlgorithm::RunStep(GDALPipelineStepRunContext &)
229
0
{
230
0
    auto poSrcDS = m_inputDataset[0].GetDatasetRef();
231
0
    CPLAssert(poSrcDS);
232
233
0
    CPLAssert(m_outputDataset.GetName().empty());
234
0
    CPLAssert(!m_outputDataset.GetDatasetRef());
235
236
0
    const int nLayerCount = poSrcDS->GetLayerCount();
237
238
0
    bool bChangeGeomType = false;
239
0
    OGRwkbGeometryType eType = wkbUnknown;
240
0
    if (!m_geometryType.empty())
241
0
    {
242
0
        eType = OGRFromOGCGeomType(m_geometryType.c_str());
243
0
        bChangeGeomType = true;
244
0
    }
245
246
0
    auto outDS = std::make_unique<GDALVectorEditOutputDataset>(*poSrcDS);
247
248
0
    outDS->SetMetadata(poSrcDS->GetMetadata());
249
250
0
    const CPLStringList aosMD(m_metadata);
251
0
    for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
252
0
    {
253
0
        if (outDS->SetMetadataItem(key, value) != CE_None)
254
0
        {
255
0
            ReportError(CE_Failure, CPLE_AppDefined,
256
0
                        "SetMetadataItem('%s', '%s') failed", key, value);
257
0
            return false;
258
0
        }
259
0
    }
260
261
0
    for (const std::string &key : m_unsetMetadata)
262
0
    {
263
0
        if (outDS->SetMetadataItem(key.c_str(), nullptr) != CE_None)
264
0
        {
265
0
            ReportError(CE_Failure, CPLE_AppDefined,
266
0
                        "SetMetadataItem('%s', NULL) failed", key.c_str());
267
0
            return false;
268
0
        }
269
0
    }
270
271
0
    bool ret = true;
272
0
    for (int i = 0; ret && i < nLayerCount; ++i)
273
0
    {
274
0
        auto poSrcLayer = poSrcDS->GetLayer(i);
275
0
        ret = (poSrcLayer != nullptr);
276
0
        if (ret)
277
0
        {
278
0
            outDS->AddLayer(*poSrcLayer,
279
0
                            std::make_unique<GDALVectorEditAlgorithmLayer>(
280
0
                                *poSrcLayer, m_activeLayer, bChangeGeomType,
281
0
                                eType, m_overrideCrs, m_layerMetadata,
282
0
                                m_unsetLayerMetadata, m_unsetFID));
283
0
        }
284
0
    }
285
286
0
    if (ret)
287
0
        m_outputDataset.Set(std::move(outDS));
288
289
0
    return ret;
290
0
}
291
292
0
GDALVectorEditAlgorithmStandalone::~GDALVectorEditAlgorithmStandalone() =
293
    default;
294
295
//! @endcond