LCOV - code coverage report
Current view: top level - apps - gdalalg_abstract_pipeline.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 260 272 95.6 %
Date: 2025-08-01 10:10:57 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         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

Generated by: LCOV version 1.14