Coverage Report

Created: 2025-06-22 06:59

/src/gdal/frmts/vrt/vrtpansharpened.cpp
Line
Count
Source (jump to first uncovered line)
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
    double adfPanGT[6] = {0, 0, 0, 0, 0, 0};
422
0
    const bool bPanGeoTransformValid =
423
0
        (poPanDataset->GetGeoTransform(adfPanGT) == 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 =
435
0
        adfPanGT[0] + nPanXSize * adfPanGT[1] + nPanYSize * adfPanGT[2];
436
0
    double dfLRPanY =
437
0
        adfPanGT[3] + nPanXSize * adfPanGT[4] + nPanYSize * adfPanGT[5];
438
0
    bool bFoundRotatingTerms = (adfPanGT[2] != 0.0 || adfPanGT[4] != 0.0);
439
0
    double dfMinX = adfPanGT[0];
440
0
    double dfMaxX = dfLRPanX;
441
0
    double dfMaxY = adfPanGT[3];
442
0
    double dfMinY = dfLRPanY;
443
0
    if (dfMinY > dfMaxY)
444
0
        std::swap(dfMinY, dfMaxY);
445
0
    const double dfPanMinY = dfMinY;
446
0
    const double dfPanMaxY = dfMaxY;
447
448
0
    const auto poPanSRS = poPanDataset->GetSpatialRef();
449
450
    /* -------------------------------------------------------------------- */
451
    /*      First pass on spectral datasets to check their georeferencing.  */
452
    /* -------------------------------------------------------------------- */
453
0
    int iSpectralBand = 0;
454
0
    for (const CPLXMLNode *psIter = psOptions->psChild; psIter;
455
0
         psIter = psIter->psNext)
456
0
    {
457
0
        GDALDataset *poDataset;
458
0
        if (psIter->eType != CXT_Element ||
459
0
            !EQUAL(psIter->pszValue, "SpectralBand"))
460
0
            continue;
461
462
0
        if (nInputSpectralBandsIn && pahInputSpectralBandsIn != nullptr)
463
0
        {
464
0
            if (iSpectralBand == nInputSpectralBandsIn)
465
0
            {
466
0
                CPLError(CE_Failure, CPLE_AppDefined,
467
0
                         "More SpectralBand elements than in source array");
468
0
                goto error;
469
0
            }
470
0
            poDataset = GDALRasterBand::FromHandle(
471
0
                            pahInputSpectralBandsIn[iSpectralBand])
472
0
                            ->GetDataset();
473
0
            if (poDataset == nullptr)
474
0
            {
475
0
                CPLError(CE_Failure, CPLE_AppDefined,
476
0
                         "SpectralBand has no associated dataset");
477
0
                goto error;
478
0
            }
479
0
            osSourceFilename = poDataset->GetDescription();
480
481
0
            oMapNamesToDataset[CPLSPrintf("%p", poDataset)] = poDataset;
482
0
            poDataset->Reference();
483
0
        }
484
0
        else
485
0
        {
486
0
            osSourceFilename = CPLGetXMLValue(psIter, "SourceFilename", "");
487
0
            if (osSourceFilename.empty())
488
0
            {
489
0
                CPLError(CE_Failure, CPLE_AppDefined,
490
0
                         "SpectralBand.SourceFilename missing");
491
0
                goto error;
492
0
            }
493
0
            int bRelativeToVRT = atoi(
494
0
                CPLGetXMLValue(psIter, "SourceFilename.relativetoVRT", "0"));
495
0
            if (bRelativeToVRT)
496
0
            {
497
0
                const std::string osAbs = CPLProjectRelativeFilenameSafe(
498
0
                    pszVRTPathIn, osSourceFilename.c_str());
499
0
                m_oMapToRelativeFilenames[osAbs] = osSourceFilename;
500
0
                osSourceFilename = osAbs;
501
0
            }
502
0
            poDataset = oMapNamesToDataset[osSourceFilename];
503
0
            if (poDataset == nullptr)
504
0
            {
505
0
                const CPLStringList aosOpenOptions(
506
0
                    GDALDeserializeOpenOptionsFromXML(psIter));
507
508
0
                poDataset = GDALDataset::Open(
509
0
                    osSourceFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
510
0
                    nullptr, aosOpenOptions.List());
511
0
                if (poDataset == nullptr)
512
0
                {
513
0
                    goto error;
514
0
                }
515
0
                oMapNamesToDataset[osSourceFilename] = poDataset;
516
0
            }
517
0
            else
518
0
            {
519
0
                poDataset->Reference();
520
0
            }
521
0
        }
522
0
        m_apoDatasetsToReleaseRef.push_back(
523
0
            std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>(
524
0
                poDataset));
525
0
        poDataset = m_apoDatasetsToReleaseRef.back().get();
526
527
        // Check that the spectral band has a georeferencing consistent
528
        // of the pan band. Allow an error of at most the size of one pixel
529
        // of the spectral band.
530
0
        const auto poSpectralSRS = poDataset->GetSpatialRef();
531
0
        if (poPanSRS)
532
0
        {
533
0
            if (poSpectralSRS)
534
0
            {
535
0
                if (!poPanSRS->IsSame(poSpectralSRS))
536
0
                {
537
0
                    CPLError(CE_Warning, CPLE_AppDefined,
538
0
                             "Pan dataset and %s do not seem to "
539
0
                             "have same projection. Results might "
540
0
                             "be incorrect",
541
0
                             osSourceFilename.c_str());
542
0
                }
543
0
            }
544
0
            else
545
0
            {
546
0
                CPLError(CE_Warning, CPLE_AppDefined,
547
0
                         "Pan dataset has a projection, whereas %s "
548
0
                         "not. Results might be incorrect",
549
0
                         osSourceFilename.c_str());
550
0
            }
551
0
        }
552
0
        else if (poSpectralSRS)
553
0
        {
554
0
            CPLError(CE_Warning, CPLE_AppDefined,
555
0
                     "Pan dataset has no projection, whereas %s has "
556
0
                     "one. Results might be incorrect",
557
0
                     osSourceFilename.c_str());
558
0
        }
559
560
0
        double adfSpectralGeoTransform[6];
561
0
        if (poDataset->GetGeoTransform(adfSpectralGeoTransform) != CE_None)
562
0
        {
563
0
            CPLError(CE_Failure, CPLE_AppDefined,
564
0
                     "Spectral band has no associated geotransform");
565
0
            goto error;
566
0
        }
567
0
        if (adfSpectralGeoTransform[3] * adfPanGT[3] < 0)
568
0
        {
569
0
            CPLError(CE_Failure, CPLE_NotSupported,
570
0
                     "Spectral band vertical orientation is "
571
0
                     "different from pan one");
572
0
            goto error;
573
0
        }
574
575
0
        int bIsThisOneNonMatching = FALSE;
576
0
        double dfPixelSize = std::max(adfSpectralGeoTransform[1],
577
0
                                      std::abs(adfSpectralGeoTransform[5]));
578
0
        if (std::abs(adfPanGT[0] - adfSpectralGeoTransform[0]) > dfPixelSize ||
579
0
            std::abs(adfPanGT[3] - adfSpectralGeoTransform[3]) > dfPixelSize)
580
0
        {
581
0
            bIsThisOneNonMatching = TRUE;
582
0
            if (m_eGTAdjustment == GTAdjust_None)
583
0
            {
584
0
                CPLError(CE_Warning, CPLE_AppDefined,
585
0
                         "Georeferencing of top-left corner of pan "
586
0
                         "dataset and %s do not match",
587
0
                         osSourceFilename.c_str());
588
0
            }
589
0
        }
590
0
        bFoundRotatingTerms =
591
0
            bFoundRotatingTerms || (adfSpectralGeoTransform[2] != 0.0 ||
592
0
                                    adfSpectralGeoTransform[4] != 0.0);
593
0
        double dfLRSpectralX =
594
0
            adfSpectralGeoTransform[0] +
595
0
            poDataset->GetRasterXSize() * adfSpectralGeoTransform[1] +
596
0
            poDataset->GetRasterYSize() * adfSpectralGeoTransform[2];
597
0
        double dfLRSpectralY =
598
0
            adfSpectralGeoTransform[3] +
599
0
            poDataset->GetRasterXSize() * adfSpectralGeoTransform[4] +
600
0
            poDataset->GetRasterYSize() * adfSpectralGeoTransform[5];
601
0
        if (std::abs(dfLRPanX - dfLRSpectralX) > dfPixelSize ||
602
0
            std::abs(dfLRPanY - dfLRSpectralY) > dfPixelSize)
603
0
        {
604
0
            bIsThisOneNonMatching = TRUE;
605
0
            if (m_eGTAdjustment == GTAdjust_None)
606
0
            {
607
0
                CPLError(CE_Warning, CPLE_AppDefined,
608
0
                         "Georeferencing of bottom-right corner of "
609
0
                         "pan dataset and %s do not match",
610
0
                         osSourceFilename.c_str());
611
0
            }
612
0
        }
613
614
0
        double dfThisMinY = dfLRSpectralY;
615
0
        double dfThisMaxY = adfSpectralGeoTransform[3];
616
0
        if (dfThisMinY > dfThisMaxY)
617
0
            std::swap(dfThisMinY, dfThisMaxY);
618
0
        if (bIsThisOneNonMatching && m_eGTAdjustment == GTAdjust_Union)
619
0
        {
620
0
            dfMinX = std::min(dfMinX, adfSpectralGeoTransform[0]);
621
0
            dfMinY = std::min(dfMinY, dfThisMinY);
622
0
            dfMaxX = std::max(dfMaxX, dfLRSpectralX);
623
0
            dfMaxY = std::max(dfMaxY, dfThisMaxY);
624
0
        }
625
0
        else if (bIsThisOneNonMatching &&
626
0
                 m_eGTAdjustment == GTAdjust_Intersection)
627
0
        {
628
0
            dfMinX = std::max(dfMinX, adfSpectralGeoTransform[0]);
629
0
            dfMinY = std::max(dfMinY, dfThisMinY);
630
0
            dfMaxX = std::min(dfMaxX, dfLRSpectralX);
631
0
            dfMaxY = std::min(dfMaxY, dfThisMaxY);
632
0
        }
633
634
0
        bFoundNonMatchingGT = bFoundNonMatchingGT || bIsThisOneNonMatching;
635
636
0
        iSpectralBand++;
637
0
    }
638
639
0
    if (nInputSpectralBandsIn && iSpectralBand != nInputSpectralBandsIn)
640
0
    {
641
0
        CPLError(CE_Failure, CPLE_AppDefined,
642
0
                 "Less SpectralBand elements than in source array");
643
0
        goto error;
644
0
    }
645
646
    /* -------------------------------------------------------------------- */
647
    /*      On-the-fly spatial extent adjustment if needed and asked.       */
648
    /* -------------------------------------------------------------------- */
649
650
0
    if (bFoundNonMatchingGT && (m_eGTAdjustment == GTAdjust_Union ||
651
0
                                m_eGTAdjustment == GTAdjust_Intersection))
652
0
    {
653
0
        if (bFoundRotatingTerms)
654
0
        {
655
0
            CPLError(
656
0
                CE_Failure, CPLE_NotSupported,
657
0
                "One of the panchromatic or spectral datasets has rotating "
658
0
                "terms in their geotransform matrix. Adjustment not possible");
659
0
            goto error;
660
0
        }
661
0
        if (m_eGTAdjustment == GTAdjust_Intersection &&
662
0
            (dfMinX >= dfMaxX || dfMinY >= dfMaxY))
663
0
        {
664
0
            CPLError(
665
0
                CE_Failure, CPLE_NotSupported,
666
0
                "One of the panchromatic or spectral datasets has rotating "
667
0
                "terms in their geotransform matrix. Adjustment not possible");
668
0
            goto error;
669
0
        }
670
0
        if (m_eGTAdjustment == GTAdjust_Union)
671
0
            CPLDebug("VRT", "Do union of bounding box of panchromatic and "
672
0
                            "spectral datasets");
673
0
        else
674
0
            CPLDebug("VRT", "Do intersection of bounding box of panchromatic "
675
0
                            "and spectral datasets");
676
677
        // If the pandataset needs adjustments, make sure the coordinates of the
678
        // union/intersection properly align with the grid of the pandataset
679
        // to avoid annoying sub-pixel shifts on the panchro band.
680
0
        double dfPixelSize = std::max(adfPanGT[1], std::abs(adfPanGT[5]));
681
0
        if (std::abs(adfPanGT[0] - dfMinX) > dfPixelSize ||
682
0
            std::abs(dfPanMaxY - dfMaxY) > dfPixelSize ||
683
0
            std::abs(dfLRPanX - dfMaxX) > dfPixelSize ||
684
0
            std::abs(dfPanMinY - dfMinY) > dfPixelSize)
685
0
        {
686
0
            dfMinX = adfPanGT[0] +
687
0
                     std::floor((dfMinX - adfPanGT[0]) / adfPanGT[1] + 0.5) *
688
0
                         adfPanGT[1];
689
0
            dfMaxX =
690
0
                dfLRPanX + std::floor((dfMaxX - dfLRPanX) / adfPanGT[1] + 0.5) *
691
0
                               adfPanGT[1];
692
0
            if (adfPanGT[5] > 0)
693
0
            {
694
0
                dfMinY = dfPanMinY +
695
0
                         std::floor((dfMinY - dfPanMinY) / adfPanGT[5] + 0.5) *
696
0
                             adfPanGT[5];
697
0
                dfMaxY = dfPanMinY +
698
0
                         std::floor((dfMaxY - dfPanMinY) / adfPanGT[5] + 0.5) *
699
0
                             adfPanGT[5];
700
0
            }
701
0
            else
702
0
            {
703
0
                dfMinY = dfPanMaxY +
704
0
                         std::floor((dfMinY - dfPanMaxY) / adfPanGT[5] + 0.5) *
705
0
                             adfPanGT[5];
706
0
                dfMaxY = dfPanMaxY +
707
0
                         std::floor((dfMaxY - dfPanMaxY) / adfPanGT[5] + 0.5) *
708
0
                             adfPanGT[5];
709
0
            }
710
0
        }
711
712
0
        std::map<CPLString, GDALDataset *>::iterator oIter =
713
0
            oMapNamesToDataset.begin();
714
0
        for (; oIter != oMapNamesToDataset.end(); ++oIter)
715
0
        {
716
0
            GDALDataset *poSrcDS = oIter->second;
717
0
            double adfGT[6];
718
0
            if (poSrcDS->GetGeoTransform(adfGT) != CE_None)
719
0
                continue;
720
721
            // Check if this dataset needs adjustments
722
0
            dfPixelSize = std::max(adfGT[1], std::abs(adfGT[5]));
723
0
            dfPixelSize = std::max(adfPanGT[1], dfPixelSize);
724
0
            dfPixelSize = std::max(std::abs(adfPanGT[5]), dfPixelSize);
725
0
            double dfThisMinX = adfGT[0];
726
0
            double dfThisMaxX = adfGT[0] + poSrcDS->GetRasterXSize() * adfGT[1];
727
0
            double dfThisMaxY = adfGT[3];
728
0
            double dfThisMinY = adfGT[3] + poSrcDS->GetRasterYSize() * adfGT[5];
729
0
            if (dfThisMinY > dfThisMaxY)
730
0
                std::swap(dfThisMinY, dfThisMaxY);
731
0
            if (std::abs(dfThisMinX - dfMinX) <= dfPixelSize &&
732
0
                std::abs(dfThisMaxY - dfMaxY) <= dfPixelSize &&
733
0
                std::abs(dfThisMaxX - dfMaxX) <= dfPixelSize &&
734
0
                std::abs(dfThisMinY - dfMinY) <= dfPixelSize)
735
0
            {
736
0
                continue;
737
0
            }
738
739
0
            double adfAdjustedGT[6];
740
0
            adfAdjustedGT[0] = dfMinX;
741
0
            adfAdjustedGT[1] = adfGT[1];
742
0
            adfAdjustedGT[2] = 0;
743
0
            adfAdjustedGT[3] = adfGT[5] > 0 ? dfMinY : dfMaxY;
744
0
            adfAdjustedGT[4] = 0;
745
0
            adfAdjustedGT[5] = adfGT[5];
746
0
            int nAdjustRasterXSize =
747
0
                static_cast<int>(0.5 + (dfMaxX - dfMinX) / adfAdjustedGT[1]);
748
0
            int nAdjustRasterYSize = static_cast<int>(
749
0
                0.5 + (dfMaxY - dfMinY) / std::abs(adfAdjustedGT[5]));
750
751
0
            VRTDataset *poVDS =
752
0
                new VRTDataset(nAdjustRasterXSize, nAdjustRasterYSize);
753
0
            poVDS->SetWritable(FALSE);
754
0
            poVDS->SetDescription(std::string("Shifted ")
755
0
                                      .append(poSrcDS->GetDescription())
756
0
                                      .c_str());
757
0
            poVDS->SetGeoTransform(adfAdjustedGT);
758
0
            poVDS->SetProjection(poPanDataset->GetProjectionRef());
759
760
0
            for (int i = 0; i < poSrcDS->GetRasterCount(); i++)
761
0
            {
762
0
                GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(i + 1);
763
0
                poVDS->AddBand(poSrcBand->GetRasterDataType(), nullptr);
764
0
                VRTSourcedRasterBand *poVRTBand =
765
0
                    static_cast<VRTSourcedRasterBand *>(
766
0
                        poVDS->GetRasterBand(i + 1));
767
768
0
                const char *pszNBITS =
769
0
                    poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
770
0
                if (pszNBITS)
771
0
                    poVRTBand->SetMetadataItem("NBITS", pszNBITS,
772
0
                                               "IMAGE_STRUCTURE");
773
774
0
                VRTSimpleSource *poSimpleSource = new VRTSimpleSource();
775
0
                poVRTBand->ConfigureSource(
776
0
                    poSimpleSource, poSrcBand, FALSE,
777
0
                    static_cast<int>(
778
0
                        std::floor((dfMinX - adfGT[0]) / adfGT[1] + 0.001)),
779
0
                    adfGT[5] < 0
780
0
                        ? static_cast<int>(std::floor(
781
0
                              (dfMaxY - dfThisMaxY) / adfGT[5] + 0.001))
782
0
                        : static_cast<int>(std::floor(
783
0
                              (dfMinY - dfThisMinY) / adfGT[5] + 0.001)),
784
0
                    static_cast<int>(0.5 + (dfMaxX - dfMinX) / adfGT[1]),
785
0
                    static_cast<int>(0.5 +
786
0
                                     (dfMaxY - dfMinY) / std::abs(adfGT[5])),
787
0
                    0, 0, nAdjustRasterXSize, nAdjustRasterYSize);
788
789
0
                poVRTBand->AddSource(poSimpleSource);
790
0
            }
791
792
0
            oIter->second = poVDS;
793
0
            if (poSrcDS == poPanDataset)
794
0
            {
795
0
                memcpy(adfPanGT, adfAdjustedGT, 6 * sizeof(double));
796
0
                poPanDataset = poVDS;
797
0
                poPanBand = poPanDataset->GetRasterBand(nPanBand);
798
0
                nPanXSize = poPanDataset->GetRasterXSize();
799
0
                nPanYSize = poPanDataset->GetRasterYSize();
800
0
            }
801
802
0
            m_apoDatasetsToReleaseRef.push_back(
803
0
                std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>(
804
0
                    poVDS));
805
0
        }
806
0
    }
807
808
0
    if (nRasterXSize == 0 && nRasterYSize == 0)
809
0
    {
810
0
        nRasterXSize = nPanXSize;
811
0
        nRasterYSize = nPanYSize;
812
0
    }
813
0
    else if (nRasterXSize != nPanXSize || nRasterYSize != nPanYSize)
814
0
    {
815
0
        CPLError(CE_Failure, CPLE_AppDefined,
816
0
                 "Inconsistent declared VRT dimensions with panchro dataset");
817
0
        goto error;
818
0
    }
819
820
    /* -------------------------------------------------------------------- */
821
    /*      Initialize all the general VRT stuff.  This will even           */
822
    /*      create the VRTPansharpenedRasterBands and initialize them.      */
823
    /* -------------------------------------------------------------------- */
824
0
    eErr = VRTDataset::XMLInit(psTree, pszVRTPathIn);
825
826
0
    if (eErr != CE_None)
827
0
    {
828
0
        goto error;
829
0
    }
830
831
    /* -------------------------------------------------------------------- */
832
    /*      Inherit georeferencing info from panchro band if not defined    */
833
    /*      in VRT.                                                         */
834
    /* -------------------------------------------------------------------- */
835
836
0
    {
837
0
        double adfOutGT[6];
838
0
        if (GetGeoTransform(adfOutGT) != CE_None && GetGCPCount() == 0 &&
839
0
            GetSpatialRef() == nullptr)
840
0
        {
841
0
            if (bPanGeoTransformValid)
842
0
            {
843
0
                SetGeoTransform(adfPanGT);
844
0
            }
845
0
            if (poPanSRS)
846
0
            {
847
0
                SetSpatialRef(poPanSRS);
848
0
            }
849
0
        }
850
0
    }
851
852
    /* -------------------------------------------------------------------- */
853
    /*      Parse rest of PansharpeningOptions                              */
854
    /* -------------------------------------------------------------------- */
855
0
    iSpectralBand = 0;
856
0
    for (const CPLXMLNode *psIter = psOptions->psChild; psIter;
857
0
         psIter = psIter->psNext)
858
0
    {
859
0
        if (psIter->eType != CXT_Element ||
860
0
            !EQUAL(psIter->pszValue, "SpectralBand"))
861
0
            continue;
862
863
0
        GDALDataset *poDataset;
864
0
        GDALRasterBand *poBand;
865
866
0
        const char *pszDstBand = CPLGetXMLValue(psIter, "dstBand", nullptr);
867
0
        int nDstBand = -1;
868
0
        if (pszDstBand != nullptr)
869
0
        {
870
0
            nDstBand = atoi(pszDstBand);
871
0
            if (nDstBand <= 0)
872
0
            {
873
0
                CPLError(CE_Failure, CPLE_AppDefined,
874
0
                         "SpectralBand.dstBand = '%s' invalid", pszDstBand);
875
0
                goto error;
876
0
            }
877
0
        }
878
879
0
        if (nInputSpectralBandsIn && pahInputSpectralBandsIn != nullptr)
880
0
        {
881
0
            poBand = GDALRasterBand::FromHandle(
882
0
                pahInputSpectralBandsIn[iSpectralBand]);
883
0
            poDataset = poBand->GetDataset();
884
0
            if (poDataset)
885
0
            {
886
0
                poDataset = oMapNamesToDataset[CPLSPrintf("%p", poDataset)];
887
0
                CPLAssert(poDataset);
888
0
                if (poDataset)
889
0
                    poBand = poDataset->GetRasterBand(poBand->GetBand());
890
0
            }
891
0
        }
892
0
        else
893
0
        {
894
0
            osSourceFilename = CPLGetXMLValue(psIter, "SourceFilename", "");
895
0
            const bool bRelativeToVRT = CPL_TO_BOOL(atoi(
896
0
                CPLGetXMLValue(psIter, "SourceFilename.relativetoVRT", "0")));
897
0
            if (bRelativeToVRT)
898
0
                osSourceFilename = CPLProjectRelativeFilenameSafe(
899
0
                    pszVRTPathIn, osSourceFilename.c_str());
900
0
            poDataset = oMapNamesToDataset[osSourceFilename];
901
0
            CPLAssert(poDataset);
902
0
            const char *pszSourceBand =
903
0
                CPLGetXMLValue(psIter, "SourceBand", "1");
904
0
            const int nBand = atoi(pszSourceBand);
905
0
            poBand = poDataset->GetRasterBand(nBand);
906
0
            if (poBand == nullptr)
907
0
            {
908
0
                CPLError(CE_Failure, CPLE_AppDefined, "%s invalid band of %s",
909
0
                         pszSourceBand, osSourceFilename.c_str());
910
0
                goto error;
911
0
            }
912
0
        }
913
914
0
        if (bHasNoData)
915
0
        {
916
0
            double dfSpectralNoData = poPanBand->GetNoDataValue(&bHasNoData);
917
0
            if (bHasNoData && dfSpectralNoData != dfNoData)
918
0
                bHasNoData = FALSE;
919
0
        }
920
921
0
        ahSpectralBands.push_back(poBand);
922
0
        if (nDstBand >= 1)
923
0
        {
924
0
            if (aMapDstBandToSpectralBand.find(nDstBand - 1) !=
925
0
                aMapDstBandToSpectralBand.end())
926
0
            {
927
0
                CPLError(
928
0
                    CE_Failure, CPLE_AppDefined,
929
0
                    "Another spectral band is already mapped to output band %d",
930
0
                    nDstBand);
931
0
                goto error;
932
0
            }
933
0
            aMapDstBandToSpectralBand[nDstBand - 1] =
934
0
                static_cast<int>(ahSpectralBands.size() - 1);
935
0
        }
936
937
0
        iSpectralBand++;
938
0
    }
939
940
0
    if (ahSpectralBands.empty())
941
0
    {
942
0
        CPLError(CE_Failure, CPLE_AppDefined, "No spectral band defined");
943
0
        goto error;
944
0
    }
945
946
0
    {
947
0
        const char *pszNoData = CPLGetXMLValue(psOptions, "NoData", nullptr);
948
0
        if (pszNoData)
949
0
        {
950
0
            if (EQUAL(pszNoData, "NONE"))
951
0
            {
952
0
                m_bNoDataDisabled = TRUE;
953
0
                bHasNoData = FALSE;
954
0
            }
955
0
            else
956
0
            {
957
0
                bHasNoData = TRUE;
958
0
                dfNoData = CPLAtof(pszNoData);
959
0
            }
960
0
        }
961
0
    }
962
963
0
    if (GetRasterCount() == 0)
964
0
    {
965
0
        for (int i = 0; i < static_cast<int>(aMapDstBandToSpectralBand.size());
966
0
             i++)
967
0
        {
968
0
            if (aMapDstBandToSpectralBand.find(i) ==
969
0
                aMapDstBandToSpectralBand.end())
970
0
            {
971
0
                CPLError(CE_Failure, CPLE_AppDefined,
972
0
                         "Hole in SpectralBand.dstBand numbering");
973
0
                goto error;
974
0
            }
975
0
            GDALRasterBand *poInputBand = GDALRasterBand::FromHandle(
976
0
                ahSpectralBands[aMapDstBandToSpectralBand[i]]);
977
0
            GDALRasterBand *poBand = new VRTPansharpenedRasterBand(
978
0
                this, i + 1, poInputBand->GetRasterDataType());
979
0
            poBand->SetColorInterpretation(
980
0
                poInputBand->GetColorInterpretation());
981
0
            if (bHasNoData)
982
0
                poBand->SetNoDataValue(dfNoData);
983
0
            SetBand(i + 1, poBand);
984
0
        }
985
0
    }
986
0
    else
987
0
    {
988
0
        int nIdxAsPansharpenedBand = 0;
989
0
        for (int i = 0; i < nBands; i++)
990
0
        {
991
0
            if (static_cast<VRTRasterBand *>(GetRasterBand(i + 1))
992
0
                    ->IsPansharpenRasterBand())
993
0
            {
994
0
                if (aMapDstBandToSpectralBand.find(i) ==
995
0
                    aMapDstBandToSpectralBand.end())
996
0
                {
997
0
                    CPLError(CE_Failure, CPLE_AppDefined,
998
0
                             "Band %d of type VRTPansharpenedRasterBand, but "
999
0
                             "no corresponding SpectralBand",
1000
0
                             i + 1);
1001
0
                    goto error;
1002
0
                }
1003
0
                else
1004
0
                {
1005
0
                    static_cast<VRTPansharpenedRasterBand *>(
1006
0
                        GetRasterBand(i + 1))
1007
0
                        ->SetIndexAsPansharpenedBand(nIdxAsPansharpenedBand);
1008
0
                    nIdxAsPansharpenedBand++;
1009
0
                }
1010
0
            }
1011
0
        }
1012
0
    }
1013
1014
    // Figure out bit depth
1015
0
    {
1016
0
        const char *pszBitDepth =
1017
0
            CPLGetXMLValue(psOptions, "BitDepth", nullptr);
1018
0
        if (pszBitDepth == nullptr)
1019
0
            pszBitDepth = GDALRasterBand::FromHandle(ahSpectralBands[0])
1020
0
                              ->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
1021
0
        if (pszBitDepth)
1022
0
            nBitDepth = atoi(pszBitDepth);
1023
0
        if (nBitDepth)
1024
0
        {
1025
0
            for (int i = 0; i < nBands; i++)
1026
0
            {
1027
0
                if (!static_cast<VRTRasterBand *>(GetRasterBand(i + 1))
1028
0
                         ->IsPansharpenRasterBand())
1029
0
                    continue;
1030
0
                if (GetRasterBand(i + 1)->GetMetadataItem(
1031
0
                        "NBITS", "IMAGE_STRUCTURE") == nullptr)
1032
0
                {
1033
0
                    if (nBitDepth != 8 && nBitDepth != 16 && nBitDepth != 32)
1034
0
                    {
1035
0
                        GetRasterBand(i + 1)->SetMetadataItem(
1036
0
                            "NBITS", CPLSPrintf("%d", nBitDepth),
1037
0
                            "IMAGE_STRUCTURE");
1038
0
                    }
1039
0
                }
1040
0
                else if (nBitDepth == 8 || nBitDepth == 16 || nBitDepth == 32)
1041
0
                {
1042
0
                    GetRasterBand(i + 1)->SetMetadataItem("NBITS", nullptr,
1043
0
                                                          "IMAGE_STRUCTURE");
1044
0
                }
1045
0
            }
1046
0
        }
1047
0
    }
1048
1049
0
    if (GDALGetRasterBandXSize(ahSpectralBands[0]) >
1050
0
            GDALGetRasterBandXSize(poPanBand) ||
1051
0
        GDALGetRasterBandYSize(ahSpectralBands[0]) >
1052
0
            GDALGetRasterBandYSize(poPanBand))
1053
0
    {
1054
0
        CPLError(CE_Warning, CPLE_AppDefined,
1055
0
                 "Dimensions of spectral band larger than panchro band");
1056
0
    }
1057
1058
0
    aMapDstBandToSpectralBandIter = aMapDstBandToSpectralBand.begin();
1059
0
    for (; aMapDstBandToSpectralBandIter != aMapDstBandToSpectralBand.end();
1060
0
         ++aMapDstBandToSpectralBandIter)
1061
0
    {
1062
0
        const int nDstBand = 1 + aMapDstBandToSpectralBandIter->first;
1063
0
        if (nDstBand > nBands ||
1064
0
            !static_cast<VRTRasterBand *>(GetRasterBand(nDstBand))
1065
0
                 ->IsPansharpenRasterBand())
1066
0
        {
1067
0
            CPLError(CE_Failure, CPLE_AppDefined,
1068
0
                     "SpectralBand.dstBand = '%d' invalid", nDstBand);
1069
0
            goto error;
1070
0
        }
1071
0
    }
1072
1073
0
    if (adfWeights.empty())
1074
0
    {
1075
0
        for (int i = 0; i < static_cast<int>(ahSpectralBands.size()); i++)
1076
0
        {
1077
0
            adfWeights.push_back(1.0 / ahSpectralBands.size());
1078
0
        }
1079
0
    }
1080
0
    else if (adfWeights.size() != ahSpectralBands.size())
1081
0
    {
1082
0
        CPLError(CE_Failure, CPLE_AppDefined,
1083
0
                 "%d weights defined, but %d input spectral bands",
1084
0
                 static_cast<int>(adfWeights.size()),
1085
0
                 static_cast<int>(ahSpectralBands.size()));
1086
0
        goto error;
1087
0
    }
1088
1089
0
    if (aMapDstBandToSpectralBand.empty())
1090
0
    {
1091
0
        CPLError(CE_Warning, CPLE_AppDefined,
1092
0
                 "No spectral band is mapped to an output band");
1093
0
    }
1094
1095
    /* -------------------------------------------------------------------- */
1096
    /*      Instantiate poPansharpener                                      */
1097
    /* -------------------------------------------------------------------- */
1098
0
    psPanOptions.reset(GDALCreatePansharpenOptions());
1099
0
    psPanOptions->ePansharpenAlg = GDAL_PSH_WEIGHTED_BROVEY;
1100
0
    psPanOptions->eResampleAlg = eResampleAlg;
1101
0
    psPanOptions->nBitDepth = nBitDepth;
1102
0
    psPanOptions->nWeightCount = static_cast<int>(adfWeights.size());
1103
0
    psPanOptions->padfWeights =
1104
0
        static_cast<double *>(CPLMalloc(sizeof(double) * adfWeights.size()));
1105
0
    memcpy(psPanOptions->padfWeights, &adfWeights[0],
1106
0
           sizeof(double) * adfWeights.size());
1107
0
    psPanOptions->hPanchroBand = poPanBand;
1108
0
    psPanOptions->nInputSpectralBands =
1109
0
        static_cast<int>(ahSpectralBands.size());
1110
0
    psPanOptions->pahInputSpectralBands = static_cast<GDALRasterBandH *>(
1111
0
        CPLMalloc(sizeof(GDALRasterBandH) * ahSpectralBands.size()));
1112
0
    memcpy(psPanOptions->pahInputSpectralBands, &ahSpectralBands[0],
1113
0
           sizeof(GDALRasterBandH) * ahSpectralBands.size());
1114
0
    psPanOptions->nOutPansharpenedBands =
1115
0
        static_cast<int>(aMapDstBandToSpectralBand.size());
1116
0
    psPanOptions->panOutPansharpenedBands = static_cast<int *>(
1117
0
        CPLMalloc(sizeof(int) * aMapDstBandToSpectralBand.size()));
1118
0
    aMapDstBandToSpectralBandIter = aMapDstBandToSpectralBand.begin();
1119
0
    for (int i = 0;
1120
0
         aMapDstBandToSpectralBandIter != aMapDstBandToSpectralBand.end();
1121
0
         ++aMapDstBandToSpectralBandIter, ++i)
1122
0
    {
1123
0
        psPanOptions->panOutPansharpenedBands[i] =
1124
0
            aMapDstBandToSpectralBandIter->second;
1125
0
    }
1126
0
    psPanOptions->bHasNoData = bHasNoData;
1127
0
    psPanOptions->dfNoData = dfNoData;
1128
0
    psPanOptions->nThreads = nThreads;
1129
1130
0
    if (nBands == psPanOptions->nOutPansharpenedBands)
1131
0
        SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
1132
1133
0
    m_poPansharpener = std::make_unique<GDALPansharpenOperation>();
1134
0
    eErr = m_poPansharpener->Initialize(psPanOptions.get());
1135
0
    if (eErr != CE_None)
1136
0
    {
1137
        // Delete the pansharper object before closing the dataset
1138
        // because it may have warped the bands into an intermediate VRT
1139
0
        m_poPansharpener.reset();
1140
1141
        // Close in reverse order (VRT firsts and real datasets after)
1142
0
        for (int i = static_cast<int>(m_apoDatasetsToReleaseRef.size()) - 1;
1143
0
             i >= 0; i--)
1144
0
        {
1145
0
            m_apoDatasetsToReleaseRef[i].reset();
1146
0
        }
1147
0
        m_apoDatasetsToReleaseRef.clear();
1148
0
    }
1149
1150
0
    return eErr;
1151
1152
0
error:
1153
    // Close in reverse order (VRT firsts and real datasets after)
1154
0
    for (int i = static_cast<int>(m_apoDatasetsToReleaseRef.size()) - 1; i >= 0;
1155
0
         i--)
1156
0
    {
1157
0
        m_apoDatasetsToReleaseRef[i].reset();
1158
0
    }
1159
0
    m_apoDatasetsToReleaseRef.clear();
1160
0
    return CE_Failure;
1161
0
}
1162
1163
/************************************************************************/
1164
/*                           SerializeToXML()                           */
1165
/************************************************************************/
1166
1167
CPLXMLNode *VRTPansharpenedDataset::SerializeToXML(const char *pszVRTPathIn)
1168
1169
0
{
1170
0
    CPLXMLNode *psTree = VRTDataset::SerializeToXML(pszVRTPathIn);
1171
1172
0
    if (psTree == nullptr)
1173
0
        return psTree;
1174
1175
    /* -------------------------------------------------------------------- */
1176
    /*      Set subclass.                                                   */
1177
    /* -------------------------------------------------------------------- */
1178
0
    CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
1179
0
                     CXT_Text, "VRTPansharpenedDataset");
