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