Coverage Report

Created: 2026-09-14 06:50

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_mdim_info.h"
31
#include "gdalalg_mdim_read.h"
32
#include "gdalalg_mdim_write.h"
33
34
#include "gdalalg_raster_as_features.h"
35
#include "gdalalg_raster_compare.h"
36
#include "gdalalg_raster_contour.h"
37
#include "gdalalg_raster_footprint.h"
38
#include "gdalalg_raster_polygonize.h"
39
#include "gdalalg_raster_info.h"
40
#include "gdalalg_raster_pixel_info.h"
41
#include "gdalalg_raster_tile.h"
42
#include "gdalalg_vector_grid.h"
43
#include "gdalalg_vector_info.h"
44
#include "gdalalg_vector_rasterize.h"
45
46
#include <algorithm>
47
#include <cassert>
48
49
#ifndef _
50
#define _(x) (x)
51
#endif
52
53
/************************************************************************/
54
/*                     GDALPipelineStepAlgorithm()                      */
55
/************************************************************************/
56
57
GDALPipelineStepAlgorithm::GDALPipelineStepAlgorithm(
58
    const std::string &name, const std::string &description,
59
    const std::string &helpURL, const ConstructorOptions &options)
60
0
    : GDALAlgorithm(name, description, helpURL),
61
0
      m_standaloneStep(options.standaloneStep), m_constructorOptions(options)
62
0
{
63
0
}
64
65
/************************************************************************/
66
/*     GDALPipelineStepAlgorithm::AddRasterHiddenInputDatasetArg()      */
67
/************************************************************************/
68
69
void GDALPipelineStepAlgorithm::AddRasterHiddenInputDatasetArg()
70
0
{
71
0
    AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER, false)
72
0
        .SetMinCount(0)
73
0
        .SetMaxCount(m_constructorOptions.inputDatasetMaxCount)
74
0
        .SetAutoOpenDataset(m_constructorOptions.autoOpenInputDatasets)
75
0
        .SetMetaVar(m_constructorOptions.inputDatasetMetaVar)
76
0
        .SetHidden();
77
0
}
78
79
/************************************************************************/
80
/*           GDALPipelineStepAlgorithm::AddRasterInputArgs()            */
81
/************************************************************************/
82
83
void GDALPipelineStepAlgorithm::AddRasterInputArgs(
84
    bool openForMixedRasterVector, bool hiddenForCLI)
85
0
{
86
0
    AddInputFormatsArg(&m_inputFormats)
87
0
        .AddMetadataItem(
88
0
            GAAMDI_REQUIRED_CAPABILITIES,
89
0
            openForMixedRasterVector
90
0
                ? std::vector<std::string>{GDAL_DCAP_RASTER, GDAL_DCAP_VECTOR}
91
0
                : std::vector<std::string>{GDAL_DCAP_RASTER})
92
0
        .SetHiddenForCLI(hiddenForCLI)
93
0
        .SetAvailableInPipelineStep(false);
94
0
    AddOpenOptionsArg(&m_openOptions)
95
0
        .SetHiddenForCLI(hiddenForCLI)
96
0
        .SetAvailableInPipelineStep(false);
97
98
0
    const int nDatasetType = openForMixedRasterVector
99
0
                                 ? (GDAL_OF_RASTER | GDAL_OF_VECTOR)
100
0
                                 : GDAL_OF_RASTER;
101
0
    auto &arg =
102
0
        AddInputDatasetArg(
103
0
            &m_inputDataset, nDatasetType, false,
104
0
            m_constructorOptions.inputDatasetHelpMsg.empty() &&
105
0
                    m_constructorOptions.inputDatasetMaxCount == 1
106
0
                ? CPLSPrintf(
107
0
                      "Input %s dataset",
108
0
                      GDALAlgorithmArgDatasetTypeName(nDatasetType).c_str())
109
0
                : m_constructorOptions.inputDatasetHelpMsg.c_str())
110
0
            .SetDatasetInputFlags(m_constructorOptions.inputDatasetInputFlags)
111
0
            .SetMinCount(m_constructorOptions.inputDatasetRequired ? 1 : 0)
112
0
            .SetMaxCount(m_constructorOptions.inputDatasetMaxCount)
113
0
            .SetAutoOpenDataset(m_constructorOptions.autoOpenInputDatasets)
114
0
            .SetMetaVar(m_constructorOptions.inputDatasetMetaVar)
115
0
            .SetHiddenForCLI(hiddenForCLI)
116
0
            .SetAvailableInPipelineStep(false);
117
0
    if (m_constructorOptions.inputDatasetPositional && !hiddenForCLI)
118
0
        arg.SetPositional();
119
0
    if (m_constructorOptions.inputDatasetRequired && !hiddenForCLI)
120
0
        arg.SetRequired();
121
0
    if (!m_constructorOptions.inputDatasetAlias.empty())
122
0
        arg.AddAlias(m_constructorOptions.inputDatasetAlias);
123
0
}
124
125
/************************************************************************/
126
/*           GDALPipelineStepAlgorithm::AddRasterOutputArgs()           */
127
/************************************************************************/
128
129
void GDALPipelineStepAlgorithm::AddRasterOutputArgs(bool hiddenForCLI)
130
0
{
131
0
    m_outputFormatArg =
132
0
        &(AddOutputFormatArg(&m_format, /* bStreamAllowed = */ true,
133
0
                             /* bGDALGAllowed = */ true)
134
0
              .AddMetadataItem(
135
0
                  GAAMDI_REQUIRED_CAPABILITIES,
136
0
                  {GDAL_DCAP_RASTER,
137
0
                   m_constructorOptions.outputFormatCreateCapability.c_str()})
138
0
              .SetHiddenForCLI(hiddenForCLI))
139
0
             .SetAvailableInPipelineStep(false);
140
0
    AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER,
141
0
                        /* positionalAndRequired = */ !hiddenForCLI,
142
0
                        m_constructorOptions.outputDatasetHelpMsg.c_str())
143
0
        .SetHiddenForCLI(hiddenForCLI)
