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_raster_read.h"
15 : #include "gdalalg_raster_calc.h"
16 : #include "gdalalg_raster_aspect.h"
17 : #include "gdalalg_raster_clip.h"
18 : #include "gdalalg_raster_color_map.h"
19 : #include "gdalalg_raster_color_merge.h"
20 : #include "gdalalg_raster_edit.h"
21 : #include "gdalalg_raster_fill_nodata.h"
22 : #include "gdalalg_raster_hillshade.h"
23 : #include "gdalalg_raster_info.h"
24 : #include "gdalalg_raster_mosaic.h"
25 : #include "gdalalg_raster_nodata_to_alpha.h"
26 : #include "gdalalg_raster_pansharpen.h"
27 : #include "gdalalg_raster_proximity.h"
28 : #include "gdalalg_raster_reclassify.h"
29 : #include "gdalalg_raster_reproject.h"
30 : #include "gdalalg_raster_resize.h"
31 : #include "gdalalg_raster_rgb_to_palette.h"
32 : #include "gdalalg_raster_roughness.h"
33 : #include "gdalalg_raster_scale.h"
34 : #include "gdalalg_raster_select.h"
35 : #include "gdalalg_raster_set_type.h"
36 : #include "gdalalg_raster_sieve.h"
37 : #include "gdalalg_raster_slope.h"
38 : #include "gdalalg_raster_stack.h"
39 : #include "gdalalg_raster_write.h"
40 : #include "gdalalg_raster_tpi.h"
41 : #include "gdalalg_raster_tri.h"
42 : #include "gdalalg_raster_unscale.h"
43 : #include "gdalalg_raster_viewshed.h"
44 :
45 : #include "cpl_conv.h"
46 : #include "cpl_progress.h"
47 : #include "cpl_string.h"
48 : #include "cpl_vsi.h"
49 : #include "gdal_priv.h"
50 : #include "gdal_utils.h"
51 :
52 : #include <algorithm>
53 : #include <array>
54 : #include <cassert>
55 :
56 : //! @cond Doxygen_Suppress
57 :
58 : #ifndef _
59 : #define _(x) (x)
60 : #endif
61 :
62 : /************************************************************************/
63 : /* GDALRasterPipelineStepAlgorithm::GDALRasterPipelineStepAlgorithm() */
64 : /************************************************************************/
65 :
66 1201 : GDALRasterPipelineStepAlgorithm::GDALRasterPipelineStepAlgorithm(
67 : const std::string &name, const std::string &description,
68 1201 : const std::string &helpURL, bool standaloneStep)
69 : : GDALRasterPipelineStepAlgorithm(
70 : name, description, helpURL,
71 1201 : ConstructorOptions().SetStandaloneStep(standaloneStep))
72 : {
73 1201 : }
74 :
75 : /************************************************************************/
76 : /* GDALRasterPipelineStepAlgorithm::GDALRasterPipelineStepAlgorithm() */
77 : /************************************************************************/
78 :
79 2171 : GDALRasterPipelineStepAlgorithm::GDALRasterPipelineStepAlgorithm(
80 : const std::string &name, const std::string &description,
81 2171 : const std::string &helpURL, const ConstructorOptions &options)
82 2171 : : GDALPipelineStepAlgorithm(name, description, helpURL, options)
83 : {
84 2171 : if (m_standaloneStep)
85 : {
86 571 : m_supportsStreamedOutput = true;
87 :
88 571 : if (m_constructorOptions.addDefaultArguments)
89 : {
90 336 : AddRasterInputArgs(false, false);
91 336 : AddProgressArg();
92 336 : AddRasterOutputArgs(false);
93 : }
94 : }
95 1600 : else if (m_constructorOptions.addDefaultArguments)
96 : {
97 865 : AddRasterHiddenInputDatasetArg();
98 : }
99 2171 : }
100 :
101 : GDALRasterPipelineStepAlgorithm::~GDALRasterPipelineStepAlgorithm() = default;
102 :
103 : /************************************************************************/
104 : /* GDALRasterPipelineStepAlgorithm::SetOutputVRTCompatible() */
105 : /************************************************************************/
106 :
107 220 : void GDALRasterPipelineStepAlgorithm::SetOutputVRTCompatible(bool b)
108 : {
109 220 : m_outputVRTCompatible = b;
110 220 : if (m_outputFormatArg)
111 : {
112 92 : m_outputFormatArg->AddMetadataItem(GAAMDI_VRT_COMPATIBLE,
113 184 : {b ? "true" : "false"});
114 : }
115 220 : }
116 :
117 : /************************************************************************/
118 : /* GDALRasterPipelineAlgorithm::GDALRasterPipelineAlgorithm() */
119 : /************************************************************************/
120 :
121 101 : GDALRasterPipelineAlgorithm::GDALRasterPipelineAlgorithm(
122 101 : bool openForMixedRasterVector)
123 : : GDALAbstractPipelineAlgorithm<GDALRasterPipelineStepAlgorithm>(
124 : NAME, DESCRIPTION, HELP_URL,
125 0 : ConstructorOptions()
126 101 : .SetAddDefaultArguments(false)
127 202 : .SetInputDatasetMaxCount(INT_MAX))
128 : {
129 101 : m_supportsStreamedOutput = true;
130 :
131 101 : AddRasterInputArgs(openForMixedRasterVector, /* hiddenForCLI = */ true);
132 101 : AddProgressArg();
133 202 : AddArg("pipeline", 0, _("Pipeline string"), &m_pipeline)
134 101 : .SetHiddenForCLI()
135 101 : .SetPositional();
136 101 : AddRasterOutputArgs(/* hiddenForCLI = */ true);
137 :
138 101 : AddOutputStringArg(&m_output).SetHiddenForCLI();
139 : AddArg("stdout", 0,
140 : _("Directly output on stdout (format=text mode only). If enabled, "
141 : "output-string will be empty"),
142 202 : &m_stdout)
143 101 : .SetHidden();
144 :
145 101 : RegisterAlgorithms(m_stepRegistry, false);
146 101 : }
147 :
148 : /************************************************************************/
149 : /* GDALRasterPipelineAlgorithm::RegisterAlgorithms() */
150 : /************************************************************************/
151 :
152 : /* static */
153 156 : void GDALRasterPipelineAlgorithm::RegisterAlgorithms(
154 : GDALAlgorithmRegistry ®istry, bool forMixedPipeline)
155 : {
156 312 : GDALAlgorithmRegistry::AlgInfo algInfo;
157 :
158 : const auto addSuffixIfNeeded =
159 1092 : [forMixedPipeline](const char *name) -> std::string
160 : {
161 1477 : return forMixedPipeline ? std::string(name).append(RASTER_SUFFIX)
162 2569 : : std::string(name);
163 156 : };
164 :
165 156 : algInfo.m_name = addSuffixIfNeeded(GDALRasterReadAlgorithm::NAME);
166 100 : algInfo.m_creationFunc = []() -> std::unique_ptr<GDALAlgorithm>
167 256 : { return std::make_unique<GDALRasterReadAlgorithm>(); };
168 156 : registry.Register(algInfo);
169 :
170 156 : registry.Register<GDALRasterCalcAlgorithm>();
171 :
172 156 : algInfo.m_name = addSuffixIfNeeded(GDALRasterWriteAlgorithm::NAME);
173 131 : algInfo.m_creationFunc = []() -> std::unique_ptr<GDALAlgorithm>
174 287 : { return std::make_unique<GDALRasterWriteAlgorithm>(); };
175 156 : registry.Register(algInfo);
176 :
177 156 : algInfo.m_name = addSuffixIfNeeded(GDALRasterInfoAlgorithm::NAME);
178 31 : algInfo.m_creationFunc = []() -> std::unique_ptr<GDALAlgorithm>
179 187 : { return std::make_unique<GDALRasterInfoAlgorithm>(); };
180 156 : registry.Register(algInfo);
181 :
182 156 : registry.Register<GDALRasterAspectAlgorithm>();
183 :
184 156 : algInfo.m_name = addSuffixIfNeeded(GDALRasterClipAlgorithm::NAME);
185 19 : algInfo.m_creationFunc = []() -> std::unique_ptr<GDALAlgorithm>
186 175 : { return std::make_unique<GDALRasterClipAlgorithm>(); };
187 156 : registry.Register(algInfo);
188 :
189 156 : registry.Register<GDALRasterColorMapAlgorithm>();
190 156 : registry.Register<GDALRasterColorMergeAlgorithm>();
191 :
192 156 : algInfo.m_name = addSuffixIfNeeded(GDALRasterEditAlgorithm::NAME);
193 33 : algInfo.m_creationFunc = []() -> std::unique_ptr<GDALAlgorithm>
194 189 : { return std::make_unique<GDALRasterEditAlgorithm>(); };
195 156 : registry.Register(algInfo);
196 :
197 156 : registry.Register<GDALRasterNoDataToAlphaAlgorithm>();
198 156 : registry.Register<GDALRasterFillNodataAlgorithm>();
199 156 : registry.Register<GDALRasterHillshadeAlgorithm>();
200 156 : registry.Register<GDALRasterMosaicAlgorithm>();
201 156 : registry.Register<GDALRasterPansharpenAlgorithm>();
202 156 : registry.Register<GDALRasterProximityAlgorithm>();
203 156 : registry.Register<GDALRasterReclassifyAlgorithm>();
204 :
205 156 : algInfo.m_name = addSuffixIfNeeded(GDALRasterReprojectAlgorithm::NAME);
206 41 : algInfo.m_creationFunc = []() -> std::unique_ptr<GDALAlgorithm>
207 197 : { return std::make_unique<GDALRasterReprojectAlgorithm>(); };
208 156 : registry.Register(algInfo);
209 :
210 156 : registry.Register<GDALRasterResizeAlgorithm>();
211 156 : registry.Register<GDALRasterRGBToPaletteAlgorithm>();
212 156 : registry.Register<GDALRasterRoughnessAlgorithm>();
213 156 : registry.Register<GDALRasterScaleAlgorithm>();
214 :
215 156 : algInfo.m_name = addSuffixIfNeeded(GDALRasterSelectAlgorithm::NAME);
216 20 : algInfo.m_creationFunc = []() -> std::unique_ptr<GDALAlgorithm>
217 176 : { return std::make_unique<GDALRasterSelectAlgorithm>(); };
218 156 : registry.Register(algInfo);
219 :
220 156 : registry.Register<GDALRasterSetTypeAlgorithm>();
221 156 : registry.Register<GDALRasterSieveAlgorithm>();
222 156 : registry.Register<GDALRasterSlopeAlgorithm>();
223 156 : registry.Register<GDALRasterStackAlgorithm>();
224 156 : registry.Register<GDALRasterTPIAlgorithm>();
225 156 : registry.Register<GDALRasterTRIAlgorithm>();
226 156 : registry.Register<GDALRasterUnscaleAlgorithm>();
227 156 : registry.Register<GDALRasterViewshedAlgorithm>();
228 156 : }
229 :
230 : /************************************************************************/
231 : /* GDALRasterPipelineAlgorithm::ParseCommandLineArguments() */
232 : /************************************************************************/
233 :
234 89 : bool GDALRasterPipelineAlgorithm::ParseCommandLineArguments(
235 : const std::vector<std::string> &args)
236 : {
237 101 : if (args.size() == 1 && (args[0] == "-h" || args[0] == "--help" ||
238 12 : args[0] == "help" || args[0] == "--json-usage"))
239 : {
240 2 : return GDALAlgorithm::ParseCommandLineArguments(args);
241 : }
242 87 : else if (args.size() == 1 && STARTS_WITH(args[0].c_str(), "--help-doc="))
243 : {
244 3 : m_helpDocCategory = args[0].substr(strlen("--help-doc="));
245 6 : return GDALAlgorithm::ParseCommandLineArguments({"--help-doc"});
246 : }
247 :
248 630 : for (const auto &arg : args)
249 : {
250 548 : if (arg.find("--pipeline") == 0)
251 2 : return GDALAlgorithm::ParseCommandLineArguments(args);
252 :
253 : // gdal raster pipeline [--quiet] "read in.tif ..."
254 547 : if (arg.find("read ") == 0)
255 1 : return GDALAlgorithm::ParseCommandLineArguments(args);
256 : }
257 :
258 82 : if (!m_steps.empty())
259 : {
260 1 : ReportError(CE_Failure, CPLE_AppDefined,
261 : "ParseCommandLineArguments() can only be called once per "
262 : "instance.");
263 1 : return false;
264 : }
265 :
266 : struct Step
267 : {
268 : std::unique_ptr<GDALRasterPipelineStepAlgorithm> alg{};
269 : std::vector<std::string> args{};
270 : };
271 :
272 162 : std::vector<Step> steps;
273 81 : steps.resize(1);
274 :
275 611 : for (const auto &arg : args)
276 : {
277 535 : if (arg == "--progress")
278 : {
279 2 : m_progressBarRequested = true;
280 2 : continue;
281 : }
282 533 : if (arg == "--quiet")
283 : {
284 0 : m_quiet = true;
285 0 : m_progressBarRequested = false;
286 0 : continue;
287 : }
288 :
289 533 : if (IsCalledFromCommandLine() && (arg == "-h" || arg == "--help"))
290 : {
291 3 : if (!steps.back().alg)
292 1 : steps.pop_back();
293 3 : if (steps.empty())
294 : {
295 5 : return GDALAlgorithm::ParseCommandLineArguments(args);
296 : }
297 : else
298 : {
299 2 : m_stepOnWhichHelpIsRequested = std::move(steps.back().alg);
300 2 : return true;
301 : }
302 : }
303 :
304 530 : auto &curStep = steps.back();
305 :
306 530 : if (arg == "!" || arg == "|")
307 : {
308 121 : if (curStep.alg)
309 : {
310 111 : steps.resize(steps.size() + 1);
311 : }
312 : }
313 : #ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
314 409 : else if (arg == "+step")
315 : {
316 4 : if (curStep.alg)
317 : {
318 2 : steps.resize(steps.size() + 1);
319 : }
320 : }
321 405 : else if (arg.find("+gdal=") == 0)
322 : {
323 3 : const std::string stepName = arg.substr(strlen("+gdal="));
324 3 : curStep.alg = GetStepAlg(stepName);
325 3 : if (!curStep.alg)
326 : {
327 1 : ReportError(CE_Failure, CPLE_AppDefined,
328 : "unknown step name: %s", stepName.c_str());
329 1 : return false;
330 : }
331 : }
332 : #endif
333 402 : else if (!curStep.alg)
334 : {
335 188 : std::string algName = arg;
336 : #ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
337 188 : if (!algName.empty() && algName[0] == '+')
338 1 : algName = algName.substr(1);
339 : #endif
340 188 : curStep.alg = GetStepAlg(algName);
341 188 : if (!curStep.alg)
342 : {
343 1 : ReportError(CE_Failure, CPLE_AppDefined,
344 : "unknown step name: %s", algName.c_str());
345 1 : return false;
346 : }
347 374 : curStep.alg->SetCallPath({std::move(algName)});
348 : }
349 : else
350 : {
351 214 : if (curStep.alg->HasSubAlgorithms())
352 : {
353 : auto subAlg = std::unique_ptr<GDALRasterPipelineStepAlgorithm>(
354 : cpl::down_cast<GDALRasterPipelineStepAlgorithm *>(
355 0 : curStep.alg->InstantiateSubAlgorithm(arg).release()));
356 0 : if (!subAlg)
357 : {
358 0 : ReportError(CE_Failure, CPLE_AppDefined,
359 : "'%s' is a unknown sub-algorithm of '%s'",
360 0 : arg.c_str(), curStep.alg->GetName().c_str());
361 0 : return false;
362 : }
363 0 : curStep.alg = std::move(subAlg);
364 0 : continue;
365 : }
366 :
367 : #ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
368 218 : if (!arg.empty() && arg[0] == '+' &&
369 4 : arg.find(' ') == std::string::npos)
370 : {
371 3 : curStep.args.push_back("--" + arg.substr(1));
372 3 : continue;
373 : }
374 : #endif
375 211 : curStep.args.push_back(arg);
376 : }
377 : }
378 :
379 : // As we initially added a step without alg to bootstrap things, make
380 : // sure to remove it if it hasn't been filled, or the user has terminated
381 : // the pipeline with a '!' separator.
382 76 : if (!steps.back().alg)
383 2 : steps.pop_back();
384 :
385 : // Automatically add a final write step if none in m_executionForStreamOutput
386 : // mode
387 89 : if (m_executionForStreamOutput && !steps.empty() &&
388 13 : steps.back().alg->GetName() != GDALRasterWriteAlgorithm::NAME)
389 : {
390 11 : steps.resize(steps.size() + 1);
391 11 : steps.back().alg = GetStepAlg(GDALRasterWriteAlgorithm::NAME);
392 11 : steps.back().args.push_back("--output-format");
393 11 : steps.back().args.push_back("stream");
394 11 : steps.back().args.push_back("streamed_dataset");
395 : }
396 :
397 76 : bool helpRequested = false;
398 76 : if (IsCalledFromCommandLine())
399 : {
400 12 : for (auto &step : steps)
401 8 : step.alg->SetCalledFromCommandLine();
402 :
403 25 : for (const std::string &v : args)
404 : {
405 21 : if (cpl::ends_with(v, "=?"))
406 1 : helpRequested = true;
407 : }
408 : }
409 :
410 76 : if (steps.size() < 2)
411 : {
412 2 : if (!steps.empty() && helpRequested)
413 : {
414 0 : steps.back().alg->ParseCommandLineArguments(steps.back().args);
415 0 : return false;
416 : }
417 :
418 2 : ReportError(CE_Failure, CPLE_AppDefined,
419 : "At least 2 steps must be provided");
420 2 : return false;
421 : }
422 :
423 148 : std::vector<GDALRasterPipelineStepAlgorithm *> stepAlgs;
424 268 : for (const auto &step : steps)
425 194 : stepAlgs.push_back(step.alg.get());
426 74 : if (!CheckFirstStep(stepAlgs))
427 2 : return false;
428 :
429 76 : if (steps.back().alg->GetName() != GDALRasterWriteAlgorithm::NAME &&
430 4 : steps.back().alg->GetName() != GDALRasterInfoAlgorithm::NAME)
431 : {
432 2 : if (helpRequested)
433 : {
434 1 : steps.back().alg->ParseCommandLineArguments(steps.back().args);
435 1 : return false;
436 : }
437 1 : ReportError(
438 : CE_Failure, CPLE_AppDefined, "Last step should be '%s' or '%s'",
439 : GDALRasterWriteAlgorithm::NAME, GDALRasterInfoAlgorithm::NAME);
440 1 : return false;
441 : }
442 184 : for (size_t i = 0; i < steps.size() - 1; ++i)
443 : {
444 229 : if (steps[i].alg->GetName() == GDALRasterWriteAlgorithm::NAME ||
445 114 : steps[i].alg->GetName() == GDALRasterInfoAlgorithm::NAME)
446 : {
447 1 : ReportError(CE_Failure, CPLE_AppDefined,
448 : "Only last step can be '%s' or '%s'",
449 : GDALRasterWriteAlgorithm::NAME,
450 : GDALRasterInfoAlgorithm::NAME);
451 1 : return false;
452 : }
453 : }
454 :
455 251 : for (auto &step : steps)
456 : {
457 182 : step.alg->SetReferencePathForRelativePaths(
458 : GetReferencePathForRelativePaths());
459 : }
460 :
461 : // Propagate input parameters set at the pipeline level to the
462 : // "read" step
463 : {
464 69 : auto &step = steps.front();
465 580 : for (auto &arg : step.alg->GetArgs())
466 : {
467 511 : if (!arg->IsHidden())
468 : {
469 441 : auto pipelineArg = GetArg(arg->GetName());
470 441 : if (pipelineArg && pipelineArg->IsExplicitlySet())
471 : {
472 4 : arg->SetSkipIfAlreadySet(true);
473 4 : arg->SetFrom(*pipelineArg);
474 : }
475 : }
476 : }
477 : }
478 :
479 : // Same with "write" step
480 : {
481 69 : auto &step = steps.back();
482 720 : for (auto &arg : step.alg->GetArgs())
483 : {
484 651 : if (!arg->IsHidden())
485 : {
486 515 : auto pipelineArg = GetArg(arg->GetName());
487 515 : if (pipelineArg && pipelineArg->IsExplicitlySet())
488 : {
489 3 : arg->SetSkipIfAlreadySet(true);
490 3 : arg->SetFrom(*pipelineArg);
491 : }
492 : }
493 : }
494 : }
495 :
496 : // Parse each step, but without running the validation
497 240 : for (const auto &step : steps)
498 : {
499 177 : step.alg->m_skipValidationInParseCommandLine = true;
500 177 : if (!step.alg->ParseCommandLineArguments(step.args))
501 6 : return false;
502 : }
503 :
504 : // Evaluate "input" argument of "read" step, together with the "output"
505 : // argument of the "write" step, in case they point to the same dataset.
506 63 : auto inputArg = steps.front().alg->GetArg(GDAL_ARG_NAME_INPUT);
507 126 : if (inputArg && inputArg->IsExplicitlySet() &&
508 189 : inputArg->GetType() == GAAT_DATASET_LIST &&
509 63 : inputArg->Get<std::vector<GDALArgDatasetValue>>().size() == 1)
510 : {
511 61 : steps.front().alg->ProcessDatasetArg(inputArg, steps.back().alg.get());
512 : }
513 :
514 222 : for (const auto &step : steps)
515 : {
516 163 : if (!step.alg->ValidateArguments())
517 4 : return false;
518 : }
519 :
520 215 : for (auto &step : steps)
521 156 : m_steps.push_back(std::move(step.alg));
522 :
523 59 : return true;
524 : }
525 :
526 : /************************************************************************/
527 : /* GDALRasterPipelineAlgorithm::GetUsageForCLI() */
528 : /************************************************************************/
529 :
530 8 : std::string GDALRasterPipelineAlgorithm::GetUsageForCLI(
531 : bool shortUsage, const UsageOptions &usageOptions) const
532 : {
533 8 : UsageOptions stepUsageOptions;
534 8 : stepUsageOptions.isPipelineStep = true;
535 :
536 8 : if (!m_helpDocCategory.empty() && m_helpDocCategory != "main")
537 : {
538 4 : auto alg = GetStepAlg(m_helpDocCategory);
539 4 : std::string ret;
540 2 : if (alg)
541 : {
542 2 : alg->SetCallPath({m_helpDocCategory});
543 1 : alg->GetArg("help-doc")->Set(true);
544 1 : return alg->GetUsageForCLI(shortUsage, stepUsageOptions);
545 : }
546 : else
547 : {
548 1 : fprintf(stderr, "ERROR: unknown pipeline step '%s'\n",
549 : m_helpDocCategory.c_str());
550 : return CPLSPrintf("ERROR: unknown pipeline step '%s'\n",
551 1 : m_helpDocCategory.c_str());
552 : }
553 : }
554 :
555 6 : UsageOptions usageOptionsMain(usageOptions);
556 6 : usageOptionsMain.isPipelineMain = true;
557 : std::string ret =
558 12 : GDALAlgorithm::GetUsageForCLI(shortUsage, usageOptionsMain);
559 6 : if (shortUsage)
560 2 : return ret;
561 :
562 : ret += "\n<PIPELINE> is of the form: read|mosaic|stack [READ-OPTIONS] "
563 4 : "( ! <STEP-NAME> [STEP-OPTIONS] )* ! write [WRITE-OPTIONS]\n";
564 :
565 4 : if (m_helpDocCategory == "main")
566 : {
567 1 : return ret;
568 : }
569 :
570 3 : ret += '\n';
571 3 : ret += "Example: 'gdal raster pipeline --progress ! read in.tif ! \\\n";
572 3 : ret += " reproject --dst-crs=EPSG:32632 ! ";
573 3 : ret += "write out.tif --overwrite'\n";
574 3 : ret += '\n';
575 3 : ret += "Potential steps are:\n";
576 :
577 93 : for (const std::string &name : m_stepRegistry.GetNames())
578 : {
579 180 : auto alg = GetStepAlg(name);
580 90 : auto [options, maxOptLen] = alg->GetArgNamesForCLI();
581 90 : stepUsageOptions.maxOptLen =
582 90 : std::max(stepUsageOptions.maxOptLen, maxOptLen);
583 : }
584 :
585 : {
586 3 : const auto name = GDALRasterReadAlgorithm::NAME;
587 3 : ret += '\n';
588 6 : auto alg = GetStepAlg(name);
589 6 : alg->SetCallPath({name});
590 3 : ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
591 : }
592 93 : for (const std::string &name : m_stepRegistry.GetNames())
593 : {
594 180 : auto alg = GetStepAlg(name);
595 90 : assert(alg);
596 102 : if (alg->CanBeFirstStep() && !alg->IsHidden() &&
597 12 : name != GDALRasterReadAlgorithm::NAME)
598 : {
599 9 : ret += '\n';
600 18 : alg->SetCallPath({name});
601 9 : ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
602 : }
603 : }
604 93 : for (const std::string &name : m_stepRegistry.GetNames())
605 : {
606 180 : auto alg = GetStepAlg(name);
607 90 : assert(alg);
608 246 : if (!alg->CanBeFirstStep() && !alg->IsHidden() &&
609 246 : name != GDALRasterWriteAlgorithm::NAME &&
610 75 : name != GDALRasterInfoAlgorithm::NAME)
611 : {
612 72 : ret += '\n';
613 144 : alg->SetCallPath({name});
614 72 : ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
615 : }
616 : }
617 6 : for (const char *name :
618 9 : {GDALRasterInfoAlgorithm::NAME, GDALRasterWriteAlgorithm::NAME})
619 : {
620 6 : ret += '\n';
621 12 : auto alg = GetStepAlg(name);
622 6 : assert(alg);
623 12 : alg->SetCallPath({name});
624 6 : ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
625 : }
626 :
627 3 : ret += GetUsageForCLIEnd();
628 :
629 3 : return ret;
630 : }
631 :
632 : /************************************************************************/
633 : /* GDALRasterPipelineNonNativelyStreamingAlgorithm() */
634 : /************************************************************************/
635 :
636 189 : GDALRasterPipelineNonNativelyStreamingAlgorithm::
637 : GDALRasterPipelineNonNativelyStreamingAlgorithm(
638 : const std::string &name, const std::string &description,
639 189 : const std::string &helpURL, bool standaloneStep)
640 : : GDALRasterPipelineStepAlgorithm(name, description, helpURL,
641 189 : standaloneStep)
642 : {
643 189 : }
644 :
645 : /************************************************************************/
646 : /* IsNativelyStreamingCompatible() */
647 : /************************************************************************/
648 :
649 41 : bool GDALRasterPipelineNonNativelyStreamingAlgorithm::
650 : IsNativelyStreamingCompatible() const
651 : {
652 41 : return false;
653 : }
654 :
655 : /************************************************************************/
656 : /* MustCreateOnDiskTempDataset() */
657 : /************************************************************************/
658 :
659 55 : static bool MustCreateOnDiskTempDataset(int nWidth, int nHeight, int nBands,
660 : GDALDataType eDT)
661 : {
662 : // Config option mostly for autotest purposes
663 55 : if (CPLTestBool(CPLGetConfigOption(
664 : "GDAL_RASTER_PIPELINE_USE_GTIFF_FOR_TEMP_DATASET", "NO")))
665 5 : return true;
666 :
667 : // Allow up to 10% of RAM usage for temporary dataset
668 50 : const auto nRAM = CPLGetUsablePhysicalRAM() / 10;
669 50 : const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
670 50 : const bool bOnDisk =
671 100 : nBands > 0 && nDTSize > 0 && nRAM > 0 &&
672 50 : static_cast<int64_t>(nWidth) * nHeight > nRAM / (nBands * nDTSize);
673 50 : return bOnDisk;
674 : }
675 :
676 : /************************************************************************/
677 : /* CreateTemporaryDataset() */
678 : /************************************************************************/
679 :
680 : std::unique_ptr<GDALDataset>
681 30 : GDALRasterPipelineNonNativelyStreamingAlgorithm::CreateTemporaryDataset(
682 : int nWidth, int nHeight, int nBands, GDALDataType eDT,
683 : bool bTiledIfPossible, GDALDataset *poSrcDSForMetadata, bool bCopyMetadata)
684 : {
685 : const bool bOnDisk =
686 30 : MustCreateOnDiskTempDataset(nWidth, nHeight, nBands, eDT);
687 30 : const char *pszDriverName = bOnDisk ? "GTIFF" : "MEM";
688 : GDALDriver *poDriver =
689 30 : GetGDALDriverManager()->GetDriverByName(pszDriverName);
690 60 : CPLStringList aosOptions;
691 60 : std::string osTmpFilename;
692 30 : if (bOnDisk)
693 : {
694 : osTmpFilename =
695 4 : CPLGenerateTempFilenameSafe(
696 : poSrcDSForMetadata
697 4 : ? CPLGetBasenameSafe(poSrcDSForMetadata->GetDescription())
698 2 : .c_str()
699 4 : : "") +
700 2 : ".tif";
701 2 : if (bTiledIfPossible)
702 2 : aosOptions.SetNameValue("TILED", "YES");
703 : const char *pszCOList =
704 2 : poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST);
705 : aosOptions.SetNameValue("COMPRESS",
706 2 : pszCOList && strstr(pszCOList, "ZSTD") ? "ZSTD"
707 4 : : "LZW");
708 2 : aosOptions.SetNameValue("SPARSE_OK", "YES");
709 : }
710 : std::unique_ptr<GDALDataset> poOutDS(
711 30 : poDriver ? poDriver->Create(osTmpFilename.c_str(), nWidth, nHeight,
712 30 : nBands, eDT, aosOptions.List())
713 60 : : nullptr);
714 30 : if (poOutDS && bOnDisk)
715 : {
716 : // In file systems that allow it (all but Windows...), we want to
717 : // delete the temporary file as soon as soon as possible after
718 : // having open it, so that if someone kills the process there are
719 : // no temp files left over. If that unlink() doesn't succeed
720 : // (on Windows), then the file will eventually be deleted when
721 : // poTmpDS is cleaned due to MarkSuppressOnClose().
722 0 : VSIUnlink(osTmpFilename.c_str());
723 0 : poOutDS->MarkSuppressOnClose();
724 : }
725 :
726 30 : if (poOutDS && poSrcDSForMetadata)
727 : {
728 28 : poOutDS->SetSpatialRef(poSrcDSForMetadata->GetSpatialRef());
729 28 : GDALGeoTransform gt;
730 28 : if (poSrcDSForMetadata->GetGeoTransform(gt) == CE_None)
731 27 : poOutDS->SetGeoTransform(gt);
732 28 : if (const int nGCPCount = poSrcDSForMetadata->GetGCPCount())
733 : {
734 0 : const auto apsGCPs = poSrcDSForMetadata->GetGCPs();
735 0 : if (apsGCPs)
736 : {
737 0 : poOutDS->SetGCPs(nGCPCount, apsGCPs,
738 0 : poSrcDSForMetadata->GetGCPSpatialRef());
739 : }
740 : }
741 28 : if (bCopyMetadata)
742 : {
743 14 : poOutDS->SetMetadata(poSrcDSForMetadata->GetMetadata());
744 : }
745 : }
746 :
747 60 : return poOutDS;
748 : }
749 :
750 : /************************************************************************/
751 : /* CreateTemporaryCopy() */
752 : /************************************************************************/
753 :
754 : std::unique_ptr<GDALDataset>
755 25 : GDALRasterPipelineNonNativelyStreamingAlgorithm::CreateTemporaryCopy(
756 : GDALAlgorithm *poAlg, GDALDataset *poSrcDS, int nSingleBand,
757 : bool bTiledIfPossible, GDALProgressFunc pfnProgress, void *pProgressData)
758 : {
759 25 : const int nBands = nSingleBand > 0 ? 1 : poSrcDS->GetRasterCount();
760 : const auto eDT =
761 25 : nBands ? poSrcDS->GetRasterBand(1)->GetRasterDataType() : GDT_Unknown;
762 25 : const bool bOnDisk = MustCreateOnDiskTempDataset(
763 : poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(), nBands, eDT);
764 25 : const char *pszDriverName = bOnDisk ? "GTIFF" : "MEM";
765 :
766 50 : CPLStringList options;
767 25 : if (nSingleBand > 0)
768 : {
769 25 : options.AddString("-b");
770 25 : options.AddString(CPLSPrintf("%d", nSingleBand));
771 : }
772 :
773 25 : options.AddString("-of");
774 25 : options.AddString(pszDriverName);
775 :
776 50 : std::string osTmpFilename;
777 25 : if (bOnDisk)
778 : {
779 : osTmpFilename =
780 3 : CPLGenerateTempFilenameSafe(
781 9 : CPLGetBasenameSafe(poSrcDS->GetDescription()).c_str()) +
782 3 : ".tif";
783 3 : if (bTiledIfPossible)
784 : {
785 3 : options.AddString("-co");
786 3 : options.AddString("TILED=YES");
787 : }
788 :
789 : GDALDriver *poDriver =
790 3 : GetGDALDriverManager()->GetDriverByName(pszDriverName);
791 : const char *pszCOList =
792 3 : poDriver ? poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST)
793 3 : : nullptr;
794 3 : options.AddString("-co");
795 3 : options.AddString(pszCOList && strstr(pszCOList, "ZSTD")
796 : ? "COMPRESS=ZSTD"
797 6 : : "COMPRESS=LZW");
798 : }
799 :
800 : GDALTranslateOptions *translateOptions =
801 25 : GDALTranslateOptionsNew(options.List(), nullptr);
802 :
803 25 : if (pfnProgress)
804 12 : GDALTranslateOptionsSetProgress(translateOptions, pfnProgress,
805 : pProgressData);
806 :
807 : std::unique_ptr<GDALDataset> poOutDS(GDALDataset::FromHandle(
808 : GDALTranslate(osTmpFilename.c_str(), GDALDataset::ToHandle(poSrcDS),
809 25 : translateOptions, nullptr)));
810 25 : GDALTranslateOptionsFree(translateOptions);
811 :
812 25 : if (!poOutDS)
813 : {
814 2 : poAlg->ReportError(CE_Failure, CPLE_AppDefined,
815 : "Failed to create temporary dataset");
816 : }
817 23 : else if (bOnDisk)
818 : {
819 : // In file systems that allow it (all but Windows...), we want to
820 : // delete the temporary file as soon as soon as possible after
821 : // having open it, so that if someone kills the process there are
822 : // no temp files left over. If that unlink() doesn't succeed
823 : // (on Windows), then the file will eventually be deleted when
824 : // poTmpDS is cleaned due to MarkSuppressOnClose().
825 1 : VSIUnlink(osTmpFilename.c_str());
826 1 : poOutDS->MarkSuppressOnClose();
827 : }
828 50 : return poOutDS;
829 : }
830 :
831 : //! @endcond
|