LCOV - code coverage report
Current view: top level - apps - gdalalg_pipeline.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 425 447 95.1 %
Date: 2025-08-01 10:10:57 Functions: 13 15 86.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "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 "cpl_error.h"
      14             : #include "cpl_error_internal.h"
      15             : #include "gdalalg_abstract_pipeline.h"
      16             : #include "gdal_priv.h"
      17             : 
      18             : #include "gdalalg_raster_read.h"
      19             : #include "gdalalg_raster_mosaic.h"
      20             : #include "gdalalg_raster_stack.h"
      21             : #include "gdalalg_raster_write.h"
      22             : 
      23             : #include "gdalalg_vector_read.h"
      24             : #include "gdalalg_vector_concat.h"
      25             : #include "gdalalg_vector_sql.h"
      26             : #include "gdalalg_vector_write.h"
      27             : 
      28             : #include "gdalalg_raster_contour.h"
      29             : #include "gdalalg_raster_footprint.h"
      30             : #include "gdalalg_raster_polygonize.h"
      31             : #include "gdalalg_raster_info.h"
      32             : #include "gdalalg_vector_grid.h"
      33             : #include "gdalalg_vector_info.h"
      34             : #include "gdalalg_vector_rasterize.h"
      35             : 
      36             : #include <algorithm>
      37             : #include <cassert>
      38             : 
      39             : //! @cond Doxygen_Suppress
      40             : 
      41             : #ifndef _
      42             : #define _(x) (x)
      43             : #endif
      44             : 
      45             : /************************************************************************/
      46             : /*                     GDALPipelineStepAlgorithm()                      */
      47             : /************************************************************************/
      48             : 
      49        3841 : GDALPipelineStepAlgorithm::GDALPipelineStepAlgorithm(
      50             :     const std::string &name, const std::string &description,
      51        3841 :     const std::string &helpURL, const ConstructorOptions &options)
      52             :     : GDALAlgorithm(name, description, helpURL),
      53        3841 :       m_standaloneStep(options.standaloneStep), m_constructorOptions(options)
      54             : {
      55        3841 : }
      56             : 
      57             : /************************************************************************/
      58             : /*       GDALPipelineStepAlgorithm::AddRasterHiddenInputDatasetArg()    */
      59             : /************************************************************************/
      60             : 
      61         904 : void GDALPipelineStepAlgorithm::AddRasterHiddenInputDatasetArg()
      62             : {
      63             :     // Added so that "band" argument validation works, because
      64             :     // GDALAlgorithm must be able to retrieve the input dataset
      65             :     AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER,
      66         904 :                        /* positionalAndRequired = */ false)
      67         904 :         .SetHidden();
      68         904 : }
      69             : 
      70             : /************************************************************************/
      71             : /*             GDALPipelineStepAlgorithm::AddRasterInputArgs()          */
      72             : /************************************************************************/
      73             : 
      74        1203 : void GDALPipelineStepAlgorithm::AddRasterInputArgs(
      75             :     bool openForMixedRasterVector, bool hiddenForCLI)
      76             : {
      77        1203 :     AddInputFormatsArg(&m_inputFormats)
      78             :         .AddMetadataItem(
      79             :             GAAMDI_REQUIRED_CAPABILITIES,
      80             :             openForMixedRasterVector
      81        3665 :                 ? std::vector<std::string>{GDAL_DCAP_RASTER, GDAL_DCAP_VECTOR}
      82        3553 :                 : std::vector<std::string>{GDAL_DCAP_RASTER})
      83        1203 :         .SetHiddenForCLI(hiddenForCLI);
      84        1203 :     AddOpenOptionsArg(&m_openOptions).SetHiddenForCLI(hiddenForCLI);
      85             :     auto &arg =
      86             :         AddInputDatasetArg(&m_inputDataset,
      87             :                            openForMixedRasterVector
      88             :                                ? (GDAL_OF_RASTER | GDAL_OF_VECTOR)
      89             :                                : GDAL_OF_RASTER,
      90        1203 :                            /* positionalAndRequired = */ !hiddenForCLI,
      91        1203 :                            m_constructorOptions.inputDatasetHelpMsg.c_str())
      92        1203 :             .SetMinCount(1)
      93        1203 :             .SetMaxCount(m_constructorOptions.inputDatasetMaxCount)
      94        1203 :             .SetAutoOpenDataset(m_constructorOptions.autoOpenInputDatasets)
      95        1203 :             .SetMetaVar(m_constructorOptions.inputDatasetMetaVar)
      96        1203 :             .SetHiddenForCLI(hiddenForCLI);
      97        1203 :     if (!m_constructorOptions.inputDatasetAlias.empty())
      98         154 :         arg.AddAlias(m_constructorOptions.inputDatasetAlias);
      99        1203 : }
     100             : 
     101             : /************************************************************************/
     102             : /*             GDALPipelineStepAlgorithm::AddRasterOutputArgs()         */
     103             : /************************************************************************/
     104             : 
     105        1028 : void GDALPipelineStepAlgorithm::AddRasterOutputArgs(bool hiddenForCLI)
     106             : {
     107        1028 :     m_outputFormatArg =
     108             :         &(AddOutputFormatArg(&m_format, /* bStreamAllowed = */ true,
     109        1028 :                              /* bGDALGAllowed = */ true)
     110             :               .AddMetadataItem(
     111             :                   GAAMDI_REQUIRED_CAPABILITIES,
     112             :                   {GDAL_DCAP_RASTER,
     113        4112 :                    m_constructorOptions.outputFormatCreateCapability.c_str()})
     114        1028 :               .SetHiddenForCLI(hiddenForCLI));
     115             :     AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER,
     116        1028 :                         /* positionalAndRequired = */ !hiddenForCLI,
     117        1028 :                         m_constructorOptions.outputDatasetHelpMsg.c_str())
     118        1028 :         .SetHiddenForCLI(hiddenForCLI)
     119        1028 :         .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
     120        1028 :     AddCreationOptionsArg(&m_creationOptions).SetHiddenForCLI(hiddenForCLI);
     121        1028 :     AddOverwriteArg(&m_overwrite).SetHiddenForCLI(hiddenForCLI);
     122        1028 : }
     123             : 
     124             : /************************************************************************/
     125             : /*             GDALPipelineStepAlgorithm::AddVectorInputArgs()         */
     126             : /************************************************************************/
     127             : 
     128         563 : void GDALPipelineStepAlgorithm::AddVectorInputArgs(bool hiddenForCLI)
     129             : {
     130         563 :     AddInputFormatsArg(&m_inputFormats)
     131        1689 :         .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR})
     132         563 :         .SetHiddenForCLI(hiddenForCLI);
     133         563 :     AddOpenOptionsArg(&m_openOptions).SetHiddenForCLI(hiddenForCLI);
     134             :     auto &datasetArg =
     135             :         AddInputDatasetArg(&m_inputDataset, GDAL_OF_VECTOR,
     136         563 :                            /* positionalAndRequired = */ !hiddenForCLI)
     137         563 :             .SetMinCount(1)
     138         563 :             .SetMaxCount(m_constructorOptions.inputDatasetMaxCount)
     139         563 :             .SetHiddenForCLI(hiddenForCLI);
     140         563 :     if (m_constructorOptions.addInputLayerNameArgument)
     141             :     {
     142             :         auto &layerArg = AddArg("input-layer", 'l', _("Input layer name(s)"),
     143        1098 :                                 &m_inputLayerNames)
     144        1098 :                              .AddAlias("layer")
     145         549 :                              .SetHiddenForCLI(hiddenForCLI);
     146         549 :         SetAutoCompleteFunctionForLayerName(layerArg, datasetArg);
     147             :     }
     148         563 : }
     149             : 
     150             : /************************************************************************/
     151             : /*             GDALPipelineStepAlgorithm::AddVectorOutputArgs()         */
     152             : /************************************************************************/
     153             : 
     154         545 : void GDALPipelineStepAlgorithm::AddVectorOutputArgs(
     155             :     bool hiddenForCLI, bool shortNameOutputLayerAllowed)
     156             : {
     157             :     AddOutputFormatArg(&m_format, /* bStreamAllowed = */ true,
     158         545 :                        /* bGDALGAllowed = */ true)
     159             :         .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
     160        2180 :                          {GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE})
     161         545 :         .SetHiddenForCLI(hiddenForCLI);
     162             :     auto &outputDatasetArg =
     163             :         AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR,
     164         545 :                             /* positionalAndRequired = */ false)
     165         545 :             .SetHiddenForCLI(hiddenForCLI)
     166         545 :             .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
     167         545 :     if (!hiddenForCLI)
     168         464 :         outputDatasetArg.SetPositional();
     169         545 :     if (!hiddenForCLI && m_constructorOptions.outputDatasetRequired)
     170         450 :         outputDatasetArg.SetRequired();
     171         545 :     if (!m_constructorOptions.outputDatasetMutualExclusionGroup.empty())
     172             :     {
     173             :         outputDatasetArg.SetMutualExclusionGroup(
     174          14 :             m_constructorOptions.outputDatasetMutualExclusionGroup);
     175             :     }
     176         545 :     AddCreationOptionsArg(&m_creationOptions).SetHiddenForCLI(hiddenForCLI);
     177         545 :     AddLayerCreationOptionsArg(&m_layerCreationOptions)
     178         545 :         .SetHiddenForCLI(hiddenForCLI);
     179         545 :     AddOverwriteArg(&m_overwrite).SetHiddenForCLI(hiddenForCLI);
     180         545 :     auto &updateArg = AddUpdateArg(&m_update).SetHiddenForCLI(hiddenForCLI);
     181         545 :     if (!m_constructorOptions.updateMutualExclusionGroup.empty())
     182             :     {
     183             :         updateArg.SetMutualExclusionGroup(
     184          14 :             m_constructorOptions.updateMutualExclusionGroup);
     185             :     }
     186         545 :     AddOverwriteLayerArg(&m_overwriteLayer).SetHiddenForCLI(hiddenForCLI);
     187         545 :     AddAppendLayerArg(&m_appendLayer).SetHiddenForCLI(hiddenForCLI);
     188        1076 :     if (GetName() != GDALVectorSQLAlgorithm::NAME &&
     189         531 :         GetName() != GDALVectorConcatAlgorithm::NAME)
     190             :     {
     191             :         AddArg("output-layer", shortNameOutputLayerAllowed ? 'l' : 0,
     192        1008 :                _("Output layer name"), &m_outputLayerName)
     193        1008 :             .AddHiddenAlias("nln")  // For ogr2ogr nostalgic people
     194         504 :             .SetHiddenForCLI(hiddenForCLI);
     195             :     }
     196         545 : }
     197             : 
     198             : /************************************************************************/
     199             : /*                 GDALPipelineStepAlgorithm::RunImpl()                 */
     200             : /************************************************************************/
     201             : 
     202        1100 : bool GDALPipelineStepAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
     203             :                                         void *pProgressData)
     204             : {
     205        1100 :     if (m_standaloneStep)
     206             :     {
     207         452 :         std::unique_ptr<GDALPipelineStepAlgorithm> readAlg;
     208         452 :         if (GetInputType() == GDAL_OF_RASTER)
     209         283 :             readAlg = std::make_unique<GDALRasterReadAlgorithm>();
     210             :         else
     211         169 :             readAlg = std::make_unique<GDALVectorReadAlgorithm>();
     212        3785 :         for (auto &arg : readAlg->GetArgs())
     213             :         {
     214        3333 :             auto stepArg = GetArg(arg->GetName());
     215        3333 :             if (stepArg && stepArg->IsExplicitlySet())
     216             :             {
     217         453 :                 arg->SetSkipIfAlreadySet(true);
     218         453 :                 arg->SetFrom(*stepArg);
     219             :             }
     220             :         }
     221             : 
     222           0 :         std::unique_ptr<GDALPipelineStepAlgorithm> writeAlg;
     223         452 :         if (GetOutputType() == GDAL_OF_RASTER)
     224             :         {
     225         283 :             if (GetName() == GDALRasterInfoAlgorithm::NAME)
     226          23 :                 writeAlg = std::make_unique<GDALRasterInfoAlgorithm>();
     227             :             else
     228         260 :                 writeAlg = std::make_unique<GDALRasterWriteAlgorithm>();
     229             :         }
     230             :         else
     231             :         {
     232         169 :             if (GetName() == GDALVectorInfoAlgorithm::NAME)
     233          23 :                 writeAlg = std::make_unique<GDALVectorInfoAlgorithm>();
     234             :             else
     235         146 :                 writeAlg = std::make_unique<GDALVectorWriteAlgorithm>();
     236             :         }
     237        5779 :         for (auto &arg : writeAlg->GetArgs())
     238             :         {
     239        5327 :             auto stepArg = GetArg(arg->GetName());
     240        5327 :             if (stepArg && stepArg->IsExplicitlySet())
     241             :             {
     242        1096 :                 arg->SetSkipIfAlreadySet(true);
     243        1096 :                 arg->SetFrom(*stepArg);
     244             :             }
     245             :         }
     246             : 
     247         452 :         const bool bIsStreaming = m_format == "stream";
     248             : 
     249             :         // Already checked by GDALAlgorithm::Run()
     250         452 :         CPLAssert(!m_executionForStreamOutput || bIsStreaming);
     251             : 
     252         452 :         bool ret = false;
     253         508 :         if (!m_outputVRTCompatible &&
     254         112 :             (EQUAL(m_format.c_str(), "VRT") ||
     255          56 :              (m_format.empty() &&
     256         453 :               EQUAL(CPLGetExtensionSafe(m_outputDataset.GetName().c_str())
     257             :                         .c_str(),
     258             :                     "VRT"))))
     259             :         {
     260           0 :             ReportError(CE_Failure, CPLE_NotSupported,
     261             :                         "VRT output is not supported. Consider using the "
     262             :                         "GDALG driver instead (files with .gdalg.json "
     263             :                         "extension)");
     264             :         }
     265         452 :         else if (readAlg->Run())
     266             :         {
     267         452 :             auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
     268             :             const bool bOutputSpecified =
     269         452 :                 outputArg && outputArg->IsExplicitlySet();
     270             : 
     271         452 :             m_inputDataset.clear();
     272         452 :             m_inputDataset.resize(1);
     273         452 :             m_inputDataset[0].Set(readAlg->m_outputDataset.GetDatasetRef());
     274         452 :             if (bOutputSpecified)
     275         403 :                 m_outputDataset.Set(nullptr);
     276             : 
     277             :             std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
     278         904 :                 pScaledData(nullptr, GDALDestroyScaledProgress);
     279             : 
     280             :             const bool bCanHandleNextStep =
     281         452 :                 !bIsStreaming && CanHandleNextStep(writeAlg.get());
     282             : 
     283         476 :             if (pfnProgress &&
     284          24 :                 (bCanHandleNextStep || !IsNativelyStreamingCompatible()))
     285             :             {
     286          36 :                 pScaledData.reset(GDALCreateScaledProgress(
     287          18 :                     0.0, bIsStreaming || bCanHandleNextStep ? 1.0 : 0.5,
     288             :                     pfnProgress, pProgressData));
     289             :             }
     290             : 
     291         452 :             GDALPipelineStepRunContext stepCtxt;
     292         452 :             stepCtxt.m_pfnProgress = pScaledData ? GDALScaledProgress : nullptr;
     293         452 :             stepCtxt.m_pProgressData = pScaledData.get();
     294         452 :             if (bCanHandleNextStep)
     295          43 :                 stepCtxt.m_poNextUsableStep = writeAlg.get();
     296         452 :             if (RunPreStepPipelineValidations() && RunStep(stepCtxt))
     297             :             {
     298         375 :                 if (bIsStreaming || bCanHandleNextStep || !bOutputSpecified)
     299             :                 {
     300         176 :                     ret = true;
     301             :                 }
     302             :                 else
     303             :                 {
     304         199 :                     writeAlg->m_outputVRTCompatible = m_outputVRTCompatible;
     305         199 :                     writeAlg->m_inputDataset.clear();
     306         199 :                     writeAlg->m_inputDataset.resize(1);
     307         199 :                     writeAlg->m_inputDataset[0].Set(
     308             :                         m_outputDataset.GetDatasetRef());
     309         199 :                     if (pfnProgress)
     310             :                     {
     311          19 :                         pScaledData.reset(GDALCreateScaledProgress(
     312          19 :                             IsNativelyStreamingCompatible() ? 0.0 : 0.5, 1.0,
     313             :                             pfnProgress, pProgressData));
     314             :                     }
     315         199 :                     stepCtxt.m_pfnProgress =
     316         199 :                         pScaledData ? GDALScaledProgress : nullptr;
     317         199 :                     stepCtxt.m_pProgressData = pScaledData.get();
     318         398 :                     if (writeAlg->ValidateArguments() &&
     319         199 :                         writeAlg->RunStep(stepCtxt))
     320             :                     {
     321         197 :                         if (pfnProgress)
     322          19 :                             pfnProgress(1.0, "", pProgressData);
     323             : 
     324         197 :                         m_outputDataset.Set(
     325         197 :                             writeAlg->m_outputDataset.GetDatasetRef());
     326         197 :                         ret = true;
     327             :                     }
     328             :                 }
     329             :             }
     330             :         }
     331             : 
     332         452 :         return ret;
     333             :     }
     334             :     else
     335             :     {
     336         648 :         GDALPipelineStepRunContext stepCtxt;
     337         648 :         stepCtxt.m_pfnProgress = pfnProgress;
     338         648 :         stepCtxt.m_pProgressData = pProgressData;
     339         648 :         return RunPreStepPipelineValidations() && RunStep(stepCtxt);
     340             :     }
     341             : }
     342             : 
     343             : /************************************************************************/
     344             : /*                          ProcessGDALGOutput()                        */
     345             : /************************************************************************/
     346             : 
     347             : GDALAlgorithm::ProcessGDALGOutputRet
     348        1470 : GDALPipelineStepAlgorithm::ProcessGDALGOutput()
     349             : {
     350        1470 :     if (m_standaloneStep)
     351             :     {
     352         765 :         return GDALAlgorithm::ProcessGDALGOutput();
     353             :     }
     354             :     else
     355             :     {
     356             :         // GDALAbstractPipelineAlgorithm<StepAlgorithm>::RunStep() might
     357             :         // actually detect a GDALG output request and process it.
     358         705 :         return GDALAlgorithm::ProcessGDALGOutputRet::NOT_GDALG;
     359             :     }
     360             : }
     361             : 
     362             : /************************************************************************/
     363             : /*          GDALPipelineStepAlgorithm::CheckSafeForStreamOutput()       */
     364             : /************************************************************************/
     365             : 
     366          49 : bool GDALPipelineStepAlgorithm::CheckSafeForStreamOutput()
     367             : {
     368          49 :     if (m_standaloneStep)
     369             :     {
     370          29 :         return GDALAlgorithm::CheckSafeForStreamOutput();
     371             :     }
     372             :     else
     373             :     {
     374             :         // The check is actually done in
     375             :         // GDALAbstractPipelineAlgorithm<StepAlgorithm>::RunStep()
     376             :         // so return true for now.
     377          20 :         return true;
     378             :     }
     379             : }
     380             : 
     381             : /************************************************************************/
     382             : /*                       GDALPipelineAlgorithm                          */
     383             : /************************************************************************/
     384             : 
     385             : class GDALPipelineAlgorithm final
     386             :     : public GDALAbstractPipelineAlgorithm<GDALPipelineStepAlgorithm>
     387             : 
     388             : {
     389             :   public:
     390             :     static constexpr const char *NAME = "pipeline";
     391             :     static constexpr const char *DESCRIPTION = "Execute a pipeline.";
     392             :     static constexpr const char *HELP_URL = "/programs/gdal_pipeline.html";
     393             : 
     394          55 :     GDALPipelineAlgorithm()
     395          55 :         : GDALAbstractPipelineAlgorithm(
     396             :               NAME, DESCRIPTION, HELP_URL,
     397          55 :               ConstructorOptions().SetStandaloneStep(false))
     398             :     {
     399          55 :         m_supportsStreamedOutput = true;
     400             : 
     401          55 :         AddProgressArg();
     402             :         AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER | GDAL_OF_VECTOR,
     403          55 :                            /* positionalAndRequired = */ false)
     404          55 :             .SetMinCount(1)
     405          55 :             .SetMaxCount(INT_MAX)
     406          55 :             .SetHiddenForCLI();
     407             :         AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER | GDAL_OF_VECTOR,
     408          55 :                             /* positionalAndRequired = */ false)
     409          55 :             .SetHiddenForCLI()
     410          55 :             .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
     411             :         AddOutputFormatArg(&m_format, /* bStreamAllowed = */ true,
     412          55 :                            /* bGDALGAllowed = */ true)
     413          55 :             .SetHiddenForCLI();
     414         110 :         AddArg("pipeline", 0, _("Pipeline string"), &m_pipeline)
     415          55 :             .SetHiddenForCLI()
     416          55 :             .SetPositional();
     417             : 
     418          55 :         AddOutputStringArg(&m_output).SetHiddenForCLI();
     419             :         AddArg(
     420             :             "stdout", 0,
     421             :             _("Directly output on stdout (format=text mode only). If enabled, "
     422             :               "output-string will be empty"),
     423         110 :             &m_stdout)
     424          55 :             .SetHidden();
     425             : 
     426          55 :         GDALRasterPipelineAlgorithm::RegisterAlgorithms(m_stepRegistry, true);
     427          55 :         GDALVectorPipelineAlgorithm::RegisterAlgorithms(m_stepRegistry, true);
     428          55 :         m_stepRegistry.Register<GDALRasterContourAlgorithm>();
     429          55 :         m_stepRegistry.Register<GDALRasterFootprintAlgorithm>();
     430          55 :         m_stepRegistry.Register<GDALRasterPolygonizeAlgorithm>();
     431          55 :         m_stepRegistry.Register<GDALVectorGridAlgorithm>();
     432          55 :         m_stepRegistry.Register<GDALVectorRasterizeAlgorithm>();
     433          55 :     }
     434             : 
     435             :     // Declared to satisfy GDALPipelineStepAlgorithm, but not called as this
     436             :     // class is not an actual step, hence return value is "random"
     437           0 :     int GetInputType() const override
     438             :     {
     439           0 :         CPLAssert(false);
     440             :         return 0;
     441             :     }
     442             : 
     443             :     // Declared to satisfy GDALPipelineStepAlgorithm, but not called as this
     444             :     // class is not an actual step, hence return value is "random"
     445           0 :     int GetOutputType() const override
     446             :     {
     447           0 :         CPLAssert(false);
     448             :         return 0;
     449             :     }
     450             : 
     451             :   protected:
     452             :     bool
     453             :     ParseCommandLineArguments(const std::vector<std::string> &args) override;
     454             : 
     455             :     std::string GetUsageForCLI(bool shortUsage,
     456             :                                const UsageOptions &usageOptions) const override;
     457             : };
     458             : 
     459             : /************************************************************************/
     460             : /*           GDALPipelineAlgorithm::ParseCommandLineArguments()         */
     461             : /************************************************************************/
     462             : 
     463          39 : bool GDALPipelineAlgorithm::ParseCommandLineArguments(
     464             :     const std::vector<std::string> &args)
     465             : {
     466          47 :     if (args.size() == 1 && (args[0] == "-h" || args[0] == "--help" ||
     467           8 :                              args[0] == "help" || args[0] == "--json-usage"))
     468             :     {
     469           1 :         return GDALAlgorithm::ParseCommandLineArguments(args);
     470             :     }
     471          38 :     else if (args.size() == 1 && STARTS_WITH(args[0].c_str(), "--help-doc="))
     472             :     {
     473           3 :         m_helpDocCategory = args[0].substr(strlen("--help-doc="));
     474           6 :         return GDALAlgorithm::ParseCommandLineArguments({"--help-doc"});
     475             :     }
     476             : 
     477         220 :     for (const auto &arg : args)
     478             :     {
     479         186 :         if (arg.find("--pipeline") == 0)
     480           1 :             return GDALAlgorithm::ParseCommandLineArguments(args);
     481             : 
     482             :         // gdal pipeline [--quiet] "read poly.gpkg ..."
     483         186 :         if (arg.find("read ") == 0)
     484           1 :             return GDALAlgorithm::ParseCommandLineArguments(args);
     485             :     }
     486             : 
     487          34 :     if (!m_steps.empty())
     488             :     {
     489           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     490             :                     "ParseCommandLineArguments() can only be called once per "
     491             :                     "instance.");
     492           1 :         return false;
     493             :     }
     494             : 
     495             :     struct Step
     496             :     {
     497             :         std::unique_ptr<GDALPipelineStepAlgorithm> alg{};
     498             :         std::vector<std::string> args{};
     499             :         bool alreadyChangedType = false;
     500             :         bool isSubAlgorithm = false;
     501             :     };
     502             : 
     503          66 :     std::vector<Step> steps;
     504          33 :     steps.resize(1);
     505             : 
     506         210 :     for (const auto &arg : args)
     507             :     {
     508         178 :         if (arg == "--progress")
     509             :         {
     510           1 :             m_progressBarRequested = true;
     511           1 :             continue;
     512             :         }
     513         177 :         if (arg == "--quiet")
     514             :         {
     515           0 :             m_quiet = true;
     516           0 :             m_progressBarRequested = false;
     517           0 :             continue;
     518             :         }
     519             : 
     520         177 :         if (IsCalledFromCommandLine() && (arg == "-h" || arg == "--help"))
     521             :         {
     522           0 :             if (!steps.back().alg)
     523           0 :                 steps.pop_back();
     524           0 :             if (steps.empty())
     525             :             {
     526           1 :                 return GDALAlgorithm::ParseCommandLineArguments(args);
     527             :             }
     528             :             else
     529             :             {
     530           0 :                 m_stepOnWhichHelpIsRequested = std::move(steps.back().alg);
     531           0 :                 return true;
     532             :             }
     533             :         }
     534             : 
     535         177 :         auto &curStep = steps.back();
     536             : 
     537         177 :         if (arg == "!" || arg == "|")
     538             :         {
     539          42 :             if (curStep.alg)
     540             :             {
     541          42 :                 steps.resize(steps.size() + 1);
     542             :             }
     543             :         }
     544         135 :         else if (!curStep.alg)
     545             :         {
     546          75 :             std::string algName = arg;
     547          75 :             if (algName == "read")
     548             :             {
     549          31 :                 curStep.alg = std::make_unique<GDALRasterReadAlgorithm>(true);
     550             :             }
     551             :             else
     552             :             {
     553          44 :                 curStep.alg = GetStepAlg(algName);
     554          44 :                 if (!curStep.alg)
     555          35 :                     curStep.alg = GetStepAlg(algName + RASTER_SUFFIX);
     556             :             }
     557          75 :             if (!curStep.alg)
     558             :             {
     559           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     560             :                             "unknown step name: %s", algName.c_str());
     561           1 :                 return false;
     562             :             }
     563         148 :             curStep.alg->SetCallPath({std::move(algName)});
     564          74 :             curStep.alg->SetReferencePathForRelativePaths(
     565             :                 GetReferencePathForRelativePaths());
     566             :         }
     567             :         else
     568             :         {
     569          60 :             if (curStep.alg->HasSubAlgorithms())
     570             :             {
     571             :                 auto subAlg = std::unique_ptr<GDALPipelineStepAlgorithm>(
     572             :                     cpl::down_cast<GDALPipelineStepAlgorithm *>(
     573           1 :                         curStep.alg->InstantiateSubAlgorithm(arg).release()));
     574           1 :                 if (!subAlg)
     575             :                 {
     576           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     577             :                                 "'%s' is a unknown sub-algorithm of '%s'",
     578           0 :                                 arg.c_str(), curStep.alg->GetName().c_str());
     579           0 :                     return false;
     580             :                 }
     581           1 :                 curStep.isSubAlgorithm = true;
     582           1 :                 curStep.alg = std::move(subAlg);
     583           1 :                 continue;
     584             :             }
     585             : 
     586          59 :             curStep.args.push_back(arg);
     587             :         }
     588             :     }
     589             : 
     590             :     // As we initially added a step without alg to bootstrap things, make
     591             :     // sure to remove it if it hasn't been filled, or the user has terminated
     592             :     // the pipeline with a '!' separator.
     593          32 :     if (!steps.back().alg)
     594           0 :         steps.pop_back();
     595             : 
     596             :     // Automatically add a final write step if none in m_executionForStreamOutput
     597             :     // mode
     598          34 :     if (m_executionForStreamOutput && !steps.empty() &&
     599           2 :         steps.back().alg->GetName() !=
     600          34 :             std::string(GDALRasterWriteAlgorithm::NAME).append(RASTER_SUFFIX))
     601             :     {
     602           2 :         steps.resize(steps.size() + 1);
     603           4 :         steps.back().alg = GetStepAlg(
     604           6 :             std::string(GDALRasterWriteAlgorithm::NAME).append(RASTER_SUFFIX));
     605           2 :         steps.back().args.push_back("--output-format");
     606           2 :         steps.back().args.push_back("stream");
     607           2 :         steps.back().args.push_back("streamed_dataset");
     608             :     }
     609             : 
     610          32 :     bool helpRequested = false;
     611          32 :     if (IsCalledFromCommandLine())
     612             :     {
     613          21 :         for (auto &step : steps)
     614          14 :             step.alg->SetCalledFromCommandLine();
     615             : 
     616          41 :         for (const std::string &v : args)
     617             :         {
     618          34 :             if (cpl::ends_with(v, "=?"))
     619           1 :                 helpRequested = true;
     620             :         }
     621             :     }
     622             : 
     623          32 :     if (steps.size() < 2)
     624             :     {
     625           1 :         if (!steps.empty() && helpRequested)
     626             :         {
     627           0 :             steps.back().alg->ParseCommandLineArguments(steps.back().args);
     628           0 :             return false;
     629             :         }
     630             : 
     631           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     632             :                     "At least 2 steps must be provided");
     633           1 :         return false;
     634             :     }
     635             : 
     636          62 :     std::vector<GDALPipelineStepAlgorithm *> stepAlgs;
     637         106 :     for (const auto &step : steps)
     638          75 :         stepAlgs.push_back(step.alg.get());
     639          31 :     if (!CheckFirstStep(stepAlgs))
     640           2 :         return false;  // CheckFirstStep emits an error
     641             : 
     642          35 :     if (steps.back().alg->GetName() != GDALRasterWriteAlgorithm::NAME &&
     643           6 :         steps.back().alg->GetName() != GDALRasterInfoAlgorithm::NAME)
     644             :     {
     645           2 :         if (helpRequested)
     646             :         {
     647           1 :             steps.back().alg->ParseCommandLineArguments(steps.back().args);
     648           1 :             return false;
     649             :         }
     650           1 :         ReportError(
     651             :             CE_Failure, CPLE_AppDefined, "Last step should be '%s' or '%s'",
     652             :             GDALRasterWriteAlgorithm::NAME, GDALRasterInfoAlgorithm::NAME);
     653           1 :         return false;
     654             :     }
     655          65 :     for (size_t i = 0; i < steps.size() - 1; ++i)
     656             :     {
     657          77 :         if (steps[i].alg->GetName() == GDALRasterWriteAlgorithm::NAME ||
     658          38 :             steps[i].alg->GetName() == GDALRasterInfoAlgorithm::NAME)
     659             :         {
     660           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     661             :                         "Only last step can be '%s' or '%s'",
     662             :                         GDALRasterWriteAlgorithm::NAME,
     663             :                         GDALRasterInfoAlgorithm::NAME);
     664           1 :             return false;
     665             :         }
     666             :     }
     667             : 
     668             :     // Propagate input parameters set at the pipeline level to the
     669             :     // "read" step
     670             :     {
     671          26 :         auto &step = steps.front();
     672         217 :         for (auto &arg : step.alg->GetArgs())
     673             :         {
     674         191 :             if (!arg->IsHidden())
     675             :             {
     676         164 :                 auto pipelineArg = GetArg(arg->GetName());
     677         164 :                 if (pipelineArg && pipelineArg->IsExplicitlySet())
     678             :                 {
     679           6 :                     arg->SetSkipIfAlreadySet(true);
     680           6 :                     arg->SetFrom(*pipelineArg);
     681             :                 }
     682             :             }
     683             :         }
     684             :     }
     685             : 
     686             :     // Same with "write" step
     687         538 :     const auto SetWriteArgFromPipeline = [this, &steps]()
     688             :     {
     689          47 :         auto &step = steps.back();
     690         616 :         for (auto &arg : step.alg->GetArgs())
     691             :         {
     692         569 :             if (!arg->IsHidden())
     693             :             {
     694         491 :                 auto pipelineArg = GetArg(arg->GetName());
     695         491 :                 if (pipelineArg && pipelineArg->IsExplicitlySet())
     696             :                 {
     697          28 :                     arg->SetSkipIfAlreadySet(true);
     698          28 :                     arg->SetFrom(*pipelineArg);
     699             :                 }
     700             :             }
     701             :         }
     702          47 :     };
     703             : 
     704          26 :     SetWriteArgFromPipeline();
     705             : 
     706             :     // Parse each step, but without running the validation
     707          85 :     for (auto &step : steps)
     708             :     {
     709             :         bool ret;
     710          61 :         CPLErrorAccumulator oAccumulator;
     711             :         {
     712             :             [[maybe_unused]] auto context =
     713          61 :                 oAccumulator.InstallForCurrentScope();
     714          61 :             step.alg->m_skipValidationInParseCommandLine = true;
     715          61 :             ret = step.alg->ParseCommandLineArguments(step.args);
     716             :         }
     717          61 :         if (!ret)
     718             :         {
     719           3 :             auto alg = GetStepAlg(step.alg->GetName() + VECTOR_SUFFIX);
     720           3 :             if (alg)
     721             :             {
     722           2 :                 step.alg = std::move(alg);
     723           2 :                 step.alg->m_skipValidationInParseCommandLine = true;
     724           2 :                 ret = step.alg->ParseCommandLineArguments(step.args);
     725           2 :                 if (!ret)
     726           1 :                     return false;
     727           2 :                 step.alg->SetCallPath({step.alg->GetName()});
     728           1 :                 step.alg->SetReferencePathForRelativePaths(
     729             :                     GetReferencePathForRelativePaths());
     730           1 :                 step.alreadyChangedType = true;
     731             :             }
     732             :             else
     733             :             {
     734           2 :                 for (const auto &sError : oAccumulator.GetErrors())
     735             :                 {
     736           1 :                     CPLError(sError.type, sError.no, "%s", sError.msg.c_str());
     737             :                 }
     738           1 :                 return false;
     739             :             }
     740             :         }
     741             :     }
     742             : 
     743             :     // Evaluate "input" argument of "read" step, together with the "output"
     744             :     // argument of the "write" step, in case they point to the same dataset.
     745          24 :     auto inputArg = steps.front().alg->GetArg(GDAL_ARG_NAME_INPUT);
     746          48 :     if (inputArg && inputArg->IsExplicitlySet() &&
     747          72 :         inputArg->GetType() == GAAT_DATASET_LIST &&
     748          24 :         inputArg->Get<std::vector<GDALArgDatasetValue>>().size() == 1)
     749             :     {
     750          24 :         steps.front().alg->ProcessDatasetArg(inputArg, steps.back().alg.get());
     751             :     }
     752             : 
     753          24 :     int nLastStepOutputType = GDAL_OF_VECTOR;
     754          24 :     if (steps.front().alg->GetName() !=
     755          49 :             std::string(GDALRasterReadAlgorithm::NAME) &&
     756           1 :         steps.front().alg->GetOutputType() == GDAL_OF_RASTER)
     757             :     {
     758           1 :         nLastStepOutputType = GDAL_OF_RASTER;
     759             :     }
     760             :     else
     761             :     {
     762          23 :         auto poSrcDS = steps.front().alg->GetInputDatasets()[0].GetDatasetRef();
     763          23 :         if (poSrcDS)
     764             :         {
     765          23 :             if (poSrcDS->GetRasterCount() != 0)
     766          12 :                 nLastStepOutputType = GDAL_OF_RASTER;
     767             :         }
     768             :     }
     769             : 
     770          52 :     for (size_t i = 1; i < steps.size(); ++i)
     771             :     {
     772          60 :         if (!steps[i].alreadyChangedType && !steps[i].isSubAlgorithm &&
     773          60 :             GetStepAlg(steps[i].alg->GetName()) == nullptr)
     774             :         {
     775          46 :             steps[i].alg = GetStepAlg(steps[i].alg->GetName() +
     776             :                                       (nLastStepOutputType == GDAL_OF_RASTER
     777             :                                            ? RASTER_SUFFIX
     778          23 :                                            : VECTOR_SUFFIX));
     779          23 :             CPLAssert(steps[i].alg);
     780             : 
     781          23 :             if (i == steps.size() - 1)
     782             :             {
     783          21 :                 SetWriteArgFromPipeline();
     784             :             }
     785             : 
     786          23 :             steps[i].alg->m_skipValidationInParseCommandLine = true;
     787          23 :             if (!steps[i].alg->ParseCommandLineArguments(steps[i].args))
     788           1 :                 return false;
     789          44 :             steps[i].alg->SetCallPath({steps[i].alg->GetName()});
     790          22 :             steps[i].alg->SetReferencePathForRelativePaths(
     791             :                 GetReferencePathForRelativePaths());
     792          22 :             if (IsCalledFromCommandLine())
     793           6 :                 steps[i].alg->SetCalledFromCommandLine();
     794          22 :             steps[i].alreadyChangedType = true;
     795             :         }
     796           8 :         else if (steps[i].alg->GetInputType() != nLastStepOutputType)
     797             :         {
     798           8 :             ReportError(
     799             :                 CE_Failure, CPLE_AppDefined,
     800             :                 "Step '%s' expects a %s input dataset, but previous step '%s' "
     801             :                 "generates a %s output dataset",
     802           2 :                 steps[i].alg->GetName().c_str(),
     803           2 :                 steps[i].alg->GetInputType() == GDAL_OF_RASTER ? "raster"
     804             :                                                                : "vector",
     805           2 :                 steps[i - 1].alg->GetName().c_str(),
     806             :                 nLastStepOutputType == GDAL_OF_RASTER ? "raster" : "vector");
     807           2 :             return false;
     808             :         }
     809          28 :         nLastStepOutputType = steps[i].alg->GetOutputType();
     810             :     }
     811             : 
     812          70 :     for (const auto &step : steps)
     813             :     {
     814          49 :         if (!step.alg->ValidateArguments())
     815           0 :             return false;
     816             :     }
     817             : 
     818          70 :     for (auto &step : steps)
     819          49 :         m_steps.push_back(std::move(step.alg));
     820             : 
     821          21 :     return true;
     822             : }
     823             : 
     824             : /************************************************************************/
     825             : /*               GDALPipelineAlgorithm::GetUsageForCLI()                */
     826             : /************************************************************************/
     827             : 
     828             : std::string
     829           4 : GDALPipelineAlgorithm::GetUsageForCLI(bool shortUsage,
     830             :                                       const UsageOptions &usageOptions) const
     831             : {
     832           4 :     UsageOptions stepUsageOptions;
     833           4 :     stepUsageOptions.isPipelineStep = true;
     834             : 
     835           4 :     if (!m_helpDocCategory.empty() && m_helpDocCategory != "main")
     836             :     {
     837           4 :         auto alg = GetStepAlg(m_helpDocCategory);
     838           4 :         std::string ret;
     839           2 :         if (alg)
     840             :         {
     841           3 :             alg->SetCallPath({CPLString(m_helpDocCategory)
     842           2 :                                   .replaceAll(RASTER_SUFFIX, "")
     843           3 :                                   .replaceAll(VECTOR_SUFFIX, "")});
     844           1 :             alg->GetArg("help-doc")->Set(true);
     845           1 :             return alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     846             :         }
     847             :         else
     848             :         {
     849           1 :             fprintf(stderr, "ERROR: unknown pipeline step '%s'\n",
     850             :                     m_helpDocCategory.c_str());
     851             :             return CPLSPrintf("ERROR: unknown pipeline step '%s'\n",
     852           1 :                               m_helpDocCategory.c_str());
     853             :         }
     854             :     }
     855             : 
     856           2 :     UsageOptions usageOptionsMain(usageOptions);
     857           2 :     usageOptionsMain.isPipelineMain = true;
     858             :     std::string ret =
     859           4 :         GDALAlgorithm::GetUsageForCLI(shortUsage, usageOptionsMain);
     860           2 :     if (shortUsage)
     861           0 :         return ret;
     862             : 
     863             :     ret += "\n<PIPELINE> is of the form: read|calc|concat|mosaic|stack "
     864             :            "[READ-OPTIONS] "
     865           2 :            "( ! <STEP-NAME> [STEP-OPTIONS] )* ! write [WRITE-OPTIONS]\n";
     866             : 
     867           2 :     if (m_helpDocCategory == "main")
     868             :     {
     869           1 :         return ret;
     870             :     }
     871             : 
     872           1 :     ret += '\n';
     873           1 :     ret += "Example: 'gdal pipeline --progress ! read in.tif ! \\\n";
     874           1 :     ret += "               rasterize --size 256 256 ! buffer 20 ! ";
     875           1 :     ret += "write out.gpkg --overwrite'\n";
     876           1 :     ret += '\n';
     877           1 :     ret += "Potential steps are:\n";
     878             : 
     879          56 :     for (const std::string &name : m_stepRegistry.GetNames())
     880             :     {
     881         110 :         auto alg = GetStepAlg(name);
     882          55 :         assert(alg);
     883          55 :         auto [options, maxOptLen] = alg->GetArgNamesForCLI();
     884          55 :         stepUsageOptions.maxOptLen =
     885          55 :             std::max(stepUsageOptions.maxOptLen, maxOptLen);
     886             :     }
     887             : 
     888             :     {
     889           1 :         ret += '\n';
     890           1 :         auto alg = std::make_unique<GDALRasterReadAlgorithm>();
     891           2 :         alg->SetCallPath({alg->GetName()});
     892           1 :         ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     893             :     }
     894             :     {
     895           1 :         ret += '\n';
     896           1 :         auto alg = std::make_unique<GDALVectorReadAlgorithm>();
     897           2 :         alg->SetCallPath({alg->GetName()});
     898           1 :         ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     899             :     }
     900          56 :     for (const std::string &name : m_stepRegistry.GetNames())
     901             :     {
     902         110 :         auto alg = GetStepAlg(name);
     903          55 :         assert(alg);
     904          61 :         if (alg->CanBeFirstStep() && !alg->IsHidden() &&
     905           6 :             !STARTS_WITH(name.c_str(), GDALRasterReadAlgorithm::NAME))
     906             :         {
     907           4 :             ret += '\n';
     908           8 :             alg->SetCallPath({name});
     909           4 :             ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     910             :         }
     911             :     }
     912          56 :     for (const std::string &name : m_stepRegistry.GetNames())
     913             :     {
     914         110 :         auto alg = GetStepAlg(name);
     915          55 :         assert(alg);
     916         103 :         if (!alg->CanBeFirstStep() && !alg->IsHidden() &&
     917          48 :             !STARTS_WITH(name.c_str(), GDALRasterWriteAlgorithm::NAME))
     918             :         {
     919          46 :             ret += '\n';
     920         138 :             alg->SetCallPath({CPLString(alg->GetName())
     921          92 :                                   .replaceAll(RASTER_SUFFIX, "")
     922         138 :                                   .replaceAll(VECTOR_SUFFIX, "")});
     923          46 :             ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     924             :         }
     925             :     }
     926           4 :     for (const std::string &name :
     927           2 :          {std::string(GDALRasterInfoAlgorithm::NAME) + RASTER_SUFFIX,
     928           2 :           std::string(GDALRasterWriteAlgorithm::NAME) + RASTER_SUFFIX,
     929           2 :           std::string(GDALVectorInfoAlgorithm::NAME) + VECTOR_SUFFIX,
     930          15 :           std::string(GDALVectorWriteAlgorithm::NAME) + VECTOR_SUFFIX})
     931             :     {
     932           4 :         ret += '\n';
     933           4 :         auto alg = GetStepAlg(name);
     934           4 :         assert(alg);
     935          12 :         alg->SetCallPath({CPLString(alg->GetName())
     936           8 :                               .replaceAll(RASTER_SUFFIX, "")
     937          12 :                               .replaceAll(VECTOR_SUFFIX, "")});
     938           4 :         ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     939             :     }
     940             : 
     941           1 :     ret += GetUsageForCLIEnd();
     942             : 
     943           1 :     return ret;
     944             : }
     945             : 
     946             : GDAL_STATIC_REGISTER_ALG(GDALPipelineAlgorithm);
     947             : 
     948             : //! @endcond

Generated by: LCOV version 1.14