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

Generated by: LCOV version 1.14