Coverage Report

Created: 2026-03-30 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_pipeline.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  gdal "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
//! @cond Doxygen_Suppress
14
15
#include "gdalalg_pipeline.h"
16
#include "cpl_error.h"
17
#include "gdal_priv.h"
18
19
#include "gdalalg_external.h"
20
21
#include "gdalalg_raster_read.h"
22
#include "gdalalg_raster_mosaic.h"
23
#include "gdalalg_raster_stack.h"
24
#include "gdalalg_raster_write.h"
25
#include "gdalalg_raster_zonal_stats.h"
26
27
#include "gdalalg_vector_read.h"
28
#include "gdalalg_vector_write.h"
29
30
#include "gdalalg_raster_as_features.h"
31
#include "gdalalg_raster_compare.h"
32
#include "gdalalg_raster_contour.h"
33
#include "gdalalg_raster_footprint.h"
34
#include "gdalalg_raster_polygonize.h"
35
#include "gdalalg_raster_info.h"
36
#include "gdalalg_raster_pixel_info.h"
37
#include "gdalalg_raster_tile.h"
38
#include "gdalalg_vector_grid.h"
39
#include "gdalalg_vector_info.h"
40
#include "gdalalg_vector_rasterize.h"
41
42
#include <algorithm>
43
#include <cassert>
44
45
#ifndef _
46
#define _(x) (x)
47
#endif
48
49
/************************************************************************/
50
/*                     GDALPipelineStepAlgorithm()                      */
51
/************************************************************************/
52
53
GDALPipelineStepAlgorithm::GDALPipelineStepAlgorithm(
54
    const std::string &name, const std::string &description,
55
    const std::string &helpURL, const ConstructorOptions &options)
56
0
    : GDALAlgorithm(name, description, helpURL),
57
0
      m_standaloneStep(options.standaloneStep), m_constructorOptions(options)
58
0
{
59
0
}
60
61
/************************************************************************/
62
/*     GDALPipelineStepAlgorithm::AddRasterHiddenInputDatasetArg()      */
63
/************************************************************************/
64
65
void GDALPipelineStepAlgorithm::AddRasterHiddenInputDatasetArg()
66
0
{
67
0
    AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER, false)
68
0
        .SetMinCount(0)
69
0
        .SetMaxCount(m_constructorOptions.inputDatasetMaxCount)
70
0
        .SetAutoOpenDataset(m_constructorOptions.autoOpenInputDatasets)
71
0
        .SetMetaVar(m_constructorOptions.inputDatasetMetaVar)
72
0
        .SetHidden();
73
0
}
74
75
/************************************************************************/
76
/*           GDALPipelineStepAlgorithm::AddRasterInputArgs()            */
77
/************************************************************************/
78
79
void GDALPipelineStepAlgorithm::AddRasterInputArgs(
80
    bool openForMixedRasterVector, bool hiddenForCLI)
