LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_stack.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 88 155 56.8 %
Date: 2025-05-15 13:16:46 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "raster stack" 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_stack.h"
      14             : 
      15             : #include "cpl_conv.h"
      16             : #include "cpl_vsi_virtual.h"
      17             : 
      18             : #include "gdal_priv.h"
      19             : #include "gdal_utils.h"
      20             : 
      21             : //! @cond Doxygen_Suppress
      22             : 
      23             : #ifndef _
      24             : #define _(x) (x)
      25             : #endif
      26             : 
      27             : /************************************************************************/
      28             : /*        GDALRasterStackAlgorithm::GDALRasterStackAlgorithm()    */
      29             : /************************************************************************/
      30             : 
      31           5 : GDALRasterStackAlgorithm::GDALRasterStackAlgorithm()
      32           5 :     : GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
      33             : {
      34           5 :     m_supportsStreamedOutput = true;
      35             : 
      36           5 :     AddProgressArg();
      37             :     AddOutputFormatArg(&m_format, /* bStreamAllowed = */ true,
      38           5 :                        /* bGDALGAllowed = */ true);
      39             :     AddArg(GDAL_ARG_NAME_INPUT, 'i',
      40             :            _("Input raster datasets (or specify a @<filename> to point to a "
      41             :              "file containing filenames)"),
      42          10 :            &m_inputDatasets, GDAL_OF_RASTER)
      43           5 :         .SetPositional()
      44           5 :         .SetMinCount(1)
      45           5 :         .SetAutoOpenDataset(false)
      46           5 :         .SetMetaVar("INPUTS");
      47           5 :     AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER);
      48           5 :     AddCreationOptionsArg(&m_creationOptions);
      49           5 :     AddBandArg(&m_bands);
      50           5 :     AddOverwriteArg(&m_overwrite);
      51             :     {
      52             :         auto &arg =
      53             :             AddArg("resolution", 0,
      54             :                    _("Target resolution (in destination CRS units)"),
      55          10 :                    &m_resolution)
      56           5 :                 .SetDefault("same")
      57           5 :                 .SetMetaVar("<xres>,<yres>|same|average|common|highest|lowest");
      58             :         arg.AddValidationAction(
      59           1 :             [this, &arg]()
      60             :             {
      61           2 :                 const std::string val = arg.Get<std::string>();
      62           2 :                 if (val != "average" && val != "highest" && val != "lowest" &&
      63           2 :                     val != "same" && val != "common")
      64             :                 {
      65             :                     const auto aosTokens =
      66           0 :                         CPLStringList(CSLTokenizeString2(val.c_str(), ",", 0));
      67           0 :                     if (aosTokens.size() != 2 ||
      68           0 :                         CPLGetValueType(aosTokens[0]) == CPL_VALUE_STRING ||
      69           0 :                         CPLGetValueType(aosTokens[1]) == CPL_VALUE_STRING ||
      70           0 :                         CPLAtof(aosTokens[0]) <= 0 ||
      71           0 :                         CPLAtof(aosTokens[1]) <= 0)
      72             :                     {
      73           0 :                         ReportError(
      74             :                             CE_Failure, CPLE_AppDefined,
      75             :                             "resolution: two comma-separated positive "
      76             :                             "values should be provided, or 'same', "
      77             :                             "'average', 'common', 'highest' or 'lowest'");
      78           0 :                         return false;
      79             :                     }
      80             :                 }
      81           1 :                 return true;
      82           5 :             });
      83             :     }
      84             :     AddBBOXArg(&m_bbox, _("Target bounding box as xmin,ymin,xmax,ymax (in "
      85           5 :                           "destination CRS units)"));
      86             :     AddArg("target-aligned-pixels", 0,
      87             :            _("Round target extent to target resolution"),
      88          10 :            &m_targetAlignedPixels)
      89           5 :         .AddHiddenAlias("tap");
      90             :     AddArg("src-nodata", 0, _("Set nodata values for input bands."),
      91          10 :            &m_srcNoData)
      92           5 :         .SetMinCount(1)
      93           5 :         .SetRepeatedArgAllowed(false);
      94             :     AddArg("dst-nodata", 0,
      95          10 :            _("Set nodata values at the destination band level."), &m_dstNoData)
      96           5 :         .SetMinCount(1)
      97           5 :         .SetRepeatedArgAllowed(false);
      98             :     AddArg("hide-nodata", 0,
      99             :            _("Makes the destination band not report the NoData."),
     100           5 :            &m_hideNoData);
     101           5 : }
     102             : 
     103             : /************************************************************************/
     104             : /*                   GDALRasterStackAlgorithm::RunImpl()               */
     105             : /************************************************************************/
     106             : 
     107           2 : bool GDALRasterStackAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
     108             :                                        void *pProgressData)
     109             : {
     110           2 :     CPLAssert(!m_outputDataset.GetDatasetRef());
     111             : 
     112           4 :     std::vector<GDALDatasetH> ahInputDatasets;
     113           4 :     CPLStringList aosInputDatasetNames;
     114           2 :     bool foundByRef = false;
     115           2 :     bool foundByName = false;
     116           5 :     for (auto &ds : m_inputDatasets)
     117             :     {
     118           3 :         if (ds.GetDatasetRef())
     119             :         {
     120           2 :             foundByRef = true;
     121           2 :             ahInputDatasets.push_back(
     122           2 :                 GDALDataset::ToHandle(ds.GetDatasetRef()));
     123             :         }
     124           1 :         else if (!ds.GetName().empty())
     125             :         {
     126           1 :             foundByName = true;
     127           1 :             if (ds.GetName()[0] == '@')
     128             :             {
     129             :                 auto f = VSIVirtualHandleUniquePtr(
     130           0 :                     VSIFOpenL(ds.GetName().c_str() + 1, "r"));
     131           0 :                 if (!f)
     132             :                 {
     133           0 :                     ReportError(CE_Failure, CPLE_FileIO, "Cannot open %s",
     134           0 :                                 ds.GetName().c_str() + 1);
     135           0 :                     return false;
     136             :                 }
     137           0 :                 while (const char *filename = CPLReadLineL(f.get()))
     138             :                 {
     139           0 :                     aosInputDatasetNames.push_back(filename);
     140           0 :                 }
     141             :             }
     142           1 :             else if (ds.GetName().find_first_of("*?[") != std::string::npos)
     143             :             {
     144           0 :                 CPLStringList aosMatches(VSIGlob(ds.GetName().c_str(), nullptr,
     145           0 :                                                  pfnProgress, pProgressData));
     146           0 :                 for (const char *pszStr : aosMatches)
     147             :                 {
     148           0 :                     aosInputDatasetNames.push_back(pszStr);
     149             :                 }
     150             :             }
     151             :             else
     152             :             {
     153           2 :                 std::string osDatasetName = ds.GetName();
     154           1 :                 if (!GetReferencePathForRelativePaths().empty())
     155             :                 {
     156           0 :                     osDatasetName = GDALDataset::BuildFilename(
     157             :                         osDatasetName.c_str(),
     158           0 :                         GetReferencePathForRelativePaths().c_str(), true);
     159             :                 }
     160           1 :                 aosInputDatasetNames.push_back(osDatasetName.c_str());
     161             :             }
     162             :         }
     163             :     }
     164           2 :     if (foundByName && foundByRef)
     165             :     {
     166           0 :         ReportError(CE_Failure, CPLE_NotSupported,
     167             :                     "Input datasets should be provided either all by reference "
     168             :                     "or all by name");
     169           0 :         return false;
     170             :     }
     171             : 
     172             :     const bool bVRTOutput =
     173           3 :         m_outputDataset.GetName().empty() || EQUAL(m_format.c_str(), "VRT") ||
     174           5 :         EQUAL(m_format.c_str(), "stream") ||
     175           2 :         EQUAL(CPLGetExtensionSafe(m_outputDataset.GetName().c_str()).c_str(),
     176             :               "VRT");
     177             : 
     178           4 :     CPLStringList aosOptions;
     179             : 
     180           2 :     aosOptions.push_back("-strict");
     181             : 
     182           2 :     aosOptions.push_back("-program_name");
     183           2 :     aosOptions.push_back("gdal raster stack");
     184             : 
     185           2 :     aosOptions.push_back("-separate");
     186             : 
     187             :     const auto aosTokens =
     188           4 :         CPLStringList(CSLTokenizeString2(m_resolution.c_str(), ",", 0));
     189           2 :     if (aosTokens.size() == 2)
     190             :     {
     191           0 :         aosOptions.push_back("-tr");
     192           0 :         aosOptions.push_back(aosTokens[0]);
     193           0 :         aosOptions.push_back(aosTokens[1]);
     194             :     }
     195             :     else
     196             :     {
     197           2 :         aosOptions.push_back("-resolution");
     198           2 :         aosOptions.push_back(m_resolution);
     199             :     }
     200             : 
     201           2 :     if (!m_bbox.empty())
     202             :     {
     203           0 :         aosOptions.push_back("-te");
     204           0 :         aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[0]));
     205           0 :         aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[1]));
     206           0 :         aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[2]));
     207           0 :         aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[3]));
     208             :     }
     209           2 :     if (m_targetAlignedPixels)
     210             :     {
     211           0 :         aosOptions.push_back("-tap");
     212             :     }
     213           2 :     if (!m_srcNoData.empty())
     214             :     {
     215           0 :         aosOptions.push_back("-srcnodata");
     216           0 :         std::string s;
     217           0 :         for (double v : m_srcNoData)
     218             :         {
     219           0 :             if (!s.empty())
     220           0 :                 s += " ";
     221           0 :             s += CPLSPrintf("%.17g", v);
     222             :         }
     223           0 :         aosOptions.push_back(s);
     224             :     }
     225           2 :     if (!m_dstNoData.empty())
     226             :     {
     227           0 :         aosOptions.push_back("-vrtnodata");
     228           0 :         std::string s;
     229           0 :         for (double v : m_dstNoData)
     230             :         {
     231           0 :             if (!s.empty())
     232           0 :                 s += " ";
     233           0 :             s += CPLSPrintf("%.17g", v);
     234             :         }
     235           0 :         aosOptions.push_back(s);
     236             :     }
     237           2 :     if (bVRTOutput)
     238             :     {
     239           2 :         for (const auto &co : m_creationOptions)
     240             :         {
     241           0 :             aosOptions.push_back("-co");
     242           0 :             aosOptions.push_back(co);
     243             :         }
     244             :     }
     245           2 :     for (const int b : m_bands)
     246             :     {
     247           0 :         aosOptions.push_back("-b");
     248           0 :         aosOptions.push_back(CPLSPrintf("%d", b));
     249             :     }
     250           2 :     if (m_hideNoData)
     251             :     {
     252           0 :         aosOptions.push_back("-hidenodata");
     253             :     }
     254             : 
     255             :     GDALBuildVRTOptions *psOptions =
     256           2 :         GDALBuildVRTOptionsNew(aosOptions.List(), nullptr);
     257           2 :     if (bVRTOutput)
     258             :     {
     259           2 :         GDALBuildVRTOptionsSetProgress(psOptions, pfnProgress, pProgressData);
     260             :     }
     261             : 
     262             :     auto poOutDS = std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
     263           2 :         GDALBuildVRT(EQUAL(m_format.c_str(), "stream") ? ""
     264           1 :                      : bVRTOutput ? m_outputDataset.GetName().c_str()
     265             :                                   : "",
     266           1 :                      foundByName ? aosInputDatasetNames.size()
     267           1 :                                  : static_cast<int>(m_inputDatasets.size()),
     268           3 :                      ahInputDatasets.empty() ? nullptr : ahInputDatasets.data(),
     269           9 :                      aosInputDatasetNames.List(), psOptions, nullptr)));
     270           2 :     GDALBuildVRTOptionsFree(psOptions);
     271           2 :     bool bOK = poOutDS != nullptr;
     272           2 :     if (bOK)
     273             :     {
     274           2 :         if (bVRTOutput)
     275             :         {
     276           2 :             m_outputDataset.Set(std::move(poOutDS));
     277             :         }
     278             :         else
     279             :         {
     280           0 :             CPLStringList aosTranslateOptions;
     281           0 :             if (!m_format.empty())
     282             :             {
     283           0 :                 aosTranslateOptions.AddString("-of");
     284           0 :                 aosTranslateOptions.AddString(m_format.c_str());
     285             :             }
     286           0 :             for (const auto &co : m_creationOptions)
     287             :             {
     288           0 :                 aosTranslateOptions.AddString("-co");
     289           0 :                 aosTranslateOptions.AddString(co.c_str());
     290             :             }
     291             : 
     292             :             GDALTranslateOptions *psTranslateOptions =
     293           0 :                 GDALTranslateOptionsNew(aosTranslateOptions.List(), nullptr);
     294           0 :             GDALTranslateOptionsSetProgress(psTranslateOptions, pfnProgress,
     295             :                                             pProgressData);
     296             : 
     297             :             auto poFinalDS =
     298             :                 std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
     299           0 :                     GDALTranslate(m_outputDataset.GetName().c_str(),
     300             :                                   GDALDataset::ToHandle(poOutDS.get()),
     301           0 :                                   psTranslateOptions, nullptr)));
     302           0 :             GDALTranslateOptionsFree(psTranslateOptions);
     303             : 
     304           0 :             bOK = poFinalDS != nullptr;
     305           0 :             if (bOK)
     306             :             {
     307           0 :                 m_outputDataset.Set(std::move(poFinalDS));
     308             :             }
     309             :         }
     310             :     }
     311             : 
     312           2 :     return bOK;
     313             : }
     314             : 
     315             : //! @endcond

Generated by: LCOV version 1.14