LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_pansharpen.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 109 109 100.0 %
Date: 2026-02-01 11:59:10 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  "pansharpen" step of "raster pipeline"
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_raster_pansharpen.h"
      14             : 
      15             : #include "gdal_priv.h"
      16             : #include "cpl_minixml.h"
      17             : 
      18             : //! @cond Doxygen_Suppress
      19             : 
      20             : #ifndef _
      21             : #define _(x) (x)
      22             : #endif
      23             : 
      24             : /************************************************************************/
      25             : /*        GDALRasterPansharpenAlgorithm::GetConstructorOptions()        */
      26             : /************************************************************************/
      27             : 
      28             : /* static */ GDALRasterPansharpenAlgorithm::ConstructorOptions
      29          58 : GDALRasterPansharpenAlgorithm::GetConstructorOptions(bool standaloneStep)
      30             : {
      31          58 :     ConstructorOptions opts;
      32          58 :     opts.SetStandaloneStep(standaloneStep);
      33          58 :     opts.SetAddDefaultArguments(false);
      34          58 :     opts.SetInputDatasetAlias("panchromatic");
      35          58 :     opts.SetInputDatasetHelpMsg(_("Input panchromatic raster dataset"));
      36          58 :     return opts;
      37             : }
      38             : 
      39             : /************************************************************************/
      40             : /*    GDALRasterPansharpenAlgorithm::GDALRasterPansharpenAlgorithm()    */
      41             : /************************************************************************/
      42             : 
      43          58 : GDALRasterPansharpenAlgorithm::GDALRasterPansharpenAlgorithm(
      44          58 :     bool standaloneStep)
      45             :     : GDALRasterPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
      46          58 :                                       GetConstructorOptions(standaloneStep))
      47             : {
      48          58 :     const auto AddSpectralDatasetArg = [this]()
      49             :     {
      50             :         auto &arg = AddArg("spectral", 0, _("Input spectral band dataset"),
      51         116 :                            &m_spectralDatasets)
      52          58 :                         .SetPositional()
      53          58 :                         .SetRequired()
      54          58 :                         .SetMinCount(1)
      55             :                         // due to ",band=" comma syntax
      56          58 :                         .SetAutoOpenDataset(false)
      57             :                         // due to ",band=" comma syntax
      58          58 :                         .SetPackedValuesAllowed(false)
      59          58 :                         .SetMetaVar("SPECTRAL");
      60             : 
      61          58 :         SetAutoCompleteFunctionForFilename(arg, GDAL_OF_RASTER);
      62          58 :     };
      63             : 
      64          58 :     if (standaloneStep)
      65             :     {
      66          21 :         AddRasterInputArgs(false, false);
      67          21 :         AddSpectralDatasetArg();
      68          21 :         AddProgressArg();
      69          21 :         AddRasterOutputArgs(false);
      70             :     }
      71             :     else
      72             :     {
      73          37 :         AddRasterHiddenInputDatasetArg();
      74          37 :         AddSpectralDatasetArg();
      75             :     }
      76             : 
      77         116 :     AddArg("resampling", 'r', _("Resampling algorithm"), &m_resampling)
      78          58 :         .SetDefault(m_resampling)
      79             :         .SetChoices("nearest", "bilinear", "cubic", "cubicspline", "lanczos",
      80          58 :                     "average");
      81          58 :     AddArg("weights", 0, _("Weight for each input spectral band"), &m_weights);
      82          58 :     AddArg("nodata", 0, _("Override nodata value of input bands"), &m_nodata);
      83         116 :     AddArg("bit-depth", 0, _("Override bit depth of input bands"), &m_bitDepth)
      84          58 :         .SetMinValueIncluded(8);
      85             :     AddArg("spatial-extent-adjustment", 0,
      86             :            _("Select behavior when bands have not the same extent"),
      87         116 :            &m_spatialExtentAdjustment)
      88          58 :         .SetDefault(m_spatialExtentAdjustment)
      89          58 :         .SetChoices("union", "intersection", "none", "none-without-warning");
      90          58 :     AddNumThreadsArg(&m_numThreads, &m_numThreadsStr);
      91          58 : }
      92             : 
      93             : /************************************************************************/
      94             : /*               GDALRasterPansharpenAlgorithm::RunStep()               */
      95             : /************************************************************************/
      96             : 
      97          13 : bool GDALRasterPansharpenAlgorithm::RunStep(GDALPipelineStepRunContext &)
      98             : {
      99          13 :     auto poPanDS = m_inputDataset[0].GetDatasetRef();
     100          13 :     CPLAssert(poPanDS);
     101          13 :     CPLAssert(m_outputDataset.GetName().empty());
     102          13 :     CPLAssert(!m_outputDataset.GetDatasetRef());
     103             : 
     104          13 :     if (poPanDS->GetRasterCount() != 1)
     105             :     {
     106           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     107             :                     "Input panchromatic dataset must have a single band");
     108           1 :         return false;
     109             :     }
     110             : 
     111             :     // to keep in this scope to keep datasets of spectral bands open until
     112             :     // GDALCreatePansharpenedVRT() runs
     113             :     std::vector<std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>>
     114          24 :         apoDatasetsToReleaseRef;
     115          24 :     std::vector<GDALRasterBandH> ahSpectralBands;
     116             : 
     117          24 :     for (auto &spectralDataset : m_spectralDatasets)
     118             :     {
     119          14 :         if (auto poSpectralDS = spectralDataset.GetDatasetRef())
     120             :         {
     121          12 :             for (int i = 1; i <= poSpectralDS->GetRasterCount(); ++i)
     122             :             {
     123           9 :                 ahSpectralBands.push_back(
     124           9 :                     GDALRasterBand::ToHandle(poSpectralDS->GetRasterBand(i)));
     125             :             }
     126             :         }
     127             :         else
     128             :         {
     129          11 :             const auto &name = spectralDataset.GetName();
     130          11 :             std::string dsName(name);
     131          11 :             const auto pos = name.find(",band=");
     132          11 :             int iBand = 0;
     133          11 :             if (pos != std::string::npos)
     134             :             {
     135           4 :                 dsName.resize(pos);
     136           4 :                 iBand = atoi(name.c_str() + pos + strlen(",band="));
     137             :             }
     138             :             std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser> poDS(
     139             :                 GDALDataset::Open(dsName.c_str(),
     140          11 :                                   GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
     141          11 :             if (!poDS)
     142           1 :                 return false;
     143             : 
     144          10 :             if (iBand <= 0)
     145             :             {
     146          20 :                 for (int i = 1; i <= poDS->GetRasterCount(); ++i)
     147             :                 {
     148          14 :                     ahSpectralBands.push_back(
     149          14 :                         GDALRasterBand::ToHandle(poDS->GetRasterBand(i)));
     150             :                 }
     151             :             }
     152           4 :             else if (iBand > poDS->GetRasterCount())
     153             :             {
     154           1 :                 ReportError(CE_Failure, CPLE_IllegalArg, "Illegal band in '%s'",
     155             :                             name.c_str());
     156           1 :                 return false;
     157             :             }
     158             :             else
     159             :             {
     160           3 :                 ahSpectralBands.push_back(
     161           3 :                     GDALRasterBand::ToHandle(poDS->GetRasterBand(iBand)));
     162             :             }
     163             : 
     164           9 :             apoDatasetsToReleaseRef.push_back(std::move(poDS));
     165             :         }
     166             :     }
     167             : 
     168          20 :     CPLXMLTreeCloser root(CPLCreateXMLNode(nullptr, CXT_Element, "VRTDataset"));
     169          36 :     for (int i = 0; i < static_cast<int>(ahSpectralBands.size()); ++i)
     170             :     {
     171             :         auto psBandNode =
     172          26 :             CPLCreateXMLNode(root.get(), CXT_Element, "VRTRasterBand");
     173          26 :         CPLAddXMLAttributeAndValue(
     174             :             psBandNode, "dataType",
     175          26 :             GDALGetDataTypeName(GDALGetRasterDataType(ahSpectralBands[i])));
     176          26 :         CPLAddXMLAttributeAndValue(psBandNode, "band", CPLSPrintf("%d", i + 1));
     177          26 :         CPLAddXMLAttributeAndValue(psBandNode, "subClass",
     178             :                                    "VRTPansharpenedRasterBand");
     179             :     }
     180          10 :     CPLAddXMLAttributeAndValue(root.get(), "subClass",
     181             :                                "VRTPansharpenedDataset");
     182             :     auto psPansharpeningOptionsNode =
     183          10 :         CPLCreateXMLNode(root.get(), CXT_Element, "PansharpeningOptions");
     184          10 :     if (!m_weights.empty())
     185             :     {
     186           2 :         auto psAlgorithmOptionsNode = CPLCreateXMLNode(
     187             :             psPansharpeningOptionsNode, CXT_Element, "AlgorithmOptions");
     188           4 :         std::string osWeights;
     189           8 :         for (double w : m_weights)
     190             :         {
     191           6 :             if (!osWeights.empty())
     192           4 :                 osWeights += ',';
     193           6 :             osWeights += CPLSPrintf("%.17g", w);
     194             :         }
     195           2 :         CPLCreateXMLElementAndValue(psAlgorithmOptionsNode, "Weights",
     196             :                                     osWeights.c_str());
     197             :     }
     198          10 :     CPLCreateXMLElementAndValue(psPansharpeningOptionsNode, "Resampling",
     199             :                                 m_resampling.c_str());
     200          10 :     CPLCreateXMLElementAndValue(psPansharpeningOptionsNode, "NumThreads",
     201             :                                 m_numThreadsStr.c_str());
     202          10 :     if (m_bitDepth > 0)
     203             :     {
     204           1 :         CPLCreateXMLElementAndValue(psPansharpeningOptionsNode, "BitDepth",
     205             :                                     CPLSPrintf("%d", m_bitDepth));
     206             :     }
     207          10 :     if (GetArg("nodata")->IsExplicitlySet())
     208             :     {
     209           1 :         CPLCreateXMLElementAndValue(psPansharpeningOptionsNode, "NoData",
     210             :                                     CPLSPrintf("%.17g", m_nodata));
     211             :     }
     212          10 :     CPLCreateXMLElementAndValue(
     213             :         psPansharpeningOptionsNode, "SpatialExtentAdjustment",
     214          20 :         CPLString(m_spatialExtentAdjustment).replaceAll('-', 0).c_str());
     215          36 :     for (int i = 0; i < static_cast<int>(ahSpectralBands.size()); ++i)
     216             :     {
     217          26 :         auto psSpectraBandNode = CPLCreateXMLNode(psPansharpeningOptionsNode,
     218             :                                                   CXT_Element, "SpectralBand");
     219          26 :         CPLAddXMLAttributeAndValue(psSpectraBandNode, "dstBand",
     220             :                                    CPLSPrintf("%d", i + 1));
     221             :     }
     222          20 :     CPLCharUniquePtr pszXML(CPLSerializeXMLTree(root.get()));
     223             :     auto poVRTDS = std::unique_ptr<GDALDataset>(
     224             :         GDALDataset::FromHandle(GDALCreatePansharpenedVRT(
     225          10 :             pszXML.get(), GDALRasterBand::ToHandle(poPanDS->GetRasterBand(1)),
     226          20 :             static_cast<int>(ahSpectralBands.size()), ahSpectralBands.data())));
     227          10 :     const bool bRet = poVRTDS != nullptr;
     228          10 :     if (poVRTDS)
     229             :     {
     230           8 :         m_outputDataset.Set(std::move(poVRTDS));
     231             :     }
     232          10 :     return bRet;
     233             : }
     234             : 
     235             : GDALRasterPansharpenAlgorithmStandalone::
     236             :     ~GDALRasterPansharpenAlgorithmStandalone() = default;
     237             : 
     238             : //! @endcond

Generated by: LCOV version 1.14