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 94 : GDALVectorUpdateAlgorithm::GDALVectorUpdateAlgorithm(bool standaloneStep)
29 : : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
30 0 : ConstructorOptions()
31 94 : .SetStandaloneStep(standaloneStep)
32 94 : .SetInputDatasetMaxCount(1)
33 94 : .SetAddInputLayerNameArgument(false)
34 188 : .SetAddDefaultArguments(false))
35 : {
36 94 : if (standaloneStep)
37 : {
38 58 : AddProgressArg();
39 58 : AddVectorInputArgs(false);
40 : }
41 : else
42 : {
43 36 : AddVectorHiddenInputDatasetArg();
44 : }
45 :
46 : {
47 : auto &layerArg = AddArg(GDAL_ARG_NAME_INPUT_LAYER, 0,
48 188 : _("Input layer name"), &m_inputLayerNames)
49 94 : .SetMaxCount(1);
50 94 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
51 94 : if (inputArg)
52 94 : SetAutoCompleteFunctionForLayerName(layerArg, *inputArg);
53 : }
54 :
55 94 : AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR)
56 94 : .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
57 94 : AddOutputOpenOptionsArg(&m_outputOpenOptions);
58 94 : AddOutputLayerNameArg(&m_outputLayerName);
59 :
60 94 : m_update = true;
61 94 : AddUpdateArg(&m_update).SetDefault(true).SetHidden();
62 :
63 188 : AddArg("mode", 0, _("Set update mode"), &m_mode)
64 94 : .SetDefault(m_mode)
65 94 : .SetChoices(MODE_MERGE, MODE_UPDATE_ONLY, MODE_APPEND_ONLY);
66 :
67 188 : AddArg("key", 0, _("Field(s) used as a key to identify features"), &m_key)
68 94 : .SetPackedValuesAllowed(false);
69 94 : }
70 :
71 : /************************************************************************/
72 : /* GDALVectorUpdateAlgorithm::RunStep() */
73 : /************************************************************************/
74 :
75 17 : bool GDALVectorUpdateAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
76 : {
77 17 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
78 17 : CPLAssert(poSrcDS);
79 :
80 17 : auto poDstDS = m_outputDataset.GetDatasetRef();
81 17 : CPLAssert(poDstDS);
82 17 : CPLAssert(poDstDS->GetAccess() == GA_Update);
83 :
84 17 : auto poSrcDriver = poSrcDS->GetDriver();
85 17 : auto poDstDriver = poDstDS->GetDriver();
86 33 : if (poSrcDS == poDstDS ||
87 16 : (poSrcDriver && poDstDriver &&
88 16 : !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 15 : if (m_inputLayerNames.empty() && poSrcDS->GetLayerCount() == 1)
98 : {
99 12 : const auto poSrcLayer = poSrcDS->GetLayer(0);
100 12 : if (poSrcLayer)
101 12 : m_inputLayerNames.push_back(poSrcLayer->GetName());
102 : }
103 15 : if (m_outputLayerName.empty() && poDstDS->GetLayerCount() == 1)
104 : {
105 10 : const auto poDstLayer = poDstDS->GetLayer(0);
106 10 : if (poDstLayer)
107 10 : m_outputLayerName = poDstLayer->GetName();
108 : }
109 :
110 15 : 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 13 : auto poSrcLayer = poSrcDS->GetLayerByName(m_inputLayerNames[0].c_str());
128 13 : 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 12 : 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 11 : auto poDstLayer = poDstDS->GetLayerByName(m_outputLayerName.c_str());
144 11 : 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 20 : std::vector<int> srcKeyFieldIndices;
153 20 : std::vector<OGRFieldType> keyFieldTypes;
154 10 : if (m_key.empty())
155 6 : m_key.push_back(SpecialFieldNames[SPF_FID]);
156 20 : for (const std::string &key : m_key)
157 : {
158 13 : if (EQUAL(key.c_str(), SpecialFieldNames[SPF_FID]))
159 : {
160 6 : srcKeyFieldIndices.push_back(
161 6 : poSrcLayer->GetLayerDefn()->GetFieldCount() + SPF_FID);
162 6 : keyFieldTypes.push_back(OFTInteger64);
163 6 : continue;
164 : }
165 :
166 : const int nSrcIdx =
167 7 : poSrcLayer->GetLayerDefn()->GetFieldIndex(key.c_str());
168 7 : 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 6 : srcKeyFieldIndices.push_back(nSrcIdx);
175 : const auto poSrcFieldDefn =
176 6 : poSrcLayer->GetLayerDefn()->GetFieldDefn(nSrcIdx);
177 6 : const auto eType = poSrcFieldDefn->GetType();
178 6 : const OGRFieldType aeAllowedTypes[] = {OFTString, OFTInteger,
179 : OFTInteger64, OFTReal};
180 6 : if (std::find(std::begin(aeAllowedTypes), std::end(aeAllowedTypes),
181 6 : 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 5 : poDstLayer->GetLayerDefn()->GetFieldIndex(key.c_str());
192 5 : 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 4 : poDstLayer->GetLayerDefn()->GetFieldDefn(nDstIdx);
200 4 : 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 4 : keyFieldTypes.push_back(eType);
209 : }
210 :
211 13 : const bool bFIDMatch = m_key.size() == 1 &&
212 6 : EQUAL(m_key[0].c_str(), SpecialFieldNames[SPF_FID]);
213 : const GIntBig nFeatureCount =
214 7 : ctxt.m_pfnProgress ? poSrcLayer->GetFeatureCount(true) : -1;
215 :
216 7 : std::string osFilter;
217 7 : int nIter = 0;
218 7 : bool bRet = true;
219 21 : for (const auto &poSrcFeature : *poSrcLayer)
220 : {
221 14 : ++nIter;
222 20 : 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 13 : if (bFIDMatch)
233 : {
234 16 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
235 8 : poDstFeature.reset(poDstLayer->GetFeature(poSrcFeature->GetFID()));
236 : }
237 : else
238 : {
239 5 : bool bSkip = false;
240 5 : osFilter.clear();
241 21 : for (size_t iField = 0; iField < srcKeyFieldIndices.size();
242 : ++iField)
243 : {
244 17 : const int nSrcFieldIdx = srcKeyFieldIndices[iField];
245 17 : if (!poSrcFeature->IsFieldSet(nSrcFieldIdx))
246 : {
247 1 : bSkip = true;
248 1 : 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 5 : if (bSkip)
282 1 : 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 12 : if (poDstFeature)
308 : {
309 6 : if (m_mode != MODE_APPEND_ONLY)
310 : {
311 : auto poDstFeatureOri =
312 5 : std::unique_ptr<OGRFeature>(poDstFeature->Clone());
313 5 : const auto nDstFID = poDstFeature->GetFID();
314 5 : poDstFeature->SetFrom(poSrcFeature.get());
315 : // restore FID unset by SetFrom()
316 5 : poDstFeature->SetFID(nDstFID);
317 10 : if (!poDstFeature->Equal(poDstFeatureOri.get()) &&
318 5 : poDstLayer->SetFeature(poDstFeature.get()) != OGRERR_NONE)
319 : {
320 0 : bRet = false;
321 0 : break;
322 : }
323 : }
324 : }
325 6 : else if (m_mode != MODE_UPDATE_ONLY)
326 : {
327 : poDstFeature =
328 3 : std::make_unique<OGRFeature>(poDstLayer->GetLayerDefn());
329 3 : poDstFeature->SetFrom(poSrcFeature.get());
330 3 : if (poDstLayer->CreateFeature(poDstFeature.get()) != OGRERR_NONE)
331 : {
332 0 : bRet = false;
333 0 : break;
334 : }
335 : }
336 : }
337 :
338 7 : poDstLayer->SetAttributeFilter(nullptr);
339 :
340 7 : return bRet;
341 : }
342 :
343 : /************************************************************************/
344 : /* ~GDALVectorUpdateAlgorithmStandalone() */
345 : /************************************************************************/
346 :
347 : GDALVectorUpdateAlgorithmStandalone::~GDALVectorUpdateAlgorithmStandalone() =
348 : default;
349 :
350 : /************************************************************************/
351 : /* GDALVectorUpdateAlgorithmStandalone::RunImpl() */
352 : /************************************************************************/
353 :
354 16 : bool GDALVectorUpdateAlgorithmStandalone::RunImpl(GDALProgressFunc pfnProgress,
355 : void *pProgressData)
356 : {
357 16 : GDALPipelineStepRunContext stepCtxt;
358 16 : stepCtxt.m_pfnProgress = pfnProgress;
359 16 : stepCtxt.m_pProgressData = pProgressData;
360 32 : return RunStep(stepCtxt);
361 : }
362 :
363 : //! @endcond
|