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