LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_reproject.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 185 197 93.9 %
Date: 2026-02-01 11:59:10 Functions: 8 10 80.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  "reproject" step of "raster pipeline"
       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_reproject.h"
      14             : #include "gdalalg_raster_write.h"
      15             : 
      16             : #include "gdal_alg.h"
      17             : #include "gdal_priv.h"
      18             : #include "gdal_utils.h"
      19             : #include "gdalwarper.h"
      20             : 
      21             : #include <cmath>
      22             : 
      23             : //! @cond Doxygen_Suppress
      24             : 
      25             : #ifndef _
      26             : #define _(x) (x)
      27             : #endif
      28             : 
      29             : /************************************************************************/
      30             : /*     GDALRasterReprojectAlgorithm::GDALRasterReprojectAlgorithm()     */
      31             : /************************************************************************/
      32             : 
      33         101 : GDALRasterReprojectAlgorithm::GDALRasterReprojectAlgorithm(bool standaloneStep)
      34             :     : GDALRasterPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
      35         101 :                                       standaloneStep)
      36             : {
      37         202 :     AddArg("src-crs", 's', _("Source CRS"), &m_srsCrs)
      38         202 :         .SetIsCRSArg()
      39         101 :         .AddHiddenAlias("s_srs");
      40         202 :     AddArg("dst-crs", 'd', _("Destination CRS"), &m_dstCrs)
      41         202 :         .SetIsCRSArg()
      42         101 :         .AddHiddenAlias("t_srs");
      43             : 
      44         101 :     GDALRasterReprojectUtils::AddResamplingArg(this, m_resampling);
      45             : 
      46             :     AddArg("resolution", 0, _("Target resolution (in destination CRS units)"),
      47         202 :            &m_resolution)
      48         101 :         .SetMinCount(2)
      49         101 :         .SetMaxCount(2)
      50         101 :         .SetMinValueExcluded(0)
      51         101 :         .SetRepeatedArgAllowed(false)
      52         101 :         .SetDisplayHintAboutRepetition(false)
      53         202 :         .SetMetaVar("<xres>,<yres>")
      54         101 :         .SetMutualExclusionGroup("resolution-size");
      55             : 
      56         202 :     AddArg("size", 0, _("Target size in pixels"), &m_size)
      57         101 :         .SetMinCount(2)
      58         101 :         .SetMaxCount(2)
      59         101 :         .SetMinValueIncluded(0)
      60         101 :         .SetRepeatedArgAllowed(false)
      61         101 :         .SetDisplayHintAboutRepetition(false)
      62         202 :         .SetMetaVar("<width>,<height>")
      63         101 :         .SetMutualExclusionGroup("resolution-size");
      64             : 
      65             :     auto &arg = AddBBOXArg(&m_bbox,
      66         101 :                            _("Target bounding box (in destination CRS units)"));
      67             : 
      68             :     arg.AddValidationAction(
      69          10 :         [this, &arg]()
      70             :         {
      71             :             // Validate it's not empty
      72           8 :             const std::vector<double> &bbox = arg.Get<std::vector<double>>();
      73           8 :             if ((bbox[0] >= bbox[2]) || (bbox[1] >= bbox[3]))
      74             :             {
      75           2 :                 ReportError(CE_Failure, CPLE_AppDefined,
      76             :                             "Invalid bounding box specified");
      77           2 :                 return false;
      78             :             }
      79             :             else
      80             :             {
      81           6 :                 return true;
      82             :             }
      83         101 :         });
      84             : 
      85         202 :     AddArg("bbox-crs", 0, _("CRS of target bounding box"), &m_bboxCrs)
      86         202 :         .SetIsCRSArg()
      87         101 :         .AddHiddenAlias("bbox_srs");
      88             : 
      89             :     AddArg("target-aligned-pixels", 0,
      90             :            _("Round target extent to target resolution"),
      91         202 :            &m_targetAlignedPixels)
      92         202 :         .AddHiddenAlias("tap")
      93         101 :         .SetCategory(GAAC_ADVANCED);
      94             :     AddArg("src-nodata", 0,
      95             :            _("Set nodata values for input bands ('None' to unset)."),
      96         202 :            &m_srcNoData)
      97         101 :         .SetMinCount(1)
      98         101 :         .SetRepeatedArgAllowed(false)
      99         101 :         .SetCategory(GAAC_ADVANCED);
     100             :     AddArg("dst-nodata", 0,
     101             :            _("Set nodata values for output bands ('None' to unset)."),
     102         202 :            &m_dstNoData)
     103         101 :         .SetMinCount(1)
     104         101 :         .SetRepeatedArgAllowed(false)
     105         101 :         .SetCategory(GAAC_ADVANCED);
     106             :     AddArg("add-alpha", 0,
     107             :            _("Adds an alpha mask band to the destination when the source "
     108             :              "raster have none."),
     109         202 :            &m_addAlpha)
     110         101 :         .SetCategory(GAAC_ADVANCED);
     111             : 
     112         101 :     GDALRasterReprojectUtils::AddWarpOptTransformOptErrorThresholdArg(
     113         101 :         this, m_warpOptions, m_transformOptions, m_errorThreshold);
     114             : 
     115         101 :     AddNumThreadsArg(&m_numThreads, &m_numThreadsStr);
     116         101 : }
     117             : 
     118             : /************************************************************************/
     119             : /*             GDALRasterReprojectUtils::AddResamplingArg()             */
     120             : /************************************************************************/
     121             : 
     122             : /*static */ void
     123         157 : GDALRasterReprojectUtils::AddResamplingArg(GDALAlgorithm *alg,
     124             :                                            std::string &resampling)
     125             : {
     126         314 :     alg->AddArg("resampling", 'r', _("Resampling method"), &resampling)
     127             :         .SetChoices("nearest", "bilinear", "cubic", "cubicspline", "lanczos",
     128             :                     "average", "rms", "mode", "min", "max", "med", "q1", "q3",
     129         157 :                     "sum")
     130         157 :         .SetDefault("nearest")
     131         157 :         .SetHiddenChoices("near");
     132         157 : }
     133             : 
     134             : /************************************************************************/
     135             : /*              AddWarpOptTransformOptErrorThresholdArg()               */
     136             : /************************************************************************/
     137             : 
     138             : /* static */
     139         157 : void GDALRasterReprojectUtils::AddWarpOptTransformOptErrorThresholdArg(
     140             :     GDALAlgorithm *alg, std::vector<std::string> &warpOptions,
     141             :     std::vector<std::string> &transformOptions, double &errorThreshold)
     142             : {
     143             :     {
     144             :         auto &arg =
     145         314 :             alg->AddArg("warp-option", 0, _("Warping option(s)"), &warpOptions)
     146         314 :                 .AddAlias("wo")
     147         314 :                 .SetMetaVar("<NAME>=<VALUE>")
     148         314 :                 .SetCategory(GAAC_ADVANCED)
     149         157 :                 .SetPackedValuesAllowed(false);
     150          10 :         arg.AddValidationAction([alg, &arg]()
     151         167 :                                 { return alg->ParseAndValidateKeyValue(arg); });
     152             :         arg.SetAutoCompleteFunction(
     153           0 :             [](const std::string &currentValue)
     154             :             {
     155           0 :                 std::vector<std::string> ret;
     156           0 :                 GDALAlgorithm::AddOptionsSuggestions(GDALWarpGetOptionList(), 0,
     157             :                                                      currentValue, ret);
     158           0 :                 return ret;
     159         157 :             });
     160             :     }
     161             :     {
     162             :         auto &arg = alg->AddArg("transform-option", 0, _("Transform option(s)"),
     163         314 :                                 &transformOptions)
     164         314 :                         .AddAlias("to")
     165         314 :                         .SetMetaVar("<NAME>=<VALUE>")
     166         314 :                         .SetCategory(GAAC_ADVANCED)
     167         157 :                         .SetPackedValuesAllowed(false);
     168           4 :         arg.AddValidationAction([alg, &arg]()
     169         161 :                                 { return alg->ParseAndValidateKeyValue(arg); });
     170             :         arg.SetAutoCompleteFunction(
     171           0 :             [](const std::string &currentValue)
     172             :             {
     173           0 :                 std::vector<std::string> ret;
     174           0 :                 GDALAlgorithm::AddOptionsSuggestions(
     175             :                     GDALGetGenImgProjTranformerOptionList(), 0, currentValue,
     176             :                     ret);
     177           0 :                 return ret;
     178         157 :             });
     179             :     }
     180         314 :     alg->AddArg("error-threshold", 0, _("Error threshold"), &errorThreshold)
     181         314 :         .AddAlias("et")
     182         157 :         .SetMinValueIncluded(0)
     183         157 :         .SetCategory(GAAC_ADVANCED);
     184         157 : }
     185             : 
     186             : /************************************************************************/
     187             : /*          GDALRasterReprojectAlgorithm::CanHandleNextStep()           */
     188             : /************************************************************************/
     189             : 
     190          35 : bool GDALRasterReprojectAlgorithm::CanHandleNextStep(
     191             :     GDALPipelineStepAlgorithm *poNextStep) const
     192             : {
     193          69 :     return poNextStep->GetName() == GDALRasterWriteAlgorithm::NAME &&
     194          69 :            poNextStep->GetOutputFormat() != "stream";
     195             : }
     196             : 
     197             : /************************************************************************/
     198             : /*               GDALRasterReprojectAlgorithm::RunStep()                */
     199             : /************************************************************************/
     200             : 
     201          20 : bool GDALRasterReprojectAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
     202             : {
     203          20 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
     204          20 :     CPLAssert(poSrcDS);
     205          20 :     CPLAssert(m_outputDataset.GetName().empty());
     206          20 :     CPLAssert(!m_outputDataset.GetDatasetRef());
     207             : 
     208          40 :     CPLStringList aosOptions;
     209          40 :     std::string outputFilename;
     210             : 
     211          20 :     if (ctxt.m_poNextUsableStep)
     212             :     {
     213          17 :         CPLAssert(CanHandleNextStep(ctxt.m_poNextUsableStep));
     214          17 :         outputFilename = ctxt.m_poNextUsableStep->GetOutputDataset().GetName();
     215          17 :         const auto &format = ctxt.m_poNextUsableStep->GetOutputFormat();
     216          17 :         if (!format.empty())
     217             :         {
     218           6 :             aosOptions.AddString("-of");
     219           6 :             aosOptions.AddString(format.c_str());
     220             :         }
     221             : 
     222          17 :         bool bFoundNumThreads = false;
     223           2 :         for (const std::string &co :
     224          21 :              ctxt.m_poNextUsableStep->GetCreationOptions())
     225             :         {
     226           2 :             aosOptions.AddString("-co");
     227           2 :             if (STARTS_WITH_CI(co.c_str(), "NUM_THREADS="))
     228           0 :                 bFoundNumThreads = true;
     229           2 :             aosOptions.AddString(co.c_str());
     230             :         }
     231             : 
     232             :         // Forward m_numThreads to GeoTIFF driver if --co NUM_THREADS not
     233             :         // specified
     234          34 :         if (!bFoundNumThreads && m_numThreads > 1 &&
     235          34 :             (EQUAL(format.c_str(), "GTIFF") || EQUAL(format.c_str(), "COG") ||
     236          17 :              (format.empty() &&
     237          28 :               EQUAL(CPLGetExtensionSafe(outputFilename.c_str()).c_str(),
     238             :                     "tif"))))
     239             :         {
     240          11 :             aosOptions.AddString("-co");
     241          11 :             aosOptions.AddString(CPLSPrintf("NUM_THREADS=%d", m_numThreads));
     242             :         }
     243             :     }
     244             :     else
     245             :     {
     246           3 :         aosOptions.AddString("-of");
     247           3 :         aosOptions.AddString("VRT");
     248             :     }
     249          20 :     if (!m_srsCrs.empty())
     250             :     {
     251           9 :         aosOptions.AddString("-s_srs");
     252           9 :         aosOptions.AddString(m_srsCrs.c_str());
     253             :     }
     254          20 :     if (!m_dstCrs.empty())
     255             :     {
     256          13 :         aosOptions.AddString("-t_srs");
     257          13 :         aosOptions.AddString(m_dstCrs.c_str());
     258             :     }
     259          20 :     if (!m_resampling.empty())
     260             :     {
     261          20 :         aosOptions.AddString("-r");
     262          20 :         aosOptions.AddString(m_resampling.c_str());
     263             :     }
     264          20 :     if (!m_resolution.empty())
     265             :     {
     266           1 :         aosOptions.AddString("-tr");
     267           1 :         aosOptions.AddString(CPLSPrintf("%.17g", m_resolution[0]));
     268           1 :         aosOptions.AddString(CPLSPrintf("%.17g", m_resolution[1]));
     269             :     }
     270          20 :     if (!m_size.empty())
     271             :     {
     272           1 :         aosOptions.AddString("-ts");
     273           1 :         aosOptions.AddString(CPLSPrintf("%d", m_size[0]));
     274           1 :         aosOptions.AddString(CPLSPrintf("%d", m_size[1]));
     275             :     }
     276          20 :     if (!m_bbox.empty())
     277             :     {
     278           2 :         aosOptions.AddString("-te");
     279           2 :         aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[0]));
     280           2 :         aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[1]));
     281           2 :         aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[2]));
     282           2 :         aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[3]));
     283             :     }
     284          20 :     if (!m_bboxCrs.empty())
     285             :     {
     286           1 :         aosOptions.AddString("-te_srs");
     287           1 :         aosOptions.AddString(m_bboxCrs.c_str());
     288             :     }
     289          20 :     if (m_targetAlignedPixels)
     290             :     {
     291           1 :         aosOptions.AddString("-tap");
     292             :     }
     293          20 :     if (!m_srcNoData.empty())
     294             :     {
     295           1 :         aosOptions.push_back("-srcnodata");
     296           2 :         std::string s;
     297           2 :         for (const std::string &v : m_srcNoData)
     298             :         {
     299           1 :             if (!s.empty())
     300           0 :                 s += " ";
     301           1 :             s += v;
     302             :         }
     303           1 :         aosOptions.push_back(s);
     304             :     }
     305          20 :     if (!m_dstNoData.empty())
     306             :     {
     307           2 :         aosOptions.push_back("-dstnodata");
     308           4 :         std::string s;
     309           5 :         for (const std::string &v : m_dstNoData)
     310             :         {
     311           3 :             if (!s.empty())
     312           1 :                 s += " ";
     313           3 :             s += v;
     314             :         }
     315           2 :         aosOptions.push_back(s);
     316             :     }
     317          20 :     if (m_addAlpha)
     318             :     {
     319           1 :         aosOptions.AddString("-dstalpha");
     320             :     }
     321             : 
     322          20 :     bool bFoundNumThreads = false;
     323          23 :     for (const std::string &opt : m_warpOptions)
     324             :     {
     325           3 :         aosOptions.AddString("-wo");
     326           3 :         if (STARTS_WITH_CI(opt.c_str(), "NUM_THREADS="))
     327           2 :             bFoundNumThreads = true;
     328           3 :         aosOptions.AddString(opt.c_str());
     329             :     }
     330          20 :     if (bFoundNumThreads)
     331             :     {
     332           2 :         if (GetArg(GDAL_ARG_NAME_NUM_THREADS)->IsExplicitlySet())
     333             :         {
     334           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     335             :                         "--num-threads argument and NUM_THREADS warp options "
     336             :                         "are mutually exclusive.");
     337           1 :             return false;
     338             :         }
     339             :     }
     340             :     else
     341             :     {
     342          18 :         aosOptions.AddString("-wo");
     343          18 :         aosOptions.AddString(CPLSPrintf("NUM_THREADS=%d", m_numThreads));
     344             :     }
     345             : 
     346          20 :     for (const std::string &opt : m_transformOptions)
     347             :     {
     348           1 :         aosOptions.AddString("-to");
     349           1 :         aosOptions.AddString(opt.c_str());
     350             :     }
     351          19 :     if (std::isfinite(m_errorThreshold))
     352             :     {
     353           0 :         aosOptions.AddString("-et");
     354           0 :         aosOptions.AddString(CPLSPrintf("%.17g", m_errorThreshold));
     355             :     }
     356             : 
     357          19 :     bool bOK = false;
     358             :     GDALWarpAppOptions *psOptions =
     359          19 :         GDALWarpAppOptionsNew(aosOptions.List(), nullptr);
     360          19 :     if (psOptions)
     361             :     {
     362          19 :         if (ctxt.m_poNextUsableStep)
     363             :         {
     364          16 :             GDALWarpAppOptionsSetProgress(psOptions, ctxt.m_pfnProgress,
     365             :                                           ctxt.m_pProgressData);
     366             :         }
     367          19 :         GDALDatasetH hSrcDS = GDALDataset::ToHandle(poSrcDS);
     368          19 :         auto poRetDS = GDALDataset::FromHandle(GDALWarp(
     369             :             outputFilename.c_str(), nullptr, 1, &hSrcDS, psOptions, nullptr));
     370          19 :         GDALWarpAppOptionsFree(psOptions);
     371          19 :         bOK = poRetDS != nullptr;
     372          19 :         if (bOK)
     373             :         {
     374          16 :             m_outputDataset.Set(std::unique_ptr<GDALDataset>(poRetDS));
     375             :         }
     376             :     }
     377          19 :     return bOK;
     378             : }
     379             : 
     380             : GDALRasterReprojectAlgorithmStandalone::
     381             :     ~GDALRasterReprojectAlgorithmStandalone() = default;
     382             : 
     383             : //! @endcond

Generated by: LCOV version 1.14