81
0
{
82
0
    AddInputFormatsArg(&m_inputFormats)
83
0
        .AddMetadataItem(
84
0
            GAAMDI_REQUIRED_CAPABILITIES,
85
0
            openForMixedRasterVector
86
0
                ? std::vector<std::string>{GDAL_DCAP_RASTER, GDAL_DCAP_VECTOR}
87
0
                : std::vector<std::string>{GDAL_DCAP_RASTER})
88
0
        .SetHiddenForCLI(hiddenForCLI);
89
0
    AddOpenOptionsArg(&m_openOptions).SetHiddenForCLI(hiddenForCLI);
90
0
    auto &arg =
91
0
        AddInputDatasetArg(
92
0
            &m_inputDataset,
93
0
            openForMixedRasterVector ? (GDAL_OF_RASTER | GDAL_OF_VECTOR)
94
0
                                     : GDAL_OF_RASTER,
95
0
            false, m_constructorOptions.inputDatasetHelpMsg.c_str())
96
0
            .SetMinCount(m_constructorOptions.inputDatasetRequired ? 1 : 0)
97
0
            .SetMaxCount(m_constructorOptions.inputDatasetMaxCount)
98
0
            .SetAutoOpenDataset(m_constructorOptions.autoOpenInputDatasets)
99
0
            .SetMetaVar(m_constructorOptions.inputDatasetMetaVar)
100
0
            .SetHiddenForCLI(hiddenForCLI);
101
0
    if (m_constructorOptions.inputDatasetPositional && !hiddenForCLI)
102
0
        arg.SetPositional();
103
0
    if (m_constructorOptions.inputDatasetRequired && !hiddenForCLI)
104
0
        arg.SetRequired();
105
0
    if (!m_constructorOptions.inputDatasetAlias.empty())
106
0
        arg.AddAlias(m_constructorOptions.inputDatasetAlias);
107
0
}
108
109
/************************************************************************/
110
/*           GDALPipelineStepAlgorithm::AddRasterOutputArgs()           */
111
/************************************************************************/
112
113
void GDALPipelineStepAlgorithm::AddRasterOutputArgs(bool hiddenForCLI)
114
0
{
115
0
    m_outputFormatArg =
116
0
        &(AddOutputFormatArg(&m_format, /* bStreamAllowed = */ true,
117
0
                             /* bGDALGAllowed = */ true)
118
0
              .AddMetadataItem(
119
0
                  GAAMDI_REQUIRED_CAPABILITIES,
120
0
                  {GDAL_DCAP_RASTER,
121
0
                   m_constructorOptions.outputFormatCreateCapability.c_str()})
122
0
              .SetHiddenForCLI(hiddenForCLI));
123
0
    AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER,
124
0
                        /* positionalAndRequired = */ !hiddenForCLI,
125
0
                        m_constructorOptions.outputDatasetHelpMsg.c_str())
126
0
        .SetHiddenForCLI(hiddenForCLI)
127
0
        .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
128
0
    AddCreationOptionsArg(&m_creationOptions).SetHiddenForCLI(hiddenForCLI);
129
0
    constexpr const char *MUTUAL_EXCLUSION_GROUP_OVERWRITE_APPEND =
130
0
        "overwrite-append";
131
0
    AddOverwriteArg(&m_overwrite)
132
0
        .SetHiddenForCLI(hiddenForCLI)
133
0
        .SetMutualExclusionGroup(MUTUAL_EXCLUSION_GROUP_OVERWRITE_APPEND);
134
0
    AddArg(GDAL_ARG_NAME_APPEND, 0,
135
0
           _("Append as a subdataset to existing output"), &m_appendRaster)
136
0
        .SetDefault(false)
137
0
        .SetHiddenForCLI(hiddenForCLI)
138
0
        .SetMutualExclusionGroup(MUTUAL_EXCLUSION_GROUP_OVERWRITE_APPEND);
139
0
}
140
141
/************************************************************************/
142
/*     GDALPipelineStepAlgorithm::AddVectorHiddenInputDatasetArg()      */
143
/************************************************************************/
144
145
void GDALPipelineStepAlgorithm::AddVectorHiddenInputDatasetArg()
146
0
{
147
0
    AddInputDatasetArg(&m_inputDataset, GDAL_OF_VECTOR, false)
148
0
        .SetMinCount(0)
149
0
        .SetMaxCount(m_constructorOptions.inputDatasetMaxCount)
150
0
        .SetAutoOpenDataset(m_constructorOptions.autoOpenInputDatasets)
151
0
        .SetMetaVar(m_constructorOptions.inputDatasetMetaVar)
152
0
        .SetHidden();
153
0
}
154
155
/************************************************************************/
156
/*           GDALPipelineStepAlgorithm::AddVectorInputArgs()            */
157
/************************************************************************/
158
159
void GDALPipelineStepAlgorithm::AddVectorInputArgs(bool hiddenForCLI)
160
0
{
161
0
    AddInputFormatsArg(&m_inputFormats)
162
0
        .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR})
163
0
        .SetHiddenForCLI(hiddenForCLI);
164
0
    AddOpenOptionsArg(&m_openOptions).SetHiddenForCLI(hiddenForCLI);
165
0
    auto &datasetArg =
