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
|