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