166
0
        AddInputDatasetArg(&m_inputDataset, GDAL_OF_VECTOR, false)
167
0
            .SetMinCount(m_constructorOptions.inputDatasetRequired ? 1 : 0)
168
0
            .SetMaxCount(m_constructorOptions.inputDatasetMaxCount)
169
0
            .SetAutoOpenDataset(m_constructorOptions.autoOpenInputDatasets)
170
0
            .SetHiddenForCLI(hiddenForCLI);
171
0
    if (m_constructorOptions.inputDatasetPositional && !hiddenForCLI)
172
0
        datasetArg.SetPositional();
173
0
    if (m_constructorOptions.inputDatasetRequired && !hiddenForCLI)
174
0
        datasetArg.SetRequired();
175
0
    if (m_constructorOptions.addInputLayerNameArgument)
176
0
    {
177
0
        auto &layerArg = AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
178
0
                                _("Input layer name(s)"), &m_inputLayerNames)
179
0
                             .AddAlias("layer")
180
0
                             .SetHiddenForCLI(hiddenForCLI);
181
0
        SetAutoCompleteFunctionForLayerName(layerArg, datasetArg);
182
0
    }
183
0
}
184
185
/************************************************************************/
186
/*           GDALPipelineStepAlgorithm::AddVectorOutputArgs()           */
187
/************************************************************************/
188
189
void GDALPipelineStepAlgorithm::AddVectorOutputArgs(
190
    bool hiddenForCLI, bool shortNameOutputLayerAllowed)
191
0
{
192
0
    AddOutputFormatArg(&m_format, /* bStreamAllowed = */ true,
193
0
                       /* bGDALGAllowed = */ true)
194
0
        .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
195
0
                         {GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE})
196
0
        .SetHiddenForCLI(hiddenForCLI);
197
0
    AddOutputOpenOptionsArg(&m_outputOpenOptions).SetHiddenForCLI(hiddenForCLI);
198
0
    auto &outputDatasetArg =
199
0
        AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR,
200
0
                            /* positionalAndRequired = */ false)
201
0
            .SetHiddenForCLI(hiddenForCLI)
202
0
            .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
203
0
    if (!hiddenForCLI)
204
0
        outputDatasetArg.SetPositional();
205
0
    if (!hiddenForCLI && m_constructorOptions.outputDatasetRequired)
206
0
        outputDatasetArg.SetRequired();
207
208
0
    AddCreationOptionsArg(&m_creationOptions).SetHiddenForCLI(hiddenForCLI);
209
0
    AddLayerCreationOptionsArg(&m_layerCreationOptions)
210
0
        .SetHiddenForCLI(hiddenForCLI);
211
0
    AddOverwriteArg(&m_overwrite).SetHiddenForCLI(hiddenForCLI);
212
0
    GDALInConstructionAlgorithmArg *updateArg = nullptr;
213
0
    if (m_constructorOptions.addUpdateArgument)
214
0
    {
215
0
        updateArg = &AddUpdateArg(&m_update).SetHiddenForCLI(hiddenForCLI);
216
0
    }
217
0
    if (m_constructorOptions.addOverwriteLayerArgument)
218
0
    {
219
0
        AddOverwriteLayerArg(&m_overwriteLayer).SetHiddenForCLI(hiddenForCLI);
220
0
    }
221
0
    constexpr const char *MUTUAL_EXCLUSION_GROUP_APPEND_UPSERT =
222
0
        "append-upsert";
223
0
    if (m_constructorOptions.addAppendLayerArgument)
224
0
    {
225
0
        AddAppendLayerArg(&m_appendLayer)
226
0
            .SetHiddenForCLI(hiddenForCLI)
227
0
            .SetMutualExclusionGroup(MUTUAL_EXCLUSION_GROUP_APPEND_UPSERT);
228
0
    }
229
0
    if (m_constructorOptions.addUpsertArgument)
