Coverage Report

Created: 2026-04-01 06:20

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