Coverage Report

Created: 2026-02-14 06:52

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
        if (poPanBand == nullptr)
340
0
            poPanBand = poPanDataset->GetRasterBand(nPanBand);
341
0
        if (poPanBand == nullptr)
342
0
        {
343
0
            CPLError(CE_Failure, CPLE_AppDefined, "%s invalid band of %s",
344
0
                     pszSourceBand, osSourceFilename.c_str());
345
0
            GDALClose(poPanDataset);
346
0
            return CE_Failure;
347
0
        }
348
0
        oMapNamesToDataset[osSourceFilename] = poPanDataset;
349
0
    }
350
0
    else
351
0
    {
352
0
        poPanBand = GDALRasterBand::FromHandle(hPanchroBandIn);
353
0
        nPanBand = poPanBand->GetBand();
354
0
        poPanDataset = poPanBand->GetDataset();
355
0
        if (poPanDataset == nullptr)
356
0
        {
357
0
            CPLError(
358
0
                CE_Failure, CPLE_AppDefined,
359
0
                "Cannot retrieve dataset associated with panchromatic band");
360
0
            return CE_Failure;
361
0
        }
362
0
        oMapNamesToDataset[CPLSPrintf("%p", poPanDataset)] = poPanDataset;
363
0
        poPanDataset->Reference();
364
0
    }
365
0
    m_apoDatasetsToReleaseRef.push_back(
366
0
        std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>(
367
0
            poPanDataset));
368
0
    poPanDataset = m_apoDatasetsToReleaseRef.back().get();
369
    // Figure out which kind of adjustment we should do if the pan and spectral
370
    // bands do not share the same geotransform
371
0
    const char *pszGTAdjustment =
372
0
        CPLGetXMLValue(psOptions, "SpatialExtentAdjustment", "Union");
373
0
    if (EQUAL(pszGTAdjustment, "Union"))
374
0
        m_eGTAdjustment = GTAdjust_Union;
375
0
    else if (EQUAL(pszGTAdjustment, "Intersection"))
376
0
        m_eGTAdjustment = GTAdjust_Intersection;
377
0
    else if (EQUAL(pszGTAdjustment, "None"))
378
0
        m_eGTAdjustment = GTAdjust_None;
379
0
    else if (EQUAL(pszGTAdjustment, "NoneWithoutWarning"))
380
0
        m_eGTAdjustment = GTAdjust_NoneWithoutWarning;
381
0
    else
382
0
    {
383
0
        m_eGTAdjustment = GTAdjust_Union;
384
0
        CPLError(CE_Failure, CPLE_AppDefined,
385
0
                 "Unsupported value for GeoTransformAdjustment. Defaulting to "
386
0
                 "Union");
387
0
    }
388
389
0
    const char *pszNumThreads =
390
0
        CPLGetXMLValue(psOptions, "NumThreads", nullptr);
391
0
    int nThreads = 0;
392
0
    if (pszNumThreads != nullptr)
393
0
    {
394
0
        if (EQUAL(pszNumThreads, "ALL_CPUS"))
395
0
            nThreads = -1;
396
0
        else
397
0
            nThreads = atoi(pszNumThreads);
398
0
    }
399
400
0
    const char *pszAlgorithm =
401
0
        CPLGetXMLValue(psOptions, "Algorithm", "WeightedBrovey");
402
0
    if (!EQUAL(pszAlgorithm, "WeightedBrovey"))
403
0
    {
404
0
        CPLError(CE_Failure, CPLE_AppDefined, "Algorithm %s unsupported",
405
0
                 pszAlgorithm);
406
0
        return CE_Failure;
407
0
    }
408
409
0
    std::vector<double> adfWeights;
410
0
    if (const CPLXMLNode *psAlgOptions =
411
0
            CPLGetXMLNode(psOptions, "AlgorithmOptions"))
412
0
    {
413
0
        const char *pszWeights =
414
0
            CPLGetXMLValue(psAlgOptions, "Weights", nullptr);
415
0
        if (pszWeights != nullptr)
416
0
        {
417
0
            char **papszTokens = CSLTokenizeString2(pszWeights, " ,", 0);
418
0
            for (int i = 0; papszTokens && papszTokens[i]; i++)
419
0
                adfWeights.push_back(CPLAtof(papszTokens[i]));
420
0
            CSLDestroy(papszTokens);
421
0
        }
422
0
    }
