Coverage Report

Created: 2026-02-14 06:52

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