144
0
        .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT)
145
0
        .SetAvailableInPipelineStep(false);
146
0
    AddCreationOptionsArg(&m_creationOptions)
147
0
        .SetHiddenForCLI(hiddenForCLI)
148
0
        .SetAvailableInPipelineStep(false);
149
0
    constexpr const char *MUTUAL_EXCLUSION_GROUP_OVERWRITE_APPEND =
150
0
        "overwrite-append";
151
0
    AddOverwriteArg(&m_overwrite)
152
0
        .SetHiddenForCLI(hiddenForCLI)
153
0
        .SetMutualExclusionGroup(MUTUAL_EXCLUSION_GROUP_OVERWRITE_APPEND)
154
0
        .SetAvailableInPipelineStep(false);
155
0
    AddArg(GDAL_ARG_NAME_APPEND, 0,
156
0
           _("Append as a subdataset to existing output"), &m_appendRaster)
157
0
        .SetDefault(false)
158
0
        .SetHiddenForCLI(hiddenForCLI)
159
0
        .SetMutualExclusionGroup(MUTUAL_EXCLUSION_GROUP_OVERWRITE_APPEND)
160
0
        .SetAvailableInPipelineStep(false);
161
0
}
162
163
/************************************************************************/
164
/*     GDALPipelineStepAlgorithm::AddVectorHiddenInputDatasetArg()      */
165
/************************************************************************/
166
167
void GDALPipelineStepAlgorithm::AddVectorHiddenInputDatasetArg()
168
0
{
169
0
    AddInputDatasetArg(&m_inputDataset, GDAL_OF_VECTOR, false)
170
0
        .SetMinCount(0)
171
0
        .SetMaxCount(m_constructorOptions.inputDatasetMaxCount)
172
0
        .SetAutoOpenDataset(m_constructorOptions.autoOpenInputDatasets)
173
0
        .SetMetaVar(m_constructorOptions.inputDatasetMetaVar)
174
0
        .SetHidden();
175
0
}
176
177
/************************************************************************/
178
/*           GDALPipelineStepAlgorithm::AddVectorInputArgs()            */
179
/************************************************************************/
180
181
void GDALPipelineStepAlgorithm::AddVectorInputArgs(bool hiddenForCLI)
182
0
{
183
0
    AddInputFormatsArg(&m_inputFormats)
184
0
        .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR})
185
0
        .SetHiddenForCLI(hiddenForCLI)
186
0
        .SetAvailableInPipelineStep(false);
187
0
    AddOpenOptionsArg(&m_openOptions)
188
0
        .SetHiddenForCLI(hiddenForCLI)
189
0
        .SetAvailableInPipelineStep(false);
190
0
    auto &datasetArg =
191
0
        AddInputDatasetArg(
192
0
            &m_inputDataset, GDAL_OF_VECTOR, false,
193
0
            m_constructorOptions.inputDatasetHelpMsg.empty() &&
194
0
                    m_constructorOptions.inputDatasetMaxCount == 1
195
0
                ? "Input vector dataset"
196
0
                : m_constructorOptions.inputDatasetHelpMsg.c_str())
197
0
            .SetMinCount(m_constructorOptions.inputDatasetRequired ? 1 : 0)
198
0
            .SetMaxCount(m_constructorOptions.inputDatasetMaxCount)
199
0
            .SetDatasetInputFlags(m_constructorOptions.inputDatasetInputFlags)
200
0
            .SetAutoOpenDataset(m_constructorOptions.autoOpenInputDatasets)
201
0
            .SetHiddenForCLI(hiddenForCLI)
202
0
            .SetAvailableInPipelineStep(false);
203
0
    if (!m_constructorOptions.inputDatasetAlias.empty())
204
0
        datasetArg.AddAlias(m_constructorOptions.inputDatasetAlias);
205
0
    if (!m_constructorOptions.inputDatasetMetaVar.empty())
206
0
        datasetArg.SetMetaVar(m_constructorOptions.inputDatasetMetaVar);
207
0
    if (m_constructorOptions.inputDatasetPositional && !hiddenForCLI)
208
0
        datasetArg.SetPositional();
209
0
    if (m_constructorOptions.inputDatasetRequired && !hiddenForCLI)
210
0
        datasetArg.SetRequired();
211
0
    if (m_constructorOptions.addInputLayerNameArgument)
212
0
    {
213
0
        auto &layerArg = AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
214
0
                                _("Input layer name(s)"), &m_inputLayerNames)
215
0
                             .AddAlias("layer")
216
0
                             .SetHiddenForCLI(hiddenForCLI)
217
0
                             .SetAvailableInPipelineStep(false);
218
0
        SetAutoCompleteFunctionForLayerName(layerArg, datasetArg);
219
0
    }
220
0
}
221
222
/************************************************************************/
223
/*           GDALPipelineStepAlgorithm::AddVectorOutputArgs()           */
224
/************************************************************************/
225
226
void GDALPipelineStepAlgorithm::AddVectorOutputArgs(
227
    bool hiddenForCLI, bool shortNameOutputLayerAllowed)
228
0
{
229
0
    AddOutputFormatArg(&m_format, /* bStreamAllowed = */ true,
230
0
                       /* bGDALGAllowed = */ true)
231
0
        .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
232
0
                         {GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE})
233
0
        .SetHiddenForCLI(hiddenForCLI)
234
0
        .SetAvailableInPipelineStep(false);
235
0
    AddOutputOpenOptionsArg(&m_outputOpenOptions)
236
0
        .SetHiddenForCLI(hiddenForCLI)
237
0
        .SetAvailableInPipelineStep(false);
238
0
    auto &outputDatasetArg =
239
0
        AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR,
240
0
                            /* positionalAndRequired = */ false)
241
0
            .SetHiddenForCLI(hiddenForCLI)
242
0
            .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT)
243
0
            .SetAvailableInPipelineStep(false);
244
0
    if (!hiddenForCLI)
245
0
        outputDatasetArg.SetPositional();
246
0
    if (!hiddenForCLI && m_constructorOptions.outputDatasetRequired)
