LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_pipeline.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 235 242 97.1 %
Date: 2025-03-28 11:40:40 Functions: 9 9 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_astype.h"
      16             : #include "gdalalg_raster_clip.h"
      17             : #include "gdalalg_raster_edit.h"
      18             : #include "gdalalg_raster_reproject.h"
      19             : #include "gdalalg_raster_resize.h"
      20             : #include "gdalalg_raster_scale.h"
      21             : #include "gdalalg_raster_select.h"
      22             : #include "gdalalg_raster_write.h"
      23             : #include "gdalalg_raster_unscale.h"
      24             : 
      25             : #include "cpl_conv.h"
      26             : #include "cpl_string.h"
      27             : 
      28             : #include <algorithm>
      29             : 
      30             : //! @cond Doxygen_Suppress
      31             : 
      32             : #ifndef _
      33             : #define _(x) (x)
      34             : #endif
      35             : 
      36             : /************************************************************************/
      37             : /*  GDALRasterPipelineStepAlgorithm::GDALRasterPipelineStepAlgorithm()  */
      38             : /************************************************************************/
      39             : 
      40         432 : GDALRasterPipelineStepAlgorithm::GDALRasterPipelineStepAlgorithm(
      41             :     const std::string &name, const std::string &description,
      42         432 :     const std::string &helpURL, bool standaloneStep)
      43             :     : GDALAlgorithm(name, description, helpURL),
      44         432 :       m_standaloneStep(standaloneStep)
      45             : {
      46         432 :     if (m_standaloneStep)
      47             :     {
      48          69 :         m_supportsStreamedOutput = true;
      49             : 
      50          69 :         AddInputArgs(false, false);
      51          69 :         AddProgressArg();
      52          69 :         AddOutputArgs(false);
      53             :     }
      54         432 : }
      55             : 
      56             : /************************************************************************/
      57             : /*             GDALRasterPipelineStepAlgorithm::AddInputArgs()          */
      58             : /************************************************************************/
      59             : 
      60         243 : void GDALRasterPipelineStepAlgorithm::AddInputArgs(
      61             :     bool openForMixedRasterVector, bool hiddenForCLI)
      62             : {
      63         243 :     AddInputFormatsArg(&m_inputFormats)
      64             :         .AddMetadataItem(
      65             :             GAAMDI_REQUIRED_CAPABILITIES,
      66             :             openForMixedRasterVector
      67         735 :                 ? std::vector<std::string>{GDAL_DCAP_RASTER, GDAL_DCAP_VECTOR}
      68         723 :                 : std::vector<std::string>{GDAL_DCAP_RASTER})
      69         243 :         .SetHiddenForCLI(hiddenForCLI);
      70         243 :     AddOpenOptionsArg(&m_openOptions).SetHiddenForCLI(hiddenForCLI);
      71             :     AddInputDatasetArg(&m_inputDataset,
      72             :                        openForMixedRasterVector
      73             :                            ? (GDAL_OF_RASTER | GDAL_OF_VECTOR)
      74             :                            : GDAL_OF_RASTER,
      75         243 :                        /* positionalAndRequired = */ !hiddenForCLI)
      76         243 :         .SetHiddenForCLI(hiddenForCLI);
      77         243 : }
      78             : 
      79             : /************************************************************************/
      80             : /*             GDALRasterPipelineStepAlgorithm::AddOutputArgs()         */
      81             : /************************************************************************/
      82             : 
      83         240 : void GDALRasterPipelineStepAlgorithm::AddOutputArgs(bool hiddenForCLI)
      84             : {
      85             :     AddOutputFormatArg(&m_format, /* bStreamAllowed = */ true,
      86         240 :                        /* bGDALGAllowed = */ true)
      87             :         .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
      88         960 :                          {GDAL_DCAP_RASTER, GDAL_DCAP_CREATECOPY})
      89         240 :         .SetHiddenForCLI(hiddenForCLI);
      90             :     AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER,
      91         240 :                         /* positionalAndRequired = */ !hiddenForCLI)
      92         240 :         .SetHiddenForCLI(hiddenForCLI);
      93         240 :     m_outputDataset.SetInputFlags(GADV_NAME | GADV_OBJECT);
      94         240 :     AddCreationOptionsArg(&m_creationOptions).SetHiddenForCLI(hiddenForCLI);
      95         240 :     AddOverwriteArg(&m_overwrite).SetHiddenForCLI(hiddenForCLI);
      96         240 : }
      97             : 
      98             : /************************************************************************/
      99             : /*            GDALRasterPipelineStepAlgorithm::RunImpl()                */
     100             : /************************************************************************/
     101             : 
     102         230 : bool GDALRasterPipelineStepAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
     103             :                                               void *pProgressData)
     104             : {
     105         230 :     if (m_standaloneStep)
     106             :     {
     107          88 :         GDALRasterReadAlgorithm readAlg;
     108         440 :         for (auto &arg : readAlg.GetArgs())
     109             :         {
     110         396 :             auto stepArg = GetArg(arg->GetName());
     111         396 :             if (stepArg && stepArg->IsExplicitlySet())
     112             :             {
     113          44 :                 arg->SetSkipIfAlreadySet(true);
     114          44 :                 arg->SetFrom(*stepArg);
     115             :             }
     116             :         }
     117             : 
     118          44 :         GDALRasterWriteAlgorithm writeAlg;
     119         484 :         for (auto &arg : writeAlg.GetArgs())
     120             :         {
     121         440 :             auto stepArg = GetArg(arg->GetName());
     122         440 :             if (stepArg && stepArg->IsExplicitlySet())
     123             :             {
     124          80 :                 arg->SetSkipIfAlreadySet(true);
     125          80 :                 arg->SetFrom(*stepArg);
     126             :             }
     127             :         }
     128             : 
     129             :         // Already checked by GDALAlgorithm::Run()
     130          44 :         CPLAssert(!m_executionForStreamOutput ||
     131             :                   EQUAL(m_format.c_str(), "stream"));
     132             : 
     133          44 :         bool ret = false;
     134          44 :         if (readAlg.Run())
     135             :         {
     136          44 :             m_inputDataset.Set(readAlg.m_outputDataset.GetDatasetRef());
     137          44 :             m_outputDataset.Set(nullptr);
     138          44 :             if (RunStep(nullptr, nullptr))
     139             :             {
     140          37 :                 if (m_format == "stream")
     141             :                 {
     142           1 :                     ret = true;
     143             :                 }
     144             :                 else
     145             :                 {
     146          36 :                     writeAlg.m_inputDataset.Set(
     147             :                         m_outputDataset.GetDatasetRef());
     148          36 :                     if (writeAlg.Run(pfnProgress, pProgressData))
     149             :                     {
     150          36 :                         m_outputDataset.Set(
     151             :                             writeAlg.m_outputDataset.GetDatasetRef());
     152          36 :                         ret = true;
     153             :                     }
     154             :                 }
     155             :             }
     156             :         }
     157             : 
     158          44 :         return ret;
     159             :     }
     160             :     else
     161             :     {
     162         186 :         return RunStep(pfnProgress, pProgressData);
     163             :     }
     164             : }
     165             : 
     166             : /************************************************************************/
     167             : /*                          ProcessGDALGOutput()                        */
     168             : /************************************************************************/
     169             : 
     170             : GDALAlgorithm::ProcessGDALGOutputRet
     171         252 : GDALRasterPipelineStepAlgorithm::ProcessGDALGOutput()
     172             : {
     173         252 :     if (m_standaloneStep)
     174             :     {
     175          61 :         return GDALAlgorithm::ProcessGDALGOutput();
     176             :     }
     177             :     else
     178             :     {
     179             :         // GDALAbstractPipelineAlgorithm<StepAlgorithm>::RunStep() might
     180             :         // actually detect a GDALG output request and process it.
     181         191 :         return GDALAlgorithm::ProcessGDALGOutputRet::NOT_GDALG;
     182             :     }
     183             : }
     184             : 
     185             : /************************************************************************/
     186             : /*      GDALRasterPipelineStepAlgorithm::CheckSafeForStreamOutput()     */
     187             : /************************************************************************/
     188             : 
     189           7 : bool GDALRasterPipelineStepAlgorithm::CheckSafeForStreamOutput()
     190             : {
     191           7 :     if (m_standaloneStep)
     192             :     {
     193           2 :         return GDALAlgorithm::CheckSafeForStreamOutput();
     194             :     }
     195             :     else
     196             :     {
     197             :         // The check is actually done in
     198             :         // GDALAbstractPipelineAlgorithm<StepAlgorithm>::RunStep()
     199             :         // so return true for now.
     200           5 :         return true;
     201             :     }
     202             : }
     203             : 
     204             : /************************************************************************/
     205             : /*        GDALRasterPipelineAlgorithm::GDALRasterPipelineAlgorithm()    */
     206             : /************************************************************************/
     207             : 
     208          73 : GDALRasterPipelineAlgorithm::GDALRasterPipelineAlgorithm(
     209          73 :     bool openForMixedRasterVector)
     210             :     : GDALAbstractPipelineAlgorithm<GDALRasterPipelineStepAlgorithm>(
     211             :           NAME, DESCRIPTION, HELP_URL,
     212          73 :           /*standaloneStep=*/false)
     213             : {
     214          73 :     m_supportsStreamedOutput = true;
     215             : 
     216          73 :     AddInputArgs(openForMixedRasterVector, /* hiddenForCLI = */ true);
     217          73 :     AddProgressArg();
     218         146 :     AddArg("pipeline", 0, _("Pipeline string"), &m_pipeline)
     219          73 :         .SetHiddenForCLI()
     220          73 :         .SetPositional();
     221          73 :     AddOutputArgs(/* hiddenForCLI = */ true);
     222             : 
     223          73 :     m_stepRegistry.Register<GDALRasterReadAlgorithm>();
     224          73 :     m_stepRegistry.Register<GDALRasterWriteAlgorithm>();
     225          73 :     m_stepRegistry.Register<GDALRasterAsTypeAlgorithm>();
     226          73 :     m_stepRegistry.Register<GDALRasterClipAlgorithm>();
     227          73 :     m_stepRegistry.Register<GDALRasterEditAlgorithm>();
     228          73 :     m_stepRegistry.Register<GDALRasterReprojectAlgorithm>();
     229          73 :     m_stepRegistry.Register<GDALRasterResizeAlgorithm>();
     230          73 :     m_stepRegistry.Register<GDALRasterScaleAlgorithm>();
     231          73 :     m_stepRegistry.Register<GDALRasterSelectAlgorithm>();
     232          73 :     m_stepRegistry.Register<GDALRasterUnscaleAlgorithm>();
     233          73 : }
     234             : 
     235             : /************************************************************************/
     236             : /*       GDALRasterPipelineAlgorithm::ParseCommandLineArguments()       */
     237             : /************************************************************************/
     238             : 
     239          57 : bool GDALRasterPipelineAlgorithm::ParseCommandLineArguments(
     240             :     const std::vector<std::string> &args)
     241             : {
     242          69 :     if (args.size() == 1 && (args[0] == "-h" || args[0] == "--help" ||
     243          12 :                              args[0] == "help" || args[0] == "--json-usage"))
     244             :     {
     245           1 :         return GDALAlgorithm::ParseCommandLineArguments(args);
     246             :     }
     247          56 :     else if (args.size() == 1 && STARTS_WITH(args[0].c_str(), "--help-doc="))
     248             :     {
     249           3 :         m_helpDocCategory = args[0].substr(strlen("--help-doc="));
     250           6 :         return GDALAlgorithm::ParseCommandLineArguments({"--help-doc"});
     251             :     }
     252             : 
     253         404 :     for (const auto &arg : args)
     254             :     {
     255         353 :         if (arg.find("--pipeline") == 0)
     256           2 :             return GDALAlgorithm::ParseCommandLineArguments(args);
     257             : 
     258             :         // gdal raster pipeline [--progress] "read in.tif ..."
     259         352 :         if (arg.find("read ") == 0)
     260           1 :             return GDALAlgorithm::ParseCommandLineArguments(args);
     261             :     }
     262             : 
     263          51 :     if (!m_steps.empty())
     264             :     {
     265           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     266             :                     "ParseCommandLineArguments() can only be called once per "
     267             :                     "instance.");
     268           1 :         return false;
     269             :     }
     270             : 
     271             :     struct Step
     272             :     {
     273             :         std::unique_ptr<GDALRasterPipelineStepAlgorithm> alg{};
     274             :         std::vector<std::string> args{};
     275             :     };
     276             : 
     277         100 :     std::vector<Step> steps;
     278          50 :     steps.resize(1);
     279             : 
     280         388 :     for (const auto &arg : args)
     281             :     {
     282         340 :         if (arg == "--progress")
     283             :         {
     284           1 :             m_progressBarRequested = true;
     285           1 :             continue;
     286             :         }
     287             : 
     288         339 :         auto &curStep = steps.back();
     289             : 
     290         339 :         if (arg == "!" || arg == "|")
     291             :         {
     292          75 :             if (curStep.alg)
     293             :             {
     294          67 :                 steps.resize(steps.size() + 1);
     295             :             }
     296             :         }
     297             : #ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
     298         264 :         else if (arg == "+step")
     299             :         {
     300           4 :             if (curStep.alg)
     301             :             {
     302           2 :                 steps.resize(steps.size() + 1);
     303             :             }
     304             :         }
     305         260 :         else if (arg.find("+gdal=") == 0)
     306             :         {
     307           3 :             const std::string stepName = arg.substr(strlen("+gdal="));
     308           3 :             curStep.alg = GetStepAlg(stepName);
     309           3 :             if (!curStep.alg)
     310             :             {
     311           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     312             :                             "unknown step name: %s", stepName.c_str());
     313           1 :                 return false;
     314             :             }
     315             :         }
     316             : #endif
     317         257 :         else if (!curStep.alg)
     318             :         {
     319         114 :             std::string algName = arg;
     320             : #ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
     321         114 :             if (!algName.empty() && algName[0] == '+')
     322           1 :                 algName = algName.substr(1);
     323             : #endif
     324         114 :             curStep.alg = GetStepAlg(algName);
     325         114 :             if (!curStep.alg)
     326             :             {
     327           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     328             :                             "unknown step name: %s", algName.c_str());
     329           1 :                 return false;
     330             :             }
     331         226 :             curStep.alg->SetCallPath({algName});
     332             :         }
     333             :         else
     334             :         {
     335         143 :             if (curStep.alg->HasSubAlgorithms())
     336             :             {
     337             :                 auto subAlg = std::unique_ptr<GDALRasterPipelineStepAlgorithm>(
     338             :                     cpl::down_cast<GDALRasterPipelineStepAlgorithm *>(
     339           0 :                         curStep.alg->InstantiateSubAlgorithm(arg).release()));
     340           0 :                 if (!subAlg)
     341             :                 {
     342           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     343             :                                 "'%s' is a unknown sub-algorithm of '%s'",
     344           0 :                                 arg.c_str(), curStep.alg->GetName().c_str());
     345           0 :                     return false;
     346             :                 }
     347           0 :                 curStep.alg = std::move(subAlg);
     348           0 :                 continue;
     349             :             }
     350             : 
     351             : #ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
     352         147 :             if (!arg.empty() && arg[0] == '+' &&
     353           4 :                 arg.find(' ') == std::string::npos)
     354             :             {
     355           3 :                 curStep.args.push_back("--" + arg.substr(1));
     356           3 :                 continue;
     357             :             }
     358             : #endif
     359         140 :             curStep.args.push_back(arg);
     360             :         }
     361             :     }
     362             : 
     363             :     // As we initially added a step without alg to bootstrap things, make
     364             :     // sure to remove it if it hasn't been filled, or the user has terminated
     365             :     // the pipeline with a '!' separator.
     366          48 :     if (!steps.back().alg)
     367           2 :         steps.pop_back();
     368             : 
     369             :     // Automatically add a final write step if none in m_executionForStreamOutput
     370             :     // mode
     371          54 :     if (m_executionForStreamOutput && !steps.empty() &&
     372           6 :         steps.back().alg->GetName() != GDALRasterWriteAlgorithm::NAME)
     373             :     {
     374           4 :         steps.resize(steps.size() + 1);
     375           4 :         steps.back().alg = GetStepAlg(GDALRasterWriteAlgorithm::NAME);
     376           4 :         steps.back().args.push_back("--output-format");
     377           4 :         steps.back().args.push_back("stream");
     378           4 :         steps.back().args.push_back("streamed_dataset");
     379             :     }
     380             : 
     381          48 :     if (steps.size() < 2)
     382             :     {
     383           2 :         ReportError(CE_Failure, CPLE_AppDefined,
     384             :                     "At least 2 steps must be provided");
     385           2 :         return false;
     386             :     }
     387             : 
     388          46 :     if (steps.front().alg->GetName() != GDALRasterReadAlgorithm::NAME)
     389             :     {
     390           1 :         ReportError(CE_Failure, CPLE_AppDefined, "First step should be '%s'",
     391             :                     GDALRasterReadAlgorithm::NAME);
     392           1 :         return false;
     393             :     }
     394          68 :     for (size_t i = 1; i < steps.size() - 1; ++i)
     395             :     {
     396          24 :         if (steps[i].alg->GetName() == GDALRasterReadAlgorithm::NAME)
     397             :         {
     398           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     399             :                         "Only first step can be '%s'",
     400             :                         GDALRasterReadAlgorithm::NAME);
     401           1 :             return false;
     402             :         }
     403             :     }
     404          44 :     if (steps.back().alg->GetName() != GDALRasterWriteAlgorithm::NAME)
     405             :     {
     406           1 :         ReportError(CE_Failure, CPLE_AppDefined, "Last step should be '%s'",
     407             :                     GDALRasterWriteAlgorithm::NAME);
     408           1 :         return false;
     409             :     }
     410         108 :     for (size_t i = 0; i < steps.size() - 1; ++i)
     411             :     {
     412          66 :         if (steps[i].alg->GetName() == GDALRasterWriteAlgorithm::NAME)
     413             :         {
     414           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     415             :                         "Only last step can be '%s'",
     416             :                         GDALRasterWriteAlgorithm::NAME);
     417           1 :             return false;
     418             :         }
     419             :     }
     420             : 
     421         148 :     for (auto &step : steps)
     422             :     {
     423         106 :         step.alg->SetReferencePathForRelativePaths(
     424             :             GetReferencePathForRelativePaths());
     425             :     }
     426             : 
     427             :     // Propagate input parameters set at the pipeline level to the
     428             :     // "read" step
     429             :     {
     430          42 :         auto &step = steps.front();
     431         420 :         for (auto &arg : step.alg->GetArgs())
     432             :         {
     433         378 :             auto pipelineArg = GetArg(arg->GetName());
     434         378 :             if (pipelineArg && pipelineArg->IsExplicitlySet())
     435             :             {
     436           2 :                 arg->SetSkipIfAlreadySet(true);
     437           2 :                 arg->SetFrom(*pipelineArg);
     438             :             }
     439             :         }
     440             :     }
     441             : 
     442             :     // Same with "write" step
     443             :     {
     444          42 :         auto &step = steps.back();
     445         462 :         for (auto &arg : step.alg->GetArgs())
     446             :         {
     447         420 :             auto pipelineArg = GetArg(arg->GetName());
     448         420 :             if (pipelineArg && pipelineArg->IsExplicitlySet())
     449             :             {
     450           1 :                 arg->SetSkipIfAlreadySet(true);
     451           1 :                 arg->SetFrom(*pipelineArg);
     452             :             }
     453             :         }
     454             :     }
     455             : 
     456             :     // Parse each step, but without running the validation
     457         137 :     for (const auto &step : steps)
     458             :     {
     459         101 :         step.alg->m_skipValidationInParseCommandLine = true;
     460         101 :         if (!step.alg->ParseCommandLineArguments(step.args))
     461           6 :             return false;
     462             :     }
     463             : 
     464             :     // Evaluate "input" argument of "read" step, together with the "output"
     465             :     // argument of the "write" step, in case they point to the same dataset.
     466          36 :     auto inputArg = steps.front().alg->GetArg(GDAL_ARG_NAME_INPUT);
     467          72 :     if (inputArg && inputArg->IsExplicitlySet() &&
     468          36 :         inputArg->GetType() == GAAT_DATASET)
     469             :     {
     470          36 :         steps.front().alg->ProcessDatasetArg(inputArg, steps.back().alg.get());
     471             :     }
     472             : 
     473         122 :     for (const auto &step : steps)
     474             :     {
     475          88 :         if (!step.alg->ValidateArguments())
     476           2 :             return false;
     477             :     }
     478             : 
     479         120 :     for (auto &step : steps)
     480          86 :         m_steps.push_back(std::move(step.alg));
     481             : 
     482          34 :     return true;
     483             : }
     484             : 
     485             : /************************************************************************/
     486             : /*            GDALRasterPipelineAlgorithm::GetUsageForCLI()             */
     487             : /************************************************************************/
     488             : 
     489           6 : std::string GDALRasterPipelineAlgorithm::GetUsageForCLI(
     490             :     bool shortUsage, const UsageOptions &usageOptions) const
     491             : {
     492           6 :     UsageOptions stepUsageOptions;
     493           6 :     stepUsageOptions.isPipelineStep = true;
     494             : 
     495           6 :     if (!m_helpDocCategory.empty() && m_helpDocCategory != "main")
     496             :     {
     497           4 :         auto alg = GetStepAlg(m_helpDocCategory);
     498           4 :         std::string ret;
     499           2 :         if (alg)
     500             :         {
     501           2 :             alg->SetCallPath({m_helpDocCategory});
     502           1 :             alg->GetArg("help-doc")->Set(true);
     503           1 :             return alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     504             :         }
     505             :         else
     506             :         {
     507           1 :             fprintf(stderr, "ERROR: unknown pipeline step '%s'\n",
     508             :                     m_helpDocCategory.c_str());
     509             :             return CPLSPrintf("ERROR: unknown pipeline step '%s'\n",
     510           1 :                               m_helpDocCategory.c_str());
     511             :         }
     512             :     }
     513             : 
     514           8 :     std::string ret = GDALAlgorithm::GetUsageForCLI(shortUsage, usageOptions);
     515           4 :     if (shortUsage)
     516           2 :         return ret;
     517             : 
     518             :     ret += "\n<PIPELINE> is of the form: read [READ-OPTIONS] "
     519           2 :            "( ! <STEP-NAME> [STEP-OPTIONS] )* ! write [WRITE-OPTIONS]\n";
     520             : 
     521           2 :     if (m_helpDocCategory == "main")
     522             :     {
     523           1 :         return ret;
     524             :     }
     525             : 
     526           1 :     ret += '\n';
     527           1 :     ret += "Example: 'gdal raster pipeline --progress ! read in.tif ! \\\n";
     528           1 :     ret += "               reproject --dst-crs=EPSG:32632 ! ";
     529           1 :     ret += "write out.tif --overwrite'\n";
     530           1 :     ret += '\n';
     531           1 :     ret += "Potential steps are:\n";
     532             : 
     533          11 :     for (const std::string &name : m_stepRegistry.GetNames())
     534             :     {
     535          20 :         auto alg = GetStepAlg(name);
     536          10 :         auto [options, maxOptLen] = alg->GetArgNamesForCLI();
     537          10 :         stepUsageOptions.maxOptLen =
     538          10 :             std::max(stepUsageOptions.maxOptLen, maxOptLen);
     539             :     }
     540             : 
     541             :     {
     542           1 :         const auto name = GDALRasterReadAlgorithm::NAME;
     543           1 :         ret += '\n';
     544           2 :         auto alg = GetStepAlg(name);
     545           2 :         alg->SetCallPath({name});
     546           1 :         ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     547             :     }
     548          11 :     for (const std::string &name : m_stepRegistry.GetNames())
     549             :     {
     550          19 :         if (name != GDALRasterReadAlgorithm::NAME &&
     551           9 :             name != GDALRasterWriteAlgorithm::NAME)
     552             :         {
     553           8 :             ret += '\n';
     554           8 :             auto alg = GetStepAlg(name);
     555          16 :             alg->SetCallPath({name});
     556           8 :             ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     557             :         }
     558             :     }
     559             :     {
     560           1 :         const auto name = GDALRasterWriteAlgorithm::NAME;
     561           1 :         ret += '\n';
     562           2 :         auto alg = GetStepAlg(name);
     563           2 :         alg->SetCallPath({name});
     564           1 :         ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     565             :     }
     566             : 
     567           1 :     return ret;
     568             : }
     569             : 
     570             : //! @endcond

Generated by: LCOV version 1.14