1180
1181
    /* -------------------------------------------------------------------- */
1182
    /*      Serialize the block size.                                       */
1183
    /* -------------------------------------------------------------------- */
1184
0
    CPLCreateXMLElementAndValue(psTree, "BlockXSize",
1185
0
                                CPLSPrintf("%d", m_nBlockXSize));
1186
0
    CPLCreateXMLElementAndValue(psTree, "BlockYSize",
1187
0
                                CPLSPrintf("%d", m_nBlockYSize));
1188
1189
    /* -------------------------------------------------------------------- */
1190
    /*      Serialize the options.                                          */
1191
    /* -------------------------------------------------------------------- */
1192
0
    if (m_poPansharpener == nullptr)
1193
0
        return psTree;
1194
0
    const GDALPansharpenOptions *psOptions = m_poPansharpener->GetOptions();
1195
0
    if (psOptions == nullptr)
1196
0
        return psTree;
1197
1198
0
    CPLXMLNode *psOptionsNode =
1199
0
        CPLCreateXMLNode(psTree, CXT_Element, "PansharpeningOptions");
1200
1201
0
    if (psOptions->ePansharpenAlg == GDAL_PSH_WEIGHTED_BROVEY)
1202
0
    {
1203
0
        CPLCreateXMLElementAndValue(psOptionsNode, "Algorithm",
1204
0
                                    "WeightedBrovey");
1205
0
    }
