Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: gdal "vector pipeline" subcommand
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gdalalg_vector_pipeline.h"
14 : #include "gdalalg_vector_read.h"
15 : #include "gdalalg_vector_clip.h"
16 : #include "gdalalg_vector_filter.h"
17 : #include "gdalalg_vector_reproject.h"
18 : #include "gdalalg_vector_select.h"
19 : #include "gdalalg_vector_sql.h"
20 : #include "gdalalg_vector_write.h"
21 :
22 : #include "cpl_conv.h"
23 : #include "cpl_string.h"
24 :
25 : #include <algorithm>
26 : #include <cassert>
27 :
28 : //! @cond Doxygen_Suppress
29 :
30 : #ifndef _
31 : #define _(x) (x)
32 : #endif
33 :
34 : /************************************************************************/
35 : /* GDALVectorPipelineStepAlgorithm::GDALVectorPipelineStepAlgorithm() */
36 : /************************************************************************/
37 :
38 356 : GDALVectorPipelineStepAlgorithm::GDALVectorPipelineStepAlgorithm(
39 : const std::string &name, const std::string &description,
40 356 : const std::string &helpURL, bool standaloneStep)
41 : : GDALAlgorithm(name, description, helpURL),
42 356 : m_standaloneStep(standaloneStep)
43 : {
44 356 : if (m_standaloneStep)
45 : {
46 59 : AddInputArgs(false);
47 59 : AddProgressArg();
48 59 : AddOutputArgs(false, false);
49 : }
50 356 : }
51 :
52 : /************************************************************************/
53 : /* GDALVectorPipelineStepAlgorithm::AddInputArgs() */
54 : /************************************************************************/
55 :
56 220 : void GDALVectorPipelineStepAlgorithm::AddInputArgs(bool hiddenForCLI)
57 : {
58 220 : AddInputFormatsArg(&m_inputFormats)
59 660 : .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR})
60 220 : .SetHiddenForCLI(hiddenForCLI);
61 220 : AddOpenOptionsArg(&m_openOptions).SetHiddenForCLI(hiddenForCLI);
62 : AddInputDatasetArg(&m_inputDataset, GDAL_OF_VECTOR,
63 220 : /* positionalAndRequired = */ !hiddenForCLI)
64 220 : .SetHiddenForCLI(hiddenForCLI);
65 220 : if (GetName() != "sql")
66 : {
67 422 : AddArg("input-layer", 'l', _("Input layer name(s)"), &m_inputLayerNames)
68 422 : .AddAlias("layer")
69 211 : .SetHiddenForCLI(hiddenForCLI);
70 : }
71 220 : }
72 :
73 : /************************************************************************/
74 : /* GDALVectorPipelineStepAlgorithm::AddOutputArgs() */
75 : /************************************************************************/
76 :
77 218 : void GDALVectorPipelineStepAlgorithm::AddOutputArgs(
78 : bool hiddenForCLI, bool shortNameOutputLayerAllowed)
79 : {
80 218 : AddOutputFormatArg(&m_format, true)
81 : .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
82 872 : {GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE})
83 218 : .SetHiddenForCLI(hiddenForCLI);
84 : AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR,
85 218 : /* positionalAndRequired = */ !hiddenForCLI)
86 218 : .SetHiddenForCLI(hiddenForCLI);
87 218 : m_outputDataset.SetInputFlags(GADV_NAME | GADV_OBJECT);
88 218 : AddCreationOptionsArg(&m_creationOptions).SetHiddenForCLI(hiddenForCLI);
89 218 : AddLayerCreationOptionsArg(&m_layerCreationOptions)
90 218 : .SetHiddenForCLI(hiddenForCLI);
91 218 : AddOverwriteArg(&m_overwrite).SetHiddenForCLI(hiddenForCLI);
92 218 : AddUpdateArg(&m_update).SetHiddenForCLI(hiddenForCLI);
93 : AddArg("overwrite-layer", 0,
94 : _("Whether overwriting existing layer is allowed"),
95 436 : &m_overwriteLayer)
96 436 : .SetDefault(false)
97 218 : .SetHiddenForCLI(hiddenForCLI);
98 : AddArg("append", 0, _("Whether appending to existing layer is allowed"),
99 436 : &m_appendLayer)
100 436 : .SetDefault(false)
101 218 : .SetHiddenForCLI(hiddenForCLI);
102 218 : if (GetName() != "sql")
103 : {
104 : AddArg("output-layer", shortNameOutputLayerAllowed ? 'l' : 0,
105 418 : _("Output layer name"), &m_outputLayerName)
106 418 : .AddHiddenAlias("nln") // For ogr2ogr nostalgic people
107 209 : .SetHiddenForCLI(hiddenForCLI);
108 : }
109 218 : }
110 :
111 : /************************************************************************/
112 : /* GDALVectorPipelineStepAlgorithm::RunImpl() */
113 : /************************************************************************/
114 :
115 237 : bool GDALVectorPipelineStepAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
116 : void *pProgressData)
117 : {
118 237 : if (m_standaloneStep)
119 : {
120 108 : GDALVectorReadAlgorithm readAlg;
121 540 : for (auto &arg : readAlg.GetArgs())
122 : {
123 486 : auto stepArg = GetArg(arg->GetName());
124 486 : if (stepArg && stepArg->IsExplicitlySet())
125 : {
126 54 : arg->SetSkipIfAlreadySet(true);
127 54 : arg->SetFrom(*stepArg);
128 : }
129 : }
130 :
131 54 : GDALVectorWriteAlgorithm writeAlg;
132 810 : for (auto &arg : writeAlg.GetArgs())
133 : {
134 756 : auto stepArg = GetArg(arg->GetName());
135 756 : if (stepArg && stepArg->IsExplicitlySet())
136 : {
137 92 : arg->SetSkipIfAlreadySet(true);
138 92 : arg->SetFrom(*stepArg);
139 : }
140 : }
141 :
142 54 : bool ret = false;
143 54 : if (readAlg.Run())
144 : {
145 54 : m_inputDataset.Set(readAlg.m_outputDataset.GetDatasetRef());
146 54 : m_outputDataset.Set(nullptr);
147 54 : if (RunStep(nullptr, nullptr))
148 : {
149 41 : if (m_format == "stream")
150 : {
151 3 : ret = true;
152 : }
153 : else
154 : {
155 38 : writeAlg.m_inputDataset.Set(
156 : m_outputDataset.GetDatasetRef());
157 38 : if (writeAlg.Run(pfnProgress, pProgressData))
158 : {
159 37 : m_outputDataset.Set(
160 : writeAlg.m_outputDataset.GetDatasetRef());
161 37 : ret = true;
162 : }
163 : }
164 : }
165 : }
166 :
167 54 : return ret;
168 : }
169 : else
170 : {
171 183 : return RunStep(pfnProgress, pProgressData);
172 : }
173 : }
174 :
175 : /************************************************************************/
176 : /* GDALVectorPipelineAlgorithm::GDALVectorPipelineAlgorithm() */
177 : /************************************************************************/
178 :
179 59 : GDALVectorPipelineAlgorithm::GDALVectorPipelineAlgorithm()
180 : : GDALAbstractPipelineAlgorithm<GDALVectorPipelineStepAlgorithm>(
181 : NAME, DESCRIPTION, HELP_URL,
182 59 : /*standaloneStep=*/false)
183 : {
184 59 : AddInputArgs(/* hiddenForCLI = */ true);
185 59 : AddProgressArg();
186 118 : AddArg("pipeline", 0, _("Pipeline string"), &m_pipeline)
187 59 : .SetHiddenForCLI()
188 59 : .SetPositional();
189 59 : AddOutputArgs(/* hiddenForCLI = */ true,
190 : /* shortNameOutputLayerAllowed=*/false);
191 :
192 59 : m_stepRegistry.Register<GDALVectorReadAlgorithm>();
193 59 : m_stepRegistry.Register<GDALVectorWriteAlgorithm>();
194 59 : m_stepRegistry.Register<GDALVectorClipAlgorithm>();
195 59 : m_stepRegistry.Register<GDALVectorReprojectAlgorithm>();
196 59 : m_stepRegistry.Register<GDALVectorFilterAlgorithm>();
197 59 : m_stepRegistry.Register<GDALVectorSelectAlgorithm>();
198 59 : m_stepRegistry.Register<GDALVectorSQLAlgorithm>();
199 59 : }
200 :
201 : /************************************************************************/
202 : /* GDALVectorPipelineAlgorithm::ParseCommandLineArguments() */
203 : /************************************************************************/
204 :
205 46 : bool GDALVectorPipelineAlgorithm::ParseCommandLineArguments(
206 : const std::vector<std::string> &args)
207 : {
208 52 : if (args.size() == 1 && (args[0] == "-h" || args[0] == "--help" ||
209 6 : args[0] == "help" || args[0] == "--json-usage"))
210 1 : return GDALAlgorithm::ParseCommandLineArguments(args);
211 :
212 314 : for (const auto &arg : args)
213 : {
214 271 : if (arg.find("--pipeline") == 0)
215 2 : return GDALAlgorithm::ParseCommandLineArguments(args);
216 :
217 : // gdal vector pipeline [--progress] "read poly.gpkg ..."
218 270 : if (arg.find("read ") == 0)
219 1 : return GDALAlgorithm::ParseCommandLineArguments(args);
220 : }
221 :
222 43 : if (!m_steps.empty())
223 : {
224 1 : ReportError(CE_Failure, CPLE_AppDefined,
225 : "ParseCommandLineArguments() can only be called once per "
226 : "instance.");
227 1 : return false;
228 : }
229 :
230 : struct Step
231 : {
232 : std::unique_ptr<GDALVectorPipelineStepAlgorithm> alg{};
233 : std::vector<std::string> args{};
234 : };
235 :
236 84 : std::vector<Step> steps;
237 42 : steps.resize(1);
238 :
239 302 : for (const auto &arg : args)
240 : {
241 261 : if (arg == "--progress")
242 : {
243 1 : m_progressBarRequested = true;
244 1 : continue;
245 : }
246 :
247 260 : auto &curStep = steps.back();
248 :
249 260 : if (arg == "!" || arg == "|")
250 : {
251 55 : if (curStep.alg)
252 : {
253 53 : steps.resize(steps.size() + 1);
254 : }
255 : }
256 : #ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
257 205 : else if (arg == "+step")
258 : {
259 2 : if (curStep.alg)
260 : {
261 1 : steps.resize(steps.size() + 1);
262 : }
263 : }
264 203 : else if (arg.find("+gdal=") == 0)
265 : {
266 1 : const std::string stepName = arg.substr(strlen("+gdal="));
267 1 : curStep.alg = GetStepAlg(stepName);
268 1 : if (!curStep.alg)
269 : {
270 0 : ReportError(CE_Failure, CPLE_AppDefined,
271 : "unknown step name: %s", stepName.c_str());
272 0 : return false;
273 : }
274 : }
275 : #endif
276 202 : else if (!curStep.alg)
277 : {
278 93 : std::string algName = arg;
279 : #ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
280 93 : if (!algName.empty() && algName[0] == '+')
281 1 : algName = algName.substr(1);
282 : #endif
283 93 : curStep.alg = GetStepAlg(algName);
284 93 : if (!curStep.alg)
285 : {
286 1 : ReportError(CE_Failure, CPLE_AppDefined,
287 : "unknown step name: %s", algName.c_str());
288 1 : return false;
289 : }
290 : }
291 : else
292 : {
293 : #ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
294 109 : if (!arg.empty() && arg[0] == '+')
295 : {
296 2 : curStep.args.push_back("--" + arg.substr(1));
297 2 : continue;
298 : }
299 : #endif
300 214 : std::string value = arg;
301 :
302 : // #define GDAL_PIPELINE_NATURAL_LANGUAGE
303 : #ifdef GDAL_PIPELINE_NATURAL_LANGUAGE
304 : // gdal vector pipeline "read [from] poly.gpkg, reproject [from EPSG:4326] to EPSG:32632 and write to out.gpkg with overwriting"
305 : if (value == "and")
306 : {
307 : steps.resize(steps.size() + 1);
308 : }
309 : else if (value == "from" && curStep.alg->GetName() == "read")
310 : {
311 : // do nothing
312 : }
313 : else if (value == "from" && curStep.alg->GetName() == "reproject")
314 : {
315 : curStep.args.push_back("--src-crs");
316 : }
317 : else if (value == "to" && curStep.alg->GetName() == "reproject")
318 : {
319 : curStep.args.push_back("--dst-crs");
320 : }
321 : else if (value == "to" && curStep.alg->GetName() == "write")
322 : {
323 : // do nothing
324 : }
325 : else if (value == "with" && curStep.alg->GetName() == "write")
326 : {
327 : // do nothing
328 : }
329 : else if (value == "overwriting" &&
330 : curStep.alg->GetName() == "write")
331 : {
332 : curStep.args.push_back("--overwrite");
333 : }
334 : else if (!value.empty() && value.back() == ',')
335 : {
336 : curStep.args.push_back(value.substr(0, value.size() - 1));
337 : steps.resize(steps.size() + 1);
338 : }
339 : else
340 : #endif
341 :
342 : {
343 107 : curStep.args.push_back(value);
344 : }
345 : }
346 : }
347 :
348 : // As we initially added a step without alg to bootstrap things, make
349 : // sure to remove it if it hasn't been filled, or the user has terminated
350 : // the pipeline with a '!' separator.
351 41 : if (!steps.back().alg)
352 2 : steps.pop_back();
353 :
354 41 : if (steps.size() < 2)
355 : {
356 2 : ReportError(CE_Failure, CPLE_AppDefined,
357 : "At least 2 steps must be provided");
358 2 : return false;
359 : }
360 :
361 39 : if (steps.front().alg->GetName() != GDALVectorReadAlgorithm::NAME)
362 : {
363 1 : ReportError(CE_Failure, CPLE_AppDefined, "First step should be '%s'",
364 : GDALVectorReadAlgorithm::NAME);
365 1 : return false;
366 : }
367 50 : for (size_t i = 1; i < steps.size() - 1; ++i)
368 : {
369 13 : if (steps[i].alg->GetName() == GDALVectorReadAlgorithm::NAME)
370 : {
371 1 : ReportError(CE_Failure, CPLE_AppDefined,
372 : "Only first step can be '%s'",
373 : GDALVectorReadAlgorithm::NAME);
374 1 : return false;
375 : }
376 : }
377 37 : if (steps.back().alg->GetName() != GDALVectorWriteAlgorithm::NAME)
378 : {
379 1 : ReportError(CE_Failure, CPLE_AppDefined, "Last step should be '%s'",
380 : GDALVectorWriteAlgorithm::NAME);
381 1 : return false;
382 : }
383 83 : for (size_t i = 0; i < steps.size() - 1; ++i)
384 : {
385 48 : if (steps[i].alg->GetName() == GDALVectorWriteAlgorithm::NAME)
386 : {
387 1 : ReportError(CE_Failure, CPLE_AppDefined,
388 : "Only last step can be '%s'",
389 : GDALVectorWriteAlgorithm::NAME);
390 1 : return false;
391 : }
392 : }
393 :
394 35 : if (!m_pipeline.empty())
395 : {
396 : // Propagate input parameters set at the pipeline level to the
397 : // "read" step
398 : {
399 7 : auto &step = steps.front();
400 70 : for (auto &arg : step.alg->GetArgs())
401 : {
402 63 : auto pipelineArg = GetArg(arg->GetName());
403 63 : if (pipelineArg && pipelineArg->IsExplicitlySet())
404 : {
405 3 : arg->SetSkipIfAlreadySet(true);
406 3 : arg->SetFrom(*pipelineArg);
407 : }
408 : }
409 : }
410 :
411 : // Same with "write" step
412 : {
413 7 : auto &step = steps.back();
414 105 : for (auto &arg : step.alg->GetArgs())
415 : {
416 98 : auto pipelineArg = GetArg(arg->GetName());
417 98 : if (pipelineArg && pipelineArg->IsExplicitlySet())
418 : {
419 1 : arg->SetSkipIfAlreadySet(true);
420 1 : arg->SetFrom(*pipelineArg);
421 : }
422 : }
423 : }
424 : }
425 :
426 : // Parse each step, but without running the validation
427 105 : for (const auto &step : steps)
428 : {
429 76 : step.alg->m_skipValidationInParseCommandLine = true;
430 76 : if (!step.alg->ParseCommandLineArguments(step.args))
431 6 : return false;
432 : }
433 :
434 : // Evaluate "input" argument of "read" step, together with the "output"
435 : // argument of the "write" step, in case they point to the same dataset.
436 29 : auto inputArg = steps.front().alg->GetArg(GDAL_ARG_NAME_INPUT);
437 58 : if (inputArg && inputArg->IsExplicitlySet() &&
438 29 : inputArg->GetType() == GAAT_DATASET)
439 : {
440 29 : steps.front().alg->ProcessDatasetArg(inputArg, steps.back().alg.get());
441 : }
442 :
443 92 : for (const auto &step : steps)
444 : {
445 64 : if (!step.alg->ValidateArguments())
446 1 : return false;
447 : }
448 :
449 90 : for (auto &step : steps)
450 62 : m_steps.push_back(std::move(step.alg));
451 :
452 28 : return true;
453 : }
454 :
455 : /************************************************************************/
456 : /* GDALVectorPipelineAlgorithm::GetUsageForCLI() */
457 : /************************************************************************/
458 :
459 2 : std::string GDALVectorPipelineAlgorithm::GetUsageForCLI(
460 : bool shortUsage, const UsageOptions &usageOptions) const
461 : {
462 2 : std::string ret = GDALAlgorithm::GetUsageForCLI(shortUsage, usageOptions);
463 2 : if (shortUsage)
464 1 : return ret;
465 :
466 : ret += "\n<PIPELINE> is of the form: read [READ-OPTIONS] "
467 1 : "( ! <STEP-NAME> [STEP-OPTIONS] )* ! write [WRITE-OPTIONS]\n";
468 1 : ret += '\n';
469 1 : ret += "Example: 'gdal vector pipeline --progress ! read in.gpkg ! \\\n";
470 1 : ret += " reproject --dst-crs=EPSG:32632 ! ";
471 1 : ret += "write out.gpkg --overwrite'\n";
472 1 : ret += '\n';
473 1 : ret += "Potential steps are:\n";
474 :
475 1 : UsageOptions stepUsageOptions;
476 1 : stepUsageOptions.isPipelineStep = true;
477 :
478 8 : for (const std::string &name : m_stepRegistry.GetNames())
479 : {
480 14 : auto alg = GetStepAlg(name);
481 7 : assert(alg);
482 7 : auto [options, maxOptLen] = alg->GetArgNamesForCLI();
483 7 : stepUsageOptions.maxOptLen =
484 7 : std::max(stepUsageOptions.maxOptLen, maxOptLen);
485 : }
486 :
487 : {
488 1 : const auto name = GDALVectorReadAlgorithm::NAME;
489 1 : ret += '\n';
490 2 : auto alg = GetStepAlg(name);
491 1 : assert(alg);
492 2 : alg->SetCallPath({name});
493 1 : ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
494 : }
495 8 : for (const std::string &name : m_stepRegistry.GetNames())
496 : {
497 13 : if (name != GDALVectorReadAlgorithm::NAME &&
498 6 : name != GDALVectorWriteAlgorithm::NAME)
499 : {
500 5 : ret += '\n';
501 5 : auto alg = GetStepAlg(name);
502 5 : assert(alg);
503 10 : alg->SetCallPath({name});
504 5 : ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
505 : }
506 : }
507 : {
508 1 : const auto name = GDALVectorWriteAlgorithm::NAME;
509 1 : ret += '\n';
510 2 : auto alg = GetStepAlg(name);
511 1 : assert(alg);
512 2 : alg->SetCallPath({name});
513 1 : ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
514 : }
515 :
516 1 : return ret;
517 : }
518 :
519 : /************************************************************************/
520 : /* GDALVectorPipelineOutputLayer */
521 : /************************************************************************/
522 :
523 : /************************************************************************/
524 : /* GDALVectorPipelineOutputLayer() */
525 : /************************************************************************/
526 :
527 28 : GDALVectorPipelineOutputLayer::GDALVectorPipelineOutputLayer(OGRLayer &srcLayer)
528 28 : : m_srcLayer(srcLayer)
529 : {
530 28 : }
531 :
532 : /************************************************************************/
533 : /* GDALVectorPipelineOutputLayer::ResetReading() */
534 : /************************************************************************/
535 :
536 22 : void GDALVectorPipelineOutputLayer::ResetReading()
537 : {
538 22 : m_srcLayer.ResetReading();
539 22 : m_pendingFeatures.clear();
540 22 : m_idxInPendingFeatures = 0;
541 22 : }
542 :
543 : /************************************************************************/
544 : /* GDALVectorPipelineOutputLayer::GetNextRawFeature() */
545 : /************************************************************************/
546 :
547 90 : OGRFeature *GDALVectorPipelineOutputLayer::GetNextRawFeature()
548 : {
549 90 : if (m_idxInPendingFeatures < m_pendingFeatures.size())
550 : {
551 : OGRFeature *poFeature =
552 1 : m_pendingFeatures[m_idxInPendingFeatures].release();
553 1 : ++m_idxInPendingFeatures;
554 1 : return poFeature;
555 : }
556 89 : m_pendingFeatures.clear();
557 89 : m_idxInPendingFeatures = 0;
558 : while (true)
559 : {
560 : auto poSrcFeature =
561 90 : std::unique_ptr<OGRFeature>(m_srcLayer.GetNextFeature());
562 90 : if (!poSrcFeature)
563 22 : return nullptr;
564 68 : TranslateFeature(std::move(poSrcFeature), m_pendingFeatures);
565 68 : if (!m_pendingFeatures.empty())
566 67 : break;
567 1 : }
568 67 : OGRFeature *poFeature = m_pendingFeatures[0].release();
569 67 : m_idxInPendingFeatures = 1;
570 67 : return poFeature;
571 : }
572 :
573 : /************************************************************************/
574 : /* GDALVectorPipelineOutputDataset */
575 : /************************************************************************/
576 :
577 : /************************************************************************/
578 : /* GDALVectorPipelineOutputDataset() */
579 : /************************************************************************/
580 :
581 36 : GDALVectorPipelineOutputDataset::GDALVectorPipelineOutputDataset(
582 36 : GDALDataset &srcDS)
583 36 : : m_srcDS(srcDS)
584 : {
585 36 : SetDescription(m_srcDS.GetDescription());
586 36 : }
587 :
588 : /************************************************************************/
589 : /* GDALVectorPipelineOutputDataset::AddLayer() */
590 : /************************************************************************/
591 :
592 42 : void GDALVectorPipelineOutputDataset::AddLayer(
593 : OGRLayer &oSrcLayer,
594 : std::unique_ptr<OGRLayerWithTranslateFeature> poNewLayer)
595 : {
596 42 : m_layersToDestroy.push_back(std::move(poNewLayer));
597 : OGRLayerWithTranslateFeature *poNewLayerRaw =
598 42 : m_layersToDestroy.back().get();
599 42 : m_layers.push_back(poNewLayerRaw);
600 42 : m_mapSrcLayerToNewLayer[&oSrcLayer] = poNewLayerRaw;
601 42 : }
602 :
603 : /************************************************************************/
604 : /* GDALVectorPipelineOutputDataset::GetLayerCount() */
605 : /************************************************************************/
606 :
607 68 : int GDALVectorPipelineOutputDataset::GetLayerCount()
608 : {
609 68 : return static_cast<int>(m_layers.size());
610 : }
611 :
612 : /************************************************************************/
613 : /* GDALVectorPipelineOutputDataset::GetLayer() */
614 : /************************************************************************/
615 :
616 31 : OGRLayer *GDALVectorPipelineOutputDataset::GetLayer(int idx)
617 : {
618 31 : return idx >= 0 && idx < GetLayerCount() ? m_layers[idx] : nullptr;
619 : }
620 :
621 : /************************************************************************/
622 : /* GDALVectorPipelineOutputDataset::TestCapability() */
623 : /************************************************************************/
624 :
625 32 : int GDALVectorPipelineOutputDataset::TestCapability(const char *pszCap)
626 : {
627 32 : if (EQUAL(pszCap, ODsCRandomLayerRead))
628 : {
629 32 : return m_srcDS.TestCapability(pszCap);
630 : }
631 0 : return false;
632 : }
633 :
634 : /************************************************************************/
635 : /* GDALVectorPipelineOutputDataset::ResetReading() */
636 : /************************************************************************/
637 :
638 2 : void GDALVectorPipelineOutputDataset::ResetReading()
639 : {
640 2 : m_srcDS.ResetReading();
641 2 : m_pendingFeatures.clear();
642 2 : m_idxInPendingFeatures = 0;
643 2 : }
644 :
645 : /************************************************************************/
646 : /* GDALVectorPipelineOutputDataset::GetNextFeature() */
647 : /************************************************************************/
648 :
649 18 : OGRFeature *GDALVectorPipelineOutputDataset::GetNextFeature(
650 : OGRLayer **ppoBelongingLayer, double *pdfProgressPct,
651 : GDALProgressFunc pfnProgress, void *pProgressData)
652 : {
653 18 : if (m_idxInPendingFeatures < m_pendingFeatures.size())
654 : {
655 : OGRFeature *poFeature =
656 0 : m_pendingFeatures[m_idxInPendingFeatures].release();
657 0 : if (ppoBelongingLayer)
658 0 : *ppoBelongingLayer = m_belongingLayer;
659 0 : ++m_idxInPendingFeatures;
660 0 : return poFeature;
661 : }
662 :
663 18 : m_pendingFeatures.clear();
664 18 : m_idxInPendingFeatures = 0;
665 :
666 : while (true)
667 : {
668 18 : OGRLayer *poSrcBelongingLayer = nullptr;
669 18 : auto poSrcFeature = std::unique_ptr<OGRFeature>(m_srcDS.GetNextFeature(
670 18 : &poSrcBelongingLayer, pdfProgressPct, pfnProgress, pProgressData));
671 18 : if (!poSrcFeature)
672 2 : return nullptr;
673 16 : auto iterToDstLayer = m_mapSrcLayerToNewLayer.find(poSrcBelongingLayer);
674 16 : if (iterToDstLayer != m_mapSrcLayerToNewLayer.end())
675 : {
676 16 : m_belongingLayer = iterToDstLayer->second;
677 16 : m_belongingLayer->TranslateFeature(std::move(poSrcFeature),
678 16 : m_pendingFeatures);
679 16 : if (!m_pendingFeatures.empty())
680 16 : break;
681 : }
682 0 : }
683 16 : OGRFeature *poFeature = m_pendingFeatures[0].release();
684 16 : if (ppoBelongingLayer)
685 16 : *ppoBelongingLayer = m_belongingLayer;
686 16 : m_idxInPendingFeatures = 1;
687 16 : return poFeature;
688 : }
689 :
690 : //! @endcond
|