247
0
        outputDatasetArg.SetRequired();
248
249
0
    AddCreationOptionsArg(&m_creationOptions)
250
0
        .SetHiddenForCLI(hiddenForCLI)
251
0
        .SetAvailableInPipelineStep(false);
252
0
    AddLayerCreationOptionsArg(&m_layerCreationOptions)
253
0
        .SetHiddenForCLI(hiddenForCLI)
254
0
        .SetAvailableInPipelineStep(false);
255
0
    AddOverwriteArg(&m_overwrite)
256
0
        .SetHiddenForCLI(hiddenForCLI)
257
0
        .SetAvailableInPipelineStep(false);
258
0
    GDALInConstructionAlgorithmArg *updateArg = nullptr;
259
0
    if (m_constructorOptions.addUpdateArgument)
260
0
    {
261
0
        updateArg = &AddUpdateArg(&m_update)
262
0
                         .SetHiddenForCLI(hiddenForCLI)
263
0
                         .SetAvailableInPipelineStep(false);
264
0
    }
265
0
    if (m_constructorOptions.addOverwriteLayerArgument)
266
0
    {
267
0
        AddOverwriteLayerArg(&m_overwriteLayer)
268
0
            .SetHiddenForCLI(hiddenForCLI)
269
0
            .SetAvailableInPipelineStep(false);
270
0
    }
271
0
    constexpr const char *MUTUAL_EXCLUSION_GROUP_APPEND_UPSERT =
272
0
        "append-upsert";
273
0
    if (m_constructorOptions.addAppendLayerArgument)
274
0
    {
275
0
        AddAppendLayerArg(&m_appendLayer)
276
0
            .SetHiddenForCLI(hiddenForCLI)
277
0
            .SetMutualExclusionGroup(MUTUAL_EXCLUSION_GROUP_APPEND_UPSERT)
278
0
            .SetAvailableInPipelineStep(false);
279
0
    }
280
0
    if (m_constructorOptions.addUpsertArgument)
281
0
    {
282
0
        AddArg("upsert", 0, _("Upsert features (implies 'append')"), &m_upsert)
283
0
            .SetHiddenForCLI(hiddenForCLI)
284
0
            .SetMutualExclusionGroup(MUTUAL_EXCLUSION_GROUP_APPEND_UPSERT)
285
286
0
            .SetAvailableInPipelineStep(false)
287
0
            .AddAction(
288
0
                [updateArg, this]()
289
0
                {
290
0
                    if (m_upsert && updateArg)
291
0
                        updateArg->Set(true);
292
0
                })
293
0
            .SetCategory(GAAC_ADVANCED);
294
0
    }
295
0
    if (m_constructorOptions.addOutputLayerNameArgument)
296
0
    {
297
0
        AddOutputLayerNameArg(hiddenForCLI, shortNameOutputLayerAllowed);
298
0
    }
299
0
    if (m_constructorOptions.addSkipErrorsArgument)
300
0
    {
301
0
        AddArg("skip-errors", 0, _("Skip errors when writing features"),
302
0
               &m_skipErrors)
303
0
            .AddHiddenAlias("skip-failures")  // For ogr2ogr nostalgic people
304
0
            .SetAvailableInPipelineStep(false);
305
0
    }
306
0
    if (m_constructorOptions.addNoCreateEmptyLayersArgument)
307
0
    {
308
0
        AddArg("no-create-empty-layers", 0,
309
0
               _("Avoid creating layers to which no features will be written"),
310
0
               &m_noCreateEmptyLayers)
311
0
            .SetAvailableInPipelineStep(false);
312
0
    }
313
0
}
314
315
/************************************************************************/
316
/*          GDALPipelineStepAlgorithm::AddOutputLayerNameArg()          */
317
/************************************************************************/
318
319
void GDALPipelineStepAlgorithm::AddOutputLayerNameArg(
320
    bool hiddenForCLI, bool shortNameOutputLayerAllowed)
321
0
{
322
0
    AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, shortNameOutputLayerAllowed ? 'l' : 0,
323
0
           _("Output layer name"),
324
0
           &m_outputLayerName)
325
0
        .AddHiddenAlias("nln")  // For ogr2ogr nostalgic people
326
0
        .SetHiddenForCLI(hiddenForCLI)
327
0
        .SetAvailableInPipelineStep(
328
0
            m_constructorOptions.outputLayerNameAvailableInPipelineStep);
329
0
}
330
331
/************************************************************************/
332
/*      GDALPipelineStepAlgorithm::AddMdimHiddenInputDatasetArg()       */
333
/************************************************************************/
334
335
void GDALPipelineStepAlgorithm::AddMdimHiddenInputDatasetArg()
336
0
{
337
0
    AddInputDatasetArg(&m_inputDataset, GDAL_OF_MULTIDIM_RASTER, false)
338
0
        .SetMinCount(0)
339
0
        .SetMaxCount(m_constructorOptions.inputDatasetMaxCount)
340
0
        .SetAutoOpenDataset(m_constructorOptions.autoOpenInputDatasets)
341
0
        .SetMetaVar(m_constructorOptions.inputDatasetMetaVar)
342
0
        .SetHidden();
343
0
}
344
345
/************************************************************************/
346
/*            GDALPipelineStepAlgorithm::AddMdimInputArgs()             */
347
/************************************************************************/
348
349
void GDALPipelineStepAlgorithm::AddMdimInputArgs(bool openForMixedMdimVector,
350
                                                 bool hiddenForCLI,
351
                                                 bool acceptRaster)
