Coverage Report

Created: 2026-04-01 06:20

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.inputDatasetAlias.empty())
172
0
        datasetArg.AddAlias(m_constructorOptions.inputDatasetAlias);
173
0
    if (!m_constructorOptions.inputDatasetMetaVar.empty())
174
0
        datasetArg.SetMetaVar(m_constructorOptions.inputDatasetMetaVar);
175
0
    if (m_constructorOptions.inputDatasetPositional && !hiddenForCLI)
176
0
        datasetArg.SetPositional();
177
0
    if (m_constructorOptions.inputDatasetRequired && !hiddenForCLI)
178
0
        datasetArg.SetRequired();
179
0
    if (m_constructorOptions.addInputLayerNameArgument)
180
0
    {
181
0
        auto &layerArg = AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
182
0
                                _("Input layer name(s)"), &m_inputLayerNames)
183
0
                             .AddAlias("layer")
184
0
                             .SetHiddenForCLI(hiddenForCLI);
185
0
        SetAutoCompleteFunctionForLayerName(layerArg, datasetArg);
186
0
    }
187
0
}
188
189
/************************************************************************/
190
/*           GDALPipelineStepAlgorithm::AddVectorOutputArgs()           */
191
/************************************************************************/
192
193
void GDALPipelineStepAlgorithm::AddVectorOutputArgs(
194
    bool hiddenForCLI, bool shortNameOutputLayerAllowed)
195
0
{
196
0
    AddOutputFormatArg(&m_format, /* bStreamAllowed = */ true,
197
0
                       /* bGDALGAllowed = */ true)
198
0
        .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
199
0
                         {GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE})
200
0
        .SetHiddenForCLI(hiddenForCLI);
201
0
    AddOutputOpenOptionsArg(&m_outputOpenOptions).SetHiddenForCLI(hiddenForCLI);
202
0
    auto &outputDatasetArg =
203
0
        AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR,
204
0
                            /* positionalAndRequired = */ false)
205
0
            .SetHiddenForCLI(hiddenForCLI)
206
0
            .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
207
0
    if (!hiddenForCLI)
208
0
        outputDatasetArg.SetPositional();
209
0
    if (!hiddenForCLI && m_constructorOptions.outputDatasetRequired)
210
0
        outputDatasetArg.SetRequired();
211
212
0
    AddCreationOptionsArg(&m_creationOptions).SetHiddenForCLI(hiddenForCLI);
213
0
    AddLayerCreationOptionsArg(&m_layerCreationOptions)
214
0
        .SetHiddenForCLI(hiddenForCLI);
215
0
    AddOverwriteArg(&m_overwrite).SetHiddenForCLI(hiddenForCLI);
216
0
    GDALInConstructionAlgorithmArg *updateArg = nullptr;
217
0
    if (m_constructorOptions.addUpdateArgument)
218
0
    {
219
0
        updateArg = &AddUpdateArg(&m_update).SetHiddenForCLI(hiddenForCLI);
220
0
    }
221
0
    if (m_constructorOptions.addOverwriteLayerArgument)
222
0
    {
223
0
        AddOverwriteLayerArg(&m_overwriteLayer).SetHiddenForCLI(hiddenForCLI);
224
0
    }
225
0
    constexpr const char *MUTUAL_EXCLUSION_GROUP_APPEND_UPSERT =
226
0
        "append-upsert";
227
0
    if (m_constructorOptions.addAppendLayerArgument)
228
0
    {
229
0
        AddAppendLayerArg(&m_appendLayer)
230
0
            .SetHiddenForCLI(hiddenForCLI)
231
0
            .SetMutualExclusionGroup(MUTUAL_EXCLUSION_GROUP_APPEND_UPSERT);
232
0
    }
233
0
    if (m_constructorOptions.addUpsertArgument)
