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

Generated by: LCOV version 1.14