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