352
0
{
353
0
    AddInputFormatsArg(&m_inputFormats)
354
0
        .AddMetadataItem(
355
0
            GAAMDI_REQUIRED_CAPABILITIES,
356
0
            openForMixedMdimVector
357
0
                ? std::vector<
358
0
                      std::string>{acceptRaster
359
0
                                       ? GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER
360
0
                                       : GDAL_DCAP_MULTIDIM_RASTER,
361
0
                                   GDAL_DCAP_VECTOR}
362
0
                : std::vector<
363
0
                      std::string>{acceptRaster
364
0
                                       ? GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER
365
0
                                       : GDAL_DCAP_MULTIDIM_RASTER})
366
0
        .SetHiddenForCLI(hiddenForCLI)
367
0
        .SetAvailableInPipelineStep(false);
368
0
    AddOpenOptionsArg(&m_openOptions)
369
0
        .SetHiddenForCLI(hiddenForCLI)
370
0
        .SetAvailableInPipelineStep(false);
371
0
    auto &arg =
372
0
        AddInputDatasetArg(&m_inputDataset,
373
0
                           (acceptRaster ? GDAL_OF_RASTER : 0) |
374
0
                               (openForMixedMdimVector
375
0
                                    ? (GDAL_OF_MULTIDIM_RASTER | GDAL_OF_VECTOR)
376
0
                                    : GDAL_OF_MULTIDIM_RASTER),
377
0
                           false,
378
0
                           m_constructorOptions.inputDatasetHelpMsg.c_str())
379
0
            .SetDatasetInputFlags(m_constructorOptions.inputDatasetInputFlags)
380
0
            .SetMinCount(m_constructorOptions.inputDatasetRequired ? 1 : 0)
381
0
            .SetMaxCount(m_constructorOptions.inputDatasetMaxCount)
382
0
            .SetAutoOpenDataset(m_constructorOptions.autoOpenInputDatasets)
383
0
            .SetMetaVar(m_constructorOptions.inputDatasetMetaVar)
384
0
            .SetHiddenForCLI(hiddenForCLI)
385
0
            .SetAvailableInPipelineStep(false);
386
0
    if (m_constructorOptions.inputDatasetPositional && !hiddenForCLI)
387
0
        arg.SetPositional();
388
0
    if (m_constructorOptions.inputDatasetRequired && !hiddenForCLI)
389
0
        arg.SetRequired();
390
0
    if (!m_constructorOptions.inputDatasetAlias.empty())
391
0
        arg.AddAlias(m_constructorOptions.inputDatasetAlias);
392
0
}
393
394
/************************************************************************/
395
/*            GDALPipelineStepAlgorithm::AddMdimOutputArgs()            */
396
/************************************************************************/
397
398
void GDALPipelineStepAlgorithm::AddMdimOutputArgs(bool hiddenForCLI)
399
0
{
400
    // Same requirements as the ones checked by GDALMultiDimTranslate(), which
401
    // writes to a classic raster driver when the driver has no
402
    // multidimensional creation capability.
403
0
    const std::vector<std::string> requiredCapabilities =
404
0
        m_constructorOptions.mdimOutputAcceptsClassicRaster
405
0
            ? std::vector<
406
0
                  std::string>{GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER,
407
0
                               GDAL_DCAP_CREATE
408
0
                               "|" GDAL_DCAP_CREATECOPY
409
0
                               "|" GDAL_DCAP_CREATE_MULTIDIMENSIONAL
410
0
                               "|" GDAL_DCAP_CREATECOPY_MULTIDIMENSIONAL}
411
0
            : std::vector<std::string>{GDAL_DCAP_CREATE_MULTIDIMENSIONAL};
412
413
0
    m_outputFormatArg =
414
0
        &(AddOutputFormatArg(&m_format, /* bStreamAllowed = */ true,
415
0
                             /* bGDALGAllowed = */ true)
416
0
              .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
417
0
                               requiredCapabilities)
418
0
              .SetHiddenForCLI(hiddenForCLI))
419
0
             .SetAvailableInPipelineStep(false);
420
0
    AddOutputDatasetArg(&m_outputDataset, GDAL_OF_MULTIDIM_RASTER,
421
0
                        /* positionalAndRequired = */ !hiddenForCLI,
422
0
                        m_constructorOptions.outputDatasetHelpMsg.c_str())
423
0
        .SetHiddenForCLI(hiddenForCLI)
424
0
        .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT)
425
0
        .SetAvailableInPipelineStep(false);
426
0
    AddCreationOptionsArg(&m_creationOptions)
427
0
        .SetHiddenForCLI(hiddenForCLI)
428
0
        .SetAvailableInPipelineStep(false);
429
0
    AddOverwriteArg(&m_overwrite)
430
0
        .SetHiddenForCLI(hiddenForCLI)
431
0
        .SetAvailableInPipelineStep(false);
432
0
}
433
434
/************************************************************************/
435
/*                 GDALPipelineStepAlgorithm::RunImpl()                 */
436
/************************************************************************/
437
438
bool GDALPipelineStepAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
439
                                        void *pProgressData)
