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_json.h" 19 : 20 : #include "gdalalgorithm.h" 21 : #include "gdal_priv.h" 22 : 23 : #include <algorithm> 24 : 25 : // This is an easter egg to pay tribute to PROJ pipeline syntax 26 : // 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" 27 : // as an alternative to (recommended): 28 : // "gdal vector pipeline ! read in.tif ! reproject--dst-crs=EPSG:32632 ! write out.tif --overwrite" 29 : #ifndef GDAL_PIPELINE_PROJ_NOSTALGIA 30 : #define GDAL_PIPELINE_PROJ_NOSTALGIA 31 : #endif 32 : 33 : /************************************************************************/ 34 : /* GDALPipelineStepRunContext */ 35 : /************************************************************************/ 36 : 37 : class GDALPipelineStepAlgorithm; 38 : 39 : class GDALPipelineStepRunContext 40 : { 41 : public: 42 : GDALPipelineStepRunContext() = default; 43 : 44 : // Progress callback to use during execution of the step 45 : GDALProgressFunc m_pfnProgress = nullptr; 46 : void *m_pProgressData = nullptr; 47 : 48 : // If there is a step in the pipeline immediately following step to which 49 : // this instance of GDALRasterPipelineStepRunContext is passed, and that 50 : // this next step is usable by the current step (as determined by 51 : // CanHandleNextStep()), then this member will point to this next step. 52 : GDALPipelineStepAlgorithm *m_poNextUsableStep = nullptr; 53 : 54 : private: 55 : CPL_DISALLOW_COPY_ASSIGN(GDALPipelineStepRunContext) 56 : }; 57 : 58 : /************************************************************************/ 59 : /* GDALPipelineStepAlgorithm */ 60 : /************************************************************************/ 61 : 62 : class GDALPipelineStepAlgorithm /* non final */ : public GDALAlgorithm 63 : { 64 : public: 65 116 : std::vector<GDALArgDatasetValue> &GetInputDatasets() 66 : { 67 116 : return m_inputDataset; 68 : } 69 : 70 : const std::vector<GDALArgDatasetValue> &GetInputDatasets() const 71 : { 72 : return m_inputDataset; 73 : } 74 : 75 99 : GDALArgDatasetValue &GetOutputDataset() 76 : { 77 99 : return m_outputDataset; 78 : } 79 : 80 : const GDALArgDatasetValue &GetOutputDataset() const 81 : { 82 : return m_outputDataset; 83 : } 84 : 85 475 : const std::string &GetOutputString() const 86 : { 87 475 : return m_output; 88 : } 89 : 90 20 : const std::string &GetOutputLayerName() const 91 : { 92 20 : return m_outputLayerName; 93 : } 94 : 95 175 : const std::string &GetOutputFormat() const 96 : { 97 175 : return m_format; 98 : } 99 : 100 68 : const std::vector<std::string> &GetCreationOptions() const 101 : { 102 68 : return m_creationOptions; 103 : } 104 : 105 16 : const std::vector<std::string> &GetLayerCreationOptions() const 106 : { 107 16 : return m_layerCreationOptions; 108 : } 109 : 110 19 : bool GetOverwriteLayer() const 111 : { 112 19 : return m_overwriteLayer; 113 : } 114 : 115 19 : bool GetAppendLayer() const 116 : { 117 19 : return m_appendLayer; 118 : } 119 : 120 : virtual int GetInputType() const = 0; 121 : 122 : virtual int GetOutputType() const = 0; 123 : 124 : bool Finalize() override; 125 : 126 : // Used by GDALDispatcherAlgorithm for vector info/convert 127 32 : GDALDataset *GetInputDatasetRef() 128 : { 129 32 : return m_inputDataset.empty() ? nullptr 130 32 : : m_inputDataset[0].GetDatasetRef(); 131 : } 132 : 133 : // Used by GDALDispatcherAlgorithm for vector info/convert 134 : void SetInputDataset(GDALDataset *poDS); 135 : 136 : protected: 137 : struct ConstructorOptions 138 : { 139 : bool standaloneStep = false; 140 : bool addDefaultArguments = true; 141 : bool autoOpenInputDatasets = true; 142 : bool inputDatasetRequired = true; 143 : bool inputDatasetPositional = true; 144 : bool outputDatasetRequired = true; 145 : bool addInputLayerNameArgument = true; // only for vector input 146 : bool addUpdateArgument = true; // only for vector output 147 : bool addAppendLayerArgument = true; // only for vector output 148 : bool addNoCreateEmptyLayersArgument = false; // only for vector output 149 : bool addOverwriteLayerArgument = true; // only for vector output 150 : bool addUpsertArgument = true; // only for vector output 151 : bool addSkipErrorsArgument = true; // only for vector output 152 : bool addOutputLayerNameArgument = true; // only for vector output 153 : int inputDatasetMaxCount = 1; 154 : std::string inputDatasetHelpMsg{}; 155 : std::string inputDatasetAlias{}; 156 : std::string inputDatasetMetaVar = "INPUT"; 157 : std::string outputDatasetHelpMsg{}; 158 : std::string outputFormatCreateCapability = GDAL_DCAP_CREATECOPY; 159 : 160 7023 : inline ConstructorOptions &SetStandaloneStep(bool b) 161 : { 162 7023 : standaloneStep = b; 163 7023 : return *this; 164 : } 165 : 166 4055 : inline ConstructorOptions &SetAddDefaultArguments(bool b) 167 : { 168 4055 : addDefaultArguments = b; 169 4055 : return *this; 170 : } 171 : 172 206 : inline ConstructorOptions &SetAddInputLayerNameArgument(bool b) 173 : { 174 206 : addInputLayerNameArgument = b; 175 206 : return *this; 176 : } 177 : 178 244 : inline ConstructorOptions &SetInputDatasetRequired(bool b) 179 : { 180 244 : inputDatasetRequired = b; 181 244 : return *this; 182 : } 183 : 184 146 : inline ConstructorOptions &SetInputDatasetPositional(bool b) 185 : { 186 146 : inputDatasetPositional = b; 187 146 : return *this; 188 : } 189 : 190 1864 : inline ConstructorOptions &SetInputDatasetMaxCount(int maxCount) 191 : { 192 1864 : inputDatasetMaxCount = maxCount; 193 1864 : return *this; 194 : } 195 : 196 1062 : inline ConstructorOptions &SetInputDatasetHelpMsg(const std::string &s) 197 : { 198 1062 : inputDatasetHelpMsg = s; 199 1062 : return *this; 200 : } 201 : 202 836 : inline ConstructorOptions &SetInputDatasetAlias(const std::string &s) 203 : { 204 836 : inputDatasetAlias = s; 205 836 : return *this; 206 : } 207 : 208 368 : inline ConstructorOptions &SetInputDatasetMetaVar(const std::string &s) 209 : { 210 368 : inputDatasetMetaVar = s; 211 368 : return *this; 212 : } 213 : 214 234 : inline ConstructorOptions &SetOutputDatasetHelpMsg(const std::string &s) 215 : { 216 234 : outputDatasetHelpMsg = s; 217 234 : return *this; 218 : } 219 : 220 927 : inline ConstructorOptions &SetAutoOpenInputDatasets(bool b) 221 : { 222 927 : autoOpenInputDatasets = b; 223 927 : return *this; 224 : } 225 : 226 56 : inline ConstructorOptions &SetOutputDatasetRequired(bool b) 227 : { 228 56 : outputDatasetRequired = b; 229 56 : return *this; 230 : } 231 : 232 : inline ConstructorOptions & 233 837 : SetOutputFormatCreateCapability(const std::string &capability) 234 : { 235 837 : outputFormatCreateCapability = capability; 236 837 : return *this; 237 : } 238 : 239 125 : inline ConstructorOptions &SetAddAppendLayerArgument(bool b) 240 : { 241 125 : addAppendLayerArgument = b; 242 125 : return *this; 243 : } 244 : 245 125 : inline ConstructorOptions &SetAddOverwriteLayerArgument(bool b) 246 : { 247 125 : addOverwriteLayerArgument = b; 248 125 : return *this; 249 : } 250 : 251 125 : inline ConstructorOptions &SetAddUpdateArgument(bool b) 252 : { 253 125 : addUpdateArgument = b; 254 125 : return *this; 255 : } 256 : 257 215 : inline ConstructorOptions &SetAddUpsertArgument(bool b) 258 : { 259 215 : addUpsertArgument = b; 260 215 : return *this; 261 : } 262 : 263 650 : inline ConstructorOptions &SetNoCreateEmptyLayersArgument(bool b) 264 : { 265 650 : addNoCreateEmptyLayersArgument = b; 266 650 : return *this; 267 : } 268 : 269 215 : inline ConstructorOptions &SetAddSkipErrorsArgument(bool b) 270 : { 271 215 : addSkipErrorsArgument = b; 272 215 : return *this; 273 : } 274 : 275 126 : inline ConstructorOptions &SetAddOutputLayerNameArgument(bool b) 276 : { 277 126 : addOutputLayerNameArgument = b; 278 126 : return *this; 279 : } 280 : }; 281 : 282 : GDALPipelineStepAlgorithm(const std::string &name, 283 : const std::string &description, 284 : const std::string &helpURL, 285 : const ConstructorOptions &); 286 : 287 : friend class GDALPipelineAlgorithm; 288 : friend class GDALRasterPipelineAlgorithm; 289 : friend class GDALVectorPipelineAlgorithm; 290 : friend class GDALAbstractPipelineAlgorithm; 291 : 292 1751 : virtual bool CanBeFirstStep() const 293 : { 294 1751 : return false; 295 : } 296 : 297 448 : virtual bool CanBeMiddleStep() const 298 : { 299 448 : return !CanBeFirstStep() && !CanBeLastStep(); 300 : } 301 : 302 683 : virtual bool CanBeLastStep() const 303 : { 304 683 : return false; 305 : } 306 : 307 : //! Whether a user parameter can cause a file to be written at a specified location 308 49 : virtual bool GeneratesFilesFromUserInput() const 309 : { 310 49 : return false; 311 : } 312 : 313 527 : virtual bool IsNativelyStreamingCompatible() const 314 : { 315 527 : return true; 316 : } 317 : 318 188 : virtual bool SupportsInputMultiThreading() const 319 : { 320 188 : return false; 321 : } 322 : 323 1103 : virtual bool CanHandleNextStep(GDALPipelineStepAlgorithm *) const 324 : { 325 1103 : return false; 326 : } 327 : 328 2 : virtual bool OutputDatasetAllowedBeforeRunningStep() const 329 : { 330 2 : return false; 331 : } 332 : 333 191 : virtual CPLJSONObject Get_OGR_SCHEMA_OpenOption_Layer() const 334 : { 335 191 : CPLJSONObject obj; 336 191 : obj.Deinit(); 337 191 : return obj; 338 : } 339 : 340 : virtual bool RunStep(GDALPipelineStepRunContext &ctxt) = 0; 341 : 342 : bool m_standaloneStep = false; 343 : const ConstructorOptions m_constructorOptions; 344 : bool m_outputVRTCompatible = true; 345 : std::string m_helpDocCategory{}; 346 : 347 : // Input arguments 348 : std::vector<GDALArgDatasetValue> m_inputDataset{}; 349 : std::vector<std::string> m_openOptions{}; 350 : std::vector<std::string> m_inputFormats{}; 351 : std::vector<std::string> m_inputLayerNames{}; 352 : 353 : // Output arguments 354 : bool m_stdout = false; 355 : std::string m_output{}; 356 : GDALArgDatasetValue m_outputDataset{}; 357 : std::string m_format{}; 358 : std::vector<std::string> m_outputOpenOptions{}; 359 : std::vector<std::string> m_creationOptions{}; 360 : bool m_overwrite = false; 361 : std::string m_outputLayerName{}; 362 : GDALInConstructionAlgorithmArg *m_outputFormatArg = nullptr; 363 : bool m_appendRaster = false; 364 : 365 : // Output arguments (vector specific) 366 : std::vector<std::string> m_layerCreationOptions{}; 367 : bool m_update = false; 368 : bool m_overwriteLayer = false; 369 : bool m_appendLayer = false; 370 : bool m_upsert = false; 371 : bool m_skipErrors = false; 372 : bool m_noCreateEmptyLayers = false; 373 : 374 : void AddRasterInputArgs(bool openForMixedRasterVector, bool hiddenForCLI); 375 : void AddRasterOutputArgs(bool hiddenForCLI); 376 : void AddRasterHiddenInputDatasetArg(); 377 : 378 : void AddVectorInputArgs(bool hiddenForCLI); 379 : void AddVectorHiddenInputDatasetArg(); 380 : void AddVectorOutputArgs(bool hiddenForCLI, 381 : bool shortNameOutputLayerAllowed); 382 : using GDALAlgorithm::AddOutputLayerNameArg; 383 : void AddOutputLayerNameArg(bool hiddenForCLI, 384 : bool shortNameOutputLayerAllowed); 385 : 386 : private: 387 : bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override; 388 : GDALAlgorithm::ProcessGDALGOutputRet ProcessGDALGOutput() override; 389 : bool CheckSafeForStreamOutput() override; 390 : 391 : CPL_DISALLOW_COPY_ASSIGN(GDALPipelineStepAlgorithm) 392 : }; 393 : 394 : /************************************************************************/ 395 : /* GDALAbstractPipelineAlgorithm */ 396 : /************************************************************************/ 397 : 398 : class GDALAbstractPipelineAlgorithm CPL_NON_FINAL 399 : : public GDALPipelineStepAlgorithm 400 : { 401 : public: 402 : std::vector<std::string> GetAutoComplete(std::vector<std::string> &args, 403 : bool lastWordIsComplete, 404 : bool /* showAllOptions*/) override; 405 : 406 : bool Finalize() override; 407 : 408 : std::string GetUsageAsJSON() const override; 409 : 410 : bool 411 : ParseCommandLineArguments(const std::vector<std::string> &args) override; 412 : 413 2 : bool HasSteps() const 414 : { 415 2 : return !m_steps.empty(); 416 : } 417 : 418 : static constexpr const char *OPEN_NESTED_PIPELINE = "["; 419 : static constexpr const char *CLOSE_NESTED_PIPELINE = "]"; 420 : 421 : static constexpr const char *RASTER_SUFFIX = "-raster"; 422 : static constexpr const char *VECTOR_SUFFIX = "-vector"; 423 : 424 : protected: 425 : friend class GDALTeeStepAlgorithmAbstract; 426 : 427 474 : GDALAbstractPipelineAlgorithm( 428 : const std::string &name, const std::string &description, 429 : const std::string &helpURL, 430 : const GDALPipelineStepAlgorithm::ConstructorOptions &options) 431 474 : : GDALPipelineStepAlgorithm( 432 : name, description, helpURL, 433 474 : ConstructorOptions(options).SetAutoOpenInputDatasets(false)) 434 : { 435 474 : } 436 : 437 : std::string m_pipeline{}; 438 : 439 : virtual GDALAlgorithmRegistry &GetStepRegistry() = 0; 440 : 441 : virtual const GDALAlgorithmRegistry &GetStepRegistry() const = 0; 442 : 443 : std::unique_ptr<GDALPipelineStepAlgorithm> 444 : GetStepAlg(const std::string &name) const; 445 : 446 : bool HasOutputString() const override; 447 : 448 : static bool IsReadSpecificArgument(const char *pszArgName); 449 : static bool IsWriteSpecificArgument(const char *pszArgName); 450 : 451 : private: 452 : friend class GDALPipelineAlgorithm; 453 : friend class GDALRasterPipelineAlgorithm; 454 : friend class GDALVectorPipelineAlgorithm; 455 : 456 : std::vector<std::unique_ptr<GDALPipelineStepAlgorithm>> m_steps{}; 457 : 458 : std::unique_ptr<GDALPipelineStepAlgorithm> m_stepOnWhichHelpIsRequested{}; 459 : 460 : bool m_bInnerPipeline = false; 461 : bool m_bExpectReadStep = true; 462 : 463 : enum class StepConstraint 464 : { 465 : MUST_BE, 466 : CAN_BE, 467 : CAN_NOT_BE 468 : }; 469 : 470 : StepConstraint m_eLastStepAsWrite = StepConstraint::CAN_BE; 471 : 472 : std::vector<std::unique_ptr<GDALAbstractPipelineAlgorithm>> 473 : m_apoNestedPipelines{}; 474 : 475 : // More would lead to unreadable pipelines 476 : static constexpr int MAX_NESTING_LEVEL = 3; 477 : 478 : bool 479 : CheckFirstAndLastStep(const std::vector<GDALPipelineStepAlgorithm *> &steps, 480 : bool forAutoComplete) const; 481 : 482 : bool ParseCommandLineArguments(const std::vector<std::string> &args, 483 : bool forAutoComplete); 484 : 485 : bool RunStep(GDALPipelineStepRunContext &ctxt) override; 486 : 487 : std::string 488 : BuildNestedPipeline(GDALPipelineStepAlgorithm *curAlg, 489 : std::vector<std::string> &nestedPipelineArgs, 490 : bool forAutoComplete); 491 : 492 : bool SaveGDALGFile(const std::string &outFilename, 493 : std::string &outString) const; 494 : 495 : virtual std::unique_ptr<GDALAbstractPipelineAlgorithm> 496 : CreateNestedPipeline() const = 0; 497 : }; 498 : 499 : //! @endcond 500 : 501 : #endif