Coverage Report

Created: 2026-09-14 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_raster_pipeline.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  gdal "raster 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
#include "gdalalg_raster_pipeline.h"
14
#include "gdalalg_external.h"
15
#include "gdalalg_materialize.h"
16
#include "gdalalg_raster_read.h"
17
#include "gdalalg_raster_calc.h"
18
#include "gdalalg_raster_aspect.h"
19
#include "gdalalg_raster_blend.h"
20
#include "gdalalg_raster_clean_collar.h"
21
#include "gdalalg_raster_clip.h"
22
#include "gdalalg_raster_color_map.h"
23
#include "gdalalg_raster_compare.h"
24
#include "gdalalg_raster_create.h"
25
#include "gdalalg_raster_edit.h"
26
#include "gdalalg_raster_fill_nodata.h"
27
#include "gdalalg_raster_hillshade.h"
28
#include "gdalalg_raster_info.h"
29
#include "gdalalg_raster_mosaic.h"
30
#include "gdalalg_raster_neighbors.h"
31
#include "gdalalg_raster_nodata_to_alpha.h"
32
#include "gdalalg_raster_overview.h"
33
#include "gdalalg_raster_pansharpen.h"
34
#include "gdalalg_raster_proximity.h"
35
#include "gdalalg_raster_reclassify.h"
36
#include "gdalalg_raster_reproject.h"
37
#include "gdalalg_raster_resize.h"
38
#include "gdalalg_raster_rgb_to_palette.h"
39
#include "gdalalg_raster_roughness.h"
40
#include "gdalalg_raster_scale.h"
41
#include "gdalalg_raster_select.h"
42
#include "gdalalg_raster_set_type.h"
43
#include "gdalalg_raster_shift_longitude.h"
44
#include "gdalalg_raster_sieve.h"
45
#include "gdalalg_raster_slope.h"
46
#include "gdalalg_raster_stack.h"
47
#include "gdalalg_raster_tile.h"
48
#include "gdalalg_raster_write.h"
49
#include "gdalalg_raster_tpi.h"
50
#include "gdalalg_raster_tri.h"
51
#include "gdalalg_raster_unscale.h"
52
#include "gdalalg_raster_update.h"
53
#include "gdalalg_raster_viewshed.h"
54
#include "gdalalg_tee.h"
55
56
#include "cpl_conv.h"
57
#include "cpl_progress.h"
58
#include "cpl_string.h"
59
#include "cpl_vsi.h"
60
#include "gdal_priv.h"
61
#include "gdal_utils.h"
62
63
#include <algorithm>
64
#include <array>
65
#include <cassert>
66
67
//! @cond Doxygen_Suppress
68
69
#ifndef _
70
#define _(x) (x)
71
#endif
72
73
0
GDALRasterAlgorithmStepRegistry::~GDALRasterAlgorithmStepRegistry() = default;
74
75
/************************************************************************/
76
/*  GDALRasterPipelineStepAlgorithm::GDALRasterPipelineStepAlgorithm()  */
77
/************************************************************************/
78
79
GDALRasterPipelineStepAlgorithm::GDALRasterPipelineStepAlgorithm(
80
    const std::string &name, const std::string &description,
81
    const std::string &helpURL, bool standaloneStep)
82
0
    : GDALRasterPipelineStepAlgorithm(
83
0
          name, description, helpURL,
84
0
          ConstructorOptions().SetStandaloneStep(standaloneStep))
85
0
{
86
0
}
87
88
/************************************************************************/
89
/*  GDALRasterPipelineStepAlgorithm::GDALRasterPipelineStepAlgorithm()  */
90
/************************************************************************/
91
92
GDALRasterPipelineStepAlgorithm::GDALRasterPipelineStepAlgorithm(
93
    const std::string &name, const std::string &description,
94
    const std::string &helpURL, const ConstructorOptions &options)
95
0
    : GDALPipelineStepAlgorithm(name, description, helpURL, options)
