Coverage Report

Created: 2025-12-31 06:48

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 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
        inline ConstructorOptions &SetStandaloneStep(bool b)
159
0
        {
160
0
            standaloneStep = b;
161
0
            return *this;
162
0
        }
163
164
        inline ConstructorOptions &SetAddDefaultArguments(bool b)
165
0
        {
166
0
            addDefaultArguments = b;
167
0
            return *this;
168
0
        }
169
170
        inline ConstructorOptions &SetAddInputLayerNameArgument(bool b)
171
0
        {
172
0
            addInputLayerNameArgument = b;
173
0
            return *this;
174
0
        }
175
176
        inline ConstructorOptions &SetInputDatasetRequired(bool b)
177
0
        {
178
0
            inputDatasetRequired = b;
179
0
            return *this;
180
0
        }
181
182
        inline ConstructorOptions &SetInputDatasetMaxCount(int maxCount)
183
0
        {
184
0
            inputDatasetMaxCount = maxCount;
185
0
            return *this;
186
0
        }
187
188
        inline ConstructorOptions &SetInputDatasetHelpMsg(const std::string &s)
189
0
        {
190
0
            inputDatasetHelpMsg = s;
191
0
            return *this;
192
0
        }
193
194
        inline ConstructorOptions &SetInputDatasetAlias(const std::string &s)
195
0
        {
196
0
            inputDatasetAlias = s;
197
0
            return *this;
198
0
        }
199
200
        inline ConstructorOptions &SetInputDatasetMetaVar(const std::string &s)
201
0
        {
202
0
            inputDatasetMetaVar = s;
203
0
            return *this;
204
0
        }
205
206
        inline ConstructorOptions &SetOutputDatasetHelpMsg(const std::string &s)
207
0
        {
208
0
            outputDatasetHelpMsg = s;
209
0
            return *this;
210
0
        }
211
212
        inline ConstructorOptions &SetAutoOpenInputDatasets(bool b)
213
0
        {
214
0
            autoOpenInputDatasets = b;
215
0
            return *this;
216
0
        }
217
218
        inline ConstructorOptions &SetOutputDatasetRequired(bool b)
219
0
        {
220
0
            outputDatasetRequired = b;
221
0
            return *this;
222
0
        }
223
224
        inline ConstructorOptions &
225
        SetOutputFormatCreateCapability(const std::string &capability)
226
0
        {
227
0
            outputFormatCreateCapability = capability;
228
0
            return *this;
229
0
        }
230
231
        inline ConstructorOptions &SetAddAppendLayerArgument(bool b)
232
0
        {
233
0
            addAppendLayerArgument = b;
234
0
            return *this;
235
0
        }
236
237
        inline ConstructorOptions &SetAddOverwriteLayerArgument(bool b)
238
0
        {
239
0
            addOverwriteLayerArgument = b;
240
0
            return *this;
241
0
        }
242
243
        inline ConstructorOptions &SetAddUpdateArgument(bool b)
244
0
        {
245
0
            addUpdateArgument = b;
246
0
            return *this;
247
0
        }
248
249
        inline ConstructorOptions &SetAddUpsertArgument(bool b)
250
0
        {
251
0
            addUpsertArgument = b;
252
0
            return *this;
253
0
        }
254
255
        inline ConstructorOptions &SetAddSkipErrorsArgument(bool b)
256
0
        {
257
0
            addSkipErrorsArgument = b;
258
0
            return *this;
259
0
        }
260
261
        inline ConstructorOptions &SetAddOutputLayerNameArgument(bool b)
262
0
        {
263
0
            addOutputLayerNameArgument = b;
264
0
            return *this;
265
0
        }
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
    virtual bool CanBeFirstStep() const
279
0
    {
280
0
        return false;
281
0
    }
282
283
    virtual bool CanBeMiddleStep() const
284
0
    {
285
0
        return !CanBeFirstStep() && !CanBeLastStep();
286
0
    }
287
288
    virtual bool CanBeLastStep() const
289
0
    {
290
0
        return false;
291
0
    }
292
293
    //! Whether a user parameter can cause a file to be written at a specified location
294
    virtual bool GeneratesFilesFromUserInput() const
295
0
    {
296
0
        return false;
297
0
    }
298
299
    virtual bool IsNativelyStreamingCompatible() const
300
0
    {
301
0
        return true;
302
0
    }
303
304
    virtual bool SupportsInputMultiThreading() const
305
0
    {
306
0
        return false;
307
0
    }
308
309
    virtual bool CanHandleNextStep(GDALPipelineStepAlgorithm *) const
310
0
    {
311
0
        return false;
312
0
    }
313
314
    virtual bool OutputDatasetAllowedBeforeRunningStep() const