230
0
    {
231
0
        AddArg("upsert", 0, _("Upsert features (implies 'append')"), &m_upsert)
232
0
            .SetHiddenForCLI(hiddenForCLI)
233
0
            .SetMutualExclusionGroup(MUTUAL_EXCLUSION_GROUP_APPEND_UPSERT)
234
0
            .AddAction(
235
0
                [updateArg, this]()
236
0
                {
237
0
                    if (m_upsert && updateArg)
238
0
                        updateArg->Set(true);
239
0
                })
240
0
            .SetCategory(GAAC_ADVANCED);
241
0
    }
242
0
    if (m_constructorOptions.addOutputLayerNameArgument)
243
0
    {
244
0
        AddOutputLayerNameArg(hiddenForCLI, shortNameOutputLayerAllowed);
245
0
    }
246
0
    if (m_constructorOptions.addSkipErrorsArgument)
247
0
    {
248
0
        AddArg("skip-errors", 0, _("Skip errors when writing features"),
249
0
               &m_skipErrors)
250
0
            .AddHiddenAlias("skip-failures");  // For ogr2ogr nostalgic people
251
0
    }
252
0
    if (m_constructorOptions.addNoCreateEmptyLayersArgument)
253
0
    {
254
0
        AddArg("no-create-empty-layers", 0,
255
0
               _("Avoid creating layers to which no features will be written"),
256
0
               &m_noCreateEmptyLayers);
257
0
    }
258
0
}
259
260
/************************************************************************/
261
/*          GDALPipelineStepAlgorithm::AddOutputLayerNameArg()          */
262
/************************************************************************/
263
264
void GDALPipelineStepAlgorithm::AddOutputLayerNameArg(
265
    bool hiddenForCLI, bool shortNameOutputLayerAllowed)
266
0
{
267
0
    AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, shortNameOutputLayerAllowed ? 'l' : 0,
268
0
           _("Output layer name"),
269
0
           &m_outputLayerName)
270
0
        .AddHiddenAlias("nln")  // For ogr2ogr nostalgic people
271
0
        .SetHiddenForCLI(hiddenForCLI);
272
0
}
273
274
/************************************************************************/
275
/*                 GDALPipelineStepAlgorithm::RunImpl()                 */
276
/************************************************************************/
277
278
bool GDALPipelineStepAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
279
                                        void *pProgressData)
