LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_stack.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 28 157 17.8 %
Date: 2025-02-20 10:14:44 Functions: 1 3 33.3 %

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

Generated by: LCOV version 1.14