440
0
{
441
0
    if (m_standaloneStep)
442
0
    {
443
0
        std::unique_ptr<GDALPipelineStepAlgorithm> readAlg;
444
0
        if (GetInputType() == GDAL_OF_RASTER)
445
0
            readAlg = std::make_unique<GDALRasterReadAlgorithm>();
446
0
        else if (GetInputType() == GDAL_OF_MULTIDIM_RASTER)
447
0
            readAlg = std::make_unique<GDALMdimReadAlgorithm>();
448
0
        else
449
0
            readAlg = std::make_unique<GDALVectorReadAlgorithm>();
450
0
        for (auto &arg : readAlg->GetArgs())
451
0
        {
452
0
            auto stepArg = GetArg(arg->GetName());
453
0
            if (stepArg && stepArg->IsExplicitlySet() &&
454
0
                stepArg->GetType() == arg->GetType())
455
0
            {
456
0
                arg->SetSkipIfAlreadySet(true);
457
0
                arg->SetFrom(*stepArg);
458
0
            }
459
0
        }
460
461
0
        std::unique_ptr<GDALPipelineStepAlgorithm> writeAlg;
462
0
        if (GetOutputType() == GDAL_OF_RASTER)
463
0
        {
464
0
            if (GetName() == GDALRasterInfoAlgorithm::NAME)
465
0
                writeAlg = std::make_unique<GDALRasterInfoAlgorithm>();
466
0
            else if (GetName() == GDALRasterCompareAlgorithm::NAME)
467
0
                writeAlg = std::make_unique<GDALRasterCompareAlgorithm>();
468
0
            else
469
0
                writeAlg = std::make_unique<GDALRasterWriteAlgorithm>();
470
0
        }
471
0
        else if (GetOutputType() == GDAL_OF_MULTIDIM_RASTER)
472
0
        {
473
0
            if (GetName() == GDALMdimInfoAlgorithm::NAME)
474
0
                writeAlg = std::make_unique<GDALMdimInfoAlgorithm>();
475
0
            else
476
0
                writeAlg = std::make_unique<GDALMdimWriteAlgorithm>();
477
0
        }
478
0
        else
479
0
        {
480
0
            if (GetName() == GDALVectorInfoAlgorithm::NAME)
481
0
                writeAlg = std::make_unique<GDALVectorInfoAlgorithm>();
482
0
            else
483
0
                writeAlg = std::make_unique<GDALVectorWriteAlgorithm>();
484
0
        }
485
0
        for (auto &arg : writeAlg->GetArgs())
486
0
        {
487
0
            auto stepArg = GetArg(arg->GetName());
488
0
            if (stepArg && stepArg->IsExplicitlySet() &&
489
0
                stepArg->GetType() == arg->GetType())
490
0
            {
491
0
                arg->SetSkipIfAlreadySet(true);
492
0
                arg->SetFrom(*stepArg);
493
0
            }
494
0
        }
495
496
0
        const bool bIsStreaming = m_format == "stream";
497
498
        // Already checked by GDALAlgorithm::Run()
499
0
        CPLAssert(!m_executionForStreamOutput || bIsStreaming);
500
501
0
        bool ret = false;
502
0
        if (!m_outputVRTCompatible &&
503
0
            (EQUAL(m_format.c_str(), "VRT") ||
504
0
             (m_format.empty() &&
505
0
              EQUAL(CPLGetExtensionSafe(m_outputDataset.GetName().c_str())
506
0
                        .c_str(),
507
0
                    "VRT"))))
508
0
        {
509
0
            ReportError(CE_Failure, CPLE_NotSupported,
510
0
                        "VRT output is not supported. Consider using the "
511
0
                        "GDALG driver instead (files with .gdalg.json "
512
0
                        "extension)");
513
0
        }
514
0
        else if (readAlg->Run())
515
0
        {
516
0
            auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
517
0
            const bool bOutputSpecified =
518
0
                outputArg && outputArg->IsExplicitlySet() &&
519
0
                (outputArg->GetType() == GAAT_DATASET ||
520
0
                 outputArg->GetType() == GAAT_DATASET_LIST);
521
522
0
            m_inputDataset.clear();
523
0
            m_inputDataset.resize(1);
524
0
            m_inputDataset[0].Set(readAlg->m_outputDataset.GetDatasetRef());
525
0
            if (bOutputSpecified)
526
0
                m_outputDataset.Set(nullptr);
527
528
0
            std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
529
0
                pScaledData(nullptr, GDALDestroyScaledProgress);
530
531
0
            const bool bCanHandleNextStep =
532
0
                !bIsStreaming && CanHandleNextStep(writeAlg.get());
533
534
0
            GDALPipelineStepRunContext stepCtxt;
535
0
            if (pfnProgress && GetName() == GDALRasterCompareAlgorithm::NAME)
536
0
            {
537
0
                stepCtxt.m_pfnProgress = pfnProgress;
538
0
                stepCtxt.m_pProgressData = pProgressData;
539
0
            }
540
0
            else if (pfnProgress &&
541
0
                     (bCanHandleNextStep || !IsNativelyStreamingCompatible()))
542
0
            {
543
0
                pScaledData.reset(GDALCreateScaledProgress(
544
0
                    0.0, bIsStreaming || bCanHandleNextStep ? 1.0 : 0.5,
545
0
                    pfnProgress, pProgressData));
546
0
                stepCtxt.m_pfnProgress =
547
0
                    pScaledData ? GDALScaledProgress : nullptr;
548
0
                stepCtxt.m_pProgressData = pScaledData.get();
549
0
            }
550
551
0
            if (bCanHandleNextStep)
552
0
                stepCtxt.m_poNextUsableStep = writeAlg.get();
553
0
            if (RunPreStepPipelineValidations() && RunStep(stepCtxt))
554
0
            {
555
0
                if (bCanHandleNextStep || !bOutputSpecified)
556
0
                {
557
0
                    ret = true;
558
0
                }
559
0
                else
560
0
                {
561
0
                    writeAlg->m_outputVRTCompatible = m_outputVRTCompatible;
562
0
                    writeAlg->m_quiet = m_quiet;
563
564
0
                    std::vector<GDALArgDatasetValue> inputDataset(1);
565
0
                    inputDataset[0].Set(m_outputDataset.GetDatasetRef());
566
0
                    auto inputArg = writeAlg->GetArg(GDAL_ARG_NAME_INPUT);
567
0
                    CPLAssert(inputArg);
568
0
                    inputArg->Set(std::move(inputDataset));
569
570
0
                    if (pfnProgress)
571
0
                    {
572
0
                        pScaledData.reset(GDALCreateScaledProgress(
573
0
                            IsNativelyStreamingCompatible() ? 0.0 : 0.5, 1.0,
574
0
                            pfnProgress, pProgressData));
575
0
                    }
576
0
                    stepCtxt.m_pfnProgress =
577
0
                        pScaledData ? GDALScaledProgress : nullptr;
578
0
                    stepCtxt.m_pProgressData = pScaledData.get();
579
0
                    if (writeAlg->ValidateArguments() &&
580
0
                        writeAlg->RunStep(stepCtxt))
581
0
                    {
582
0
                        if (pfnProgress)
583
0
                            pfnProgress(1.0, "", pProgressData);
584
585
0
                        m_outputDataset.Set(
586
0
                            writeAlg->m_outputDataset.GetDatasetRef());
587
0
                        ret = true;
588
0
                    }
589
0
                }
590
0
            }
591
0
        }
592
593
0
        return ret;
594
0
    }
595
0
    else
596
0
    {
597
0
        GDALPipelineStepRunContext stepCtxt;
598
0
        stepCtxt.m_pfnProgress = pfnProgress;
599
0
        stepCtxt.m_pProgressData = pProgressData;
600
0
        return RunPreStepPipelineValidations() && RunStep(stepCtxt);
601
0
    }
602
0
}
603
604
/************************************************************************/
605
/*                          SetInputDataset()                           */
606
/************************************************************************/
607
608
void GDALPipelineStepAlgorithm::SetInputDataset(GDALDataset *poDS)
609
0
{
610
0
    auto arg = GetArg(GDAL_ARG_NAME_INPUT);
611
0
    if (arg)
612
0
    {
613
0
        auto &val = arg->Get<std::vector<GDALArgDatasetValue>>();
614
0
        val.resize(1);
615
0
        val[0].Set(poDS);
616
0
        arg->NotifyValueSet();
617
0
        arg->SetSkipIfAlreadySet();
618
0
    }
619
0
}
620
621
/************************************************************************/
622
/*                         ProcessGDALGOutput()                         */
623
/************************************************************************/
624
625
GDALAlgorithm::ProcessGDALGOutputRet
626
GDALPipelineStepAlgorithm::ProcessGDALGOutput()
627
0
{
628
0
    if (m_standaloneStep)
629
0
    {
630
0
        return GDALAlgorithm::ProcessGDALGOutput();
631
0
    }
632
0
    else
633
0
    {
634
        // GDALAbstractPipelineAlgorithm<StepAlgorithm>::RunStep() might
635
        // actually detect a GDALG output request and process it.
636
0
        return GDALAlgorithm::ProcessGDALGOutputRet::NOT_GDALG;
637
0
    }
638
0
}
639
640
/************************************************************************/
641
/*        GDALPipelineStepAlgorithm::CheckSafeForStreamOutput()         */
642
/************************************************************************/
643
644
bool GDALPipelineStepAlgorithm::CheckSafeForStreamOutput()
645
0
{
646
0
    if (m_standaloneStep)
647
0
    {
648
0
        return GDALAlgorithm::CheckSafeForStreamOutput();
649
0
    }
650
0
    else
651
0
    {
652
        // The check is actually done in
653
        // GDALAbstractPipelineAlgorithm<StepAlgorithm>::RunStep()
654
        // so return true for now.
655
0
        return true;
656
0
    }
657
0
}
658
659
/************************************************************************/
660
/*                GDALPipelineStepAlgorithm::Finalize()                 */
661
/************************************************************************/
662
663
bool GDALPipelineStepAlgorithm::Finalize()
664
0
{
665
0
    bool ret = GDALAlgorithm::Finalize();
666
0
    for (auto &argValue : m_inputDataset)
667
0
        ret = argValue.Close() && ret;
668
0
    ret = m_outputDataset.Close() && ret;
669
0
    return ret;
670
0
}
671
672
/************************************************************************/
673
/*               CreateDatasetSingleOutputLayerIfNeeded()               */
674
/************************************************************************/
675
676
bool GDALPipelineStepAlgorithm::CreateDatasetSingleOutputLayerIfNeeded(
677
    GDALPipelineStepRunContext &ctxt, const std::string &defaultLayerName,
678
    GDALDataset *&poDstDS, bool &bTemporaryFile,
679
    std::unique_ptr<GDALDataset> &poNewRetDS, std::string &outputLayerName,
680
    OGRLayer *&poDstLayer)
