LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_buildvrt.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 133 137 97.1 %
Date: 2025-01-18 12:42:00 Functions: 3 3 100.0 %

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

Generated by: LCOV version 1.14