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