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