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