681
0
{
682
0
    auto poWriteStep = ctxt.m_poNextUsableStep ? ctxt.m_poNextUsableStep : this;
683
0
    std::string outputFilename = poWriteStep->GetOutputDataset().GetName();
684
685
0
    poDstDS = poWriteStep->GetOutputDataset().GetDatasetRef();
686
0
    poNewRetDS.reset();
687
0
    bTemporaryFile = false;
688
0
    poDstLayer = nullptr;
689
0
    GDALDriver *poDstDriver = nullptr;
690
0
    if (!poDstDS)
691
0
    {
692
0
        auto poDriverManager = GetGDALDriverManager();
693
0
        std::string format = poWriteStep->GetOutputFormat();
694
0
        if (m_standaloneStep || (ctxt.m_poNextUsableStep && format.empty()))
695
0
        {
696
0
            if (format.empty())
697
0
            {
698
0
                const auto aosFormats =
699
0
                    CPLStringList(GDALGetOutputDriversForDatasetName(
700
0
                        outputFilename.c_str(), GDAL_OF_VECTOR,
701
0
                        /* bSingleMatch = */ true,
702
0
                        /* bWarn = */ true));
703
0
                if (aosFormats.size() != 1)
704
0
                {
705
0
                    ReportError(CE_Failure, CPLE_AppDefined,
706
0
                                "Cannot guess driver for %s",
707
0
                                outputFilename.c_str());
708
0
                    return false;
709
0
                }
710
0
                format = aosFormats[0];
711
0
            }
712
0
        }
713
0
        else if (!ctxt.m_poNextUsableStep)
714
0
        {
715
0
            poDstDriver = poDriverManager->GetDriverByName("GPKG");
716
0
            if (poDstDriver)
717
0
            {
718
0
                bTemporaryFile = true;
719
0
                outputFilename =
720
0
                    CPLGenerateTempFilenameSafe(
721
0
                        std::string("_").append(defaultLayerName).c_str()) +
722
0
                    ".gpkg";
723
0
                format = "GPKG";
724
0
            }
725
0
            else
726
0
                format = "MEM";
727
0
        }
728
729
0
        if (!poDstDriver)
730
0
            poDstDriver = poDriverManager->GetDriverByName(format.c_str());
731
0
        if (!poDstDriver)
732
0
        {
733
0
            ReportError(CE_Failure, CPLE_AppDefined, "Cannot find driver %s",
734
0
                        format.c_str());
735
0
            return false;
736
0
        }
737
738
0
        poNewRetDS.reset(poDstDriver->Create(
739
0
            outputFilename.c_str(), 0, 0, 0, GDT_Unknown,
740
0
            CPLStringList(poWriteStep->GetCreationOptions()).List()));
741
0
        if (!poNewRetDS)
742
0
            return false;
743
744
0
        if (bTemporaryFile)
745
0
            poNewRetDS->MarkSuppressOnClose();
746
747
0
        poDstDS = poNewRetDS.get();
748
0
    }
749
0
    else
750
0
    {
751
0
        poDstDriver = poDstDS->GetDriver();
752
0
    }
753
754
0
    outputLayerName = poWriteStep->GetOutputLayerName();
755
0
    if (outputLayerName.empty() && poDstDS->GetLayerCount() > 1)
756
0
    {
757
0
        ReportError(CE_Failure, CPLE_AppDefined, "--%s must be specified",
758
0
                    GDAL_ARG_NAME_OUTPUT_LAYER);
759
0
        return false;
760
0
    }
761
762
0
    if (poDstDriver && EQUAL(poDstDriver->GetDescription(), "ESRI Shapefile") &&
763
0
        (EQUAL(CPLGetExtensionSafe(poDstDS->GetDescription()).c_str(), "shp") ||
764
0
         EQUAL(CPLGetExtensionSafe(poDstDS->GetDescription()).c_str(),
765
0
               "shz")) &&
766
0
        poDstDS->GetLayerCount() <= 1)
767
0
    {
768
0
        outputLayerName = CPLGetBasenameSafe(poDstDS->GetDescription());
769
0
    }
770
0
    if (outputLayerName.empty())
771
0
        outputLayerName = defaultLayerName;
772
773
0
    poDstLayer = poDstDS->GetLayerByName(outputLayerName.c_str());
774
0
    if (poDstLayer)
775
0
    {
776
0
        if (poWriteStep->GetOverwriteLayer())
777
0
        {
778
0
            int iLayer = -1;
779
0
            const int nLayerCount = poDstDS->GetLayerCount();
780
0
            for (iLayer = 0; iLayer < nLayerCount; iLayer++)
781
0
            {
782
0
                if (poDstDS->GetLayer(iLayer) == poDstLayer)
783
0
                    break;
784
0
            }
785
786
0
            if (iLayer < nLayerCount)
787
0
            {
788
0
                if (poDstDS->DeleteLayer(iLayer) != OGRERR_NONE)
789
0
                {
790
0
                    ReportError(CE_Failure, CPLE_AppDefined,
791
0
                                "Cannot delete layer '%s'",
792
0
                                outputLayerName.c_str());
793
0
                    return false;
794
0
                }
795
0
            }
796
0
            poDstLayer = nullptr;
797
0
        }
798
0
        else if (!poWriteStep->GetAppendLayer())
799
0
        {
800
0
            ReportError(CE_Failure, CPLE_AppDefined,
801
0
                        "Layer '%s' already exists. Specify the "
802
0
                        "--%s option to overwrite it, or --%s "
803
0
                        "to append to it.",
804
0
                        outputLayerName.c_str(), GDAL_ARG_NAME_OVERWRITE_LAYER,
805
0
                        GDAL_ARG_NAME_APPEND);
806
0
            return false;
807
0
        }
808
0
    }
809
0
    else if (poWriteStep->GetAppendLayer() || poWriteStep->GetOverwriteLayer())
810
0
    {
811
0
        ReportError(CE_Failure, CPLE_AppDefined, "Cannot find layer '%s'",
812
0
                    outputLayerName.c_str());
813
0
        return false;
814
0
    }
815
816
0
    return true;
817
0
}
818
819
0
GDALAlgorithmStepRegistry::~GDALAlgorithmStepRegistry() = default;
820
821
/************************************************************************/
822
/*                        GDALPipelineAlgorithm                         */
823
/************************************************************************/
824
825
GDALPipelineAlgorithm::GDALPipelineAlgorithm()
826
0
    : GDALAbstractPipelineAlgorithm(
827
0
          NAME, DESCRIPTION, HELP_URL,
828
0
          ConstructorOptions().SetStandaloneStep(false))
