LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_pipeline.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 366 367 99.7 %
Date: 2025-05-15 13:16:46 Functions: 20 21 95.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "vector 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_vector_pipeline.h"
      14             : #include "gdalalg_vector_read.h"
      15             : #include "gdalalg_vector_clip.h"
      16             : #include "gdalalg_vector_concat.h"
      17             : #include "gdalalg_vector_edit.h"
      18             : #include "gdalalg_vector_filter.h"
      19             : #include "gdalalg_vector_geom.h"
      20             : #include "gdalalg_vector_reproject.h"
      21             : #include "gdalalg_vector_select.h"
      22             : #include "gdalalg_vector_sql.h"
      23             : #include "gdalalg_vector_write.h"
      24             : 
      25             : #include "cpl_conv.h"
      26             : #include "cpl_string.h"
      27             : 
      28             : #include <algorithm>
      29             : #include <cassert>
      30             : 
      31             : //! @cond Doxygen_Suppress
      32             : 
      33             : #ifndef _
      34             : #define _(x) (x)
      35             : #endif
      36             : 
      37             : /************************************************************************/
      38             : /*  GDALVectorPipelineStepAlgorithm::GDALVectorPipelineStepAlgorithm()  */
      39             : /************************************************************************/
      40             : 
      41         797 : GDALVectorPipelineStepAlgorithm::GDALVectorPipelineStepAlgorithm(
      42             :     const std::string &name, const std::string &description,
      43         797 :     const std::string &helpURL, bool standaloneStep)
      44             :     : GDALAlgorithm(name, description, helpURL),
      45         797 :       m_standaloneStep(standaloneStep)
      46             : {
      47         797 :     if (m_standaloneStep)
      48             :     {
      49         173 :         m_supportsStreamedOutput = true;
      50             : 
      51         173 :         AddInputArgs(false);
      52         173 :         AddProgressArg();
      53         173 :         AddOutputArgs(false, false);
      54             :     }
      55         797 : }
      56             : 
      57             : /************************************************************************/
      58             : /*             GDALVectorPipelineStepAlgorithm::AddInputArgs()          */
      59             : /************************************************************************/
      60             : 
      61         440 : void GDALVectorPipelineStepAlgorithm::AddInputArgs(bool hiddenForCLI)
      62             : {
      63         440 :     AddInputFormatsArg(&m_inputFormats)
      64        1320 :         .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR})
      65         440 :         .SetHiddenForCLI(hiddenForCLI);
      66         440 :     AddOpenOptionsArg(&m_openOptions).SetHiddenForCLI(hiddenForCLI);
      67             :     AddInputDatasetArg(&m_inputDataset, GDAL_OF_VECTOR,
      68         440 :                        /* positionalAndRequired = */ !hiddenForCLI)
      69         440 :         .SetMinCount(1)
      70         803 :         .SetMaxCount((GetName() == GDALVectorPipelineAlgorithm::NAME ||
      71         363 :                       GetName() == GDALVectorConcatAlgorithm::NAME)
      72             :                          ? INT_MAX
      73         803 :                          : 1)
      74         440 :         .SetHiddenForCLI(hiddenForCLI);
      75         440 :     if (GetName() != GDALVectorSQLAlgorithm::NAME)
      76             :     {
      77         862 :         AddArg("input-layer", 'l', _("Input layer name(s)"), &m_inputLayerNames)
      78         862 :             .AddAlias("layer")
      79         431 :             .SetHiddenForCLI(hiddenForCLI);
      80             :     }
      81         440 : }
      82             : 
      83             : /************************************************************************/
      84             : /*             GDALVectorPipelineStepAlgorithm::AddOutputArgs()         */
      85             : /************************************************************************/
      86             : 
      87         457 : void GDALVectorPipelineStepAlgorithm::AddOutputArgs(
      88             :     bool hiddenForCLI, bool shortNameOutputLayerAllowed)
      89             : {
      90             :     AddOutputFormatArg(&m_format, /* bStreamAllowed = */ true,
      91         457 :                        /* bGDALGAllowed = */ true)
      92             :         .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
      93        1828 :                          {GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE})
      94         457 :         .SetHiddenForCLI(hiddenForCLI);
      95             :     AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR,
      96         457 :                         /* positionalAndRequired = */ !hiddenForCLI)
      97         457 :         .SetHiddenForCLI(hiddenForCLI)
      98         457 :         .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
      99         457 :     AddCreationOptionsArg(&m_creationOptions).SetHiddenForCLI(hiddenForCLI);
     100         457 :     AddLayerCreationOptionsArg(&m_layerCreationOptions)
     101         457 :         .SetHiddenForCLI(hiddenForCLI);
     102         457 :     AddOverwriteArg(&m_overwrite).SetHiddenForCLI(hiddenForCLI);
     103         457 :     auto &updateArg = AddUpdateArg(&m_update).SetHiddenForCLI(hiddenForCLI);
     104             :     AddArg("overwrite-layer", 0,
     105             :            _("Whether overwriting existing layer is allowed"),
     106         914 :            &m_overwriteLayer)
     107         457 :         .SetDefault(false)
     108         457 :         .SetHiddenForCLI(hiddenForCLI)
     109             :         .AddValidationAction(
     110           3 :             [&updateArg]()
     111             :             {
     112           3 :                 updateArg.Set(true);
     113           3 :                 return true;
     114         457 :             });
     115             :     AddAppendUpdateArg(&m_appendLayer,
     116         457 :                        _("Whether appending to existing layer is allowed"))
     117         457 :         .SetDefault(false)
     118         457 :         .SetHiddenForCLI(hiddenForCLI);
     119         905 :     if (GetName() != GDALVectorSQLAlgorithm::NAME &&
     120         448 :         GetName() != GDALVectorConcatAlgorithm::NAME)
     121             :     {
     122             :         AddArg("output-layer", shortNameOutputLayerAllowed ? 'l' : 0,
     123         842 :                _("Output layer name"), &m_outputLayerName)
     124         842 :             .AddHiddenAlias("nln")  // For ogr2ogr nostalgic people
     125         421 :             .SetHiddenForCLI(hiddenForCLI);
     126             :     }
     127         457 : }
     128             : 
     129             : /************************************************************************/
     130             : /*            GDALVectorPipelineStepAlgorithm::RunImpl()                */
     131             : /************************************************************************/
     132             : 
     133         431 : bool GDALVectorPipelineStepAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
     134             :                                               void *pProgressData)
     135             : {
     136         431 :     if (m_standaloneStep)
     137             :     {
     138         246 :         GDALVectorReadAlgorithm readAlg;
     139        1107 :         for (auto &arg : readAlg.GetArgs())
     140             :         {
     141         984 :             auto stepArg = GetArg(arg->GetName());
     142         984 :             if (stepArg && stepArg->IsExplicitlySet())
     143             :             {
     144         123 :                 arg->SetSkipIfAlreadySet(true);
     145         123 :                 arg->SetFrom(*stepArg);
     146             :             }
     147             :         }
     148             : 
     149         123 :         GDALVectorWriteAlgorithm writeAlg;
     150        1722 :         for (auto &arg : writeAlg.GetArgs())
     151             :         {
     152        1599 :             auto stepArg = GetArg(arg->GetName());
     153        1599 :             if (stepArg && stepArg->IsExplicitlySet())
     154             :             {
     155         232 :                 arg->SetSkipIfAlreadySet(true);
     156         232 :                 arg->SetFrom(*stepArg);
     157             :             }
     158             :         }
     159             : 
     160             :         // Already checked by GDALAlgorithm::Run()
     161         123 :         CPLAssert(!m_executionForStreamOutput ||
     162             :                   EQUAL(m_format.c_str(), "stream"));
     163             : 
     164         123 :         bool ret = false;
     165         123 :         if (readAlg.Run())
     166             :         {
     167         123 :             m_inputDataset.clear();
     168         123 :             m_inputDataset.resize(1);
     169         123 :             m_inputDataset[0].Set(readAlg.m_outputDataset.GetDatasetRef());
     170         123 :             m_outputDataset.Set(nullptr);
     171         123 :             if (RunPreStepPipelineValidations() && RunStep(nullptr, nullptr))
     172             :             {
     173         102 :                 if (m_format == "stream")
     174             :                 {
     175          54 :                     ret = true;
     176             :                 }
     177             :                 else
     178             :                 {
     179          48 :                     writeAlg.m_inputDataset.clear();
     180          48 :                     writeAlg.m_inputDataset.resize(1);
     181          48 :                     writeAlg.m_inputDataset[0].Set(
     182             :                         m_outputDataset.GetDatasetRef());
     183          48 :                     if (writeAlg.Run(pfnProgress, pProgressData))
     184             :                     {
     185          48 :                         m_outputDataset.Set(
     186             :                             writeAlg.m_outputDataset.GetDatasetRef());
     187          48 :                         ret = true;
     188             :                     }
     189             :                 }
     190             :             }
     191             :         }
     192             : 
     193         123 :         return ret;
     194             :     }
     195             :     else
     196             :     {
     197         616 :         return RunPreStepPipelineValidations() &&
     198         616 :                RunStep(pfnProgress, pProgressData);
     199             :     }
     200             : }
     201             : 
     202             : /************************************************************************/
     203             : /*                          ProcessGDALGOutput()                        */
     204             : /************************************************************************/
     205             : 
     206             : GDALAlgorithm::ProcessGDALGOutputRet
     207         485 : GDALVectorPipelineStepAlgorithm::ProcessGDALGOutput()
     208             : {
     209         485 :     if (m_standaloneStep)
     210             :     {
     211         150 :         return GDALAlgorithm::ProcessGDALGOutput();
     212             :     }
     213             :     else
     214             :     {
     215             :         // GDALAbstractPipelineAlgorithm<StepAlgorithm>::RunStep() might
     216             :         // actually detect a GDALG output request and process it.
     217         335 :         return GDALAlgorithm::ProcessGDALGOutputRet::NOT_GDALG;
     218             :     }
     219             : }
     220             : 
     221             : /************************************************************************/
     222             : /*      GDALVectorPipelineStepAlgorithm::CheckSafeForStreamOutput()     */
     223             : /************************************************************************/
     224             : 
     225          16 : bool GDALVectorPipelineStepAlgorithm::CheckSafeForStreamOutput()
     226             : {
     227          16 :     if (m_standaloneStep)
     228             :     {
     229          11 :         return GDALAlgorithm::CheckSafeForStreamOutput();
     230             :     }
     231             :     else
     232             :     {
     233             :         // The check is actually done in
     234             :         // GDALAbstractPipelineAlgorithm<StepAlgorithm>::RunStep()
     235             :         // so return true for now.
     236           5 :         return true;
     237             :     }
     238             : }
     239             : 
     240             : /************************************************************************/
     241             : /*        GDALVectorPipelineAlgorithm::GDALVectorPipelineAlgorithm()    */
     242             : /************************************************************************/
     243             : 
     244          77 : GDALVectorPipelineAlgorithm::GDALVectorPipelineAlgorithm()
     245             :     : GDALAbstractPipelineAlgorithm<GDALVectorPipelineStepAlgorithm>(
     246             :           NAME, DESCRIPTION, HELP_URL,
     247          77 :           /*standaloneStep=*/false)
     248             : {
     249          77 :     m_supportsStreamedOutput = true;
     250             : 
     251          77 :     AddInputArgs(/* hiddenForCLI = */ true);
     252          77 :     AddProgressArg();
     253         154 :     AddArg("pipeline", 0, _("Pipeline string"), &m_pipeline)
     254          77 :         .SetHiddenForCLI()
     255          77 :         .SetPositional();
     256          77 :     AddOutputArgs(/* hiddenForCLI = */ true,
     257             :                   /* shortNameOutputLayerAllowed=*/false);
     258             : 
     259          77 :     m_stepRegistry.Register<GDALVectorReadAlgorithm>();
     260          77 :     m_stepRegistry.Register<GDALVectorConcatAlgorithm>();
     261          77 :     m_stepRegistry.Register<GDALVectorWriteAlgorithm>();
     262          77 :     m_stepRegistry.Register<GDALVectorClipAlgorithm>();
     263          77 :     m_stepRegistry.Register<GDALVectorEditAlgorithm>();
     264          77 :     m_stepRegistry.Register<GDALVectorReprojectAlgorithm>();
     265          77 :     m_stepRegistry.Register<GDALVectorFilterAlgorithm>();
     266          77 :     m_stepRegistry.Register<GDALVectorGeomAlgorithm>();
     267          77 :     m_stepRegistry.Register<GDALVectorSelectAlgorithm>();
     268          77 :     m_stepRegistry.Register<GDALVectorSQLAlgorithm>();
     269          77 : }
     270             : 
     271             : /************************************************************************/
     272             : /*       GDALVectorPipelineAlgorithm::ParseCommandLineArguments()       */
     273             : /************************************************************************/
     274             : 
     275          63 : bool GDALVectorPipelineAlgorithm::ParseCommandLineArguments(
     276             :     const std::vector<std::string> &args)
     277             : {
     278          75 :     if (args.size() == 1 && (args[0] == "-h" || args[0] == "--help" ||
     279          12 :                              args[0] == "help" || args[0] == "--json-usage"))
     280             :     {
     281           1 :         return GDALAlgorithm::ParseCommandLineArguments(args);
     282             :     }
     283          62 :     else if (args.size() == 1 && STARTS_WITH(args[0].c_str(), "--help-doc="))
     284             :     {
     285           3 :         m_helpDocCategory = args[0].substr(strlen("--help-doc="));
     286           6 :         return GDALAlgorithm::ParseCommandLineArguments({"--help-doc"});
     287             :     }
     288             : 
     289         408 :     for (const auto &arg : args)
     290             :     {
     291         351 :         if (arg.find("--pipeline") == 0)
     292           2 :             return GDALAlgorithm::ParseCommandLineArguments(args);
     293             : 
     294             :         // gdal vector pipeline [--progress] "read poly.gpkg ..."
     295         350 :         if (arg.find("read ") == 0)
     296           1 :             return GDALAlgorithm::ParseCommandLineArguments(args);
     297             :     }
     298             : 
     299          57 :     if (!m_steps.empty())
     300             :     {
     301           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     302             :                     "ParseCommandLineArguments() can only be called once per "
     303             :                     "instance.");
     304           1 :         return false;
     305             :     }
     306             : 
     307             :     struct Step
     308             :     {
     309             :         std::unique_ptr<GDALVectorPipelineStepAlgorithm> alg{};
     310             :         std::vector<std::string> args{};
     311             :     };
     312             : 
     313         112 :     std::vector<Step> steps;
     314          56 :     steps.resize(1);
     315             : 
     316         389 :     for (const auto &arg : args)
     317             :     {
     318         336 :         if (arg == "--progress")
     319             :         {
     320           1 :             m_progressBarRequested = true;
     321           1 :             continue;
     322             :         }
     323             : 
     324         335 :         auto &curStep = steps.back();
     325             : 
     326         335 :         if (arg == "!" || arg == "|")
     327             :         {
     328          73 :             if (curStep.alg)
     329             :             {
     330          68 :                 steps.resize(steps.size() + 1);
     331             :             }
     332             :         }
     333             : #ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
     334         262 :         else if (arg == "+step")
     335             :         {
     336           4 :             if (curStep.alg)
     337             :             {
     338           2 :                 steps.resize(steps.size() + 1);
     339             :             }
     340             :         }
     341         258 :         else if (arg.find("+gdal=") == 0)
     342             :         {
     343           3 :             const std::string stepName = arg.substr(strlen("+gdal="));
     344           3 :             curStep.alg = GetStepAlg(stepName);
     345           3 :             if (!curStep.alg)
     346             :             {
     347           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     348             :                             "unknown step name: %s", stepName.c_str());
     349           1 :                 return false;
     350             :             }
     351             :         }
     352             : #endif
     353         255 :         else if (!curStep.alg)
     354             :         {
     355         121 :             std::string algName = arg;
     356             : #ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
     357         121 :             if (!algName.empty() && algName[0] == '+')
     358           1 :                 algName = algName.substr(1);
     359             : #endif
     360         121 :             curStep.alg = GetStepAlg(algName);
     361         121 :             if (!curStep.alg)
     362             :             {
     363           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     364             :                             "unknown step name: %s", algName.c_str());
     365           1 :                 return false;
     366             :             }
     367         240 :             curStep.alg->SetCallPath({algName});
     368             :         }
     369             :         else
     370             :         {
     371         134 :             if (curStep.alg->HasSubAlgorithms())
     372             :             {
     373             :                 auto subAlg = std::unique_ptr<GDALVectorPipelineStepAlgorithm>(
     374             :                     cpl::down_cast<GDALVectorPipelineStepAlgorithm *>(
     375           3 :                         curStep.alg->InstantiateSubAlgorithm(arg).release()));
     376           3 :                 if (!subAlg)
     377             :                 {
     378           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
     379             :                                 "'%s' is a unknown sub-algorithm of '%s'",
     380           1 :                                 arg.c_str(), curStep.alg->GetName().c_str());
     381           1 :                     return false;
     382             :                 }
     383           2 :                 curStep.alg = std::move(subAlg);
     384           2 :                 continue;
     385             :             }
     386             : 
     387             : #ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
     388         135 :             if (!arg.empty() && arg[0] == '+' &&
     389           4 :                 arg.find(' ') == std::string::npos)
     390             :             {
     391           3 :                 curStep.args.push_back("--" + arg.substr(1));
     392           3 :                 continue;
     393             :             }
     394             : #endif
     395         256 :             std::string value = arg;
     396             : 
     397             : // #define GDAL_PIPELINE_NATURAL_LANGUAGE
     398             : #ifdef GDAL_PIPELINE_NATURAL_LANGUAGE
     399             :             // gdal vector pipeline "read [from] poly.gpkg, reproject [from EPSG:4326] to EPSG:32632 and write to out.gpkg with overwriting"
     400             :             if (value == "and")
     401             :             {
     402             :                 steps.resize(steps.size() + 1);
     403             :             }
     404             :             else if (value == "from" &&
     405             :                      curStep.alg->GetName() == GDALVectorReadAlgorithm::NAME)
     406             :             {
     407             :                 // do nothing
     408             :             }
     409             :             else if (value == "from" && curStep.alg->GetName() == "reproject")
     410             :             {
     411             :                 curStep.args.push_back("--src-crs");
     412             :             }
     413             :             else if (value == "to" && curStep.alg->GetName() == "reproject")
     414             :             {
     415             :                 curStep.args.push_back("--dst-crs");
     416             :             }
     417             :             else if (value == "to" &&
     418             :                      curStep.alg->GetName() == GDALVectorWriteAlgorithm::NAME)
     419             :             {
     420             :                 // do nothing
     421             :             }
     422             :             else if (value == "with" &&
     423             :                      curStep.alg->GetName() == GDALVectorWriteAlgorithm::NAME)
     424             :             {
     425             :                 // do nothing
     426             :             }
     427             :             else if (value == "overwriting" &&
     428             :                      curStep.alg->GetName() == GDALVectorWriteAlgorithm::NAME)
     429             :             {
     430             :                 curStep.args.push_back("--overwrite");
     431             :             }
     432             :             else if (!value.empty() && value.back() == ',')
     433             :             {
     434             :                 curStep.args.push_back(value.substr(0, value.size() - 1));
     435             :                 steps.resize(steps.size() + 1);
     436             :             }
     437             :             else
     438             : #endif
     439             : 
     440             :             {
     441         128 :                 curStep.args.push_back(value);
     442             :             }
     443             :         }
     444             :     }
     445             : 
     446             :     // As we initially added a step without alg to bootstrap things, make
     447             :     // sure to remove it if it hasn't been filled, or the user has terminated
     448             :     // the pipeline with a '!' separator.
     449          53 :     if (!steps.back().alg)
     450           2 :         steps.pop_back();
     451             : 
     452             :     // Automatically add a final write step if none in m_executionForStreamOutput
     453             :     // mode
     454          56 :     if (m_executionForStreamOutput && !steps.empty() &&
     455           3 :         steps.back().alg->GetName() != GDALVectorWriteAlgorithm::NAME)
     456             :     {
     457           2 :         steps.resize(steps.size() + 1);
     458           2 :         steps.back().alg = GetStepAlg(GDALVectorWriteAlgorithm::NAME);
     459           2 :         steps.back().args.push_back("--output-format");
     460           2 :         steps.back().args.push_back("stream");
     461           2 :         steps.back().args.push_back("streamed_dataset");
     462             :     }
     463             : 
     464          53 :     if (steps.size() < 2)
     465             :     {
     466           2 :         ReportError(CE_Failure, CPLE_AppDefined,
     467             :                     "At least 2 steps must be provided");
     468           2 :         return false;
     469             :     }
     470             : 
     471          53 :     if (steps.front().alg->GetName() != GDALVectorReadAlgorithm::NAME &&
     472           2 :         steps.front().alg->GetName() != GDALVectorConcatAlgorithm::NAME)
     473             :     {
     474           1 :         ReportError(
     475             :             CE_Failure, CPLE_AppDefined, "First step should be '%s' or '%s'",
     476             :             GDALVectorReadAlgorithm::NAME, GDALVectorConcatAlgorithm::NAME);
     477           1 :         return false;
     478             :     }
     479          66 :     for (size_t i = 1; i < steps.size() - 1; ++i)
     480             :     {
     481          33 :         if (steps[i].alg->GetName() == GDALVectorReadAlgorithm::NAME ||
     482          16 :             steps[i].alg->GetName() == GDALVectorConcatAlgorithm::NAME)
     483             :         {
     484           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     485             :                         "Only first step can be '%s' or '%s'",
     486             :                         GDALVectorReadAlgorithm::NAME,
     487             :                         GDALVectorConcatAlgorithm::NAME);
     488           1 :             return false;
     489             :         }
     490             :     }
     491          49 :     if (steps.back().alg->GetName() != GDALVectorWriteAlgorithm::NAME)
     492             :     {
     493           1 :         ReportError(CE_Failure, CPLE_AppDefined, "Last step should be '%s'",
     494             :                     GDALVectorWriteAlgorithm::NAME);
     495           1 :         return false;
     496             :     }
     497         111 :     for (size_t i = 0; i < steps.size() - 1; ++i)
     498             :     {
     499          64 :         if (steps[i].alg->GetName() == GDALVectorWriteAlgorithm::NAME)
     500             :         {
     501           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     502             :                         "Only last step can be '%s'",
     503             :                         GDALVectorWriteAlgorithm::NAME);
     504           1 :             return false;
     505             :         }
     506             :     }
     507             : 
     508         156 :     for (auto &step : steps)
     509             :     {
     510         109 :         step.alg->SetReferencePathForRelativePaths(
     511             :             GetReferencePathForRelativePaths());
     512             :     }
     513             : 
     514             :     // Propagate input parameters set at the pipeline level to the
     515             :     // "read" step
     516             :     {
     517          47 :         auto &step = steps.front();
     518         430 :         for (auto &arg : step.alg->GetArgs())
     519             :         {
     520         383 :             auto pipelineArg = GetArg(arg->GetName());
     521         383 :             if (pipelineArg && pipelineArg->IsExplicitlySet())
     522             :             {
     523           8 :                 arg->SetSkipIfAlreadySet(true);
     524           8 :                 arg->SetFrom(*pipelineArg);
     525             :             }
     526             :         }
     527             :     }
     528             : 
     529             :     // Same with "write" step
     530             :     {
     531          47 :         auto &step = steps.back();
     532         658 :         for (auto &arg : step.alg->GetArgs())
     533             :         {
     534         611 :             auto pipelineArg = GetArg(arg->GetName());
     535         611 :             if (pipelineArg && pipelineArg->IsExplicitlySet())
     536             :             {
     537          10 :                 arg->SetSkipIfAlreadySet(true);
     538          10 :                 arg->SetFrom(*pipelineArg);
     539             :             }
     540             :         }
     541             :     }
     542             : 
     543             :     // Parse each step, but without running the validation
     544         143 :     for (const auto &step : steps)
     545             :     {
     546         103 :         step.alg->m_skipValidationInParseCommandLine = true;
     547         103 :         if (!step.alg->ParseCommandLineArguments(step.args))
     548           7 :             return false;
     549             :     }
     550             : 
     551             :     // Evaluate "input" argument of "read" step, together with the "output"
     552             :     // argument of the "write" step, in case they point to the same dataset.
     553          40 :     auto inputArg = steps.front().alg->GetArg(GDAL_ARG_NAME_INPUT);
     554          80 :     if (inputArg && inputArg->IsExplicitlySet() &&
     555         120 :         inputArg->GetType() == GAAT_DATASET_LIST &&
     556          40 :         inputArg->Get<std::vector<GDALArgDatasetValue>>().size() == 1)
     557             :     {
     558          38 :         steps.front().alg->ProcessDatasetArg(inputArg, steps.back().alg.get());
     559             :     }
     560             : 
     561         126 :     for (const auto &step : steps)
     562             :     {
     563          89 :         if (!step.alg->ValidateArguments())
     564           3 :             return false;
     565             :     }
     566             : 
     567         121 :     for (auto &step : steps)
     568          84 :         m_steps.push_back(std::move(step.alg));
     569             : 
     570          37 :     return true;
     571             : }
     572             : 
     573             : /************************************************************************/
     574             : /*            GDALVectorPipelineAlgorithm::GetUsageForCLI()             */
     575             : /************************************************************************/
     576             : 
     577           6 : std::string GDALVectorPipelineAlgorithm::GetUsageForCLI(
     578             :     bool shortUsage, const UsageOptions &usageOptions) const
     579             : {
     580           6 :     UsageOptions stepUsageOptions;
     581           6 :     stepUsageOptions.isPipelineStep = true;
     582             : 
     583           6 :     if (!m_helpDocCategory.empty() && m_helpDocCategory != "main")
     584             :     {
     585           4 :         auto alg = GetStepAlg(m_helpDocCategory);
     586           4 :         std::string ret;
     587           2 :         if (alg)
     588             :         {
     589           2 :             alg->SetCallPath({m_helpDocCategory});
     590           1 :             alg->GetArg("help-doc")->Set(true);
     591           1 :             return alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     592             :         }
     593             :         else
     594             :         {
     595           1 :             fprintf(stderr, "ERROR: unknown pipeline step '%s'\n",
     596             :                     m_helpDocCategory.c_str());
     597             :             return CPLSPrintf("ERROR: unknown pipeline step '%s'\n",
     598           1 :                               m_helpDocCategory.c_str());
     599             :         }
     600             :     }
     601             : 
     602           4 :     UsageOptions usageOptionsMain(usageOptions);
     603           4 :     usageOptionsMain.isPipelineMain = true;
     604             :     std::string ret =
     605           8 :         GDALAlgorithm::GetUsageForCLI(shortUsage, usageOptionsMain);
     606           4 :     if (shortUsage)
     607           2 :         return ret;
     608             : 
     609             :     ret += "\n<PIPELINE> is of the form: read|concat [READ-OPTIONS] "
     610           2 :            "( ! <STEP-NAME> [STEP-OPTIONS] )* ! write [WRITE-OPTIONS]\n";
     611             : 
     612           2 :     if (m_helpDocCategory == "main")
     613             :     {
     614           1 :         return ret;
     615             :     }
     616             : 
     617           1 :     ret += '\n';
     618           1 :     ret += "Example: 'gdal vector pipeline --progress ! read in.gpkg ! \\\n";
     619           1 :     ret += "               reproject --dst-crs=EPSG:32632 ! ";
     620           1 :     ret += "write out.gpkg --overwrite'\n";
     621           1 :     ret += '\n';
     622           1 :     ret += "Potential steps are:\n";
     623             : 
     624          11 :     for (const std::string &name : m_stepRegistry.GetNames())
     625             :     {
     626          20 :         auto alg = GetStepAlg(name);
     627          10 :         assert(alg);
     628          10 :         auto [options, maxOptLen] = alg->GetArgNamesForCLI();
     629          10 :         stepUsageOptions.maxOptLen =
     630          10 :             std::max(stepUsageOptions.maxOptLen, maxOptLen);
     631             :     }
     632             : 
     633             :     {
     634           1 :         const auto name = GDALVectorReadAlgorithm::NAME;
     635           1 :         ret += '\n';
     636           2 :         auto alg = GetStepAlg(name);
     637           1 :         assert(alg);
     638           2 :         alg->SetCallPath({name});
     639           1 :         ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     640             :     }
     641             :     {
     642           1 :         const auto name = GDALVectorConcatAlgorithm::NAME;
     643           1 :         ret += '\n';
     644           2 :         auto alg = GetStepAlg(name);
     645           1 :         assert(alg);
     646           2 :         alg->SetCallPath({name});
     647           1 :         ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     648             :     }
     649          11 :     for (const std::string &name : m_stepRegistry.GetNames())
     650             :     {
     651          19 :         if (name != GDALVectorReadAlgorithm::NAME &&
     652          19 :             name != GDALVectorConcatAlgorithm::NAME &&
     653           8 :             name != GDALVectorWriteAlgorithm::NAME)
     654             :         {
     655           7 :             ret += '\n';
     656           7 :             auto alg = GetStepAlg(name);
     657           7 :             assert(alg);
     658          14 :             alg->SetCallPath({name});
     659           7 :             ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     660             :         }
     661             :     }
     662             :     {
     663           1 :         const auto name = GDALVectorWriteAlgorithm::NAME;
     664           1 :         ret += '\n';
     665           2 :         auto alg = GetStepAlg(name);
     666           1 :         assert(alg);
     667           2 :         alg->SetCallPath({name});
     668           1 :         ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     669             :     }
     670             : 
     671           1 :     ret += GetUsageForCLIEnd();
     672             : 
     673           1 :     return ret;
     674             : }
     675             : 
     676             : /************************************************************************/
     677             : /*                  GDALVectorPipelineOutputLayer                       */
     678             : /************************************************************************/
     679             : 
     680             : /************************************************************************/
     681             : /*                  GDALVectorPipelineOutputLayer()                     */
     682             : /************************************************************************/
     683             : 
     684         102 : GDALVectorPipelineOutputLayer::GDALVectorPipelineOutputLayer(OGRLayer &srcLayer)
     685         102 :     : m_srcLayer(srcLayer)
     686             : {
     687         102 : }
     688             : 
     689             : /************************************************************************/
     690             : /*             GDALVectorPipelineOutputLayer::ResetReading()            */
     691             : /************************************************************************/
     692             : 
     693         480 : void GDALVectorPipelineOutputLayer::ResetReading()
     694             : {
     695         480 :     m_srcLayer.ResetReading();
     696         480 :     m_pendingFeatures.clear();
     697         480 :     m_idxInPendingFeatures = 0;
     698         480 : }
     699             : 
     700             : /************************************************************************/
     701             : /*           GDALVectorPipelineOutputLayer::GetNextRawFeature()         */
     702             : /************************************************************************/
     703             : 
     704        1798 : OGRFeature *GDALVectorPipelineOutputLayer::GetNextRawFeature()
     705             : {
     706        1798 :     if (m_idxInPendingFeatures < m_pendingFeatures.size())
     707             :     {
     708             :         OGRFeature *poFeature =
     709          14 :             m_pendingFeatures[m_idxInPendingFeatures].release();
     710          14 :         ++m_idxInPendingFeatures;
     711          14 :         return poFeature;
     712             :     }
     713        1784 :     m_pendingFeatures.clear();
     714        1784 :     m_idxInPendingFeatures = 0;
     715             :     while (true)
     716             :     {
     717             :         auto poSrcFeature =
     718        1787 :             std::unique_ptr<OGRFeature>(m_srcLayer.GetNextFeature());
     719        1787 :         if (!poSrcFeature)
     720         180 :             return nullptr;
     721        1607 :         TranslateFeature(std::move(poSrcFeature), m_pendingFeatures);
     722        1607 :         if (!m_pendingFeatures.empty())
     723        1604 :             break;
     724           3 :     }
     725        1604 :     OGRFeature *poFeature = m_pendingFeatures[0].release();
     726        1604 :     m_idxInPendingFeatures = 1;
     727        1604 :     return poFeature;
     728             : }
     729             : 
     730             : /************************************************************************/
     731             : /*                 GDALVectorPipelineOutputDataset                      */
     732             : /************************************************************************/
     733             : 
     734             : /************************************************************************/
     735             : /*                 GDALVectorPipelineOutputDataset()                    */
     736             : /************************************************************************/
     737             : 
     738          98 : GDALVectorPipelineOutputDataset::GDALVectorPipelineOutputDataset(
     739          98 :     GDALDataset &srcDS)
     740          98 :     : m_srcDS(srcDS)
     741             : {
     742          98 :     SetDescription(m_srcDS.GetDescription());
     743          98 :     SetMetadata(m_srcDS.GetMetadata());
     744          98 : }
     745             : 
     746             : /************************************************************************/
     747             : /*            GDALVectorPipelineOutputDataset::AddLayer()               */
     748             : /************************************************************************/
     749             : 
     750         110 : void GDALVectorPipelineOutputDataset::AddLayer(
     751             :     OGRLayer &oSrcLayer,
     752             :     std::unique_ptr<OGRLayerWithTranslateFeature> poNewLayer)
     753             : {
     754         110 :     m_layersToDestroy.push_back(std::move(poNewLayer));
     755             :     OGRLayerWithTranslateFeature *poNewLayerRaw =
     756         110 :         m_layersToDestroy.back().get();
     757         110 :     m_layers.push_back(poNewLayerRaw);
     758         110 :     m_mapSrcLayerToNewLayer[&oSrcLayer] = poNewLayerRaw;
     759         110 : }
     760             : 
     761             : /************************************************************************/
     762             : /*          GDALVectorPipelineOutputDataset::GetLayerCount()            */
     763             : /************************************************************************/
     764             : 
     765         365 : int GDALVectorPipelineOutputDataset::GetLayerCount()
     766             : {
     767         365 :     return static_cast<int>(m_layers.size());
     768             : }
     769             : 
     770             : /************************************************************************/
     771             : /*             GDALVectorPipelineOutputDataset::GetLayer()              */
     772             : /************************************************************************/
     773             : 
     774         192 : OGRLayer *GDALVectorPipelineOutputDataset::GetLayer(int idx)
     775             : {
     776         192 :     return idx >= 0 && idx < GetLayerCount() ? m_layers[idx] : nullptr;
     777             : }
     778             : 
     779             : /************************************************************************/
     780             : /*           GDALVectorPipelineOutputDataset::TestCapability()          */
     781             : /************************************************************************/
     782             : 
     783          74 : int GDALVectorPipelineOutputDataset::TestCapability(const char *pszCap)
     784             : {
     785          74 :     if (EQUAL(pszCap, ODsCRandomLayerRead) ||
     786          31 :         EQUAL(pszCap, ODsCMeasuredGeometries) || EQUAL(pszCap, ODsCZGeometries))
     787             :     {
     788          58 :         return m_srcDS.TestCapability(pszCap);
     789             :     }
     790          16 :     return false;
     791             : }
     792             : 
     793             : /************************************************************************/
     794             : /*             GDALVectorPipelineOutputDataset::ResetReading()          */
     795             : /************************************************************************/
     796             : 
     797           3 : void GDALVectorPipelineOutputDataset::ResetReading()
     798             : {
     799           3 :     m_srcDS.ResetReading();
     800           3 :     m_pendingFeatures.clear();
     801           3 :     m_idxInPendingFeatures = 0;
     802           3 : }
     803             : 
     804             : /************************************************************************/
     805             : /*            GDALVectorPipelineOutputDataset::GetNextFeature()         */
     806             : /************************************************************************/
     807             : 
     808          23 : OGRFeature *GDALVectorPipelineOutputDataset::GetNextFeature(
     809             :     OGRLayer **ppoBelongingLayer, double *pdfProgressPct,
     810             :     GDALProgressFunc pfnProgress, void *pProgressData)
     811             : {
     812          23 :     if (m_idxInPendingFeatures < m_pendingFeatures.size())
     813             :     {
     814             :         OGRFeature *poFeature =
     815           3 :             m_pendingFeatures[m_idxInPendingFeatures].release();
     816           3 :         if (ppoBelongingLayer)
     817           3 :             *ppoBelongingLayer = m_belongingLayer;
     818           3 :         ++m_idxInPendingFeatures;
     819           3 :         return poFeature;
     820             :     }
     821             : 
     822          20 :     m_pendingFeatures.clear();
     823          20 :     m_idxInPendingFeatures = 0;
     824             : 
     825             :     while (true)
     826             :     {
     827          20 :         OGRLayer *poSrcBelongingLayer = nullptr;
     828          20 :         auto poSrcFeature = std::unique_ptr<OGRFeature>(m_srcDS.GetNextFeature(
     829          20 :             &poSrcBelongingLayer, pdfProgressPct, pfnProgress, pProgressData));
     830          20 :         if (!poSrcFeature)
     831           3 :             return nullptr;
     832          17 :         auto iterToDstLayer = m_mapSrcLayerToNewLayer.find(poSrcBelongingLayer);
     833          17 :         if (iterToDstLayer != m_mapSrcLayerToNewLayer.end())
     834             :         {
     835          17 :             m_belongingLayer = iterToDstLayer->second;
     836          17 :             m_belongingLayer->TranslateFeature(std::move(poSrcFeature),
     837          17 :                                                m_pendingFeatures);
     838          17 :             if (!m_pendingFeatures.empty())
     839          17 :                 break;
     840             :         }
     841           0 :     }
     842          17 :     OGRFeature *poFeature = m_pendingFeatures[0].release();
     843          17 :     if (ppoBelongingLayer)
     844          17 :         *ppoBelongingLayer = m_belongingLayer;
     845          17 :     m_idxInPendingFeatures = 1;
     846          17 :     return poFeature;
     847             : }
     848             : 
     849             : //! @endcond

Generated by: LCOV version 1.14