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 : m_inputLayerNames.push_back(poSrcDS->GetLayer(0)->GetName());
100 : }
101 15 : if (m_outputLayerName.empty() && poDstDS->GetLayerCount() == 1)
102 : {
103 10 : m_outputLayerName = poDstDS->GetLayer(0)->GetName();
104 : }
105 :
106 15 : if (m_inputLayerNames.empty())
107 : {
108 2 : if (!m_outputLayerName.empty())
109 : {
110 1 : ReportError(CE_Failure, CPLE_AppDefined,
111 : "Please specify the 'input-layer' argument.");
112 1 : return false;
113 : }
114 : else
115 : {
116 1 : ReportError(CE_Failure, CPLE_AppDefined,
117 : "Please specify the 'input-layer' and 'output-layer' "
118 : "arguments.");
119 1 : return false;
120 : }
121 : }
122 :
123 13 : auto poSrcLayer = poSrcDS->GetLayerByName(m_inputLayerNames[0].c_str());
124 13 : if (!poSrcLayer)
125 : {
126 1 : ReportError(CE_Failure, CPLE_AppDefined,
127 : "No layer named '%s' in input dataset.",
128 1 : m_inputLayerNames[0].c_str());
129 1 : return false;
130 : }
131 :
132 12 : if (m_outputLayerName.empty())
133 : {
134 1 : ReportError(CE_Failure, CPLE_AppDefined,
135 : "Please specify the 'output-layer' argument.");
136 1 : return false;
137 : }
138 :
139 11 : auto poDstLayer = poDstDS->GetLayerByName(m_outputLayerName.c_str());
140 11 : if (!poDstLayer)
141 : {
142 1 : ReportError(CE_Failure, CPLE_AppDefined,
143 : "No layer named '%s' in output dataset",
144 : m_outputLayerName.c_str());
145 1 : return false;
146 : }
147 :
148 20 : std::vector<int> srcKeyFieldIndices;
149 20 : std::vector<OGRFieldType> keyFieldTypes;
150 10 : if (m_key.empty())
151 6 : m_key.push_back(SpecialFieldNames[SPF_FID]);
152 20 : for (const std::string &key : m_key)
153 : {
154 13 : if (EQUAL(key.c_str(), SpecialFieldNames[SPF_FID]))
155 : {
156 6 : srcKeyFieldIndices.push_back(
157 6 : poSrcLayer->GetLayerDefn()->GetFieldCount() + SPF_FID);
158 6 : keyFieldTypes.push_back(OFTInteger64);
159 6 : continue;
160 : }
161 :
162 : const int nSrcIdx =
163 7 : poSrcLayer->GetLayerDefn()->GetFieldIndex(key.c_str());
164 7 : if (nSrcIdx < 0)
165 : {
166 1 : ReportError(CE_Failure, CPLE_AppDefined,
167 : "Cannot find field '%s' in input layer", key.c_str());
168 3 : return false;
169 : }
170 6 : srcKeyFieldIndices.push_back(nSrcIdx);
171 : const auto poSrcFieldDefn =
172 6 : poSrcLayer->GetLayerDefn()->GetFieldDefn(nSrcIdx);
173 6 : const auto eType = poSrcFieldDefn->GetType();
174 6 : const OGRFieldType aeAllowedTypes[] = {OFTString, OFTInteger,
175 : OFTInteger64, OFTReal};
176 6 : if (std::find(std::begin(aeAllowedTypes), std::end(aeAllowedTypes),
177 6 : eType) == std::end(aeAllowedTypes))
178 : {
179 1 : ReportError(CE_Failure, CPLE_NotSupported,
180 : "Type of field '%s' is not one of those supported for "
181 : "a key field: String, Integer, Integer64, Real",
182 : key.c_str());
183 1 : return false;
184 : }
185 :
186 : const int nDstIdx =
187 5 : poDstLayer->GetLayerDefn()->GetFieldIndex(key.c_str());
188 5 : if (nDstIdx < 0)
189 : {
190 1 : ReportError(CE_Failure, CPLE_AppDefined,
191 : "Cannot find field '%s' in output layer", key.c_str());
192 1 : return false;
193 : }
194 : const auto poDstFieldDefn =
195 4 : poDstLayer->GetLayerDefn()->GetFieldDefn(nDstIdx);
196 4 : if (poDstFieldDefn->GetType() != eType)
197 : {
198 0 : ReportError(
199 : CE_Failure, CPLE_NotSupported,
200 : "Type of field '%s' is not the same in input and output layers",
201 : key.c_str());
202 0 : return false;
203 : }
204 4 : keyFieldTypes.push_back(eType);
205 : }
206 :
207 13 : const bool bFIDMatch = m_key.size() == 1 &&
208 6 : EQUAL(m_key[0].c_str(), SpecialFieldNames[SPF_FID]);
209 : const GIntBig nFeatureCount =
210 7 : ctxt.m_pfnProgress ? poSrcLayer->GetFeatureCount(true) : -1;
211 :
212 7 : std::string osFilter;
213 7 : int nIter = 0;
214 7 : bool bRet = true;
215 21 : for (const auto &poSrcFeature : *poSrcLayer)
216 : {
217 14 : ++nIter;
218 20 : if (ctxt.m_pfnProgress && nFeatureCount > 0 &&
219 6 : !ctxt.m_pfnProgress(static_cast<double>(nIter) / nFeatureCount, "",
220 : ctxt.m_pProgressData))
221 : {
222 1 : ReportError(CE_Failure, CPLE_UserInterrupt, "Interrupted by user");
223 1 : bRet = false;
224 1 : break;
225 : }
226 :
227 0 : std::unique_ptr<OGRFeature> poDstFeature;
228 13 : if (bFIDMatch)
229 : {
230 16 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
231 8 : poDstFeature.reset(poDstLayer->GetFeature(poSrcFeature->GetFID()));
232 : }
233 : else
234 : {
235 5 : bool bSkip = false;
236 5 : osFilter.clear();
237 21 : for (size_t iField = 0; iField < srcKeyFieldIndices.size();
238 : ++iField)
239 : {
240 17 : const int nSrcFieldIdx = srcKeyFieldIndices[iField];
241 17 : if (!poSrcFeature->IsFieldSet(nSrcFieldIdx))
242 : {
243 1 : bSkip = true;
244 1 : break;
245 : }
246 16 : if (!osFilter.empty())
247 12 : osFilter += " AND ";
248 16 : osFilter += CPLString(m_key[iField]).SQLQuotedIdentifier();
249 16 : osFilter += " = ";
250 16 : switch (keyFieldTypes[iField])
251 : {
252 4 : case OFTString:
253 : {
254 4 : osFilter += CPLString(poSrcFeature->GetFieldAsString(
255 : nSrcFieldIdx))
256 4 : .SQLQuotedLiteral();
257 4 : break;
258 : }
259 :
260 4 : case OFTReal:
261 : {
262 : osFilter += CPLSPrintf(
263 : "%.17g",
264 4 : poSrcFeature->GetFieldAsDouble(nSrcFieldIdx));
265 4 : break;
266 : }
267 :
268 8 : default:
269 : {
270 : osFilter += CPLSPrintf(
271 : CPL_FRMT_GIB,
272 8 : poSrcFeature->GetFieldAsInteger64(nSrcFieldIdx));
273 8 : break;
274 : }
275 : }
276 : }
277 5 : if (bSkip)
278 1 : continue;
279 4 : if (poDstLayer->SetAttributeFilter(osFilter.c_str()) != OGRERR_NONE)
280 : {
281 0 : bRet = false;
282 0 : break;
283 : }
284 4 : poDstFeature.reset(poDstLayer->GetNextFeature());
285 4 : if (poDstFeature)
286 : {
287 : // Check there is only one feature matching the criterion
288 3 : if (std::unique_ptr<OGRFeature>(poDstLayer->GetNextFeature()))
289 : {
290 1 : poDstFeature.reset();
291 : }
292 : else
293 : {
294 2 : CPLDebugOnly("GDAL",
295 : "Updating output feature " CPL_FRMT_GIB
296 : " with src input " CPL_FRMT_GIB,
297 : poDstFeature->GetFID(),
298 : poSrcFeature->GetFID());
299 : }
300 : }
301 : }
302 :
303 12 : if (poDstFeature)
304 : {
305 6 : if (m_mode != MODE_APPEND_ONLY)
306 : {
307 : auto poDstFeatureOri =
308 5 : std::unique_ptr<OGRFeature>(poDstFeature->Clone());
309 5 : const auto nDstFID = poDstFeature->GetFID();
310 5 : poDstFeature->SetFrom(poSrcFeature.get());
311 : // restore FID unset by SetFrom()
312 5 : poDstFeature->SetFID(nDstFID);
313 10 : if (!poDstFeature->Equal(poDstFeatureOri.get()) &&
314 5 : poDstLayer->SetFeature(poDstFeature.get()) != OGRERR_NONE)
315 : {
316 0 : bRet = false;
317 0 : break;
318 : }
319 : }
320 : }
321 6 : else if (m_mode != MODE_UPDATE_ONLY)
322 : {
323 : poDstFeature =
324 3 : std::make_unique<OGRFeature>(poDstLayer->GetLayerDefn());
325 3 : poDstFeature->SetFrom(poSrcFeature.get());
326 3 : if (poDstLayer->CreateFeature(poDstFeature.get()) != OGRERR_NONE)
327 : {
328 0 : bRet = false;
329 0 : break;
330 : }
331 : }
332 : }
333 :
334 7 : poDstLayer->SetAttributeFilter(nullptr);
335 :
336 7 : return bRet;
337 : }
338 :
339 : /************************************************************************/
340 : /* ~GDALVectorUpdateAlgorithmStandalone() */
341 : /************************************************************************/
342 :
343 : GDALVectorUpdateAlgorithmStandalone::~GDALVectorUpdateAlgorithmStandalone() =
344 : default;
345 :
346 : /************************************************************************/
347 : /* GDALVectorUpdateAlgorithmStandalone::RunImpl() */
348 : /************************************************************************/
349 :
350 16 : bool GDALVectorUpdateAlgorithmStandalone::RunImpl(GDALProgressFunc pfnProgress,
351 : void *pProgressData)
352 : {
353 16 : GDALPipelineStepRunContext stepCtxt;
354 16 : stepCtxt.m_pfnProgress = pfnProgress;
355 16 : stepCtxt.m_pProgressData = pProgressData;
356 32 : return RunStep(stepCtxt);
357 : }
358 :
359 : //! @endcond
|