Coverage Report

Created: 2026-07-25 06:50

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