1206
0
    else
1207
0
    {
1208
0
        CPLAssert(false);
1209
0
    }
1210
0
    if (psOptions->nWeightCount)
1211
0
    {
1212
0
        CPLString osWeights;
1213
0
        for (int i = 0; i < psOptions->nWeightCount; i++)
1214
0
        {
1215
0
            if (i)
1216
0
                osWeights += ",";
1217
0
            osWeights += CPLSPrintf("%.16g", psOptions->padfWeights[i]);
1218
0
        }
1219
0
        CPLCreateXMLElementAndValue(
1220
0
            CPLCreateXMLNode(psOptionsNode, CXT_Element, "AlgorithmOptions"),
1221
0
            "Weights", osWeights.c_str());
1222
0
    }
1223
0
    CPLCreateXMLElementAndValue(
1224
0
        psOptionsNode, "Resampling",
1225
0
        GDALRasterIOGetResampleAlg(psOptions->eResampleAlg));
1226
1227
0
    if (psOptions->nThreads == -1)
1228
0
    {
1229
0
        CPLCreateXMLElementAndValue(psOptionsNode, "NumThreads", "ALL_CPUS");
1230
0
    }
1231
0
    else if (psOptions->nThreads > 1)
1232
0
    {
1233
0
        CPLCreateXMLElementAndValue(psOptionsNode, "NumThreads",
1234
0
                                    CPLSPrintf("%d", psOptions->nThreads));
1235
0
    }