423
424
0
    GDALRIOResampleAlg eResampleAlg = GDALRasterIOGetResampleAlg(
425
0
        CPLGetXMLValue(psOptions, "Resampling", "Cubic"));
426
427
0
    std::vector<GDALRasterBand *> ahSpectralBands;
428
0
    std::map<int, int> aMapDstBandToSpectralBand;
429
0
    std::map<int, int>::iterator aMapDstBandToSpectralBandIter;
430
0
    int nBitDepth = 0;
431
0
    bool bFoundNonMatchingGT = false;
432
0
    GDALGeoTransform panGT;
433
0
    const bool bPanGeoTransformValid =
434
0
        (poPanDataset->GetGeoTransform(panGT) == CE_None);
435
0
    if (!bPanGeoTransformValid)
436
0
    {
437
0
        CPLError(CE_Failure, CPLE_AppDefined,
438
0
                 "Panchromatic band has no associated geotransform");
439
0
        return CE_Failure;
440
0
    }
441
0
    int nPanXSize = poPanBand->GetXSize();
442
0
    int nPanYSize = poPanBand->GetYSize();
443
0
    int bHasNoData = FALSE;
444
0
    double dfNoData = poPanBand->GetNoDataValue(&bHasNoData);
445
0
    double dfLRPanX = panGT[0] + nPanXSize * panGT[1] + nPanYSize * panGT[2];
446
0
    double dfLRPanY = panGT[3] + nPanXSize * panGT[4] + nPanYSize * panGT[5];
447
0
    bool bFoundRotatingTerms = (panGT[2] != 0.0 || panGT[4] != 0.0);
448
0
    double dfMinX = panGT[0];
449
0
    double dfMaxX = dfLRPanX;
450
0
    double dfMaxY = panGT[3];
451
0
    double dfMinY = dfLRPanY;
452
0
    if (dfMinY > dfMaxY)
453
0
        std::swap(dfMinY, dfMaxY);
454
0
    const double dfPanMinY = dfMinY;
455
0
    const double dfPanMaxY = dfMaxY;
456
457
0
    const auto poPanSRS = poPanDataset->GetSpatialRef();
458
459
    /* -------------------------------------------------------------------- */
460
    /*      First pass on spectral datasets to check their georeferencing.  */
461
    /* -------------------------------------------------------------------- */
462
0
    int iSpectralBand = 0;
463
0
    for (const CPLXMLNode *psIter = psOptions->psChild; psIter;
464
0
         psIter = psIter->psNext)
