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

Generated by: LCOV version 1.14