1236
1237
0
    if (psOptions->nBitDepth)
1238
0
        CPLCreateXMLElementAndValue(psOptionsNode, "BitDepth",
1239
0
                                    CPLSPrintf("%d", psOptions->nBitDepth));
1240
1241
0
    const char *pszAdjust = nullptr;
1242
0
    switch (m_eGTAdjustment)
1243
0
    {
1244
0
        case GTAdjust_Union:
1245
0
            pszAdjust = "Union";
1246
0
            break;
1247
0
        case GTAdjust_Intersection:
1248
0
            pszAdjust = "Intersection";
1249
0
            break;
1250
0
        case GTAdjust_None:
1251
0
            pszAdjust = "None";
1252
0
            break;
1253
0
        case GTAdjust_NoneWithoutWarning:
1254
0
            pszAdjust = "NoneWithoutWarning";
1255
0
            break;
1256
0
        default:
1257
0
            break;
1258
0
    }
1259
1260
0
    if (psOptions->bHasNoData)
1261
0
    {
1262
0
        CPLCreateXMLElementAndValue(psOptionsNode, "NoData",
1263
0
                                    CPLSPrintf("%.16g", psOptions->dfNoData));
1264
0
    }
1265
0
    else if (m_bNoDataDisabled)
1266
0
    {
1267
0
        CPLCreateXMLElementAndValue(psOptionsNode, "NoData", "None");
1268
0
    }