465
0
    {
466
0
        GDALDataset *poDataset;
467
0
        if (psIter->eType != CXT_Element ||
468
0
            !EQUAL(psIter->pszValue, "SpectralBand"))
469
0
            continue;
470
471
0
        if (nInputSpectralBandsIn && pahInputSpectralBandsIn != nullptr)
472
0
        {
473
0
            if (iSpectralBand == nInputSpectralBandsIn)
474
0
            {
475
0
                CPLError(CE_Failure, CPLE_AppDefined,
476
0
                         "More SpectralBand elements than in source array");
477
0
                goto error;
478
0
            }
479
0
            poDataset = GDALRasterBand::FromHandle(
480
0
                            pahInputSpectralBandsIn[iSpectralBand])
481
0
                            ->GetDataset();
482
0
            if (poDataset == nullptr)
483
0
            {
484
0
                CPLError(CE_Failure, CPLE_AppDefined,
485
0
                         "SpectralBand has no associated dataset");
486
0
                goto error;
487
0
            }
488
0
            osSourceFilename = poDataset->GetDescription();
489
490
0
            oMapNamesToDataset[CPLSPrintf("%p", poDataset)] = poDataset;
491
0
            poDataset->Reference();
492
0
        }
493
0
        else
494
0
        {
495
0
            osSourceFilename = CPLGetXMLValue(psIter, "SourceFilename", "");
496
0
            if (osSourceFilename.empty())
497
0
            {
498
0
                CPLError(CE_Failure, CPLE_AppDefined,
499
0
                         "SpectralBand.SourceFilename missing");
500
0
                goto error;
501
0
            }
502
0
            int bRelativeToVRT = atoi(
503
0
                CPLGetXMLValue(psIter, "SourceFilename.relativetoVRT", "0"));
504
0
            if (bRelativeToVRT)
505
0
            {
506
0
                const std::string osAbs = CPLProjectRelativeFilenameSafe(
507
0
                    pszVRTPathIn, osSourceFilename.c_str());
508
0
                m_oMapToRelativeFilenames[osAbs] = osSourceFilename;
509
0
                osSourceFilename = osAbs;
510
0
            }
511
0
            poDataset = oMapNamesToDataset[osSourceFilename];
512
0
            if (poDataset == nullptr)
513
0
            {
514
0
                const CPLStringList aosOpenOptions(
515
0
                    GDALDeserializeOpenOptionsFromXML(psIter));
516
517
0
                poDataset = GDALDataset::Open(
518
0
                    osSourceFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
519
0
                    nullptr, aosOpenOptions.List());
520
0
                if (poDataset == nullptr)
521
0
                {
522
0
                    goto error;
523
0
                }
524
0
                oMapNamesToDataset[osSourceFilename] = poDataset;
525
0
            }
526
0
            else
527
0
            {
528
0
                poDataset->Reference();
529
0
            }
530
0
        }
531
0
        m_apoDatasetsToReleaseRef.push_back(
532
0
            std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>(
533
0
                poDataset));
534
0
        poDataset = m_apoDatasetsToReleaseRef.back().get();
535
536
        // Check that the spectral band has a georeferencing consistent
537
        // of the pan band. Allow an error of at most the size of one pixel
538
        // of the spectral band.
539
0
        const auto poSpectralSRS = poDataset->GetSpatialRef();
540
0
        if (poPanSRS)
541
0
        {
542
0
            if (poSpectralSRS)
543
0
            {
544
0
                if (!poPanSRS->IsSame(poSpectralSRS))
545
0
                {
546
0
                    CPLError(CE_Warning, CPLE_AppDefined,
547
0
                             "Pan dataset and %s do not seem to "
548
0
                             "have same projection. Results might "
549
0
                             "be incorrect",
550
0
                             osSourceFilename.c_str());
551
0
                }
552
0
            }
553
0
            else
554
0
            {
555
0
                CPLError(CE_Warning, CPLE_AppDefined,
556
0
                         "Pan dataset has a projection, whereas %s "
557
0
                         "not. Results might be incorrect",
558
0
                         osSourceFilename.c_str());
559
0
            }
560
0
        }
561
0
        else if (poSpectralSRS)
562
0
        {
563
0
            CPLError(CE_Warning, CPLE_AppDefined,
564
0
                     "Pan dataset has no projection, whereas %s has "
565
0
                     "one. Results might be incorrect",
566
0
                     osSourceFilename.c_str());
567
0
        }
568
569
0
        GDALGeoTransform spectralGT;
570
0
        if (poDataset->GetGeoTransform(spectralGT) != CE_None)
571
0
        {
572
0
            CPLError(CE_Failure, CPLE_AppDefined,
573
0
                     "Spectral band has no associated geotransform");
574
0
            goto error;
575
0
        }
576
0
        if (spectralGT[5] * panGT[5] < 0)
577
0
        {
578
0
            CPLError(CE_Failure, CPLE_NotSupported,
579
0
                     "Spectral band vertical orientation is "
580
0
                     "different from pan one");
581
0
            goto error;
582
0
        }
583
584
0
        int bIsThisOneNonMatching = FALSE;
585
0
        double dfPixelSize = std::max(spectralGT[1], std::abs(spectralGT[5]));
586
0
        if (std::abs(panGT[0] - spectralGT[0]) > dfPixelSize ||
587
0
            std::abs(panGT[3] - spectralGT[3]) > dfPixelSize)
588
0
        {
589
0
            bIsThisOneNonMatching = TRUE;
590
0
            if (m_eGTAdjustment == GTAdjust_None)
591
0
            {
592
0
                CPLError(CE_Warning, CPLE_AppDefined,
593
0
                         "Georeferencing of top-left corner of pan "
594
0
                         "dataset and %s do not match",
595
0
                         osSourceFilename.c_str());
596
0
            }
597
0
        }
598
0
        bFoundRotatingTerms = bFoundRotatingTerms ||
599
0
                              (spectralGT[2] != 0.0 || spectralGT[4] != 0.0);
600
0
        double dfLRSpectralX = spectralGT[0] +
601
0
                               poDataset->GetRasterXSize() * spectralGT[1] +
602
0
                               poDataset->GetRasterYSize() * spectralGT[2];
603
0
        double dfLRSpectralY = spectralGT[3] +
604
0
                               poDataset->GetRasterXSize() * spectralGT[4] +
605
0
                               poDataset->GetRasterYSize() * spectralGT[5];
606
0
        if (std::abs(dfLRPanX - dfLRSpectralX) > dfPixelSize ||
607
0
            std::abs(dfLRPanY - dfLRSpectralY) > dfPixelSize)
608
0
        {
609
0
            bIsThisOneNonMatching = TRUE;
610
0
            if (m_eGTAdjustment == GTAdjust_None)
611
0
            {
612
0
                CPLError(CE_Warning, CPLE_AppDefined,
613
0
                         "Georeferencing of bottom-right corner of "
614
0
                         "pan dataset and %s do not match",
615
0
                         osSourceFilename.c_str());
616
0
            }
617
0
        }
618
619
0
        double dfThisMinY = dfLRSpectralY;
620
0
        double dfThisMaxY = spectralGT[3];
621
0
        if (dfThisMinY > dfThisMaxY)
622
0
            std::swap(dfThisMinY, dfThisMaxY);
623
0
        if (bIsThisOneNonMatching && m_eGTAdjustment == GTAdjust_Union)
624
0
        {
625
0
            dfMinX = std::min(dfMinX, spectralGT[0]);
626
0
            dfMinY = std::min(dfMinY, dfThisMinY);
627
0
            dfMaxX = std::max(dfMaxX, dfLRSpectralX);
628
0
            dfMaxY = std::max(dfMaxY, dfThisMaxY);
629
0
        }
630
0
        else if (bIsThisOneNonMatching &&
631
0
                 m_eGTAdjustment == GTAdjust_Intersection)
632
0
        {
633
0
            dfMinX = std::max(dfMinX, spectralGT[0]);
634
0
            dfMinY = std::max(dfMinY, dfThisMinY);
635
0
            dfMaxX = std::min(dfMaxX, dfLRSpectralX);
636
0
            dfMaxY = std::min(dfMaxY, dfThisMaxY);
637
0
        }
638
639
0
        bFoundNonMatchingGT = bFoundNonMatchingGT || bIsThisOneNonMatching;
640
641
0
        iSpectralBand++;
642
0
    }
