Coverage Report

Created: 2026-04-01 06:20

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
    {
91
0
        SetDescription(oSrcLayer.GetDescription());
92
0
        SetMetadata(oSrcLayer.GetMetadata());
93
94
0
        m_poFeatureDefn = oSrcLayer.GetLayerDefn()->Clone();
95
0
        m_poFeatureDefn->Reference();
96
97
0
        if (activeLayer.empty() || activeLayer == GetDescription())
98
0
        {
99
0
            const CPLStringList aosMD(layerMetadata);
100
0
            for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
101
0
            {
102
0
                if (SetMetadataItem(key, value) != CE_None)
103
0
                {
104
0
                    CPLError(CE_Warning, CPLE_AppDefined,
105
0
                             "SetMetadataItem('%s', '%s') failed", key, value);
106
0
                }
107
0
            }
108
109
0
            for (const std::string &key : unsetLayerMetadata)
110
0
            {
111
0
                if (SetMetadataItem(key.c_str(), nullptr) != CE_None)
112
0
                {
113
0
                    CPLError(CE_Warning, CPLE_AppDefined,
114
0
                             "SetMetadataItem('%s', NULL) failed", key.c_str());
115
0
                }
116
0
            }
117
118
0
            if (bChangeGeomType)
119
0
            {
120
0
                for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
121
0
                {
122
0
                    m_poFeatureDefn->GetGeomFieldDefn(i)->SetType(eType);
123
0
                }
124
0
            }
125
126
0
            if (!overrideCrs.empty())
127
0
            {
128
0
                if (!EQUAL(overrideCrs.c_str(), "null") &&
129
0
                    !EQUAL(overrideCrs.c_str(), "none"))
130
0
                {
131
0
                    m_poSRS = new OGRSpatialReference();
132
0
                    m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
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);
139
0
                }
140
0
            }
141
0
        }
142
0
    }
143
144
    ~GDALVectorEditAlgorithmLayer() override
145
0
    {
146
0
        m_poFeatureDefn->Release();
147
0
        if (m_poSRS)
148
0
            m_poSRS->Release();
149
0
    }
150
151
    const char *GetFIDColumn() const override
152
0
    {
153
0
        if (m_unsetFID)
154
0
            return "";
155
0
        return m_srcLayer.GetFIDColumn();
156
0
    }
157
158
    const OGRFeatureDefn *GetLayerDefn() const override
159
0
    {
160
0
        return m_poFeatureDefn;
161
0
    }
162
163
    void TranslateFeature(
164
        std::unique_ptr<OGRFeature> poSrcFeature,
165
        std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures) override
166
0
    {
167
0
        poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn);
168
0
        if (m_bOverrideCrs)
169
0
        {
170
0
            for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
171
0
            {
172
0
                auto poGeom = poSrcFeature->GetGeomFieldRef(i);
173
0
                if (poGeom)
174
0
                    poGeom->assignSpatialReference(m_poSRS);
175
0
            }
176
0
        }
177
0
        if (m_unsetFID)
178
0
            poSrcFeature->SetFID(OGRNullFID);
179
0
        apoOutFeatures.push_back(std::move(poSrcFeature));
180
0
    }
181
182
    int TestCapability(const char *pszCap) const override
183
0
    {
184
0
        if (EQUAL(pszCap, OLCStringsAsUTF8) ||
185
0
            EQUAL(pszCap, OLCCurveGeometries) || EQUAL(pszCap, OLCZGeometries))
186
0
            return m_srcLayer.TestCapability(pszCap);
187
0
        return false;
188
0
    }
189
190
  private:
191
    const bool m_bOverrideCrs;
192
    const bool m_unsetFID;
193
    OGRFeatureDefn *m_poFeatureDefn = nullptr;
194
    OGRSpatialReference *m_poSRS = nullptr;
195
196
    CPL_DISALLOW_COPY_ASSIGN(GDALVectorEditAlgorithmLayer)