280
0
{
281
0
    if (m_standaloneStep)
282
0
    {
283
0
        std::unique_ptr<GDALPipelineStepAlgorithm> readAlg;
284
0
        if (GetInputType() == GDAL_OF_RASTER)
285
0
            readAlg = std::make_unique<GDALRasterReadAlgorithm>();
286
0
        else
287
0
            readAlg = std::make_unique<GDALVectorReadAlgorithm>();
288
0
        for (auto &arg : readAlg->GetArgs())
289
0
        {
290
0
            auto stepArg = GetArg(arg->GetName());
291
0
            if (stepArg && stepArg->IsExplicitlySet() &&
292
0
                stepArg->GetType() == arg->GetType())
293
0
            {
294
0
                arg->SetSkipIfAlreadySet(true);
295
0
                arg->SetFrom(*stepArg);
296
0
            }
297
0
        }
298
299
0
        std::unique_ptr<GDALPipelineStepAlgorithm> writeAlg;
300
0
        if (GetOutputType() == GDAL_OF_RASTER)
301
0
        {
302
0
            if (GetName() == GDALRasterInfoAlgorithm::NAME)
303
0
                writeAlg = std::make_unique<GDALRasterInfoAlgorithm>();
304
0
            else if (GetName() == GDALRasterCompareAlgorithm::NAME)
305
0
                writeAlg = std::make_unique<GDALRasterCompareAlgorithm>();
306
0
            else
307
0
                writeAlg = std::make_unique<GDALRasterWriteAlgorithm>();
308
0
        }
309
0
        else
310
0
        {
311
0
            if (GetName() == GDALVectorInfoAlgorithm::NAME)
312
0
                writeAlg = std::make_unique<GDALVectorInfoAlgorithm>();
313
0
            else
314
0
                writeAlg = std::make_unique<GDALVectorWriteAlgorithm>();
315
0
        }
316
0
        for (auto &arg : writeAlg->GetArgs())
317
0
        {
318
0
            auto stepArg = GetArg(arg->GetName());
319
0
            if (stepArg && stepArg->IsExplicitlySet() &&
320
0
                stepArg->GetType() == arg->GetType())
321
0
            {
322
0
                arg->SetSkipIfAlreadySet(true);
323
0
                arg->SetFrom(*stepArg);
324
0
            }
325
0
        }
326
327
0
        const bool bIsStreaming = m_format == "stream";
328
329
        // Already checked by GDALAlgorithm::Run()
330
0
        CPLAssert(!m_executionForStreamOutput || bIsStreaming);
331
332
0
        bool ret = false;
333
0
        if (!m_outputVRTCompatible &&
334
0
            (EQUAL(m_format.c_str(), "VRT") ||
335
0
             (m_format.empty() &&
336
0
              EQUAL(CPLGetExtensionSafe(m_outputDataset.GetName().c_str())
337
0
                        .c_str(),
338
0
                    "VRT"))))
339
0
        {
340
0
            ReportError(CE_Failure, CPLE_NotSupported,
341
0
                        "VRT output is not supported. Consider using the "
342
0
                        "GDALG driver instead (files with .gdalg.json "
343
0
                        "extension)");
344
0
        }
345
0
        else if (readAlg->Run())
346
0
        {
347
0
            auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
348
0
            const bool bOutputSpecified =
349
0
                outputArg && outputArg->IsExplicitlySet();
350
351
0
            m_inputDataset.clear();
352
0
            m_inputDataset.resize(1);
353
0
            m_inputDataset[0].Set(readAlg->m_outputDataset.GetDatasetRef());
354
0
            if (bOutputSpecified)
355
0
                m_outputDataset.Set(nullptr);
356
357
0
            std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
358
0
                pScaledData(nullptr, GDALDestroyScaledProgress);
359
360
0
            const bool bCanHandleNextStep =
361
0
                !bIsStreaming && CanHandleNextStep(writeAlg.get());
362
363
0
            GDALPipelineStepRunContext stepCtxt;
364
0
            if (pfnProgress && GetName() == GDALRasterCompareAlgorithm::NAME)
365
0
            {
366
0
                stepCtxt.m_pfnProgress = pfnProgress;
367
0
                stepCtxt.m_pProgressData = pProgressData;
368
0
            }
369
0
            else if (pfnProgress &&
370
0
                     (bCanHandleNextStep || !IsNativelyStreamingCompatible()))
371
0
            {
372
0
                pScaledData.reset(GDALCreateScaledProgress(
373
0
                    0.0, bIsStreaming || bCanHandleNextStep ? 1.0 : 0.5,
374
0
                    pfnProgress, pProgressData));
375
0
                stepCtxt.m_pfnProgress =
376
0
                    pScaledData ? GDALScaledProgress : nullptr;
377
0
                stepCtxt.m_pProgressData = pScaledData.get();
378
0
            }
379
380
0
            if (bCanHandleNextStep)
381
0
                stepCtxt.m_poNextUsableStep = writeAlg.get();
382
0
            if (RunPreStepPipelineValidations() && RunStep(stepCtxt))
383
0
            {
384
0
                if (bCanHandleNextStep || !bOutputSpecified)
385
0
                {
386
0
                    ret = true;
387
0
                }
388
0
                else
389
0
                {
390
0
                    writeAlg->m_outputVRTCompatible = m_outputVRTCompatible;
391
0
                    writeAlg->m_quiet = m_quiet;
392
393
0
                    std::vector<GDALArgDatasetValue> inputDataset(1);
394
0
                    inputDataset[0].Set(m_outputDataset.GetDatasetRef());
395
0
                    auto inputArg = writeAlg->GetArg(GDAL_ARG_NAME_INPUT);
396
0
                    CPLAssert(inputArg);
397
0
                    inputArg->Set(std::move(inputDataset));
398
399
0
                    if (pfnProgress)
400
0
                    {
401
0
                        pScaledData.reset(GDALCreateScaledProgress(
402
0
                            IsNativelyStreamingCompatible() ? 0.0 : 0.5, 1.0,
403
0
                            pfnProgress, pProgressData));
404
0
                    }
405
0
                    stepCtxt.m_pfnProgress =
406
0
                        pScaledData ? GDALScaledProgress : nullptr;
407
0
                    stepCtxt.m_pProgressData = pScaledData.get();
408
0
                    if (writeAlg->ValidateArguments() &&
409
0
                        writeAlg->RunStep(stepCtxt))
410
0
                    {
411
0
                        if (pfnProgress)
412
0
                            pfnProgress(1.0, "", pProgressData);
413
414
0
                        m_outputDataset.Set(
415
0
                            writeAlg->m_outputDataset.GetDatasetRef());
416
0
                        ret = true;
417
0
                    }
418
0
                }
419
0
            }
420
0
        }
421
422
0
        return ret;
423
0
    }
424
0
    else
425
0
    {
426
0
        GDALPipelineStepRunContext stepCtxt;
427
0
        stepCtxt.m_pfnProgress = pfnProgress;
428
0
        stepCtxt.m_pProgressData = pProgressData;
429
0
        return RunPreStepPipelineValidations() && RunStep(stepCtxt);
430
0
    }
431
0
}
432
433
/************************************************************************/
434
/*                          SetInputDataset()                           */
435
/************************************************************************/
436
437
void GDALPipelineStepAlgorithm::SetInputDataset(GDALDataset *poDS)
438
0
{
439
0
    auto arg = GetArg(GDAL_ARG_NAME_INPUT);
440
0
    if (arg)
441
0
    {
442
0
        auto &val = arg->Get<std::vector<GDALArgDatasetValue>>();
443
0
        val.resize(1);
444
0
        val[0].Set(poDS);
445
0
        arg->NotifyValueSet();
446
0
        arg->SetSkipIfAlreadySet();
447
0
    }
448
0
}
449
450
/************************************************************************/
451
/*                         ProcessGDALGOutput()                         */
452
/************************************************************************/
453
454
GDALAlgorithm::ProcessGDALGOutputRet
455
GDALPipelineStepAlgorithm::ProcessGDALGOutput()
456
0
{
457
0
    if (m_standaloneStep)
458
0
    {
459
0
        return GDALAlgorithm::ProcessGDALGOutput();
460
0
    }
461
0
    else
462
0
    {
463
        // GDALAbstractPipelineAlgorithm<StepAlgorithm>::RunStep() might
464
        // actually detect a GDALG output request and process it.
465
0
        return GDALAlgorithm::ProcessGDALGOutputRet::NOT_GDALG;
466
0
    }
467
0
}
468
469
/************************************************************************/
470
/*        GDALPipelineStepAlgorithm::CheckSafeForStreamOutput()         */
471
/************************************************************************/
472
473
bool GDALPipelineStepAlgorithm::CheckSafeForStreamOutput()
474
0
{
475
0
    if (m_standaloneStep)
476
0
    {
477
0
        return GDALAlgorithm::CheckSafeForStreamOutput();
478
0
    }
479
0
    else
480
0
    {
481
        // The check is actually done in
482
        // GDALAbstractPipelineAlgorithm<StepAlgorithm>::RunStep()
483
        // so return true for now.
484
0
        return true;
485
0
    }
486
0
}
487
488
/************************************************************************/
489
/*                GDALPipelineStepAlgorithm::Finalize()                 */
490
/************************************************************************/
491
492
bool GDALPipelineStepAlgorithm::Finalize()
493
0
{
494
0
    bool ret = GDALAlgorithm::Finalize();
495
0
    for (auto &argValue : m_inputDataset)
496
0
        ret = argValue.Close() && ret;
497
0
    ret = m_outputDataset.Close() && ret;
498
0
    return ret;
499
0
}
500
501
0
GDALAlgorithmStepRegistry::~GDALAlgorithmStepRegistry() = default;
502
503
/************************************************************************/
504
/*                        GDALPipelineAlgorithm                         */
505
/************************************************************************/
506
507
GDALPipelineAlgorithm::GDALPipelineAlgorithm()
508
0
    : GDALAbstractPipelineAlgorithm(
509
0
          NAME, DESCRIPTION, HELP_URL,
510
0
          ConstructorOptions().SetStandaloneStep(false))
