Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: "update" 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_update.h"
14 :
15 : #include "ogr_p.h"
16 : #include "ogrsf_frmts.h"
17 :
18 : //! @cond Doxygen_Suppress
19 :
20 : #ifndef _
21 : #define _(x) (x)
22 : #endif
23 :
24 : /************************************************************************/
25 : /* GDALVectorUpdateAlgorithm::GDALVectorUpdateAlgorithm() */
26 : /************************************************************************/
27 :
28 57 : GDALVectorUpdateAlgorithm::GDALVectorUpdateAlgorithm(bool standaloneStep)
29 : : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
30 0 : ConstructorOptions()
31 57 : .SetStandaloneStep(standaloneStep)
32 57 : .SetInputDatasetMaxCount(1)
33 57 : .SetAddInputLayerNameArgument(false)
34 114 : .SetAddDefaultArguments(false))
35 : {
36 57 : if (standaloneStep)
37 : {
38 23 : AddVectorInputArgs(false);
39 : }
40 : else
41 : {
42 34 : AddVectorHiddenInputDatasetArg();
43 : }
44 :
45 : {
46 : auto &layerArg = AddArg(GDAL_ARG_NAME_INPUT_LAYER, 0,
47 114 : _("Input layer name"), &m_inputLayerNames)
48 57 : .SetMaxCount(1);
49 57 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
50 57 : if (inputArg)
51 57 : SetAutoCompleteFunctionForLayerName(layerArg, *inputArg);
52 : }
53 :
54 57 : AddProgressArg();
55 :
56 57 : AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR)
57 57 : .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
58 57 : AddOutputOpenOptionsArg(&m_outputOpenOptions);
59 57 : AddOutputLayerNameArg(&m_outputLayerName);
60 :
61 57 : m_update = true;
62 57 : AddUpdateArg(&m_update).SetDefault(true).SetHidden();
63 :
64 114 : AddArg("mode", 0, _("Set update mode"), &m_mode)
65 57 : .SetDefault(m_mode)
66 57 : .SetChoices(MODE_MERGE, MODE_UPDATE_ONLY, MODE_APPEND_ONLY);
67 :
68 114 : AddArg("key", 0, _("Field(s) used as a key to identify features"), &m_key)
69 57 : .SetPackedValuesAllowed(false);
70 57 : }
71 :
72 : /************************************************************************/
73 : /* GDALVectorUpdateAlgorithm::RunStep() */
74 : /************************************************************************/
75 :
76 17 : bool GDALVectorUpdateAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
77 : {
78 17 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
79 17 : CPLAssert(poSrcDS);
80 :
81 17 : auto poDstDS = m_outputDataset.GetDatasetRef();
82 17 : CPLAssert(poDstDS);
83 17 : CPLAssert(poDstDS->GetAccess() == GA_Update);
84 :
85 17 : auto poSrcDriver = poSrcDS->GetDriver();
86 17 : auto poDstDriver = poDstDS->GetDriver();
87 33 : if (poSrcDS == poDstDS ||
88 16 : (poSrcDriver && poDstDriver &&
89 16 : !EQUAL(poSrcDriver->GetDescription(), "MEM") &&
90 1 : !EQUAL(poDstDriver->GetDescription(), "MEM") &&
91 1 : strcmp(poSrcDS->GetDescription(), poDstDS->GetDescription()) == 0))
92 : {
93 2 : ReportError(CE_Failure, CPLE_NotSupported,
94 : "Input and output datasets must be different");
95 2 : return false;
96 : }
97 :
98 15 : if (m_inputLayerNames.empty() && poSrcDS->GetLayerCount() == 1)
99 : {
100 12 : m_inputLayerNames.push_back(poSrcDS->GetLayer(0)->GetName());
101 : }
102 15 : if (m_outputLayerName.empty() && poDstDS->GetLayerCount() == 1)
103 : {
104 10 : m_outputLayerName = poDstDS->GetLayer(0)->GetName();
105 : }
106 :
107 15 : if (m_inputLayerNames.empty())
108 : {
109 2 : if (!m_outputLayerName.empty())
110 : {
111 1 : ReportError(CE_Failure, CPLE_AppDefined,
112 : "Please specify the 'input-layer' argument.");
113 1 : return false;
114 : }
115 : else
116 : {
117 1 : ReportError(CE_Failure, CPLE_AppDefined,
118 : "Please specify the 'input-layer' and 'output-layer' "
119 : "arguments.");
120 1 : return false;
121 : }
122 : }
123 :
124 13 : auto poSrcLayer = poSrcDS->GetLayerByName(m_inputLayerNames[0].c_str());
125 13 : if (!poSrcLayer)
126 : {
127 1 : ReportError(CE_Failure, CPLE_AppDefined,
128 : "No layer named '%s' in input dataset.",
129 1 : m_inputLayerNames[0].c_str());
130 1 : return false;
131 : }
132 :
133 12 : if (m_outputLayerName.empty())
134 : {
135 1 : ReportError(CE_Failure, CPLE_AppDefined,
136 : "Please specify the 'output-layer' argument.");
137 1 : return false;
138 : }
139 :
140 11 : auto poDstLayer = poDstDS->GetLayerByName(m_outputLayerName.c_str());
141 11 : if (!poDstLayer)
142 : {
143 1 : ReportError(CE_Failure, CPLE_AppDefined,
144 : "No layer named '%s' in output dataset",
145 : m_outputLayerName.c_str());
146 1 : return false;
147 : }
148 :
149 20 : std::vector<int> srcKeyFieldIndices;
150 20 : std::vector<OGRFieldType> keyFieldTypes;
151 10 : if (m_key.empty())
152 6 : m_key.push_back(SpecialFieldNames[SPF_FID]);
153 20 : for (const std::string &key : m_key)
154 : {
155 13 : if (EQUAL(key.c_str(), SpecialFieldNames[SPF_FID]))
156 : {
157 6 : srcKeyFieldIndices.push_back(
158 6 : poSrcLayer->GetLayerDefn()->GetFieldCount() + SPF_FID);
159 6 : keyFieldTypes.push_back(OFTInteger64);
160 6 : continue;
161 : }
162 :
163 : const int nSrcIdx =
164 7 : poSrcLayer->GetLayerDefn()->GetFieldIndex(key.c_str());
165 7 : if (nSrcIdx < 0)
166 : {
167 1 : ReportError(CE_Failure, CPLE_AppDefined,
168 : "Cannot find field '%s' in input layer", key.c_str());
169 3 : return false;
170 : }
171 6 : srcKeyFieldIndices.push_back(nSrcIdx);
172 : const auto poSrcFieldDefn =
173 6 : poSrcLayer->GetLayerDefn()->GetFieldDefn(nSrcIdx);
174 6 : const auto eType = poSrcFieldDefn->GetType();
175 6 : const OGRFieldType aeAllowedTypes[] = {OFTString, OFTInteger,
176 : OFTInteger64, OFTReal};
177 6 : if (std::find(std::begin(aeAllowedTypes), std::end(aeAllowedTypes),
178 6 : eType) == std::end(aeAllowedTypes))
179 : {
180 1 : ReportError(CE_Failure, CPLE_NotSupported,
181 : "Type of field '%s' is not one of those supported for "
182 : "a key field: String, Integer, Integer64, Real",
183 : key.c_str());
184 1 : return false;
185 : }
186 :
187 : const int nDstIdx =
188 5 : poDstLayer->GetLayerDefn()->GetFieldIndex(key.c_str());
189 5 : if (nDstIdx < 0)
190 : {
191 1 : ReportError(CE_Failure, CPLE_AppDefined,
192 : "Cannot find field '%s' in output layer", key.c_str());
193 1 : return false;
194 : }
195 : const auto poDstFieldDefn =
196 4 : poDstLayer->GetLayerDefn()->GetFieldDefn(nDstIdx);
197 4 : if (poDstFieldDefn->GetType() != eType)
198 : {
199 0 : ReportError(
200 : CE_Failure, CPLE_NotSupported,
201 : "Type of field '%s' is not the same in input and output layers",
202 : key.c_str());
203 0 : return false;
204 : }
205 4 : keyFieldTypes.push_back(eType);
206 : }
207 :
208 13 : const bool bFIDMatch = m_key.size() == 1 &&
209 6 : EQUAL(m_key[0].c_str(), SpecialFieldNames[SPF_FID]);
210 : const GIntBig nFeatureCount =
211 7 : ctxt.m_pfnProgress ? poSrcLayer->GetFeatureCount(true) : -1;
212 :
213 7 : std::string osFilter;
214 7 : int nIter = 0;
215 7 : bool bRet = true;
216 21 : for (const auto &poSrcFeature : *poSrcLayer)
217 : {
218 14 : ++nIter;
219 20 : if (ctxt.m_pfnProgress && nFeatureCount > 0 &&
220 6 : !ctxt.m_pfnProgress(static_cast<double>(nIter) / nFeatureCount, "",
221 : ctxt.m_pProgressData))
222 : {
223 1 : ReportError(CE_Failure, CPLE_UserInterrupt, "Interrupted by user");
224 1 : bRet = false;
225 1 : break;
226 : }
227 :
228 0 : std::unique_ptr<OGRFeature> poDstFeature;
229 13 : if (bFIDMatch)
230 : {
231 16 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
232 8 : poDstFeature.reset(poDstLayer->GetFeature(poSrcFeature->GetFID()));
233 : }
234 : else
235 : {
236 5 : bool bSkip = false;
237 5 : osFilter.clear();
238 21 : for (size_t iField = 0; iField < srcKeyFieldIndices.size();
239 : ++iField)
240 : {
241 17 : const int nSrcFieldIdx = srcKeyFieldIndices[iField];
242 17 : if (!poSrcFeature->IsFieldSet(nSrcFieldIdx))
243 : {
244 1 : bSkip = true;
245 1 : break;
246 : }
247 16 : if (!osFilter.empty())
248 12 : osFilter += " AND ";
249 16 : osFilter += CPLString(m_key[iField]).SQLQuotedIdentifier();
250 16 : osFilter += " = ";
251 16 : switch (keyFieldTypes[iField])
252 : {
253 4 : case OFTString:
254 : {
255 4 : osFilter += CPLString(poSrcFeature->GetFieldAsString(
256 : nSrcFieldIdx))
257 4 : .SQLQuotedLiteral();
258 4 : break;
259 : }
260 :
261 4 : case OFTReal:
262 : {
263 : osFilter += CPLSPrintf(
264 : "%.17g",
265 4 : poSrcFeature->GetFieldAsDouble(nSrcFieldIdx));
266 4 : break;
267 : }
268 :
269 8 : default:
270 : {
271 : osFilter += CPLSPrintf(
272 : CPL_FRMT_GIB,
273 8 : poSrcFeature->GetFieldAsInteger64(nSrcFieldIdx));
274 8 : break;
275 : }
276 : }
277 : }
278 5 : if (bSkip)
279 1 : continue;
280 4 : if (poDstLayer->SetAttributeFilter(osFilter.c_str()) != OGRERR_NONE)
281 : {
282 0 : bRet = false;
283 0 : break;
284 : }
285 4 : poDstFeature.reset(poDstLayer->GetNextFeature());
286 4 : if (poDstFeature)
287 : {
288 : // Check there is only one feature matching the criterion
289 3 : if (std::unique_ptr<OGRFeature>(poDstLayer->GetNextFeature()))
290 : {
291 1 : poDstFeature.reset();
292 : }
293 : else
294 : {
295 2 : CPLDebugOnly("GDAL",
296 : "Updating output feature " CPL_FRMT_GIB
297 : " with src input " CPL_FRMT_GIB,
298 : poDstFeature->GetFID(),
299 : poSrcFeature->GetFID());
300 : }
301 : }
302 : }
303 :
304 12 : if (poDstFeature)
305 : {
306 6 : if (m_mode != MODE_APPEND_ONLY)
307 : {
308 : auto poDstFeatureOri =
309 5 : std::unique_ptr<OGRFeature>(poDstFeature->Clone());
310 5 : const auto nDstFID = poDstFeature->GetFID();
311 5 : poDstFeature->SetFrom(poSrcFeature.get());
312 : // restore FID unset by SetFrom()
313 5 : poDstFeature->SetFID(nDstFID);
314 10 : if (!poDstFeature->Equal(poDstFeatureOri.get()) &&
315 5 : poDstLayer->SetFeature(poDstFeature.get()) != OGRERR_NONE)
316 : {
317 0 : bRet = false;
318 0 : break;
319 : }
320 : }
321 : }
322 6 : else if (m_mode != MODE_UPDATE_ONLY)
323 : {
324 : poDstFeature =
325 3 : std::make_unique<OGRFeature>(poDstLayer->GetLayerDefn());
326 3 : poDstFeature->SetFrom(poSrcFeature.get());
327 3 : if (poDstLayer->CreateFeature(poDstFeature.get()) != OGRERR_NONE)
328 : {
329 0 : bRet = false;
330 0 : break;
331 : }
332 : }
333 : }
334 :
335 7 : poDstLayer->SetAttributeFilter(nullptr);
336 :
337 7 : return bRet;
338 : }
339 :
340 : /************************************************************************/
341 : /* ~GDALVectorUpdateAlgorithmStandalone() */
342 : /************************************************************************/
343 :
344 : GDALVectorUpdateAlgorithmStandalone::~GDALVectorUpdateAlgorithmStandalone() =
345 : default;
346 :
347 : /************************************************************************/
348 : /* GDALVectorUpdateAlgorithmStandalone::RunImpl() */
349 : /************************************************************************/
350 :
351 16 : bool GDALVectorUpdateAlgorithmStandalone::RunImpl(GDALProgressFunc pfnProgress,
352 : void *pProgressData)
353 : {
354 16 : GDALPipelineStepRunContext stepCtxt;
355 16 : stepCtxt.m_pfnProgress = pfnProgress;
356 16 : stepCtxt.m_pProgressData = pProgressData;
357 32 : return RunStep(stepCtxt);
358 : }
359 :
360 : //! @endcond
|