96
0
{
97
0
    if (m_standaloneStep)
98
0
    {
99
0
        m_supportsStreamedOutput = true;
100
101
0
        if (m_constructorOptions.addDefaultArguments)
102
0
        {
103
0
            AddRasterInputArgs(false, false);
104
0
            AddProgressArg();
105
0
            AddRasterOutputArgs(false);
106
0
        }
107
0
    }
108
0
    else if (m_constructorOptions.addDefaultArguments)
109
0
    {
110
0
        AddRasterHiddenInputDatasetArg();
111
0
    }
112
0
}
113
114
0
GDALRasterPipelineStepAlgorithm::~GDALRasterPipelineStepAlgorithm() = default;
115
116
/************************************************************************/
117
/*      GDALRasterPipelineStepAlgorithm::SetOutputVRTCompatible()       */
118
/************************************************************************/
119
120
void GDALRasterPipelineStepAlgorithm::SetOutputVRTCompatible(bool b)
121
0
{
122
0
    m_outputVRTCompatible = b;
123
0
    if (m_outputFormatArg)
124
0
    {
125
0
        m_outputFormatArg->AddMetadataItem(GAAMDI_VRT_COMPATIBLE,
126
0
                                           {b ? "true" : "false"});
127
0
    }
128
0
}
129
130
/************************************************************************/
131
/*      GDALRasterPipelineAlgorithm::GDALRasterPipelineAlgorithm()      */
132
/************************************************************************/
133
134
GDALRasterPipelineAlgorithm::GDALRasterPipelineAlgorithm(
135
    bool openForMixedRasterVector)
136
0
    : GDALAbstractPipelineAlgorithm(NAME, DESCRIPTION, HELP_URL,
137
0
                                    ConstructorOptions()
138
0
                                        .SetAddDefaultArguments(false)
139
0
                                        .SetInputDatasetRequired(false)
140
0
                                        .SetInputDatasetPositional(false)
141
0
                                        .SetInputDatasetMaxCount(INT_MAX))
142
0
{
143
0
    m_supportsStreamedOutput = true;
144
145
0
    AddRasterInputArgs(openForMixedRasterVector, /* hiddenForCLI = */ true);
146
0
    AddProgressArg();
147
0
    AddArg("pipeline", 0, _("Pipeline string"), &m_pipeline)
148
0
        .SetHiddenForCLI()
149
0
        .SetPositional();
150
0
    AddRasterOutputArgs(/* hiddenForCLI = */ true);
151
152
0
    AddOutputStringArg(&m_output).SetHiddenForCLI();
153
0
    AddStdoutArg(&m_stdout);
154
155
0
    RegisterAlgorithms(m_stepRegistry, false);
156
0
}
157
158
/************************************************************************/
159
/*          GDALRasterPipelineAlgorithm::RegisterAlgorithms()           */
160
/************************************************************************/
161
162
/* static */
163
void GDALRasterPipelineAlgorithm::RegisterAlgorithms(
164
    GDALRasterAlgorithmStepRegistry &registry, bool forMixedPipeline)