234
0
    {
235
0
        AddArg("upsert", 0, _("Upsert features (implies 'append')"), &m_upsert)
236
0
            .SetHiddenForCLI(hiddenForCLI)
237
0
            .SetMutualExclusionGroup(MUTUAL_EXCLUSION_GROUP_APPEND_UPSERT)
238
0
            .AddAction(
239
0
                [updateArg, this]()
240
0
                {
241
0
                    if (m_upsert && updateArg)
242
0
                        updateArg->Set(true);
243
0
                })
244
0
            .SetCategory(GAAC_ADVANCED);
245
0
    }
246
0
    if (m_constructorOptions.addOutputLayerNameArgument)
247
0
    {
248
0
        AddOutputLayerNameArg(hiddenForCLI, shortNameOutputLayerAllowed);
249
0
    }
250
0
    if (m_constructorOptions.addSkipErrorsArgument)
251
0
    {
252
0
        AddArg("skip-errors", 0, _("Skip errors when writing features"),
253
0
               &m_skipErrors)
254
0
            .AddHiddenAlias("skip-failures");  // For ogr2ogr nostalgic people
255
0
    }
256
0
    if (m_constructorOptions.addNoCreateEmptyLayersArgument)
257
0
    {
258
0
        AddArg("no-create-empty-layers", 0,
259
0
               _("Avoid creating layers to which no features will be written"),
260
0
               &m_noCreateEmptyLayers);
261
0
    }
262
0
}
263
264
/************************************************************************/
265
/*          GDALPipelineStepAlgorithm::AddOutputLayerNameArg()          */
266
/************************************************************************/
267
268
void GDALPipelineStepAlgorithm::AddOutputLayerNameArg(
269
    bool hiddenForCLI, bool shortNameOutputLayerAllowed)
270
0
{
271
0
    AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, shortNameOutputLayerAllowed ? 'l' : 0,
272
0
           _("Output layer name"),
273
0
           &m_outputLayerName)
274
0
        .AddHiddenAlias("nln")  // For ogr2ogr nostalgic people
275
0
        .SetHiddenForCLI(hiddenForCLI);
276
0
}
277
278
/************************************************************************/
279
/*                 GDALPipelineStepAlgorithm::RunImpl()                 */
280
/************************************************************************/
281
282
bool GDALPipelineStepAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
283
                                        void *pProgressData)
