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