643
644
0
    if (nInputSpectralBandsIn && iSpectralBand != nInputSpectralBandsIn)
645
0
    {
646
0
        CPLError(CE_Failure, CPLE_AppDefined,
647
0
                 "Less SpectralBand elements than in source array");
648
0
        goto error;
649
0
    }
650
651
    /* -------------------------------------------------------------------- */
652
    /*      On-the-fly spatial extent adjustment if needed and asked.       */
653
    /* -------------------------------------------------------------------- */
654
655
0
    if (bFoundNonMatchingGT && (m_eGTAdjustment == GTAdjust_Union ||
656
0
                                m_eGTAdjustment == GTAdjust_Intersection))
657
0
    {
658
0
        if (bFoundRotatingTerms)
659
0
        {
660
0
            CPLError(
661
0
                CE_Failure, CPLE_NotSupported,
662
0
                "One of the panchromatic or spectral datasets has rotating "
663
0
                "terms in their geotransform matrix. Adjustment not possible");
664
0
            goto error;
665
0
        }
666
0
        if (m_eGTAdjustment == GTAdjust_Intersection &&
667
0
            (dfMinX >= dfMaxX || dfMinY >= dfMaxY))
668
0
        {
669
0
            CPLError(
670
0
                CE_Failure, CPLE_NotSupported,
671
0
                "One of the panchromatic or spectral datasets has rotating "
672
0
                "terms in their geotransform matrix. Adjustment not possible");
673
0
            goto error;
674
0
        }
675
0
        if (m_eGTAdjustment == GTAdjust_Union)
676
0
            CPLDebug("VRT", "Do union of bounding box of panchromatic and "
677
0
                            "spectral datasets");
678
0
        else
679
0
            CPLDebug("VRT", "Do intersection of bounding box of panchromatic "
680
0
                            "and spectral datasets");
681
682
        // If the pandataset needs adjustments, make sure the coordinates of the
683
        // union/intersection properly align with the grid of the pandataset
684
        // to avoid annoying sub-pixel shifts on the panchro band.
685
0
        double dfPixelSize = std::max(panGT[1], std::abs(panGT[5]));
686
0
        if (std::abs(panGT[0] - dfMinX) > dfPixelSize ||
687
0
            std::abs(dfPanMaxY - dfMaxY) > dfPixelSize ||
688
0
            std::abs(dfLRPanX - dfMaxX) > dfPixelSize ||
689
0
            std::abs(dfPanMinY - dfMinY) > dfPixelSize)
690
0
        {
691
0
            dfMinX =
692
0
                panGT[0] +
693
0
                std::floor((dfMinX - panGT[0]) / panGT[1] + 0.5) * panGT[1];
694
0
            dfMaxX =
695
0
                dfLRPanX +
696
0
                std::floor((dfMaxX - dfLRPanX) / panGT[1] + 0.5) * panGT[1];
697
0
            if (panGT[5] > 0)
698
0
            {
699
0
                dfMinY = dfPanMinY +
700
0
                         std::floor((dfMinY - dfPanMinY) / panGT[5] + 0.5) *
701
0
                             panGT[5];
702
0
                dfMaxY = dfPanMinY +
703
0
                         std::floor((dfMaxY - dfPanMinY) / panGT[5] + 0.5) *
704
0
                             panGT[5];
705
0
            }
706
0
            else
707
0
            {
708
0
                dfMinY = dfPanMaxY +
709
0
                         std::floor((dfMinY - dfPanMaxY) / panGT[5] + 0.5) *
710
0
                             panGT[5];
711
0
                dfMaxY = dfPanMaxY +
712
0
                         std::floor((dfMaxY - dfPanMaxY) / panGT[5] + 0.5) *
713
0
                             panGT[5];
714
0
            }
715
0
        }
716
717
0
        std::map<CPLString, GDALDataset *>::iterator oIter =
718
0
            oMapNamesToDataset.begin();
719
0
        for (; oIter != oMapNamesToDataset.end(); ++oIter)
720
0
        {
721
0
            GDALDataset *poSrcDS = oIter->second;
722
0
            GDALGeoTransform gt;
723
0
            if (poSrcDS->GetGeoTransform(gt) != CE_None)
724
0
                continue;
725
726
            // Check if this dataset needs adjustments
727
0
            dfPixelSize = std::max(gt.xscale, std::abs(gt.yscale));
728
0
            dfPixelSize = std::max(panGT[1], dfPixelSize);
729
0
            dfPixelSize = std::max(std::abs(panGT[5]), dfPixelSize);
730
0
            double dfThisMinX = gt.xorig;
731
0
            double dfThisMaxX =
732
0
                gt.xorig + poSrcDS->GetRasterXSize() * gt.xscale;
733
0
            double dfThisMaxY = gt.yorig;
734
0
            double dfThisMinY =
735
0
                gt.yorig + poSrcDS->GetRasterYSize() * gt.yscale;
736
0
            if (dfThisMinY > dfThisMaxY)
737
0
                std::swap(dfThisMinY, dfThisMaxY);
738
0
            if (std::abs(dfThisMinX - dfMinX) <= dfPixelSize &&
739
0
                std::abs(dfThisMaxY - dfMaxY) <= dfPixelSize &&
740
0
                std::abs(dfThisMaxX - dfMaxX) <= dfPixelSize &&
741
0
                std::abs(dfThisMinY - dfMinY) <= dfPixelSize)
742
0
            {
743
0
                continue;
744
0
            }
745
746
0
            GDALGeoTransform adjustedGT;
747
0
            adjustedGT[0] = dfMinX;
748
0
            adjustedGT[1] = gt.xscale;
749
0
            adjustedGT[2] = 0;
750
0
            adjustedGT[3] = gt.yscale > 0 ? dfMinY : dfMaxY;
751
0
            adjustedGT[4] = 0;
752
0
            adjustedGT[5] = gt.yscale;
753
0
            const double dfAdjustedRasterXSize =
754
0
                0.5 + (dfMaxX - dfMinX) / adjustedGT[1];
755
0
            const double dfAdjustedRasterYSize =
756
0
                0.5 + (dfMaxY - dfMinY) / std::abs(adjustedGT[5]);
757
0
            if (!(dfAdjustedRasterXSize >= 1 &&
758
0
                  dfAdjustedRasterXSize < INT_MAX &&
759
0
                  dfAdjustedRasterYSize >= 1 &&
760
0
                  dfAdjustedRasterYSize < INT_MAX))
761
0
            {
762
0
                CPLError(CE_Failure, CPLE_AppDefined,
763
0
                         "Datasets are too disjoint");
764
0
                goto error;
765
0
            }
766
0
            const int nAdjustRasterXSize =
767
0
                static_cast<int>(dfAdjustedRasterXSize);
768
0
            const int nAdjustRasterYSize =
769
0
                static_cast<int>(dfAdjustedRasterYSize);
770
771
0
            VRTDataset *poVDS =
772
0
                new VRTDataset(nAdjustRasterXSize, nAdjustRasterYSize);
773
0
            poVDS->SetWritable(FALSE);
774
0
            poVDS->SetMetadataItem("UNDERLYING_DATASET",
775
0
                                   poSrcDS->GetDescription());
776
0
            poVDS->SetGeoTransform(adjustedGT);
777
0
            poVDS->SetProjection(poPanDataset->GetProjectionRef());
778
779
0
            for (int i = 0; i < poSrcDS->GetRasterCount(); i++)
780
0
            {
781
0
                GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(i + 1);
782
0
                poVDS->AddBand(poSrcBand->GetRasterDataType(), nullptr);
783
0
                VRTSourcedRasterBand *poVRTBand =
784
0
                    static_cast<VRTSourcedRasterBand *>(
785
0
                        poVDS->GetRasterBand(i + 1));
786
787
0
                const char *pszNBITS =
788
0
                    poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
789
0
                if (pszNBITS)
790
0
                    poVRTBand->SetMetadataItem("NBITS", pszNBITS,
791
0
                                               "IMAGE_STRUCTURE");
792
793
0
                VRTSimpleSource *poSimpleSource = new VRTSimpleSource();
794
0
                poVRTBand->ConfigureSource(
795
0
                    poSimpleSource, poSrcBand, FALSE,
796
0
                    static_cast<int>(
797
0
                        std::floor((dfMinX - gt.xorig) / gt.xscale + 0.001)),
798
0
                    gt.yscale < 0
799
0
                        ? static_cast<int>(std::floor(
800
0
                              (dfMaxY - dfThisMaxY) / gt.yscale + 0.001))
801
0
                        : static_cast<int>(std::floor(
802
0
                              (dfMinY - dfThisMinY) / gt.yscale + 0.001)),
803
0
                    static_cast<int>(0.5 + (dfMaxX - dfMinX) / gt.xscale),
804
0
                    static_cast<int>(0.5 +
805
0
                                     (dfMaxY - dfMinY) / std::abs(gt.yscale)),
806
0
                    0, 0, nAdjustRasterXSize, nAdjustRasterYSize);
807
808
0
                poVRTBand->AddSource(poSimpleSource);
809
0
            }
810
811
0
            oIter->second = poVDS;
812
0
            if (poSrcDS == poPanDataset)
813
0
            {
814
0
                panGT = adjustedGT;
815
0
                poPanDataset = poVDS;
816
0
                poPanBand = poPanDataset->GetRasterBand(nPanBand);
817
0
                nPanXSize = poPanDataset->GetRasterXSize();
818
0
                nPanYSize = poPanDataset->GetRasterYSize();
819
0
            }
820
821
0
            m_apoDatasetsToReleaseRef.push_back(
822
0
                std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>(
823
0
                    poVDS));
824
0
        }
825
0
    }
