LCOV - code coverage report
Current view: top level - apps - gdalalg_abstract_pipeline.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 74 82 90.2 %
Date: 2025-02-20 10:14:44 Functions: 14 18 77.8 %

          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             : 
      22             : #include <algorithm>
      23             : 
      24             : template <class StepAlgorithm>
      25             : class GDALAbstractPipelineAlgorithm CPL_NON_FINAL : public StepAlgorithm
      26             : {
      27             :   public:
      28             :     std::vector<std::string> GetAutoComplete(std::vector<std::string> &args,
      29             :                                              bool /* showAllOptions*/) override;
      30             : 
      31             :     bool Finalize() override;
      32             : 
      33             :     std::string GetUsageAsJSON() const override;
      34             : 
      35             :     /* cppcheck-suppress functionStatic */
      36           0 :     void SetDataset(GDALDataset *)
      37             :     {
      38           0 :     }
      39             : 
      40             :   protected:
      41         119 :     GDALAbstractPipelineAlgorithm(const std::string &name,
      42             :                                   const std::string &description,
      43             :                                   const std::string &helpURL,
      44             :                                   bool standaloneStep)
      45         119 :         : StepAlgorithm(name, description, helpURL, standaloneStep)
      46             :     {
      47         119 :     }
      48             : 
      49         119 :     ~GDALAbstractPipelineAlgorithm() override
      50             :     {
      51             :         // Destroy steps in the reverse order they have been constructed,
      52             :         // as a step can create object that depends on the validity of
      53             :         // objects of previous steps, and while cleaning them it needs those
      54             :         // prior objects to be still alive.
      55             :         // Typically for "gdal vector pipeline read ... ! sql ..."
      56         247 :         for (auto it = std::rbegin(m_steps); it != std::rend(m_steps); it++)
      57             :         {
      58         128 :             it->reset();
      59             :         }
      60         238 :     }
      61             : 
      62             :     virtual GDALArgDatasetValue &GetOutputDataset() = 0;
      63             : 
      64             :     std::string m_pipeline{};
      65             : 
      66             :     std::unique_ptr<StepAlgorithm> GetStepAlg(const std::string &name) const;
      67             : 
      68             :     GDALAlgorithmRegistry m_stepRegistry{};
      69             :     std::vector<std::unique_ptr<StepAlgorithm>> m_steps{};
      70             : 
      71             :   private:
      72             :     bool RunStep(GDALProgressFunc pfnProgress, void *pProgressData) override;
      73             : };
      74             : 
      75             : /************************************************************************/
      76             : /*              GDALAbstractPipelineAlgorithm::GetStepAlg()             */
      77             : /************************************************************************/
      78             : 
      79             : template <class StepAlgorithm>
      80             : std::unique_ptr<StepAlgorithm>
      81         262 : GDALAbstractPipelineAlgorithm<StepAlgorithm>::GetStepAlg(
      82             :     const std::string &name) const
      83             : {
      84         524 :     auto alg = m_stepRegistry.Instantiate(name);
      85             :     return std::unique_ptr<StepAlgorithm>(
      86         524 :         cpl::down_cast<StepAlgorithm *>(alg.release()));
      87             : }
      88             : 
      89             : /************************************************************************/
      90             : /*         GDALAbstractPipelineAlgorithm::GetAutoComplete()             */
      91             : /************************************************************************/
      92             : 
      93             : template <class StepAlgorithm>
      94             : std::vector<std::string>
      95          16 : GDALAbstractPipelineAlgorithm<StepAlgorithm>::GetAutoComplete(
      96             :     std::vector<std::string> &args, bool /* showAllOptions*/)
      97             : {
      98          16 :     std::vector<std::string> ret;
      99          16 :     if (args.size() <= 1)
     100             :     {
     101           6 :         if (args.empty() || args.front() != "read")
     102           4 :             ret.push_back("read");
     103             :     }
     104          10 :     else if (args.back() == "!" || args[args.size() - 2] == "!")
     105             :     {
     106          28 :         for (const std::string &name : m_stepRegistry.GetNames())
     107             :         {
     108          24 :             if (name != "read")
     109             :             {
     110          20 :                 ret.push_back(name);
     111             :             }
     112             :         }
     113             :     }
     114             :     else
     115             :     {
     116          12 :         std::string lastStep = "read";
     117          12 :         std::vector<std::string> lastArgs;
     118          21 :         for (size_t i = 1; i < args.size(); ++i)
     119             :         {
     120          15 :             lastArgs.push_back(args[i]);
     121          15 :             if (i + 1 < args.size() && args[i] == "!")
     122             :             {
     123           5 :                 ++i;
     124           5 :                 lastArgs.clear();
     125           5 :                 lastStep = args[i];
     126             :             }
     127             :         }
     128             : 
     129          12 :         auto curAlg = GetStepAlg(lastStep);
     130           6 :         if (curAlg)
     131             :         {
     132           6 :             ret =
     133           6 :                 curAlg->GetAutoComplete(lastArgs, /* showAllOptions = */ false);
     134             :         }
     135             :     }
     136          16 :     return ret;
     137             : }
     138             : 
     139             : /************************************************************************/
     140             : /*              GDALAbstractPipelineAlgorithm::RunStep()                */
     141             : /************************************************************************/
     142             : 
     143             : template <class StepAlgorithm>
     144          60 : bool GDALAbstractPipelineAlgorithm<StepAlgorithm>::RunStep(
     145             :     GDALProgressFunc pfnProgress, void *pProgressData)
     146             : {
     147          60 :     if (m_steps.empty())
     148             :     {
     149             :         // If invoked programmatically, not from the command line.
     150             : 
     151          17 :         if (m_pipeline.empty())
     152             :         {
     153           2 :             StepAlgorithm::ReportError(CE_Failure, CPLE_AppDefined,
     154             :                                        "'pipeline' argument not set");
     155           4 :             return false;
     156             :         }
     157             : 
     158          15 :         const CPLStringList aosTokens(CSLTokenizeString(m_pipeline.c_str()));
     159          15 :         if (!this->ParseCommandLineArguments(aosTokens))
     160           2 :             return false;
     161             :     }
     162             : 
     163          56 :     GDALDataset *poCurDS = nullptr;
     164         172 :     for (size_t i = 0; i < m_steps.size(); ++i)
     165             :     {
     166         125 :         auto &step = m_steps[i];
     167         125 :         if (i > 0)
     168             :         {
     169          69 :             if (step->m_inputDataset.GetDatasetRef())
     170             :             {
     171             :                 // Shouldn't happen
     172           0 :                 StepAlgorithm::ReportError(
     173             :                     CE_Failure, CPLE_AppDefined,
     174             :                     "Step nr %d (%s) has already an input dataset",
     175           0 :                     static_cast<int>(i), step->GetName().c_str());
     176           0 :                 return false;
     177             :             }
     178          69 :             step->m_inputDataset.Set(poCurDS);
     179             :         }
     180         125 :         if (i + 1 < m_steps.size() && step->m_outputDataset.GetDatasetRef())
     181             :         {
     182             :             // Shouldn't happen
     183           2 :             StepAlgorithm::ReportError(
     184             :                 CE_Failure, CPLE_AppDefined,
     185             :                 "Step nr %d (%s) has already an output dataset",
     186           2 :                 static_cast<int>(i), step->GetName().c_str());
     187           2 :             return false;
     188             :         }
     189         246 :         if (!step->Run(i < m_steps.size() - 1 ? nullptr : pfnProgress,
     190         123 :                        i < m_steps.size() - 1 ? nullptr : pProgressData))
     191             :         {
     192           7 :             return false;
     193             :         }
     194         116 :         poCurDS = step->m_outputDataset.GetDatasetRef();
     195         116 :         if (!poCurDS)
     196             :         {
     197           0 :             StepAlgorithm::ReportError(
     198             :                 CE_Failure, CPLE_AppDefined,
     199             :                 "Step nr %d (%s) failed to produce an output dataset",
     200           0 :                 static_cast<int>(i), step->GetName().c_str());
     201           0 :             return false;
     202             :         }
     203             :     }
     204             : 
     205          47 :     if (!GetOutputDataset().GetDatasetRef())
     206             :     {
     207          47 :         GetOutputDataset().Set(poCurDS);
     208             :     }
     209             : 
     210          47 :     return true;
     211             : }
     212             : 
     213             : /************************************************************************/
     214             : /*               GDALAbstractPipelineAlgorithm::Finalize()              */
     215             : /************************************************************************/
     216             : 
     217             : template <class StepAlgorithm>
     218          45 : bool GDALAbstractPipelineAlgorithm<StepAlgorithm>::Finalize()
     219             : {
     220          45 :     bool ret = GDALAlgorithm::Finalize();
     221         147 :     for (auto &step : m_steps)
     222             :     {
     223         102 :         ret = step->Finalize() && ret;
     224             :     }
     225          45 :     return ret;
     226             : }
     227             : 
     228             : /************************************************************************/
     229             : /*             GDALAbstractPipelineAlgorithm::GetUsageAsJSON()          */
     230             : /************************************************************************/
     231             : 
     232             : template <class StepAlgorithm>
     233           7 : std::string GDALAbstractPipelineAlgorithm<StepAlgorithm>::GetUsageAsJSON() const
     234             : {
     235          14 :     CPLJSONDocument oDoc;
     236           7 :     CPL_IGNORE_RET_VAL(oDoc.LoadMemory(GDALAlgorithm::GetUsageAsJSON()));
     237             : 
     238          14 :     CPLJSONArray jPipelineSteps;
     239          48 :     for (const std::string &name : m_stepRegistry.GetNames())
     240             :     {
     241          82 :         auto alg = GetStepAlg(name);
     242          41 :         CPLJSONDocument oStepDoc;
     243          41 :         CPL_IGNORE_RET_VAL(oStepDoc.LoadMemory(alg->GetUsageAsJSON()));
     244          41 :         jPipelineSteps.Add(oStepDoc.GetRoot());
     245             :     }
     246           7 :     oDoc.GetRoot().Add("pipeline_algorithms", jPipelineSteps);
     247             : 
     248          14 :     return oDoc.SaveAsString();
     249             : }
     250             : 
     251             : //! @endcond
     252             : 
     253             : #endif

Generated by: LCOV version 1.14