Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: gdal "raster/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 : #ifndef GDALALG_ABSTRACT_PIPELINE_INCLUDED
14 : #define GDALALG_ABSTRACT_PIPELINE_INCLUDED
15 :
16 : //! @cond Doxygen_Suppress
17 :
18 : #include "cpl_conv.h"
19 : #include "cpl_json.h"
20 : #include "gdalalgorithm.h"
21 : #include "gdal_priv.h"
22 :
23 : #include <algorithm>
24 :
25 : /************************************************************************/
26 : /* GDALPipelineStepRunContext */
27 : /************************************************************************/
28 :
29 : class GDALPipelineStepAlgorithm;
30 :
31 : class GDALPipelineStepRunContext
32 : {
33 : public:
34 : GDALPipelineStepRunContext() = default;
35 :
36 : // Progress callback to use during execution of the step
37 : GDALProgressFunc m_pfnProgress = nullptr;
38 : void *m_pProgressData = nullptr;
39 :
40 : // If there is a step in the pipeline immediately following step to which
41 : // this instance of GDALRasterPipelineStepRunContext is passed, and that
42 : // this next step is usable by the current step (as determined by
43 : // CanHandleNextStep()), then this member will point to this next step.
44 : GDALPipelineStepAlgorithm *m_poNextUsableStep = nullptr;
45 :
46 : private:
47 : CPL_DISALLOW_COPY_ASSIGN(GDALPipelineStepRunContext)
48 : };
49 :
50 : /************************************************************************/
51 : /* GDALAbstractPipelineAlgorithm */
52 : /************************************************************************/
53 :
54 : template <class StepAlgorithm>
55 : class GDALAbstractPipelineAlgorithm CPL_NON_FINAL : public StepAlgorithm
56 : {
57 : public:
58 : std::vector<std::string> GetAutoComplete(std::vector<std::string> &args,
59 : bool lastWordIsComplete,
60 : bool /* showAllOptions*/) override;
61 :
62 : bool Finalize() override;
63 :
64 : std::string GetUsageAsJSON() const override;
65 :
66 : /* cppcheck-suppress functionStatic */
67 : void SetDataset(GDALDataset *)
68 : {
69 : }
70 :
71 : protected:
72 : GDALAbstractPipelineAlgorithm(const std::string &name,
73 : const std::string &description,
74 : const std::string &helpURL,
75 : bool standaloneStep)
76 : : StepAlgorithm(name, description, helpURL, standaloneStep)
77 : {
78 : }
79 :
80 226 : GDALAbstractPipelineAlgorithm(
81 : const std::string &name, const std::string &description,
82 : const std::string &helpURL,
83 : const typename StepAlgorithm::ConstructorOptions &options)
84 226 : : StepAlgorithm(name, description, helpURL, options)
85 : {
86 226 : }
87 :
88 226 : ~GDALAbstractPipelineAlgorithm() override
89 : {
90 : // Destroy steps in the reverse order they have been constructed,
91 : // as a step can create object that depends on the validity of
92 : // objects of previous steps, and while cleaning them it needs those
93 : // prior objects to be still alive.
94 : // Typically for "gdal vector pipeline read ... ! sql ..."
95 504 : for (auto it = std::rbegin(m_steps); it != std::rend(m_steps); it++)
96 : {
97 278 : it->reset();
98 : }
99 452 : }
100 :
101 : std::string m_pipeline{};
102 : GDALAlgorithmRegistry m_stepRegistry{};
103 : std::vector<std::unique_ptr<StepAlgorithm>> m_steps{};
104 : std::unique_ptr<StepAlgorithm> m_stepOnWhichHelpIsRequested{};
105 :
106 : std::unique_ptr<StepAlgorithm> GetStepAlg(const std::string &name) const;
107 :
108 : bool CheckFirstStep(const std::vector<StepAlgorithm *> &steps) const;
109 :
110 : static constexpr const char *RASTER_SUFFIX = "-raster";
111 : static constexpr const char *VECTOR_SUFFIX = "-vector";
112 :
113 : private:
114 : bool RunStep(GDALPipelineStepRunContext &ctxt) override;
115 : };
116 :
117 : /************************************************************************/
118 : /* GDALPipelineStepAlgorithm */
119 : /************************************************************************/
120 :
121 : class GDALPipelineStepAlgorithm /* non final */ : public GDALAlgorithm
122 : {
123 : public:
124 19 : const std::vector<GDALArgDatasetValue> &GetInputDatasets() const
125 : {
126 19 : return m_inputDataset;
127 : }
128 :
129 47 : const GDALArgDatasetValue &GetOutputDataset() const
130 : {
131 47 : return m_outputDataset;
132 : }
133 :
134 146 : const std::string &GetOutputFormat() const
135 : {
136 146 : return m_format;
137 : }
138 :
139 47 : const std::vector<std::string> &GetCreationOptions() const
140 : {
141 47 : return m_creationOptions;
142 : }
143 :
144 : virtual int GetInputType() const = 0;
145 :
146 : virtual int GetOutputType() const = 0;
147 :
148 : protected:
149 : struct ConstructorOptions
150 : {
151 : bool standaloneStep = false;
152 : bool addDefaultArguments = true;
153 : bool autoOpenInputDatasets = true;
154 : bool outputDatasetRequired = true;
155 : bool addInputLayerNameArgument = true; // only for vector input
156 : int inputDatasetMaxCount = 1;
157 : std::string inputDatasetHelpMsg{};
158 : std::string inputDatasetAlias{};
159 : std::string inputDatasetMetaVar = "INPUT";
160 : std::string outputDatasetHelpMsg{};
161 : std::string updateMutualExclusionGroup{};
162 : std::string outputDatasetMutualExclusionGroup{};
163 : std::string outputFormatCreateCapability = GDAL_DCAP_CREATECOPY;
164 :
165 2842 : inline ConstructorOptions &SetStandaloneStep(bool b)
166 : {
167 2842 : standaloneStep = b;
168 2842 : return *this;
169 : }
170 :
171 801 : inline ConstructorOptions &SetAddDefaultArguments(bool b)
172 : {
173 801 : addDefaultArguments = b;
174 801 : return *this;
175 : }
176 :
177 33 : inline ConstructorOptions &SetAddInputLayerNameArgument(bool b)
178 : {
179 33 : addInputLayerNameArgument = b;
180 33 : return *this;
181 : }
182 :
183 407 : inline ConstructorOptions &SetInputDatasetMaxCount(int maxCount)
184 : {
185 407 : inputDatasetMaxCount = maxCount;
186 407 : return *this;
187 : }
188 :
189 163 : inline ConstructorOptions &SetInputDatasetHelpMsg(const std::string &s)
190 : {
191 163 : inputDatasetHelpMsg = s;
192 163 : return *this;
193 : }
194 :
195 72 : inline ConstructorOptions &SetInputDatasetAlias(const std::string &s)
196 : {
197 72 : inputDatasetAlias = s;
198 72 : return *this;
199 : }
200 :
201 132 : inline ConstructorOptions &SetInputDatasetMetaVar(const std::string &s)
202 : {
203 132 : inputDatasetMetaVar = s;
204 132 : return *this;
205 : }
206 :
207 39 : inline ConstructorOptions &SetOutputDatasetHelpMsg(const std::string &s)
208 : {
209 39 : outputDatasetHelpMsg = s;
210 39 : return *this;
211 : }
212 :
213 184 : inline ConstructorOptions &SetAutoOpenInputDatasets(bool b)
214 : {
215 184 : autoOpenInputDatasets = b;
216 184 : return *this;
217 : }
218 :
219 33 : inline ConstructorOptions &SetOutputDatasetRequired(bool b)
220 : {
221 33 : outputDatasetRequired = b;
222 33 : return *this;
223 : }
224 :
225 : inline ConstructorOptions &
226 33 : SetUpdateMutualExclusionGroup(const std::string &s)
227 : {
228 33 : updateMutualExclusionGroup = s;
229 33 : return *this;
230 : }
231 :
232 : inline ConstructorOptions &
233 33 : SetOutputDatasetMutualExclusionGroup(const std::string &s)
234 : {
235 33 : outputDatasetMutualExclusionGroup = s;
236 33 : return *this;
237 : }
238 :
239 : inline ConstructorOptions &
240 371 : SetOutputFormatCreateCapability(const std::string &capability)
241 : {
242 371 : outputFormatCreateCapability = capability;
243 371 : return *this;
244 : }
245 : };
246 :
247 : GDALPipelineStepAlgorithm(const std::string &name,
248 : const std::string &description,
249 : const std::string &helpURL,
250 : const ConstructorOptions &);
251 :
252 : friend class GDALPipelineAlgorithm;
253 : friend class GDALAbstractPipelineAlgorithm<GDALPipelineStepAlgorithm>;
254 :
255 502 : virtual bool CanBeFirstStep() const
256 : {
257 502 : return false;
258 : }
259 :
260 138 : virtual bool IsNativelyStreamingCompatible() const
261 : {
262 138 : return true;
263 : }
264 :
265 392 : virtual bool CanHandleNextStep(GDALPipelineStepAlgorithm *) const
266 : {
267 392 : return false;
268 : }
269 :
270 : virtual bool RunStep(GDALPipelineStepRunContext &ctxt) = 0;
271 :
272 : bool m_standaloneStep = false;
273 : const ConstructorOptions m_constructorOptions;
274 : bool m_outputVRTCompatible = true;
275 : std::string m_helpDocCategory{};
276 :
277 : // Input arguments
278 : std::vector<GDALArgDatasetValue> m_inputDataset{};
279 : std::vector<std::string> m_openOptions{};
280 : std::vector<std::string> m_inputFormats{};
281 : std::vector<std::string> m_inputLayerNames{};
282 :
283 : // Output arguments
284 : GDALArgDatasetValue m_outputDataset{};
285 : std::string m_format{};
286 : std::vector<std::string> m_creationOptions{};
287 : bool m_overwrite = false;
288 : std::string m_outputLayerName{};
289 : GDALInConstructionAlgorithmArg *m_outputFormatArg = nullptr;
290 :
291 : // Output arguments (vector specific)
292 : std::vector<std::string> m_layerCreationOptions{};
293 : bool m_update = false;
294 : bool m_overwriteLayer = false;
295 : bool m_appendLayer = false;
296 :
297 : void AddRasterInputArgs(bool openForMixedRasterVector, bool hiddenForCLI);
298 : void AddRasterOutputArgs(bool hiddenForCLI);
299 : void AddRasterHiddenInputDatasetArg();
300 :
301 : void AddVectorInputArgs(bool hiddenForCLI);
302 : void AddVectorOutputArgs(bool hiddenForCLI,
303 : bool shortNameOutputLayerAllowed);
304 :
305 : private:
306 : bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override;
307 : GDALAlgorithm::ProcessGDALGOutputRet ProcessGDALGOutput() override;
308 : bool CheckSafeForStreamOutput() override;
309 :
310 : CPL_DISALLOW_COPY_ASSIGN(GDALPipelineStepAlgorithm)
311 : };
312 :
313 : /************************************************************************/
314 : /* GDALAbstractPipelineAlgorithm::CheckFirstStep() */
315 : /************************************************************************/
316 :
317 : template <class StepAlgorithm>
318 150 : bool GDALAbstractPipelineAlgorithm<StepAlgorithm>::CheckFirstStep(
319 : const std::vector<StepAlgorithm *> &steps) const
320 : {
321 150 : if (!steps.front()->CanBeFirstStep())
322 : {
323 15 : std::vector<std::string> firstStepNames{"read"};
324 102 : for (const auto &stepName : m_stepRegistry.GetNames())
325 : {
326 198 : auto alg = GetStepAlg(stepName);
327 99 : if (alg && alg->CanBeFirstStep() && stepName != "read")
328 : {
329 10 : firstStepNames.push_back(stepName);
330 : }
331 : }
332 :
333 3 : std::string msg = "First step should be ";
334 16 : for (size_t i = 0; i < firstStepNames.size(); ++i)
335 : {
336 13 : if (i == firstStepNames.size() - 1)
337 3 : msg += " or ";
338 10 : else if (i > 0)
339 7 : msg += ", ";
340 13 : msg += '\'';
341 13 : msg += firstStepNames[i];
342 13 : msg += '\'';
343 : }
344 :
345 3 : StepAlgorithm::ReportError(CE_Failure, CPLE_AppDefined, "%s",
346 : msg.c_str());
347 3 : return false;
348 : }
349 221 : for (size_t i = 1; i < steps.size() - 1; ++i)
350 : {
351 77 : if (steps[i]->CanBeFirstStep())
352 : {
353 3 : StepAlgorithm::ReportError(CE_Failure, CPLE_AppDefined,
354 : "Only first step can be '%s'",
355 3 : steps[i]->GetName().c_str());
356 3 : return false;
357 : }
358 : }
359 144 : return true;
360 : }
361 :
362 : /************************************************************************/
363 : /* GDALAbstractPipelineAlgorithm::GetStepAlg() */
364 : /************************************************************************/
365 :
366 : template <class StepAlgorithm>
367 : std::unique_ptr<StepAlgorithm>
368 1378 : GDALAbstractPipelineAlgorithm<StepAlgorithm>::GetStepAlg(
369 : const std::string &name) const
370 : {
371 2756 : auto alg = m_stepRegistry.Instantiate(name);
372 : return std::unique_ptr<StepAlgorithm>(
373 2756 : cpl::down_cast<StepAlgorithm *>(alg.release()));
374 : }
375 :
376 : /************************************************************************/
377 : /* GDALAbstractPipelineAlgorithm::GetAutoComplete() */
378 : /************************************************************************/
379 :
380 : template <class StepAlgorithm>
381 : std::vector<std::string>
382 18 : GDALAbstractPipelineAlgorithm<StepAlgorithm>::GetAutoComplete(
383 : std::vector<std::string> &args, bool lastWordIsComplete,
384 : bool /* showAllOptions*/)
385 : {
386 18 : std::vector<std::string> ret;
387 18 : if (args.size() <= 1)
388 : {
389 6 : if (args.empty() || args.front() != "read")
390 4 : ret.push_back("read");
391 : }
392 25 : else if (args.back() == "!" ||
393 25 : (args[args.size() - 2] == "!" && !GetStepAlg(args.back())))
394 : {
395 98 : for (const std::string &name : m_stepRegistry.GetNames())
396 : {
397 94 : if (name != "read")
398 : {
399 90 : ret.push_back(name);
400 : }
401 : }
402 : }
403 : else
404 : {
405 16 : std::string lastStep = "read";
406 16 : std::vector<std::string> lastArgs;
407 27 : for (size_t i = 1; i < args.size(); ++i)
408 : {
409 19 : lastArgs.push_back(args[i]);
410 19 : if (i + 1 < args.size() && args[i] == "!")
411 : {
412 6 : ++i;
413 6 : lastArgs.clear();
414 6 : lastStep = args[i];
415 : }
416 : }
417 :
418 16 : auto curAlg = GetStepAlg(lastStep);
419 8 : if (curAlg)
420 : {
421 8 : ret = curAlg->GetAutoComplete(lastArgs, lastWordIsComplete,
422 : /* showAllOptions = */ false);
423 : }
424 : }
425 18 : return ret;
426 : }
427 :
428 : /************************************************************************/
429 : /* GDALAbstractPipelineAlgorithm::RunStep() */
430 : /************************************************************************/
431 :
432 : template <class StepAlgorithm>
433 132 : bool GDALAbstractPipelineAlgorithm<StepAlgorithm>::RunStep(
434 : GDALPipelineStepRunContext &ctxt)
435 : {
436 132 : if (m_stepOnWhichHelpIsRequested)
437 : {
438 4 : printf(
439 : "%s",
440 4 : m_stepOnWhichHelpIsRequested->GetUsageForCLI(false).c_str()); /*ok*/
441 4 : return true;
442 : }
443 :
444 128 : if (m_steps.empty())
445 : {
446 : // If invoked programmatically, not from the command line.
447 :
448 63 : if (m_pipeline.empty())
449 : {
450 2 : StepAlgorithm::ReportError(CE_Failure, CPLE_AppDefined,
451 : "'pipeline' argument not set");
452 16 : return false;
453 : }
454 :
455 61 : const CPLStringList aosTokens(CSLTokenizeString(m_pipeline.c_str()));
456 61 : if (!this->ParseCommandLineArguments(aosTokens))
457 14 : return false;
458 : }
459 :
460 : // Handle output to GDALG file
461 112 : if (!m_steps.empty() && m_steps.back()->GetName() == "write")
462 : {
463 112 : if (m_steps.back()->IsGDALGOutput())
464 : {
465 8 : const auto outputArg = m_steps.back()->GetArg(GDAL_ARG_NAME_OUTPUT);
466 8 : const auto &filename =
467 : outputArg->GDALAlgorithmArg::template Get<GDALArgDatasetValue>()
468 8 : .GetName();
469 8 : const char *pszType = "";
470 8 : if (GDALDoesFileOrDatasetExist(filename.c_str(), &pszType))
471 : {
472 1 : const auto overwriteArg =
473 1 : m_steps.back()->GetArg(GDAL_ARG_NAME_OVERWRITE);
474 1 : if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
475 : {
476 1 : if (!overwriteArg->GDALAlgorithmArg::template Get<bool>())
477 : {
478 0 : CPLError(CE_Failure, CPLE_AppDefined,
479 : "%s '%s' already exists. Specify the "
480 : "--overwrite option to overwrite it.",
481 : pszType, filename.c_str());
482 0 : return false;
483 : }
484 : }
485 : }
486 :
487 16 : std::string osCommandLine;
488 :
489 30 : for (const auto &path : GDALAlgorithm::m_callPath)
490 : {
491 22 : if (!osCommandLine.empty())
492 14 : osCommandLine += ' ';
493 22 : osCommandLine += path;
494 : }
495 :
496 : // Do not include the last step
497 22 : for (size_t i = 0; i + 1 < m_steps.size(); ++i)
498 : {
499 14 : const auto &step = m_steps[i];
500 14 : if (!step->IsNativelyStreamingCompatible())
501 : {
502 2 : GDALAlgorithm::ReportError(
503 : CE_Warning, CPLE_AppDefined,
504 : "Step %s is not natively streaming compatible, and "
505 : "may cause significant processing time at opening",
506 2 : step->GDALAlgorithm::GetName().c_str());
507 : }
508 :
509 14 : if (i > 0)
510 6 : osCommandLine += " !";
511 29 : for (const auto &path : step->GDALAlgorithm::m_callPath)
512 : {
513 15 : if (!osCommandLine.empty())
514 15 : osCommandLine += ' ';
515 15 : osCommandLine += path;
516 : }
517 :
518 154 : for (const auto &arg : step->GetArgs())
519 : {
520 140 : if (arg->IsExplicitlySet())
521 : {
522 12 : osCommandLine += ' ';
523 12 : std::string strArg;
524 12 : if (!arg->Serialize(strArg))
525 : {
526 0 : CPLError(CE_Failure, CPLE_AppDefined,
527 : "Cannot serialize argument %s",
528 0 : arg->GetName().c_str());
529 0 : return false;
530 : }
531 12 : osCommandLine += strArg;
532 : }
533 : }
534 : }
535 :
536 8 : return GDALAlgorithm::SaveGDALG(filename, osCommandLine);
537 : }
538 :
539 104 : const auto outputFormatArg =
540 104 : m_steps.back()->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
541 104 : const auto outputArg = m_steps.back()->GetArg(GDAL_ARG_NAME_OUTPUT);
542 104 : if (outputArg && outputArg->GetType() == GAAT_DATASET &&
543 : outputArg->IsExplicitlySet())
544 : {
545 104 : const auto &outputFile =
546 : outputArg
547 : ->GDALAlgorithmArg::template Get<GDALArgDatasetValue>();
548 : bool isVRTOutput;
549 104 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
550 : outputFormatArg->IsExplicitlySet())
551 : {
552 41 : const auto &val =
553 : outputFormatArg
554 : ->GDALAlgorithmArg::template Get<std::string>();
555 41 : isVRTOutput = EQUAL(val.c_str(), "vrt");
556 : }
557 : else
558 : {
559 63 : isVRTOutput = EQUAL(
560 : CPLGetExtensionSafe(outputFile.GetName().c_str()).c_str(),
561 : "vrt");
562 : }
563 117 : if (isVRTOutput && !outputFile.GetName().empty() &&
564 13 : m_steps.size() > 3)
565 : {
566 1 : StepAlgorithm::ReportError(
567 : CE_Failure, CPLE_NotSupported,
568 : "VRT output is not supported when there are more than 3 "
569 : "steps. Consider using the GDALG driver (files with "
570 : ".gdalg.json extension)");
571 1 : return false;
572 : }
573 103 : if (isVRTOutput)
574 : {
575 24 : for (const auto &step : m_steps)
576 : {
577 24 : if (!step->m_outputVRTCompatible)
578 : {
579 12 : step->ReportError(
580 : CE_Failure, CPLE_NotSupported,
581 : "VRT output is not supported. Consider using the "
582 : "GDALG driver instead (files with .gdalg.json "
583 : "extension)");
584 12 : return false;
585 : }
586 : }
587 : }
588 : }
589 : }
590 :
591 91 : if (GDALAlgorithm::m_executionForStreamOutput)
592 : {
593 : // For security reasons, to avoid that reading a .gdalg.json file writes
594 : // a file on the file system.
595 50 : for (const auto &step : m_steps)
596 : {
597 52 : if (step->GetName() == "write" &&
598 16 : !EQUAL(step->m_format.c_str(), "stream"))
599 : {
600 2 : StepAlgorithm::ReportError(CE_Failure, CPLE_AppDefined,
601 : "in streamed execution, --format "
602 : "stream should be used");
603 2 : return false;
604 : }
605 : }
606 : }
607 :
608 89 : int countPipelinesWithProgress = 0;
609 185 : for (size_t i = 1; i < m_steps.size(); ++i)
610 : {
611 96 : const bool bCanHandleNextStep =
612 130 : i < m_steps.size() - 1 &&
613 34 : !m_steps[i]->CanHandleNextStep(m_steps[i + 1].get());
614 125 : if (bCanHandleNextStep &&
615 29 : !m_steps[i + 1]->IsNativelyStreamingCompatible())
616 27 : ++countPipelinesWithProgress;
617 69 : else if (!m_steps[i]->IsNativelyStreamingCompatible())
618 62 : ++countPipelinesWithProgress;
619 96 : if (bCanHandleNextStep)
620 29 : ++i;
621 : }
622 89 : if (countPipelinesWithProgress == 0)
623 0 : countPipelinesWithProgress = 1;
624 :
625 89 : GDALDataset *poCurDS = nullptr;
626 89 : int iCurPipelineWithProgress = 0;
627 284 : for (size_t i = 0; i < m_steps.size(); ++i)
628 : {
629 202 : auto &step = m_steps[i];
630 202 : if (i > 0)
631 : {
632 113 : if (!step->m_inputDataset.empty() &&
633 0 : step->m_inputDataset[0].GetDatasetRef())
634 : {
635 : // Shouldn't happen
636 0 : StepAlgorithm::ReportError(
637 : CE_Failure, CPLE_AppDefined,
638 : "Step nr %d (%s) has already an input dataset",
639 0 : static_cast<int>(i), step->GetName().c_str());
640 7 : return false;
641 : }
642 113 : step->m_inputDataset.clear();
643 113 : step->m_inputDataset.resize(1);
644 113 : step->m_inputDataset[0].Set(poCurDS);
645 : }
646 202 : if (i + 1 < m_steps.size() && step->m_outputDataset.GetDatasetRef())
647 : {
648 : // Shouldn't happen
649 2 : StepAlgorithm::ReportError(
650 : CE_Failure, CPLE_AppDefined,
651 : "Step nr %d (%s) has already an output dataset",
652 2 : static_cast<int>(i), step->GetName().c_str());
653 2 : return false;
654 : }
655 :
656 200 : const bool bCanHandleNextStep =
657 323 : i < m_steps.size() - 1 &&
658 123 : step->CanHandleNextStep(m_steps[i + 1].get());
659 :
660 0 : std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)> pScaledData(
661 200 : nullptr, GDALDestroyScaledProgress);
662 200 : GDALPipelineStepRunContext stepCtxt;
663 5 : if ((bCanHandleNextStep &&
664 400 : m_steps[i + 1]->IsNativelyStreamingCompatible()) ||
665 200 : !step->IsNativelyStreamingCompatible())
666 : {
667 87 : pScaledData.reset(GDALCreateScaledProgress(
668 : iCurPipelineWithProgress /
669 : static_cast<double>(countPipelinesWithProgress),
670 87 : (iCurPipelineWithProgress + 1) /
671 : static_cast<double>(countPipelinesWithProgress),
672 : ctxt.m_pfnProgress, ctxt.m_pProgressData));
673 87 : ++iCurPipelineWithProgress;
674 87 : stepCtxt.m_pfnProgress = pScaledData ? GDALScaledProgress : nullptr;
675 87 : stepCtxt.m_pProgressData = pScaledData.get();
676 : }
677 200 : if (bCanHandleNextStep)
678 : {
679 5 : stepCtxt.m_poNextUsableStep = m_steps[i + 1].get();
680 : }
681 200 : if (!step->ValidateArguments() || !step->RunStep(stepCtxt))
682 : {
683 5 : return false;
684 : }
685 195 : poCurDS = step->m_outputDataset.GetDatasetRef();
686 195 : if (!poCurDS)
687 : {
688 0 : StepAlgorithm::ReportError(
689 : CE_Failure, CPLE_AppDefined,
690 : "Step nr %d (%s) failed to produce an output dataset",
691 0 : static_cast<int>(i), step->GetName().c_str());
692 0 : return false;
693 : }
694 :
695 195 : if (bCanHandleNextStep)
696 : {
697 5 : ++i;
698 : }
699 : }
700 :
701 82 : if (ctxt.m_pfnProgress)
702 11 : ctxt.m_pfnProgress(1.0, "", ctxt.m_pProgressData);
703 :
704 82 : if (!StepAlgorithm::m_outputDataset.GetDatasetRef())
705 : {
706 82 : StepAlgorithm::m_outputDataset.Set(poCurDS);
707 : }
708 :
709 82 : return true;
710 : }
711 :
712 : /************************************************************************/
713 : /* GDALAbstractPipelineAlgorithm::Finalize() */
714 : /************************************************************************/
715 :
716 : template <class StepAlgorithm>
717 87 : bool GDALAbstractPipelineAlgorithm<StepAlgorithm>::Finalize()
718 : {
719 87 : bool ret = GDALAlgorithm::Finalize();
720 250 : for (auto &step : m_steps)
721 : {
722 163 : ret = step->Finalize() && ret;
723 : }
724 87 : return ret;
725 : }
726 :
727 : /************************************************************************/
728 : /* GDALAbstractPipelineAlgorithm::GetUsageAsJSON() */
729 : /************************************************************************/
730 :
731 : template <class StepAlgorithm>
732 8 : std::string GDALAbstractPipelineAlgorithm<StepAlgorithm>::GetUsageAsJSON() const
733 : {
734 16 : CPLJSONDocument oDoc;
735 8 : CPL_IGNORE_RET_VAL(oDoc.LoadMemory(GDALAlgorithm::GetUsageAsJSON()));
736 :
737 16 : CPLJSONArray jPipelineSteps;
738 230 : for (const std::string &name : m_stepRegistry.GetNames())
739 : {
740 444 : auto alg = GetStepAlg(name);
741 222 : if (!alg->IsHidden())
742 : {
743 218 : CPLJSONDocument oStepDoc;
744 218 : CPL_IGNORE_RET_VAL(oStepDoc.LoadMemory(alg->GetUsageAsJSON()));
745 218 : jPipelineSteps.Add(oStepDoc.GetRoot());
746 : }
747 : }
748 8 : oDoc.GetRoot().Add("pipeline_algorithms", jPipelineSteps);
749 :
750 16 : return oDoc.SaveAsString();
751 : }
752 :
753 : //! @endcond
754 :
755 : #endif
|