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 109 : GDALVectorEditAlgorithm::GDALVectorEditAlgorithm(bool standaloneStep)
29 : : GDALVectorPipelineStepAlgorithm(
30 : NAME, DESCRIPTION, HELP_URL,
31 0 : ConstructorOptions()
32 109 : .SetStandaloneStep(standaloneStep)
33 218 : .SetOutputLayerNameAvailableInPipelineStep(true))
34 : {
35 109 : AddActiveLayerArg(&m_activeLayer);
36 109 : if (!standaloneStep)
37 : {
38 57 : AddOutputLayerNameArg(/* hiddenForCLI = */ false,
39 : /* shortNameOutputLayerAllowed = */ false);
40 : }
41 :
42 109 : AddGeometryTypeArg(&m_geometryType, _("Layer geometry type"));
43 :
44 218 : AddArg("crs", 0, _("Override CRS (without reprojection)"), &m_overrideCrs)
45 218 : .AddHiddenAlias("a_srs")
46 109 : .SetIsCRSArg(/*noneAllowed=*/true);
47 :
48 : {
49 : auto &arg = AddArg("metadata", 0, _("Add/update dataset metadata item"),
50 218 : &m_metadata)
51 218 : .SetMetaVar("<KEY>=<VALUE>")
52 109 : .SetPackedValuesAllowed(false);
53 6 : arg.AddValidationAction([this, &arg]()
54 115 : { return ParseAndValidateKeyValue(arg); });
55 109 : arg.AddHiddenAlias("mo");
56 : }
57 :
58 : AddArg("unset-metadata", 0, _("Remove dataset metadata item"),
59 218 : &m_unsetMetadata)
60 109 : .SetMetaVar("<KEY>");
61 :
62 : {
63 : auto &arg =
64 : AddArg("layer-metadata", 0, _("Add/update layer metadata item"),
65 218 : &m_layerMetadata)
66 218 : .SetMetaVar("<KEY>=<VALUE>")
67 109 : .SetPackedValuesAllowed(false);
68 6 : arg.AddValidationAction([this, &arg]()
69 115 : { return ParseAndValidateKeyValue(arg); });
70 : }
71 :
72 : AddArg("unset-layer-metadata", 0, _("Remove layer metadata item"),
73 218 : &m_unsetLayerMetadata)
74 109 : .SetMetaVar("<KEY>");
75 :
76 : AddArg("unset-fid", 0,
77 : _("Unset the identifier of each feature and the FID column name"),
78 109 : &m_unsetFID);
79 :
80 109 : AddValidationAction(
81 56 : [this]()
82 : {
83 47 : 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 42 : return true;
97 : });
98 109 : }
99 :
100 : namespace
101 : {
102 :
103 : /************************************************************************/
104 : /* GDALVectorEditAlgorithmLayer */
105 : /************************************************************************/
106 :
107 : class GDALVectorEditAlgorithmLayer final : public GDALVectorPipelineOutputLayer
108 : {
109 : public:
110 21 : 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 21 : : GDALVectorPipelineOutputLayer(oSrcLayer),
117 42 : m_bOverrideCrs(!overrideCrs.empty()), m_unsetFID(unsetFID),
118 21 : m_poFeatureDefn(oSrcLayer.GetLayerDefn()->Clone())
119 : {
120 21 : SetMetadata(oSrcLayer.GetMetadata());
121 :
122 21 : if (activeLayer.empty() || activeLayer == oSrcLayer.GetDescription())
123 : {
124 19 : if (!outputLayerName.empty())
125 : {
126 2 : m_poFeatureDefn->SetName(outputLayerName.c_str());
127 : }
128 :
129 38 : const CPLStringList aosMD(layerMetadata);
130 21 : 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 20 : 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 19 : if (bChangeGeomType)
149 : {
150 6 : for (auto *poGeomFieldDefns : m_poFeatureDefn->GetGeomFields())
151 : {
152 3 : poGeomFieldDefns->SetType(eType);
153 : }
154 : }
155 :
156 19 : 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 21 : SetDescription(m_poFeatureDefn->GetName());
175 21 : }
176 :
177 24 : const char *GetFIDColumn() const override
178 : {
179 24 : if (m_unsetFID)
180 2 : return "";
181 22 : return m_srcLayer.GetFIDColumn();
182 : }
183 :
184 91 : const OGRFeatureDefn *GetLayerDefn() const override
185 : {
186 91 : return m_poFeatureDefn.get();
187 : }
188 :
189 305 : void TranslateFeature(
190 : std::unique_ptr<OGRFeature> poSrcFeature,
191 : std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures) override
192 : {
193 305 : poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn.get());
194 305 : 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 305 : if (m_unsetFID)
204 1 : poSrcFeature->SetFID(OGRNullFID);
205 305 : apoOutFeatures.push_back(std::move(poSrcFeature));
206 305 : }
207 :
208 12 : int TestCapability(const char *pszCap) const override
209 : {
210 12 : if (EQUAL(pszCap, OLCStringsAsUTF8) ||
211 12 : EQUAL(pszCap, OLCCurveGeometries) || EQUAL(pszCap, OLCZGeometries))
212 0 : return m_srcLayer.TestCapability(pszCap);
213 12 : return false;
214 : }
215 :
216 : private:
217 : const bool m_bOverrideCrs;
218 : const bool m_unsetFID;
219 : const OGRFeatureDefnRefCountedPtr m_poFeatureDefn;
220 : OGRSpatialReferenceRefCountedPtr m_poSRS{};
221 :
222 : CPL_DISALLOW_COPY_ASSIGN(GDALVectorEditAlgorithmLayer)
223 : };
224 :
225 : /************************************************************************/
226 : /* GDALVectorEditOutputDataset */
227 : /************************************************************************/
228 :
229 : class GDALVectorEditOutputDataset final : public GDALVectorPipelineOutputDataset
230 : {
231 : public:
232 19 : explicit GDALVectorEditOutputDataset(GDALDataset &oSrcDS)
233 19 : : GDALVectorPipelineOutputDataset(oSrcDS)
234 : {
235 19 : }
236 :
237 : CSLConstList GetMetadata(const char *pszDomain) override;
238 :
239 0 : const char *GetMetadataItem(const char *pszName,
240 : const char *pszDomain) override
241 : {
242 0 : if (!pszDomain || pszDomain[0] == 0)
243 0 : return GDALDataset::GetMetadataItem(pszName, pszDomain);
244 0 : return m_srcDS.GetMetadataItem(pszName, pszDomain);
245 : }
246 : };
247 :
248 11 : CSLConstList GDALVectorEditOutputDataset::GetMetadata(const char *pszDomain)
249 : {
250 11 : if (!pszDomain || pszDomain[0] == 0)
251 11 : return GDALDataset::GetMetadata(pszDomain);
252 0 : return m_srcDS.GetMetadata(pszDomain);
253 : }
254 :
255 : } // namespace
256 :
257 : /************************************************************************/
258 : /* GDALVectorEditAlgorithm::RunStep() */
259 : /************************************************************************/
260 :
261 19 : bool GDALVectorEditAlgorithm::RunStep(GDALPipelineStepRunContext &)
262 : {
263 19 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
264 19 : CPLAssert(poSrcDS);
265 :
266 19 : CPLAssert(m_outputDataset.GetName().empty());
267 19 : CPLAssert(!m_outputDataset.GetDatasetRef());
268 :
269 19 : const int nLayerCount = poSrcDS->GetLayerCount();
270 :
271 19 : bool bChangeGeomType = false;
272 19 : OGRwkbGeometryType eType = wkbUnknown;
273 19 : if (!m_geometryType.empty())
274 : {
275 3 : eType = OGRFromOGCGeomType(m_geometryType.c_str());
276 3 : bChangeGeomType = true;
277 : }
278 :
279 38 : auto outDS = std::make_unique<GDALVectorEditOutputDataset>(*poSrcDS);
280 :
281 19 : outDS->SetMetadata(poSrcDS->GetMetadata());
282 :
283 38 : const CPLStringList aosMD(m_metadata);
284 21 : for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
285 : {
286 2 : if (outDS->SetMetadataItem(key, value) != CE_None)
287 : {
288 0 : ReportError(CE_Failure, CPLE_AppDefined,
289 : "SetMetadataItem('%s', '%s') failed", key, value);
290 0 : return false;
291 : }
292 : }
293 :
294 20 : for (const std::string &key : m_unsetMetadata)
295 : {
296 1 : if (outDS->SetMetadataItem(key.c_str(), nullptr) != CE_None)
297 : {
298 0 : ReportError(CE_Failure, CPLE_AppDefined,
299 : "SetMetadataItem('%s', NULL) failed", key.c_str());
300 0 : return false;
301 : }
302 : }
303 :
304 19 : bool ret = true;
305 40 : for (int i = 0; ret && i < nLayerCount; ++i)
306 : {
307 21 : auto poSrcLayer = poSrcDS->GetLayer(i);
308 21 : ret = (poSrcLayer != nullptr);
309 21 : if (ret)
310 : {
311 42 : outDS->AddLayer(*poSrcLayer,
312 21 : std::make_unique<GDALVectorEditAlgorithmLayer>(
313 21 : *poSrcLayer, m_activeLayer, m_outputLayerName,
314 21 : bChangeGeomType, eType, m_overrideCrs,
315 21 : m_layerMetadata, m_unsetLayerMetadata,
316 21 : m_unsetFID));
317 : }
318 : }
319 :
320 19 : if (ret)
321 19 : m_outputDataset.Set(std::move(outDS));
322 :
323 19 : return ret;
324 : }
325 :
326 : GDALVectorEditAlgorithmStandalone::~GDALVectorEditAlgorithmStandalone() =
327 : default;
328 :
329 : //! @endcond
|