284
0
{
285
0
    if (m_standaloneStep)
286
0
    {
287
0
        std::unique_ptr<GDALPipelineStepAlgorithm> readAlg;
288
0
        if (GetInputType() == GDAL_OF_RASTER)
289
0
            readAlg = std::make_unique<GDALRasterReadAlgorithm>();
290
0
        else
291
0
            readAlg = std::make_unique<GDALVectorReadAlgorithm>();
292
0
        for (auto &arg : readAlg->GetArgs())
293
0
        {
294
0
            auto stepArg = GetArg(arg->GetName());
295
0
            if (stepArg && stepArg->IsExplicitlySet() &&
296
0
                stepArg->GetType() == arg->GetType())
297
0
            {
298
0
                arg->SetSkipIfAlreadySet(true);
299
0
                arg->SetFrom(*stepArg);
300
0
            }
301
0
        }
302
303
0
        std::unique_ptr<GDALPipelineStepAlgorithm> writeAlg;
304
0
        if (GetOutputType() == GDAL_OF_RASTER)
305
0
        {
306
0
            if (GetName() == GDALRasterInfoAlgorithm::NAME)
307
0
                writeAlg = std::make_unique<GDALRasterInfoAlgorithm>();
308
0
            else if (GetName() == GDALRasterCompareAlgorithm::NAME)
309
0
                writeAlg = std::make_unique<GDALRasterCompareAlgorithm>();
310
0
            else
311
0
                writeAlg = std::make_unique<GDALRasterWriteAlgorithm>();
312
0
        }
313
0
        else
314
0
        {
315
0
            if (GetName() == GDALVectorInfoAlgorithm::NAME)
316
0
                writeAlg = std::make_unique<GDALVectorInfoAlgorithm>();
317
0
            else
318
0
                writeAlg = std::make_unique<GDALVectorWriteAlgorithm>();
319
0
        }
320
0
        for (auto &arg : writeAlg->GetArgs())
321
0
        {
322
0
            auto stepArg = GetArg(arg->GetName());
323
0
            if (stepArg && stepArg->IsExplicitlySet() &&
324
0
                stepArg->GetType() == arg->GetType())
325
0
            {
326
0
                arg->SetSkipIfAlreadySet(true);
327
0
                arg->SetFrom(*stepArg);
328
0
            }
329
0
        }
330
331
0
        const bool bIsStreaming = m_format == "stream";
332
333
        // Already checked by GDALAlgorithm::Run()
334
0
        CPLAssert(!m_executionForStreamOutput || bIsStreaming);
335
336
0
        bool ret = false;
337
0
        if (!m_outputVRTCompatible &&
338
0
            (EQUAL(m_format.c_str(), "VRT") ||
339
0
             (m_format.empty() &&
340
0
              EQUAL(CPLGetExtensionSafe(m_outputDataset.GetName().c_str())
341
0
                        .c_str(),
342
0
                    "VRT"))))
343
0
        {
344
0
            ReportError(CE_Failure, CPLE_NotSupported,
345
0
                        "VRT output is not supported. Consider using the "
346
0
                        "GDALG driver instead (files with .gdalg.json "
347
0
                        "extension)");
348
0
        }
349
0
        else if (readAlg->Run())
350
0
        {
351
0
            auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
352
0
            const bool bOutputSpecified =
353
0
                outputArg && outputArg->IsExplicitlySet();
354
355
0
            m_inputDataset.clear();
356
0
            m_inputDataset.resize(1);
357
0
            m_inputDataset[0].Set(readAlg->m_outputDataset.GetDatasetRef());
358
0
            if (bOutputSpecified)
359
0
                m_outputDataset.Set(nullptr);
360
361
0
            std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
362
0
                pScaledData(nullptr, GDALDestroyScaledProgress);
363
364
0
            const bool bCanHandleNextStep =
365
0
                !bIsStreaming && CanHandleNextStep(writeAlg.get());
366
367
0
            GDALPipelineStepRunContext stepCtxt;
368
0
            if (pfnProgress && GetName() == GDALRasterCompareAlgorithm::NAME)
369
0
            {
370
0
                stepCtxt.m_pfnProgress = pfnProgress;
371
0
                stepCtxt.m_pProgressData = pProgressData;
372
0
            }
373
0
            else if (pfnProgress &&
374
0
                     (bCanHandleNextStep || !IsNativelyStreamingCompatible()))
375
0
            {
376
0
                pScaledData.reset(GDALCreateScaledProgress(
377
0
                    0.0, bIsStreaming || bCanHandleNextStep ? 1.0 : 0.5,
378
0
                    pfnProgress, pProgressData));
379
0
                stepCtxt.m_pfnProgress =
380
0
                    pScaledData ? GDALScaledProgress : nullptr;
381
0
                stepCtxt.m_pProgressData = pScaledData.get();
382
0
            }
383
384
0
            if (bCanHandleNextStep)
385
0
                stepCtxt.m_poNextUsableStep = writeAlg.get();
386
0
            if (RunPreStepPipelineValidations() && RunStep(stepCtxt))
387
0
            {
388
0
                if (bCanHandleNextStep || !bOutputSpecified)
389
0
                {
390
0
                    ret = true;
391
0
                }
392
0
                else
393
0
                {
394
0
                    writeAlg->m_outputVRTCompatible = m_outputVRTCompatible;
395
0
                    writeAlg->m_quiet = m_quiet;
396
397
0
                    std::vector<GDALArgDatasetValue> inputDataset(1);
398
0
                    inputDataset[0].Set(m_outputDataset.GetDatasetRef());
399
0
                    auto inputArg = writeAlg->GetArg(GDAL_ARG_NAME_INPUT);
400
0
                    CPLAssert(inputArg);
401
0
                    inputArg->Set(std::move(inputDataset));
402
403
0
                    if (pfnProgress)
404
0
                    {
405
0
                        pScaledData.reset(GDALCreateScaledProgress(
406
0
                            IsNativelyStreamingCompatible() ? 0.0 : 0.5, 1.0,
407
0
                            pfnProgress, pProgressData));
408
0
                    }
409
0
                    stepCtxt.m_pfnProgress =
410
0
                        pScaledData ? GDALScaledProgress : nullptr;
411
0
                    stepCtxt.m_pProgressData = pScaledData.get();
412
0
                    if (writeAlg->ValidateArguments() &&
413
0
                        writeAlg->RunStep(stepCtxt))
414
0
                    {
415
0
                        if (pfnProgress)
416
0
                            pfnProgress(1.0, "", pProgressData);
417
418
0
                        m_outputDataset.Set(
419
0
                            writeAlg->m_outputDataset.GetDatasetRef());
420
0
                        ret = true;
421
0
                    }
422
0
                }
423
0
            }
424
0
        }
425
426
0
        return ret;
427
0
    }
428
0
    else
429
0
    {
430
0
        GDALPipelineStepRunContext stepCtxt;
431
0
        stepCtxt.m_pfnProgress = pfnProgress;
432
0
        stepCtxt.m_pProgressData = pProgressData;
433
0
        return RunPreStepPipelineValidations() && RunStep(stepCtxt);
434
0
    }
435
0
}
436
437
/************************************************************************/
438
/*                          SetInputDataset()                           */
439
/************************************************************************/
440
441
void GDALPipelineStepAlgorithm::SetInputDataset(GDALDataset *poDS)
442
0
{
443
0
    auto arg = GetArg(GDAL_ARG_NAME_INPUT);
444
0
    if (arg)
445
0
    {
446
0
        auto &val = arg->Get<std::vector<GDALArgDatasetValue>>();
447
0
        val.resize(1);
448
0
        val[0].Set(poDS);
449
0
        arg->NotifyValueSet();
450
0
        arg->SetSkipIfAlreadySet();
451
0
    }
452
0
}
453
454
/************************************************************************/
455
/*                         ProcessGDALGOutput()                         */
456
/************************************************************************/
457
458
GDALAlgorithm::ProcessGDALGOutputRet
459
GDALPipelineStepAlgorithm::ProcessGDALGOutput()
460
0
{
461
0
    if (m_standaloneStep)
462
0
    {
463
0
        return GDALAlgorithm::ProcessGDALGOutput();
464
0
    }
465
0
    else
466
0
    {
467
        // GDALAbstractPipelineAlgorithm<StepAlgorithm>::RunStep() might
468
        // actually detect a GDALG output request and process it.
469
0
        return GDALAlgorithm::ProcessGDALGOutputRet::NOT_GDALG;
470
0
    }
471
0
}
472
473
/************************************************************************/
474
/*        GDALPipelineStepAlgorithm::CheckSafeForStreamOutput()         */
475
/************************************************************************/
476
477
bool GDALPipelineStepAlgorithm::CheckSafeForStreamOutput()
478
0
{
479
0
    if (m_standaloneStep)
480
0
    {
481
0
        return GDALAlgorithm::CheckSafeForStreamOutput();
482
0
    }
483
0
    else
484
0
    {
485
        // The check is actually done in
486
        // GDALAbstractPipelineAlgorithm<StepAlgorithm>::RunStep()
487
        // so return true for now.
488
0
        return true;
489
0
    }
490
0
}
491
492
/************************************************************************/
493
/*                GDALPipelineStepAlgorithm::Finalize()                 */
494
/************************************************************************/
495
496
bool GDALPipelineStepAlgorithm::Finalize()
497
0
{
498
0
    bool ret = GDALAlgorithm::Finalize();
499
0
    for (auto &argValue : m_inputDataset)
500
0
        ret = argValue.Close() && ret;
501
0
    ret = m_outputDataset.Close() && ret;
502
0
    return ret;
503
0
}
504
505
0
GDALAlgorithmStepRegistry::~GDALAlgorithmStepRegistry() = default;
506
507
/************************************************************************/
508
/*                        GDALPipelineAlgorithm                         */
509
/************************************************************************/
510
511
GDALPipelineAlgorithm::GDALPipelineAlgorithm()
512
0
    : GDALAbstractPipelineAlgorithm(
513
0
          NAME, DESCRIPTION, HELP_URL,
514
0
          ConstructorOptions().SetStandaloneStep(false))