165
0
{
166
0
    GDALAlgorithmRegistry::AlgInfo algInfo;
167
168
0
    const auto addSuffixIfNeeded =
169
0
        [forMixedPipeline](const char *name) -> std::string
170
0
    {
171
0
        return forMixedPipeline ? std::string(name).append(RASTER_SUFFIX)
172
0
                                : std::string(name);
173
0
    };
174
175
0
    registry.Register<GDALRasterReadAlgorithm>(
176
0
        addSuffixIfNeeded(GDALRasterReadAlgorithm::NAME));
177
178
0
    registry.Register<GDALRasterCalcAlgorithm>();
179
0
    registry.Register<GDALRasterCreateAlgorithm>();
180
181
0
    registry.Register<GDALRasterNeighborsAlgorithm>();
182
183
0
    registry.Register<GDALRasterWriteAlgorithm>(
184
0
        addSuffixIfNeeded(GDALRasterWriteAlgorithm::NAME));
185
186
0
    registry.Register<GDALRasterInfoAlgorithm>(
187
0
        addSuffixIfNeeded(GDALRasterInfoAlgorithm::NAME));
188
189
0
    registry.Register<GDALRasterAspectAlgorithm>();
190
0
    registry.Register<GDALRasterBlendAlgorithm>();
191
192
0
    registry.Register<GDALRasterCleanCollarAlgorithm>();
193
0
    registry.Register<GDALRasterClipAlgorithm>(
194
0
        addSuffixIfNeeded(GDALRasterClipAlgorithm::NAME));
195
196
0
    registry.Register<GDALRasterColorMapAlgorithm>();
197
0
    registry.Register<GDALRasterCompareAlgorithm>();
198
199
0
    registry.Register<GDALRasterEditAlgorithm>(
200
0
        addSuffixIfNeeded(GDALRasterEditAlgorithm::NAME));
201
202
0
    registry.Register<GDALRasterNoDataToAlphaAlgorithm>();
203
0
    registry.Register<GDALRasterFillNodataAlgorithm>();
204
0
    registry.Register<GDALRasterHillshadeAlgorithm>();
205
206
0
    registry.Register<GDALMaterializeRasterAlgorithm>(
207
0
        addSuffixIfNeeded(GDALMaterializeRasterAlgorithm::NAME));
208
209
0
    registry.Register<GDALRasterMosaicAlgorithm>();
210
0
    registry.Register<GDALRasterOverviewAlgorithm>();
211
0
    registry.Register<GDALRasterPansharpenAlgorithm>();
212
0
    registry.Register<GDALRasterProximityAlgorithm>();
213
0
    registry.Register<GDALRasterReclassifyAlgorithm>();
214
215
0
    registry.Register<GDALRasterReprojectAlgorithm>(
216
0
        addSuffixIfNeeded(GDALRasterReprojectAlgorithm::NAME));
217
218
0
    registry.Register<GDALRasterResizeAlgorithm>();
219
0
    registry.Register<GDALRasterRGBToPaletteAlgorithm>();
220
0
    registry.Register<GDALRasterRoughnessAlgorithm>();
221
0
    registry.Register<GDALRasterScaleAlgorithm>();
222
223
0
    registry.Register<GDALRasterSelectAlgorithm>(
224
0
        addSuffixIfNeeded(GDALRasterSelectAlgorithm::NAME));
225
226
0
    registry.Register<GDALRasterSetTypeAlgorithm>();
227
0
    registry.Register<GDALRasterShiftLongitudeAlgorithm>();
228
0
    registry.Register<GDALRasterSieveAlgorithm>();
229
0
    registry.Register<GDALRasterSlopeAlgorithm>();
230
0
    registry.Register<GDALRasterStackAlgorithm>();
231
0
    registry.Register<GDALRasterTileAlgorithm>();
232
0
    registry.Register<GDALRasterTPIAlgorithm>();
233
0
    registry.Register<GDALRasterTRIAlgorithm>();
234
0
    registry.Register<GDALRasterUnscaleAlgorithm>();
235
0
    registry.Register<GDALRasterUpdateAlgorithm>(
236
0
        addSuffixIfNeeded(GDALRasterUpdateAlgorithm::NAME));
237
0
    registry.Register<GDALRasterViewshedAlgorithm>();
238
0
    registry.Register<GDALTeeRasterAlgorithm>(
239
0
        addSuffixIfNeeded(GDALTeeRasterAlgorithm::NAME));
240
241
0
    if (!forMixedPipeline)
242
0
    {
243
0
        registry.Register<GDALExternalRasterAlgorithm>();
244
0
    }
245
0
}
246
247
/************************************************************************/
248
/*            GDALRasterPipelineAlgorithm::GetUsageForCLI()             */
249
/************************************************************************/
250
251
std::string GDALRasterPipelineAlgorithm::GetUsageForCLI(
252
    bool shortUsage, const UsageOptions &usageOptions) const
