LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_pipeline.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 365 384 95.1 %
Date: 2025-08-01 10:10:57 Functions: 20 20 100.0 %

          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 &registry, 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

Generated by: LCOV version 1.14