Coverage Report

Created: 2026-07-14 06:21

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