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 14 : GDALVectorEditAlgorithm::GDALVectorEditAlgorithm(bool standaloneStep)
29 : : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
30 14 : standaloneStep)
31 : {
32 14 : AddActiveLayerArg(&m_activeLayer);
33 28 : AddArg("geometry-type", 0, _("Layer geometry type"), &m_geometryType)
34 : .SetAutoCompleteFunction(
35 0 : [](const std::string ¤tValue)
36 : {
37 0 : std::vector<std::string> oRet;
38 0 : for (const char *type :
39 : {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
40 : "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
41 : "GEOMETRYCOLLECTION", "CURVE", "CIRCULARSTRING",
42 : "COMPOUNDCURVE", "SURFACE", "CURVEPOLYGON", "MULTICURVE",
43 0 : "MULTISURFACE", "POLYHEDRALSURFACE", "TIN"})
44 : {
45 0 : if (currentValue.empty() ||
46 0 : STARTS_WITH(type, currentValue.c_str()))
47 : {
48 0 : oRet.push_back(type);
49 0 : oRet.push_back(std::string(type).append("Z"));
50 0 : oRet.push_back(std::string(type).append("M"));
51 0 : oRet.push_back(std::string(type).append("ZM"));
52 : }
53 : }
54 0 : return oRet;
55 14 : });
56 :
57 28 : AddArg("crs", 0, _("Override CRS (without reprojection)"), &m_overrideCrs)
58 28 : .AddHiddenAlias("a_srs")
59 14 : .SetIsCRSArg(/*noneAllowed=*/true);
60 :
61 : {
62 : auto &arg = AddArg("metadata", 0, _("Add/update dataset metadata item"),
63 28 : &m_metadata)
64 28 : .SetMetaVar("<KEY>=<VALUE>")
65 14 : .SetPackedValuesAllowed(false);
66 1 : arg.AddValidationAction([this, &arg]()
67 15 : { return ParseAndValidateKeyValue(arg); });
68 14 : arg.AddHiddenAlias("mo");
69 : }
70 :
71 : AddArg("unset-metadata", 0, _("Remove dataset metadata item"),
72 28 : &m_unsetMetadata)
73 14 : .SetMetaVar("<KEY>");
74 :
75 : {
76 : auto &arg =
77 : AddArg("layer-metadata", 0, _("Add/update layer metadata item"),
78 28 : &m_layerMetadata)
79 28 : .SetMetaVar("<KEY>=<VALUE>")
80 14 : .SetPackedValuesAllowed(false);
81 1 : arg.AddValidationAction([this, &arg]()
82 15 : { return ParseAndValidateKeyValue(arg); });
83 : }
84 :
85 : AddArg("unset-layer-metadata", 0, _("Remove layer metadata item"),
86 28 : &m_unsetLayerMetadata)
87 14 : .SetMetaVar("<KEY>");
88 14 : }
89 :
90 : /************************************************************************/
91 : /* GDALVectorEditAlgorithmLayer */
92 : /************************************************************************/
93 :
94 : namespace
95 : {
96 : class GDALVectorEditAlgorithmLayer final : public GDALVectorPipelineOutputLayer
97 : {
98 : public:
99 6 : GDALVectorEditAlgorithmLayer(
100 : OGRLayer &oSrcLayer, const std::string &activeLayer,
101 : bool bChangeGeomType, OGRwkbGeometryType eType,
102 : const std::string &overrideCrs,
103 : const std::vector<std::string> &layerMetadata,
104 : const std::vector<std::string> &unsetLayerMetadata)
105 6 : : GDALVectorPipelineOutputLayer(oSrcLayer),
106 6 : m_bOverrideCrs(!overrideCrs.empty())
107 : {
108 6 : SetDescription(oSrcLayer.GetDescription());
109 6 : SetMetadata(oSrcLayer.GetMetadata());
110 :
111 6 : m_poFeatureDefn = oSrcLayer.GetLayerDefn()->Clone();
112 6 : m_poFeatureDefn->Reference();
113 :
114 6 : if (activeLayer.empty() || activeLayer == GetDescription())
115 : {
116 10 : const CPLStringList aosMD(layerMetadata);
117 6 : for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
118 : {
119 1 : if (SetMetadataItem(key, value) != CE_None)
120 : {
121 0 : CPLError(CE_Warning, CPLE_AppDefined,
122 : "SetMetadataItem('%s', '%s') failed", key, value);
123 : }
124 : }
125 :
126 6 : for (const std::string &key : unsetLayerMetadata)
127 : {
128 1 : if (SetMetadataItem(key.c_str(), nullptr) != CE_None)
129 : {
130 0 : CPLError(CE_Warning, CPLE_AppDefined,
131 : "SetMetadataItem('%s', NULL) failed", key.c_str());
132 : }
133 : }
134 :
135 5 : if (bChangeGeomType)
136 : {
137 4 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
138 : {
139 2 : m_poFeatureDefn->GetGeomFieldDefn(i)->SetType(eType);
140 : }
141 : }
142 :
143 5 : if (!overrideCrs.empty())
144 : {
145 4 : if (!EQUAL(overrideCrs.c_str(), "null") &&
146 2 : !EQUAL(overrideCrs.c_str(), "none"))
147 : {
148 1 : m_poSRS = new OGRSpatialReference();
149 1 : m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
150 1 : m_poSRS->SetFromUserInput(overrideCrs.c_str());
151 : }
152 4 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
153 : {
154 2 : m_poFeatureDefn->GetGeomFieldDefn(i)->SetSpatialRef(
155 2 : m_poSRS);
156 : }
157 : }
158 : }
159 6 : }
160 :
161 12 : ~GDALVectorEditAlgorithmLayer()
162 6 : {
163 6 : m_poFeatureDefn->Release();
164 6 : if (m_poSRS)
165 1 : m_poSRS->Release();
166 12 : }
167 :
168 72 : OGRFeatureDefn *GetLayerDefn() override
169 : {
170 72 : return m_poFeatureDefn;
171 : }
172 :
173 2 : void TranslateFeature(
174 : std::unique_ptr<OGRFeature> poSrcFeature,
175 : std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures) override
176 : {
177 2 : poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn);
178 2 : if (m_bOverrideCrs)
179 : {
180 4 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
181 : {
182 2 : auto poGeom = poSrcFeature->GetGeomFieldRef(i);
183 2 : if (poGeom)
184 2 : poGeom->assignSpatialReference(m_poSRS);
185 : }
186 : }
187 2 : apoOutFeatures.push_back(std::move(poSrcFeature));
188 2 : }
189 :
190 6 : int TestCapability(const char *pszCap) override
191 : {
192 6 : if (EQUAL(pszCap, OLCStringsAsUTF8) ||
193 6 : EQUAL(pszCap, OLCCurveGeometries) || EQUAL(pszCap, OLCZGeometries))
194 0 : return m_srcLayer.TestCapability(pszCap);
195 6 : return false;
196 : }
197 :
198 : private:
199 : const bool m_bOverrideCrs;
200 : OGRFeatureDefn *m_poFeatureDefn = nullptr;
201 : OGRSpatialReference *m_poSRS = nullptr;
202 :
203 : CPL_DISALLOW_COPY_ASSIGN(GDALVectorEditAlgorithmLayer)
204 : };
205 :
206 : } // namespace
207 :
208 : /************************************************************************/
209 : /* GDALVectorEditAlgorithm::RunStep() */
210 : /************************************************************************/
211 :
212 7 : bool GDALVectorEditAlgorithm::RunStep(GDALProgressFunc, void *)
213 : {
214 7 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
215 7 : CPLAssert(poSrcDS);
216 :
217 7 : CPLAssert(m_outputDataset.GetName().empty());
218 7 : CPLAssert(!m_outputDataset.GetDatasetRef());
219 :
220 7 : const int nLayerCount = poSrcDS->GetLayerCount();
221 :
222 7 : bool bChangeGeomType = false;
223 7 : OGRwkbGeometryType eType = wkbUnknown;
224 7 : if (!m_geometryType.empty())
225 : {
226 3 : eType = OGRFromOGCGeomType(m_geometryType.c_str());
227 5 : if (wkbFlatten(eType) == wkbUnknown &&
228 2 : !STARTS_WITH_CI(m_geometryType.c_str(), "GEOMETRY"))
229 : {
230 1 : ReportError(CE_Failure, CPLE_AppDefined,
231 : "Invalid geometry type '%s'", m_geometryType.c_str());
232 1 : return false;
233 : }
234 2 : bChangeGeomType = true;
235 : }
236 :
237 12 : auto outDS = std::make_unique<GDALVectorPipelineOutputDataset>(*poSrcDS);
238 :
239 12 : const CPLStringList aosMD(m_metadata);
240 7 : for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
241 : {
242 1 : if (outDS->SetMetadataItem(key, value) != CE_None)
243 : {
244 0 : ReportError(CE_Failure, CPLE_AppDefined,
245 : "SetMetadataItem('%s', '%s') failed", key, value);
246 0 : return false;
247 : }
248 : }
249 :
250 7 : for (const std::string &key : m_unsetMetadata)
251 : {
252 1 : if (outDS->SetMetadataItem(key.c_str(), nullptr) != CE_None)
253 : {
254 0 : ReportError(CE_Failure, CPLE_AppDefined,
255 : "SetMetadataItem('%s', NULL) failed", key.c_str());
256 0 : return false;
257 : }
258 : }
259 :
260 6 : bool ret = true;
261 12 : for (int i = 0; ret && i < nLayerCount; ++i)
262 : {
263 6 : auto poSrcLayer = poSrcDS->GetLayer(i);
264 6 : ret = (poSrcLayer != nullptr);
265 6 : if (ret)
266 : {
267 12 : outDS->AddLayer(*poSrcLayer,
268 6 : std::make_unique<GDALVectorEditAlgorithmLayer>(
269 6 : *poSrcLayer, m_activeLayer, bChangeGeomType,
270 6 : eType, m_overrideCrs, m_layerMetadata,
271 6 : m_unsetLayerMetadata));
272 : }
273 : }
274 :
275 6 : if (ret)
276 6 : m_outputDataset.Set(std::move(outDS));
277 :
278 6 : return ret;
279 : }
280 :
281 : //! @endcond
|