829
0
{
830
0
    m_supportsStreamedOutput = true;
831
832
0
    AddProgressArg();
833
0
    AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER | GDAL_OF_VECTOR,
834
0
                       /* positionalAndRequired = */ false)
835
0
        .SetMinCount(1)
836
0
        .SetMaxCount(INT_MAX)
837
0
        .SetHiddenForCLI();
838
0
    AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER | GDAL_OF_VECTOR,
839
0
                        /* positionalAndRequired = */ false)
840
0
        .SetHiddenForCLI()
841
0
        .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
842
0
    AddOutputFormatArg(&m_format, /* bStreamAllowed = */ true,
843
0
                       /* bGDALGAllowed = */ true)
844
0
        .SetHiddenForCLI();
845
0
    AddArg("pipeline", 0, _("Pipeline string or filename"), &m_pipeline)
846
0
        .SetHiddenForCLI()
847
0
        .SetPositional();
848
849
0
    AddOutputStringArg(&m_output).SetHiddenForCLI();
850
0
    AddStdoutArg(&m_stdout);
851
852
0
    AllowArbitraryLongNameArgs();
853
854
0
    GDALRasterPipelineAlgorithm::RegisterAlgorithms(m_stepRegistry, true);
855
0
    GDALVectorPipelineAlgorithm::RegisterAlgorithms(m_stepRegistry, true);
856
0
    m_stepRegistry.Register<GDALExternalRasterOrVectorAlgorithm>();
857
0
    m_stepRegistry.Register<GDALRasterAsFeaturesAlgorithm>();
858
0
    m_stepRegistry.Register<GDALRasterContourAlgorithm>();
859
0
    m_stepRegistry.Register<GDALRasterFootprintAlgorithm>();
860
0
    m_stepRegistry.Register<GDALRasterPixelInfoAlgorithm>();
861
0
    m_stepRegistry.Register<GDALRasterPolygonizeAlgorithm>();