826
827
0
    if (nRasterXSize == 0 && nRasterYSize == 0)
828
0
    {
829
0
        nRasterXSize = nPanXSize;
830
0
        nRasterYSize = nPanYSize;
831
0
    }
832
0
    else if (nRasterXSize != nPanXSize || nRasterYSize != nPanYSize)
833
0
    {
834
0
        CPLError(CE_Failure, CPLE_AppDefined,
835
0
                 "Inconsistent declared VRT dimensions with panchro dataset");
836
0
        goto error;
837
0
    }
838
839
    /* -------------------------------------------------------------------- */
840
    /*      Initialize all the general VRT stuff.  This will even           */
841
    /*      create the VRTPansharpenedRasterBands and initialize them.      */
842
    /* -------------------------------------------------------------------- */
843
0
    eErr = VRTDataset::XMLInit(psTree, pszVRTPathIn);
844
845
0
    if (eErr != CE_None)
846
0
    {
847
0
        goto error;
848
0
    }
849
850
    /* -------------------------------------------------------------------- */
851
    /*      Inherit georeferencing info from panchro band if not defined    */
852
    /*      in VRT.                                                         */
853
    /* -------------------------------------------------------------------- */
854
855
0
    {
856
0
        GDALGeoTransform outGT;
857
0
        if (GetGeoTransform(outGT) != CE_None && GetGCPCount() == 0 &&
858
0
            GetSpatialRef() == nullptr)
859
0
        {
860
0
            if (bPanGeoTransformValid)
861
0
            {
862
0
                SetGeoTransform(panGT);
863
0
            }
864
0
            if (poPanSRS)
865
0
            {
866
0
                SetSpatialRef(poPanSRS);
867
0
            }
868
0
        }
869
0
    }
