LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_mosaic_stack_common.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 149 155 96.1 %
Date: 2026-04-23 19:47:11 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Common code of "raster mosaic" and "raster stack"
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_raster_mosaic_stack_common.h"
      14             : #include "gdalalg_raster_write.h"
      15             : 
      16             : #include "cpl_conv.h"
      17             : #include "cpl_vsi_virtual.h"
      18             : 
      19             : #include "gdal_priv.h"
      20             : #include "gdal_utils.h"
      21             : 
      22             : //! @cond Doxygen_Suppress
      23             : 
      24             : #ifndef _
      25             : #define _(x) (x)
      26             : #endif
      27             : 
      28             : /************************************************************************/
      29             : /*                       GetConstructorOptions()                        */
      30             : /************************************************************************/
      31             : 
      32             : /* static */ GDALRasterMosaicStackCommonAlgorithm::ConstructorOptions
      33         221 : GDALRasterMosaicStackCommonAlgorithm::GetConstructorOptions(bool standaloneStep)
      34             : {
      35         221 :     ConstructorOptions opts;
      36         221 :     opts.SetStandaloneStep(standaloneStep);
      37         221 :     opts.SetAutoOpenInputDatasets(false);
      38             :     opts.SetInputDatasetHelpMsg(
      39             :         _("Input raster datasets (or specify a @<filename> to point to a "
      40         221 :           "file containing filenames)"));
      41         221 :     opts.SetAddDefaultArguments(false);
      42         221 :     opts.SetInputDatasetMetaVar("INPUTS");
      43         221 :     opts.SetInputDatasetMaxCount(INT_MAX);
      44         221 :     return opts;
      45             : }
      46             : 
      47             : /************************************************************************/
      48             : /*                GDALRasterMosaicStackCommonAlgorithm()                */
      49             : /************************************************************************/
      50             : 
      51         221 : GDALRasterMosaicStackCommonAlgorithm::GDALRasterMosaicStackCommonAlgorithm(
      52             :     const std::string &name, const std::string &description,
      53         221 :     const std::string &helpURL, bool bStandalone)
      54             :     : GDALRasterPipelineStepAlgorithm(name, description, helpURL,
      55         221 :                                       GetConstructorOptions(bStandalone))
      56             : {
      57         221 :     AddRasterInputArgs(/* openForMixedRasterVector = */ false,
      58             :                        /* hiddenForCLI = */ false);
      59         221 :     if (bStandalone)
      60             :     {
      61         134 :         AddProgressArg();
      62         134 :         AddRasterOutputArgs(false);
      63             :     }
      64             : 
      65         221 :     AddBandArg(&m_bands);
      66             :     AddAbsolutePathArg(
      67             :         &m_writeAbsolutePaths,
      68             :         _("Whether the path to the input datasets should be stored as an "
      69         221 :           "absolute path"));
      70             : 
      71             :     auto &resArg =
      72             :         AddArg("resolution", 0,
      73         442 :                _("Target resolution (in destination CRS units)"), &m_resolution)
      74         221 :             .SetDefault("same")
      75         221 :             .SetMetaVar("<xres>,<yres>|same|average|common|highest|lowest");
      76             :     resArg.AddValidationAction(
      77          27 :         [this, &resArg]()
      78             :         {
      79          48 :             const std::string val = resArg.Get<std::string>();
      80          45 :             if (val != "average" && val != "highest" && val != "lowest" &&
      81          45 :                 val != "same" && val != "common")
      82             :             {
      83             :                 const auto aosTokens =
      84           9 :                     CPLStringList(CSLTokenizeString2(val.c_str(), ",", 0));
      85           9 :                 if (aosTokens.size() != 2 ||
      86           7 :                     CPLGetValueType(aosTokens[0]) == CPL_VALUE_STRING ||
      87           7 :                     CPLGetValueType(aosTokens[1]) == CPL_VALUE_STRING ||
      88          16 :                     CPLAtof(aosTokens[0]) <= 0 || CPLAtof(aosTokens[1]) <= 0)
      89             :                 {
      90           3 :                     ReportError(CE_Failure, CPLE_AppDefined,
      91             :                                 "resolution: two comma separated positive "
      92             :                                 "values should be provided, or 'same', "
      93             :                                 "'average', 'common', 'highest' or 'lowest'");
      94           3 :                     return false;
      95             :                 }
      96             :             }
      97          21 :             return true;
      98         221 :         });
      99             : 
     100             :     AddBBOXArg(&m_bbox, _("Target bounding box as xmin,ymin,xmax,ymax (in "
     101         221 :                           "destination CRS units)"));
     102             :     auto &tapArg = AddArg("target-aligned-pixels", 0,
     103             :                           _("Round target extent to target resolution"),
     104         442 :                           &m_targetAlignedPixels)
     105         221 :                        .AddHiddenAlias("tap");
     106             :     AddArg("input-nodata", 0, _("Set nodata values for input bands."),
     107         442 :            &m_srcNoData)
     108         221 :         .SetMinCount(1)
     109         442 :         .AddHiddenAlias("src-nodata")
     110         221 :         .SetRepeatedArgAllowed(false);
     111             :     AddArg("output-nodata", 0,
     112         442 :            _("Set nodata values at the destination band level."), &m_dstNoData)
     113         221 :         .SetMinCount(1)
     114         442 :         .AddHiddenAlias("dst-nodata")
     115         221 :         .SetRepeatedArgAllowed(false);
     116             :     AddArg("hide-nodata", 0,
     117             :            _("Makes the destination band not report the NoData."),
     118         221 :            &m_hideNoData);
     119             : 
     120         221 :     AddValidationAction(
     121         114 :         [this, &resArg, &tapArg]()
     122             :         {
     123         110 :             if (tapArg.IsExplicitlySet() && !resArg.IsExplicitlySet())
     124             :             {
     125           1 :                 ReportError(
     126             :                     CE_Failure, CPLE_IllegalArg,
     127             :                     "Argument 'target-aligned-pixels' can only be specified if "
     128             :                     "argument 'resolution' is also specified.");
     129           1 :                 return false;
     130             :             }
     131         109 :             return true;
     132             :         });
     133         221 : }
     134             : 
     135             : /************************************************************************/
     136             : /*     GDALRasterMosaicStackCommonAlgorithm::GetInputDatasetNames()     */
     137             : /************************************************************************/
     138             : 
     139          46 : bool GDALRasterMosaicStackCommonAlgorithm::GetInputDatasetNames(
     140             :     GDALPipelineStepRunContext &ctxt,
     141             :     std::vector<GDALDatasetH> &ahInputDatasets,
     142             :     CPLStringList &aosInputDatasetNames, bool &foundByName)
     143             : {
     144          46 :     bool foundByRef = false;
     145         104 :     for (auto &ds : m_inputDataset)
     146             :     {
     147          60 :         if (ds.GetDatasetRef())
     148             :         {
     149          30 :             foundByRef = true;
     150          30 :             ahInputDatasets.push_back(
     151          30 :                 GDALDataset::ToHandle(ds.GetDatasetRef()));
     152             :         }
     153          30 :         else if (!ds.GetName().empty())
     154             :         {
     155          30 :             foundByName = true;
     156          30 :             if (ds.GetName()[0] == '@')
     157             :             {
     158             :                 auto f = VSIVirtualHandleUniquePtr(
     159           3 :                     VSIFOpenL(ds.GetName().c_str() + 1, "r"));
     160           3 :                 if (!f)
     161             :                 {
     162           2 :                     ReportError(CE_Failure, CPLE_FileIO, "Cannot open %s",
     163           2 :                                 ds.GetName().c_str() + 1);
     164           2 :                     return false;
     165             :                 }
     166           2 :                 while (const char *filename = CPLReadLineL(f.get()))
     167             :                 {
     168           1 :                     aosInputDatasetNames.push_back(filename);
     169           1 :                 }
     170             :             }
     171          27 :             else if (ds.GetName().find_first_of("*?[") != std::string::npos)
     172             :             {
     173           1 :                 CPLStringList aosMatches(VSIGlob(ds.GetName().c_str(), nullptr,
     174             :                                                  ctxt.m_pfnProgress,
     175           2 :                                                  ctxt.m_pProgressData));
     176           2 :                 for (const char *pszStr : aosMatches)
     177             :                 {
     178           1 :                     aosInputDatasetNames.push_back(pszStr);
     179             :                 }
     180             :             }
     181             :             else
     182             :             {
     183          52 :                 std::string osDatasetName = ds.GetName();
     184          26 :                 if (!GetReferencePathForRelativePaths().empty())
     185             :                 {
     186           0 :                     osDatasetName = GDALDataset::BuildFilename(
     187             :                         osDatasetName.c_str(),
     188           0 :                         GetReferencePathForRelativePaths().c_str(), true);
     189             :                 }
     190          26 :                 aosInputDatasetNames.push_back(osDatasetName.c_str());
     191             :             }
     192             :         }
     193             :     }
     194          44 :     if (foundByName && foundByRef)
     195             :     {
     196           0 :         ReportError(CE_Failure, CPLE_NotSupported,
     197             :                     "Input datasets should be provided either all by reference "
     198             :                     "or all by name");
     199           0 :         return false;
     200             :     }
     201             : 
     202          44 :     return true;
     203             : }
     204             : 
     205             : /************************************************************************/
     206             : /*      GDALRasterMosaicStackCommonAlgorithm::SetBuildVRTOptions()      */
     207             : /************************************************************************/
     208             : 
     209          44 : void GDALRasterMosaicStackCommonAlgorithm::SetBuildVRTOptions(
     210             :     CPLStringList &aosOptions)
     211             : {
     212             :     const auto aosTokens =
     213          88 :         CPLStringList(CSLTokenizeString2(m_resolution.c_str(), ",", 0));
     214          44 :     if (aosTokens.size() == 2)
     215             :     {
     216           2 :         aosOptions.push_back("-tr");
     217           2 :         aosOptions.push_back(aosTokens[0]);
     218           2 :         aosOptions.push_back(aosTokens[1]);
     219             :     }
     220             :     else
     221             :     {
     222          42 :         aosOptions.push_back("-resolution");
     223          42 :         aosOptions.push_back(m_resolution);
     224             :     }
     225             : 
     226          44 :     if (!m_bbox.empty())
     227             :     {
     228           1 :         aosOptions.push_back("-te");
     229           1 :         aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[0]));
     230           1 :         aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[1]));
     231           1 :         aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[2]));
     232           1 :         aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[3]));
     233             :     }
     234          44 :     if (m_targetAlignedPixels)
     235             :     {
     236           1 :         aosOptions.push_back("-tap");
     237             :     }
     238          44 :     if (!m_srcNoData.empty())
     239             :     {
     240           2 :         aosOptions.push_back("-srcnodata");
     241           4 :         std::string s;
     242           4 :         for (double v : m_srcNoData)
     243             :         {
     244           2 :             if (!s.empty())
     245           0 :                 s += " ";
     246           2 :             s += CPLSPrintf("%.17g", v);
     247             :         }
     248           2 :         aosOptions.push_back(s);
     249             :     }
     250          44 :     if (!m_dstNoData.empty())
     251             :     {
     252           4 :         aosOptions.push_back("-vrtnodata");
     253           8 :         std::string s;
     254           8 :         for (double v : m_dstNoData)
     255             :         {
     256           4 :             if (!s.empty())
     257           0 :                 s += " ";
     258           4 :             s += CPLSPrintf("%.17g", v);
     259             :         }
     260           4 :         aosOptions.push_back(s);
     261             :     }
     262          46 :     for (const int b : m_bands)
     263             :     {
     264           2 :         aosOptions.push_back("-b");
     265           2 :         aosOptions.push_back(CPLSPrintf("%d", b));
     266             :     }
     267          44 :     if (m_hideNoData)
     268             :     {
     269           1 :         aosOptions.push_back("-hidenodata");
     270             :     }
     271          44 :     if (m_writeAbsolutePaths)
     272             :     {
     273           2 :         aosOptions.push_back("-write_absolute_path");
     274             :     }
     275          44 : }
     276             : 
     277             : /************************************************************************/
     278             : /*           GDALRasterMosaicStackCommonAlgorithm::RunImpl()            */
     279             : /************************************************************************/
     280             : 
     281          62 : bool GDALRasterMosaicStackCommonAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
     282             :                                                    void *pProgressData)
     283             : {
     284          62 :     if (m_standaloneStep)
     285             :     {
     286          31 :         GDALRasterWriteAlgorithm writeAlg;
     287         341 :         for (auto &arg : writeAlg.GetArgs())
     288             :         {
     289         310 :             if (!arg->IsHidden())
     290             :             {
     291         248 :                 auto stepArg = GetArg(arg->GetName());
     292         248 :                 if (stepArg && stepArg->IsExplicitlySet())
     293             :                 {
     294          59 :                     arg->SetSkipIfAlreadySet(true);
     295          59 :                     arg->SetFrom(*stepArg);
     296             :                 }
     297             :             }
     298             :         }
     299             : 
     300             :         // Already checked by GDALAlgorithm::Run()
     301          31 :         CPLAssert(!m_executionForStreamOutput ||
     302             :                   EQUAL(m_format.c_str(), "stream"));
     303             : 
     304          31 :         m_standaloneStep = false;
     305          31 :         m_alreadyRun = false;
     306          31 :         bool ret = Run(pfnProgress, pProgressData);
     307          31 :         m_standaloneStep = true;
     308          31 :         if (ret)
     309             :         {
     310          27 :             if (m_format == "stream")
     311             :             {
     312          19 :                 ret = true;
     313             :             }
     314             :             else
     315             :             {
     316          16 :                 std::vector<GDALArgDatasetValue> inputDataset(1);
     317           8 :                 inputDataset[0].Set(m_outputDataset.GetDatasetRef());
     318           8 :                 auto inputArg = writeAlg.GetArg(GDAL_ARG_NAME_INPUT);
     319           8 :                 CPLAssert(inputArg);
     320           8 :                 inputArg->Set(std::move(inputDataset));
     321           8 :                 if (writeAlg.Run(pfnProgress, pProgressData))
     322             :                 {
     323           8 :                     m_outputDataset.Set(
     324             :                         writeAlg.m_outputDataset.GetDatasetRef());
     325           8 :                     ret = true;
     326             :                 }
     327             :             }
     328             :         }
     329             : 
     330          31 :         return ret;
     331             :     }
     332             :     else
     333             :     {
     334          31 :         GDALPipelineStepRunContext stepCtxt;
     335          31 :         stepCtxt.m_pfnProgress = pfnProgress;
     336          31 :         stepCtxt.m_pProgressData = pProgressData;
     337          31 :         return RunStep(stepCtxt);
     338             :     }
     339             : }
     340             : 
     341             : //! @endcond

Generated by: LCOV version 1.14