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 "gdalalgorithm.h" 19 : #include "gdal_priv.h" 20 : 21 : #include <algorithm> 22 : 23 : // This is an easter egg to pay tribute to PROJ pipeline syntax 24 : // We accept "gdal vector +gdal=pipeline +step +gdal=read +input=in.tif +step +gdal=reproject +dst-crs=EPSG:32632 +step +gdal=write +output=out.tif +overwrite" 25 : // as an alternative to (recommended): 26 : // "gdal vector pipeline ! read in.tif ! reproject--dst-crs=EPSG:32632 ! write out.tif --overwrite" 27 : #ifndef GDAL_PIPELINE_PROJ_NOSTALGIA 28 : #define GDAL_PIPELINE_PROJ_NOSTALGIA 29 : #endif 30 : 31 : /************************************************************************/ 32 : /* GDALPipelineStepRunContext */ 33 : /************************************************************************/ 34 : 35 : class GDALPipelineStepAlgorithm; 36 : 37 : class GDALPipelineStepRunContext 38 : { 39 : public: 40 : GDALPipelineStepRunContext() = default; 41 : 42 : // Progress callback to use during execution of the step 43 : GDALProgressFunc m_pfnProgress = nullptr; 44 : void *m_pProgressData = nullptr; 45 : 46 : // If there is a step in the pipeline immediately following step to which 47 : // this instance of GDALRasterPipelineStepRunContext is passed, and that 48 : // this next step is usable by the current step (as determined by 49 : // CanHandleNextStep()), then this member will point to this next step. 50 : GDALPipelineStepAlgorithm *m_poNextUsableStep = nullptr; 51 : 52 : private: 53 : CPL_DISALLOW_COPY_ASSIGN(GDALPipelineStepRunContext) 54 : }; 55 : 56 : /************************************************************************/ 57 : /* GDALPipelineStepAlgorithm */ 58 : /************************************************************************/ 59 : 60 : class GDALPipelineStepAlgorithm /* non final */ : public GDALAlgorithm 61 : { 62 : public: 63 78 : std::vector<GDALArgDatasetValue> &GetInputDatasets() 64 : { 65 78 : return m_inputDataset; 66 : } 67 : 68 : const std::vector<GDALArgDatasetValue> &GetInputDatasets() const 69 : { 70 : return m_inputDataset; 71 : } 72 : 73 51 : GDALArgDatasetValue &GetOutputDataset() 74 : { 75 51 : return m_outputDataset; 76 : } 77 : 78 : const GDALArgDatasetValue &GetOutputDataset() const 79 : { 80 : return m_outputDataset; 81 : } 82 : 83 144 : const std::string &GetOutputFormat() const 84 : { 85 144 : return m_format; 86 : } 87 : 88 48 : const std::vector<std::string> &GetCreationOptions() const 89 : { 90 48 : return m_creationOptions; 91 : } 92 : 93 : virtual int GetInputType() const = 0; 94 : 95 : virtual int GetOutputType() const = 0; 96 : 97 : bool Finalize() override; 98 : 99 : // Used by GDALDispatcherAlgorithm for vector info/convert 100 26 : GDALDataset *GetInputDatasetRef() 101 : { 102 26 : return m_inputDataset.empty() ? nullptr 103 26 : : m_inputDataset[0].GetDatasetRef(); 104 : } 105 : 106 : // Used by GDALDispatcherAlgorithm for vector info/convert 107 : void SetInputDataset(GDALDataset *poDS); 108 : 109 : protected: 110 : struct ConstructorOptions 111 : { 112 : bool standaloneStep = false; 113 : bool addDefaultArguments = true; 114 : bool autoOpenInputDatasets = true; 115 : bool outputDatasetRequired = true; 116 : bool addInputLayerNameArgument = true; // only for vector input 117 : int inputDatasetMaxCount = 1; 118 : std::string inputDatasetHelpMsg{}; 119 : std::string inputDatasetAlias{}; 120 : std::string inputDatasetMetaVar = "INPUT"; 121 : std::string outputDatasetHelpMsg{}; 122 : std::string updateMutualExclusionGroup{}; 123 : std::string outputDatasetMutualExclusionGroup{}; 124 : std::string outputFormatCreateCapability = GDAL_DCAP_CREATECOPY; 125 : 126 6017 : inline ConstructorOptions &SetStandaloneStep(bool b) 127 : { 128 6017 : standaloneStep = b; 129 6017 : return *this; 130 : } 131 : 132 2817 : inline ConstructorOptions &SetAddDefaultArguments(bool b) 133 : { 134 2817 : addDefaultArguments = b; 135 2817 : return *this; 136 : } 137 : 138 150 : inline ConstructorOptions &SetAddInputLayerNameArgument(bool b) 139 : { 140 150 : addInputLayerNameArgument = b; 141 150 : return *this; 142 : } 143 : 144 1532 : inline ConstructorOptions &SetInputDatasetMaxCount(int maxCount) 145 : { 146 1532 : inputDatasetMaxCount = maxCount; 147 1532 : return *this; 148 : } 149 : 150 843 : inline ConstructorOptions &SetInputDatasetHelpMsg(const std::string &s) 151 : { 152 843 : inputDatasetHelpMsg = s; 153 843 : return *this; 154 : } 155 : 156 488 : inline ConstructorOptions &SetInputDatasetAlias(const std::string &s) 157 : { 158 488 : inputDatasetAlias = s; 159 488 : return *this; 160 : } 161 : 162 203 : inline ConstructorOptions &SetInputDatasetMetaVar(const std::string &s) 163 : { 164 203 : inputDatasetMetaVar = s; 165 203 : return *this; 166 : } 167 : 168 71 : inline ConstructorOptions &SetOutputDatasetHelpMsg(const std::string &s) 169 : { 170 71 : outputDatasetHelpMsg = s; 171 71 : return *this; 172 : } 173 : 174 362 : inline ConstructorOptions &SetAutoOpenInputDatasets(bool b) 175 : { 176 362 : autoOpenInputDatasets = b; 177 362 : return *this; 178 : } 179 : 180 58 : inline ConstructorOptions &SetOutputDatasetRequired(bool b) 181 : { 182 58 : outputDatasetRequired = b; 183 58 : return *this; 184 : } 185 : 186 : inline ConstructorOptions & 187 58 : SetUpdateMutualExclusionGroup(const std::string &s) 188 : { 189 58 : updateMutualExclusionGroup = s; 190 58 : return *this; 191 : } 192 : 193 : inline ConstructorOptions & 194 58 : SetOutputDatasetMutualExclusionGroup(const std::string &s) 195 : { 196 58 : outputDatasetMutualExclusionGroup = s; 197 58 : return *this; 198 : } 199 : 200 : inline ConstructorOptions & 201 459 : SetOutputFormatCreateCapability(const std::string &capability) 202 : { 203 459 : outputFormatCreateCapability = capability; 204 459 : return *this; 205 : } 206 : }; 207 : 208 : GDALPipelineStepAlgorithm(const std::string &name, 209 : const std::string &description, 210 : const std::string &helpURL, 211 : const ConstructorOptions &); 212 : 213 : friend class GDALPipelineAlgorithm; 214 : friend class GDALRasterPipelineAlgorithm; 215 : friend class GDALVectorPipelineAlgorithm; 216 : friend class GDALAbstractPipelineAlgorithm; 217 : 218 1477 : virtual bool CanBeFirstStep() const 219 : { 220 1477 : return false; 221 : } 222 : 223 422 : virtual bool CanBeMiddleStep() const 224 : { 225 422 : return !CanBeFirstStep() && !CanBeLastStep(); 226 : } 227 : 228 1213 : virtual bool CanBeLastStep() const 229 : { 230 1213 : return false; 231 : } 232 : 233 : //! Whether a user parameter can cause a file to be written at a specified location 234 43 : virtual bool GeneratesFilesFromUserInput() const 235 : { 236 43 : return false; 237 : } 238 : 239 338 : virtual bool IsNativelyStreamingCompatible() const 240 : { 241 338 : return true; 242 : } 243 : 244 158 : virtual bool SupportsInputMultiThreading() const 245 : { 246 158 : return false; 247 : } 248 : 249 890 : virtual bool CanHandleNextStep(GDALPipelineStepAlgorithm *) const 250 : { 251 890 : return false; 252 : } 253 : 254 : virtual bool RunStep(GDALPipelineStepRunContext &ctxt) = 0; 255 : 256 : bool m_standaloneStep = false; 257 : const ConstructorOptions m_constructorOptions; 258 : bool m_outputVRTCompatible = true; 259 : std::string m_helpDocCategory{}; 260 : 261 : // Input arguments 262 : std::vector<GDALArgDatasetValue> m_inputDataset{}; 263 : std::vector<std::string> m_openOptions{}; 264 : std::vector<std::string> m_inputFormats{}; 265 : std::vector<std::string> m_inputLayerNames{}; 266 : 267 : // Output arguments 268 : bool m_stdout = false; 269 : std::string m_output{}; 270 : GDALArgDatasetValue m_outputDataset{}; 271 : std::string m_format{}; 272 : std::vector<std::string> m_outputOpenOptions{}; 273 : std::vector<std::string> m_creationOptions{}; 274 : bool m_overwrite = false; 275 : std::string m_outputLayerName{}; 276 : GDALInConstructionAlgorithmArg *m_outputFormatArg = nullptr; 277 : bool m_appendRaster = false; 278 : 279 : // Output arguments (vector specific) 280 : std::vector<std::string> m_layerCreationOptions{}; 281 : bool m_update = false; 282 : bool m_overwriteLayer = false; 283 : bool m_appendLayer = false; 284 : bool m_upsert = false; 285 : bool m_skipErrors = false; 286 : 287 : void AddRasterInputArgs(bool openForMixedRasterVector, bool hiddenForCLI); 288 : void AddRasterOutputArgs(bool hiddenForCLI); 289 : void AddRasterHiddenInputDatasetArg(); 290 : 291 : void AddVectorInputArgs(bool hiddenForCLI); 292 : void AddVectorOutputArgs(bool hiddenForCLI, 293 : bool shortNameOutputLayerAllowed); 294 : 295 : private: 296 : bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override; 297 : GDALAlgorithm::ProcessGDALGOutputRet ProcessGDALGOutput() override; 298 : bool CheckSafeForStreamOutput() override; 299 : 300 : CPL_DISALLOW_COPY_ASSIGN(GDALPipelineStepAlgorithm) 301 : }; 302 : 303 : /************************************************************************/ 304 : /* GDALAbstractPipelineAlgorithm */ 305 : /************************************************************************/ 306 : 307 : class GDALAbstractPipelineAlgorithm CPL_NON_FINAL 308 : : public GDALPipelineStepAlgorithm 309 : { 310 : public: 311 : std::vector<std::string> GetAutoComplete(std::vector<std::string> &args, 312 : bool lastWordIsComplete, 313 : bool /* showAllOptions*/) override; 314 : 315 : bool Finalize() override; 316 : 317 : std::string GetUsageAsJSON() const override; 318 : 319 : static constexpr const char *OPEN_NESTED_PIPELINE = "["; 320 : static constexpr const char *CLOSE_NESTED_PIPELINE = "]"; 321 : 322 : protected: 323 402 : GDALAbstractPipelineAlgorithm( 324 : const std::string &name, const std::string &description, 325 : const std::string &helpURL, 326 : const typename GDALPipelineStepAlgorithm::ConstructorOptions &options) 327 402 : : GDALPipelineStepAlgorithm(name, description, helpURL, options) 328 : { 329 402 : } 330 : 331 : std::string m_pipeline{}; 332 : 333 : virtual GDALAlgorithmRegistry &GetStepRegistry() = 0; 334 : 335 : virtual const GDALAlgorithmRegistry &GetStepRegistry() const = 0; 336 : 337 : std::unique_ptr<GDALPipelineStepAlgorithm> 338 : GetStepAlg(const std::string &name) const; 339 : 340 : bool 341 : ParseCommandLineArguments(const std::vector<std::string> &args) override; 342 : 343 : static bool IsReadSpecificArgument(const char *pszArgName); 344 : static bool IsWriteSpecificArgument(const char *pszArgName); 345 : 346 : static constexpr const char *RASTER_SUFFIX = "-raster"; 347 : static constexpr const char *VECTOR_SUFFIX = "-vector"; 348 : 349 : private: 350 : std::vector<std::unique_ptr<GDALPipelineStepAlgorithm>> m_steps{}; 351 : 352 : std::unique_ptr<GDALPipelineStepAlgorithm> m_stepOnWhichHelpIsRequested{}; 353 : 354 : bool m_bExpectReadStep = true; 355 : bool m_bExpectWriteStep = true; 356 : 357 : std::vector<std::unique_ptr<GDALAbstractPipelineAlgorithm>> 358 : m_apoNestedPipelines{}; 359 : 360 : // More would lead to unreadable pipelines 361 : static constexpr int MAX_NESTING_LEVEL = 3; 362 : 363 : bool CheckFirstAndLastStep( 364 : const std::vector<GDALPipelineStepAlgorithm *> &steps) const; 365 : 366 : bool ParseCommandLineArguments(const std::vector<std::string> &args, 367 : bool forAutoComplete); 368 : 369 : bool RunStep(GDALPipelineStepRunContext &ctxt) override; 370 : 371 : std::string 372 : BuildNestedPipeline(GDALPipelineStepAlgorithm *curAlg, 373 : std::vector<std::string> &nestedPipelineArgs, 374 : bool forAutoComplete); 375 : 376 : bool SaveGDALGFile(const std::string &outFilename, 377 : std::string &outString) const; 378 : 379 : virtual std::unique_ptr<GDALAbstractPipelineAlgorithm> 380 : CreateNestedPipeline() const = 0; 381 : }; 382 : 383 : //! @endcond 384 : 385 : #endif