253
0
{
254
0
    UsageOptions stepUsageOptions;
255
0
    stepUsageOptions.isPipelineStep = true;
256
257
0
    if (!m_helpDocCategory.empty() && m_helpDocCategory != "main")
258
0
    {
259
0
        auto alg = GetStepAlg(m_helpDocCategory);
260
0
        if (alg)
261
0
        {
262
0
            alg->SetCallPath({m_helpDocCategory});
263
0
            alg->GetArg("help-doc")->Set(true);
264
0
            return alg->GetUsageForCLI(shortUsage, stepUsageOptions);
265
0
        }
266
0
        else
267
0
        {
268
0
            fprintf(stderr, "ERROR: unknown pipeline step '%s'\n",
269
0
                    m_helpDocCategory.c_str());
270
0
            return CPLSPrintf("ERROR: unknown pipeline step '%s'\n",
271
0
                              m_helpDocCategory.c_str());
272
0
        }
273
0
    }
274
275
0
    UsageOptions usageOptionsMain(usageOptions);
276
0
    usageOptionsMain.isPipelineMain = true;
277
0
    std::string ret =
278
0
        GDALAlgorithm::GetUsageForCLI(shortUsage, usageOptionsMain);
279
0
    if (shortUsage)
280
0
        return ret;
281
282
0
    ret += "\n<PIPELINE> is of the form: read|mosaic|stack [READ-OPTIONS] "
283
0
           "( ! <STEP-NAME> [STEP-OPTIONS] )* ! info|compare|tile|write "
284
0
           "[WRITE-OPTIONS]\n";
285
286
0
    if (m_helpDocCategory == "main")
287
0
    {
288
0
        return ret;
289
0
    }
290
291
0
    ret += '\n';
292
0
    ret += "Example: 'gdal raster pipeline --progress ! read in.tif ! \\\n";
293
0
    ret += "               reproject --output-crs=EPSG:32632 ! ";
294
0
    ret += "write out.tif --overwrite'\n";
295
0
    ret += '\n';
296
0
    ret += "Potential steps are:\n";
297
298
0
    for (const std::string &name : m_stepRegistry.GetNames())
299
0
    {
300
0
        auto alg = GetStepAlg(name);
301
0
        auto [options, maxOptLen] = alg->GetArgNamesForCLI();
302
0
        stepUsageOptions.maxOptLen =
303
0
            std::max(stepUsageOptions.maxOptLen, maxOptLen);
304
0
    }
305
306
0
    {
307
0
        const auto name = GDALRasterReadAlgorithm::NAME;
308
0
        ret += '\n';
309
0
        auto alg = GetStepAlg(name);
310
0
        alg->SetCallPath({name});
311
0
        ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
312
0
    }
313
0
    for (const std::string &name : m_stepRegistry.GetNames())
314
0
    {
315
0
        auto alg = GetStepAlg(name);
316
0
        assert(alg);
317
0
        if (alg->CanBeFirstStep() && !alg->CanBeMiddleStep() &&
318
0
            !alg->IsHidden() && name != GDALRasterReadAlgorithm::NAME)
319
0
        {
320
0
            ret += '\n';
321
0
            alg->SetCallPath({name});
322
0
            ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
323
0
        }
324
0
    }
325
0
    for (const std::string &name : m_stepRegistry.GetNames())
326
0
    {
327
0
        auto alg = GetStepAlg(name);
328
0
        assert(alg);
329
0
        if (alg->CanBeMiddleStep() && !alg->IsHidden())
330
0
        {
331
0
            ret += '\n';
332
0
            alg->SetCallPath({name});
333
0
            ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
334
0
        }
335
0
    }
336
0
    for (const std::string &name : m_stepRegistry.GetNames())
337
0
    {
338
0
        auto alg = GetStepAlg(name);
339
0
        assert(alg);
340
0
        if (alg->CanBeLastStep() && !alg->CanBeMiddleStep() &&
341
0
            !alg->IsHidden() && name != GDALRasterWriteAlgorithm::NAME)
342
0
        {
343
0
            ret += '\n';
344
0
            alg->SetCallPath({name});
345
0
            ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
346
0
        }
347
0
    }
348
0
    {
349
0
        const auto name = GDALRasterWriteAlgorithm::NAME;
350
0
        ret += '\n';
351
0
        auto alg = GetStepAlg(name);
352
0
        alg->SetCallPath({name});
353
0
        ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
354
0
    }
355
0
    ret += GetUsageForCLIEnd();
356
357
0
    return ret;
358
0
}
359
360
/************************************************************************/
361
/*          GDALRasterPipelineNonNativelyStreamingAlgorithm()           */
362
/************************************************************************/
363
364
GDALRasterPipelineNonNativelyStreamingAlgorithm::
365
    GDALRasterPipelineNonNativelyStreamingAlgorithm(
366
        const std::string &name, const std::string &description,
367
        const std::string &helpURL, bool standaloneStep)
368
0
    : GDALRasterPipelineStepAlgorithm(name, description, helpURL,
369
0
                                      standaloneStep)
370
0
{
371
0
}
372
373
GDALRasterPipelineNonNativelyStreamingAlgorithm::
374
    GDALRasterPipelineNonNativelyStreamingAlgorithm(
375
        const std::string &name, const std::string &description,
376
        const std::string &helpURL, const ConstructorOptions &options)
377
0
    : GDALRasterPipelineStepAlgorithm(name, description, helpURL, options)
