LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_pipeline.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 376 382 98.4 %
Date: 2025-08-01 10:10:57 Functions: 31 32 96.9 %

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

Generated by: LCOV version 1.14