862
0
    m_stepRegistry.Register<GDALRasterZonalStatsAlgorithm>();
863
0
    m_stepRegistry.Register<GDALVectorGridAlgorithm>();
864
0
    m_stepRegistry.Register<GDALVectorRasterizeAlgorithm>();
865
0
}
866
867
/************************************************************************/
868
/*               GDALPipelineAlgorithm::GetUsageForCLI()                */
869
/************************************************************************/
870
871
std::string
872
GDALPipelineAlgorithm::GetUsageForCLI(bool shortUsage,
873
                                      const UsageOptions &usageOptions) const
874
0
{
875
0
    UsageOptions stepUsageOptions;
876
0
    stepUsageOptions.isPipelineStep = true;
877
878
0
    if (!m_helpDocCategory.empty() && m_helpDocCategory != "main")
879
0
    {
880
0
        auto alg = GetStepAlg(m_helpDocCategory);
881
0
        if (alg)
882
0
        {
883
0
            alg->SetCallPath({CPLString(m_helpDocCategory)
884
0
                                  .replaceAll(RASTER_SUFFIX, "")
885
0
                                  .replaceAll(VECTOR_SUFFIX, "")});
886
0
            alg->GetArg("help-doc")->Set(true);
887
0
            return alg->GetUsageForCLI(shortUsage, stepUsageOptions);
888
0
        }
889
0
        else
890
0
        {
891
0
            fprintf(stderr, "ERROR: unknown pipeline step '%s'\n",
892
0
                    m_helpDocCategory.c_str());
893
0
            return CPLSPrintf("ERROR: unknown pipeline step '%s'\n",
894
0
                              m_helpDocCategory.c_str());
895
0
        }
896
0
    }
897
898
0
    UsageOptions usageOptionsMain(usageOptions);
899
0
    usageOptionsMain.isPipelineMain = true;
900
0
    std::string ret =
901
0
        GDALAlgorithm::GetUsageForCLI(shortUsage, usageOptionsMain);
902
0
    if (shortUsage)
903
0
        return ret;
904
905
0
    ret +=
906
0
        "\n<PIPELINE> is of the form: read|calc|concat|create|mosaic|stack "
907
0
        "[READ-OPTIONS] "
908
0
        "( ! <STEP-NAME> [STEP-OPTIONS] )* ! write!info!tile [WRITE-OPTIONS]\n";
909
910
0
    if (m_helpDocCategory == "main")
911
0
    {
912
0
        return ret;
913
0
    }
914
915
0
    ret += '\n';
916
0
    ret += "Example: 'gdal pipeline --progress ! read in.tif ! \\\n";
917
0
    ret += "               rasterize --size 256 256 ! buffer 20 ! ";
918
0
    ret += "write out.gpkg --overwrite'\n";
919
0
    ret += '\n';
920
0
    ret += "Potential steps are:\n";
921
922
0
    for (const std::string &name : m_stepRegistry.GetNames())
923
0
    {
924
0
        auto alg = GetStepAlg(name);
925
0
        assert(alg);
926
0
        auto [options, maxOptLen] = alg->GetArgNamesForCLI();
927
0
        stepUsageOptions.maxOptLen =
928
0
            std::max(stepUsageOptions.maxOptLen, maxOptLen);
929
0
    }
930
931
0
    {
932
0
        ret += '\n';
933
0
        auto alg = std::make_unique<GDALRasterReadAlgorithm>();
934
0
        alg->SetCallPath({alg->GetName()});
935
0
        ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
936
0
    }
937
0
    {
938
0
        ret += '\n';
939
0
        auto alg = std::make_unique<GDALVectorReadAlgorithm>();
940
0
        alg->SetCallPath({alg->GetName()});
941
0
        ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
942
0
    }
943
0
    for (const std::string &name : m_stepRegistry.GetNames())
944
0
    {
945
0
        auto alg = GetStepAlg(name);
946
0
        assert(alg);
947
0
        if (alg->CanBeFirstStep() && !alg->CanBeMiddleStep() &&
948
0
            !alg->IsHidden() &&
949
0
            !STARTS_WITH(name.c_str(), GDALRasterReadAlgorithm::NAME))
950
0
        {
951
0
            ret += '\n';
952
0
            alg->SetCallPath({name});
953
0
            ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
954
0
        }
955
0
    }
956
0
    for (const std::string &name : m_stepRegistry.GetNames())
957
0
    {
958
0
        auto alg = GetStepAlg(name);
959
0
        assert(alg);
960
0
        if (alg->CanBeMiddleStep() && !alg->IsHidden())
961
0
        {
962
0
            ret += '\n';
963
0
            alg->SetCallPath({CPLString(alg->GetName())
964
0
                                  .replaceAll(RASTER_SUFFIX, "")
965
0
                                  .replaceAll(VECTOR_SUFFIX, "")});
966
0
            ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
967
0
        }
968
0
    }
969
0
    for (const std::string &name : m_stepRegistry.GetNames())
970
0
    {
971
0
        auto alg = GetStepAlg(name);
972
0
        assert(alg);
973
0
        if (alg->CanBeLastStep() && !alg->CanBeMiddleStep() &&
974
0
            !alg->IsHidden() &&
975
0
            !STARTS_WITH(name.c_str(), GDALRasterWriteAlgorithm::NAME))
976
0
        {
977
0
            ret += '\n';
978
0
            alg->SetCallPath({name});
979
0
            ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
980
0
        }
981
0
    }
982
0
    {
983
0
        ret += '\n';
984
0
        auto alg = std::make_unique<GDALRasterWriteAlgorithm>();
985
0
        alg->SetCallPath({alg->GetName()});
986
0
        ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
987
0
    }
988
0
    {
989
0
        ret += '\n';
990
0
        auto alg = std::make_unique<GDALVectorWriteAlgorithm>();
991
0
        alg->SetCallPath({alg->GetName()});
992
0
        ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
993
0
    }
994
995
0
    ret += GetUsageForCLIEnd();
996
997
0
    return ret;
998
0
}
999
1000
//! @endcond