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

Generated by: LCOV version 1.14