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