LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_pipeline.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 362 365 99.2 %
Date: 2025-07-01 22:47:05 Functions: 30 31 96.8 %

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

Generated by: LCOV version 1.14