197
};
198
199
/************************************************************************/
200
/*                     GDALVectorEditOutputDataset                      */
201
/************************************************************************/
202
203
class GDALVectorEditOutputDataset final : public GDALVectorPipelineOutputDataset
204
{
205
  public:
206
    explicit GDALVectorEditOutputDataset(GDALDataset &oSrcDS)
207
0
        : GDALVectorPipelineOutputDataset(oSrcDS)
208
0
    {
209
0
    }
210
211
    CSLConstList GetMetadata(const char *pszDomain) override;
212
213
    const char *GetMetadataItem(const char *pszName,
214
                                const char *pszDomain) override
215
0
    {
216
0
        if (!pszDomain || pszDomain[0] == 0)
217
0
            return GDALDataset::GetMetadataItem(pszName, pszDomain);
218
0
        return m_srcDS.GetMetadataItem(pszName, pszDomain);
219
0
    }
220
};
221
222
CSLConstList GDALVectorEditOutputDataset::GetMetadata(const char *pszDomain)
223
0
{
224
0
    if (!pszDomain || pszDomain[0] == 0)
225
0
        return GDALDataset::GetMetadata(pszDomain);
226
0
    return m_srcDS.GetMetadata(pszDomain);
227
0
}
228
229
}  // namespace
230
231
/************************************************************************/
232
/*                  GDALVectorEditAlgorithm::RunStep()                  */
233
/************************************************************************/
234
235
bool GDALVectorEditAlgorithm::RunStep(GDALPipelineStepRunContext &)
236
0
{
237
0
    auto poSrcDS = m_inputDataset[0].GetDatasetRef();
238
0
    CPLAssert(poSrcDS);
239
240
0
    CPLAssert(m_outputDataset.GetName().empty());
241
0
    CPLAssert(!m_outputDataset.GetDatasetRef());
242
243
0
    const int nLayerCount = poSrcDS->GetLayerCount();
244
245
0
    bool bChangeGeomType = false;
246
0
    OGRwkbGeometryType eType = wkbUnknown;
247
0
    if (!m_geometryType.empty())
248
0
    {
249
0
        eType = OGRFromOGCGeomType(m_geometryType.c_str());
250
0
        bChangeGeomType = true;
251
0
    }
252
253
0
    auto outDS = std::make_unique<GDALVectorEditOutputDataset>(*poSrcDS);
254
255
0
    outDS->SetMetadata(poSrcDS->GetMetadata());
256
257
0
    const CPLStringList aosMD(m_metadata);
258
0
    for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
259
0
    {
260
0
        if (outDS->SetMetadataItem(key, value) != CE_None)
261
0
        {
262
0
            ReportError(CE_Failure, CPLE_AppDefined,
263
0
                        "SetMetadataItem('%s', '%s') failed", key, value);
264
0
            return false;
265
0
        }
266
0
    }
267
268
0
    for (const std::string &key : m_unsetMetadata)
269
0
    {
270
0
        if (outDS->SetMetadataItem(key.c_str(), nullptr) != CE_None)
271
0
        {
272
0
            ReportError(CE_Failure, CPLE_AppDefined,
273
0
                        "SetMetadataItem('%s', NULL) failed", key.c_str());
274
0
            return false;
275
0
        }
276
0
    }
277
278
0
    bool ret = true;
279
0
    for (int i = 0; ret && i < nLayerCount; ++i)
280
0
    {
281
0
        auto poSrcLayer = poSrcDS->GetLayer(i);
282
0
        ret = (poSrcLayer != nullptr);
283
0
        if (ret)
284
0
        {
285
0
            outDS->AddLayer(*poSrcLayer,
286
0
                            std::make_unique<GDALVectorEditAlgorithmLayer>(
287
0
                                *poSrcLayer, m_activeLayer, bChangeGeomType,
288
0
                                eType, m_overrideCrs, m_layerMetadata,
289
0
                                m_unsetLayerMetadata, m_unsetFID));
290
0
        }
291
0
    }
292
293
0
    if (ret)
294
0
        m_outputDataset.Set(std::move(outDS));
295
296
0
    return ret;
297
0
}
298
299
0
GDALVectorEditAlgorithmStandalone::~GDALVectorEditAlgorithmStandalone() =
300
    default;
301
302
//! @endcond