LCOV - code coverage report
Current view: top level - frmts/vrt - vrtpansharpened.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 807 890 90.7 %
Date: 2025-07-06 19:57:03 Functions: 20 20 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Virtual GDAL Datasets
       4             :  * Purpose:  Implementation of VRTPansharpenedRasterBand and
       5             :  *VRTPansharpenedDataset. Author:   Even Rouault <even.rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2015, Even Rouault <even.rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "gdal_vrt.h"
      15             : #include "vrtdataset.h"
      16             : 
      17             : #include <cassert>
      18             : #include <cmath>
      19             : #include <cstddef>
      20             : #include <cstdlib>
      21             : #include <cstring>
      22             : 
      23             : #include <algorithm>
      24             : #include <limits>
      25             : #include <map>
      26             : #include <set>
      27             : #include <string>
      28             : #include <utility>
      29             : #include <vector>
      30             : 
      31             : #include "cpl_conv.h"
      32             : #include "cpl_error.h"
      33             : #include "cpl_minixml.h"
      34             : #include "cpl_string.h"
      35             : #include "cpl_vsi.h"
      36             : #include "gdal.h"
      37             : #include "gdal_priv.h"
      38             : #include "gdalpansharpen.h"
      39             : #include "ogr_core.h"
      40             : #include "ogr_spatialref.h"
      41             : 
      42             : /************************************************************************/
      43             : /*                    GDALCreatePansharpenedVRT()                       */
      44             : /************************************************************************/
      45             : 
      46             : /**
      47             :  * Create a virtual pansharpened dataset.
      48             :  *
      49             :  * This function will create a virtual pansharpened dataset.
      50             :  *
      51             :  * Starting with GDAL 3.12, a reference will be taken on the dataset owing
      52             :  * each input bands.
      53             :  * Before 3.12, no reference were taken on the passed bands. Consequently,
      54             :  * they or their dataset to which they belong had to be kept open until
      55             :  * this virtual pansharpened dataset is closed.
      56             :  *
      57             :  * The returned dataset will have no associated filename for itself.  If you
      58             :  * want to write the virtual dataset description to a file, use the
      59             :  * GDALSetDescription() function (or SetDescription() method) on the dataset
      60             :  * to assign a filename before it is closed.
      61             :  *
      62             :  * @param pszXML Pansharpened VRT XML where &lt;SpectralBand&gt; elements have
      63             :  * no explicit SourceFilename and SourceBand. The spectral bands in the XML will
      64             :  * be assigned the successive values of the pahInputSpectralBands array. Must
      65             :  * not be NULL.
      66             :  *
      67             :  * @param hPanchroBand Panchromatic band. Must not be NULL.
      68             :  *
      69             :  * @param nInputSpectralBands Number of input spectral bands.  Must be greater
      70             :  * than zero.
      71             :  *
      72             :  * @param pahInputSpectralBands Array of nInputSpectralBands spectral bands.
      73             :  *
      74             :  * @return NULL on failure, or a new virtual dataset handle on success to be
      75             :  * closed with GDALClose().
      76             :  *
      77             :  * @since GDAL 2.1
      78             :  */
      79             : 
      80          16 : GDALDatasetH GDALCreatePansharpenedVRT(const char *pszXML,
      81             :                                        GDALRasterBandH hPanchroBand,
      82             :                                        int nInputSpectralBands,
      83             :                                        GDALRasterBandH *pahInputSpectralBands)
      84             : {
      85          16 :     VALIDATE_POINTER1(pszXML, "GDALCreatePansharpenedVRT", nullptr);
      86          16 :     VALIDATE_POINTER1(hPanchroBand, "GDALCreatePansharpenedVRT", nullptr);
      87          16 :     VALIDATE_POINTER1(pahInputSpectralBands, "GDALCreatePansharpenedVRT",
      88             :                       nullptr);
      89             : 
      90          16 :     CPLXMLNode *psTree = CPLParseXMLString(pszXML);
      91          16 :     if (psTree == nullptr)
      92           1 :         return nullptr;
      93          15 :     VRTPansharpenedDataset *poDS = new VRTPansharpenedDataset(0, 0);
      94          15 :     CPLErr eErr = poDS->XMLInit(psTree, nullptr, hPanchroBand,
      95             :                                 nInputSpectralBands, pahInputSpectralBands);
      96          15 :     CPLDestroyXMLNode(psTree);
      97          15 :     if (eErr != CE_None)
      98             :     {
      99           3 :         delete poDS;
     100           3 :         return nullptr;
     101             :     }
     102          12 :     return GDALDataset::ToHandle(poDS);
     103             : }
     104             : 
     105             : /*! @cond Doxygen_Suppress */
     106             : 
     107             : /************************************************************************/
     108             : /* ==================================================================== */
     109             : /*                        VRTPansharpenedDataset                        */
     110             : /* ==================================================================== */
     111             : /************************************************************************/
     112             : 
     113             : /************************************************************************/
     114             : /*                       VRTPansharpenedDataset()                       */
     115             : /************************************************************************/
     116             : 
     117          92 : VRTPansharpenedDataset::VRTPansharpenedDataset(int nXSize, int nYSize,
     118          92 :                                                int nBlockXSize, int nBlockYSize)
     119             :     : VRTDataset(nXSize, nYSize,
     120          92 :                  nBlockXSize > 0 ? nBlockXSize : std::min(nXSize, 512),
     121          92 :                  nBlockYSize > 0 ? nBlockYSize : std::min(nYSize, 512)),
     122             :       m_poMainDataset(nullptr), m_bLoadingOtherBands(FALSE),
     123             :       m_pabyLastBufferBandRasterIO(nullptr), m_nLastBandRasterIOXOff(0),
     124             :       m_nLastBandRasterIOYOff(0), m_nLastBandRasterIOXSize(0),
     125             :       m_nLastBandRasterIOYSize(0), m_eLastBandRasterIODataType(GDT_Unknown),
     126         276 :       m_eGTAdjustment(GTAdjust_Union), m_bNoDataDisabled(FALSE)
     127             : {
     128          92 :     eAccess = GA_Update;
     129          92 :     m_poMainDataset = this;
     130          92 : }
     131             : 
     132             : /************************************************************************/
     133             : /*                    ~VRTPansharpenedDataset()                         */
     134             : /************************************************************************/
     135             : 
     136         184 : VRTPansharpenedDataset::~VRTPansharpenedDataset()
     137             : 
     138             : {
     139          92 :     VRTPansharpenedDataset::FlushCache(true);
     140          92 :     VRTPansharpenedDataset::CloseDependentDatasets();
     141          92 :     CPLFree(m_pabyLastBufferBandRasterIO);
     142         184 : }
     143             : 
     144             : /************************************************************************/
     145             : /*                        CloseDependentDatasets()                      */
     146             : /************************************************************************/
     147             : 
     148          92 : int VRTPansharpenedDataset::CloseDependentDatasets()
     149             : {
     150          92 :     int bHasDroppedRef = VRTDataset::CloseDependentDatasets();
     151             : 
     152             :     /* -------------------------------------------------------------------- */
     153             :     /*      Destroy the raster bands if they exist.                         */
     154             :     /* -------------------------------------------------------------------- */
     155         288 :     for (int iBand = 0; iBand < nBands; iBand++)
     156             :     {
     157         196 :         delete papoBands[iBand];
     158             :     }
     159          92 :     nBands = 0;
     160             : 
     161             :     // Destroy the overviews before m_poPansharpener as they might reference
     162             :     // files that are in m_apoDatasetsToReleaseRef.
     163          92 :     bHasDroppedRef |= !m_apoOverviewDatasets.empty();
     164          92 :     m_apoOverviewDatasets.clear();
     165             : 
     166          92 :     if (m_poPansharpener != nullptr)
     167             :     {
     168             :         // Delete the pansharper object before closing the dataset
     169             :         // because it may have warped the bands into an intermediate VRT
     170          69 :         m_poPansharpener.reset();
     171             : 
     172             :         // Close in reverse order (VRT firsts and real datasets after)
     173         324 :         for (int i = static_cast<int>(m_apoDatasetsToReleaseRef.size()) - 1;
     174         324 :              i >= 0; i--)
     175             :         {
     176         255 :             bHasDroppedRef = TRUE;
     177         255 :             m_apoDatasetsToReleaseRef[i].reset();
     178             :         }
     179          69 :         m_apoDatasetsToReleaseRef.clear();
     180             :     }
     181             : 
     182          92 :     return bHasDroppedRef;
     183             : }
     184             : 
     185             : /************************************************************************/
     186             : /*                            GetFileList()                             */
     187             : /************************************************************************/
     188             : 
     189           1 : char **VRTPansharpenedDataset::GetFileList()
     190             : {
     191           1 :     char **papszFileList = GDALDataset::GetFileList();
     192             : 
     193           1 :     if (m_poPansharpener != nullptr)
     194             :     {
     195           1 :         const GDALPansharpenOptions *psOptions = m_poPansharpener->GetOptions();
     196           1 :         if (psOptions != nullptr)
     197             :         {
     198           2 :             std::set<CPLString> oSetNames;
     199           1 :             if (psOptions->hPanchroBand != nullptr)
     200             :             {
     201           1 :                 GDALDatasetH hDS = GDALGetBandDataset(psOptions->hPanchroBand);
     202           1 :                 if (hDS != nullptr)
     203             :                 {
     204             :                     papszFileList =
     205           1 :                         CSLAddString(papszFileList, GDALGetDescription(hDS));
     206           1 :                     oSetNames.insert(GDALGetDescription(hDS));
     207             :                 }
     208             :             }
     209           4 :             for (int i = 0; i < psOptions->nInputSpectralBands; i++)
     210             :             {
     211           3 :                 if (psOptions->pahInputSpectralBands[i] != nullptr)
     212             :                 {
     213             :                     GDALDatasetH hDS =
     214           3 :                         GDALGetBandDataset(psOptions->pahInputSpectralBands[i]);
     215           6 :                     if (hDS != nullptr && oSetNames.find(GDALGetDescription(
     216           6 :                                               hDS)) == oSetNames.end())
     217             :                     {
     218           1 :                         papszFileList = CSLAddString(papszFileList,
     219             :                                                      GDALGetDescription(hDS));
     220           1 :                         oSetNames.insert(GDALGetDescription(hDS));
     221             :                     }
     222             :                 }
     223             :             }
     224             :         }
     225             :     }
     226             : 
     227           1 :     return papszFileList;
     228             : }
     229             : 
     230             : /************************************************************************/
     231             : /*                              XMLInit()                               */
     232             : /************************************************************************/
     233             : 
     234          74 : CPLErr VRTPansharpenedDataset::XMLInit(const CPLXMLNode *psTree,
     235             :                                        const char *pszVRTPathIn)
     236             : 
     237             : {
     238          74 :     return XMLInit(psTree, pszVRTPathIn, nullptr, 0, nullptr);
     239             : }
     240             : 
     241          89 : CPLErr VRTPansharpenedDataset::XMLInit(const CPLXMLNode *psTree,
     242             :                                        const char *pszVRTPathIn,
     243             :                                        GDALRasterBandH hPanchroBandIn,
     244             :                                        int nInputSpectralBandsIn,
     245             :                                        GDALRasterBandH *pahInputSpectralBandsIn)
     246             : {
     247             :     CPLErr eErr;
     248             :     std::unique_ptr<GDALPansharpenOptions,
     249             :                     decltype(&GDALDestroyPansharpenOptions)>
     250         178 :         psPanOptions(nullptr, GDALDestroyPansharpenOptions);
     251             : 
     252             :     /* -------------------------------------------------------------------- */
     253             :     /*      Initialize blocksize before calling sub-init so that the        */
     254             :     /*      band initializers can get it from the dataset object when       */
     255             :     /*      they are created.                                               */
     256             :     /* -------------------------------------------------------------------- */
     257          89 :     m_nBlockXSize = atoi(CPLGetXMLValue(psTree, "BlockXSize", "512"));
     258          89 :     m_nBlockYSize = atoi(CPLGetXMLValue(psTree, "BlockYSize", "512"));
     259             : 
     260             :     /* -------------------------------------------------------------------- */
     261             :     /*      Parse PansharpeningOptions                                      */
     262             :     /* -------------------------------------------------------------------- */
     263             : 
     264          89 :     const CPLXMLNode *psOptions = CPLGetXMLNode(psTree, "PansharpeningOptions");
     265          89 :     if (psOptions == nullptr)
     266             :     {
     267           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Missing PansharpeningOptions");
     268           1 :         return CE_Failure;
     269             :     }
     270             : 
     271         176 :     if (CPLGetXMLValue(psOptions, "MSShiftX", nullptr) != nullptr ||
     272          88 :         CPLGetXMLValue(psOptions, "MSShiftY", nullptr) != nullptr)
     273             :     {
     274           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     275             :                  "Options MSShiftX and MSShiftY are no longer supported. "
     276             :                  "Spatial adjustment between panchromatic and multispectral "
     277             :                  "bands is done through their geotransform.");
     278           0 :         return CE_Failure;
     279             :     }
     280             : 
     281         176 :     CPLString osSourceFilename;
     282          88 :     GDALDataset *poPanDataset = nullptr;
     283          88 :     GDALRasterBand *poPanBand = nullptr;
     284         176 :     std::map<CPLString, GDALDataset *> oMapNamesToDataset;
     285             :     int nPanBand;
     286             : 
     287          88 :     if (hPanchroBandIn == nullptr)
     288             :     {
     289             :         const CPLXMLNode *psPanchroBand =
     290          73 :             CPLGetXMLNode(psOptions, "PanchroBand");
     291          73 :         if (psPanchroBand == nullptr)
     292             :         {
     293           1 :             CPLError(CE_Failure, CPLE_AppDefined, "PanchroBand missing");
     294           4 :             return CE_Failure;
     295             :         }
     296             : 
     297          72 :         osSourceFilename = CPLGetXMLValue(psPanchroBand, "SourceFilename", "");
     298          72 :         if (osSourceFilename.empty())
     299             :         {
     300           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     301             :                      "PanchroBand.SourceFilename missing");
     302           1 :             return CE_Failure;
     303             :         }
     304          71 :         const bool bRelativeToVRT = CPL_TO_BOOL(atoi(CPLGetXMLValue(
     305             :             psPanchroBand, "SourceFilename.relativetoVRT", "0")));
     306          71 :         if (bRelativeToVRT)
     307             :         {
     308             :             const std::string osAbs = CPLProjectRelativeFilenameSafe(
     309          41 :                 pszVRTPathIn, osSourceFilename.c_str());
     310          41 :             m_oMapToRelativeFilenames[osAbs] = osSourceFilename;
     311          41 :             osSourceFilename = osAbs;
     312             :         }
     313             : 
     314             :         const CPLStringList aosOpenOptions(
     315          71 :             GDALDeserializeOpenOptionsFromXML(psPanchroBand));
     316             : 
     317          71 :         poPanDataset = GDALDataset::Open(osSourceFilename,
     318             :                                          GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
     319             :                                          nullptr, aosOpenOptions.List());
     320          71 :         if (poPanDataset == nullptr)
     321             :         {
     322           1 :             return CE_Failure;
     323             :         }
     324             : 
     325             :         const char *pszSourceBand =
     326          70 :             CPLGetXMLValue(psPanchroBand, "SourceBand", "1");
     327          70 :         nPanBand = atoi(pszSourceBand);
     328          70 :         if (poPanBand == nullptr)
     329          70 :             poPanBand = poPanDataset->GetRasterBand(nPanBand);
     330          70 :         if (poPanBand == nullptr)
     331             :         {
     332           1 :             CPLError(CE_Failure, CPLE_AppDefined, "%s invalid band of %s",
     333             :                      pszSourceBand, osSourceFilename.c_str());
     334           1 :             GDALClose(poPanDataset);
     335           1 :             return CE_Failure;
     336             :         }
     337          69 :         oMapNamesToDataset[osSourceFilename] = poPanDataset;
     338             :     }
     339             :     else
     340             :     {
     341          15 :         poPanBand = GDALRasterBand::FromHandle(hPanchroBandIn);
     342          15 :         nPanBand = poPanBand->GetBand();
     343          15 :         poPanDataset = poPanBand->GetDataset();
     344          15 :         if (poPanDataset == nullptr)
     345             :         {
     346           0 :             CPLError(
     347             :                 CE_Failure, CPLE_AppDefined,
     348             :                 "Cannot retrieve dataset associated with panchromatic band");
     349           0 :             return CE_Failure;
     350             :         }
     351          15 :         oMapNamesToDataset[CPLSPrintf("%p", poPanDataset)] = poPanDataset;
     352          15 :         poPanDataset->Reference();
     353             :     }
     354          84 :     m_apoDatasetsToReleaseRef.push_back(
     355         168 :         std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>(
     356             :             poPanDataset));
     357          84 :     poPanDataset = m_apoDatasetsToReleaseRef.back().get();
     358             :     // Figure out which kind of adjustment we should do if the pan and spectral
     359             :     // bands do not share the same geotransform
     360             :     const char *pszGTAdjustment =
     361          84 :         CPLGetXMLValue(psOptions, "SpatialExtentAdjustment", "Union");
     362          84 :     if (EQUAL(pszGTAdjustment, "Union"))
     363          78 :         m_eGTAdjustment = GTAdjust_Union;
     364           6 :     else if (EQUAL(pszGTAdjustment, "Intersection"))
     365           2 :         m_eGTAdjustment = GTAdjust_Intersection;
     366           4 :     else if (EQUAL(pszGTAdjustment, "None"))
     367           2 :         m_eGTAdjustment = GTAdjust_None;
     368           2 :     else if (EQUAL(pszGTAdjustment, "NoneWithoutWarning"))
     369           1 :         m_eGTAdjustment = GTAdjust_NoneWithoutWarning;
     370             :     else
     371             :     {
     372           1 :         m_eGTAdjustment = GTAdjust_Union;
     373           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     374             :                  "Unsupported value for GeoTransformAdjustment. Defaulting to "
     375             :                  "Union");
     376             :     }
     377             : 
     378             :     const char *pszNumThreads =
     379          84 :         CPLGetXMLValue(psOptions, "NumThreads", nullptr);
     380          84 :     int nThreads = 0;
     381          84 :     if (pszNumThreads != nullptr)
     382             :     {
     383          37 :         if (EQUAL(pszNumThreads, "ALL_CPUS"))
     384          37 :             nThreads = -1;
     385             :         else
     386           0 :             nThreads = atoi(pszNumThreads);
     387             :     }
     388             : 
     389             :     const char *pszAlgorithm =
     390          84 :         CPLGetXMLValue(psOptions, "Algorithm", "WeightedBrovey");
     391          84 :     if (!EQUAL(pszAlgorithm, "WeightedBrovey"))
     392             :     {
     393           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Algorithm %s unsupported",
     394             :                  pszAlgorithm);
     395           1 :         return CE_Failure;
     396             :     }
     397             : 
     398         166 :     std::vector<double> adfWeights;
     399          83 :     if (const CPLXMLNode *psAlgOptions =
     400          83 :             CPLGetXMLNode(psOptions, "AlgorithmOptions"))
     401             :     {
     402             :         const char *pszWeights =
     403          35 :             CPLGetXMLValue(psAlgOptions, "Weights", nullptr);
     404          35 :         if (pszWeights != nullptr)
     405             :         {
     406          35 :             char **papszTokens = CSLTokenizeString2(pszWeights, " ,", 0);
     407         124 :             for (int i = 0; papszTokens && papszTokens[i]; i++)
     408          89 :                 adfWeights.push_back(CPLAtof(papszTokens[i]));
     409          35 :             CSLDestroy(papszTokens);
     410             :         }
     411             :     }
     412             : 
     413          83 :     GDALRIOResampleAlg eResampleAlg = GDALRasterIOGetResampleAlg(
     414             :         CPLGetXMLValue(psOptions, "Resampling", "Cubic"));
     415             : 
     416         166 :     std::vector<GDALRasterBand *> ahSpectralBands;
     417         166 :     std::map<int, int> aMapDstBandToSpectralBand;
     418          83 :     std::map<int, int>::iterator aMapDstBandToSpectralBandIter;
     419          83 :     int nBitDepth = 0;
     420          83 :     bool bFoundNonMatchingGT = false;
     421          83 :     GDALGeoTransform panGT;
     422             :     const bool bPanGeoTransformValid =
     423          83 :         (poPanDataset->GetGeoTransform(panGT) == CE_None);
     424          83 :     if (!bPanGeoTransformValid)
     425             :     {
     426           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     427             :                  "Panchromatic band has no associated geotransform");
     428           1 :         return CE_Failure;
     429             :     }
     430          82 :     int nPanXSize = poPanBand->GetXSize();
     431          82 :     int nPanYSize = poPanBand->GetYSize();
     432          82 :     int bHasNoData = FALSE;
     433          82 :     double dfNoData = poPanBand->GetNoDataValue(&bHasNoData);
     434          82 :     double dfLRPanX = panGT[0] + nPanXSize * panGT[1] + nPanYSize * panGT[2];
     435          82 :     double dfLRPanY = panGT[3] + nPanXSize * panGT[4] + nPanYSize * panGT[5];
     436          82 :     bool bFoundRotatingTerms = (panGT[2] != 0.0 || panGT[4] != 0.0);
     437          82 :     double dfMinX = panGT[0];
     438          82 :     double dfMaxX = dfLRPanX;
     439          82 :     double dfMaxY = panGT[3];
     440          82 :     double dfMinY = dfLRPanY;
     441          82 :     if (dfMinY > dfMaxY)
     442          11 :         std::swap(dfMinY, dfMaxY);
     443          82 :     const double dfPanMinY = dfMinY;
     444          82 :     const double dfPanMaxY = dfMaxY;
     445             : 
     446          82 :     const auto poPanSRS = poPanDataset->GetSpatialRef();
     447             : 
     448             :     /* -------------------------------------------------------------------- */
     449             :     /*      First pass on spectral datasets to check their georeferencing.  */
     450             :     /* -------------------------------------------------------------------- */
     451          82 :     int iSpectralBand = 0;
     452         528 :     for (const CPLXMLNode *psIter = psOptions->psChild; psIter;
     453         446 :          psIter = psIter->psNext)
     454             :     {
     455             :         GDALDataset *poDataset;
     456         450 :         if (psIter->eType != CXT_Element ||
     457         450 :             !EQUAL(psIter->pszValue, "SpectralBand"))
     458         246 :             continue;
     459             : 
     460         204 :         if (nInputSpectralBandsIn && pahInputSpectralBandsIn != nullptr)
     461             :         {
     462          40 :             if (iSpectralBand == nInputSpectralBandsIn)
     463             :             {
     464           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     465             :                          "More SpectralBand elements than in source array");
     466           4 :                 goto error;
     467             :             }
     468             :             poDataset = GDALRasterBand::FromHandle(
     469          39 :                             pahInputSpectralBandsIn[iSpectralBand])
     470          39 :                             ->GetDataset();
     471          39 :             if (poDataset == nullptr)
     472             :             {
     473           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     474             :                          "SpectralBand has no associated dataset");
     475           0 :                 goto error;
     476             :             }
     477          39 :             osSourceFilename = poDataset->GetDescription();
     478             : 
     479          39 :             oMapNamesToDataset[CPLSPrintf("%p", poDataset)] = poDataset;
     480          39 :             poDataset->Reference();
     481             :         }
     482             :         else
     483             :         {
     484         164 :             osSourceFilename = CPLGetXMLValue(psIter, "SourceFilename", "");
     485         164 :             if (osSourceFilename.empty())
     486             :             {
     487           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     488             :                          "SpectralBand.SourceFilename missing");
     489           1 :                 goto error;
     490             :             }
     491         163 :             int bRelativeToVRT = atoi(
     492             :                 CPLGetXMLValue(psIter, "SourceFilename.relativetoVRT", "0"));
     493         163 :             if (bRelativeToVRT)
     494             :             {
     495             :                 const std::string osAbs = CPLProjectRelativeFilenameSafe(
     496         107 :                     pszVRTPathIn, osSourceFilename.c_str());
     497         107 :                 m_oMapToRelativeFilenames[osAbs] = osSourceFilename;
     498         107 :                 osSourceFilename = osAbs;
     499             :             }
     500         163 :             poDataset = oMapNamesToDataset[osSourceFilename];
     501         163 :             if (poDataset == nullptr)
     502             :             {
     503             :                 const CPLStringList aosOpenOptions(
     504          68 :                     GDALDeserializeOpenOptionsFromXML(psIter));
     505             : 
     506          68 :                 poDataset = GDALDataset::Open(
     507             :                     osSourceFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
     508             :                     nullptr, aosOpenOptions.List());
     509          68 :                 if (poDataset == nullptr)
     510             :                 {
     511           1 :                     goto error;
     512             :                 }
     513          67 :                 oMapNamesToDataset[osSourceFilename] = poDataset;
     514             :             }
     515             :             else
     516             :             {
     517          95 :                 poDataset->Reference();
     518             :             }
     519             :         }
     520         201 :         m_apoDatasetsToReleaseRef.push_back(
     521         402 :             std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>(
     522             :                 poDataset));
     523         201 :         poDataset = m_apoDatasetsToReleaseRef.back().get();
     524             : 
     525             :         // Check that the spectral band has a georeferencing consistent
     526             :         // of the pan band. Allow an error of at most the size of one pixel
     527             :         // of the spectral band.
     528         201 :         const auto poSpectralSRS = poDataset->GetSpatialRef();
     529         201 :         if (poPanSRS)
     530             :         {
     531         164 :             if (poSpectralSRS)
     532             :             {
     533         164 :                 if (!poPanSRS->IsSame(poSpectralSRS))
     534             :                 {
     535           1 :                     CPLError(CE_Warning, CPLE_AppDefined,
     536             :                              "Pan dataset and %s do not seem to "
     537             :                              "have same projection. Results might "
     538             :                              "be incorrect",
     539             :                              osSourceFilename.c_str());
     540             :                 }
     541             :             }
     542             :             else
     543             :             {
     544           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     545             :                          "Pan dataset has a projection, whereas %s "
     546             :                          "not. Results might be incorrect",
     547             :                          osSourceFilename.c_str());
     548             :             }
     549             :         }
     550          37 :         else if (poSpectralSRS)
     551             :         {
     552           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     553             :                      "Pan dataset has no projection, whereas %s has "
     554             :                      "one. Results might be incorrect",
     555             :                      osSourceFilename.c_str());
     556             :         }
     557             : 
     558         201 :         GDALGeoTransform spectralGT;
     559         201 :         if (poDataset->GetGeoTransform(spectralGT) != CE_None)
     560             :         {
     561           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     562             :                      "Spectral band has no associated geotransform");
     563           0 :             goto error;
     564             :         }
     565         201 :         if (spectralGT[3] * panGT[3] < 0)
     566             :         {
     567           1 :             CPLError(CE_Failure, CPLE_NotSupported,
     568             :                      "Spectral band vertical orientation is "
     569             :                      "different from pan one");
     570           1 :             goto error;
     571             :         }
     572             : 
     573         200 :         int bIsThisOneNonMatching = FALSE;
     574         200 :         double dfPixelSize = std::max(spectralGT[1], std::abs(spectralGT[5]));
     575         376 :         if (std::abs(panGT[0] - spectralGT[0]) > dfPixelSize ||
     576         176 :             std::abs(panGT[3] - spectralGT[3]) > dfPixelSize)
     577             :         {
     578          24 :             bIsThisOneNonMatching = TRUE;
     579          24 :             if (m_eGTAdjustment == GTAdjust_None)
     580             :             {
     581           2 :                 CPLError(CE_Warning, CPLE_AppDefined,
     582             :                          "Georeferencing of top-left corner of pan "
     583             :                          "dataset and %s do not match",
     584             :                          osSourceFilename.c_str());
     585             :             }
     586             :         }
     587         400 :         bFoundRotatingTerms = bFoundRotatingTerms ||
     588         200 :                               (spectralGT[2] != 0.0 || spectralGT[4] != 0.0);
     589         200 :         double dfLRSpectralX = spectralGT[0] +
     590         200 :                                poDataset->GetRasterXSize() * spectralGT[1] +
     591         200 :                                poDataset->GetRasterYSize() * spectralGT[2];
     592         200 :         double dfLRSpectralY = spectralGT[3] +
     593         200 :                                poDataset->GetRasterXSize() * spectralGT[4] +
     594         200 :                                poDataset->GetRasterYSize() * spectralGT[5];
     595         376 :         if (std::abs(dfLRPanX - dfLRSpectralX) > dfPixelSize ||
     596         176 :             std::abs(dfLRPanY - dfLRSpectralY) > dfPixelSize)
     597             :         {
     598          24 :             bIsThisOneNonMatching = TRUE;
     599          24 :             if (m_eGTAdjustment == GTAdjust_None)
     600             :             {
     601           2 :                 CPLError(CE_Warning, CPLE_AppDefined,
     602             :                          "Georeferencing of bottom-right corner of "
     603             :                          "pan dataset and %s do not match",
     604             :                          osSourceFilename.c_str());
     605             :             }
     606             :         }
     607             : 
     608         200 :         double dfThisMinY = dfLRSpectralY;
     609         200 :         double dfThisMaxY = spectralGT[3];
     610         200 :         if (dfThisMinY > dfThisMaxY)
     611          23 :             std::swap(dfThisMinY, dfThisMaxY);
     612         200 :         if (bIsThisOneNonMatching && m_eGTAdjustment == GTAdjust_Union)
     613             :         {
     614          20 :             dfMinX = std::min(dfMinX, spectralGT[0]);
     615          20 :             dfMinY = std::min(dfMinY, dfThisMinY);
     616          20 :             dfMaxX = std::max(dfMaxX, dfLRSpectralX);
     617          20 :             dfMaxY = std::max(dfMaxY, dfThisMaxY);
     618             :         }
     619         180 :         else if (bIsThisOneNonMatching &&
     620           4 :                  m_eGTAdjustment == GTAdjust_Intersection)
     621             :         {
     622           1 :             dfMinX = std::max(dfMinX, spectralGT[0]);
     623           1 :             dfMinY = std::max(dfMinY, dfThisMinY);
     624           1 :             dfMaxX = std::min(dfMaxX, dfLRSpectralX);
     625           1 :             dfMaxY = std::min(dfMaxY, dfThisMaxY);
     626             :         }
     627             : 
     628         200 :         bFoundNonMatchingGT = bFoundNonMatchingGT || bIsThisOneNonMatching;
     629             : 
     630         200 :         iSpectralBand++;
     631             :     }
     632             : 
     633          78 :     if (nInputSpectralBandsIn && iSpectralBand != nInputSpectralBandsIn)
     634             :     {
     635           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     636             :                  "Less SpectralBand elements than in source array");
     637           1 :         goto error;
     638             :     }
     639             : 
     640             :     /* -------------------------------------------------------------------- */
     641             :     /*      On-the-fly spatial extent adjustment if needed and asked.       */
     642             :     /* -------------------------------------------------------------------- */
     643             : 
     644          77 :     if (bFoundNonMatchingGT && (m_eGTAdjustment == GTAdjust_Union ||
     645           4 :                                 m_eGTAdjustment == GTAdjust_Intersection))
     646             :     {
     647           8 :         if (bFoundRotatingTerms)
     648             :         {
     649           0 :             CPLError(
     650             :                 CE_Failure, CPLE_NotSupported,
     651             :                 "One of the panchromatic or spectral datasets has rotating "
     652             :                 "terms in their geotransform matrix. Adjustment not possible");
     653           0 :             goto error;
     654             :         }
     655           8 :         if (m_eGTAdjustment == GTAdjust_Intersection &&
     656           1 :             (dfMinX >= dfMaxX || dfMinY >= dfMaxY))
     657             :         {
     658           0 :             CPLError(
     659             :                 CE_Failure, CPLE_NotSupported,
     660             :                 "One of the panchromatic or spectral datasets has rotating "
     661             :                 "terms in their geotransform matrix. Adjustment not possible");
     662           0 :             goto error;
     663             :         }
     664           8 :         if (m_eGTAdjustment == GTAdjust_Union)
     665           7 :             CPLDebug("VRT", "Do union of bounding box of panchromatic and "
     666             :                             "spectral datasets");
     667             :         else
     668           1 :             CPLDebug("VRT", "Do intersection of bounding box of panchromatic "
     669             :                             "and spectral datasets");
     670             : 
     671             :         // If the pandataset needs adjustments, make sure the coordinates of the
     672             :         // union/intersection properly align with the grid of the pandataset
     673             :         // to avoid annoying sub-pixel shifts on the panchro band.
     674           8 :         double dfPixelSize = std::max(panGT[1], std::abs(panGT[5]));
     675           8 :         if (std::abs(panGT[0] - dfMinX) > dfPixelSize ||
     676           3 :             std::abs(dfPanMaxY - dfMaxY) > dfPixelSize ||
     677          13 :             std::abs(dfLRPanX - dfMaxX) > dfPixelSize ||
     678           2 :             std::abs(dfPanMinY - dfMinY) > dfPixelSize)
     679             :         {
     680           6 :             dfMinX =
     681           6 :                 panGT[0] +
     682           6 :                 std::floor((dfMinX - panGT[0]) / panGT[1] + 0.5) * panGT[1];
     683           6 :             dfMaxX =
     684           6 :                 dfLRPanX +
     685           6 :                 std::floor((dfMaxX - dfLRPanX) / panGT[1] + 0.5) * panGT[1];
     686           6 :             if (panGT[5] > 0)
     687             :             {
     688           0 :                 dfMinY = dfPanMinY +
     689           0 :                          std::floor((dfMinY - dfPanMinY) / panGT[5] + 0.5) *
     690           0 :                              panGT[5];
     691           0 :                 dfMaxY = dfPanMinY +
     692           0 :                          std::floor((dfMaxY - dfPanMinY) / panGT[5] + 0.5) *
     693           0 :                              panGT[5];
     694             :             }
     695             :             else
     696             :             {
     697           6 :                 dfMinY = dfPanMaxY +
     698           6 :                          std::floor((dfMinY - dfPanMaxY) / panGT[5] + 0.5) *
     699           6 :                              panGT[5];
     700           6 :                 dfMaxY = dfPanMaxY +
     701          12 :                          std::floor((dfMaxY - dfPanMaxY) / panGT[5] + 0.5) *
     702           6 :                              panGT[5];
     703             :             }
     704             :         }
     705             : 
     706             :         std::map<CPLString, GDALDataset *>::iterator oIter =
     707           8 :             oMapNamesToDataset.begin();
     708          24 :         for (; oIter != oMapNamesToDataset.end(); ++oIter)
     709             :         {
     710          16 :             GDALDataset *poSrcDS = oIter->second;
     711          16 :             GDALGeoTransform gt;
     712          16 :             if (poSrcDS->GetGeoTransform(gt) != CE_None)
     713           3 :                 continue;
     714             : 
     715             :             // Check if this dataset needs adjustments
     716          16 :             dfPixelSize = std::max(gt[1], std::abs(gt[5]));
     717          16 :             dfPixelSize = std::max(panGT[1], dfPixelSize);
     718          16 :             dfPixelSize = std::max(std::abs(panGT[5]), dfPixelSize);
     719          16 :             double dfThisMinX = gt[0];
     720          16 :             double dfThisMaxX = gt[0] + poSrcDS->GetRasterXSize() * gt[1];
     721          16 :             double dfThisMaxY = gt[3];
     722          16 :             double dfThisMinY = gt[3] + poSrcDS->GetRasterYSize() * gt[5];
     723          16 :             if (dfThisMinY > dfThisMaxY)
     724           2 :                 std::swap(dfThisMinY, dfThisMaxY);
     725          16 :             if (std::abs(dfThisMinX - dfMinX) <= dfPixelSize &&
     726           8 :                 std::abs(dfThisMaxY - dfMaxY) <= dfPixelSize &&
     727          27 :                 std::abs(dfThisMaxX - dfMaxX) <= dfPixelSize &&
     728           3 :                 std::abs(dfThisMinY - dfMinY) <= dfPixelSize)
     729             :             {
     730           3 :                 continue;
     731             :             }
     732             : 
     733          13 :             GDALGeoTransform adjustedGT;
     734          13 :             adjustedGT[0] = dfMinX;
     735          13 :             adjustedGT[1] = gt[1];
     736          13 :             adjustedGT[2] = 0;
     737          13 :             adjustedGT[3] = gt[5] > 0 ? dfMinY : dfMaxY;
     738          13 :             adjustedGT[4] = 0;
     739          13 :             adjustedGT[5] = gt[5];
     740             :             int nAdjustRasterXSize =
     741          13 :                 static_cast<int>(0.5 + (dfMaxX - dfMinX) / adjustedGT[1]);
     742             :             int nAdjustRasterYSize = static_cast<int>(
     743          13 :                 0.5 + (dfMaxY - dfMinY) / std::abs(adjustedGT[5]));
     744             : 
     745             :             VRTDataset *poVDS =
     746          13 :                 new VRTDataset(nAdjustRasterXSize, nAdjustRasterYSize);
     747          13 :             poVDS->SetWritable(FALSE);
     748          26 :             poVDS->SetDescription(std::string("Shifted ")
     749          13 :                                       .append(poSrcDS->GetDescription())
     750          13 :                                       .c_str());
     751          13 :             poVDS->SetGeoTransform(adjustedGT);
     752          13 :             poVDS->SetProjection(poPanDataset->GetProjectionRef());
     753             : 
     754          32 :             for (int i = 0; i < poSrcDS->GetRasterCount(); i++)
     755             :             {
     756          19 :                 GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(i + 1);
     757          19 :                 poVDS->AddBand(poSrcBand->GetRasterDataType(), nullptr);
     758             :                 VRTSourcedRasterBand *poVRTBand =
     759             :                     static_cast<VRTSourcedRasterBand *>(
     760          19 :                         poVDS->GetRasterBand(i + 1));
     761             : 
     762             :                 const char *pszNBITS =
     763          19 :                     poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
     764          19 :                 if (pszNBITS)
     765           0 :                     poVRTBand->SetMetadataItem("NBITS", pszNBITS,
     766           0 :                                                "IMAGE_STRUCTURE");
     767             : 
     768          19 :                 VRTSimpleSource *poSimpleSource = new VRTSimpleSource();
     769         114 :                 poVRTBand->ConfigureSource(
     770             :                     poSimpleSource, poSrcBand, FALSE,
     771          19 :                     static_cast<int>(
     772          19 :                         std::floor((dfMinX - gt[0]) / gt[1] + 0.001)),
     773          19 :                     gt[5] < 0 ? static_cast<int>(std::floor(
     774          16 :                                     (dfMaxY - dfThisMaxY) / gt[5] + 0.001))
     775           3 :                               : static_cast<int>(std::floor(
     776           3 :                                     (dfMinY - dfThisMinY) / gt[5] + 0.001)),
     777          19 :                     static_cast<int>(0.5 + (dfMaxX - dfMinX) / gt[1]),
     778          19 :                     static_cast<int>(0.5 + (dfMaxY - dfMinY) / std::abs(gt[5])),
     779             :                     0, 0, nAdjustRasterXSize, nAdjustRasterYSize);
     780             : 
     781          19 :                 poVRTBand->AddSource(poSimpleSource);
     782             :             }
     783             : 
     784          13 :             oIter->second = poVDS;
     785          13 :             if (poSrcDS == poPanDataset)
     786             :             {
     787           6 :                 panGT = adjustedGT;
     788           6 :                 poPanDataset = poVDS;
     789           6 :                 poPanBand = poPanDataset->GetRasterBand(nPanBand);
     790           6 :                 nPanXSize = poPanDataset->GetRasterXSize();
     791           6 :                 nPanYSize = poPanDataset->GetRasterYSize();
     792             :             }
     793             : 
     794          13 :             m_apoDatasetsToReleaseRef.push_back(
     795          26 :                 std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>(
     796             :                     poVDS));
     797             :         }
     798             :     }
     799             : 
     800          77 :     if (nRasterXSize == 0 && nRasterYSize == 0)
     801             :     {
     802          57 :         nRasterXSize = nPanXSize;
     803          57 :         nRasterYSize = nPanYSize;
     804             :     }
     805          20 :     else if (nRasterXSize != nPanXSize || nRasterYSize != nPanYSize)
     806             :     {
     807           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     808             :                  "Inconsistent declared VRT dimensions with panchro dataset");
     809           1 :         goto error;
     810             :     }
     811             : 
     812             :     /* -------------------------------------------------------------------- */
     813             :     /*      Initialize all the general VRT stuff.  This will even           */
     814             :     /*      create the VRTPansharpenedRasterBands and initialize them.      */
     815             :     /* -------------------------------------------------------------------- */
     816          76 :     eErr = VRTDataset::XMLInit(psTree, pszVRTPathIn);
     817             : 
     818          76 :     if (eErr != CE_None)
     819             :     {
     820           1 :         goto error;
     821             :     }
     822             : 
     823             :     /* -------------------------------------------------------------------- */
     824             :     /*      Inherit georeferencing info from panchro band if not defined    */
     825             :     /*      in VRT.                                                         */
     826             :     /* -------------------------------------------------------------------- */
     827             : 
     828             :     {
     829          75 :         GDALGeoTransform outGT;
     830         140 :         if (GetGeoTransform(outGT) != CE_None && GetGCPCount() == 0 &&
     831          65 :             GetSpatialRef() == nullptr)
     832             :         {
     833          65 :             if (bPanGeoTransformValid)
     834             :             {
     835          65 :                 SetGeoTransform(panGT);
     836             :             }
     837          65 :             if (poPanSRS)
     838             :             {
     839          47 :                 SetSpatialRef(poPanSRS);
     840             :             }
     841             :         }
     842             :     }
     843             : 
     844             :     /* -------------------------------------------------------------------- */
     845             :     /*      Parse rest of PansharpeningOptions                              */
     846             :     /* -------------------------------------------------------------------- */
     847          75 :     iSpectralBand = 0;
     848         476 :     for (const CPLXMLNode *psIter = psOptions->psChild; psIter;
     849         401 :          psIter = psIter->psNext)
     850             :     {
     851         404 :         if (psIter->eType != CXT_Element ||
     852         404 :             !EQUAL(psIter->pszValue, "SpectralBand"))
     853         220 :             continue;
     854             : 
     855             :         GDALDataset *poDataset;
     856             :         GDALRasterBand *poBand;
     857             : 
     858         184 :         const char *pszDstBand = CPLGetXMLValue(psIter, "dstBand", nullptr);
     859         184 :         int nDstBand = -1;
     860         184 :         if (pszDstBand != nullptr)
     861             :         {
     862         178 :             nDstBand = atoi(pszDstBand);
     863         178 :             if (nDstBand <= 0)
     864             :             {
     865           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     866             :                          "SpectralBand.dstBand = '%s' invalid", pszDstBand);
     867           3 :                 goto error;
     868             :             }
     869             :         }
     870             : 
     871         183 :         if (nInputSpectralBandsIn && pahInputSpectralBandsIn != nullptr)
     872             :         {
     873          68 :             poBand = GDALRasterBand::FromHandle(
     874          34 :                 pahInputSpectralBandsIn[iSpectralBand]);
     875          34 :             poDataset = poBand->GetDataset();
     876          34 :             if (poDataset)
     877             :             {
     878          34 :                 poDataset = oMapNamesToDataset[CPLSPrintf("%p", poDataset)];
     879          34 :                 CPLAssert(poDataset);
     880          34 :                 if (poDataset)
     881          34 :                     poBand = poDataset->GetRasterBand(poBand->GetBand());
     882             :             }
     883             :         }
     884             :         else
     885             :         {
     886         149 :             osSourceFilename = CPLGetXMLValue(psIter, "SourceFilename", "");
     887         149 :             const bool bRelativeToVRT = CPL_TO_BOOL(atoi(
     888             :                 CPLGetXMLValue(psIter, "SourceFilename.relativetoVRT", "0")));
     889         149 :             if (bRelativeToVRT)
     890         188 :                 osSourceFilename = CPLProjectRelativeFilenameSafe(
     891          94 :                     pszVRTPathIn, osSourceFilename.c_str());
     892         149 :             poDataset = oMapNamesToDataset[osSourceFilename];
     893         149 :             CPLAssert(poDataset);
     894             :             const char *pszSourceBand =
     895         149 :                 CPLGetXMLValue(psIter, "SourceBand", "1");
     896         149 :             const int nBand = atoi(pszSourceBand);
     897         149 :             poBand = poDataset->GetRasterBand(nBand);
     898         149 :             if (poBand == nullptr)
     899             :             {
     900           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "%s invalid band of %s",
     901             :                          pszSourceBand, osSourceFilename.c_str());
     902           1 :                 goto error;
     903             :             }
     904             :         }
     905             : 
     906         182 :         if (bHasNoData)
     907             :         {
     908           3 :             double dfSpectralNoData = poPanBand->GetNoDataValue(&bHasNoData);
     909           3 :             if (bHasNoData && dfSpectralNoData != dfNoData)
     910           0 :                 bHasNoData = FALSE;
     911             :         }
     912             : 
     913         182 :         ahSpectralBands.push_back(poBand);
     914         182 :         if (nDstBand >= 1)
     915             :         {
     916         176 :             if (aMapDstBandToSpectralBand.find(nDstBand - 1) !=
     917         352 :                 aMapDstBandToSpectralBand.end())
     918             :             {
     919           1 :                 CPLError(
     920             :                     CE_Failure, CPLE_AppDefined,
     921             :                     "Another spectral band is already mapped to output band %d",
     922             :                     nDstBand);
     923           1 :                 goto error;
     924             :             }
     925         175 :             aMapDstBandToSpectralBand[nDstBand - 1] =
     926         175 :                 static_cast<int>(ahSpectralBands.size() - 1);
     927             :         }
     928             : 
     929         181 :         iSpectralBand++;
     930             :     }
     931             : 
     932          72 :     if (ahSpectralBands.empty())
     933             :     {
     934           1 :         CPLError(CE_Failure, CPLE_AppDefined, "No spectral band defined");
     935           1 :         goto error;
     936             :     }
     937             : 
     938             :     {
     939          71 :         const char *pszNoData = CPLGetXMLValue(psOptions, "NoData", nullptr);
     940          71 :         if (pszNoData)
     941             :         {
     942           7 :             if (EQUAL(pszNoData, "NONE"))
     943             :             {
     944           0 :                 m_bNoDataDisabled = TRUE;
     945           0 :                 bHasNoData = FALSE;
     946             :             }
     947             :             else
     948             :             {
     949           7 :                 bHasNoData = TRUE;
     950           7 :                 dfNoData = CPLAtof(pszNoData);
     951             :             }
     952             :         }
     953             :     }
     954             : 
     955          71 :     if (GetRasterCount() == 0)
     956             :     {
     957         156 :         for (int i = 0; i < static_cast<int>(aMapDstBandToSpectralBand.size());
     958             :              i++)
     959             :         {
     960         109 :             if (aMapDstBandToSpectralBand.find(i) ==
     961         218 :                 aMapDstBandToSpectralBand.end())
     962             :             {
     963           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     964             :                          "Hole in SpectralBand.dstBand numbering");
     965           1 :                 goto error;
     966             :             }
     967         108 :             GDALRasterBand *poInputBand = GDALRasterBand::FromHandle(
     968         108 :                 ahSpectralBands[aMapDstBandToSpectralBand[i]]);
     969             :             GDALRasterBand *poBand = new VRTPansharpenedRasterBand(
     970         108 :                 this, i + 1, poInputBand->GetRasterDataType());
     971         108 :             poBand->SetColorInterpretation(
     972         108 :                 poInputBand->GetColorInterpretation());
     973         108 :             if (bHasNoData)
     974          13 :                 poBand->SetNoDataValue(dfNoData);
     975         108 :             SetBand(i + 1, poBand);
     976             :         }
     977             :     }
     978             :     else
     979             :     {
     980          23 :         int nIdxAsPansharpenedBand = 0;
     981          91 :         for (int i = 0; i < nBands; i++)
     982             :         {
     983          69 :             if (static_cast<VRTRasterBand *>(GetRasterBand(i + 1))
     984          69 :                     ->IsPansharpenRasterBand())
     985             :             {
     986          65 :                 if (aMapDstBandToSpectralBand.find(i) ==
     987         130 :                     aMapDstBandToSpectralBand.end())
     988             :                 {
     989           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
     990             :                              "Band %d of type VRTPansharpenedRasterBand, but "
     991             :                              "no corresponding SpectralBand",
     992             :                              i + 1);
     993           1 :                     goto error;
     994             :                 }
     995             :                 else
     996             :                 {
     997             :                     static_cast<VRTPansharpenedRasterBand *>(
     998          64 :                         GetRasterBand(i + 1))
     999          64 :                         ->SetIndexAsPansharpenedBand(nIdxAsPansharpenedBand);
    1000          64 :                     nIdxAsPansharpenedBand++;
    1001             :                 }
    1002             :             }
    1003             :         }
    1004             :     }
    1005             : 
    1006             :     // Figure out bit depth
    1007             :     {
    1008             :         const char *pszBitDepth =
    1009          69 :             CPLGetXMLValue(psOptions, "BitDepth", nullptr);
    1010          69 :         if (pszBitDepth == nullptr)
    1011          56 :             pszBitDepth = GDALRasterBand::FromHandle(ahSpectralBands[0])
    1012          56 :                               ->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
    1013          69 :         if (pszBitDepth)
    1014          15 :             nBitDepth = atoi(pszBitDepth);
    1015          69 :         if (nBitDepth)
    1016             :         {
    1017          44 :             for (int i = 0; i < nBands; i++)
    1018             :             {
    1019          29 :                 if (!static_cast<VRTRasterBand *>(GetRasterBand(i + 1))
    1020          29 :                          ->IsPansharpenRasterBand())
    1021           2 :                     continue;
    1022          27 :                 if (GetRasterBand(i + 1)->GetMetadataItem(
    1023          27 :                         "NBITS", "IMAGE_STRUCTURE") == nullptr)
    1024             :                 {
    1025          27 :                     if (nBitDepth != 8 && nBitDepth != 16 && nBitDepth != 32)
    1026             :                     {
    1027          12 :                         GetRasterBand(i + 1)->SetMetadataItem(
    1028             :                             "NBITS", CPLSPrintf("%d", nBitDepth),
    1029           6 :                             "IMAGE_STRUCTURE");
    1030             :                     }
    1031             :                 }
    1032           0 :                 else if (nBitDepth == 8 || nBitDepth == 16 || nBitDepth == 32)
    1033             :                 {
    1034           0 :                     GetRasterBand(i + 1)->SetMetadataItem("NBITS", nullptr,
    1035           0 :                                                           "IMAGE_STRUCTURE");
    1036             :                 }
    1037             :             }
    1038             :         }
    1039             :     }
    1040             : 
    1041          69 :     if (GDALGetRasterBandXSize(ahSpectralBands[0]) >
    1042         138 :             GDALGetRasterBandXSize(poPanBand) ||
    1043          69 :         GDALGetRasterBandYSize(ahSpectralBands[0]) >
    1044          69 :             GDALGetRasterBandYSize(poPanBand))
    1045             :     {
    1046           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1047             :                  "Dimensions of spectral band larger than panchro band");
    1048             :     }
    1049             : 
    1050          69 :     aMapDstBandToSpectralBandIter = aMapDstBandToSpectralBand.begin();
    1051         236 :     for (; aMapDstBandToSpectralBandIter != aMapDstBandToSpectralBand.end();
    1052         167 :          ++aMapDstBandToSpectralBandIter)
    1053             :     {
    1054         168 :         const int nDstBand = 1 + aMapDstBandToSpectralBandIter->first;
    1055         335 :         if (nDstBand > nBands ||
    1056         167 :             !static_cast<VRTRasterBand *>(GetRasterBand(nDstBand))
    1057         167 :                  ->IsPansharpenRasterBand())
    1058             :         {
    1059           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    1060             :                      "SpectralBand.dstBand = '%d' invalid", nDstBand);
    1061           1 :             goto error;
    1062             :         }
    1063             :     }
    1064             : 
    1065          68 :     if (adfWeights.empty())
    1066             :     {
    1067         158 :         for (int i = 0; i < static_cast<int>(ahSpectralBands.size()); i++)
    1068             :         {
    1069         114 :             adfWeights.push_back(1.0 / ahSpectralBands.size());
    1070             :         }
    1071             :     }
    1072          24 :     else if (adfWeights.size() != ahSpectralBands.size())
    1073             :     {
    1074           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1075             :                  "%d weights defined, but %d input spectral bands",
    1076           1 :                  static_cast<int>(adfWeights.size()),
    1077           1 :                  static_cast<int>(ahSpectralBands.size()));
    1078           1 :         goto error;
    1079             :     }
    1080             : 
    1081          67 :     if (aMapDstBandToSpectralBand.empty())
    1082             :     {
    1083           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    1084             :                  "No spectral band is mapped to an output band");
    1085             :     }
    1086             : 
    1087             :     /* -------------------------------------------------------------------- */
    1088             :     /*      Instantiate poPansharpener                                      */
    1089             :     /* -------------------------------------------------------------------- */
    1090          67 :     psPanOptions.reset(GDALCreatePansharpenOptions());
    1091          67 :     psPanOptions->ePansharpenAlg = GDAL_PSH_WEIGHTED_BROVEY;
    1092          67 :     psPanOptions->eResampleAlg = eResampleAlg;
    1093          67 :     psPanOptions->nBitDepth = nBitDepth;
    1094          67 :     psPanOptions->nWeightCount = static_cast<int>(adfWeights.size());
    1095         134 :     psPanOptions->padfWeights =
    1096          67 :         static_cast<double *>(CPLMalloc(sizeof(double) * adfWeights.size()));
    1097          67 :     memcpy(psPanOptions->padfWeights, &adfWeights[0],
    1098          67 :            sizeof(double) * adfWeights.size());
    1099          67 :     psPanOptions->hPanchroBand = poPanBand;
    1100          67 :     psPanOptions->nInputSpectralBands =
    1101          67 :         static_cast<int>(ahSpectralBands.size());
    1102         134 :     psPanOptions->pahInputSpectralBands = static_cast<GDALRasterBandH *>(
    1103          67 :         CPLMalloc(sizeof(GDALRasterBandH) * ahSpectralBands.size()));
    1104          67 :     memcpy(psPanOptions->pahInputSpectralBands, &ahSpectralBands[0],
    1105          67 :            sizeof(GDALRasterBandH) * ahSpectralBands.size());
    1106          67 :     psPanOptions->nOutPansharpenedBands =
    1107          67 :         static_cast<int>(aMapDstBandToSpectralBand.size());
    1108         134 :     psPanOptions->panOutPansharpenedBands = static_cast<int *>(
    1109          67 :         CPLMalloc(sizeof(int) * aMapDstBandToSpectralBand.size()));
    1110          67 :     aMapDstBandToSpectralBandIter = aMapDstBandToSpectralBand.begin();
    1111         229 :     for (int i = 0;
    1112         229 :          aMapDstBandToSpectralBandIter != aMapDstBandToSpectralBand.end();
    1113         162 :          ++aMapDstBandToSpectralBandIter, ++i)
    1114             :     {
    1115         162 :         psPanOptions->panOutPansharpenedBands[i] =
    1116         162 :             aMapDstBandToSpectralBandIter->second;
    1117             :     }
    1118          67 :     psPanOptions->bHasNoData = bHasNoData;
    1119          67 :     psPanOptions->dfNoData = dfNoData;
    1120          67 :     psPanOptions->nThreads = nThreads;
    1121             : 
    1122          67 :     if (nBands == psPanOptions->nOutPansharpenedBands)
    1123          63 :         SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
    1124             : 
    1125          67 :     m_poPansharpener = std::make_unique<GDALPansharpenOperation>();
    1126          67 :     eErr = m_poPansharpener->Initialize(psPanOptions.get());
    1127          67 :     if (eErr != CE_None)
    1128             :     {
    1129             :         // Delete the pansharper object before closing the dataset
    1130             :         // because it may have warped the bands into an intermediate VRT
    1131           1 :         m_poPansharpener.reset();
    1132             : 
    1133             :         // Close in reverse order (VRT firsts and real datasets after)
    1134           4 :         for (int i = static_cast<int>(m_apoDatasetsToReleaseRef.size()) - 1;
    1135           4 :              i >= 0; i--)
    1136             :         {
    1137           3 :             m_apoDatasetsToReleaseRef[i].reset();
    1138             :         }
    1139           1 :         m_apoDatasetsToReleaseRef.clear();
    1140             :     }
    1141             : 
    1142          67 :     return eErr;
    1143             : 
    1144          15 : error:
    1145             :     // Close in reverse order (VRT firsts and real datasets after)
    1146          63 :     for (int i = static_cast<int>(m_apoDatasetsToReleaseRef.size()) - 1; i >= 0;
    1147             :          i--)
    1148             :     {
    1149          48 :         m_apoDatasetsToReleaseRef[i].reset();
    1150             :     }
    1151          15 :     m_apoDatasetsToReleaseRef.clear();
    1152          15 :     return CE_Failure;
    1153             : }
    1154             : 
    1155             : /************************************************************************/
    1156             : /*                           SerializeToXML()                           */
    1157             : /************************************************************************/
    1158             : 
    1159           7 : CPLXMLNode *VRTPansharpenedDataset::SerializeToXML(const char *pszVRTPathIn)
    1160             : 
    1161             : {
    1162           7 :     CPLXMLNode *psTree = VRTDataset::SerializeToXML(pszVRTPathIn);
    1163             : 
    1164           7 :     if (psTree == nullptr)
    1165           0 :         return psTree;
    1166             : 
    1167             :     /* -------------------------------------------------------------------- */
    1168             :     /*      Set subclass.                                                   */
    1169             :     /* -------------------------------------------------------------------- */
    1170           7 :     CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
    1171             :                      CXT_Text, "VRTPansharpenedDataset");
    1172             : 
    1173             :     /* -------------------------------------------------------------------- */
    1174             :     /*      Serialize the block size.                                       */
    1175             :     /* -------------------------------------------------------------------- */
    1176           7 :     CPLCreateXMLElementAndValue(psTree, "BlockXSize",
    1177             :                                 CPLSPrintf("%d", m_nBlockXSize));
    1178           7 :     CPLCreateXMLElementAndValue(psTree, "BlockYSize",
    1179             :                                 CPLSPrintf("%d", m_nBlockYSize));
    1180             : 
    1181             :     /* -------------------------------------------------------------------- */
    1182             :     /*      Serialize the options.                                          */
    1183             :     /* -------------------------------------------------------------------- */
    1184           7 :     if (m_poPansharpener == nullptr)
    1185           0 :         return psTree;
    1186           7 :     const GDALPansharpenOptions *psOptions = m_poPansharpener->GetOptions();
    1187           7 :     if (psOptions == nullptr)
    1188           0 :         return psTree;
    1189             : 
    1190             :     CPLXMLNode *psOptionsNode =
    1191           7 :         CPLCreateXMLNode(psTree, CXT_Element, "PansharpeningOptions");
    1192             : 
    1193           7 :     if (psOptions->ePansharpenAlg == GDAL_PSH_WEIGHTED_BROVEY)
    1194             :     {
    1195           7 :         CPLCreateXMLElementAndValue(psOptionsNode, "Algorithm",
    1196             :                                     "WeightedBrovey");
    1197             :     }
    1198             :     else
    1199             :     {
    1200           0 :         CPLAssert(false);
    1201             :     }
    1202           7 :     if (psOptions->nWeightCount)
    1203             :     {
    1204          14 :         CPLString osWeights;
    1205          27 :         for (int i = 0; i < psOptions->nWeightCount; i++)
    1206             :         {
    1207          20 :             if (i)
    1208          13 :                 osWeights += ",";
    1209          20 :             osWeights += CPLSPrintf("%.16g", psOptions->padfWeights[i]);
    1210             :         }
    1211           7 :         CPLCreateXMLElementAndValue(
    1212             :             CPLCreateXMLNode(psOptionsNode, CXT_Element, "AlgorithmOptions"),
    1213             :             "Weights", osWeights.c_str());
    1214             :     }
    1215           7 :     CPLCreateXMLElementAndValue(
    1216             :         psOptionsNode, "Resampling",
    1217           7 :         GDALRasterIOGetResampleAlg(psOptions->eResampleAlg));
    1218             : 
    1219           7 :     if (psOptions->nThreads == -1)
    1220             :     {
    1221           6 :         CPLCreateXMLElementAndValue(psOptionsNode, "NumThreads", "ALL_CPUS");
    1222             :     }
    1223           1 :     else if (psOptions->nThreads > 1)
    1224             :     {
    1225           0 :         CPLCreateXMLElementAndValue(psOptionsNode, "NumThreads",
    1226           0 :                                     CPLSPrintf("%d", psOptions->nThreads));
    1227             :     }
    1228             : 
    1229           7 :     if (psOptions->nBitDepth)
    1230           0 :         CPLCreateXMLElementAndValue(psOptionsNode, "BitDepth",
    1231           0 :                                     CPLSPrintf("%d", psOptions->nBitDepth));
    1232             : 
    1233           7 :     const char *pszAdjust = nullptr;
    1234           7 :     switch (m_eGTAdjustment)
    1235             :     {
    1236           7 :         case GTAdjust_Union:
    1237           7 :             pszAdjust = "Union";
    1238           7 :             break;
    1239           0 :         case GTAdjust_Intersection:
    1240           0 :             pszAdjust = "Intersection";
    1241           0 :             break;
    1242           0 :         case GTAdjust_None:
    1243           0 :             pszAdjust = "None";
    1244           0 :             break;
    1245           0 :         case GTAdjust_NoneWithoutWarning:
    1246           0 :             pszAdjust = "NoneWithoutWarning";
    1247           0 :             break;
    1248           0 :         default:
    1249           0 :             break;
    1250             :     }
    1251             : 
    1252           7 :     if (psOptions->bHasNoData)
    1253             :     {
    1254           1 :         CPLCreateXMLElementAndValue(psOptionsNode, "NoData",
    1255           1 :                                     CPLSPrintf("%.16g", psOptions->dfNoData));
    1256             :     }
    1257           6 :     else if (m_bNoDataDisabled)
    1258             :     {
    1259           0 :         CPLCreateXMLElementAndValue(psOptionsNode, "NoData", "None");
    1260             :     }
    1261             : 
    1262           7 :     if (pszAdjust)
    1263           7 :         CPLCreateXMLElementAndValue(psOptionsNode, "SpatialExtentAdjustment",
    1264             :                                     pszAdjust);
    1265             : 
    1266           7 :     if (psOptions->hPanchroBand)
    1267             :     {
    1268             :         CPLXMLNode *psBand =
    1269           7 :             CPLCreateXMLNode(psOptionsNode, CXT_Element, "PanchroBand");
    1270             :         GDALRasterBand *poBand =
    1271           7 :             GDALRasterBand::FromHandle(psOptions->hPanchroBand);
    1272           7 :         if (poBand->GetDataset())
    1273             :         {
    1274           7 :             auto poPanchoDS = poBand->GetDataset();
    1275             :             std::map<CPLString, CPLString>::iterator oIter =
    1276           7 :                 m_oMapToRelativeFilenames.find(poPanchoDS->GetDescription());
    1277           7 :             if (oIter == m_oMapToRelativeFilenames.end())
    1278             :             {
    1279           6 :                 CPLCreateXMLElementAndValue(psBand, "SourceFilename",
    1280           6 :                                             poPanchoDS->GetDescription());
    1281             :             }
    1282             :             else
    1283             :             {
    1284           1 :                 CPLXMLNode *psSourceFilename = CPLCreateXMLElementAndValue(
    1285           1 :                     psBand, "SourceFilename", oIter->second);
    1286           1 :                 CPLCreateXMLNode(CPLCreateXMLNode(psSourceFilename,
    1287             :                                                   CXT_Attribute,
    1288             :                                                   "relativeToVRT"),
    1289             :                                  CXT_Text, "1");
    1290             :             }
    1291             : 
    1292           7 :             GDALSerializeOpenOptionsToXML(psBand, poPanchoDS->GetOpenOptions());
    1293             : 
    1294           7 :             CPLCreateXMLElementAndValue(psBand, "SourceBand",
    1295             :                                         CPLSPrintf("%d", poBand->GetBand()));
    1296             :         }
    1297             :     }
    1298          27 :     for (int i = 0; i < psOptions->nInputSpectralBands; i++)
    1299             :     {
    1300             :         CPLXMLNode *psBand =
    1301          20 :             CPLCreateXMLNode(psOptionsNode, CXT_Element, "SpectralBand");
    1302             : 
    1303          39 :         for (int j = 0; j < psOptions->nOutPansharpenedBands; j++)
    1304             :         {
    1305          39 :             if (psOptions->panOutPansharpenedBands[j] == i)
    1306             :             {
    1307          40 :                 for (int k = 0; k < nBands; k++)
    1308             :                 {
    1309          40 :                     if (static_cast<VRTRasterBand *>(GetRasterBand(k + 1))
    1310          40 :                             ->IsPansharpenRasterBand())
    1311             :                     {
    1312          39 :                         if (static_cast<VRTPansharpenedRasterBand *>(
    1313          39 :                                 GetRasterBand(k + 1))
    1314          39 :                                 ->GetIndexAsPansharpenedBand() == j)
    1315             :                         {
    1316          20 :                             CPLCreateXMLNode(CPLCreateXMLNode(psBand,
    1317             :                                                               CXT_Attribute,
    1318             :                                                               "dstBand"),
    1319             :                                              CXT_Text, CPLSPrintf("%d", k + 1));
    1320          20 :                             break;
    1321             :                         }
    1322             :                     }
    1323             :                 }
    1324          20 :                 break;
    1325             :             }
    1326             :         }
    1327             : 
    1328             :         GDALRasterBand *poBand =
    1329          20 :             GDALRasterBand::FromHandle(psOptions->pahInputSpectralBands[i]);
    1330          20 :         if (poBand->GetDataset())
    1331             :         {
    1332          20 :             auto poSpectralDS = poBand->GetDataset();
    1333             :             std::map<CPLString, CPLString>::iterator oIter =
    1334          20 :                 m_oMapToRelativeFilenames.find(poSpectralDS->GetDescription());
    1335          20 :             if (oIter == m_oMapToRelativeFilenames.end())
    1336             :             {
    1337          20 :                 CPLCreateXMLElementAndValue(psBand, "SourceFilename",
    1338          20 :                                             poSpectralDS->GetDescription());
    1339             :             }
    1340             :             else
    1341             :             {
    1342           0 :                 CPLXMLNode *psSourceFilename = CPLCreateXMLElementAndValue(
    1343           0 :                     psBand, "SourceFilename", oIter->second);
    1344           0 :                 CPLCreateXMLNode(CPLCreateXMLNode(psSourceFilename,
    1345             :                                                   CXT_Attribute,
    1346             :                                                   "relativeToVRT"),
    1347             :                                  CXT_Text, "1");
    1348             :             }
    1349             : 
    1350          20 :             GDALSerializeOpenOptionsToXML(psBand,
    1351          20 :                                           poSpectralDS->GetOpenOptions());
    1352             : 
    1353          20 :             CPLCreateXMLElementAndValue(psBand, "SourceBand",
    1354             :                                         CPLSPrintf("%d", poBand->GetBand()));
    1355             :         }
    1356             :     }
    1357             : 
    1358           7 :     return psTree;
    1359             : }
    1360             : 
    1361             : /************************************************************************/
    1362             : /*                            GetBlockSize()                            */
    1363             : /************************************************************************/
    1364             : 
    1365         192 : void VRTPansharpenedDataset::GetBlockSize(int *pnBlockXSize,
    1366             :                                           int *pnBlockYSize) const
    1367             : 
    1368             : {
    1369         192 :     assert(nullptr != pnBlockXSize);
    1370         192 :     assert(nullptr != pnBlockYSize);
    1371             : 
    1372         192 :     *pnBlockXSize = m_nBlockXSize;
    1373         192 :     *pnBlockYSize = m_nBlockYSize;
    1374         192 : }
    1375             : 
    1376             : /************************************************************************/
    1377             : /*                              AddBand()                               */
    1378             : /************************************************************************/
    1379             : 
    1380           1 : CPLErr VRTPansharpenedDataset::AddBand(CPL_UNUSED GDALDataType eType,
    1381             :                                        CPL_UNUSED char **papszOptions)
    1382             : 
    1383             : {
    1384           1 :     CPLError(CE_Failure, CPLE_NotSupported, "AddBand() not supported");
    1385             : 
    1386           1 :     return CE_Failure;
    1387             : }
    1388             : 
    1389             : /************************************************************************/
    1390             : /*                              IRasterIO()                             */
    1391             : /************************************************************************/
    1392             : 
    1393          21 : CPLErr VRTPansharpenedDataset::IRasterIO(
    1394             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
    1395             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    1396             :     int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
    1397             :     GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
    1398             : {
    1399          21 :     if (eRWFlag == GF_Write)
    1400           0 :         return CE_Failure;
    1401             : 
    1402             :     /* Try to pass the request to the most appropriate overview dataset */
    1403          21 :     if (nBufXSize < nXSize && nBufYSize < nYSize)
    1404             :     {
    1405             :         int bTried;
    1406           1 :         CPLErr eErr = TryOverviewRasterIO(
    1407             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
    1408             :             eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
    1409             :             nBandSpace, psExtraArg, &bTried);
    1410           1 :         if (bTried)
    1411           0 :             return eErr;
    1412             :     }
    1413             : 
    1414          21 :     const int nDataTypeSize = GDALGetDataTypeSizeBytes(eBufType);
    1415          21 :     if (nXSize == nBufXSize && nYSize == nBufYSize &&
    1416          20 :         nDataTypeSize == nPixelSpace && nLineSpace == nPixelSpace * nBufXSize &&
    1417          20 :         nBandSpace == nLineSpace * nBufYSize && nBandCount == nBands)
    1418             :     {
    1419          82 :         for (int i = 0; i < nBands; i++)
    1420             :         {
    1421         124 :             if (panBandMap[i] != i + 1 ||
    1422          62 :                 !static_cast<VRTRasterBand *>(GetRasterBand(i + 1))
    1423          62 :                      ->IsPansharpenRasterBand())
    1424             :             {
    1425           0 :                 goto default_path;
    1426             :             }
    1427             :         }
    1428             : 
    1429             :         //{static int bDone = 0; if (!bDone) printf("(2)\n"); bDone = 1; }
    1430          20 :         return m_poPansharpener->ProcessRegion(nXOff, nYOff, nXSize, nYSize,
    1431          20 :                                                pData, eBufType);
    1432             :     }
    1433             : 
    1434           1 : default_path:
    1435             :     //{static int bDone = 0; if (!bDone) printf("(3)\n"); bDone = 1; }
    1436           1 :     return VRTDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
    1437             :                                  nBufXSize, nBufYSize, eBufType, nBandCount,
    1438             :                                  panBandMap, nPixelSpace, nLineSpace,
    1439           1 :                                  nBandSpace, psExtraArg);
    1440             : }
    1441             : 
    1442             : /************************************************************************/
    1443             : /* ==================================================================== */
    1444             : /*                        VRTPansharpenedRasterBand                     */
    1445             : /* ==================================================================== */
    1446             : /************************************************************************/
    1447             : 
    1448             : /************************************************************************/
    1449             : /*                        VRTPansharpenedRasterBand()                   */
    1450             : /************************************************************************/
    1451             : 
    1452         192 : VRTPansharpenedRasterBand::VRTPansharpenedRasterBand(GDALDataset *poDSIn,
    1453             :                                                      int nBandIn,
    1454         192 :                                                      GDALDataType eDataTypeIn)
    1455         192 :     : m_nIndexAsPansharpenedBand(nBandIn - 1)
    1456             : {
    1457         192 :     Initialize(poDSIn->GetRasterXSize(), poDSIn->GetRasterYSize());
    1458             : 
    1459         192 :     poDS = poDSIn;
    1460         192 :     nBand = nBandIn;
    1461         192 :     eAccess = GA_Update;
    1462         192 :     eDataType = eDataTypeIn;
    1463             : 
    1464         192 :     static_cast<VRTPansharpenedDataset *>(poDS)->GetBlockSize(&nBlockXSize,
    1465             :                                                               &nBlockYSize);
    1466         192 : }
    1467             : 
    1468             : /************************************************************************/
    1469             : /*                        ~VRTPansharpenedRasterBand()                  */
    1470             : /************************************************************************/
    1471             : 
    1472         384 : VRTPansharpenedRasterBand::~VRTPansharpenedRasterBand()
    1473             : 
    1474             : {
    1475         192 :     FlushCache(true);
    1476         384 : }
    1477             : 
    1478             : /************************************************************************/
    1479             : /*                             IReadBlock()                             */
    1480             : /************************************************************************/
    1481             : 
    1482          26 : CPLErr VRTPansharpenedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
    1483             :                                              void *pImage)
    1484             : 
    1485             : {
    1486          26 :     const int nReqXOff = nBlockXOff * nBlockXSize;
    1487          26 :     const int nReqYOff = nBlockYOff * nBlockYSize;
    1488          26 :     int nReqXSize = nBlockXSize;
    1489          26 :     int nReqYSize = nBlockYSize;
    1490          26 :     if (nReqXOff + nReqXSize > nRasterXSize)
    1491          13 :         nReqXSize = nRasterXSize - nReqXOff;
    1492          26 :     if (nReqYOff + nReqYSize > nRasterYSize)
    1493          22 :         nReqYSize = nRasterYSize - nReqYOff;
    1494             : 
    1495             :     //{static int bDone = 0; if (!bDone) printf("(4)\n"); bDone = 1; }
    1496          26 :     const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
    1497             :     GDALRasterIOExtraArg sExtraArg;
    1498          26 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    1499          52 :     if (IRasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize, pImage,
    1500             :                   nReqXSize, nReqYSize, eDataType, nDataTypeSize,
    1501          26 :                   static_cast<GSpacing>(nDataTypeSize) * nReqXSize,
    1502          26 :                   &sExtraArg) != CE_None)
    1503             :     {
    1504           0 :         return CE_Failure;
    1505             :     }
    1506             : 
    1507          26 :     if (nReqXSize < nBlockXSize)
    1508             :     {
    1509        5613 :         for (int j = nReqYSize - 1; j >= 0; j--)
    1510             :         {
    1511        5600 :             memmove(static_cast<GByte *>(pImage) +
    1512        5600 :                         static_cast<size_t>(j) * nDataTypeSize * nBlockXSize,
    1513        5600 :                     static_cast<GByte *>(pImage) +
    1514        5600 :                         static_cast<size_t>(j) * nDataTypeSize * nReqXSize,
    1515        5600 :                     static_cast<size_t>(nReqXSize) * nDataTypeSize);
    1516        5600 :             memset(static_cast<GByte *>(pImage) +
    1517        5600 :                        (static_cast<size_t>(j) * nBlockXSize + nReqXSize) *
    1518        5600 :                            nDataTypeSize,
    1519             :                    0,
    1520        5600 :                    static_cast<size_t>(nBlockXSize - nReqXSize) *
    1521        5600 :                        nDataTypeSize);
    1522             :         }
    1523             :     }
    1524          26 :     if (nReqYSize < nBlockYSize)
    1525             :     {
    1526          22 :         memset(static_cast<GByte *>(pImage) +
    1527          22 :                    static_cast<size_t>(nReqYSize) * nBlockXSize * nDataTypeSize,
    1528             :                0,
    1529          22 :                static_cast<size_t>(nBlockYSize - nReqYSize) * nBlockXSize *
    1530          22 :                    nDataTypeSize);
    1531             :     }
    1532             : 
    1533             :     // Cache other bands
    1534          26 :     CPLErr eErr = CE_None;
    1535          26 :     VRTPansharpenedDataset *poGDS = static_cast<VRTPansharpenedDataset *>(poDS);
    1536          26 :     if (poGDS->nBands != 1 && !poGDS->m_bLoadingOtherBands)
    1537             :     {
    1538          10 :         poGDS->m_bLoadingOtherBands = TRUE;
    1539             : 
    1540          36 :         for (int iOtherBand = 1; iOtherBand <= poGDS->nBands; iOtherBand++)
    1541             :         {
    1542          26 :             if (iOtherBand == nBand)
    1543          10 :                 continue;
    1544             : 
    1545             :             GDALRasterBlock *poBlock =
    1546          16 :                 poGDS->GetRasterBand(iOtherBand)
    1547          16 :                     ->GetLockedBlockRef(nBlockXOff, nBlockYOff);
    1548          16 :             if (poBlock == nullptr)
    1549             :             {
    1550           0 :                 eErr = CE_Failure;
    1551           0 :                 break;
    1552             :             }
    1553          16 :             poBlock->DropLock();
    1554             :         }
    1555             : 
    1556          10 :         poGDS->m_bLoadingOtherBands = FALSE;
    1557             :     }
    1558             : 
    1559          26 :     return eErr;
    1560             : }
    1561             : 
    1562             : /************************************************************************/
    1563             : /*                              IRasterIO()                             */
    1564             : /************************************************************************/
    1565             : 
    1566         127 : CPLErr VRTPansharpenedRasterBand::IRasterIO(
    1567             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
    1568             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    1569             :     GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
    1570             : {
    1571         127 :     if (eRWFlag == GF_Write)
    1572           0 :         return CE_Failure;
    1573             : 
    1574         127 :     VRTPansharpenedDataset *poGDS = static_cast<VRTPansharpenedDataset *>(poDS);
    1575             : 
    1576             :     /* Try to pass the request to the most appropriate overview dataset */
    1577         127 :     if (nBufXSize < nXSize && nBufYSize < nYSize)
    1578             :     {
    1579             :         int bTried;
    1580           3 :         CPLErr eErr = TryOverviewRasterIO(
    1581             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
    1582             :             eBufType, nPixelSpace, nLineSpace, psExtraArg, &bTried);
    1583           3 :         if (bTried)
    1584           0 :             return eErr;
    1585             :     }
    1586             : 
    1587         127 :     const int nDataTypeSize = GDALGetDataTypeSizeBytes(eBufType);
    1588         127 :     if (nDataTypeSize > 0 && nXSize == nBufXSize && nYSize == nBufYSize &&
    1589         124 :         nDataTypeSize == nPixelSpace && nLineSpace == nPixelSpace * nBufXSize)
    1590             :     {
    1591             :         const GDALPansharpenOptions *psOptions =
    1592         124 :             poGDS->m_poPansharpener->GetOptions();
    1593             : 
    1594             :         // Have we already done this request for another band ?
    1595             :         // If so use the cached result
    1596         124 :         const size_t nBufferSizePerBand =
    1597         124 :             static_cast<size_t>(nXSize) * nYSize * nDataTypeSize;
    1598         124 :         if (nXOff == poGDS->m_nLastBandRasterIOXOff &&
    1599         118 :             nYOff >= poGDS->m_nLastBandRasterIOYOff &&
    1600         111 :             nXSize == poGDS->m_nLastBandRasterIOXSize &&
    1601          69 :             nYOff + nYSize <= poGDS->m_nLastBandRasterIOYOff +
    1602          69 :                                   poGDS->m_nLastBandRasterIOYSize &&
    1603          59 :             eBufType == poGDS->m_eLastBandRasterIODataType)
    1604             :         {
    1605             :             //{static int bDone = 0; if (!bDone) printf("(6)\n"); bDone = 1; }
    1606          53 :             if (poGDS->m_pabyLastBufferBandRasterIO == nullptr)
    1607           0 :                 return CE_Failure;
    1608          53 :             const size_t nBufferSizePerBandCached =
    1609          53 :                 static_cast<size_t>(nXSize) * poGDS->m_nLastBandRasterIOYSize *
    1610          53 :                 nDataTypeSize;
    1611          53 :             memcpy(pData,
    1612          53 :                    poGDS->m_pabyLastBufferBandRasterIO +
    1613          53 :                        nBufferSizePerBandCached * m_nIndexAsPansharpenedBand +
    1614          53 :                        static_cast<size_t>(nYOff -
    1615          53 :                                            poGDS->m_nLastBandRasterIOYOff) *
    1616          53 :                            nXSize * nDataTypeSize,
    1617             :                    nBufferSizePerBand);
    1618          53 :             return CE_None;
    1619             :         }
    1620             : 
    1621          71 :         int nYSizeToCache = nYSize;
    1622          71 :         if (nYSize == 1 && nXSize == nRasterXSize)
    1623             :         {
    1624             :             //{static int bDone = 0; if (!bDone) printf("(7)\n"); bDone = 1; }
    1625             :             // For efficiency, try to cache at leak 256 K
    1626           0 :             nYSizeToCache = (256 * 1024) / nXSize / nDataTypeSize;
    1627           0 :             if (nYSizeToCache == 0)
    1628           0 :                 nYSizeToCache = 1;
    1629           0 :             else if (nYOff + nYSizeToCache > nRasterYSize)
    1630           0 :                 nYSizeToCache = nRasterYSize - nYOff;
    1631             :         }
    1632          71 :         const GUIntBig nBufferSize = static_cast<GUIntBig>(nXSize) *
    1633          71 :                                      nYSizeToCache * nDataTypeSize *
    1634          71 :                                      psOptions->nOutPansharpenedBands;
    1635             :         // Check the we don't overflow (for 32 bit platforms)
    1636          71 :         if (nBufferSize > std::numeric_limits<size_t>::max() / 2)
    1637             :         {
    1638           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    1639             :                      "Out of memory error while allocating working buffers");
    1640           0 :             return CE_Failure;
    1641             :         }
    1642             :         GByte *pabyTemp = static_cast<GByte *>(
    1643          71 :             VSI_REALLOC_VERBOSE(poGDS->m_pabyLastBufferBandRasterIO,
    1644             :                                 static_cast<size_t>(nBufferSize)));
    1645          71 :         if (pabyTemp == nullptr)
    1646             :         {
    1647           0 :             return CE_Failure;
    1648             :         }
    1649          71 :         poGDS->m_nLastBandRasterIOXOff = nXOff;
    1650          71 :         poGDS->m_nLastBandRasterIOYOff = nYOff;
    1651          71 :         poGDS->m_nLastBandRasterIOXSize = nXSize;
    1652          71 :         poGDS->m_nLastBandRasterIOYSize = nYSizeToCache;
    1653          71 :         poGDS->m_eLastBandRasterIODataType = eBufType;
    1654          71 :         poGDS->m_pabyLastBufferBandRasterIO = pabyTemp;
    1655             : 
    1656          71 :         CPLErr eErr = poGDS->m_poPansharpener->ProcessRegion(
    1657             :             nXOff, nYOff, nXSize, nYSizeToCache,
    1658          71 :             poGDS->m_pabyLastBufferBandRasterIO, eBufType);
    1659          71 :         if (eErr == CE_None)
    1660             :         {
    1661             :             //{static int bDone = 0; if (!bDone) printf("(8)\n"); bDone = 1; }
    1662          71 :             size_t nBufferSizePerBandCached = static_cast<size_t>(nXSize) *
    1663          71 :                                               poGDS->m_nLastBandRasterIOYSize *
    1664          71 :                                               nDataTypeSize;
    1665          71 :             memcpy(pData,
    1666          71 :                    poGDS->m_pabyLastBufferBandRasterIO +
    1667          71 :                        nBufferSizePerBandCached * m_nIndexAsPansharpenedBand,
    1668             :                    nBufferSizePerBand);
    1669             :         }
    1670             :         else
    1671             :         {
    1672           0 :             VSIFree(poGDS->m_pabyLastBufferBandRasterIO);
    1673           0 :             poGDS->m_pabyLastBufferBandRasterIO = nullptr;
    1674             :         }
    1675          71 :         return eErr;
    1676             :     }
    1677             : 
    1678             :     //{static int bDone = 0; if (!bDone) printf("(9)\n"); bDone = 1; }
    1679           3 :     CPLErr eErr = VRTRasterBand::IRasterIO(
    1680             :         eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
    1681             :         eBufType, nPixelSpace, nLineSpace, psExtraArg);
    1682             : 
    1683           3 :     return eErr;
    1684             : }
    1685             : 
    1686             : /************************************************************************/
    1687             : /*                           SerializeToXML()                           */
    1688             : /************************************************************************/
    1689             : 
    1690             : CPLXMLNode *
    1691          20 : VRTPansharpenedRasterBand::SerializeToXML(const char *pszVRTPathIn,
    1692             :                                           bool &bHasWarnedAboutRAMUsage,
    1693             :                                           size_t &nAccRAMUsage)
    1694             : 
    1695             : {
    1696          20 :     CPLXMLNode *psTree = VRTRasterBand::SerializeToXML(
    1697             :         pszVRTPathIn, bHasWarnedAboutRAMUsage, nAccRAMUsage);
    1698             : 
    1699             :     /* -------------------------------------------------------------------- */
    1700             :     /*      Set subclass.                                                   */
    1701             :     /* -------------------------------------------------------------------- */
    1702          20 :     CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
    1703             :                      CXT_Text, "VRTPansharpenedRasterBand");
    1704             : 
    1705          20 :     return psTree;
    1706             : }
    1707             : 
    1708             : /************************************************************************/
    1709             : /*                         GetOverviewCount()                           */
    1710             : /************************************************************************/
    1711             : 
    1712          23 : int VRTPansharpenedRasterBand::GetOverviewCount()
    1713             : {
    1714          23 :     VRTPansharpenedDataset *poGDS = static_cast<VRTPansharpenedDataset *>(poDS);
    1715             : 
    1716             :     // Build on-the-fly overviews from overviews of pan and spectral bands
    1717          46 :     if (poGDS->m_poPansharpener != nullptr &&
    1718          46 :         poGDS->m_apoOverviewDatasets.empty() && poGDS->m_poMainDataset == poGDS)
    1719             :     {
    1720             :         const GDALPansharpenOptions *psOptions =
    1721          19 :             poGDS->m_poPansharpener->GetOptions();
    1722             : 
    1723             :         GDALRasterBand *poPanBand =
    1724          19 :             GDALRasterBand::FromHandle(psOptions->hPanchroBand);
    1725          19 :         int nOvrCount = poPanBand->GetOverviewCount();
    1726          19 :         if (nOvrCount > 0)
    1727             :         {
    1728          14 :             for (int i = 0; i < poGDS->GetRasterCount(); i++)
    1729             :             {
    1730          10 :                 if (!static_cast<VRTRasterBand *>(poGDS->GetRasterBand(i + 1))
    1731          10 :                          ->IsPansharpenRasterBand())
    1732             :                 {
    1733           0 :                     return 0;
    1734             :                 }
    1735             :             }
    1736             : 
    1737             :             // Limit number of overviews of the VRTPansharpenedRasterBand to
    1738             :             // the minimum number of overviews of the pan and spectral source
    1739             :             // bands, and also make sure all spectral bands have overviews
    1740             :             // of the same dimension for a given level.
    1741           4 :             std::vector<std::pair<int, int>> sizeSpectralOverviews;
    1742          14 :             for (int i = 0; i < psOptions->nInputSpectralBands; i++)
    1743             :             {
    1744          10 :                 auto poSpectralBand = GDALRasterBand::FromHandle(
    1745          10 :                     psOptions->pahInputSpectralBands[i]);
    1746          10 :                 nOvrCount =
    1747          10 :                     std::min(nOvrCount, poSpectralBand->GetOverviewCount());
    1748          10 :                 if (i == 0)
    1749             :                 {
    1750           7 :                     for (int iOvr = 0; iOvr < nOvrCount; ++iOvr)
    1751             :                     {
    1752           3 :                         auto poOvrBand = poSpectralBand->GetOverview(iOvr);
    1753             :                         sizeSpectralOverviews.emplace_back(
    1754           3 :                             poOvrBand->GetXSize(), poOvrBand->GetYSize());
    1755             :                     }
    1756             :                 }
    1757             :                 else
    1758             :                 {
    1759          10 :                     for (int iOvr = 0; iOvr < nOvrCount; ++iOvr)
    1760             :                     {
    1761           4 :                         auto poOvrBand = poSpectralBand->GetOverview(iOvr);
    1762           4 :                         if (sizeSpectralOverviews[iOvr].first !=
    1763           8 :                                 poOvrBand->GetXSize() ||
    1764           4 :                             sizeSpectralOverviews[iOvr].second !=
    1765           4 :                                 poOvrBand->GetYSize())
    1766             :                         {
    1767           0 :                             nOvrCount = iOvr;
    1768           0 :                             break;
    1769             :                         }
    1770             :                     }
    1771             :                 }
    1772             :             }
    1773             : 
    1774           4 :             auto poPanBandDS = poPanBand->GetDataset();
    1775           7 :             for (int j = 0; j < nOvrCount; j++)
    1776             :             {
    1777             :                 std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>
    1778           3 :                     poPanOvrDS(GDALCreateOverviewDataset(poPanBandDS, j, true));
    1779           3 :                 if (!poPanOvrDS)
    1780             :                 {
    1781           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1782             :                              "GDALCreateOverviewDataset(poPanBandDS, %d, true) "
    1783             :                              "failed",
    1784             :                              j);
    1785           0 :                     break;
    1786             :                 }
    1787             :                 GDALRasterBand *poPanOvrBand =
    1788           3 :                     poPanOvrDS->GetRasterBand(poPanBand->GetBand());
    1789             :                 auto poOvrDS = std::make_unique<VRTPansharpenedDataset>(
    1790           3 :                     poPanOvrBand->GetXSize(), poPanOvrBand->GetYSize());
    1791           6 :                 poOvrDS->m_apoDatasetsToReleaseRef.push_back(
    1792           3 :                     std::move(poPanOvrDS));
    1793          10 :                 for (int i = 0; i < poGDS->GetRasterCount(); i++)
    1794             :                 {
    1795           7 :                     GDALRasterBand *poSrcBand = poGDS->GetRasterBand(i + 1);
    1796             :                     GDALRasterBand *poBand = new VRTPansharpenedRasterBand(
    1797           7 :                         poOvrDS.get(), i + 1, poSrcBand->GetRasterDataType());
    1798             :                     const char *pszNBITS =
    1799           7 :                         poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
    1800           7 :                     if (pszNBITS)
    1801           0 :                         poBand->SetMetadataItem("NBITS", pszNBITS,
    1802           0 :                                                 "IMAGE_STRUCTURE");
    1803           7 :                     poOvrDS->SetBand(i + 1, poBand);
    1804             :                 }
    1805             : 
    1806             :                 std::unique_ptr<GDALPansharpenOptions,
    1807             :                                 decltype(&GDALDestroyPansharpenOptions)>
    1808             :                     psPanOvrOptions(GDALClonePansharpenOptions(psOptions),
    1809           3 :                                     GDALDestroyPansharpenOptions);
    1810           3 :                 psPanOvrOptions->hPanchroBand = poPanOvrBand;
    1811          10 :                 for (int i = 0; i < psOptions->nInputSpectralBands; i++)
    1812             :                 {
    1813           7 :                     auto poSpectralBand = GDALRasterBand::FromHandle(
    1814           7 :                         psOptions->pahInputSpectralBands[i]);
    1815             :                     std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>
    1816             :                         poSpectralOvrDS(GDALCreateOverviewDataset(
    1817           7 :                             poSpectralBand->GetDataset(), j, true));
    1818           7 :                     if (!poSpectralOvrDS)
    1819             :                     {
    1820           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1821             :                                  "GDALCreateOverviewDataset(poSpectralBand->"
    1822             :                                  "GetDataset(), %d, true) failed",
    1823             :                                  j);
    1824           0 :                         return 0;
    1825             :                     }
    1826          14 :                     psPanOvrOptions->pahInputSpectralBands[i] =
    1827           7 :                         poSpectralOvrDS->GetRasterBand(
    1828             :                             poSpectralBand->GetBand());
    1829          14 :                     poOvrDS->m_apoDatasetsToReleaseRef.push_back(
    1830           7 :                         std::move(poSpectralOvrDS));
    1831             :                 }
    1832           3 :                 poOvrDS->m_poPansharpener =
    1833           6 :                     std::make_unique<GDALPansharpenOperation>();
    1834           6 :                 if (poOvrDS->m_poPansharpener->Initialize(
    1835           6 :                         psPanOvrOptions.get()) != CE_None)
    1836             :                 {
    1837           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1838             :                              "Unable to initialize pansharpener.");
    1839           0 :                     return 0;
    1840             :                 }
    1841             : 
    1842           3 :                 poOvrDS->m_poMainDataset = poGDS;
    1843           3 :                 poOvrDS->SetMetadataItem("INTERLEAVE", "PIXEL",
    1844           3 :                                          "IMAGE_STRUCTURE");
    1845             : 
    1846           3 :                 poGDS->m_apoOverviewDatasets.push_back(std::move(poOvrDS));
    1847             :             }
    1848             :         }
    1849             :     }
    1850          23 :     return static_cast<int>(poGDS->m_apoOverviewDatasets.size());
    1851             : }
    1852             : 
    1853             : /************************************************************************/
    1854             : /*                            GetOverview()                             */
    1855             : /************************************************************************/
    1856             : 
    1857           6 : GDALRasterBand *VRTPansharpenedRasterBand::GetOverview(int iOvr)
    1858             : {
    1859           6 :     if (iOvr < 0 || iOvr >= GetOverviewCount())
    1860           2 :         return nullptr;
    1861             : 
    1862           4 :     VRTPansharpenedDataset *poGDS = static_cast<VRTPansharpenedDataset *>(poDS);
    1863             : 
    1864           4 :     return poGDS->m_apoOverviewDatasets[iOvr]->GetRasterBand(nBand);
    1865             : }
    1866             : 
    1867             : /*! @endcond */

Generated by: LCOV version 1.14