1269
1270
0
    if (pszAdjust)
1271
0
        CPLCreateXMLElementAndValue(psOptionsNode, "SpatialExtentAdjustment",
1272
0
                                    pszAdjust);
1273
1274
0
    if (psOptions->hPanchroBand)
1275
0
    {
1276
0
        CPLXMLNode *psBand =
1277
0
            CPLCreateXMLNode(psOptionsNode, CXT_Element, "PanchroBand");
1278
0
        GDALRasterBand *poBand =
1279
0
            GDALRasterBand::FromHandle(psOptions->hPanchroBand);
1280
0
        if (poBand->GetDataset())
1281
0
        {
1282
0
            auto poPanchoDS = poBand->GetDataset();
1283
0
            std::map<CPLString, CPLString>::iterator oIter =
1284
0
                m_oMapToRelativeFilenames.find(poPanchoDS->GetDescription());
1285
0
            if (oIter == m_oMapToRelativeFilenames.end())
1286
0
            {
1287
0
                CPLCreateXMLElementAndValue(psBand, "SourceFilename",
1288
0
                                            poPanchoDS->GetDescription());
1289
0
            }
1290
0
            else
1291
0
            {
1292
0
                CPLXMLNode *psSourceFilename = CPLCreateXMLElementAndValue(
1293
0
                    psBand, "SourceFilename", oIter->second);
1294
0
                CPLCreateXMLNode(CPLCreateXMLNode(psSourceFilename,
1295
0
                                                  CXT_Attribute,
1296
0
                                                  "relativeToVRT"),
1297
0
                                 CXT_Text, "1");
1298
0
            }
1299
1300
0
            GDALSerializeOpenOptionsToXML(psBand, poPanchoDS->GetOpenOptions());
1301
1302
0
            CPLCreateXMLElementAndValue(psBand, "SourceBand",
1303
0
                                        CPLSPrintf("%d", poBand->GetBand()));
1304
0
        }
1305
0
    }
1306
0
    for (int i = 0; i < psOptions->nInputSpectralBands; i++)
