Coverage Report

Created: 2025-11-16 06:25

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