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