870
871
    /* -------------------------------------------------------------------- */
872
    /*      Parse rest of PansharpeningOptions                              */
873
    /* -------------------------------------------------------------------- */
874
0
    iSpectralBand = 0;
875
0
    for (const CPLXMLNode *psIter = psOptions->psChild; psIter;
876
0
         psIter = psIter->psNext)
877
0
    {
878
0
        if (psIter->eType != CXT_Element ||
879
0
            !EQUAL(psIter->pszValue, "SpectralBand"))
880
0
            continue;
881
882
0
        GDALDataset *poDataset;
883
0
        GDALRasterBand *poBand;
884
885
0
        const char *pszDstBand = CPLGetXMLValue(psIter, "dstBand", nullptr);
886
0
        int nDstBand = -1;
887
0
        if (pszDstBand != nullptr)
888
0
        {
889
0
            nDstBand = atoi(pszDstBand);
890
0
            if (nDstBand <= 0)
891
0
            {
892
0
                CPLError(CE_Failure, CPLE_AppDefined,
893
0
                         "SpectralBand.dstBand = '%s' invalid", pszDstBand);
894
0
                goto error;
895
0
            }
896
0
        }
897
898
0
        if (nInputSpectralBandsIn && pahInputSpectralBandsIn != nullptr)
899
0
        {
900
0
            poBand = GDALRasterBand::FromHandle(
901
0
                pahInputSpectralBandsIn[iSpectralBand]);
902
0
            poDataset = poBand->GetDataset();
903
0
            if (poDataset)
904
0
            {
905
0
                poDataset = oMapNamesToDataset[CPLSPrintf("%p", poDataset)];
906
0
                CPLAssert(poDataset);
907
0
                if (poDataset)
908
0
                    poBand = poDataset->GetRasterBand(poBand->GetBand());
909
0
            }
910
0
        }
911
0
        else
912
0
        {
913
0
            osSourceFilename = CPLGetXMLValue(psIter, "SourceFilename", "");
914
0
            const bool bRelativeToVRT = CPL_TO_BOOL(atoi(
915
0
                CPLGetXMLValue(psIter, "SourceFilename.relativetoVRT", "0")));
916
0
            if (bRelativeToVRT)
917
0
                osSourceFilename = CPLProjectRelativeFilenameSafe(
918
0
                    pszVRTPathIn, osSourceFilename.c_str());
919
0
            poDataset = oMapNamesToDataset[osSourceFilename];
920
0
            CPLAssert(poDataset);
921
0
            const char *pszSourceBand =
922
0
                CPLGetXMLValue(psIter, "SourceBand", "1");
923
0
            const int nBand = atoi(pszSourceBand);
924
0
            poBand = poDataset->GetRasterBand(nBand);
925
0
            if (poBand == nullptr)
926
0
            {
927
0
                CPLError(CE_Failure, CPLE_AppDefined, "%s invalid band of %s",
928
0
                         pszSourceBand, osSourceFilename.c_str());
929
0
                goto error;
930
0
            }
931
0
        }
932
933
0
        if (bHasNoData)
934
0
        {
935
0
            double dfSpectralNoData = poPanBand->GetNoDataValue(&bHasNoData);
936
0
            if (bHasNoData && dfSpectralNoData != dfNoData)
937
0
                bHasNoData = FALSE;
938
0
        }
939
940
0
        ahSpectralBands.push_back(poBand);
941
0
        if (nDstBand >= 1)
942
0
        {
943
0
            if (aMapDstBandToSpectralBand.find(nDstBand - 1) !=
944
0
                aMapDstBandToSpectralBand.end())
945
0
            {
946
0
                CPLError(
947
0
                    CE_Failure, CPLE_AppDefined,
948
0
                    "Another spectral band is already mapped to output band %d",
949
0
                    nDstBand);
950
0
                goto error;
951
0
            }
952
0
            aMapDstBandToSpectralBand[nDstBand - 1] =
953
0
                static_cast<int>(ahSpectralBands.size() - 1);
954
0
        }
955
956
0
        iSpectralBand++;
957
0
    }
