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

Generated by: LCOV version 1.14