Coverage Report

Created: 2025-11-16 06:25

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