315
0
    {
316
0
        return false;
317
0
    }
318
319
    virtual CPLJSONObject Get_OGR_SCHEMA_OpenOption_Layer() const
320
0
    {
321
0
        CPLJSONObject obj;
322
0
        obj.Deinit();
323
0
        return obj;
324
0
    }
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
367
  private:
368
    bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override;
369
    GDALAlgorithm::ProcessGDALGOutputRet ProcessGDALGOutput() override;
370
    bool CheckSafeForStreamOutput() override;
371
372
    CPL_DISALLOW_COPY_ASSIGN(GDALPipelineStepAlgorithm)
373
};
374
375
/************************************************************************/
376
/*                    GDALAbstractPipelineAlgorithm                     */
377
/************************************************************************/
378
379
class GDALAbstractPipelineAlgorithm CPL_NON_FINAL
380
    : public GDALPipelineStepAlgorithm
381
{
382
  public:
383
    std::vector<std::string> GetAutoComplete(std::vector<std::string> &args,
384
                                             bool lastWordIsComplete,
385
                                             bool /* showAllOptions*/) override;
386
387
    bool Finalize() override;
388
389
    std::string GetUsageAsJSON() const override;
390
391
    bool
392
    ParseCommandLineArguments(const std::vector<std::string> &args) override;
393
394
    bool HasSteps() const
395
0
    {
396
0
        return !m_steps.empty();
397
0
    }
398
399
    static constexpr const char *OPEN_NESTED_PIPELINE = "[";
400
    static constexpr const char *CLOSE_NESTED_PIPELINE = "]";
401
402
    static constexpr const char *RASTER_SUFFIX = "-raster";
403
    static constexpr const char *VECTOR_SUFFIX = "-vector";
404
405
  protected:
406
    friend class GDALTeeStepAlgorithmAbstract;
407
408
    GDALAbstractPipelineAlgorithm(
409
        const std::string &name, const std::string &description,
410
        const std::string &helpURL,
411
        const GDALPipelineStepAlgorithm::ConstructorOptions &options)
412
        : GDALPipelineStepAlgorithm(
413
              name, description, helpURL,
414
              ConstructorOptions(options).SetAutoOpenInputDatasets(false))
415
0
    {
416
0
    }
417
418
    std::string m_pipeline{};
419
420
    virtual GDALAlgorithmRegistry &GetStepRegistry() = 0;
421
422
    virtual const GDALAlgorithmRegistry &GetStepRegistry() const = 0;
423
424
    std::unique_ptr<GDALPipelineStepAlgorithm>
425
    GetStepAlg(const std::string &name) const;
426
427
    bool HasOutputString() const override;
428
429
    static bool IsReadSpecificArgument(const char *pszArgName);
430
    static bool IsWriteSpecificArgument(const char *pszArgName);
431
432
  private:
433
    friend class GDALPipelineAlgorithm;
434
    friend class GDALRasterPipelineAlgorithm;
435
    friend class GDALVectorPipelineAlgorithm;
436
437
    std::vector<std::unique_ptr<GDALPipelineStepAlgorithm>> m_steps{};
438
439
    std::unique_ptr<GDALPipelineStepAlgorithm> m_stepOnWhichHelpIsRequested{};
440
441
    bool m_bInnerPipeline = false;
442
    bool m_bExpectReadStep = true;
443
444
    enum class StepConstraint
445
    {
446
        MUST_BE,
447
        CAN_BE,
448
        CAN_NOT_BE
449
    };
450
451
    StepConstraint m_eLastStepAsWrite = StepConstraint::CAN_BE;
452
453
    std::vector<std::unique_ptr<GDALAbstractPipelineAlgorithm>>
454
        m_apoNestedPipelines{};
455
456
    // More would lead to unreadable pipelines
457
    static constexpr int MAX_NESTING_LEVEL = 3;
458
459
    bool
460
    CheckFirstAndLastStep(const std::vector<GDALPipelineStepAlgorithm *> &steps,
461
                          bool forAutoComplete) const;
462
463
    bool ParseCommandLineArguments(const std::vector<std::string> &args,
464
                                   bool forAutoComplete);
465
466
    bool RunStep(GDALPipelineStepRunContext &ctxt) override;
467
468
    std::string
469
    BuildNestedPipeline(GDALPipelineStepAlgorithm *curAlg,
470
                        std::vector<std::string> &nestedPipelineArgs,
471
                        bool forAutoComplete);
472
473
    bool SaveGDALGFile(const std::string &outFilename,
474
                       std::string &outString) const;
475
476
    virtual std::unique_ptr<GDALAbstractPipelineAlgorithm>
477
    CreateNestedPipeline() const = 0;
478
};
479
480
//! @endcond
481
482
#endif