LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_pipeline.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 336 338 99.4 %
Date: 2025-03-28 11:40:40 Functions: 19 20 95.0 %

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

Generated by: LCOV version 1.14