LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_mosaic.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 154 159 96.9 %
Date: 2025-02-20 10:14:44 Functions: 3 3 100.0 %

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

Generated by: LCOV version 1.14