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 237 : GDALAbstractPipelineAlgorithm(
81 : const std::string &name, const std::string &description,
82 : const std::string &helpURL,
83 : const typename StepAlgorithm::ConstructorOptions &options)
84 237 : : StepAlgorithm(name, description, helpURL, options)
85 : {
86 237 : }
87 :
88 237 : ~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 533 : for (auto it = std::rbegin(m_steps); it != std::rend(m_steps); it++)
96 : {
97 296 : it->reset();
98 : }
99 474 : }
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 23 : const std::vector<GDALArgDatasetValue> &GetInputDatasets() const
125 : {
126 23 : return m_inputDataset;
127 : }
128 :
129 48 : const GDALArgDatasetValue &GetOutputDataset() const
130 : {
131 48 : return m_outputDataset;
132 : }
133 :
134 149 : const std::string &GetOutputFormat() const
135 : {
136 149 : return m_format;
137 : }
138 :
139 48 : const std::vector<std::string> &GetCreationOptions() const
140 : {
141 48 : 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 3180 : inline ConstructorOptions &SetStandaloneStep(bool b)
166 : {
167 3180 : standaloneStep = b;
168 3180 : return *this;
169 : }
170 :
171 1069 : inline ConstructorOptions &SetAddDefaultArguments(bool b)
172 : {
173 1069 : addDefaultArguments = b;
174 1069 : return *this;
175 : }
176 :
177 33 : inline ConstructorOptions &SetAddInputLayerNameArgument(bool b)
178 : {
179 33 : addInputLayerNameArgument = b;
180 33 : return *this;
181 : }
182 :
183 645 : inline ConstructorOptions &SetInputDatasetMaxCount(int maxCount)
184 : {
185 645 : inputDatasetMaxCount = maxCount;
186 645 : 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 193 : inline ConstructorOptions &SetInputDatasetAlias(const std::string &s)
196 : {
197 193 : inputDatasetAlias = s;
198 193 : return *this;
199 : }
200 :
201 145 : inline ConstructorOptions &SetInputDatasetMetaVar(const std::string &s)
202 : {
203 145 : inputDatasetMetaVar = s;
204 145 : 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 197 : inline ConstructorOptions &SetAutoOpenInputDatasets(bool b)
214 : {
215 197 : autoOpenInputDatasets = b;
216 197 : 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 532 : virtual bool CanBeFirstStep() const
256 : {
257 532 : return false;
258 : }
259 :
260 168 : virtual bool IsNativelyStreamingCompatible() const
261 : {
262 168 : return true;
263 : }
264 :
265 453 : virtual bool CanHandleNextStep(GDALPipelineStepAlgorithm *) const
266 : {
267 453 : 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 : bool m_stdout = false;
285 : std::string m_output{};
286 : GDALArgDatasetValue m_outputDataset{};
287 : std::string m_format{};
288 : std::vector<std::string> m_creationOptions{};
289 : bool m_overwrite = false;
290 : std::string m_outputLayerName{};
291 : GDALInConstructionAlgorithmArg *m_outputFormatArg = nullptr;
292 :
293 : // Output arguments (vector specific)
294 : std::vector<std::string> m_layerCreationOptions{};
295 : bool m_update = false;
296 : bool m_overwriteLayer = false;
297 : bool m_appendLayer = false;
298 :
299 : void AddRasterInputArgs(bool openForMixedRasterVector, bool hiddenForCLI);
300 : void AddRasterOutputArgs(bool hiddenForCLI);
301 : void AddRasterHiddenInputDatasetArg();
302 :
303 : void AddVectorInputArgs(bool hiddenForCLI);
304 : void AddVectorOutputArgs(bool hiddenForCLI,
305 : bool shortNameOutputLayerAllowed);
306 :
307 : private:
308 : bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override;
309 : GDALAlgorithm::ProcessGDALGOutputRet ProcessGDALGOutput() override;
310 : bool CheckSafeForStreamOutput() override;
311 :
312 : CPL_DISALLOW_COPY_ASSIGN(GDALPipelineStepAlgorithm)
313 : };
314 :
315 : /************************************************************************/
316 : /* GDALAbstractPipelineAlgorithm::CheckFirstStep() */
317 : /************************************************************************/
318 :
319 : template <class StepAlgorithm>
320 159 : bool GDALAbstractPipelineAlgorithm<StepAlgorithm>::CheckFirstStep(
321 : const std::vector<StepAlgorithm *> &steps) const
322 : {
323 159 : if (!steps.front()->CanBeFirstStep())
324 : {
325 15 : std::vector<std::string> firstStepNames{"read"};
326 108 : for (const auto &stepName : m_stepRegistry.GetNames())
327 : {
328 210 : auto alg = GetStepAlg(stepName);
329 105 : if (alg && alg->CanBeFirstStep() && stepName != "read")
330 : {
331 10 : firstStepNames.push_back(stepName);
332 : }
333 : }
334 :
335 3 : std::string msg = "First step should be ";
336 16 : for (size_t i = 0; i < firstStepNames.size(); ++i)
337 : {
338 13 : if (i == firstStepNames.size() - 1)
339 3 : msg += " or ";
340 10 : else if (i > 0)
341 7 : msg += ", ";
342 13 : msg += '\'';
343 13 : msg += firstStepNames[i];
344 13 : msg += '\'';
345 : }
346 :
347 3 : StepAlgorithm::ReportError(CE_Failure, CPLE_AppDefined, "%s",
348 : msg.c_str());
349 3 : return false;
350 : }
351 230 : for (size_t i = 1; i < steps.size() - 1; ++i)
352 : {
353 77 : if (steps[i]->CanBeFirstStep())
354 : {
355 3 : StepAlgorithm::ReportError(CE_Failure, CPLE_AppDefined,
356 : "Only first step can be '%s'",
357 3 : steps[i]->GetName().c_str());
358 3 : return false;
359 : }
360 : }
361 153 : return true;
362 : }
363 :
364 : /************************************************************************/
365 : /* GDALAbstractPipelineAlgorithm::GetStepAlg() */
366 : /************************************************************************/
367 :
368 : template <class StepAlgorithm>
369 : std::unique_ptr<StepAlgorithm>
370 1469 : GDALAbstractPipelineAlgorithm<StepAlgorithm>::GetStepAlg(
371 : const std::string &name) const
372 : {
373 2938 : auto alg = m_stepRegistry.Instantiate(name);
374 : return std::unique_ptr<StepAlgorithm>(
375 2938 : cpl::down_cast<StepAlgorithm *>(alg.release()));
376 : }
377 :
378 : /************************************************************************/
379 : /* GDALAbstractPipelineAlgorithm::GetAutoComplete() */
380 : /************************************************************************/
381 :
382 : template <class StepAlgorithm>
383 : std::vector<std::string>
384 18 : GDALAbstractPipelineAlgorithm<StepAlgorithm>::GetAutoComplete(
385 : std::vector<std::string> &args, bool lastWordIsComplete,
386 : bool /* showAllOptions*/)
387 : {
388 18 : std::vector<std::string> ret;
389 18 : if (args.size() <= 1)
390 : {
391 6 : if (args.empty() || args.front() != "read")
392 4 : ret.push_back("read");
393 : }
394 25 : else if (args.back() == "!" ||
395 25 : (args[args.size() - 2] == "!" && !GetStepAlg(args.back())))
396 : {
397 104 : for (const std::string &name : m_stepRegistry.GetNames())
398 : {
399 100 : if (name != "read")
400 : {
401 96 : ret.push_back(name);
402 : }
403 : }
404 : }
405 : else
406 : {
407 16 : std::string lastStep = "read";
408 16 : std::vector<std::string> lastArgs;
409 27 : for (size_t i = 1; i < args.size(); ++i)
410 : {
411 19 : lastArgs.push_back(args[i]);
412 19 : if (i + 1 < args.size() && args[i] == "!")
413 : {
414 6 : ++i;
415 6 : lastArgs.clear();
416 6 : lastStep = args[i];
417 : }
418 : }
419 :
420 16 : auto curAlg = GetStepAlg(lastStep);
421 8 : if (curAlg)
422 : {
423 8 : ret = curAlg->GetAutoComplete(lastArgs, lastWordIsComplete,
424 : /* showAllOptions = */ false);
425 : }
426 : }
427 18 : return ret;
428 : }
429 :
430 : /************************************************************************/
431 : /* GDALAbstractPipelineAlgorithm::RunStep() */
432 : /************************************************************************/
433 :
434 : template <class StepAlgorithm>
435 141 : bool GDALAbstractPipelineAlgorithm<StepAlgorithm>::RunStep(
436 : GDALPipelineStepRunContext &ctxt)
437 : {
438 141 : if (m_stepOnWhichHelpIsRequested)
439 : {
440 4 : printf(
441 : "%s",
442 4 : m_stepOnWhichHelpIsRequested->GetUsageForCLI(false).c_str()); /*ok*/
443 4 : return true;
444 : }
445 :
446 137 : if (m_steps.empty())
447 : {
448 : // If invoked programmatically, not from the command line.
449 :
450 67 : if (m_pipeline.empty())
451 : {
452 2 : StepAlgorithm::ReportError(CE_Failure, CPLE_AppDefined,
453 : "'pipeline' argument not set");
454 16 : return false;
455 : }
456 :
457 65 : const CPLStringList aosTokens(CSLTokenizeString(m_pipeline.c_str()));
458 65 : if (!this->ParseCommandLineArguments(aosTokens))
459 14 : return false;
460 : }
461 :
462 : // Handle output to GDALG file
463 121 : if (!m_steps.empty() && m_steps.back()->GetName() == "write")
464 : {
465 113 : if (m_steps.back()->IsGDALGOutput())
466 : {
467 8 : const auto outputArg = m_steps.back()->GetArg(GDAL_ARG_NAME_OUTPUT);
468 8 : const auto &filename =
469 : outputArg->GDALAlgorithmArg::template Get<GDALArgDatasetValue>()
470 8 : .GetName();
471 8 : const char *pszType = "";
472 8 : if (GDALDoesFileOrDatasetExist(filename.c_str(), &pszType))
473 : {
474 1 : const auto overwriteArg =
475 1 : m_steps.back()->GetArg(GDAL_ARG_NAME_OVERWRITE);
476 1 : if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
477 : {
478 1 : if (!overwriteArg->GDALAlgorithmArg::template Get<bool>())
479 : {
480 0 : CPLError(CE_Failure, CPLE_AppDefined,
481 : "%s '%s' already exists. Specify the "
482 : "--overwrite option to overwrite it.",
483 : pszType, filename.c_str());
484 0 : return false;
485 : }
486 : }
487 : }
488 :
489 16 : std::string osCommandLine;
490 :
491 30 : for (const auto &path : GDALAlgorithm::m_callPath)
492 : {
493 22 : if (!osCommandLine.empty())
494 14 : osCommandLine += ' ';
495 22 : osCommandLine += path;
496 : }
497 :
498 : // Do not include the last step
499 22 : for (size_t i = 0; i + 1 < m_steps.size(); ++i)
500 : {
501 14 : const auto &step = m_steps[i];
502 14 : if (!step->IsNativelyStreamingCompatible())
503 : {
504 2 : GDALAlgorithm::ReportError(
505 : CE_Warning, CPLE_AppDefined,
506 : "Step %s is not natively streaming compatible, and "
507 : "may cause significant processing time at opening",
508 2 : step->GDALAlgorithm::GetName().c_str());
509 : }
510 :
511 14 : if (i > 0)
512 6 : osCommandLine += " !";
513 29 : for (const auto &path : step->GDALAlgorithm::m_callPath)
514 : {
515 15 : if (!osCommandLine.empty())
516 15 : osCommandLine += ' ';
517 15 : osCommandLine += path;
518 : }
519 :
520 154 : for (const auto &arg : step->GetArgs())
521 : {
522 140 : if (arg->IsExplicitlySet())
523 : {
524 12 : osCommandLine += ' ';
525 12 : std::string strArg;
526 12 : if (!arg->Serialize(strArg))
527 : {
528 0 : CPLError(CE_Failure, CPLE_AppDefined,
529 : "Cannot serialize argument %s",
530 0 : arg->GetName().c_str());
531 0 : return false;
532 : }
533 12 : osCommandLine += strArg;
534 : }
535 : }
536 : }
537 :
538 8 : return GDALAlgorithm::SaveGDALG(filename, osCommandLine);
539 : }
540 :
541 105 : const auto outputFormatArg =
542 105 : m_steps.back()->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
543 105 : const auto outputArg = m_steps.back()->GetArg(GDAL_ARG_NAME_OUTPUT);
544 105 : if (outputArg && outputArg->GetType() == GAAT_DATASET &&
545 : outputArg->IsExplicitlySet())
546 : {
547 105 : const auto &outputFile =
548 : outputArg
549 : ->GDALAlgorithmArg::template Get<GDALArgDatasetValue>();
550 : bool isVRTOutput;
551 105 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
552 : outputFormatArg->IsExplicitlySet())
553 : {
554 41 : const auto &val =
555 : outputFormatArg
556 : ->GDALAlgorithmArg::template Get<std::string>();
557 41 : isVRTOutput = EQUAL(val.c_str(), "vrt");
558 : }
559 : else
560 : {
561 64 : isVRTOutput = EQUAL(
562 : CPLGetExtensionSafe(outputFile.GetName().c_str()).c_str(),
563 : "vrt");
564 : }
565 118 : if (isVRTOutput && !outputFile.GetName().empty() &&
566 13 : m_steps.size() > 3)
567 : {
568 1 : StepAlgorithm::ReportError(
569 : CE_Failure, CPLE_NotSupported,
570 : "VRT output is not supported when there are more than 3 "
571 : "steps. Consider using the GDALG driver (files with "
572 : ".gdalg.json extension)");
573 1 : return false;
574 : }
575 104 : if (isVRTOutput)
576 : {
577 24 : for (const auto &step : m_steps)
578 : {
579 24 : if (!step->m_outputVRTCompatible)
580 : {
581 12 : step->ReportError(
582 : CE_Failure, CPLE_NotSupported,
583 : "VRT output is not supported. Consider using the "
584 : "GDALG driver instead (files with .gdalg.json "
585 : "extension)");
586 12 : return false;
587 : }
588 : }
589 : }
590 : }
591 : }
592 :
593 100 : if (GDALAlgorithm::m_executionForStreamOutput)
594 : {
595 : // For security reasons, to avoid that reading a .gdalg.json file writes
596 : // a file on the file system.
597 50 : for (const auto &step : m_steps)
598 : {
599 52 : if (step->GetName() == "write" &&
600 16 : !EQUAL(step->m_format.c_str(), "stream"))
601 : {
602 2 : StepAlgorithm::ReportError(CE_Failure, CPLE_AppDefined,
603 : "in streamed execution, --format "
604 : "stream should be used");
605 2 : return false;
606 : }
607 : }
608 : }
609 :
610 98 : int countPipelinesWithProgress = 0;
611 203 : for (size_t i = 1; i < m_steps.size(); ++i)
612 : {
613 105 : const bool bCanHandleNextStep =
614 139 : i < m_steps.size() - 1 &&
615 34 : !m_steps[i]->CanHandleNextStep(m_steps[i + 1].get());
616 134 : if (bCanHandleNextStep &&
617 29 : !m_steps[i + 1]->IsNativelyStreamingCompatible())
618 27 : ++countPipelinesWithProgress;
619 78 : else if (!m_steps[i]->IsNativelyStreamingCompatible())
620 63 : ++countPipelinesWithProgress;
621 105 : if (bCanHandleNextStep)
622 29 : ++i;
623 : }
624 98 : if (countPipelinesWithProgress == 0)
625 8 : countPipelinesWithProgress = 1;
626 :
627 98 : GDALDataset *poCurDS = nullptr;
628 98 : int iCurPipelineWithProgress = 0;
629 311 : for (size_t i = 0; i < m_steps.size(); ++i)
630 : {
631 220 : auto &step = m_steps[i];
632 220 : if (i > 0)
633 : {
634 122 : if (!step->m_inputDataset.empty() &&
635 0 : step->m_inputDataset[0].GetDatasetRef())
636 : {
637 : // Shouldn't happen
638 0 : StepAlgorithm::ReportError(
639 : CE_Failure, CPLE_AppDefined,
640 : "Step nr %d (%s) has already an input dataset",
641 0 : static_cast<int>(i), step->GetName().c_str());
642 7 : return false;
643 : }
644 122 : step->m_inputDataset.clear();
645 122 : step->m_inputDataset.resize(1);
646 122 : step->m_inputDataset[0].Set(poCurDS);
647 : }
648 220 : if (i + 1 < m_steps.size() && step->m_outputDataset.GetDatasetRef())
649 : {
650 : // Shouldn't happen
651 2 : StepAlgorithm::ReportError(
652 : CE_Failure, CPLE_AppDefined,
653 : "Step nr %d (%s) has already an output dataset",
654 2 : static_cast<int>(i), step->GetName().c_str());
655 2 : return false;
656 : }
657 :
658 218 : const bool bCanHandleNextStep =
659 350 : i < m_steps.size() - 1 &&
660 132 : step->CanHandleNextStep(m_steps[i + 1].get());
661 :
662 0 : std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)> pScaledData(
663 218 : nullptr, GDALDestroyScaledProgress);
664 218 : GDALPipelineStepRunContext stepCtxt;
665 5 : if ((bCanHandleNextStep &&
666 436 : m_steps[i + 1]->IsNativelyStreamingCompatible()) ||
667 218 : !step->IsNativelyStreamingCompatible())
668 : {
669 88 : pScaledData.reset(GDALCreateScaledProgress(
670 : iCurPipelineWithProgress /
671 : static_cast<double>(countPipelinesWithProgress),
672 88 : (iCurPipelineWithProgress + 1) /
673 : static_cast<double>(countPipelinesWithProgress),
674 : ctxt.m_pfnProgress, ctxt.m_pProgressData));
675 88 : ++iCurPipelineWithProgress;
676 88 : stepCtxt.m_pfnProgress = pScaledData ? GDALScaledProgress : nullptr;
677 88 : stepCtxt.m_pProgressData = pScaledData.get();
678 : }
679 218 : if (bCanHandleNextStep)
680 : {
681 5 : stepCtxt.m_poNextUsableStep = m_steps[i + 1].get();
682 : }
683 228 : if (i + 1 == m_steps.size() && StepAlgorithm::m_stdout &&
684 228 : step->GetArg("stdout") != nullptr)
685 : {
686 4 : step->m_stdout = true;
687 : }
688 218 : if (!step->ValidateArguments() || !step->RunStep(stepCtxt))
689 : {
690 5 : return false;
691 : }
692 213 : poCurDS = step->m_outputDataset.GetDatasetRef();
693 229 : if (!poCurDS &&
694 16 : !(i + 1 == m_steps.size() &&
695 225 : (!step->m_output.empty() || step->GetArg("stdout") != nullptr)))
696 : {
697 0 : StepAlgorithm::ReportError(
698 : CE_Failure, CPLE_AppDefined,
699 : "Step nr %d (%s) failed to produce an output dataset",
700 0 : static_cast<int>(i), step->GetName().c_str());
701 0 : return false;
702 : }
703 :
704 213 : if (bCanHandleNextStep)
705 : {
706 5 : ++i;
707 : }
708 : }
709 :
710 108 : if (ctxt.m_pfnProgress &&
711 108 : m_steps.back()->GetArg("output-string") == nullptr)
712 13 : ctxt.m_pfnProgress(1.0, "", ctxt.m_pProgressData);
713 :
714 91 : if (!m_steps.back()->m_output.empty())
715 : {
716 4 : auto outputStringArg = StepAlgorithm::GetArg("output-string");
717 4 : if (outputStringArg && outputStringArg->GetType() == GAAT_STRING)
718 4 : outputStringArg->Set(m_steps.back()->m_output);
719 : }
720 87 : else if (!StepAlgorithm::m_outputDataset.GetDatasetRef())
721 : {
722 87 : StepAlgorithm::m_outputDataset.Set(poCurDS);
723 : }
724 :
725 91 : return true;
726 : }
727 :
728 : /************************************************************************/
729 : /* GDALAbstractPipelineAlgorithm::Finalize() */
730 : /************************************************************************/
731 :
732 : template <class StepAlgorithm>
733 95 : bool GDALAbstractPipelineAlgorithm<StepAlgorithm>::Finalize()
734 : {
735 95 : bool ret = GDALAlgorithm::Finalize();
736 274 : for (auto &step : m_steps)
737 : {
738 179 : ret = step->Finalize() && ret;
739 : }
740 95 : return ret;
741 : }
742 :
743 : /************************************************************************/
744 : /* GDALAbstractPipelineAlgorithm::GetUsageAsJSON() */
745 : /************************************************************************/
746 :
747 : template <class StepAlgorithm>
748 8 : std::string GDALAbstractPipelineAlgorithm<StepAlgorithm>::GetUsageAsJSON() const
749 : {
750 16 : CPLJSONDocument oDoc;
751 8 : CPL_IGNORE_RET_VAL(oDoc.LoadMemory(GDALAlgorithm::GetUsageAsJSON()));
752 :
753 16 : CPLJSONArray jPipelineSteps;
754 243 : for (const std::string &name : m_stepRegistry.GetNames())
755 : {
756 470 : auto alg = GetStepAlg(name);
757 235 : if (!alg->IsHidden())
758 : {
759 231 : CPLJSONDocument oStepDoc;
760 231 : CPL_IGNORE_RET_VAL(oStepDoc.LoadMemory(alg->GetUsageAsJSON()));
761 231 : jPipelineSteps.Add(oStepDoc.GetRoot());
762 : }
763 : }
764 8 : oDoc.GetRoot().Add("pipeline_algorithms", jPipelineSteps);
765 :
766 16 : return oDoc.SaveAsString();
767 : }
768 :
769 : //! @endcond
770 :
771 : #endif
|