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