515
0
{
516
0
    m_supportsStreamedOutput = true;
517
518
0
    AddProgressArg();
519
0
    AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER | GDAL_OF_VECTOR,
520
0
                       /* positionalAndRequired = */ false)
521
0
        .SetMinCount(1)
522
0
        .SetMaxCount(INT_MAX)
523
0
        .SetHiddenForCLI();
524
0
    AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER | GDAL_OF_VECTOR,
525
0
                        /* positionalAndRequired = */ false)
526
0
        .SetHiddenForCLI()
527
0
        .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
528
0
    AddOutputFormatArg(&m_format, /* bStreamAllowed = */ true,
529
0
                       /* bGDALGAllowed = */ true)
530
0
        .SetHiddenForCLI();
531
0
    AddArg("pipeline", 0, _("Pipeline string or filename"), &m_pipeline)
532
0
        .SetHiddenForCLI()
533
0
        .SetPositional();
534
535
0
    AddOutputStringArg(&m_output).SetHiddenForCLI();
536
0
    AddStdoutArg(&m_stdout);
537
538
0
    AllowArbitraryLongNameArgs();
539
540
0
    GDALRasterPipelineAlgorithm::RegisterAlgorithms(m_stepRegistry, true);
541
0
    GDALVectorPipelineAlgorithm::RegisterAlgorithms(m_stepRegistry, true);
