LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_mosaic.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 150 157 95.5 %
Date: 2025-05-15 18:21:54 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 :     AddBandArg(&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("src-nodata", 0, _("Set nodata values for input bands."),
      91          62 :            &m_srcNoData)
      92          31 :         .SetMinCount(1)
      93          31 :         .SetRepeatedArgAllowed(false);
      94             :     AddArg("dst-nodata", 0,
      95          62 :            _("Set nodata values at the destination band level."), &m_dstNoData)
      96          31 :         .SetMinCount(1)
      97          31 :         .SetRepeatedArgAllowed(false);
      98             :     AddArg("hide-nodata", 0,
      99             :            _("Makes the destination band not report the NoData."),
     100          31 :            &m_hideNoData);
     101             :     AddArg("add-alpha", 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          24 : bool GDALRasterMosaicAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
     113             :                                         void *pProgressData)
     114             : {
     115          24 :     CPLAssert(!m_outputDataset.GetDatasetRef());
     116             : 
     117          48 :     std::vector<GDALDatasetH> ahInputDatasets;
     118          48 :     CPLStringList aosInputDatasetNames;
     119          24 :     bool foundByRef = false;
     120          24 :     bool foundByName = false;
     121          56 :     for (auto &ds : m_inputDatasets)
     122             :     {
     123          33 :         if (ds.GetDatasetRef())
     124             :         {
     125          20 :             foundByRef = true;
     126          20 :             ahInputDatasets.push_back(
     127          20 :                 GDALDataset::ToHandle(ds.GetDatasetRef()));
     128             :         }
     129          13 :         else if (!ds.GetName().empty())
     130             :         {
     131          13 :             foundByName = true;
     132          13 :             if (ds.GetName()[0] == '@')
     133             :             {
     134             :                 auto f = VSIVirtualHandleUniquePtr(
     135           2 :                     VSIFOpenL(ds.GetName().c_str() + 1, "r"));
     136           2 :                 if (!f)
     137             :                 {
     138           1 :                     ReportError(CE_Failure, CPLE_FileIO, "Cannot open %s",
     139           1 :                                 ds.GetName().c_str() + 1);
     140           1 :                     return false;
     141             :                 }
     142           2 :                 while (const char *filename = CPLReadLineL(f.get()))
     143             :                 {
     144           1 :                     aosInputDatasetNames.push_back(filename);
     145           1 :                 }
     146             :             }
     147          11 :             else if (ds.GetName().find_first_of("*?[") != std::string::npos)
     148             :             {
     149           1 :                 CPLStringList aosMatches(VSIGlob(ds.GetName().c_str(), nullptr,
     150           2 :                                                  pfnProgress, pProgressData));
     151           2 :                 for (const char *pszStr : aosMatches)
     152             :                 {
     153           1 :                     aosInputDatasetNames.push_back(pszStr);
     154             :                 }
     155             :             }
     156             :             else
     157             :             {
     158          20 :                 std::string osDatasetName = ds.GetName();
     159          10 :                 if (!GetReferencePathForRelativePaths().empty())
     160             :                 {
     161           0 :                     osDatasetName = GDALDataset::BuildFilename(
     162             :                         osDatasetName.c_str(),
     163           0 :                         GetReferencePathForRelativePaths().c_str(), true);
     164             :                 }
     165          10 :                 aosInputDatasetNames.push_back(osDatasetName.c_str());
     166             :             }
     167             :         }
     168             :     }
     169          23 :     if (foundByName && foundByRef)
     170             :     {
     171           0 :         ReportError(CE_Failure, CPLE_NotSupported,
     172             :                     "Input datasets should be provided either all by reference "
     173             :                     "or all by name");
     174           0 :         return false;
     175             :     }
     176             : 
     177             :     const bool bVRTOutput =
     178          29 :         m_outputDataset.GetName().empty() || EQUAL(m_format.c_str(), "VRT") ||
     179          57 :         EQUAL(m_format.c_str(), "stream") ||
     180          28 :         EQUAL(CPLGetExtensionSafe(m_outputDataset.GetName().c_str()).c_str(),
     181             :               "VRT");
     182             : 
     183          46 :     CPLStringList aosOptions;
     184          23 :     aosOptions.push_back("-strict");
     185             : 
     186          23 :     aosOptions.push_back("-program_name");
     187          23 :     aosOptions.push_back("gdal raster mosaic");
     188             : 
     189             :     const auto aosTokens =
     190          46 :         CPLStringList(CSLTokenizeString2(m_resolution.c_str(), ",", 0));
     191          23 :     if (aosTokens.size() == 2)
     192             :     {
     193           2 :         aosOptions.push_back("-tr");
     194           2 :         aosOptions.push_back(aosTokens[0]);
     195           2 :         aosOptions.push_back(aosTokens[1]);
     196             :     }
     197             :     else
     198             :     {
     199          21 :         aosOptions.push_back("-resolution");
     200          21 :         aosOptions.push_back(m_resolution);
     201             :     }
     202             : 
     203          23 :     if (!m_bbox.empty())
     204             :     {
     205           1 :         aosOptions.push_back("-te");
     206           1 :         aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[0]));
     207           1 :         aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[1]));
     208           1 :         aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[2]));
     209           1 :         aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[3]));
     210             :     }
     211          23 :     if (m_targetAlignedPixels)
     212             :     {
     213           1 :         aosOptions.push_back("-tap");
     214             :     }
     215          23 :     if (!m_srcNoData.empty())
     216             :     {
     217           2 :         aosOptions.push_back("-srcnodata");
     218           4 :         std::string s;
     219           4 :         for (double v : m_srcNoData)
     220             :         {
     221           2 :             if (!s.empty())
     222           0 :                 s += " ";
     223           2 :             s += CPLSPrintf("%.17g", v);
     224             :         }
     225           2 :         aosOptions.push_back(s);
     226             :     }
     227          23 :     if (!m_dstNoData.empty())
     228             :     {
     229           2 :         aosOptions.push_back("-vrtnodata");
     230           4 :         std::string s;
     231           4 :         for (double v : m_dstNoData)
     232             :         {
     233           2 :             if (!s.empty())
     234           0 :                 s += " ";
     235           2 :             s += CPLSPrintf("%.17g", v);
     236             :         }
     237           2 :         aosOptions.push_back(s);
     238             :     }
     239          23 :     if (bVRTOutput)
     240             :     {
     241          22 :         for (const auto &co : m_creationOptions)
     242             :         {
     243           2 :             aosOptions.push_back("-co");
     244           2 :             aosOptions.push_back(co);
     245             :         }
     246             :     }
     247          25 :     for (const int b : m_bands)
     248             :     {
     249           2 :         aosOptions.push_back("-b");
     250           2 :         aosOptions.push_back(CPLSPrintf("%d", b));
     251             :     }
     252          23 :     if (m_addAlpha)
     253             :     {
     254           0 :         aosOptions.push_back("-addalpha");
     255             :     }
     256          23 :     if (m_hideNoData)
     257             :     {
     258           1 :         aosOptions.push_back("-hidenodata");
     259             :     }
     260             : 
     261             :     GDALBuildVRTOptions *psOptions =
     262          23 :         GDALBuildVRTOptionsNew(aosOptions.List(), nullptr);
     263          23 :     if (bVRTOutput)
     264             :     {
     265          20 :         GDALBuildVRTOptionsSetProgress(psOptions, pfnProgress, pProgressData);
     266             :     }
     267             : 
     268             :     auto poOutDS = std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
     269          23 :         GDALBuildVRT(EQUAL(m_format.c_str(), "stream") ? ""
     270          22 :                      : bVRTOutput ? m_outputDataset.GetName().c_str()
     271             :                                   : "",
     272          12 :                      foundByName ? aosInputDatasetNames.size()
     273          11 :                                  : static_cast<int>(m_inputDatasets.size()),
     274          34 :                      ahInputDatasets.empty() ? nullptr : ahInputDatasets.data(),
     275         113 :                      aosInputDatasetNames.List(), psOptions, nullptr)));
     276          23 :     GDALBuildVRTOptionsFree(psOptions);
     277          23 :     bool bOK = poOutDS != nullptr;
     278          23 :     if (bOK)
     279             :     {
     280          21 :         if (bVRTOutput)
     281             :         {
     282          18 :             m_outputDataset.Set(std::move(poOutDS));
     283             :         }
     284             :         else
     285             :         {
     286           6 :             CPLStringList aosTranslateOptions;
     287           3 :             if (!m_format.empty())
     288             :             {
     289           2 :                 aosTranslateOptions.AddString("-of");
     290           2 :                 aosTranslateOptions.AddString(m_format.c_str());
     291             :             }
     292           4 :             for (const auto &co : m_creationOptions)
     293             :             {
     294           1 :                 aosTranslateOptions.AddString("-co");
     295           1 :                 aosTranslateOptions.AddString(co.c_str());
     296             :             }
     297             : 
     298             :             GDALTranslateOptions *psTranslateOptions =
     299           3 :                 GDALTranslateOptionsNew(aosTranslateOptions.List(), nullptr);
     300           3 :             GDALTranslateOptionsSetProgress(psTranslateOptions, pfnProgress,
     301             :                                             pProgressData);
     302             : 
     303             :             auto poFinalDS =
     304             :                 std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
     305           3 :                     GDALTranslate(m_outputDataset.GetName().c_str(),
     306             :                                   GDALDataset::ToHandle(poOutDS.get()),
     307           9 :                                   psTranslateOptions, nullptr)));
     308           3 :             GDALTranslateOptionsFree(psTranslateOptions);
     309             : 
     310           3 :             bOK = poFinalDS != nullptr;
     311           3 :             if (bOK)
     312             :             {
     313           3 :                 m_outputDataset.Set(std::move(poFinalDS));
     314             :             }
     315             :         }
     316             :     }
     317             : 
     318          23 :     return bOK;
     319             : }
     320             : 
     321             : //! @endcond

Generated by: LCOV version 1.14