378
0
{
379
0
}
380
381
/************************************************************************/
382
/*                   IsNativelyStreamingCompatible()                    */
383
/************************************************************************/
384
385
bool GDALRasterPipelineNonNativelyStreamingAlgorithm::
386
    IsNativelyStreamingCompatible() const
387
0
{
388
0
    return false;
389
0
}
390
391
/************************************************************************/
392
/*                    MustCreateOnDiskTempDataset()                     */
393
/************************************************************************/
394
395
static bool MustCreateOnDiskTempDataset(int nWidth, int nHeight, int nBands,
396
                                        GDALDataType eDT)
397
0
{
398
    // Config option mostly for autotest purposes
399
0
    if (CPLTestBool(CPLGetConfigOption(
400
0
            "GDAL_RASTER_PIPELINE_USE_GTIFF_FOR_TEMP_DATASET", "NO")))
401
0
        return true;
402
403
    // Allow up to 10% of RAM usage for temporary dataset
404
0
    const auto nRAM = CPLGetUsablePhysicalRAM() / 10;
405
0
    const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
406
0
    const bool bOnDisk =
407
0
        nBands > 0 && nDTSize > 0 && nRAM > 0 &&
408
0
        static_cast<int64_t>(nWidth) * nHeight > nRAM / (nBands * nDTSize);
409
0
    return bOnDisk;
410
0
}
411
412
/************************************************************************/
413
/*                       CreateTemporaryDataset()                       */
414
/************************************************************************/
415
416
std::unique_ptr<GDALDataset>
417
GDALRasterPipelineNonNativelyStreamingAlgorithm::CreateTemporaryDataset(
418
    int nWidth, int nHeight, int nBands, GDALDataType eDT,
419
    bool bTiledIfPossible, GDALDataset *poSrcDSForMetadata, bool bCopyMetadata)
420
0
{
421
0
    const bool bOnDisk =
422
0
        MustCreateOnDiskTempDataset(nWidth, nHeight, nBands, eDT);
423
0
    const char *pszDriverName = bOnDisk ? "GTIFF" : "MEM";
424
0
    GDALDriver *poDriver =
425
0
        GetGDALDriverManager()->GetDriverByName(pszDriverName);
426
0
    CPLStringList aosOptions;
427
0
    std::string osTmpFilename;
428
0
    if (bOnDisk)
429
0
    {
430
0
        osTmpFilename =
431
0
            CPLGenerateTempFilenameSafe(
432
0
                poSrcDSForMetadata
433
0
                    ? CPLGetBasenameSafe(poSrcDSForMetadata->GetDescription())
434
0
                          .c_str()
435
0
                    : "") +
436
0
            ".tif";
437
0
        if (bTiledIfPossible)
438
0
            aosOptions.SetNameValue("TILED", "YES");
439
0
        const char *pszCOList =
440
0
            poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST);
441
0
        aosOptions.SetNameValue("COMPRESS",
442
0
                                pszCOList && strstr(pszCOList, "ZSTD") ? "ZSTD"
443
0
                                                                       : "LZW");
444
0
        aosOptions.SetNameValue("SPARSE_OK", "YES");
445
0
    }
446
0
    std::unique_ptr<GDALDataset> poOutDS(
447
0
        poDriver ? poDriver->Create(osTmpFilename.c_str(), nWidth, nHeight,
448
0
                                    nBands, eDT, aosOptions.List())
449
0
                 : nullptr);
450
0
    if (poOutDS && bOnDisk)
451
0
    {
452
        // In file systems that allow it (all but Windows...), we want to
453
        // delete the temporary file as soon as soon as possible after
454
        // having open it, so that if someone kills the process there are
455
        // no temp files left over. If that unlink() doesn't succeed
456
        // (on Windows), then the file will eventually be deleted when
457
        // poTmpDS is cleaned due to MarkSuppressOnClose().
458
0
        VSIUnlink(osTmpFilename.c_str());
459
0
        poOutDS->MarkSuppressOnClose();
460
0
    }
461
462
0
    if (poOutDS && poSrcDSForMetadata)