958
959
0
    if (ahSpectralBands.empty())
960
0
    {
961
0
        CPLError(CE_Failure, CPLE_AppDefined, "No spectral band defined");
962
0
        goto error;
963
0
    }
964
965
0
    {
966
0
        const char *pszNoData = CPLGetXMLValue(psOptions, "NoData", nullptr);
967
0
        if (pszNoData)
968
0
        {
969
0
            if (EQUAL(pszNoData, "NONE"))
970
0
            {
971
0
                m_bNoDataDisabled = TRUE;
972
0
                bHasNoData = FALSE;
973
0
            }
974
0
            else
975
0
            {
976
0
                bHasNoData = TRUE;
977
0
                dfNoData = CPLAtof(pszNoData);
978
0
            }
979
0
        }
980
0
    }
981
982
0
    if (GetRasterCount() == 0)
983
0
    {
984
0
        for (int i = 0; i < static_cast<int>(aMapDstBandToSpectralBand.size());
985
0
             i++)
986
0
        {
987
0
            if (aMapDstBandToSpectralBand.find(i) ==
988
0
                aMapDstBandToSpectralBand.end())
989
0
            {
990
0
                CPLError(CE_Failure, CPLE_AppDefined,
991
0
                         "Hole in SpectralBand.dstBand numbering");
992
0
                goto error;
993
0
            }
994
0
            GDALRasterBand *poInputBand = GDALRasterBand::FromHandle(
995
0
                ahSpectralBands[aMapDstBandToSpectralBand[i]]);
996
0
            GDALRasterBand *poBand = new VRTPansharpenedRasterBand(
997
0
                this, i + 1, poInputBand->GetRasterDataType());
998
0
            poBand->SetColorInterpretation(
999
0
                poInputBand->GetColorInterpretation());
1000
0
            if (bHasNoData)
1001
0
                poBand->SetNoDataValue(dfNoData);
1002
0
            SetBand(i + 1, poBand);
1003
0
        }
1004
0
    }