1307
0
    {
1308
0
        CPLXMLNode *psBand =
1309
0
            CPLCreateXMLNode(psOptionsNode, CXT_Element, "SpectralBand");
1310
1311
0
        for (int j = 0; j < psOptions->nOutPansharpenedBands; j++)
1312
0
        {
1313
0
            if (psOptions->panOutPansharpenedBands[j] == i)
1314
0
            {
1315
0
                for (int k = 0; k < nBands; k++)
1316
0
                {
1317
0
                    if (static_cast<VRTRasterBand *>(GetRasterBand(k + 1))
1318
0
                            ->IsPansharpenRasterBand())
1319
0
                    {
1320
0
                        if (static_cast<VRTPansharpenedRasterBand *>(
1321
0
                                GetRasterBand(k + 1))
1322
0
                                ->GetIndexAsPansharpenedBand() == j)
1323
0
                        {
1324
0
                            CPLCreateXMLNode(CPLCreateXMLNode(psBand,
1325
0
                                                              CXT_Attribute,
1326
0
                                                              "dstBand"),
1327
0
                                             CXT_Text, CPLSPrintf("%d", k + 1));
1328
0
                            break;
1329
0
                        }
1330
0
                    }
1331
0
                }
1332
0
                break;
1333
0
            }
1334
0
        }
1335
1336
0
        GDALRasterBand *poBand =
1337
0
            GDALRasterBand::FromHandle(psOptions->pahInputSpectralBands[i]);
1338
0
        if (poBand->GetDataset())
1339
0
        {
1340
0
            auto poSpectralDS = poBand->GetDataset();
1341
0
            std::map<CPLString, CPLString>::iterator oIter =
1342
0
                m_oMapToRelativeFilenames.find(poSpectralDS->GetDescription());
1343
0
            if (oIter == m_oMapToRelativeFilenames.end())
1344
0
            {
1345
0
                CPLCreateXMLElementAndValue(psBand, "SourceFilename",
1346
0
                                            poSpectralDS->GetDescription());
1347
0
            }
1348
0
            else
1349
0
            {
1350
0
                CPLXMLNode *psSourceFilename = CPLCreateXMLElementAndValue(
1351
0
                    psBand, "SourceFilename", oIter->second);
1352
0
                CPLCreateXMLNode(CPLCreateXMLNode(psSourceFilename,
1353
0
                                                  CXT_Attribute,
1354
0
                                                  "relativeToVRT"),
1355
0
                                 CXT_Text, "1");
1356
0
            }
1357
1358
0
            GDALSerializeOpenOptionsToXML(psBand,
1359
0
                                          poSpectralDS->GetOpenOptions());
1360
1361
0
            CPLCreateXMLElementAndValue(psBand, "SourceBand",
1362
0
                                        CPLSPrintf("%d", poBand->GetBand()));
1363
0
        }
1364
0
    }
1365
1366
0
    return psTree;
1367
0
}
1368
1369
/************************************************************************/
1370
/*                            GetBlockSize()                            */
1371
/************************************************************************/
1372
1373
void VRTPansharpenedDataset::GetBlockSize(int *pnBlockXSize,
1374
                                          int *pnBlockYSize) const
1375
1376
0
{
1377
0
    assert(nullptr != pnBlockXSize);
1378
0
    assert(nullptr != pnBlockYSize);
1379
1380
0
    *pnBlockXSize = m_nBlockXSize;
1381
0
    *pnBlockYSize = m_nBlockYSize;
1382
0
}
1383
1384
/************************************************************************/
1385
/*                              AddBand()                               */
1386
/************************************************************************/
1387
1388
CPLErr VRTPansharpenedDataset::AddBand(CPL_UNUSED GDALDataType eType,
1389
                                       CPL_UNUSED char **papszOptions)
1390
1391
0
{
1392
0
    CPLError(CE_Failure, CPLE_NotSupported, "AddBand() not supported");
1393
1394
0
    return CE_Failure;
1395
0
}
1396
1397
/************************************************************************/
1398
/*                              IRasterIO()                             */
1399
/************************************************************************/
1400
1401
CPLErr VRTPansharpenedDataset::IRasterIO(
1402
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
1403
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
1404
    int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
1405
    GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
1406
0
{
1407
0
    if (eRWFlag == GF_Write)
1408
0
        return CE_Failure;
1409
1410
    /* Try to pass the request to the most appropriate overview dataset */
1411
0
    if (nBufXSize < nXSize && nBufYSize < nYSize)
1412
0
    {
1413
0
        int bTried;
1414
0
        CPLErr eErr = TryOverviewRasterIO(
1415
0
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
1416
0
            eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
1417
0
            nBandSpace, psExtraArg, &bTried);
1418
0
        if (bTried)
1419
0
            return eErr;
1420
0
    }
1421
1422
0
    const int nDataTypeSize = GDALGetDataTypeSizeBytes(eBufType);
1423
0
    if (nXSize == nBufXSize && nYSize == nBufYSize &&
1424
0
        nDataTypeSize == nPixelSpace && nLineSpace == nPixelSpace * nBufXSize &&
1425
0
        nBandSpace == nLineSpace * nBufYSize && nBandCount == nBands)
1426
0
    {
1427
0
        for (int i = 0; i < nBands; i++)
1428
0
        {
1429
0
            if (panBandMap[i] != i + 1 ||
1430
0
                !static_cast<VRTRasterBand *>(GetRasterBand(i + 1))
1431
0
                     ->IsPansharpenRasterBand())
1432
0
            {
1433
0
                goto default_path;
1434
0
            }
1435
0
        }
1436
1437
        //{static int bDone = 0; if (!bDone) printf("(2)\n"); bDone = 1; }
1438
0
        return m_poPansharpener->ProcessRegion(nXOff, nYOff, nXSize, nYSize,
1439
0
                                               pData, eBufType);
1440
0
    }
1441
1442
0
default_path:
1443
    //{static int bDone = 0; if (!bDone) printf("(3)\n"); bDone = 1; }
1444
0
    return VRTDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
1445
0
                                 nBufXSize, nBufYSize, eBufType, nBandCount,
1446
0
                                 panBandMap, nPixelSpace, nLineSpace,
1447
0
                                 nBandSpace, psExtraArg);
1448
0
}
1449
1450
/************************************************************************/
1451
/* ==================================================================== */
1452
/*                        VRTPansharpenedRasterBand                     */
1453
/* ==================================================================== */
1454
/************************************************************************/
1455
1456
/************************************************************************/
1457
/*                        VRTPansharpenedRasterBand()                   */
1458
/************************************************************************/
1459
1460
VRTPansharpenedRasterBand::VRTPansharpenedRasterBand(GDALDataset *poDSIn,
1461
                                                     int nBandIn,
1462
                                                     GDALDataType eDataTypeIn)
1463
0
    : m_nIndexAsPansharpenedBand(nBandIn - 1)
1464
0
{
1465
0
    Initialize(poDSIn->GetRasterXSize(), poDSIn->GetRasterYSize());
1466
1467
0
    poDS = poDSIn;
1468
0
    nBand = nBandIn;
1469
0
    eAccess = GA_Update;
1470
0
    eDataType = eDataTypeIn;
1471
1472
0
    static_cast<VRTPansharpenedDataset *>(poDS)->GetBlockSize(&nBlockXSize,
1473
0
                                                              &nBlockYSize);
1474
0
}
1475
1476
/************************************************************************/
1477
/*                        ~VRTPansharpenedRasterBand()                  */
1478
/************************************************************************/
1479
1480
VRTPansharpenedRasterBand::~VRTPansharpenedRasterBand()
1481
1482
0
{
1483
0
    FlushCache(true);
1484
0
}
1485
1486
/************************************************************************/
1487
/*                             IReadBlock()                             */
1488
/************************************************************************/
1489
1490
CPLErr VRTPansharpenedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
1491
                                             void *pImage)
1492
1493
0
{
1494
0
    const int nReqXOff = nBlockXOff * nBlockXSize;
1495
0
    const int nReqYOff = nBlockYOff * nBlockYSize;
1496
0
    int nReqXSize = nBlockXSize;
1497
0
    int nReqYSize = nBlockYSize;
1498
0
    if (nReqXOff + nReqXSize > nRasterXSize)
1499
0
        nReqXSize = nRasterXSize - nReqXOff;
1500
0
    if (nReqYOff + nReqYSize > nRasterYSize)
1501
0
        nReqYSize = nRasterYSize - nReqYOff;
1502
1503
    //{static int bDone = 0; if (!bDone) printf("(4)\n"); bDone = 1; }
1504
0
    const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
1505
0
    GDALRasterIOExtraArg sExtraArg;
1506
0
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1507
0
    if (IRasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize, pImage,
1508
0
                  nReqXSize, nReqYSize, eDataType, nDataTypeSize,
1509
0
                  static_cast<GSpacing>(nDataTypeSize) * nReqXSize,
1510
0
                  &sExtraArg) != CE_None)
1511
0
    {
1512
0
        return CE_Failure;
1513
0
    }
