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