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