1514
1515
0
    if (nReqXSize < nBlockXSize)
1516
0
    {
1517
0
        for (int j = nReqYSize - 1; j >= 0; j--)
1518
0
        {
1519
0
            memmove(static_cast<GByte *>(pImage) +
1520
0
                        static_cast<size_t>(j) * nDataTypeSize * nBlockXSize,
1521
0
                    static_cast<GByte *>(pImage) +
1522
0
                        static_cast<size_t>(j) * nDataTypeSize * nReqXSize,
1523
0
                    static_cast<size_t>(nReqXSize) * nDataTypeSize);
1524
0
            memset(static_cast<GByte *>(pImage) +
1525
0
                       (static_cast<size_t>(j) * nBlockXSize + nReqXSize) *
1526
0
                           nDataTypeSize,
1527
0
                   0,
1528
0
                   static_cast<size_t>(nBlockXSize - nReqXSize) *
1529
0
                       nDataTypeSize);
1530
0
        }
1531
0
    }
1532
0
    if (nReqYSize < nBlockYSize)
1533
0
    {
1534
0
        memset(static_cast<GByte *>(pImage) +
1535
0
                   static_cast<size_t>(nReqYSize) * nBlockXSize * nDataTypeSize,
1536
0
               0,
1537
0
               static_cast<size_t>(nBlockYSize - nReqYSize) * nBlockXSize *
1538
0
                   nDataTypeSize);
1539
0
    }
1540
1541
    // Cache other bands
1542
0
    CPLErr eErr = CE_None;
1543
0
    VRTPansharpenedDataset *poGDS = static_cast<VRTPansharpenedDataset *>(poDS);
1544
0
    if (poGDS->nBands != 1 && !poGDS->m_bLoadingOtherBands)
1545
0
    {
1546
0
        poGDS->m_bLoadingOtherBands = TRUE;
1547
1548
0
        for (int iOtherBand = 1; iOtherBand <= poGDS->nBands; iOtherBand++)
1549
0
        {
1550
0
            if (iOtherBand == nBand)
1551
0
                continue;
1552
1553
0
            GDALRasterBlock *poBlock =
1554
0
                poGDS->GetRasterBand(iOtherBand)
1555
0
                    ->GetLockedBlockRef(nBlockXOff, nBlockYOff);
1556
0
            if (poBlock == nullptr)
1557
0
            {
1558
0
                eErr = CE_Failure;
1559
0
                break;
1560
0
            }
1561
0
            poBlock->DropLock();
1562
0
        }
1563
1564
0
        poGDS->m_bLoadingOtherBands = FALSE;
1565
0
    }
1566
1567
0
    return eErr;
1568
0
}
1569
1570
/************************************************************************/
1571
/*                              IRasterIO()                             */
1572
/************************************************************************/
1573
1574
CPLErr VRTPansharpenedRasterBand::IRasterIO(
1575
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
1576
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
1577
    GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
1578
0
{
1579
0
    if (eRWFlag == GF_Write)
1580
0
        return CE_Failure;
1581
1582
0
    VRTPansharpenedDataset *poGDS = static_cast<VRTPansharpenedDataset *>(poDS);
1583
1584
    /* Try to pass the request to the most appropriate overview dataset */
1585
0
    if (nBufXSize < nXSize && nBufYSize < nYSize)
1586
0
    {
1587
0
        int bTried;
1588
0
        CPLErr eErr = TryOverviewRasterIO(
1589
0
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
1590
0
            eBufType, nPixelSpace, nLineSpace, psExtraArg, &bTried);
1591
0
        if (bTried)
1592
0
            return eErr;
1593
0
    }
1594
1595
0
    const int nDataTypeSize = GDALGetDataTypeSizeBytes(eBufType);
1596
0
    if (nDataTypeSize > 0 && nXSize == nBufXSize && nYSize == nBufYSize &&
1597
0
        nDataTypeSize == nPixelSpace && nLineSpace == nPixelSpace * nBufXSize)
1598
0
    {
1599
0
        const GDALPansharpenOptions *psOptions =
1600
0
            poGDS->m_poPansharpener->GetOptions();
1601
1602
        // Have we already done this request for another band ?
1603
        // If so use the cached result
1604
0
        const size_t nBufferSizePerBand =
1605
0
            static_cast<size_t>(nXSize) * nYSize * nDataTypeSize;
1606
0
        if (nXOff == poGDS->m_nLastBandRasterIOXOff &&
1607
0
            nYOff >= poGDS->m_nLastBandRasterIOYOff &&
1608
0
            nXSize == poGDS->m_nLastBandRasterIOXSize &&
1609
0
            nYOff + nYSize <= poGDS->m_nLastBandRasterIOYOff +
1610
0
                                  poGDS->m_nLastBandRasterIOYSize &&
1611
0
            eBufType == poGDS->m_eLastBandRasterIODataType)
1612
0
        {
1613
            //{static int bDone = 0; if (!bDone) printf("(6)\n"); bDone = 1; }
1614
0
            if (poGDS->m_pabyLastBufferBandRasterIO == nullptr)
1615
0
                return CE_Failure;
1616
0
            const size_t nBufferSizePerBandCached =
1617
0
                static_cast<size_t>(nXSize) * poGDS->m_nLastBandRasterIOYSize *
1618
0
                nDataTypeSize;
1619
0
            memcpy(pData,
1620
0
                   poGDS->m_pabyLastBufferBandRasterIO +
1621
0
                       nBufferSizePerBandCached * m_nIndexAsPansharpenedBand +
1622
0
                       static_cast<size_t>(nYOff -
1623
0
                                           poGDS->m_nLastBandRasterIOYOff) *
1624
0
                           nXSize * nDataTypeSize,
1625
0
                   nBufferSizePerBand);
1626
0
            return CE_None;
1627
0
        }
1628
1629
0
        int nYSizeToCache = nYSize;
1630
0
        if (nYSize == 1 && nXSize == nRasterXSize)
1631
0
        {
1632
            //{static int bDone = 0; if (!bDone) printf("(7)\n"); bDone = 1; }
1633
            // For efficiency, try to cache at leak 256 K
1634
0
            nYSizeToCache = (256 * 1024) / nXSize / nDataTypeSize;
1635
0
            if (nYSizeToCache == 0)
1636
0
                nYSizeToCache = 1;
1637
0
            else if (nYOff + nYSizeToCache > nRasterYSize)
1638
0
                nYSizeToCache = nRasterYSize - nYOff;
1639
0
        }
1640
0
        const GUIntBig nBufferSize = static_cast<GUIntBig>(nXSize) *
1641
0
                                     nYSizeToCache * nDataTypeSize *
1642
0
                                     psOptions->nOutPansharpenedBands;
1643
        // Check the we don't overflow (for 32 bit platforms)
1644
0
        if (nBufferSize > std::numeric_limits<size_t>::max() / 2)
1645
0
        {
1646
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
1647
0
                     "Out of memory error while allocating working buffers");
1648
0
            return CE_Failure;
1649
0
        }
1650
0
        GByte *pabyTemp = static_cast<GByte *>(
1651
0
            VSI_REALLOC_VERBOSE(poGDS->m_pabyLastBufferBandRasterIO,
1652
0
                                static_cast<size_t>(nBufferSize)));
1653
0
        if (pabyTemp == nullptr)
1654
0
        {
1655
0
            return CE_Failure;
1656
0
        }
1657
0
        poGDS->m_nLastBandRasterIOXOff = nXOff;
1658
0
        poGDS->m_nLastBandRasterIOYOff = nYOff;
1659
0
        poGDS->m_nLastBandRasterIOXSize = nXSize;
1660
0
        poGDS->m_nLastBandRasterIOYSize = nYSizeToCache;
1661
0
        poGDS->m_eLastBandRasterIODataType = eBufType;
1662
0
        poGDS->m_pabyLastBufferBandRasterIO = pabyTemp;
1663
1664
0
        CPLErr eErr = poGDS->m_poPansharpener->ProcessRegion(
1665
0
            nXOff, nYOff, nXSize, nYSizeToCache,
1666
0
            poGDS->m_pabyLastBufferBandRasterIO, eBufType);
1667
0
        if (eErr == CE_None)
1668
0
        {
1669
            //{static int bDone = 0; if (!bDone) printf("(8)\n"); bDone = 1; }
1670
0
            size_t nBufferSizePerBandCached = static_cast<size_t>(nXSize) *
1671
0
                                              poGDS->m_nLastBandRasterIOYSize *
1672
0
                                              nDataTypeSize;
1673
0
            memcpy(pData,
1674
0
                   poGDS->m_pabyLastBufferBandRasterIO +
1675
0
                       nBufferSizePerBandCached * m_nIndexAsPansharpenedBand,
1676
0
                   nBufferSizePerBand);
1677
0
        }
1678
0
        else
1679
0
        {
1680
0
            VSIFree(poGDS->m_pabyLastBufferBandRasterIO);
1681
0
            poGDS->m_pabyLastBufferBandRasterIO = nullptr;
1682
0
        }
1683
0
        return eErr;
1684
0
    }
1685
1686
    //{static int bDone = 0; if (!bDone) printf("(9)\n"); bDone = 1; }
1687
0
    CPLErr eErr = VRTRasterBand::IRasterIO(
1688
0
        eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
1689
0
        eBufType, nPixelSpace, nLineSpace, psExtraArg);
1690
1691
0
    return eErr;
1692
0
}
1693
1694
/************************************************************************/
1695
/*                           SerializeToXML()                           */
1696
/************************************************************************/
1697
1698
CPLXMLNode *
1699
VRTPansharpenedRasterBand::SerializeToXML(const char *pszVRTPathIn,
1700
                                          bool &bHasWarnedAboutRAMUsage,
1701
                                          size_t &nAccRAMUsage)
