GDAL
gdalalg_abstract_pipeline.h
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
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 +output-crs=EPSG:32632 +step +gdal=write +output=out.tif +overwrite"
27// as an alternative to (recommended):
28// "gdal vector pipeline ! read in.tif ! reproject--output-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
37class GDALPipelineStepAlgorithm;
38
39class 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
62class GDALPipelineStepAlgorithm /* non final */ : public GDALAlgorithm
63{
64 public:
65 std::vector<GDALArgDatasetValue> &GetInputDatasets()
66 {
67 return m_inputDataset;
68 }
69
70 const std::vector<GDALArgDatasetValue> &GetInputDatasets() const
71 {
72 return m_inputDataset;
73 }
74
75 GDALArgDatasetValue &GetOutputDataset()
76 {
77 return m_outputDataset;
78 }
79
80 const GDALArgDatasetValue &GetOutputDataset() const
81 {
82 return m_outputDataset;
83 }
84
85 const std::string &GetOutputString() const
86 {
87 return m_output;
88 }
89
90 const std::string &GetOutputLayerName() const
91 {
92 return m_outputLayerName;
93 }
94
95 const std::string &GetOutputFormat() const
96 {
97 return m_format;
98 }
99
100 const std::vector<std::string> &GetCreationOptions() const
101 {
102 return m_creationOptions;
103 }
104
105 const std::vector<std::string> &GetLayerCreationOptions() const
106 {
107 return m_layerCreationOptions;
108 }
109
110 bool GetOverwriteLayer() const
111 {
112 return m_overwriteLayer;
113 }
114
115 bool GetAppendLayer() const
116 {
117 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 GDALDataset *GetInputDatasetRef()
128 {
129 return m_inputDataset.empty() ? nullptr
130 : 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 inline ConstructorOptions &SetStandaloneStep(bool b)
163 {
164 standaloneStep = b;
165 return *this;
166 }
167
168 inline ConstructorOptions &SetAddDefaultArguments(bool b)
169 {
170 addDefaultArguments = b;
171 return *this;
172 }
173
174 inline ConstructorOptions &SetAddInputLayerNameArgument(bool b)
175 {
176 addInputLayerNameArgument = b;
177 return *this;
178 }
179
180 inline ConstructorOptions &SetInputDatasetRequired(bool b)
181 {
182 inputDatasetRequired = b;
183 return *this;
184 }
185
186 inline ConstructorOptions &SetInputDatasetPositional(bool b)
187 {
188 inputDatasetPositional = b;
189 return *this;
190 }
191
192 inline ConstructorOptions &SetInputDatasetMaxCount(int maxCount)
193 {
194 inputDatasetMaxCount = maxCount;
195 return *this;
196 }
197
198 inline ConstructorOptions &SetInputDatasetInputFlags(int flags)
199 {
200 inputDatasetInputFlags = flags;
201 return *this;
202 }
203
204 inline ConstructorOptions &SetInputDatasetHelpMsg(const std::string &s)
205 {
206 inputDatasetHelpMsg = s;
207 return *this;
208 }
209
210 inline ConstructorOptions &SetInputDatasetAlias(const std::string &s)
211 {
212 inputDatasetAlias = s;
213 return *this;
214 }
215
216 inline ConstructorOptions &SetInputDatasetMetaVar(const std::string &s)
217 {
218 inputDatasetMetaVar = s;
219 return *this;
220 }
221
222 inline ConstructorOptions &SetOutputDatasetHelpMsg(const std::string &s)
223 {
224 outputDatasetHelpMsg = s;
225 return *this;
226 }
227
228 inline ConstructorOptions &SetAutoOpenInputDatasets(bool b)
229 {
230 autoOpenInputDatasets = b;
231 return *this;
232 }
233
234 inline ConstructorOptions &SetOutputDatasetRequired(bool b)
235 {
236 outputDatasetRequired = b;
237 return *this;
238 }
239
240 inline ConstructorOptions &
241 SetOutputFormatCreateCapability(const std::string &capability)
242 {
243 outputFormatCreateCapability = capability;
244 return *this;
245 }
246
247 inline ConstructorOptions &SetAddAppendLayerArgument(bool b)
248 {
249 addAppendLayerArgument = b;
250 return *this;
251 }
252
253 inline ConstructorOptions &SetAddOverwriteLayerArgument(bool b)
254 {
255 addOverwriteLayerArgument = b;
256 return *this;
257 }
258
259 inline ConstructorOptions &SetAddUpdateArgument(bool b)
260 {
261 addUpdateArgument = b;
262 return *this;
263 }
264
265 inline ConstructorOptions &SetAddUpsertArgument(bool b)
266 {
267 addUpsertArgument = b;
268 return *this;
269 }
270
271 inline ConstructorOptions &SetNoCreateEmptyLayersArgument(bool b)
272 {
273 addNoCreateEmptyLayersArgument = b;
274 return *this;
275 }
276
277 inline ConstructorOptions &SetAddSkipErrorsArgument(bool b)
278 {
279 addSkipErrorsArgument = b;
280 return *this;
281 }
282
283 inline ConstructorOptions &SetAddOutputLayerNameArgument(bool b)
284 {
285 addOutputLayerNameArgument = b;
286 return *this;
287 }
288
289 inline ConstructorOptions &
290 SetOutputLayerNameAvailableInPipelineStep(bool b)
291 {
292 outputLayerNameAvailableInPipelineStep = b;
293 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 virtual bool CanBeFirstStep() const
308 {
309 return false;
310 }
311
312 virtual bool CanBeMiddleStep() const
313 {
314 return !CanBeFirstStep() && !CanBeLastStep();
315 }
316
317 virtual bool CanBeLastStep() const
318 {
319 return false;
320 }
321
323 virtual bool GeneratesFilesFromUserInput() const
324 {
325 return false;
326 }
327
328 virtual bool IsNativelyStreamingCompatible() const
329 {
330 return true;
331 }
332
333 virtual bool SupportsInputMultiThreading() const
334 {
335 return false;
336 }
337
338 virtual bool CanHandleNextStep(GDALPipelineStepAlgorithm *) const
339 {
340 return false;
341 }
342
343 virtual bool OutputDatasetAllowedBeforeRunningStep() const
344 {
345 return false;
346 }
347
348 virtual CPLJSONObject Get_OGR_SCHEMA_OpenOption_Layer() const
349 {
350 CPLJSONObject obj;
351 obj.Deinit();
352 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);
398 void AddOutputLayerNameArg(bool hiddenForCLI,
399 bool shortNameOutputLayerAllowed);
400
401 private:
402 bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override;
404 bool CheckSafeForStreamOutput() override;
405
406 CPL_DISALLOW_COPY_ASSIGN(GDALPipelineStepAlgorithm)
407};
408
409/************************************************************************/
410/* GDALAbstractPipelineAlgorithm */
411/************************************************************************/
412
413class 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 bool HasSteps() const
429 {
430 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 GDALAbstractPipelineAlgorithm(
443 const std::string &name, const std::string &description,
444 const std::string &helpURL,
445 const GDALPipelineStepAlgorithm::ConstructorOptions &options)
446 : GDALPipelineStepAlgorithm(
447 name, description, helpURL,
448 ConstructorOptions(options).SetAutoOpenInputDatasets(false))
449 {
450 }
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 static int GetInputDatasetType(const GDALPipelineStepAlgorithm *alg);
499
500 bool CopyStepAlgorithmFromAnother(GDALPipelineStepAlgorithm *dst,
501 const GDALPipelineStepAlgorithm *src,
502 bool maybeWriteStep) const;
503
504 bool ParseCommandLineArguments(const std::vector<std::string> &args,
505 bool forAutoComplete);
506
507 bool RunStep(GDALPipelineStepRunContext &ctxt) override;
508
509 std::string
510 BuildNestedPipeline(GDALPipelineStepAlgorithm *curAlg,
511 std::vector<std::string> &nestedPipelineArgs,
512 bool forAutoComplete);
513
514 bool SaveGDALGIntoFileOrString(const std::string &outFilename,
515 std::string &outString) const;
516
517 virtual std::unique_ptr<GDALAbstractPipelineAlgorithm>
518 CreateNestedPipeline() const = 0;
519};
520
522
523#endif
The CPLJSONArray class holds JSON object from CPLJSONDocument.
Definition cpl_json.h:60
void Deinit()
Decrement reference counter and make pointer NULL.
Definition cpl_json.cpp:1390
Registry of GDAL algorithms.
Definition gdalalgorithm_cpp.h:2316
GDAL algorithm.
Definition gdalalgorithm_cpp.h:2445
virtual bool Finalize()
Complete any pending actions, and return the final status.
Definition gdalalgorithm.cpp:6307
ProcessGDALGOutputRet
Return value for ProcessGDALGOutput.
Definition gdalalgorithm_cpp.h:3147
GDALInConstructionAlgorithmArg & AddOutputLayerNameArg(std::string *pValue, const char *helpMessage=nullptr)
Add (single) output layer name argument.
Definition gdalalgorithm.cpp:5033
virtual bool CheckSafeForStreamOutput()
Method executed by Run() when m_executionForStreamOutput is set to ensure the command is safe to exec...
Definition gdalalgorithm.cpp:6284
virtual ProcessGDALGOutputRet ProcessGDALGOutput()
Process output to a .gdalg file.
Definition gdalalgorithm.cpp:5726
Value for an argument that points to a GDALDataset.
Definition gdalalgorithm_cpp.h:163
A set of associated raster bands, usually from one file.
Definition gdal_dataset.h:77
Technical class used by GDALAlgorithm when constructing argument declarations.
Definition gdalalgorithm_cpp.h:1918
Interface for read and write JSON documents.
#define CPL_NON_FINAL
Mark that a class is explicitly recognized as non-final.
Definition cpl_port.h:1094
#define CPL_DISALLOW_COPY_ASSIGN(ClassName)
Helper to remove the copy and assignment constructors so that the compiler will not generate the defa...
Definition cpl_port.h:1101
#define GDAL_DCAP_CREATECOPY
Capability set by a driver that implements the CreateCopy() API.
Definition gdal.h:623
This file is legacy since GDAL 3.12, but will be kept at least in the whole GDAL 3....