511
0
{
512
0
    m_supportsStreamedOutput = true;
513
514
0
    AddProgressArg();
515
0
    AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER | GDAL_OF_VECTOR,
516
0
                       /* positionalAndRequired = */ false)
517
0
        .SetMinCount(1)
518
0
        .SetMaxCount(INT_MAX)
519
0
        .SetHiddenForCLI();
520
0
    AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER | GDAL_OF_VECTOR,
521
0
                        /* positionalAndRequired = */ false)
522
0
        .SetHiddenForCLI()
523
0
        .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
524
0
    AddOutputFormatArg(&m_format, /* bStreamAllowed = */ true,
525
0
                       /* bGDALGAllowed = */ true)
526
0
        .SetHiddenForCLI();
527
0
    AddArg("pipeline", 0, _("Pipeline string or filename"), &m_pipeline)
528
0
        .SetHiddenForCLI()
529
0
        .SetPositional();
530
531
0
    AddOutputStringArg(&m_output).SetHiddenForCLI();
532
0
    AddStdoutArg(&m_stdout);
533
534
0
    AllowArbitraryLongNameArgs();
535
536
0
    GDALRasterPipelineAlgorithm::RegisterAlgorithms(m_stepRegistry, true);
537
0
    GDALVectorPipelineAlgorithm::RegisterAlgorithms(m_stepRegistry, true);