1702
1703
0
{
1704
0
    CPLXMLNode *psTree = VRTRasterBand::SerializeToXML(
1705
0
        pszVRTPathIn, bHasWarnedAboutRAMUsage, nAccRAMUsage);
1706
1707
    /* -------------------------------------------------------------------- */
1708
    /*      Set subclass.                                                   */
1709
    /* -------------------------------------------------------------------- */
1710
0
    CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
1711
0
                     CXT_Text, "VRTPansharpenedRasterBand");
1712
1713
0
    return psTree;
1714
0
}
1715
1716
/************************************************************************/
1717
/*                         GetOverviewCount()                           */
1718
/************************************************************************/
1719
1720
int VRTPansharpenedRasterBand::GetOverviewCount()
1721
0
{
1722
0
    VRTPansharpenedDataset *poGDS = static_cast<VRTPansharpenedDataset *>(poDS);
1723
1724
    // Build on-the-fly overviews from overviews of pan and spectral bands
1725
0
    if (poGDS->m_poPansharpener != nullptr &&
1726
0
        poGDS->m_apoOverviewDatasets.empty() && poGDS->m_poMainDataset == poGDS)
1727
0
    {
1728
0
        const GDALPansharpenOptions *psOptions =
1729
0
            poGDS->m_poPansharpener->GetOptions();
1730
1731
0
        GDALRasterBand *poPanBand =
1732
0
            GDALRasterBand::FromHandle(psOptions->hPanchroBand);
1733
0
        const int nPanOvrCount = poPanBand->GetOverviewCount();
1734
0
        if (nPanOvrCount > 0)
1735
0
        {
1736
0
            for (int i = 0; i < poGDS->GetRasterCount(); i++)
1737
0
            {
1738
0
                if (!static_cast<VRTRasterBand *>(poGDS->GetRasterBand(i + 1))
1739
0
                         ->IsPansharpenRasterBand())
1740
0
                {
1741
0
                    return 0;
1742
0
                }
1743
0
            }
1744
1745
0
            int nSpectralOvrCount =
1746
0
                GDALRasterBand::FromHandle(psOptions->pahInputSpectralBands[0])
1747
0
                    ->GetOverviewCount();
1748
0
            for (int i = 1; i < psOptions->nInputSpectralBands; i++)
1749
0
            {
1750
0
                auto poSpectralBand = GDALRasterBand::FromHandle(
1751
0
                    psOptions->pahInputSpectralBands[i]);
1752
0
                if (poSpectralBand->GetOverviewCount() != nSpectralOvrCount)
1753
0
                {
1754
0
                    return 0;
1755
0
                }
1756
0
            }
1757
0
            auto poPanBandDS = poPanBand->GetDataset();
1758
0
            for (int j = 0; j < std::min(nPanOvrCount, nSpectralOvrCount); j++)
1759
0
            {
1760
0
                std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>
1761
0
                    poPanOvrDS(GDALCreateOverviewDataset(poPanBandDS, j, true));
1762
0
                if (!poPanOvrDS)
1763
0
                {
1764
0
                    CPLError(CE_Warning, CPLE_AppDefined,
1765
0
                             "GDALCreateOverviewDataset(poPanBandDS, %d, true) "
1766
0
                             "failed",
1767
0
                             j);
1768
0
                    break;
1769
0
                }
1770
0
                GDALRasterBand *poPanOvrBand =
1771
0
                    poPanOvrDS->GetRasterBand(poPanBand->GetBand());
1772
0
                auto poOvrDS = std::make_unique<VRTPansharpenedDataset>(
1773
0
                    poPanOvrBand->GetXSize(), poPanOvrBand->GetYSize());
1774
0
                poOvrDS->m_apoDatasetsToReleaseRef.push_back(
1775
0
                    std::move(poPanOvrDS));
1776
0
                for (int i = 0; i < poGDS->GetRasterCount(); i++)
1777
0
                {
1778
0
                    GDALRasterBand *poSrcBand = poGDS->GetRasterBand(i + 1);
1779
0
                    GDALRasterBand *poBand = new VRTPansharpenedRasterBand(
1780
0
                        poOvrDS.get(), i + 1, poSrcBand->GetRasterDataType());
1781
0
                    const char *pszNBITS =
1782
0
                        poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
1783
0
                    if (pszNBITS)
1784
0
                        poBand->SetMetadataItem("NBITS", pszNBITS,
1785
0
                                                "IMAGE_STRUCTURE");
1786
0
                    poOvrDS->SetBand(i + 1, poBand);
1787
0
                }
1788
1789
0
                std::unique_ptr<GDALPansharpenOptions,
1790
0
                                decltype(&GDALDestroyPansharpenOptions)>
1791
0
                    psPanOvrOptions(GDALClonePansharpenOptions(psOptions),
1792
0
                                    GDALDestroyPansharpenOptions);
1793
0
                psPanOvrOptions->hPanchroBand = poPanOvrBand;
1794
0
                for (int i = 0; i < psOptions->nInputSpectralBands; i++)
1795
0
                {
1796
0
                    auto poSpectralBand = GDALRasterBand::FromHandle(
1797
0
                        psOptions->pahInputSpectralBands[i]);
1798
0
                    std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>
1799
0
                        poSpectralOvrDS(GDALCreateOverviewDataset(
1800
0
                            poSpectralBand->GetDataset(), j, true));
1801
0
                    if (!poSpectralOvrDS)
1802
0
                    {
1803
0
                        CPLError(CE_Warning, CPLE_AppDefined,
1804
0
                                 "GDALCreateOverviewDataset(poSpectralBand->"
1805
0
                                 "GetDataset(), %d, true) failed",
1806
0
                                 j);
1807
0
                        return 0;
1808
0
                    }
1809
0
                    psPanOvrOptions->pahInputSpectralBands[i] =
1810
0
                        poSpectralOvrDS->GetRasterBand(
1811
0
                            poSpectralBand->GetBand());
1812
0
                    poOvrDS->m_apoDatasetsToReleaseRef.push_back(
1813
0
                        std::move(poSpectralOvrDS));
1814
0
                }
1815
0
                poOvrDS->m_poPansharpener =
1816
0
                    std::make_unique<GDALPansharpenOperation>();
1817
0
                if (poOvrDS->m_poPansharpener->Initialize(
1818
0
                        psPanOvrOptions.get()) != CE_None)
1819
0
                {
1820
0
                    CPLError(CE_Warning, CPLE_AppDefined,
1821
0
                             "Unable to initialize pansharpener.");
1822
0
                    return 0;
1823
0
                }
1824
1825
0
                poOvrDS->m_poMainDataset = poGDS;
1826
0
                poOvrDS->SetMetadataItem("INTERLEAVE", "PIXEL",
1827
0
                                         "IMAGE_STRUCTURE");
1828
1829
0
                poGDS->m_apoOverviewDatasets.push_back(std::move(poOvrDS));
1830
0
            }
1831
0
        }
1832
0
    }
1833
0
    return static_cast<int>(poGDS->m_apoOverviewDatasets.size());
1834
0
}
1835
1836
/************************************************************************/
1837
/*                            GetOverview()                             */
1838
/************************************************************************/
1839
1840
GDALRasterBand *VRTPansharpenedRasterBand::GetOverview(int iOvr)
1841
0
{
1842
0
    if (iOvr < 0 || iOvr >= GetOverviewCount())
1843
0
        return nullptr;
1844
1845
0
    VRTPansharpenedDataset *poGDS = static_cast<VRTPansharpenedDataset *>(poDS);
1846
1847
0
    return poGDS->m_apoOverviewDatasets[iOvr]->GetRasterBand(nBand);
1848
0
}
1849
1850
/*! @endcond */