542
0
    m_stepRegistry.Register<GDALExternalRasterOrVectorAlgorithm>();
543
0
    m_stepRegistry.Register<GDALRasterAsFeaturesAlgorithm>();
544
0
    m_stepRegistry.Register<GDALRasterContourAlgorithm>();
545
0
    m_stepRegistry.Register<GDALRasterFootprintAlgorithm>();
546
0
    m_stepRegistry.Register<GDALRasterPixelInfoAlgorithm>();
547
0
    m_stepRegistry.Register<GDALRasterPolygonizeAlgorithm>();
548
0
    m_stepRegistry.Register<GDALRasterZonalStatsAlgorithm>();
549
0
    m_stepRegistry.Register<GDALVectorGridAlgorithm>();
550
0
    m_stepRegistry.Register<GDALVectorRasterizeAlgorithm>();
551
0
}
552
553
/************************************************************************/
554
/*               GDALPipelineAlgorithm::GetUsageForCLI()                */
555
/************************************************************************/
556
557
std::string
558
GDALPipelineAlgorithm::GetUsageForCLI(bool shortUsage,
559
                                      const UsageOptions &usageOptions) const
560
0
{
561
0
    UsageOptions stepUsageOptions;
562
0
    stepUsageOptions.isPipelineStep = true;
563
564
0
    if (!m_helpDocCategory.empty() && m_helpDocCategory != "main")
565
0
    {
566
0
        auto alg = GetStepAlg(m_helpDocCategory);
567
0
        if (alg)
568
0
        {
569
0
            alg->SetCallPath({CPLString(m_helpDocCategory)
570
0
                                  .replaceAll(RASTER_SUFFIX, "")
571
0
                                  .replaceAll(VECTOR_SUFFIX, "")});
572
0
            alg->GetArg("help-doc")->Set(true);
573
0
            return alg->GetUsageForCLI(shortUsage, stepUsageOptions);
574
0
        }
575
0
        else
576
0
        {
577
0
            fprintf(stderr, "ERROR: unknown pipeline step '%s'\n",
578
0
                    m_helpDocCategory.c_str());
579
0
            return CPLSPrintf("ERROR: unknown pipeline step '%s'\n",
580
0
                              m_helpDocCategory.c_str());
581
0
        }
582
0
    }
583
584
0
    UsageOptions usageOptionsMain(usageOptions);
585
0
    usageOptionsMain.isPipelineMain = true;
586
0
    std::string ret =
587
0
        GDALAlgorithm::GetUsageForCLI(shortUsage, usageOptionsMain);
588
0
    if (shortUsage)
589
0
        return ret;
590
591
0
    ret +=
592
0
        "\n<PIPELINE> is of the form: read|calc|concat|create|mosaic|stack "
593
0
        "[READ-OPTIONS] "
594
0
        "( ! <STEP-NAME> [STEP-OPTIONS] )* ! write!info!tile [WRITE-OPTIONS]\n";
595
596
0
    if (m_helpDocCategory == "main")
597
0
    {
598
0
        return ret;
599
0
    }
600
601
0
    ret += '\n';
602
0
    ret += "Example: 'gdal pipeline --progress ! read in.tif ! \\\n";
603
0
    ret += "               rasterize --size 256 256 ! buffer 20 ! ";
604
0
    ret += "write out.gpkg --overwrite'\n";
605
0
    ret += '\n';
606
0
    ret += "Potential steps are:\n";
607
608
0
    for (const std::string &name : m_stepRegistry.GetNames())
609
0
    {
610
0
        auto alg = GetStepAlg(name);
611
0
        assert(alg);
612
0
        auto [options, maxOptLen] = alg->GetArgNamesForCLI();
613
0
        stepUsageOptions.maxOptLen =
614
0
            std::max(stepUsageOptions.maxOptLen, maxOptLen);
615
0
    }
616
617
0
    {
618
0
        ret += '\n';
619
0
        auto alg = std::make_unique<GDALRasterReadAlgorithm>();
620
0
        alg->SetCallPath({alg->GetName()});
621
0
        ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
622
0
    }
623
0
    {
624
0
        ret += '\n';
625
0
        auto alg = std::make_unique<GDALVectorReadAlgorithm>();
626
0
        alg->SetCallPath({alg->GetName()});
627
0
        ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
628
0
    }
629
0
    for (const std::string &name : m_stepRegistry.GetNames())
630
0
    {
631
0
        auto alg = GetStepAlg(name);
632
0
        assert(alg);
633
0
        if (alg->CanBeFirstStep() && !alg->CanBeMiddleStep() &&
634
0
            !alg->IsHidden() &&
635
0
            !STARTS_WITH(name.c_str(), GDALRasterReadAlgorithm::NAME))
636
0
        {
637
0
            ret += '\n';
638
0
            alg->SetCallPath({name});
639
0
            ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
640
0
        }
641
0
    }
642
0
    for (const std::string &name : m_stepRegistry.GetNames())
643
0
    {
644
0
        auto alg = GetStepAlg(name);
645
0
        assert(alg);
646
0
        if (alg->CanBeMiddleStep() && !alg->IsHidden())
647
0
        {
648
0
            ret += '\n';
649
0
            alg->SetCallPath({CPLString(alg->GetName())
650
0
                                  .replaceAll(RASTER_SUFFIX, "")
651
0
                                  .replaceAll(VECTOR_SUFFIX, "")});
652
0
            ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
653
0
        }
654
0
    }
655
0
    for (const std::string &name : m_stepRegistry.GetNames())
656
0
    {
657
0
        auto alg = GetStepAlg(name);
658
0
        assert(alg);
659
0
        if (alg->CanBeLastStep() && !alg->CanBeMiddleStep() &&
660
0
            !alg->IsHidden() &&
661
0
            !STARTS_WITH(name.c_str(), GDALRasterWriteAlgorithm::NAME))
662
0
        {
663
0
            ret += '\n';
664
0
            alg->SetCallPath({name});
665
0
            ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
666
0
        }
667
0
    }
668
0
    {
669
0
        ret += '\n';
670
0
        auto alg = std::make_unique<GDALRasterWriteAlgorithm>();
671
0
        alg->SetCallPath({alg->GetName()});
672
0
        ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
673
0
    }
674
0
    {
675
0
        ret += '\n';
676
0
        auto alg = std::make_unique<GDALVectorWriteAlgorithm>();
677
0
        alg->SetCallPath({alg->GetName()});
678
0
        ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
679
0
    }
680
681
0
    ret += GetUsageForCLIEnd();
682
683
0
    return ret;
684
0
}
685
686
//! @endcond