1005
0
    else
1006
0
    {
1007
0
        int nIdxAsPansharpenedBand = 0;
1008
0
        for (int i = 0; i < nBands; i++)
1009
0
        {
1010
0
            if (static_cast<VRTRasterBand *>(GetRasterBand(i + 1))
1011
0
                    ->IsPansharpenRasterBand())
1012
0
            {
1013
0
                if (aMapDstBandToSpectralBand.find(i) ==
1014
0
                    aMapDstBandToSpectralBand.end())
1015
0
                {
1016
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1017
0
                             "Band %d of type VRTPansharpenedRasterBand, but "
1018
0
                             "no corresponding SpectralBand",
1019
0
                             i + 1);
1020
0
                    goto error;
1021
0
                }
1022
0
                else
1023
0
                {
1024
0
                    static_cast<VRTPansharpenedRasterBand *>(
1025
0
                        GetRasterBand(i + 1))
1026
0
                        ->SetIndexAsPansharpenedBand(nIdxAsPansharpenedBand);
1027
0
                    nIdxAsPansharpenedBand++;
1028
0
                }
1029
0
            }
1030
0
        }
1031
0
    }
1032
1033
    // Figure out bit depth
1034
0
    {
1035
0
        const char *pszBitDepth =
1036
0
            CPLGetXMLValue(psOptions, "BitDepth", nullptr);
1037
0
        if (pszBitDepth == nullptr)
1038
0
            pszBitDepth = GDALRasterBand::FromHandle(ahSpectralBands[0])
1039
0
                              ->GetMetadataItem("NBITS", "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
                        "NBITS", "IMAGE_STRUCTURE") == nullptr)
1051
0
                {
1052
0
                    if (nBitDepth != 8 && nBitDepth != 16 && nBitDepth != 32)
1053
0
                    {
1054
0
                        GetRasterBand(i + 1)->SetMetadataItem(
1055
0
                            "NBITS", CPLSPrintf("%d", nBitDepth),
1056
0
                            "IMAGE_STRUCTURE");
1057
0
                    }
1058
0
                }
1059
0
                else if (nBitDepth == 8 || nBitDepth == 16 || nBitDepth == 32)
1060
0
                {
1061
0
                    GetRasterBand(i + 1)->SetMetadataItem("NBITS", nullptr,
1062
0
                                                          "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("INTERLEAVE", "PIXEL", "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 =
1831
0
                        poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
1832
0
                    if (pszNBITS)
1833
0
                        poBand->SetMetadataItem("NBITS", pszNBITS,
1834
0
                                                "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("INTERLEAVE", "PIXEL",
1883
0
                                         "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 */