Coverage Report

Created: 2026-04-01 06:20

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