LCOV - code coverage report
Current view: top level - apps - gdalalg_abstract_pipeline.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 249 262 95.0 %
Date: 2025-06-19 12:30:01 Functions: 43 47 91.5 %

          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

Generated by: LCOV version 1.14