LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_reproject.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 178 190 93.7 %
Date: 2025-06-19 12:30:01 Functions: 7 9 77.8 %

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

Generated by: LCOV version 1.14