538
0
    m_stepRegistry.Register<GDALExternalRasterOrVectorAlgorithm>();
539
0
    m_stepRegistry.Register<GDALRasterAsFeaturesAlgorithm>();
540
0
    m_stepRegistry.Register<GDALRasterContourAlgorithm>();
541
0
    m_stepRegistry.Register<GDALRasterFootprintAlgorithm>();
542
0
    m_stepRegistry.Register<GDALRasterPixelInfoAlgorithm>();
543
0
    m_stepRegistry.Register<GDALRasterPolygonizeAlgorithm>();
544
0
    m_stepRegistry.Register<GDALRasterZonalStatsAlgorithm>();
545
0
    m_stepRegistry.Register<GDALVectorGridAlgorithm>();
546
0
    m_stepRegistry.Register<GDALVectorRasterizeAlgorithm>();
547
0
}
548
549
/************************************************************************/
550
/*               GDALPipelineAlgorithm::GetUsageForCLI()                */
551
/************************************************************************/
552
553
std::string
554
GDALPipelineAlgorithm::GetUsageForCLI(bool shortUsage,
555
                                      const UsageOptions &usageOptions) const
556
0
{
557
0
    UsageOptions stepUsageOptions;
558
0
    stepUsageOptions.isPipelineStep = true;
559
560
0
    if (!m_helpDocCategory.empty() && m_helpDocCategory != "main")
561
0
    {
562
0
        auto alg = GetStepAlg(m_helpDocCategory);
563
0
        if (alg)
564
0
        {
565
0
            alg->SetCallPath({CPLString(m_helpDocCategory)
566
0
                                  .replaceAll(RASTER_SUFFIX, "")
567
0
                                  .replaceAll(VECTOR_SUFFIX, "")});
568
0
            alg->GetArg("help-doc")->Set(true);
569
0
            return alg->GetUsageForCLI(shortUsage, stepUsageOptions);
570
0
        }
571
0
        else
572
0
        {
573
0
            fprintf(stderr, "ERROR: unknown pipeline step '%s'\n",
574
0
                    m_helpDocCategory.c_str());
575
0
            return CPLSPrintf("ERROR: unknown pipeline step '%s'\n",
576
0
                              m_helpDocCategory.c_str());
577
0
        }
578
0
    }
579
580
0
    UsageOptions usageOptionsMain(usageOptions);
581
0
    usageOptionsMain.isPipelineMain = true;
582
0
    std::string ret =
583
0
        GDALAlgorithm::GetUsageForCLI(shortUsage, usageOptionsMain);
584
0
    if (shortUsage)
585
0
        return ret;
586
587
0
    ret +=
588
0
        "\n<PIPELINE> is of the form: read|calc|concat|create|mosaic|stack "
589
0
        "[READ-OPTIONS] "
590
0
        "( ! <STEP-NAME> [STEP-OPTIONS] )* ! write!info!tile [WRITE-OPTIONS]\n";
591
592
0
    if (m_helpDocCategory == "main")
593
0
    {
594
0
        return ret;
595
0
    }
596
597
0
    ret += '\n';
598
0
    ret += "Example: 'gdal pipeline --progress ! read in.tif ! \\\n";
599
0
    ret += "               rasterize --size 256 256 ! buffer 20 ! ";
600
0
    ret += "write out.gpkg --overwrite'\n";
601
0
    ret += '\n';
602
0
    ret += "Potential steps are:\n";
603
604
0
    for (const std::string &name : m_stepRegistry.GetNames())
605
0
    {
606
0
        auto alg = GetStepAlg(name);
607
0
        assert(alg);
608
0
        auto [options, maxOptLen] = alg->GetArgNamesForCLI();
609
0
        stepUsageOptions.maxOptLen =
610
0
            std::max(stepUsageOptions.maxOptLen, maxOptLen);
611
0
    }
612
613
0
    {
614
0
        ret += '\n';
615
0
        auto alg = std::make_unique<GDALRasterReadAlgorithm>();
616
0
        alg->SetCallPath({alg->GetName()});
617
0
        ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
618
0
    }
619
0
    {
620
0
        ret += '\n';
621
0
        auto alg = std::make_unique<GDALVectorReadAlgorithm>();
622
0
        alg->SetCallPath({alg->GetName()});
623
0
        ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
624
0
    }
625
0
    for (const std::string &name : m_stepRegistry.GetNames())
626
0
    {
627
0
        auto alg = GetStepAlg(name);
628
0
        assert(alg);
629
0
        if (alg->CanBeFirstStep() && !alg->CanBeMiddleStep() &&
630
0
            !alg->IsHidden() &&
631
0
            !STARTS_WITH(name.c_str(), GDALRasterReadAlgorithm::NAME))
632
0
        {
633
0
            ret += '\n';
634
0
            alg->SetCallPath({name});
635
0
            ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
636
0
        }
637
0
    }
638
0
    for (const std::string &name : m_stepRegistry.GetNames())
639
0
    {
640
0
        auto alg = GetStepAlg(name);
641
0
        assert(alg);
642
0
        if (alg->CanBeMiddleStep() && !alg->IsHidden())
643
0
        {
644
0
            ret += '\n';
645
0
            alg->SetCallPath({CPLString(alg->GetName())
646
0
                                  .replaceAll(RASTER_SUFFIX, "")
647
0
                                  .replaceAll(VECTOR_SUFFIX, "")});
648
0
            ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
649
0
        }
650
0
    }
651
0
    for (const std::string &name : m_stepRegistry.GetNames())
652
0
    {
653
0
        auto alg = GetStepAlg(name);
654
0
        assert(alg);
655
0
        if (alg->CanBeLastStep() && !alg->CanBeMiddleStep() &&
656
0
            !alg->IsHidden() &&
657
0
            !STARTS_WITH(name.c_str(), GDALRasterWriteAlgorithm::NAME))
658
0
        {
659
0
            ret += '\n';
660
0
            alg->SetCallPath({name});
661
0
            ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
662
0
        }
663
0
    }
664
0
    {
665
0
        ret += '\n';
666
0
        auto alg = std::make_unique<GDALRasterWriteAlgorithm>();
667
0
        alg->SetCallPath({alg->GetName()});
668
0
        ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
669
0
    }
670
0
    {
671
0
        ret += '\n';
672
0
        auto alg = std::make_unique<GDALVectorWriteAlgorithm>();
673
0
        alg->SetCallPath({alg->GetName()});
674
0
        ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
675
0
    }
676
677
0
    ret += GetUsageForCLIEnd();
678
679
0
    return ret;
680
0
}
681
682
//! @endcond