LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_pipeline.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 361 376 96.0 %
Date: 2025-05-15 13:16:46 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "raster 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_raster_pipeline.h"
      14             : #include "gdalalg_raster_read.h"
      15             : #include "gdalalg_raster_aspect.h"
      16             : #include "gdalalg_raster_clip.h"
      17             : #include "gdalalg_raster_color_map.h"
      18             : #include "gdalalg_raster_edit.h"
      19             : #include "gdalalg_raster_fill_nodata.h"
      20             : #include "gdalalg_raster_hillshade.h"
      21             : #include "gdalalg_raster_proximity.h"
      22             : #include "gdalalg_raster_reclassify.h"
      23             : #include "gdalalg_raster_reproject.h"
      24             : #include "gdalalg_raster_resize.h"
      25             : #include "gdalalg_raster_rgb_to_palette.h"
      26             : #include "gdalalg_raster_roughness.h"
      27             : #include "gdalalg_raster_scale.h"
      28             : #include "gdalalg_raster_select.h"
      29             : #include "gdalalg_raster_set_type.h"
      30             : #include "gdalalg_raster_sieve.h"
      31             : #include "gdalalg_raster_slope.h"
      32             : #include "gdalalg_raster_write.h"
      33             : #include "gdalalg_raster_tpi.h"
      34             : #include "gdalalg_raster_tri.h"
      35             : #include "gdalalg_raster_unscale.h"
      36             : #include "gdalalg_raster_viewshed.h"
      37             : 
      38             : #include "cpl_conv.h"
      39             : #include "cpl_progress.h"
      40             : #include "cpl_string.h"
      41             : #include "cpl_vsi.h"
      42             : #include "gdal_priv.h"
      43             : #include "gdal_utils.h"
      44             : 
      45             : #include <algorithm>
      46             : #include <array>
      47             : 
      48             : //! @cond Doxygen_Suppress
      49             : 
      50             : #ifndef _
      51             : #define _(x) (x)
      52             : #endif
      53             : 
      54             : /************************************************************************/
      55             : /*  GDALRasterPipelineStepAlgorithm::GDALRasterPipelineStepAlgorithm()  */
      56             : /************************************************************************/
      57             : 
      58        1147 : GDALRasterPipelineStepAlgorithm::GDALRasterPipelineStepAlgorithm(
      59             :     const std::string &name, const std::string &description,
      60        1147 :     const std::string &helpURL, bool standaloneStep)
      61             :     : GDALAlgorithm(name, description, helpURL),
      62        1147 :       m_standaloneStep(standaloneStep)
      63             : {
      64        1147 :     if (m_standaloneStep)
      65             :     {
      66         289 :         m_supportsStreamedOutput = true;
      67             : 
      68         289 :         AddInputArgs(false, false);
      69         289 :         AddProgressArg();
      70         289 :         AddOutputArgs(false);
      71             :     }
      72        1623 :     else if (name != GDALRasterPipelineAlgorithm::NAME &&
      73         765 :              name != GDALRasterReadAlgorithm::NAME)
      74             :     {
      75             :         AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER,
      76         476 :                            /* positionalAndRequired = */ false)
      77         476 :             .SetHidden();
      78             :     }
      79        1147 : }
      80             : 
      81             : /************************************************************************/
      82             : /*             GDALRasterPipelineStepAlgorithm::AddInputArgs()          */
      83             : /************************************************************************/
      84             : 
      85         671 : void GDALRasterPipelineStepAlgorithm::AddInputArgs(
      86             :     bool openForMixedRasterVector, bool hiddenForCLI)
      87             : {
      88         671 :     AddInputFormatsArg(&m_inputFormats)
      89             :         .AddMetadataItem(
      90             :             GAAMDI_REQUIRED_CAPABILITIES,
      91             :             openForMixedRasterVector
      92        2019 :                 ? std::vector<std::string>{GDAL_DCAP_RASTER, GDAL_DCAP_VECTOR}
      93        2007 :                 : std::vector<std::string>{GDAL_DCAP_RASTER})
      94         671 :         .SetHiddenForCLI(hiddenForCLI);
      95         671 :     AddOpenOptionsArg(&m_openOptions).SetHiddenForCLI(hiddenForCLI);
      96             :     AddInputDatasetArg(&m_inputDataset,
      97             :                        openForMixedRasterVector
      98             :                            ? (GDAL_OF_RASTER | GDAL_OF_VECTOR)
      99             :                            : GDAL_OF_RASTER,
     100         671 :                        /* positionalAndRequired = */ !hiddenForCLI)
     101         671 :         .SetHiddenForCLI(hiddenForCLI);
     102         671 : }
     103             : 
     104             : /************************************************************************/
     105             : /*             GDALRasterPipelineStepAlgorithm::AddOutputArgs()         */
     106             : /************************************************************************/
     107             : 
     108         668 : void GDALRasterPipelineStepAlgorithm::AddOutputArgs(bool hiddenForCLI)
     109             : {
     110         668 :     m_outputFormatArg =
     111             :         &(AddOutputFormatArg(&m_format, /* bStreamAllowed = */ true,
     112         668 :                              /* bGDALGAllowed = */ true)
     113             :               .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
     114        2672 :                                {GDAL_DCAP_RASTER, GDAL_DCAP_CREATECOPY})
     115         668 :               .SetHiddenForCLI(hiddenForCLI));
     116             :     AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER,
     117         668 :                         /* positionalAndRequired = */ !hiddenForCLI)
     118         668 :         .SetHiddenForCLI(hiddenForCLI)
     119         668 :         .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
     120         668 :     AddCreationOptionsArg(&m_creationOptions).SetHiddenForCLI(hiddenForCLI);
     121         668 :     AddOverwriteArg(&m_overwrite).SetHiddenForCLI(hiddenForCLI);
     122         668 : }
     123             : 
     124             : /************************************************************************/
     125             : /*        GDALRasterPipelineStepAlgorithm::SetOutputVRTCompatible()     */
     126             : /************************************************************************/
     127             : 
     128         124 : void GDALRasterPipelineStepAlgorithm::SetOutputVRTCompatible(bool b)
     129             : {
     130         124 :     m_outputVRTCompatible = b;
     131         124 :     if (m_outputFormatArg)
     132             :     {
     133          76 :         m_outputFormatArg->AddMetadataItem(GAAMDI_VRT_COMPATIBLE,
     134         152 :                                            {b ? "true" : "false"});
     135             :     }
     136         124 : }
     137             : 
     138             : /************************************************************************/
     139             : /*            GDALRasterPipelineStepAlgorithm::RunImpl()                */
     140             : /************************************************************************/
     141             : 
     142         721 : bool GDALRasterPipelineStepAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
     143             :                                               void *pProgressData)
     144             : {
     145         721 :     if (m_standaloneStep)
     146             :     {
     147         424 :         GDALRasterReadAlgorithm readAlg;
     148        1696 :         for (auto &arg : readAlg.GetArgs())
     149             :         {
     150        1484 :             auto stepArg = GetArg(arg->GetName());
     151        1484 :             if (stepArg && stepArg->IsExplicitlySet())
     152             :             {
     153         212 :                 arg->SetSkipIfAlreadySet(true);
     154         212 :                 arg->SetFrom(*stepArg);
     155             :             }
     156             :         }
     157             : 
     158         212 :         GDALRasterWriteAlgorithm writeAlg;
     159        2120 :         for (auto &arg : writeAlg.GetArgs())
     160             :         {
     161        1908 :             auto stepArg = GetArg(arg->GetName());
     162        1908 :             if (stepArg && stepArg->IsExplicitlySet())
     163             :             {
     164         588 :                 arg->SetSkipIfAlreadySet(true);
     165         588 :                 arg->SetFrom(*stepArg);
     166             :             }
     167             :         }
     168             : 
     169         212 :         const bool bIsStreaming = m_format == "stream";
     170             : 
     171             :         // Already checked by GDALAlgorithm::Run()
     172         212 :         CPLAssert(!m_executionForStreamOutput || bIsStreaming);
     173             : 
     174         212 :         bool ret = false;
     175         212 :         if (readAlg.Run())
     176             :         {
     177         212 :             m_inputDataset.Set(readAlg.m_outputDataset.GetDatasetRef());
     178         212 :             m_outputDataset.Set(nullptr);
     179             : 
     180             :             std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
     181         424 :                 pScaledData(nullptr, GDALDestroyScaledProgress);
     182         212 :             if (!IsNativelyStreamingCompatible())
     183             :             {
     184          69 :                 pScaledData.reset(GDALCreateScaledProgress(
     185             :                     0.0, bIsStreaming ? 1.0 : 0.5, pfnProgress, pProgressData));
     186             :             }
     187             : 
     188         424 :             if (RunPreStepPipelineValidations() &&
     189         212 :                 RunStep(pScaledData ? GDALScaledProgress : nullptr,
     190         212 :                         pScaledData.get()))
     191             :             {
     192         180 :                 if (bIsStreaming)
     193             :                 {
     194           8 :                     ret = true;
     195             :                 }
     196         210 :                 else if (!m_outputVRTCompatible &&
     197          76 :                          (EQUAL(m_format.c_str(), "VRT") ||
     198          38 :                           (m_format.empty() &&
     199         172 :                            EQUAL(CPLGetExtensionSafe(
     200             :                                      writeAlg.m_outputDataset.GetName().c_str())
     201             :                                      .c_str(),
     202             :                                  "VRT"))))
     203             :                 {
     204           0 :                     ReportError(
     205             :                         CE_Failure, CPLE_NotSupported,
     206             :                         "VRT output is not supported. Consider using the "
     207             :                         "GDALG driver instead (files with .gdalg.json "
     208             :                         "extension)");
     209           0 :                     ret = false;
     210             :                 }
     211             :                 else
     212             :                 {
     213         172 :                     writeAlg.m_outputVRTCompatible = m_outputVRTCompatible;
     214         172 :                     writeAlg.m_inputDataset.Set(
     215             :                         m_outputDataset.GetDatasetRef());
     216         172 :                     if (pfnProgress)
     217             :                     {
     218          20 :                         pScaledData.reset(GDALCreateScaledProgress(
     219          20 :                             IsNativelyStreamingCompatible() ? 0.0 : 0.5, 1.0,
     220             :                             pfnProgress, pProgressData));
     221             :                     }
     222         172 :                     if (writeAlg.Run(pScaledData ? GDALScaledProgress : nullptr,
     223             :                                      pScaledData.get()))
     224             :                     {
     225         171 :                         if (pfnProgress)
     226          20 :                             pfnProgress(1.0, "", pProgressData);
     227             : 
     228         171 :                         m_outputDataset.Set(
     229             :                             writeAlg.m_outputDataset.GetDatasetRef());
     230         171 :                         ret = true;
     231             :                     }
     232             :                 }
     233             :             }
     234             :         }
     235             : 
     236         212 :         return ret;
     237             :     }
     238             :     else
     239             :     {
     240        1018 :         return RunPreStepPipelineValidations() &&
     241        1018 :                RunStep(pfnProgress, pProgressData);
     242             :     }
     243             : }
     244             : 
     245             : /************************************************************************/
     246             : /*                          ProcessGDALGOutput()                        */
     247             : /************************************************************************/
     248             : 
     249             : GDALAlgorithm::ProcessGDALGOutputRet
     250         755 : GDALRasterPipelineStepAlgorithm::ProcessGDALGOutput()
     251             : {
     252         755 :     if (m_standaloneStep)
     253             :     {
     254         239 :         return GDALAlgorithm::ProcessGDALGOutput();
     255             :     }
     256             :     else
     257             :     {
     258             :         // GDALAbstractPipelineAlgorithm<StepAlgorithm>::RunStep() might
     259             :         // actually detect a GDALG output request and process it.
     260         516 :         return GDALAlgorithm::ProcessGDALGOutputRet::NOT_GDALG;
     261             :     }
     262             : }
     263             : 
     264             : /************************************************************************/
     265             : /*      GDALRasterPipelineStepAlgorithm::CheckSafeForStreamOutput()     */
     266             : /************************************************************************/
     267             : 
     268          20 : bool GDALRasterPipelineStepAlgorithm::CheckSafeForStreamOutput()
     269             : {
     270          20 :     if (m_standaloneStep)
     271             :     {
     272           9 :         return GDALAlgorithm::CheckSafeForStreamOutput();
     273             :     }
     274             :     else
     275             :     {
     276             :         // The check is actually done in
     277             :         // GDALAbstractPipelineAlgorithm<StepAlgorithm>::RunStep()
     278             :         // so return true for now.
     279          11 :         return true;
     280             :     }
     281             : }
     282             : 
     283             : /************************************************************************/
     284             : /*        GDALRasterPipelineAlgorithm::GDALRasterPipelineAlgorithm()    */
     285             : /************************************************************************/
     286             : 
     287          93 : GDALRasterPipelineAlgorithm::GDALRasterPipelineAlgorithm(
     288          93 :     bool openForMixedRasterVector)
     289             :     : GDALAbstractPipelineAlgorithm<GDALRasterPipelineStepAlgorithm>(
     290             :           NAME, DESCRIPTION, HELP_URL,
     291          93 :           /*standaloneStep=*/false)
     292             : {
     293          93 :     m_supportsStreamedOutput = true;
     294             : 
     295          93 :     AddInputArgs(openForMixedRasterVector, /* hiddenForCLI = */ true);
     296          93 :     AddProgressArg();
     297         186 :     AddArg("pipeline", 0, _("Pipeline string"), &m_pipeline)
     298          93 :         .SetHiddenForCLI()
     299          93 :         .SetPositional();
     300          93 :     AddOutputArgs(/* hiddenForCLI = */ true);
     301             : 
     302          93 :     m_stepRegistry.Register<GDALRasterReadAlgorithm>();
     303          93 :     m_stepRegistry.Register<GDALRasterWriteAlgorithm>();
     304          93 :     m_stepRegistry.Register<GDALRasterAspectAlgorithm>();
     305          93 :     m_stepRegistry.Register<GDALRasterClipAlgorithm>();
     306          93 :     m_stepRegistry.Register<GDALRasterColorMapAlgorithm>();
     307          93 :     m_stepRegistry.Register<GDALRasterEditAlgorithm>();
     308          93 :     m_stepRegistry.Register<GDALRasterFillNodataAlgorithm>();
     309          93 :     m_stepRegistry.Register<GDALRasterHillshadeAlgorithm>();
     310          93 :     m_stepRegistry.Register<GDALRasterProximityAlgorithm>();
     311          93 :     m_stepRegistry.Register<GDALRasterReclassifyAlgorithm>();
     312          93 :     m_stepRegistry.Register<GDALRasterReprojectAlgorithm>();
     313          93 :     m_stepRegistry.Register<GDALRasterResizeAlgorithm>();
     314          93 :     m_stepRegistry.Register<GDALRasterRGBToPaletteAlgorithm>();
     315          93 :     m_stepRegistry.Register<GDALRasterRoughnessAlgorithm>();
     316          93 :     m_stepRegistry.Register<GDALRasterScaleAlgorithm>();
     317          93 :     m_stepRegistry.Register<GDALRasterSelectAlgorithm>();
     318          93 :     m_stepRegistry.Register<GDALRasterSetTypeAlgorithm>();
     319          93 :     m_stepRegistry.Register<GDALRasterSieveAlgorithm>();
     320          93 :     m_stepRegistry.Register<GDALRasterSlopeAlgorithm>();
     321          93 :     m_stepRegistry.Register<GDALRasterTPIAlgorithm>();
     322          93 :     m_stepRegistry.Register<GDALRasterTRIAlgorithm>();
     323          93 :     m_stepRegistry.Register<GDALRasterUnscaleAlgorithm>();
     324          93 :     m_stepRegistry.Register<GDALRasterViewshedAlgorithm>();
     325          93 : }
     326             : 
     327             : /************************************************************************/
     328             : /*       GDALRasterPipelineAlgorithm::ParseCommandLineArguments()       */
     329             : /************************************************************************/
     330             : 
     331          77 : bool GDALRasterPipelineAlgorithm::ParseCommandLineArguments(
     332             :     const std::vector<std::string> &args)
     333             : {
     334          89 :     if (args.size() == 1 && (args[0] == "-h" || args[0] == "--help" ||
     335          12 :                              args[0] == "help" || args[0] == "--json-usage"))
     336             :     {
     337           1 :         return GDALAlgorithm::ParseCommandLineArguments(args);
     338             :     }
     339          76 :     else if (args.size() == 1 && STARTS_WITH(args[0].c_str(), "--help-doc="))
     340             :     {
     341           3 :         m_helpDocCategory = args[0].substr(strlen("--help-doc="));
     342           6 :         return GDALAlgorithm::ParseCommandLineArguments({"--help-doc"});
     343             :     }
     344             : 
     345         553 :     for (const auto &arg : args)
     346             :     {
     347         482 :         if (arg.find("--pipeline") == 0)
     348           2 :             return GDALAlgorithm::ParseCommandLineArguments(args);
     349             : 
     350             :         // gdal raster pipeline [--progress] "read in.tif ..."
     351         481 :         if (arg.find("read ") == 0)
     352           1 :             return GDALAlgorithm::ParseCommandLineArguments(args);
     353             :     }
     354             : 
     355          71 :     if (!m_steps.empty())
     356             :     {
     357           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     358             :                     "ParseCommandLineArguments() can only be called once per "
     359             :                     "instance.");
     360           1 :         return false;
     361             :     }
     362             : 
     363             :     struct Step
     364             :     {
     365             :         std::unique_ptr<GDALRasterPipelineStepAlgorithm> alg{};
     366             :         std::vector<std::string> args{};
     367             :     };
     368             : 
     369         140 :     std::vector<Step> steps;
     370          70 :     steps.resize(1);
     371             : 
     372         537 :     for (const auto &arg : args)
     373             :     {
     374         469 :         if (arg == "--progress")
     375             :         {
     376           1 :             m_progressBarRequested = true;
     377           1 :             continue;
     378             :         }
     379             : 
     380         468 :         auto &curStep = steps.back();
     381             : 
     382         468 :         if (arg == "!" || arg == "|")
     383             :         {
     384         108 :             if (curStep.alg)
     385             :             {
     386          98 :                 steps.resize(steps.size() + 1);
     387             :             }
     388             :         }
     389             : #ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
     390         360 :         else if (arg == "+step")
     391             :         {
     392           4 :             if (curStep.alg)
     393             :             {
     394           2 :                 steps.resize(steps.size() + 1);
     395             :             }
     396             :         }
     397         356 :         else if (arg.find("+gdal=") == 0)
     398             :         {
     399           3 :             const std::string stepName = arg.substr(strlen("+gdal="));
     400           3 :             curStep.alg = GetStepAlg(stepName);
     401           3 :             if (!curStep.alg)
     402             :             {
     403           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     404             :                             "unknown step name: %s", stepName.c_str());
     405           1 :                 return false;
     406             :             }
     407             :         }
     408             : #endif
     409         353 :         else if (!curStep.alg)
     410             :         {
     411         165 :             std::string algName = arg;
     412             : #ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
     413         165 :             if (!algName.empty() && algName[0] == '+')
     414           1 :                 algName = algName.substr(1);
     415             : #endif
     416         165 :             curStep.alg = GetStepAlg(algName);
     417         165 :             if (!curStep.alg)
     418             :             {
     419           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     420             :                             "unknown step name: %s", algName.c_str());
     421           1 :                 return false;
     422             :             }
     423         328 :             curStep.alg->SetCallPath({algName});
     424             :         }
     425             :         else
     426             :         {
     427         188 :             if (curStep.alg->HasSubAlgorithms())
     428             :             {
     429             :                 auto subAlg = std::unique_ptr<GDALRasterPipelineStepAlgorithm>(
     430             :                     cpl::down_cast<GDALRasterPipelineStepAlgorithm *>(
     431           0 :                         curStep.alg->InstantiateSubAlgorithm(arg).release()));
     432           0 :                 if (!subAlg)
     433             :                 {
     434           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     435             :                                 "'%s' is a unknown sub-algorithm of '%s'",
     436           0 :                                 arg.c_str(), curStep.alg->GetName().c_str());
     437           0 :                     return false;
     438             :                 }
     439           0 :                 curStep.alg = std::move(subAlg);
     440           0 :                 continue;
     441             :             }
     442             : 
     443             : #ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
     444         192 :             if (!arg.empty() && arg[0] == '+' &&
     445           4 :                 arg.find(' ') == std::string::npos)
     446             :             {
     447           3 :                 curStep.args.push_back("--" + arg.substr(1));
     448           3 :                 continue;
     449             :             }
     450             : #endif
     451         185 :             curStep.args.push_back(arg);
     452             :         }
     453             :     }
     454             : 
     455             :     // As we initially added a step without alg to bootstrap things, make
     456             :     // sure to remove it if it hasn't been filled, or the user has terminated
     457             :     // the pipeline with a '!' separator.
     458          68 :     if (!steps.back().alg)
     459           2 :         steps.pop_back();
     460             : 
     461             :     // Automatically add a final write step if none in m_executionForStreamOutput
     462             :     // mode
     463          81 :     if (m_executionForStreamOutput && !steps.empty() &&
     464          13 :         steps.back().alg->GetName() != GDALRasterWriteAlgorithm::NAME)
     465             :     {
     466          11 :         steps.resize(steps.size() + 1);
     467          11 :         steps.back().alg = GetStepAlg(GDALRasterWriteAlgorithm::NAME);
     468          11 :         steps.back().args.push_back("--output-format");
     469          11 :         steps.back().args.push_back("stream");
     470          11 :         steps.back().args.push_back("streamed_dataset");
     471             :     }
     472             : 
     473          68 :     if (steps.size() < 2)
     474             :     {
     475           2 :         ReportError(CE_Failure, CPLE_AppDefined,
     476             :                     "At least 2 steps must be provided");
     477           2 :         return false;
     478             :     }
     479             : 
     480          66 :     if (steps.front().alg->GetName() != GDALRasterReadAlgorithm::NAME)
     481             :     {
     482           1 :         ReportError(CE_Failure, CPLE_AppDefined, "First step should be '%s'",
     483             :                     GDALRasterReadAlgorithm::NAME);
     484           1 :         return false;
     485             :     }
     486         106 :     for (size_t i = 1; i < steps.size() - 1; ++i)
     487             :     {
     488          42 :         if (steps[i].alg->GetName() == GDALRasterReadAlgorithm::NAME)
     489             :         {
     490           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     491             :                         "Only first step can be '%s'",
     492             :                         GDALRasterReadAlgorithm::NAME);
     493           1 :             return false;
     494             :         }
     495             :     }
     496          64 :     if (steps.back().alg->GetName() != GDALRasterWriteAlgorithm::NAME)
     497             :     {
     498           1 :         ReportError(CE_Failure, CPLE_AppDefined, "Last step should be '%s'",
     499             :                     GDALRasterWriteAlgorithm::NAME);
     500           1 :         return false;
     501             :     }
     502         166 :     for (size_t i = 0; i < steps.size() - 1; ++i)
     503             :     {
     504         104 :         if (steps[i].alg->GetName() == GDALRasterWriteAlgorithm::NAME)
     505             :         {
     506           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     507             :                         "Only last step can be '%s'",
     508             :                         GDALRasterWriteAlgorithm::NAME);
     509           1 :             return false;
     510             :         }
     511             :     }
     512             : 
     513         226 :     for (auto &step : steps)
     514             :     {
     515         164 :         step.alg->SetReferencePathForRelativePaths(
     516             :             GetReferencePathForRelativePaths());
     517             :     }
     518             : 
     519             :     // Propagate input parameters set at the pipeline level to the
     520             :     // "read" step
     521             :     {
     522          62 :         auto &step = steps.front();
     523         496 :         for (auto &arg : step.alg->GetArgs())
     524             :         {
     525         434 :             if (!arg->IsHidden())
     526             :             {
     527         372 :                 auto pipelineArg = GetArg(arg->GetName());
     528         372 :                 if (pipelineArg && pipelineArg->IsExplicitlySet())
     529             :                 {
     530           2 :                     arg->SetSkipIfAlreadySet(true);
     531           2 :                     arg->SetFrom(*pipelineArg);
     532             :                 }
     533             :             }
     534             :         }
     535             :     }
     536             : 
     537             :     // Same with "write" step
     538             :     {
     539          62 :         auto &step = steps.back();
     540         620 :         for (auto &arg : step.alg->GetArgs())
     541             :         {
     542         558 :             if (!arg->IsHidden())
     543             :             {
     544         434 :                 auto pipelineArg = GetArg(arg->GetName());
     545         434 :                 if (pipelineArg && pipelineArg->IsExplicitlySet())
     546             :                 {
     547           1 :                     arg->SetSkipIfAlreadySet(true);
     548           1 :                     arg->SetFrom(*pipelineArg);
     549             :                 }
     550             :             }
     551             :         }
     552             :     }
     553             : 
     554             :     // Parse each step, but without running the validation
     555         215 :     for (const auto &step : steps)
     556             :     {
     557         159 :         step.alg->m_skipValidationInParseCommandLine = true;
     558         159 :         if (!step.alg->ParseCommandLineArguments(step.args))
     559           6 :             return false;
     560             :     }
     561             : 
     562             :     // Evaluate "input" argument of "read" step, together with the "output"
     563             :     // argument of the "write" step, in case they point to the same dataset.
     564          56 :     auto inputArg = steps.front().alg->GetArg(GDAL_ARG_NAME_INPUT);
     565         112 :     if (inputArg && inputArg->IsExplicitlySet() &&
     566          56 :         inputArg->GetType() == GAAT_DATASET)
     567             :     {
     568          56 :         steps.front().alg->ProcessDatasetArg(inputArg, steps.back().alg.get());
     569             :     }
     570             : 
     571         195 :     for (const auto &step : steps)
     572             :     {
     573         144 :         if (!step.alg->ValidateArguments())
     574           5 :             return false;
     575             :     }
     576             : 
     577         187 :     for (auto &step : steps)
     578         136 :         m_steps.push_back(std::move(step.alg));
     579             : 
     580          51 :     return true;
     581             : }
     582             : 
     583             : /************************************************************************/
     584             : /*            GDALRasterPipelineAlgorithm::GetUsageForCLI()             */
     585             : /************************************************************************/
     586             : 
     587           6 : std::string GDALRasterPipelineAlgorithm::GetUsageForCLI(
     588             :     bool shortUsage, const UsageOptions &usageOptions) const
     589             : {
     590           6 :     UsageOptions stepUsageOptions;
     591           6 :     stepUsageOptions.isPipelineStep = true;
     592             : 
     593           6 :     if (!m_helpDocCategory.empty() && m_helpDocCategory != "main")
     594             :     {
     595           4 :         auto alg = GetStepAlg(m_helpDocCategory);
     596           4 :         std::string ret;
     597           2 :         if (alg)
     598             :         {
     599           2 :             alg->SetCallPath({m_helpDocCategory});
     600           1 :             alg->GetArg("help-doc")->Set(true);
     601           1 :             return alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     602             :         }
     603             :         else
     604             :         {
     605           1 :             fprintf(stderr, "ERROR: unknown pipeline step '%s'\n",
     606             :                     m_helpDocCategory.c_str());
     607             :             return CPLSPrintf("ERROR: unknown pipeline step '%s'\n",
     608           1 :                               m_helpDocCategory.c_str());
     609             :         }
     610             :     }
     611             : 
     612           4 :     UsageOptions usageOptionsMain(usageOptions);
     613           4 :     usageOptionsMain.isPipelineMain = true;
     614             :     std::string ret =
     615           8 :         GDALAlgorithm::GetUsageForCLI(shortUsage, usageOptionsMain);
     616           4 :     if (shortUsage)
     617           2 :         return ret;
     618             : 
     619             :     ret += "\n<PIPELINE> is of the form: read [READ-OPTIONS] "
     620           2 :            "( ! <STEP-NAME> [STEP-OPTIONS] )* ! write [WRITE-OPTIONS]\n";
     621             : 
     622           2 :     if (m_helpDocCategory == "main")
     623             :     {
     624           1 :         return ret;
     625             :     }
     626             : 
     627           1 :     ret += '\n';
     628           1 :     ret += "Example: 'gdal raster pipeline --progress ! read in.tif ! \\\n";
     629           1 :     ret += "               reproject --dst-crs=EPSG:32632 ! ";
     630           1 :     ret += "write out.tif --overwrite'\n";
     631           1 :     ret += '\n';
     632           1 :     ret += "Potential steps are:\n";
     633             : 
     634          24 :     for (const std::string &name : m_stepRegistry.GetNames())
     635             :     {
     636          46 :         auto alg = GetStepAlg(name);
     637          23 :         auto [options, maxOptLen] = alg->GetArgNamesForCLI();
     638          23 :         stepUsageOptions.maxOptLen =
     639          23 :             std::max(stepUsageOptions.maxOptLen, maxOptLen);
     640             :     }
     641             : 
     642             :     {
     643           1 :         const auto name = GDALRasterReadAlgorithm::NAME;
     644           1 :         ret += '\n';
     645           2 :         auto alg = GetStepAlg(name);
     646           2 :         alg->SetCallPath({name});
     647           1 :         ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     648             :     }
     649          24 :     for (const std::string &name : m_stepRegistry.GetNames())
     650             :     {
     651          45 :         if (name != GDALRasterReadAlgorithm::NAME &&
     652          22 :             name != GDALRasterWriteAlgorithm::NAME)
     653             :         {
     654          21 :             ret += '\n';
     655          21 :             auto alg = GetStepAlg(name);
     656          42 :             alg->SetCallPath({name});
     657          21 :             ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     658             :         }
     659             :     }
     660             :     {
     661           1 :         const auto name = GDALRasterWriteAlgorithm::NAME;
     662           1 :         ret += '\n';
     663           2 :         auto alg = GetStepAlg(name);
     664           2 :         alg->SetCallPath({name});
     665           1 :         ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
     666             :     }
     667             : 
     668           1 :     ret += GetUsageForCLIEnd();
     669             : 
     670           1 :     return ret;
     671             : }
     672             : 
     673             : /************************************************************************/
     674             : /*           GDALRasterPipelineNonNativelyStreamingAlgorithm()          */
     675             : /************************************************************************/
     676             : 
     677         119 : GDALRasterPipelineNonNativelyStreamingAlgorithm::
     678             :     GDALRasterPipelineNonNativelyStreamingAlgorithm(
     679             :         const std::string &name, const std::string &description,
     680         119 :         const std::string &helpURL, bool standaloneStep)
     681             :     : GDALRasterPipelineStepAlgorithm(name, description, helpURL,
     682         119 :                                       standaloneStep)
     683             : {
     684         119 : }
     685             : 
     686             : /************************************************************************/
     687             : /*                     MustCreateOnDiskTempDataset()                        */
     688             : /************************************************************************/
     689             : 
     690          55 : static bool MustCreateOnDiskTempDataset(int nWidth, int nHeight, int nBands,
     691             :                                         GDALDataType eDT)
     692             : {
     693             :     // Config option mostly for autotest purposes
     694          55 :     if (CPLTestBool(CPLGetConfigOption(
     695             :             "GDAL_RASTER_PIPELINE_USE_GTIFF_FOR_TEMP_DATASET", "NO")))
     696           5 :         return true;
     697             : 
     698             :     // Allow up to 10% of RAM usage for temporary dataset
     699          50 :     const auto nRAM = CPLGetUsablePhysicalRAM() / 10;
     700          50 :     const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
     701          50 :     const bool bOnDisk =
     702         100 :         nBands > 0 && nDTSize > 0 && nRAM > 0 &&
     703          50 :         static_cast<int64_t>(nWidth) * nHeight > nRAM / (nBands * nDTSize);
     704          50 :     return bOnDisk;
     705             : }
     706             : 
     707             : /************************************************************************/
     708             : /*                      CreateTemporaryDataset()                        */
     709             : /************************************************************************/
     710             : 
     711             : std::unique_ptr<GDALDataset>
     712          30 : GDALRasterPipelineNonNativelyStreamingAlgorithm::CreateTemporaryDataset(
     713             :     int nWidth, int nHeight, int nBands, GDALDataType eDT,
     714             :     bool bTiledIfPossible, GDALDataset *poSrcDSForMetadata, bool bCopyMetadata)
     715             : {
     716             :     const bool bOnDisk =
     717          30 :         MustCreateOnDiskTempDataset(nWidth, nHeight, nBands, eDT);
     718          30 :     const char *pszDriverName = bOnDisk ? "GTIFF" : "MEM";
     719             :     GDALDriver *poDriver =
     720          30 :         GetGDALDriverManager()->GetDriverByName(pszDriverName);
     721          60 :     CPLStringList aosOptions;
     722          60 :     std::string osTmpFilename;
     723          30 :     if (bOnDisk)
     724             :     {
     725             :         osTmpFilename =
     726           4 :             CPLGenerateTempFilenameSafe(
     727             :                 poSrcDSForMetadata
     728           4 :                     ? CPLGetBasenameSafe(poSrcDSForMetadata->GetDescription())
     729           2 :                           .c_str()
     730           4 :                     : "") +
     731           2 :             ".tif";
     732           2 :         if (bTiledIfPossible)
     733           2 :             aosOptions.SetNameValue("TILED", "YES");
     734             :         const char *pszCOList =
     735           2 :             poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST);
     736             :         aosOptions.SetNameValue("COMPRESS",
     737           2 :                                 pszCOList && strstr(pszCOList, "ZSTD") ? "ZSTD"
     738           4 :                                                                        : "LZW");
     739           2 :         aosOptions.SetNameValue("SPARSE_OK", "YES");
     740             :     }
     741             :     std::unique_ptr<GDALDataset> poOutDS(
     742          30 :         poDriver ? poDriver->Create(osTmpFilename.c_str(), nWidth, nHeight,
     743          30 :                                     nBands, eDT, aosOptions.List())
     744          60 :                  : nullptr);
     745          30 :     if (poOutDS && bOnDisk)
     746             :     {
     747             :         // In file systems that allow it (all but Windows...), we want to
     748             :         // delete the temporary file as soon as soon as possible after
     749             :         // having open it, so that if someone kills the process there are
     750             :         // no temp files left over. If that unlink() doesn't succeed
     751             :         // (on Windows), then the file will eventually be deleted when
     752             :         // poTmpDS is cleaned due to MarkSuppressOnClose().
     753           0 :         VSIUnlink(osTmpFilename.c_str());
     754           0 :         poOutDS->MarkSuppressOnClose();
     755             :     }
     756             : 
     757          30 :     if (poOutDS && poSrcDSForMetadata)
     758             :     {
     759          28 :         poOutDS->SetSpatialRef(poSrcDSForMetadata->GetSpatialRef());
     760          28 :         std::array<double, 6> adfGT{};
     761          28 :         if (poSrcDSForMetadata->GetGeoTransform(adfGT.data()) == CE_None)
     762          27 :             poOutDS->SetGeoTransform(adfGT.data());
     763          28 :         if (const int nGCPCount = poSrcDSForMetadata->GetGCPCount())
     764             :         {
     765           0 :             const auto apsGCPs = poSrcDSForMetadata->GetGCPs();
     766           0 :             if (apsGCPs)
     767             :             {
     768           0 :                 poOutDS->SetGCPs(nGCPCount, apsGCPs,
     769           0 :                                  poSrcDSForMetadata->GetGCPSpatialRef());
     770             :             }
     771             :         }
     772          28 :         if (bCopyMetadata)
     773             :         {
     774          14 :             poOutDS->SetMetadata(poSrcDSForMetadata->GetMetadata());
     775             :         }
     776             :     }
     777             : 
     778          60 :     return poOutDS;
     779             : }
     780             : 
     781             : /************************************************************************/
     782             : /*                       CreateTemporaryCopy()                          */
     783             : /************************************************************************/
     784             : 
     785             : std::unique_ptr<GDALDataset>
     786          25 : GDALRasterPipelineNonNativelyStreamingAlgorithm::CreateTemporaryCopy(
     787             :     GDALDataset *poSrcDS, int nSingleBand, bool bTiledIfPossible,
     788             :     GDALProgressFunc pfnProgress, void *pProgressData)
     789             : {
     790          25 :     const int nBands = nSingleBand > 0 ? 1 : poSrcDS->GetRasterCount();
     791             :     const auto eDT =
     792          25 :         nBands ? poSrcDS->GetRasterBand(1)->GetRasterDataType() : GDT_Unknown;
     793          25 :     const bool bOnDisk = MustCreateOnDiskTempDataset(
     794             :         poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(), nBands, eDT);
     795          25 :     const char *pszDriverName = bOnDisk ? "GTIFF" : "MEM";
     796             : 
     797          50 :     CPLStringList options;
     798          25 :     if (nSingleBand > 0)
     799             :     {
     800          25 :         options.AddString("-b");
     801          25 :         options.AddString(CPLSPrintf("%d", nSingleBand));
     802             :     }
     803             : 
     804          25 :     options.AddString("-of");
     805          25 :     options.AddString(pszDriverName);
     806             : 
     807          50 :     std::string osTmpFilename;
     808          25 :     if (bOnDisk)
     809             :     {
     810             :         osTmpFilename =
     811           3 :             CPLGenerateTempFilenameSafe(
     812           9 :                 CPLGetBasenameSafe(poSrcDS->GetDescription()).c_str()) +
     813           3 :             ".tif";
     814           3 :         if (bTiledIfPossible)
     815             :         {
     816           3 :             options.AddString("-co");
     817           3 :             options.AddString("TILED=YES");
     818             :         }
     819             : 
     820             :         GDALDriver *poDriver =
     821           3 :             GetGDALDriverManager()->GetDriverByName(pszDriverName);
     822             :         const char *pszCOList =
     823           3 :             poDriver ? poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST)
     824           3 :                      : nullptr;
     825           3 :         options.AddString("-co");
     826           3 :         options.AddString(pszCOList && strstr(pszCOList, "ZSTD")
     827             :                               ? "COMPRESS=ZSTD"
     828           6 :                               : "COMPRESS=LZW");
     829             :     }
     830             : 
     831             :     GDALTranslateOptions *translateOptions =
     832          25 :         GDALTranslateOptionsNew(options.List(), nullptr);
     833             : 
     834          25 :     if (pfnProgress)
     835          12 :         GDALTranslateOptionsSetProgress(translateOptions, pfnProgress,
     836             :                                         pProgressData);
     837             : 
     838             :     std::unique_ptr<GDALDataset> poOutDS(GDALDataset::FromHandle(
     839             :         GDALTranslate(osTmpFilename.c_str(), GDALDataset::ToHandle(poSrcDS),
     840          25 :                       translateOptions, nullptr)));
     841          25 :     GDALTranslateOptionsFree(translateOptions);
     842             : 
     843          25 :     if (!poOutDS)
     844             :     {
     845           2 :         ReportError(CE_Failure, CPLE_AppDefined,
     846             :                     "Failed to create temporary dataset");
     847             :     }
     848          23 :     else if (bOnDisk)
     849             :     {
     850             :         // In file systems that allow it (all but Windows...), we want to
     851             :         // delete the temporary file as soon as soon as possible after
     852             :         // having open it, so that if someone kills the process there are
     853             :         // no temp files left over. If that unlink() doesn't succeed
     854             :         // (on Windows), then the file will eventually be deleted when
     855             :         // poTmpDS is cleaned due to MarkSuppressOnClose().
     856           1 :         VSIUnlink(osTmpFilename.c_str());
     857           1 :         poOutDS->MarkSuppressOnClose();
     858             :     }
     859          50 :     return poOutDS;
     860             : }
     861             : 
     862             : //! @endcond

Generated by: LCOV version 1.14