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 118 : std::vector<GDALArgDatasetValue> &GetInputDatasets() 66 : { 67 118 : return m_inputDataset; 68 : } 69 : 70 : const std::vector<GDALArgDatasetValue> &GetInputDatasets() const 71 : { 72 : return m_inputDataset; 73 : } 74 : 75 102 : GDALArgDatasetValue &GetOutputDataset() 76 : { 77 102 : return m_outputDataset; 78 : } 79 : 80 : const GDALArgDatasetValue &GetOutputDataset() const 81 : { 82 : return m_outputDataset; 83 : } 84 : 85 519 : const std::string &GetOutputString() const 86 : { 87 519 : return m_output; 88 : } 89 : 90 20 : const std::string &GetOutputLayerName() const 91 : { 92 20 : return m_outputLayerName; 93 : } 94 : 95 178 : const std::string &GetOutputFormat() const 96 : { 97 178 : return m_format; 98 : } 99 : 100 69 : const std::vector<std::string> &GetCreationOptions() const 101 : { 102 69 : return m_creationOptions; 103 : } 104 : 105 53 : const std::vector<std::string> &GetLayerCreationOptions() const 106 : { 107 53 : return m_layerCreationOptions; 108 : } 109 : 110 57 : bool GetOverwriteLayer() const 111 : { 112 57 : 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 : bool outputLayerNameAvailableInPipelineStep = false; 154 : int inputDatasetMaxCount = 1; 155 : int inputDatasetInputFlags = GADV_NAME | GADV_OBJECT; 156 : std::string inputDatasetHelpMsg{}; 157 : std::string inputDatasetAlias{}; 158 : std::string inputDatasetMetaVar = "INPUT"; 159 : std::string outputDatasetHelpMsg{}; 160 : std::string outputFormatCreateCapability = GDAL_DCAP_CREATECOPY; 161 : 162 10872 : inline ConstructorOptions &SetStandaloneStep(bool b) 163 : { 164 10872 : standaloneStep = b; 165 10872 : return *this; 166 : } 167 : 168 5113 : inline ConstructorOptions &SetAddDefaultArguments(bool b) 169 : { 170 5113 : addDefaultArguments = b; 171 5113 : return *this; 172 : } 173 : 174 411 : inline ConstructorOptions &SetAddInputLayerNameArgument(bool b) 175 : { 176 411 : addInputLayerNameArgument = b; 177 411 : return *this; 178 : } 179 : 180 455 : inline ConstructorOptions &SetInputDatasetRequired(bool b) 181 : { 182 455 : inputDatasetRequired = b; 183 455 : return *this; 184 : } 185 : 186 455 : inline ConstructorOptions &SetInputDatasetPositional(bool b) 187 : { 188 455 : inputDatasetPositional = b; 189 455 : return *this; 190 : } 191 : 192 2545 : inline ConstructorOptions &SetInputDatasetMaxCount(int maxCount) 193 : { 194 2545 : inputDatasetMaxCount = maxCount; 195 2545 : return *this; 196 : } 197 : 198 175 : inline ConstructorOptions &SetInputDatasetInputFlags(int flags) 199 : { 200 175 : inputDatasetInputFlags = flags; 201 175 : return *this; 202 : } 203 : 204 1381 : inline ConstructorOptions &SetInputDatasetHelpMsg(const std::string &s) 205 : { 206 1381 : inputDatasetHelpMsg = s; 207 1381 : return *this; 208 : } 209 : 210 1263 : inline ConstructorOptions &SetInputDatasetAlias(const std::string &s) 211 : { 212 1263 : inputDatasetAlias = s; 213 1263 : return *this; 214 : } 215 : 216 1044 : inline ConstructorOptions &SetInputDatasetMetaVar(const std::string &s) 217 : { 218 1044 : inputDatasetMetaVar = s; 219 1044 : return *this; 220 : } 221 : 222 269 : inline ConstructorOptions &SetOutputDatasetHelpMsg(const std::string &s) 223 : { 224 269 : outputDatasetHelpMsg = s; 225 269 : return *this; 226 : } 227 : 228 1380 : inline ConstructorOptions &SetAutoOpenInputDatasets(bool b) 229 : { 230 1380 : autoOpenInputDatasets = b; 231 1380 : return *this; 232 : } 233 : 234 96 : inline ConstructorOptions &SetOutputDatasetRequired(bool b) 235 : { 236 96 : outputDatasetRequired = b; 237 96 : return *this; 238 : } 239 : 240 : inline ConstructorOptions & 241 1655 : SetOutputFormatCreateCapability(const std::string &capability) 242 : { 243 1655 : outputFormatCreateCapability = capability; 244 1655 : return *this; 245 : } 246 : 247 330 : inline ConstructorOptions &SetAddAppendLayerArgument(bool b) 248 : { 249 330 : addAppendLayerArgument = b; 250 330 : return *this; 251 : } 252 : 253 201 : inline ConstructorOptions &SetAddOverwriteLayerArgument(bool b) 254 : { 255 201 : addOverwriteLayerArgument = b; 256 201 : return *this; 257 : } 258 : 259 201 : inline ConstructorOptions &SetAddUpdateArgument(bool b) 260 : { 261 201 : addUpdateArgument = b; 262 201 : return *this; 263 : } 264 : 265 492 : inline ConstructorOptions &SetAddUpsertArgument(bool b) 266 : { 267 492 : addUpsertArgument = b; 268 492 : return *this; 269 : } 270 : 271 1115 : inline ConstructorOptions &SetNoCreateEmptyLayersArgument(bool b) 272 : { 273 1115 : addNoCreateEmptyLayersArgument = b; 274 1115 : return *this; 275 : } 276 : 277 492 : inline ConstructorOptions &SetAddSkipErrorsArgument(bool b) 278 : { 279 492 : addSkipErrorsArgument = b; 280 492 : return *this; 281 : } 282 : 283 202 : inline ConstructorOptions &SetAddOutputLayerNameArgument(bool b) 284 : { 285 202 : addOutputLayerNameArgument = b; 286 202 : return *this; 287 : } 288 : 289 : inline ConstructorOptions & 290 197 : SetOutputLayerNameAvailableInPipelineStep(bool b) 291 : { 292 197 : outputLayerNameAvailableInPipelineStep = b; 293 197 : return *this; 294 : } 295 : }; 296 : 297 : GDALPipelineStepAlgorithm(const std::string &name, 298 : const std::string &description, 299 : const std::string &helpURL, 300 : const ConstructorOptions &); 301 : 302 : friend class GDALPipelineAlgorithm; 303 : friend class GDALRasterPipelineAlgorithm; 304 : friend class GDALVectorPipelineAlgorithm; 305 : friend class GDALAbstractPipelineAlgorithm; 306 : 307 1909 : virtual bool CanBeFirstStep() const 308 : { 309 1909 : return false; 310 : } 311 : 312 482 : virtual bool CanBeMiddleStep() const 313 : { 314 482 : return !CanBeFirstStep() && !CanBeLastStep(); 315 : } 316 : 317 747 : virtual bool CanBeLastStep() const 318 : { 319 747 : return false; 320 : } 321 : 322 : //! Whether a user parameter can cause a file to be written at a specified location 323 49 : virtual bool GeneratesFilesFromUserInput() const 324 : { 325 49 : return false; 326 : } 327 : 328 597 : virtual bool IsNativelyStreamingCompatible() const 329 : { 330 597 : return true; 331 : } 332 : 333 205 : virtual bool SupportsInputMultiThreading() const 334 : { 335 205 : return false; 336 : } 337 : 338 1183 : virtual bool CanHandleNextStep(GDALPipelineStepAlgorithm *) const 339 : { 340 1183 : return false; 341 : } 342 : 343 0 : virtual bool OutputDatasetAllowedBeforeRunningStep() const 344 : { 345 0 : return false; 346 : } 347 : 348 200 : virtual CPLJSONObject Get_OGR_SCHEMA_OpenOption_Layer() const 349 : { 350 200 : CPLJSONObject obj; 351 200 : obj.Deinit(); 352 200 : return obj; 353 : } 354 : 355 : virtual bool RunStep(GDALPipelineStepRunContext &ctxt) = 0; 356 : 357 : bool m_standaloneStep = false; 358 : const ConstructorOptions m_constructorOptions; 359 : bool m_outputVRTCompatible = true; 360 : std::string m_helpDocCategory{}; 361 : 362 : // Input arguments 363 : std::vector<GDALArgDatasetValue> m_inputDataset{}; 364 : std::vector<std::string> m_openOptions{}; 365 : std::vector<std::string> m_inputFormats{}; 366 : std::vector<std::string> m_inputLayerNames{}; 367 : 368 : // Output arguments 369 : bool m_stdout = false; 370 : std::string m_output{}; 371 : GDALArgDatasetValue m_outputDataset{}; 372 : std::string m_format{}; 373 : std::vector<std::string> m_outputOpenOptions{}; 374 : std::vector<std::string> m_creationOptions{}; 375 : bool m_overwrite = false; 376 : std::string m_outputLayerName{}; 377 : GDALInConstructionAlgorithmArg *m_outputFormatArg = nullptr; 378 : bool m_appendRaster = false; 379 : 380 : // Output arguments (vector specific) 381 : std::vector<std::string> m_layerCreationOptions{}; 382 : bool m_update = false; 383 : bool m_overwriteLayer = false; 384 : bool m_appendLayer = false; 385 : bool m_upsert = false; 386 : bool m_skipErrors = false; 387 : bool m_noCreateEmptyLayers = false; 388 : 389 : void AddRasterInputArgs(bool openForMixedRasterVector, bool hiddenForCLI); 390 : void AddRasterOutputArgs(bool hiddenForCLI); 391 : void AddRasterHiddenInputDatasetArg(); 392 : 393 : void AddVectorInputArgs(bool hiddenForCLI); 394 : void AddVectorHiddenInputDatasetArg(); 395 : void AddVectorOutputArgs(bool hiddenForCLI, 396 : bool shortNameOutputLayerAllowed); 397 : using GDALAlgorithm::AddOutputLayerNameArg; 398 : void AddOutputLayerNameArg(bool hiddenForCLI, 399 : bool shortNameOutputLayerAllowed); 400 : 401 : private: 402 : bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override; 403 : GDALAlgorithm::ProcessGDALGOutputRet ProcessGDALGOutput() override; 404 : bool CheckSafeForStreamOutput() override; 405 : 406 : CPL_DISALLOW_COPY_ASSIGN(GDALPipelineStepAlgorithm) 407 : }; 408 : 409 : /************************************************************************/ 410 : /* GDALAbstractPipelineAlgorithm */ 411 : /************************************************************************/ 412 : 413 : class GDALAbstractPipelineAlgorithm CPL_NON_FINAL 414 : : public GDALPipelineStepAlgorithm 415 : { 416 : public: 417 : std::vector<std::string> GetAutoComplete(std::vector<std::string> &args, 418 : bool lastWordIsComplete, 419 : bool /* showAllOptions*/) override; 420 : 421 : bool Finalize() override; 422 : 423 : std::string GetUsageAsJSON() const override; 424 : 425 : bool 426 : ParseCommandLineArguments(const std::vector<std::string> &args) override; 427 : 428 2 : bool HasSteps() const 429 : { 430 2 : return !m_steps.empty(); 431 : } 432 : 433 : static constexpr const char *OPEN_NESTED_PIPELINE = "["; 434 : static constexpr const char *CLOSE_NESTED_PIPELINE = "]"; 435 : 436 : static constexpr const char *RASTER_SUFFIX = "-raster"; 437 : static constexpr const char *VECTOR_SUFFIX = "-vector"; 438 : 439 : protected: 440 : friend class GDALTeeStepAlgorithmAbstract; 441 : 442 605 : GDALAbstractPipelineAlgorithm( 443 : const std::string &name, const std::string &description, 444 : const std::string &helpURL, 445 : const GDALPipelineStepAlgorithm::ConstructorOptions &options) 446 605 : : GDALPipelineStepAlgorithm( 447 : name, description, helpURL, 448 605 : ConstructorOptions(options).SetAutoOpenInputDatasets(false)) 449 : { 450 605 : } 451 : 452 : std::string m_pipeline{}; 453 : 454 : virtual GDALAlgorithmRegistry &GetStepRegistry() = 0; 455 : 456 : virtual const GDALAlgorithmRegistry &GetStepRegistry() const = 0; 457 : 458 : std::unique_ptr<GDALPipelineStepAlgorithm> 459 : GetStepAlg(const std::string &name) const; 460 : 461 : bool HasOutputString() const override; 462 : 463 : static bool IsReadSpecificArgument(const char *pszArgName); 464 : static bool IsWriteSpecificArgument(const char *pszArgName); 465 : 466 : private: 467 : friend class GDALPipelineAlgorithm; 468 : friend class GDALRasterPipelineAlgorithm; 469 : friend class GDALVectorPipelineAlgorithm; 470 : 471 : std::vector<std::unique_ptr<GDALPipelineStepAlgorithm>> m_steps{}; 472 : 473 : std::unique_ptr<GDALPipelineStepAlgorithm> m_stepOnWhichHelpIsRequested{}; 474 : 475 : bool m_bInnerPipeline = false; 476 : bool m_bExpectReadStep = true; 477 : int m_nFirstStepWithUnknownInputType = -1; 478 : 479 : enum class StepConstraint 480 : { 481 : MUST_BE, 482 : CAN_BE, 483 : CAN_NOT_BE 484 : }; 485 : 486 : StepConstraint m_eLastStepAsWrite = StepConstraint::CAN_BE; 487 : 488 : std::vector<std::unique_ptr<GDALAbstractPipelineAlgorithm>> 489 : m_apoNestedPipelines{}; 490 : 491 : // More would lead to unreadable pipelines 492 : static constexpr int MAX_NESTING_LEVEL = 3; 493 : 494 : bool 495 : CheckFirstAndLastStep(const std::vector<GDALPipelineStepAlgorithm *> &steps, 496 : bool forAutoComplete) const; 497 : 498 : bool ParseCommandLineArguments(const std::vector<std::string> &args, 499 : bool forAutoComplete); 500 : 501 : bool RunStep(GDALPipelineStepRunContext &ctxt) override; 502 : 503 : std::string 504 : BuildNestedPipeline(GDALPipelineStepAlgorithm *curAlg, 505 : std::vector<std::string> &nestedPipelineArgs, 506 : bool forAutoComplete); 507 : 508 : bool SaveGDALGFile(const std::string &outFilename, 509 : std::string &outString) const; 510 : 511 : virtual std::unique_ptr<GDALAbstractPipelineAlgorithm> 512 : CreateNestedPipeline() const = 0; 513 : }; 514 : 515 : //! @endcond 516 : 517 : #endif