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