463
0
    {
464
0
        poOutDS->SetSpatialRef(poSrcDSForMetadata->GetSpatialRef());
465
0
        GDALGeoTransform gt;
466
0
        if (poSrcDSForMetadata->GetGeoTransform(gt) == CE_None)
467
0
            poOutDS->SetGeoTransform(gt);
468
0
        if (const int nGCPCount = poSrcDSForMetadata->GetGCPCount())
469
0
        {
470
0
            const auto apsGCPs = poSrcDSForMetadata->GetGCPs();
471
0
            if (apsGCPs)
472
0
            {
473
0
                poOutDS->SetGCPs(nGCPCount, apsGCPs,
474
0
                                 poSrcDSForMetadata->GetGCPSpatialRef());
475
0
            }
476
0
        }
477
0
        if (bCopyMetadata)
478
0
        {
479
0
            poOutDS->SetMetadata(poSrcDSForMetadata->GetMetadata());
480
0
        }
481
0
    }
482
483
0
    return poOutDS;
484
0
}
485
486
/************************************************************************/
487
/*                        CreateTemporaryCopy()                         */
488
/************************************************************************/
489
490
std::unique_ptr<GDALDataset>
491
GDALRasterPipelineNonNativelyStreamingAlgorithm::CreateTemporaryCopy(
492
    GDALAlgorithm *poAlg, GDALDataset *poSrcDS, int nSingleBand,
493
    bool bTiledIfPossible, GDALProgressFunc pfnProgress, void *pProgressData)
494
0
{
495
0
    const int nBands = nSingleBand > 0 ? 1 : poSrcDS->GetRasterCount();
496
0
    const auto eDT =
497
0
        nBands ? poSrcDS->GetRasterBand(1)->GetRasterDataType() : GDT_Unknown;
498
0
    const bool bOnDisk = MustCreateOnDiskTempDataset(
499
0
        poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(), nBands, eDT);
500
0
    const char *pszDriverName = bOnDisk ? "GTIFF" : "MEM";
501
502
0
    CPLStringList options;
503
0
    if (nSingleBand > 0)
504
0
    {
505
0
        options.AddString("-b");
506
0
        options.AddString(CPLSPrintf("%d", nSingleBand));
507
0
    }
508
509
0
    options.AddString("-of");
510
0
    options.AddString(pszDriverName);
511
512
0
    std::string osTmpFilename;
513
0
    if (bOnDisk)
514
0
    {
515
0
        osTmpFilename =
516
0
            CPLGenerateTempFilenameSafe(
517
0
                CPLGetBasenameSafe(poSrcDS->GetDescription()).c_str()) +
518
0
            ".tif";
519
0
        if (bTiledIfPossible)
520
0
        {
521
0
            options.AddString("-co");
522
0
            options.AddString("TILED=YES");
523
0
        }
524
525
0
        GDALDriver *poDriver =
526
0
            GetGDALDriverManager()->GetDriverByName(pszDriverName);
527
0
        const char *pszCOList =
528
0
            poDriver ? poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST)
529
0
                     : nullptr;
530
0
        options.AddString("-co");
531
0
        options.AddString(pszCOList && strstr(pszCOList, "ZSTD")
532
0
                              ? "COMPRESS=ZSTD"
533
0
                              : "COMPRESS=LZW");
534
0
    }
535
536
0
    GDALTranslateOptions *translateOptions =
537
0
        GDALTranslateOptionsNew(options.List(), nullptr);
538
539
0
    if (pfnProgress)
540
0
        GDALTranslateOptionsSetProgress(translateOptions, pfnProgress,
541
0
                                        pProgressData);
542
543
0
    std::unique_ptr<GDALDataset> poOutDS(GDALDataset::FromHandle(
544
0
        GDALTranslate(osTmpFilename.c_str(), GDALDataset::ToHandle(poSrcDS),
545
0
                      translateOptions, nullptr)));
546
0
    GDALTranslateOptionsFree(translateOptions);
547
548
0
    if (!poOutDS)
549
0
    {
550
0
        poAlg->ReportError(CE_Failure, CPLE_AppDefined,
551
0
                           "Failed to create temporary dataset");
552
0
    }
553
0
    else if (bOnDisk)
554
0
    {
555
        // In file systems that allow it (all but Windows...), we want to
556
        // delete the temporary file as soon as soon as possible after
557
        // having open it, so that if someone kills the process there are
558
        // no temp files left over. If that unlink() doesn't succeed
559
        // (on Windows), then the file will eventually be deleted when
560
        // poTmpDS is cleaned due to MarkSuppressOnClose().
561
0
        VSIUnlink(osTmpFilename.c_str());
562
0
        poOutDS->MarkSuppressOnClose();
563
0
    }
564
0
    return poOutDS;
565
0
}
566
567
//! @endcond