• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.67
/frmts/vrt/vrtpansharpened.cpp
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,
16✔
81
                                       GDALRasterBandH hPanchroBand,
82
                                       int nInputSpectralBands,
83
                                       GDALRasterBandH *pahInputSpectralBands)
84
{
85
    VALIDATE_POINTER1(pszXML, "GDALCreatePansharpenedVRT", nullptr);
16✔
86
    VALIDATE_POINTER1(hPanchroBand, "GDALCreatePansharpenedVRT", nullptr);
16✔
87
    VALIDATE_POINTER1(pahInputSpectralBands, "GDALCreatePansharpenedVRT",
16✔
88
                      nullptr);
89

90
    CPLXMLNode *psTree = CPLParseXMLString(pszXML);
16✔
91
    if (psTree == nullptr)
16✔
92
        return nullptr;
1✔
93
    VRTPansharpenedDataset *poDS = new VRTPansharpenedDataset(0, 0);
15✔
94
    CPLErr eErr = poDS->XMLInit(psTree, nullptr, hPanchroBand,
15✔
95
                                nInputSpectralBands, pahInputSpectralBands);
96
    CPLDestroyXMLNode(psTree);
15✔
97
    if (eErr != CE_None)
15✔
98
    {
99
        delete poDS;
3✔
100
        return nullptr;
3✔
101
    }
102
    return GDALDataset::ToHandle(poDS);
12✔
103
}
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,
92✔
118
                                               int nBlockXSize, int nBlockYSize)
92✔
119
    : VRTDataset(nXSize, nYSize,
120
                 nBlockXSize > 0 ? nBlockXSize : std::min(nXSize, 512),
92✔
121
                 nBlockYSize > 0 ? nBlockYSize : std::min(nYSize, 512)),
92✔
122
      m_poMainDataset(nullptr), m_bLoadingOtherBands(FALSE),
123
      m_pabyLastBufferBandRasterIO(nullptr), m_nLastBandRasterIOXOff(0),
124
      m_nLastBandRasterIOYOff(0), m_nLastBandRasterIOXSize(0),
125
      m_nLastBandRasterIOYSize(0), m_eLastBandRasterIODataType(GDT_Unknown),
126
      m_eGTAdjustment(GTAdjust_Union), m_bNoDataDisabled(FALSE)
276✔
127
{
128
    eAccess = GA_Update;
92✔
129
    m_poMainDataset = this;
92✔
130
}
92✔
131

132
/************************************************************************/
133
/*                    ~VRTPansharpenedDataset()                         */
134
/************************************************************************/
135

136
VRTPansharpenedDataset::~VRTPansharpenedDataset()
184✔
137

138
{
139
    VRTPansharpenedDataset::FlushCache(true);
92✔
140
    VRTPansharpenedDataset::CloseDependentDatasets();
92✔
141
    CPLFree(m_pabyLastBufferBandRasterIO);
92✔
142
}
184✔
143

144
/************************************************************************/
145
/*                        CloseDependentDatasets()                      */
146
/************************************************************************/
147

148
int VRTPansharpenedDataset::CloseDependentDatasets()
92✔
149
{
150
    int bHasDroppedRef = VRTDataset::CloseDependentDatasets();
92✔
151

152
    /* -------------------------------------------------------------------- */
153
    /*      Destroy the raster bands if they exist.                         */
154
    /* -------------------------------------------------------------------- */
155
    for (int iBand = 0; iBand < nBands; iBand++)
288✔
156
    {
157
        delete papoBands[iBand];
196✔
158
    }
159
    nBands = 0;
92✔
160

161
    // Destroy the overviews before m_poPansharpener as they might reference
162
    // files that are in m_apoDatasetsToReleaseRef.
163
    bHasDroppedRef |= !m_apoOverviewDatasets.empty();
92✔
164
    m_apoOverviewDatasets.clear();
92✔
165

166
    if (m_poPansharpener != nullptr)
92✔
167
    {
168
        // Delete the pansharper object before closing the dataset
169
        // because it may have warped the bands into an intermediate VRT
170
        m_poPansharpener.reset();
69✔
171

172
        // Close in reverse order (VRT firsts and real datasets after)
173
        for (int i = static_cast<int>(m_apoDatasetsToReleaseRef.size()) - 1;
324✔
174
             i >= 0; i--)
324✔
175
        {
176
            bHasDroppedRef = TRUE;
255✔
177
            m_apoDatasetsToReleaseRef[i].reset();
255✔
178
        }
179
        m_apoDatasetsToReleaseRef.clear();
69✔
180
    }
181

182
    return bHasDroppedRef;
92✔
183
}
184

185
/************************************************************************/
186
/*                            GetFileList()                             */
187
/************************************************************************/
188

189
char **VRTPansharpenedDataset::GetFileList()
1✔
190
{
191
    char **papszFileList = GDALDataset::GetFileList();
1✔
192

193
    if (m_poPansharpener != nullptr)
1✔
194
    {
195
        const GDALPansharpenOptions *psOptions = m_poPansharpener->GetOptions();
1✔
196
        if (psOptions != nullptr)
1✔
197
        {
198
            std::set<CPLString> oSetNames;
2✔
199
            if (psOptions->hPanchroBand != nullptr)
1✔
200
            {
201
                GDALDatasetH hDS = GDALGetBandDataset(psOptions->hPanchroBand);
1✔
202
                if (hDS != nullptr)
1✔
203
                {
204
                    papszFileList =
205
                        CSLAddString(papszFileList, GDALGetDescription(hDS));
1✔
206
                    oSetNames.insert(GDALGetDescription(hDS));
1✔
207
                }
208
            }
209
            for (int i = 0; i < psOptions->nInputSpectralBands; i++)
4✔
210
            {
211
                if (psOptions->pahInputSpectralBands[i] != nullptr)
3✔
212
                {
213
                    GDALDatasetH hDS =
214
                        GDALGetBandDataset(psOptions->pahInputSpectralBands[i]);
3✔
215
                    if (hDS != nullptr && oSetNames.find(GDALGetDescription(
6✔
216
                                              hDS)) == oSetNames.end())
6✔
217
                    {
218
                        papszFileList = CSLAddString(papszFileList,
1✔
219
                                                     GDALGetDescription(hDS));
220
                        oSetNames.insert(GDALGetDescription(hDS));
1✔
221
                    }
222
                }
223
            }
224
        }
225
    }
226

227
    return papszFileList;
1✔
228
}
229

230
/************************************************************************/
231
/*                              XMLInit()                               */
232
/************************************************************************/
233

234
CPLErr VRTPansharpenedDataset::XMLInit(const CPLXMLNode *psTree,
74✔
235
                                       const char *pszVRTPathIn)
236

237
{
238
    return XMLInit(psTree, pszVRTPathIn, nullptr, 0, nullptr);
74✔
239
}
240

241
CPLErr VRTPansharpenedDataset::XMLInit(const CPLXMLNode *psTree,
89✔
242
                                       const char *pszVRTPathIn,
243
                                       GDALRasterBandH hPanchroBandIn,
244
                                       int nInputSpectralBandsIn,
245
                                       GDALRasterBandH *pahInputSpectralBandsIn)
246
{
247
    CPLErr eErr;
248
    std::unique_ptr<GDALPansharpenOptions,
249
                    decltype(&GDALDestroyPansharpenOptions)>
250
        psPanOptions(nullptr, GDALDestroyPansharpenOptions);
178✔
251

252
    /* -------------------------------------------------------------------- */
253
    /*      Initialize blocksize before calling sub-init so that the        */
254
    /*      band initializers can get it from the dataset object when       */
255
    /*      they are created.                                               */
256
    /* -------------------------------------------------------------------- */
257
    m_nBlockXSize = atoi(CPLGetXMLValue(psTree, "BlockXSize", "512"));
89✔
258
    m_nBlockYSize = atoi(CPLGetXMLValue(psTree, "BlockYSize", "512"));
89✔
259

260
    /* -------------------------------------------------------------------- */
261
    /*      Parse PansharpeningOptions                                      */
262
    /* -------------------------------------------------------------------- */
263

264
    const CPLXMLNode *psOptions = CPLGetXMLNode(psTree, "PansharpeningOptions");
89✔
265
    if (psOptions == nullptr)
89✔
266
    {
267
        CPLError(CE_Failure, CPLE_AppDefined, "Missing PansharpeningOptions");
1✔
268
        return CE_Failure;
1✔
269
    }
270

271
    if (CPLGetXMLValue(psOptions, "MSShiftX", nullptr) != nullptr ||
176✔
272
        CPLGetXMLValue(psOptions, "MSShiftY", nullptr) != nullptr)
88✔
273
    {
274
        CPLError(CE_Failure, CPLE_AppDefined,
×
275
                 "Options MSShiftX and MSShiftY are no longer supported. "
276
                 "Spatial adjustment between panchromatic and multispectral "
277
                 "bands is done through their geotransform.");
278
        return CE_Failure;
×
279
    }
280

281
    CPLString osSourceFilename;
176✔
282
    GDALDataset *poPanDataset = nullptr;
88✔
283
    GDALRasterBand *poPanBand = nullptr;
88✔
284
    std::map<CPLString, GDALDataset *> oMapNamesToDataset;
176✔
285
    int nPanBand;
286

287
    if (hPanchroBandIn == nullptr)
88✔
288
    {
289
        const CPLXMLNode *psPanchroBand =
290
            CPLGetXMLNode(psOptions, "PanchroBand");
73✔
291
        if (psPanchroBand == nullptr)
73✔
292
        {
293
            CPLError(CE_Failure, CPLE_AppDefined, "PanchroBand missing");
1✔
294
            return CE_Failure;
4✔
295
        }
296

297
        osSourceFilename = CPLGetXMLValue(psPanchroBand, "SourceFilename", "");
72✔
298
        if (osSourceFilename.empty())
72✔
299
        {
300
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
301
                     "PanchroBand.SourceFilename missing");
302
            return CE_Failure;
1✔
303
        }
304
        const bool bRelativeToVRT = CPL_TO_BOOL(atoi(CPLGetXMLValue(
71✔
305
            psPanchroBand, "SourceFilename.relativetoVRT", "0")));
306
        if (bRelativeToVRT)
71✔
307
        {
308
            const std::string osAbs = CPLProjectRelativeFilenameSafe(
309
                pszVRTPathIn, osSourceFilename.c_str());
41✔
310
            m_oMapToRelativeFilenames[osAbs] = osSourceFilename;
41✔
311
            osSourceFilename = osAbs;
41✔
312
        }
313

314
        const CPLStringList aosOpenOptions(
315
            GDALDeserializeOpenOptionsFromXML(psPanchroBand));
71✔
316

317
        poPanDataset = GDALDataset::Open(osSourceFilename,
71✔
318
                                         GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
319
                                         nullptr, aosOpenOptions.List());
320
        if (poPanDataset == nullptr)
71✔
321
        {
322
            return CE_Failure;
1✔
323
        }
324

325
        const char *pszSourceBand =
326
            CPLGetXMLValue(psPanchroBand, "SourceBand", "1");
70✔
327
        nPanBand = atoi(pszSourceBand);
70✔
328
        if (poPanBand == nullptr)
70✔
329
            poPanBand = poPanDataset->GetRasterBand(nPanBand);
70✔
330
        if (poPanBand == nullptr)
70✔
331
        {
332
            CPLError(CE_Failure, CPLE_AppDefined, "%s invalid band of %s",
1✔
333
                     pszSourceBand, osSourceFilename.c_str());
334
            GDALClose(poPanDataset);
1✔
335
            return CE_Failure;
1✔
336
        }
337
        oMapNamesToDataset[osSourceFilename] = poPanDataset;
69✔
338
    }
339
    else
340
    {
341
        poPanBand = GDALRasterBand::FromHandle(hPanchroBandIn);
15✔
342
        nPanBand = poPanBand->GetBand();
15✔
343
        poPanDataset = poPanBand->GetDataset();
15✔
344
        if (poPanDataset == nullptr)
15✔
345
        {
346
            CPLError(
×
347
                CE_Failure, CPLE_AppDefined,
348
                "Cannot retrieve dataset associated with panchromatic band");
349
            return CE_Failure;
×
350
        }
351
        oMapNamesToDataset[CPLSPrintf("%p", poPanDataset)] = poPanDataset;
15✔
352
        poPanDataset->Reference();
15✔
353
    }
354
    m_apoDatasetsToReleaseRef.push_back(
84✔
355
        std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>(
168✔
356
            poPanDataset));
357
    poPanDataset = m_apoDatasetsToReleaseRef.back().get();
84✔
358
    // Figure out which kind of adjustment we should do if the pan and spectral
359
    // bands do not share the same geotransform
360
    const char *pszGTAdjustment =
361
        CPLGetXMLValue(psOptions, "SpatialExtentAdjustment", "Union");
84✔
362
    if (EQUAL(pszGTAdjustment, "Union"))
84✔
363
        m_eGTAdjustment = GTAdjust_Union;
78✔
364
    else if (EQUAL(pszGTAdjustment, "Intersection"))
6✔
365
        m_eGTAdjustment = GTAdjust_Intersection;
2✔
366
    else if (EQUAL(pszGTAdjustment, "None"))
4✔
367
        m_eGTAdjustment = GTAdjust_None;
2✔
368
    else if (EQUAL(pszGTAdjustment, "NoneWithoutWarning"))
2✔
369
        m_eGTAdjustment = GTAdjust_NoneWithoutWarning;
1✔
370
    else
371
    {
372
        m_eGTAdjustment = GTAdjust_Union;
1✔
373
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
374
                 "Unsupported value for GeoTransformAdjustment. Defaulting to "
375
                 "Union");
376
    }
377

378
    const char *pszNumThreads =
379
        CPLGetXMLValue(psOptions, "NumThreads", nullptr);
84✔
380
    int nThreads = 0;
84✔
381
    if (pszNumThreads != nullptr)
84✔
382
    {
383
        if (EQUAL(pszNumThreads, "ALL_CPUS"))
37✔
384
            nThreads = -1;
37✔
385
        else
386
            nThreads = atoi(pszNumThreads);
×
387
    }
388

389
    const char *pszAlgorithm =
390
        CPLGetXMLValue(psOptions, "Algorithm", "WeightedBrovey");
84✔
391
    if (!EQUAL(pszAlgorithm, "WeightedBrovey"))
84✔
392
    {
393
        CPLError(CE_Failure, CPLE_AppDefined, "Algorithm %s unsupported",
1✔
394
                 pszAlgorithm);
395
        return CE_Failure;
1✔
396
    }
397

398
    std::vector<double> adfWeights;
166✔
399
    if (const CPLXMLNode *psAlgOptions =
83✔
400
            CPLGetXMLNode(psOptions, "AlgorithmOptions"))
83✔
401
    {
402
        const char *pszWeights =
403
            CPLGetXMLValue(psAlgOptions, "Weights", nullptr);
35✔
404
        if (pszWeights != nullptr)
35✔
405
        {
406
            char **papszTokens = CSLTokenizeString2(pszWeights, " ,", 0);
35✔
407
            for (int i = 0; papszTokens && papszTokens[i]; i++)
124✔
408
                adfWeights.push_back(CPLAtof(papszTokens[i]));
89✔
409
            CSLDestroy(papszTokens);
35✔
410
        }
411
    }
412

413
    GDALRIOResampleAlg eResampleAlg = GDALRasterIOGetResampleAlg(
83✔
414
        CPLGetXMLValue(psOptions, "Resampling", "Cubic"));
415

416
    std::vector<GDALRasterBand *> ahSpectralBands;
166✔
417
    std::map<int, int> aMapDstBandToSpectralBand;
166✔
418
    std::map<int, int>::iterator aMapDstBandToSpectralBandIter;
83✔
419
    int nBitDepth = 0;
83✔
420
    bool bFoundNonMatchingGT = false;
83✔
421
    GDALGeoTransform panGT;
83✔
422
    const bool bPanGeoTransformValid =
423
        (poPanDataset->GetGeoTransform(panGT) == CE_None);
83✔
424
    if (!bPanGeoTransformValid)
83✔
425
    {
426
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
427
                 "Panchromatic band has no associated geotransform");
428
        return CE_Failure;
1✔
429
    }
430
    int nPanXSize = poPanBand->GetXSize();
82✔
431
    int nPanYSize = poPanBand->GetYSize();
82✔
432
    int bHasNoData = FALSE;
82✔
433
    double dfNoData = poPanBand->GetNoDataValue(&bHasNoData);
82✔
434
    double dfLRPanX = panGT[0] + nPanXSize * panGT[1] + nPanYSize * panGT[2];
82✔
435
    double dfLRPanY = panGT[3] + nPanXSize * panGT[4] + nPanYSize * panGT[5];
82✔
436
    bool bFoundRotatingTerms = (panGT[2] != 0.0 || panGT[4] != 0.0);
82✔
437
    double dfMinX = panGT[0];
82✔
438
    double dfMaxX = dfLRPanX;
82✔
439
    double dfMaxY = panGT[3];
82✔
440
    double dfMinY = dfLRPanY;
82✔
441
    if (dfMinY > dfMaxY)
82✔
442
        std::swap(dfMinY, dfMaxY);
11✔
443
    const double dfPanMinY = dfMinY;
82✔
444
    const double dfPanMaxY = dfMaxY;
82✔
445

446
    const auto poPanSRS = poPanDataset->GetSpatialRef();
82✔
447

448
    /* -------------------------------------------------------------------- */
449
    /*      First pass on spectral datasets to check their georeferencing.  */
450
    /* -------------------------------------------------------------------- */
451
    int iSpectralBand = 0;
82✔
452
    for (const CPLXMLNode *psIter = psOptions->psChild; psIter;
528✔
453
         psIter = psIter->psNext)
446✔
454
    {
455
        GDALDataset *poDataset;
456
        if (psIter->eType != CXT_Element ||
450✔
457
            !EQUAL(psIter->pszValue, "SpectralBand"))
450✔
458
            continue;
246✔
459

460
        if (nInputSpectralBandsIn && pahInputSpectralBandsIn != nullptr)
204✔
461
        {
462
            if (iSpectralBand == nInputSpectralBandsIn)
40✔
463
            {
464
                CPLError(CE_Failure, CPLE_AppDefined,
1✔
465
                         "More SpectralBand elements than in source array");
466
                goto error;
4✔
467
            }
468
            poDataset = GDALRasterBand::FromHandle(
469
                            pahInputSpectralBandsIn[iSpectralBand])
39✔
470
                            ->GetDataset();
39✔
471
            if (poDataset == nullptr)
39✔
472
            {
473
                CPLError(CE_Failure, CPLE_AppDefined,
×
474
                         "SpectralBand has no associated dataset");
475
                goto error;
×
476
            }
477
            osSourceFilename = poDataset->GetDescription();
39✔
478

479
            oMapNamesToDataset[CPLSPrintf("%p", poDataset)] = poDataset;
39✔
480
            poDataset->Reference();
39✔
481
        }
482
        else
483
        {
484
            osSourceFilename = CPLGetXMLValue(psIter, "SourceFilename", "");
164✔
485
            if (osSourceFilename.empty())
164✔
486
            {
487
                CPLError(CE_Failure, CPLE_AppDefined,
1✔
488
                         "SpectralBand.SourceFilename missing");
489
                goto error;
1✔
490
            }
491
            int bRelativeToVRT = atoi(
163✔
492
                CPLGetXMLValue(psIter, "SourceFilename.relativetoVRT", "0"));
493
            if (bRelativeToVRT)
163✔
494
            {
495
                const std::string osAbs = CPLProjectRelativeFilenameSafe(
496
                    pszVRTPathIn, osSourceFilename.c_str());
107✔
497
                m_oMapToRelativeFilenames[osAbs] = osSourceFilename;
107✔
498
                osSourceFilename = osAbs;
107✔
499
            }
500
            poDataset = oMapNamesToDataset[osSourceFilename];
163✔
501
            if (poDataset == nullptr)
163✔
502
            {
503
                const CPLStringList aosOpenOptions(
504
                    GDALDeserializeOpenOptionsFromXML(psIter));
68✔
505

506
                poDataset = GDALDataset::Open(
68✔
507
                    osSourceFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
508
                    nullptr, aosOpenOptions.List());
509
                if (poDataset == nullptr)
68✔
510
                {
511
                    goto error;
1✔
512
                }
513
                oMapNamesToDataset[osSourceFilename] = poDataset;
67✔
514
            }
515
            else
516
            {
517
                poDataset->Reference();
95✔
518
            }
519
        }
520
        m_apoDatasetsToReleaseRef.push_back(
201✔
521
            std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>(
402✔
522
                poDataset));
523
        poDataset = m_apoDatasetsToReleaseRef.back().get();
201✔
524

525
        // Check that the spectral band has a georeferencing consistent
526
        // of the pan band. Allow an error of at most the size of one pixel
527
        // of the spectral band.
528
        const auto poSpectralSRS = poDataset->GetSpatialRef();
201✔
529
        if (poPanSRS)
201✔
530
        {
531
            if (poSpectralSRS)
164✔
532
            {
533
                if (!poPanSRS->IsSame(poSpectralSRS))
164✔
534
                {
535
                    CPLError(CE_Warning, CPLE_AppDefined,
1✔
536
                             "Pan dataset and %s do not seem to "
537
                             "have same projection. Results might "
538
                             "be incorrect",
539
                             osSourceFilename.c_str());
540
                }
541
            }
542
            else
543
            {
544
                CPLError(CE_Warning, CPLE_AppDefined,
×
545
                         "Pan dataset has a projection, whereas %s "
546
                         "not. Results might be incorrect",
547
                         osSourceFilename.c_str());
548
            }
549
        }
550
        else if (poSpectralSRS)
37✔
551
        {
552
            CPLError(CE_Warning, CPLE_AppDefined,
×
553
                     "Pan dataset has no projection, whereas %s has "
554
                     "one. Results might be incorrect",
555
                     osSourceFilename.c_str());
556
        }
557

558
        GDALGeoTransform spectralGT;
201✔
559
        if (poDataset->GetGeoTransform(spectralGT) != CE_None)
201✔
560
        {
561
            CPLError(CE_Failure, CPLE_AppDefined,
×
562
                     "Spectral band has no associated geotransform");
563
            goto error;
×
564
        }
565
        if (spectralGT[3] * panGT[3] < 0)
201✔
566
        {
567
            CPLError(CE_Failure, CPLE_NotSupported,
1✔
568
                     "Spectral band vertical orientation is "
569
                     "different from pan one");
570
            goto error;
1✔
571
        }
572

573
        int bIsThisOneNonMatching = FALSE;
200✔
574
        double dfPixelSize = std::max(spectralGT[1], std::abs(spectralGT[5]));
200✔
575
        if (std::abs(panGT[0] - spectralGT[0]) > dfPixelSize ||
376✔
576
            std::abs(panGT[3] - spectralGT[3]) > dfPixelSize)
176✔
577
        {
578
            bIsThisOneNonMatching = TRUE;
24✔
579
            if (m_eGTAdjustment == GTAdjust_None)
24✔
580
            {
581
                CPLError(CE_Warning, CPLE_AppDefined,
2✔
582
                         "Georeferencing of top-left corner of pan "
583
                         "dataset and %s do not match",
584
                         osSourceFilename.c_str());
585
            }
586
        }
587
        bFoundRotatingTerms = bFoundRotatingTerms ||
400✔
588
                              (spectralGT[2] != 0.0 || spectralGT[4] != 0.0);
200✔
589
        double dfLRSpectralX = spectralGT[0] +
200✔
590
                               poDataset->GetRasterXSize() * spectralGT[1] +
200✔
591
                               poDataset->GetRasterYSize() * spectralGT[2];
200✔
592
        double dfLRSpectralY = spectralGT[3] +
200✔
593
                               poDataset->GetRasterXSize() * spectralGT[4] +
200✔
594
                               poDataset->GetRasterYSize() * spectralGT[5];
200✔
595
        if (std::abs(dfLRPanX - dfLRSpectralX) > dfPixelSize ||
376✔
596
            std::abs(dfLRPanY - dfLRSpectralY) > dfPixelSize)
176✔
597
        {
598
            bIsThisOneNonMatching = TRUE;
24✔
599
            if (m_eGTAdjustment == GTAdjust_None)
24✔
600
            {
601
                CPLError(CE_Warning, CPLE_AppDefined,
2✔
602
                         "Georeferencing of bottom-right corner of "
603
                         "pan dataset and %s do not match",
604
                         osSourceFilename.c_str());
605
            }
606
        }
607

608
        double dfThisMinY = dfLRSpectralY;
200✔
609
        double dfThisMaxY = spectralGT[3];
200✔
610
        if (dfThisMinY > dfThisMaxY)
200✔
611
            std::swap(dfThisMinY, dfThisMaxY);
23✔
612
        if (bIsThisOneNonMatching && m_eGTAdjustment == GTAdjust_Union)
200✔
613
        {
614
            dfMinX = std::min(dfMinX, spectralGT[0]);
20✔
615
            dfMinY = std::min(dfMinY, dfThisMinY);
20✔
616
            dfMaxX = std::max(dfMaxX, dfLRSpectralX);
20✔
617
            dfMaxY = std::max(dfMaxY, dfThisMaxY);
20✔
618
        }
619
        else if (bIsThisOneNonMatching &&
180✔
620
                 m_eGTAdjustment == GTAdjust_Intersection)
4✔
621
        {
622
            dfMinX = std::max(dfMinX, spectralGT[0]);
1✔
623
            dfMinY = std::max(dfMinY, dfThisMinY);
1✔
624
            dfMaxX = std::min(dfMaxX, dfLRSpectralX);
1✔
625
            dfMaxY = std::min(dfMaxY, dfThisMaxY);
1✔
626
        }
627

628
        bFoundNonMatchingGT = bFoundNonMatchingGT || bIsThisOneNonMatching;
200✔
629

630
        iSpectralBand++;
200✔
631
    }
632

633
    if (nInputSpectralBandsIn && iSpectralBand != nInputSpectralBandsIn)
78✔
634
    {
635
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
636
                 "Less SpectralBand elements than in source array");
637
        goto error;
1✔
638
    }
639

640
    /* -------------------------------------------------------------------- */
641
    /*      On-the-fly spatial extent adjustment if needed and asked.       */
642
    /* -------------------------------------------------------------------- */
643

644
    if (bFoundNonMatchingGT && (m_eGTAdjustment == GTAdjust_Union ||
77✔
645
                                m_eGTAdjustment == GTAdjust_Intersection))
4✔
646
    {
647
        if (bFoundRotatingTerms)
8✔
648
        {
649
            CPLError(
×
650
                CE_Failure, CPLE_NotSupported,
651
                "One of the panchromatic or spectral datasets has rotating "
652
                "terms in their geotransform matrix. Adjustment not possible");
653
            goto error;
×
654
        }
655
        if (m_eGTAdjustment == GTAdjust_Intersection &&
8✔
656
            (dfMinX >= dfMaxX || dfMinY >= dfMaxY))
1✔
657
        {
658
            CPLError(
×
659
                CE_Failure, CPLE_NotSupported,
660
                "One of the panchromatic or spectral datasets has rotating "
661
                "terms in their geotransform matrix. Adjustment not possible");
662
            goto error;
×
663
        }
664
        if (m_eGTAdjustment == GTAdjust_Union)
8✔
665
            CPLDebug("VRT", "Do union of bounding box of panchromatic and "
7✔
666
                            "spectral datasets");
667
        else
668
            CPLDebug("VRT", "Do intersection of bounding box of panchromatic "
1✔
669
                            "and spectral datasets");
670

671
        // If the pandataset needs adjustments, make sure the coordinates of the
672
        // union/intersection properly align with the grid of the pandataset
673
        // to avoid annoying sub-pixel shifts on the panchro band.
674
        double dfPixelSize = std::max(panGT[1], std::abs(panGT[5]));
8✔
675
        if (std::abs(panGT[0] - dfMinX) > dfPixelSize ||
8✔
676
            std::abs(dfPanMaxY - dfMaxY) > dfPixelSize ||
3✔
677
            std::abs(dfLRPanX - dfMaxX) > dfPixelSize ||
13✔
678
            std::abs(dfPanMinY - dfMinY) > dfPixelSize)
2✔
679
        {
680
            dfMinX =
6✔
681
                panGT[0] +
6✔
682
                std::floor((dfMinX - panGT[0]) / panGT[1] + 0.5) * panGT[1];
6✔
683
            dfMaxX =
6✔
684
                dfLRPanX +
6✔
685
                std::floor((dfMaxX - dfLRPanX) / panGT[1] + 0.5) * panGT[1];
6✔
686
            if (panGT[5] > 0)
6✔
687
            {
688
                dfMinY = dfPanMinY +
×
689
                         std::floor((dfMinY - dfPanMinY) / panGT[5] + 0.5) *
×
690
                             panGT[5];
×
691
                dfMaxY = dfPanMinY +
×
692
                         std::floor((dfMaxY - dfPanMinY) / panGT[5] + 0.5) *
×
693
                             panGT[5];
×
694
            }
695
            else
696
            {
697
                dfMinY = dfPanMaxY +
6✔
698
                         std::floor((dfMinY - dfPanMaxY) / panGT[5] + 0.5) *
6✔
699
                             panGT[5];
6✔
700
                dfMaxY = dfPanMaxY +
6✔
701
                         std::floor((dfMaxY - dfPanMaxY) / panGT[5] + 0.5) *
12✔
702
                             panGT[5];
6✔
703
            }
704
        }
705

706
        std::map<CPLString, GDALDataset *>::iterator oIter =
707
            oMapNamesToDataset.begin();
8✔
708
        for (; oIter != oMapNamesToDataset.end(); ++oIter)
24✔
709
        {
710
            GDALDataset *poSrcDS = oIter->second;
16✔
711
            GDALGeoTransform gt;
16✔
712
            if (poSrcDS->GetGeoTransform(gt) != CE_None)
16✔
713
                continue;
3✔
714

715
            // Check if this dataset needs adjustments
716
            dfPixelSize = std::max(gt[1], std::abs(gt[5]));
16✔
717
            dfPixelSize = std::max(panGT[1], dfPixelSize);
16✔
718
            dfPixelSize = std::max(std::abs(panGT[5]), dfPixelSize);
16✔
719
            double dfThisMinX = gt[0];
16✔
720
            double dfThisMaxX = gt[0] + poSrcDS->GetRasterXSize() * gt[1];
16✔
721
            double dfThisMaxY = gt[3];
16✔
722
            double dfThisMinY = gt[3] + poSrcDS->GetRasterYSize() * gt[5];
16✔
723
            if (dfThisMinY > dfThisMaxY)
16✔
724
                std::swap(dfThisMinY, dfThisMaxY);
2✔
725
            if (std::abs(dfThisMinX - dfMinX) <= dfPixelSize &&
16✔
726
                std::abs(dfThisMaxY - dfMaxY) <= dfPixelSize &&
8✔
727
                std::abs(dfThisMaxX - dfMaxX) <= dfPixelSize &&
27✔
728
                std::abs(dfThisMinY - dfMinY) <= dfPixelSize)
3✔
729
            {
730
                continue;
3✔
731
            }
732

733
            GDALGeoTransform adjustedGT;
13✔
734
            adjustedGT[0] = dfMinX;
13✔
735
            adjustedGT[1] = gt[1];
13✔
736
            adjustedGT[2] = 0;
13✔
737
            adjustedGT[3] = gt[5] > 0 ? dfMinY : dfMaxY;
13✔
738
            adjustedGT[4] = 0;
13✔
739
            adjustedGT[5] = gt[5];
13✔
740
            int nAdjustRasterXSize =
741
                static_cast<int>(0.5 + (dfMaxX - dfMinX) / adjustedGT[1]);
13✔
742
            int nAdjustRasterYSize = static_cast<int>(
743
                0.5 + (dfMaxY - dfMinY) / std::abs(adjustedGT[5]));
13✔
744

745
            VRTDataset *poVDS =
746
                new VRTDataset(nAdjustRasterXSize, nAdjustRasterYSize);
13✔
747
            poVDS->SetWritable(FALSE);
13✔
748
            poVDS->SetDescription(std::string("Shifted ")
26✔
749
                                      .append(poSrcDS->GetDescription())
13✔
750
                                      .c_str());
13✔
751
            poVDS->SetGeoTransform(adjustedGT);
13✔
752
            poVDS->SetProjection(poPanDataset->GetProjectionRef());
13✔
753

754
            for (int i = 0; i < poSrcDS->GetRasterCount(); i++)
32✔
755
            {
756
                GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(i + 1);
19✔
757
                poVDS->AddBand(poSrcBand->GetRasterDataType(), nullptr);
19✔
758
                VRTSourcedRasterBand *poVRTBand =
759
                    static_cast<VRTSourcedRasterBand *>(
760
                        poVDS->GetRasterBand(i + 1));
19✔
761

762
                const char *pszNBITS =
763
                    poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
19✔
764
                if (pszNBITS)
19✔
765
                    poVRTBand->SetMetadataItem("NBITS", pszNBITS,
×
766
                                               "IMAGE_STRUCTURE");
×
767

768
                VRTSimpleSource *poSimpleSource = new VRTSimpleSource();
19✔
769
                poVRTBand->ConfigureSource(
114✔
770
                    poSimpleSource, poSrcBand, FALSE,
771
                    static_cast<int>(
19✔
772
                        std::floor((dfMinX - gt[0]) / gt[1] + 0.001)),
19✔
773
                    gt[5] < 0 ? static_cast<int>(std::floor(
19✔
774
                                    (dfMaxY - dfThisMaxY) / gt[5] + 0.001))
16✔
775
                              : static_cast<int>(std::floor(
3✔
776
                                    (dfMinY - dfThisMinY) / gt[5] + 0.001)),
3✔
777
                    static_cast<int>(0.5 + (dfMaxX - dfMinX) / gt[1]),
19✔
778
                    static_cast<int>(0.5 + (dfMaxY - dfMinY) / std::abs(gt[5])),
19✔
779
                    0, 0, nAdjustRasterXSize, nAdjustRasterYSize);
780

781
                poVRTBand->AddSource(poSimpleSource);
19✔
782
            }
783

784
            oIter->second = poVDS;
13✔
785
            if (poSrcDS == poPanDataset)
13✔
786
            {
787
                panGT = adjustedGT;
6✔
788
                poPanDataset = poVDS;
6✔
789
                poPanBand = poPanDataset->GetRasterBand(nPanBand);
6✔
790
                nPanXSize = poPanDataset->GetRasterXSize();
6✔
791
                nPanYSize = poPanDataset->GetRasterYSize();
6✔
792
            }
793

794
            m_apoDatasetsToReleaseRef.push_back(
13✔
795
                std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>(
26✔
796
                    poVDS));
797
        }
798
    }
799

800
    if (nRasterXSize == 0 && nRasterYSize == 0)
77✔
801
    {
802
        nRasterXSize = nPanXSize;
57✔
803
        nRasterYSize = nPanYSize;
57✔
804
    }
805
    else if (nRasterXSize != nPanXSize || nRasterYSize != nPanYSize)
20✔
806
    {
807
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
808
                 "Inconsistent declared VRT dimensions with panchro dataset");
809
        goto error;
1✔
810
    }
811

812
    /* -------------------------------------------------------------------- */
813
    /*      Initialize all the general VRT stuff.  This will even           */
814
    /*      create the VRTPansharpenedRasterBands and initialize them.      */
815
    /* -------------------------------------------------------------------- */
816
    eErr = VRTDataset::XMLInit(psTree, pszVRTPathIn);
76✔
817

818
    if (eErr != CE_None)
76✔
819
    {
820
        goto error;
1✔
821
    }
822

823
    /* -------------------------------------------------------------------- */
824
    /*      Inherit georeferencing info from panchro band if not defined    */
825
    /*      in VRT.                                                         */
826
    /* -------------------------------------------------------------------- */
827

828
    {
829
        GDALGeoTransform outGT;
75✔
830
        if (GetGeoTransform(outGT) != CE_None && GetGCPCount() == 0 &&
140✔
831
            GetSpatialRef() == nullptr)
65✔
832
        {
833
            if (bPanGeoTransformValid)
65✔
834
            {
835
                SetGeoTransform(panGT);
65✔
836
            }
837
            if (poPanSRS)
65✔
838
            {
839
                SetSpatialRef(poPanSRS);
47✔
840
            }
841
        }
842
    }
843

844
    /* -------------------------------------------------------------------- */
845
    /*      Parse rest of PansharpeningOptions                              */
846
    /* -------------------------------------------------------------------- */
847
    iSpectralBand = 0;
75✔
848
    for (const CPLXMLNode *psIter = psOptions->psChild; psIter;
476✔
849
         psIter = psIter->psNext)
401✔
850
    {
851
        if (psIter->eType != CXT_Element ||
404✔
852
            !EQUAL(psIter->pszValue, "SpectralBand"))
404✔
853
            continue;
220✔
854

855
        GDALDataset *poDataset;
856
        GDALRasterBand *poBand;
857

858
        const char *pszDstBand = CPLGetXMLValue(psIter, "dstBand", nullptr);
184✔
859
        int nDstBand = -1;
184✔
860
        if (pszDstBand != nullptr)
184✔
861
        {
862
            nDstBand = atoi(pszDstBand);
178✔
863
            if (nDstBand <= 0)
178✔
864
            {
865
                CPLError(CE_Failure, CPLE_AppDefined,
1✔
866
                         "SpectralBand.dstBand = '%s' invalid", pszDstBand);
867
                goto error;
3✔
868
            }
869
        }
870

871
        if (nInputSpectralBandsIn && pahInputSpectralBandsIn != nullptr)
183✔
872
        {
873
            poBand = GDALRasterBand::FromHandle(
68✔
874
                pahInputSpectralBandsIn[iSpectralBand]);
34✔
875
            poDataset = poBand->GetDataset();
34✔
876
            if (poDataset)
34✔
877
            {
878
                poDataset = oMapNamesToDataset[CPLSPrintf("%p", poDataset)];
34✔
879
                CPLAssert(poDataset);
34✔
880
                if (poDataset)
34✔
881
                    poBand = poDataset->GetRasterBand(poBand->GetBand());
34✔
882
            }
883
        }
884
        else
885
        {
886
            osSourceFilename = CPLGetXMLValue(psIter, "SourceFilename", "");
149✔
887
            const bool bRelativeToVRT = CPL_TO_BOOL(atoi(
149✔
888
                CPLGetXMLValue(psIter, "SourceFilename.relativetoVRT", "0")));
889
            if (bRelativeToVRT)
149✔
890
                osSourceFilename = CPLProjectRelativeFilenameSafe(
188✔
891
                    pszVRTPathIn, osSourceFilename.c_str());
94✔
892
            poDataset = oMapNamesToDataset[osSourceFilename];
149✔
893
            CPLAssert(poDataset);
149✔
894
            const char *pszSourceBand =
895
                CPLGetXMLValue(psIter, "SourceBand", "1");
149✔
896
            const int nBand = atoi(pszSourceBand);
149✔
897
            poBand = poDataset->GetRasterBand(nBand);
149✔
898
            if (poBand == nullptr)
149✔
899
            {
900
                CPLError(CE_Failure, CPLE_AppDefined, "%s invalid band of %s",
1✔
901
                         pszSourceBand, osSourceFilename.c_str());
902
                goto error;
1✔
903
            }
904
        }
905

906
        if (bHasNoData)
182✔
907
        {
908
            double dfSpectralNoData = poPanBand->GetNoDataValue(&bHasNoData);
3✔
909
            if (bHasNoData && dfSpectralNoData != dfNoData)
3✔
910
                bHasNoData = FALSE;
×
911
        }
912

913
        ahSpectralBands.push_back(poBand);
182✔
914
        if (nDstBand >= 1)
182✔
915
        {
916
            if (aMapDstBandToSpectralBand.find(nDstBand - 1) !=
176✔
917
                aMapDstBandToSpectralBand.end())
352✔
918
            {
919
                CPLError(
1✔
920
                    CE_Failure, CPLE_AppDefined,
921
                    "Another spectral band is already mapped to output band %d",
922
                    nDstBand);
923
                goto error;
1✔
924
            }
925
            aMapDstBandToSpectralBand[nDstBand - 1] =
175✔
926
                static_cast<int>(ahSpectralBands.size() - 1);
175✔
927
        }
928

929
        iSpectralBand++;
181✔
930
    }
931

932
    if (ahSpectralBands.empty())
72✔
933
    {
934
        CPLError(CE_Failure, CPLE_AppDefined, "No spectral band defined");
1✔
935
        goto error;
1✔
936
    }
937

938
    {
939
        const char *pszNoData = CPLGetXMLValue(psOptions, "NoData", nullptr);
71✔
940
        if (pszNoData)
71✔
941
        {
942
            if (EQUAL(pszNoData, "NONE"))
7✔
943
            {
944
                m_bNoDataDisabled = TRUE;
×
945
                bHasNoData = FALSE;
×
946
            }
947
            else
948
            {
949
                bHasNoData = TRUE;
7✔
950
                dfNoData = CPLAtof(pszNoData);
7✔
951
            }
952
        }
953
    }
954

955
    if (GetRasterCount() == 0)
71✔
956
    {
957
        for (int i = 0; i < static_cast<int>(aMapDstBandToSpectralBand.size());
156✔
958
             i++)
959
        {
960
            if (aMapDstBandToSpectralBand.find(i) ==
109✔
961
                aMapDstBandToSpectralBand.end())
218✔
962
            {
963
                CPLError(CE_Failure, CPLE_AppDefined,
1✔
964
                         "Hole in SpectralBand.dstBand numbering");
965
                goto error;
1✔
966
            }
967
            GDALRasterBand *poInputBand = GDALRasterBand::FromHandle(
108✔
968
                ahSpectralBands[aMapDstBandToSpectralBand[i]]);
108✔
969
            GDALRasterBand *poBand = new VRTPansharpenedRasterBand(
970
                this, i + 1, poInputBand->GetRasterDataType());
108✔
971
            poBand->SetColorInterpretation(
108✔
972
                poInputBand->GetColorInterpretation());
108✔
973
            if (bHasNoData)
108✔
974
                poBand->SetNoDataValue(dfNoData);
13✔
975
            SetBand(i + 1, poBand);
108✔
976
        }
977
    }
978
    else
979
    {
980
        int nIdxAsPansharpenedBand = 0;
23✔
981
        for (int i = 0; i < nBands; i++)
91✔
982
        {
983
            if (static_cast<VRTRasterBand *>(GetRasterBand(i + 1))
69✔
984
                    ->IsPansharpenRasterBand())
69✔
985
            {
986
                if (aMapDstBandToSpectralBand.find(i) ==
65✔
987
                    aMapDstBandToSpectralBand.end())
130✔
988
                {
989
                    CPLError(CE_Failure, CPLE_AppDefined,
1✔
990
                             "Band %d of type VRTPansharpenedRasterBand, but "
991
                             "no corresponding SpectralBand",
992
                             i + 1);
993
                    goto error;
1✔
994
                }
995
                else
996
                {
997
                    static_cast<VRTPansharpenedRasterBand *>(
998
                        GetRasterBand(i + 1))
64✔
999
                        ->SetIndexAsPansharpenedBand(nIdxAsPansharpenedBand);
64✔
1000
                    nIdxAsPansharpenedBand++;
64✔
1001
                }
1002
            }
1003
        }
1004
    }
1005

1006
    // Figure out bit depth
1007
    {
1008
        const char *pszBitDepth =
1009
            CPLGetXMLValue(psOptions, "BitDepth", nullptr);
69✔
1010
        if (pszBitDepth == nullptr)
69✔
1011
            pszBitDepth = GDALRasterBand::FromHandle(ahSpectralBands[0])
56✔
1012
                              ->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
56✔
1013
        if (pszBitDepth)
69✔
1014
            nBitDepth = atoi(pszBitDepth);
15✔
1015
        if (nBitDepth)
69✔
1016
        {
1017
            for (int i = 0; i < nBands; i++)
44✔
1018
            {
1019
                if (!static_cast<VRTRasterBand *>(GetRasterBand(i + 1))
29✔
1020
                         ->IsPansharpenRasterBand())
29✔
1021
                    continue;
2✔
1022
                if (GetRasterBand(i + 1)->GetMetadataItem(
27✔
1023
                        "NBITS", "IMAGE_STRUCTURE") == nullptr)
27✔
1024
                {
1025
                    if (nBitDepth != 8 && nBitDepth != 16 && nBitDepth != 32)
27✔
1026
                    {
1027
                        GetRasterBand(i + 1)->SetMetadataItem(
12✔
1028
                            "NBITS", CPLSPrintf("%d", nBitDepth),
1029
                            "IMAGE_STRUCTURE");
6✔
1030
                    }
1031
                }
1032
                else if (nBitDepth == 8 || nBitDepth == 16 || nBitDepth == 32)
×
1033
                {
1034
                    GetRasterBand(i + 1)->SetMetadataItem("NBITS", nullptr,
×
1035
                                                          "IMAGE_STRUCTURE");
×
1036
                }
1037
            }
1038
        }
1039
    }
1040

1041
    if (GDALGetRasterBandXSize(ahSpectralBands[0]) >
69✔
1042
            GDALGetRasterBandXSize(poPanBand) ||
138✔
1043
        GDALGetRasterBandYSize(ahSpectralBands[0]) >
69✔
1044
            GDALGetRasterBandYSize(poPanBand))
69✔
1045
    {
1046
        CPLError(CE_Warning, CPLE_AppDefined,
×
1047
                 "Dimensions of spectral band larger than panchro band");
1048
    }
1049

1050
    aMapDstBandToSpectralBandIter = aMapDstBandToSpectralBand.begin();
69✔
1051
    for (; aMapDstBandToSpectralBandIter != aMapDstBandToSpectralBand.end();
236✔
1052
         ++aMapDstBandToSpectralBandIter)
167✔
1053
    {
1054
        const int nDstBand = 1 + aMapDstBandToSpectralBandIter->first;
168✔
1055
        if (nDstBand > nBands ||
335✔
1056
            !static_cast<VRTRasterBand *>(GetRasterBand(nDstBand))
167✔
1057
                 ->IsPansharpenRasterBand())
167✔
1058
        {
1059
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1060
                     "SpectralBand.dstBand = '%d' invalid", nDstBand);
1061
            goto error;
1✔
1062
        }
1063
    }
1064

1065
    if (adfWeights.empty())
68✔
1066
    {
1067
        for (int i = 0; i < static_cast<int>(ahSpectralBands.size()); i++)
158✔
1068
        {
1069
            adfWeights.push_back(1.0 / ahSpectralBands.size());
114✔
1070
        }
1071
    }
1072
    else if (adfWeights.size() != ahSpectralBands.size())
24✔
1073
    {
1074
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
1075
                 "%d weights defined, but %d input spectral bands",
1076
                 static_cast<int>(adfWeights.size()),
1✔
1077
                 static_cast<int>(ahSpectralBands.size()));
1✔
1078
        goto error;
1✔
1079
    }
1080

1081
    if (aMapDstBandToSpectralBand.empty())
67✔
1082
    {
1083
        CPLError(CE_Warning, CPLE_AppDefined,
1✔
1084
                 "No spectral band is mapped to an output band");
1085
    }
1086

1087
    /* -------------------------------------------------------------------- */
1088
    /*      Instantiate poPansharpener                                      */
1089
    /* -------------------------------------------------------------------- */
1090
    psPanOptions.reset(GDALCreatePansharpenOptions());
67✔
1091
    psPanOptions->ePansharpenAlg = GDAL_PSH_WEIGHTED_BROVEY;
67✔
1092
    psPanOptions->eResampleAlg = eResampleAlg;
67✔
1093
    psPanOptions->nBitDepth = nBitDepth;
67✔
1094
    psPanOptions->nWeightCount = static_cast<int>(adfWeights.size());
67✔
1095
    psPanOptions->padfWeights =
134✔
1096
        static_cast<double *>(CPLMalloc(sizeof(double) * adfWeights.size()));
67✔
1097
    memcpy(psPanOptions->padfWeights, &adfWeights[0],
67✔
1098
           sizeof(double) * adfWeights.size());
67✔
1099
    psPanOptions->hPanchroBand = poPanBand;
67✔
1100
    psPanOptions->nInputSpectralBands =
67✔
1101
        static_cast<int>(ahSpectralBands.size());
67✔
1102
    psPanOptions->pahInputSpectralBands = static_cast<GDALRasterBandH *>(
134✔
1103
        CPLMalloc(sizeof(GDALRasterBandH) * ahSpectralBands.size()));
67✔
1104
    memcpy(psPanOptions->pahInputSpectralBands, &ahSpectralBands[0],
67✔
1105
           sizeof(GDALRasterBandH) * ahSpectralBands.size());
67✔
1106
    psPanOptions->nOutPansharpenedBands =
67✔
1107
        static_cast<int>(aMapDstBandToSpectralBand.size());
67✔
1108
    psPanOptions->panOutPansharpenedBands = static_cast<int *>(
134✔
1109
        CPLMalloc(sizeof(int) * aMapDstBandToSpectralBand.size()));
67✔
1110
    aMapDstBandToSpectralBandIter = aMapDstBandToSpectralBand.begin();
67✔
1111
    for (int i = 0;
229✔
1112
         aMapDstBandToSpectralBandIter != aMapDstBandToSpectralBand.end();
229✔
1113
         ++aMapDstBandToSpectralBandIter, ++i)
162✔
1114
    {
1115
        psPanOptions->panOutPansharpenedBands[i] =
162✔
1116
            aMapDstBandToSpectralBandIter->second;
162✔
1117
    }
1118
    psPanOptions->bHasNoData = bHasNoData;
67✔
1119
    psPanOptions->dfNoData = dfNoData;
67✔
1120
    psPanOptions->nThreads = nThreads;
67✔
1121

1122
    if (nBands == psPanOptions->nOutPansharpenedBands)
67✔
1123
        SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
63✔
1124

1125
    m_poPansharpener = std::make_unique<GDALPansharpenOperation>();
67✔
1126
    eErr = m_poPansharpener->Initialize(psPanOptions.get());
67✔
1127
    if (eErr != CE_None)
67✔
1128
    {
1129
        // Delete the pansharper object before closing the dataset
1130
        // because it may have warped the bands into an intermediate VRT
1131
        m_poPansharpener.reset();
1✔
1132

1133
        // Close in reverse order (VRT firsts and real datasets after)
1134
        for (int i = static_cast<int>(m_apoDatasetsToReleaseRef.size()) - 1;
4✔
1135
             i >= 0; i--)
4✔
1136
        {
1137
            m_apoDatasetsToReleaseRef[i].reset();
3✔
1138
        }
1139
        m_apoDatasetsToReleaseRef.clear();
1✔
1140
    }
1141

1142
    return eErr;
67✔
1143

1144
error:
15✔
1145
    // Close in reverse order (VRT firsts and real datasets after)
1146
    for (int i = static_cast<int>(m_apoDatasetsToReleaseRef.size()) - 1; i >= 0;
63✔
1147
         i--)
1148
    {
1149
        m_apoDatasetsToReleaseRef[i].reset();
48✔
1150
    }
1151
    m_apoDatasetsToReleaseRef.clear();
15✔
1152
    return CE_Failure;
15✔
1153
}
1154

1155
/************************************************************************/
1156
/*                           SerializeToXML()                           */
1157
/************************************************************************/
1158

1159
CPLXMLNode *VRTPansharpenedDataset::SerializeToXML(const char *pszVRTPathIn)
7✔
1160

1161
{
1162
    CPLXMLNode *psTree = VRTDataset::SerializeToXML(pszVRTPathIn);
7✔
1163

1164
    if (psTree == nullptr)
7✔
1165
        return psTree;
×
1166

1167
    /* -------------------------------------------------------------------- */
1168
    /*      Set subclass.                                                   */
1169
    /* -------------------------------------------------------------------- */
1170
    CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
7✔
1171
                     CXT_Text, "VRTPansharpenedDataset");
1172

1173
    /* -------------------------------------------------------------------- */
1174
    /*      Serialize the block size.                                       */
1175
    /* -------------------------------------------------------------------- */
1176
    CPLCreateXMLElementAndValue(psTree, "BlockXSize",
7✔
1177
                                CPLSPrintf("%d", m_nBlockXSize));
1178
    CPLCreateXMLElementAndValue(psTree, "BlockYSize",
7✔
1179
                                CPLSPrintf("%d", m_nBlockYSize));
1180

1181
    /* -------------------------------------------------------------------- */
1182
    /*      Serialize the options.                                          */
1183
    /* -------------------------------------------------------------------- */
1184
    if (m_poPansharpener == nullptr)
7✔
1185
        return psTree;
×
1186
    const GDALPansharpenOptions *psOptions = m_poPansharpener->GetOptions();
7✔
1187
    if (psOptions == nullptr)
7✔
1188
        return psTree;
×
1189

1190
    CPLXMLNode *psOptionsNode =
1191
        CPLCreateXMLNode(psTree, CXT_Element, "PansharpeningOptions");
7✔
1192

1193
    if (psOptions->ePansharpenAlg == GDAL_PSH_WEIGHTED_BROVEY)
7✔
1194
    {
1195
        CPLCreateXMLElementAndValue(psOptionsNode, "Algorithm",
7✔
1196
                                    "WeightedBrovey");
1197
    }
1198
    else
1199
    {
1200
        CPLAssert(false);
×
1201
    }
1202
    if (psOptions->nWeightCount)
7✔
1203
    {
1204
        CPLString osWeights;
14✔
1205
        for (int i = 0; i < psOptions->nWeightCount; i++)
27✔
1206
        {
1207
            if (i)
20✔
1208
                osWeights += ",";
13✔
1209
            osWeights += CPLSPrintf("%.16g", psOptions->padfWeights[i]);
20✔
1210
        }
1211
        CPLCreateXMLElementAndValue(
7✔
1212
            CPLCreateXMLNode(psOptionsNode, CXT_Element, "AlgorithmOptions"),
1213
            "Weights", osWeights.c_str());
1214
    }
1215
    CPLCreateXMLElementAndValue(
7✔
1216
        psOptionsNode, "Resampling",
1217
        GDALRasterIOGetResampleAlg(psOptions->eResampleAlg));
7✔
1218

1219
    if (psOptions->nThreads == -1)
7✔
1220
    {
1221
        CPLCreateXMLElementAndValue(psOptionsNode, "NumThreads", "ALL_CPUS");
6✔
1222
    }
1223
    else if (psOptions->nThreads > 1)
1✔
1224
    {
1225
        CPLCreateXMLElementAndValue(psOptionsNode, "NumThreads",
×
1226
                                    CPLSPrintf("%d", psOptions->nThreads));
×
1227
    }
1228

1229
    if (psOptions->nBitDepth)
7✔
1230
        CPLCreateXMLElementAndValue(psOptionsNode, "BitDepth",
×
1231
                                    CPLSPrintf("%d", psOptions->nBitDepth));
×
1232

1233
    const char *pszAdjust = nullptr;
7✔
1234
    switch (m_eGTAdjustment)
7✔
1235
    {
1236
        case GTAdjust_Union:
7✔
1237
            pszAdjust = "Union";
7✔
1238
            break;
7✔
1239
        case GTAdjust_Intersection:
×
1240
            pszAdjust = "Intersection";
×
1241
            break;
×
1242
        case GTAdjust_None:
×
1243
            pszAdjust = "None";
×
1244
            break;
×
1245
        case GTAdjust_NoneWithoutWarning:
×
1246
            pszAdjust = "NoneWithoutWarning";
×
1247
            break;
×
1248
        default:
×
1249
            break;
×
1250
    }
1251

1252
    if (psOptions->bHasNoData)
7✔
1253
    {
1254
        CPLCreateXMLElementAndValue(psOptionsNode, "NoData",
1✔
1255
                                    CPLSPrintf("%.16g", psOptions->dfNoData));
1✔
1256
    }
1257
    else if (m_bNoDataDisabled)
6✔
1258
    {
1259
        CPLCreateXMLElementAndValue(psOptionsNode, "NoData", "None");
×
1260
    }
1261

1262
    if (pszAdjust)
7✔
1263
        CPLCreateXMLElementAndValue(psOptionsNode, "SpatialExtentAdjustment",
7✔
1264
                                    pszAdjust);
1265

1266
    if (psOptions->hPanchroBand)
7✔
1267
    {
1268
        CPLXMLNode *psBand =
1269
            CPLCreateXMLNode(psOptionsNode, CXT_Element, "PanchroBand");
7✔
1270
        GDALRasterBand *poBand =
1271
            GDALRasterBand::FromHandle(psOptions->hPanchroBand);
7✔
1272
        if (poBand->GetDataset())
7✔
1273
        {
1274
            auto poPanchoDS = poBand->GetDataset();
7✔
1275
            std::map<CPLString, CPLString>::iterator oIter =
1276
                m_oMapToRelativeFilenames.find(poPanchoDS->GetDescription());
7✔
1277
            if (oIter == m_oMapToRelativeFilenames.end())
7✔
1278
            {
1279
                CPLCreateXMLElementAndValue(psBand, "SourceFilename",
6✔
1280
                                            poPanchoDS->GetDescription());
6✔
1281
            }
1282
            else
1283
            {
1284
                CPLXMLNode *psSourceFilename = CPLCreateXMLElementAndValue(
1✔
1285
                    psBand, "SourceFilename", oIter->second);
1✔
1286
                CPLCreateXMLNode(CPLCreateXMLNode(psSourceFilename,
1✔
1287
                                                  CXT_Attribute,
1288
                                                  "relativeToVRT"),
1289
                                 CXT_Text, "1");
1290
            }
1291

1292
            GDALSerializeOpenOptionsToXML(psBand, poPanchoDS->GetOpenOptions());
7✔
1293

1294
            CPLCreateXMLElementAndValue(psBand, "SourceBand",
7✔
1295
                                        CPLSPrintf("%d", poBand->GetBand()));
1296
        }
1297
    }
1298
    for (int i = 0; i < psOptions->nInputSpectralBands; i++)
27✔
1299
    {
1300
        CPLXMLNode *psBand =
1301
            CPLCreateXMLNode(psOptionsNode, CXT_Element, "SpectralBand");
20✔
1302

1303
        for (int j = 0; j < psOptions->nOutPansharpenedBands; j++)
39✔
1304
        {
1305
            if (psOptions->panOutPansharpenedBands[j] == i)
39✔
1306
            {
1307
                for (int k = 0; k < nBands; k++)
40✔
1308
                {
1309
                    if (static_cast<VRTRasterBand *>(GetRasterBand(k + 1))
40✔
1310
                            ->IsPansharpenRasterBand())
40✔
1311
                    {
1312
                        if (static_cast<VRTPansharpenedRasterBand *>(
39✔
1313
                                GetRasterBand(k + 1))
39✔
1314
                                ->GetIndexAsPansharpenedBand() == j)
39✔
1315
                        {
1316
                            CPLCreateXMLNode(CPLCreateXMLNode(psBand,
20✔
1317
                                                              CXT_Attribute,
1318
                                                              "dstBand"),
1319
                                             CXT_Text, CPLSPrintf("%d", k + 1));
1320
                            break;
20✔
1321
                        }
1322
                    }
1323
                }
1324
                break;
20✔
1325
            }
1326
        }
1327

1328
        GDALRasterBand *poBand =
1329
            GDALRasterBand::FromHandle(psOptions->pahInputSpectralBands[i]);
20✔
1330
        if (poBand->GetDataset())
20✔
1331
        {
1332
            auto poSpectralDS = poBand->GetDataset();
20✔
1333
            std::map<CPLString, CPLString>::iterator oIter =
1334
                m_oMapToRelativeFilenames.find(poSpectralDS->GetDescription());
20✔
1335
            if (oIter == m_oMapToRelativeFilenames.end())
20✔
1336
            {
1337
                CPLCreateXMLElementAndValue(psBand, "SourceFilename",
20✔
1338
                                            poSpectralDS->GetDescription());
20✔
1339
            }
1340
            else
1341
            {
1342
                CPLXMLNode *psSourceFilename = CPLCreateXMLElementAndValue(
×
1343
                    psBand, "SourceFilename", oIter->second);
×
1344
                CPLCreateXMLNode(CPLCreateXMLNode(psSourceFilename,
×
1345
                                                  CXT_Attribute,
1346
                                                  "relativeToVRT"),
1347
                                 CXT_Text, "1");
1348
            }
1349

1350
            GDALSerializeOpenOptionsToXML(psBand,
20✔
1351
                                          poSpectralDS->GetOpenOptions());
20✔
1352

1353
            CPLCreateXMLElementAndValue(psBand, "SourceBand",
20✔
1354
                                        CPLSPrintf("%d", poBand->GetBand()));
1355
        }
1356
    }
1357

1358
    return psTree;
7✔
1359
}
1360

1361
/************************************************************************/
1362
/*                            GetBlockSize()                            */
1363
/************************************************************************/
1364

1365
void VRTPansharpenedDataset::GetBlockSize(int *pnBlockXSize,
192✔
1366
                                          int *pnBlockYSize) const
1367

1368
{
1369
    assert(nullptr != pnBlockXSize);
192✔
1370
    assert(nullptr != pnBlockYSize);
192✔
1371

1372
    *pnBlockXSize = m_nBlockXSize;
192✔
1373
    *pnBlockYSize = m_nBlockYSize;
192✔
1374
}
192✔
1375

1376
/************************************************************************/
1377
/*                              AddBand()                               */
1378
/************************************************************************/
1379

1380
CPLErr VRTPansharpenedDataset::AddBand(CPL_UNUSED GDALDataType eType,
1✔
1381
                                       CPL_UNUSED char **papszOptions)
1382

1383
{
1384
    CPLError(CE_Failure, CPLE_NotSupported, "AddBand() not supported");
1✔
1385

1386
    return CE_Failure;
1✔
1387
}
1388

1389
/************************************************************************/
1390
/*                              IRasterIO()                             */
1391
/************************************************************************/
1392

1393
CPLErr VRTPansharpenedDataset::IRasterIO(
21✔
1394
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
1395
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
1396
    int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
1397
    GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
1398
{
1399
    if (eRWFlag == GF_Write)
21✔
1400
        return CE_Failure;
×
1401

1402
    /* Try to pass the request to the most appropriate overview dataset */
1403
    if (nBufXSize < nXSize && nBufYSize < nYSize)
21✔
1404
    {
1405
        int bTried;
1406
        CPLErr eErr = TryOverviewRasterIO(
1✔
1407
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
1408
            eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
1409
            nBandSpace, psExtraArg, &bTried);
1410
        if (bTried)
1✔
1411
            return eErr;
×
1412
    }
1413

1414
    const int nDataTypeSize = GDALGetDataTypeSizeBytes(eBufType);
21✔
1415
    if (nXSize == nBufXSize && nYSize == nBufYSize &&
21✔
1416
        nDataTypeSize == nPixelSpace && nLineSpace == nPixelSpace * nBufXSize &&
20✔
1417
        nBandSpace == nLineSpace * nBufYSize && nBandCount == nBands)
20✔
1418
    {
1419
        for (int i = 0; i < nBands; i++)
82✔
1420
        {
1421
            if (panBandMap[i] != i + 1 ||
124✔
1422
                !static_cast<VRTRasterBand *>(GetRasterBand(i + 1))
62✔
1423
                     ->IsPansharpenRasterBand())
62✔
1424
            {
1425
                goto default_path;
×
1426
            }
1427
        }
1428

1429
        //{static int bDone = 0; if (!bDone) printf("(2)\n"); bDone = 1; }
1430
        return m_poPansharpener->ProcessRegion(nXOff, nYOff, nXSize, nYSize,
20✔
1431
                                               pData, eBufType);
20✔
1432
    }
1433

1434
default_path:
1✔
1435
    //{static int bDone = 0; if (!bDone) printf("(3)\n"); bDone = 1; }
1436
    return VRTDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
1✔
1437
                                 nBufXSize, nBufYSize, eBufType, nBandCount,
1438
                                 panBandMap, nPixelSpace, nLineSpace,
1439
                                 nBandSpace, psExtraArg);
1✔
1440
}
1441

1442
/************************************************************************/
1443
/* ==================================================================== */
1444
/*                        VRTPansharpenedRasterBand                     */
1445
/* ==================================================================== */
1446
/************************************************************************/
1447

1448
/************************************************************************/
1449
/*                        VRTPansharpenedRasterBand()                   */
1450
/************************************************************************/
1451

1452
VRTPansharpenedRasterBand::VRTPansharpenedRasterBand(GDALDataset *poDSIn,
192✔
1453
                                                     int nBandIn,
1454
                                                     GDALDataType eDataTypeIn)
192✔
1455
    : m_nIndexAsPansharpenedBand(nBandIn - 1)
192✔
1456
{
1457
    Initialize(poDSIn->GetRasterXSize(), poDSIn->GetRasterYSize());
192✔
1458

1459
    poDS = poDSIn;
192✔
1460
    nBand = nBandIn;
192✔
1461
    eAccess = GA_Update;
192✔
1462
    eDataType = eDataTypeIn;
192✔
1463

1464
    static_cast<VRTPansharpenedDataset *>(poDS)->GetBlockSize(&nBlockXSize,
192✔
1465
                                                              &nBlockYSize);
1466
}
192✔
1467

1468
/************************************************************************/
1469
/*                        ~VRTPansharpenedRasterBand()                  */
1470
/************************************************************************/
1471

1472
VRTPansharpenedRasterBand::~VRTPansharpenedRasterBand()
384✔
1473

1474
{
1475
    FlushCache(true);
192✔
1476
}
384✔
1477

1478
/************************************************************************/
1479
/*                             IReadBlock()                             */
1480
/************************************************************************/
1481

1482
CPLErr VRTPansharpenedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
26✔
1483
                                             void *pImage)
1484

1485
{
1486
    const int nReqXOff = nBlockXOff * nBlockXSize;
26✔
1487
    const int nReqYOff = nBlockYOff * nBlockYSize;
26✔
1488
    int nReqXSize = nBlockXSize;
26✔
1489
    int nReqYSize = nBlockYSize;
26✔
1490
    if (nReqXOff + nReqXSize > nRasterXSize)
26✔
1491
        nReqXSize = nRasterXSize - nReqXOff;
13✔
1492
    if (nReqYOff + nReqYSize > nRasterYSize)
26✔
1493
        nReqYSize = nRasterYSize - nReqYOff;
22✔
1494

1495
    //{static int bDone = 0; if (!bDone) printf("(4)\n"); bDone = 1; }
1496
    const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
26✔
1497
    GDALRasterIOExtraArg sExtraArg;
1498
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
26✔
1499
    if (IRasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize, pImage,
52✔
1500
                  nReqXSize, nReqYSize, eDataType, nDataTypeSize,
1501
                  static_cast<GSpacing>(nDataTypeSize) * nReqXSize,
26✔
1502
                  &sExtraArg) != CE_None)
26✔
1503
    {
1504
        return CE_Failure;
×
1505
    }
1506

1507
    if (nReqXSize < nBlockXSize)
26✔
1508
    {
1509
        for (int j = nReqYSize - 1; j >= 0; j--)
5,613✔
1510
        {
1511
            memmove(static_cast<GByte *>(pImage) +
5,600✔
1512
                        static_cast<size_t>(j) * nDataTypeSize * nBlockXSize,
5,600✔
1513
                    static_cast<GByte *>(pImage) +
5,600✔
1514
                        static_cast<size_t>(j) * nDataTypeSize * nReqXSize,
5,600✔
1515
                    static_cast<size_t>(nReqXSize) * nDataTypeSize);
5,600✔
1516
            memset(static_cast<GByte *>(pImage) +
5,600✔
1517
                       (static_cast<size_t>(j) * nBlockXSize + nReqXSize) *
5,600✔
1518
                           nDataTypeSize,
5,600✔
1519
                   0,
1520
                   static_cast<size_t>(nBlockXSize - nReqXSize) *
5,600✔
1521
                       nDataTypeSize);
5,600✔
1522
        }
1523
    }
1524
    if (nReqYSize < nBlockYSize)
26✔
1525
    {
1526
        memset(static_cast<GByte *>(pImage) +
22✔
1527
                   static_cast<size_t>(nReqYSize) * nBlockXSize * nDataTypeSize,
22✔
1528
               0,
1529
               static_cast<size_t>(nBlockYSize - nReqYSize) * nBlockXSize *
22✔
1530
                   nDataTypeSize);
22✔
1531
    }
1532

1533
    // Cache other bands
1534
    CPLErr eErr = CE_None;
26✔
1535
    VRTPansharpenedDataset *poGDS = static_cast<VRTPansharpenedDataset *>(poDS);
26✔
1536
    if (poGDS->nBands != 1 && !poGDS->m_bLoadingOtherBands)
26✔
1537
    {
1538
        poGDS->m_bLoadingOtherBands = TRUE;
10✔
1539

1540
        for (int iOtherBand = 1; iOtherBand <= poGDS->nBands; iOtherBand++)
36✔
1541
        {
1542
            if (iOtherBand == nBand)
26✔
1543
                continue;
10✔
1544

1545
            GDALRasterBlock *poBlock =
1546
                poGDS->GetRasterBand(iOtherBand)
16✔
1547
                    ->GetLockedBlockRef(nBlockXOff, nBlockYOff);
16✔
1548
            if (poBlock == nullptr)
16✔
1549
            {
1550
                eErr = CE_Failure;
×
1551
                break;
×
1552
            }
1553
            poBlock->DropLock();
16✔
1554
        }
1555

1556
        poGDS->m_bLoadingOtherBands = FALSE;
10✔
1557
    }
1558

1559
    return eErr;
26✔
1560
}
1561

1562
/************************************************************************/
1563
/*                              IRasterIO()                             */
1564
/************************************************************************/
1565

1566
CPLErr VRTPansharpenedRasterBand::IRasterIO(
127✔
1567
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
1568
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
1569
    GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
1570
{
1571
    if (eRWFlag == GF_Write)
127✔
1572
        return CE_Failure;
×
1573

1574
    VRTPansharpenedDataset *poGDS = static_cast<VRTPansharpenedDataset *>(poDS);
127✔
1575

1576
    /* Try to pass the request to the most appropriate overview dataset */
1577
    if (nBufXSize < nXSize && nBufYSize < nYSize)
127✔
1578
    {
1579
        int bTried;
1580
        CPLErr eErr = TryOverviewRasterIO(
3✔
1581
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
1582
            eBufType, nPixelSpace, nLineSpace, psExtraArg, &bTried);
1583
        if (bTried)
3✔
1584
            return eErr;
×
1585
    }
1586

1587
    const int nDataTypeSize = GDALGetDataTypeSizeBytes(eBufType);
127✔
1588
    if (nDataTypeSize > 0 && nXSize == nBufXSize && nYSize == nBufYSize &&
127✔
1589
        nDataTypeSize == nPixelSpace && nLineSpace == nPixelSpace * nBufXSize)
124✔
1590
    {
1591
        const GDALPansharpenOptions *psOptions =
1592
            poGDS->m_poPansharpener->GetOptions();
124✔
1593

1594
        // Have we already done this request for another band ?
1595
        // If so use the cached result
1596
        const size_t nBufferSizePerBand =
124✔
1597
            static_cast<size_t>(nXSize) * nYSize * nDataTypeSize;
124✔
1598
        if (nXOff == poGDS->m_nLastBandRasterIOXOff &&
124✔
1599
            nYOff >= poGDS->m_nLastBandRasterIOYOff &&
118✔
1600
            nXSize == poGDS->m_nLastBandRasterIOXSize &&
111✔
1601
            nYOff + nYSize <= poGDS->m_nLastBandRasterIOYOff +
69✔
1602
                                  poGDS->m_nLastBandRasterIOYSize &&
69✔
1603
            eBufType == poGDS->m_eLastBandRasterIODataType)
59✔
1604
        {
1605
            //{static int bDone = 0; if (!bDone) printf("(6)\n"); bDone = 1; }
1606
            if (poGDS->m_pabyLastBufferBandRasterIO == nullptr)
53✔
1607
                return CE_Failure;
×
1608
            const size_t nBufferSizePerBandCached =
53✔
1609
                static_cast<size_t>(nXSize) * poGDS->m_nLastBandRasterIOYSize *
53✔
1610
                nDataTypeSize;
53✔
1611
            memcpy(pData,
53✔
1612
                   poGDS->m_pabyLastBufferBandRasterIO +
53✔
1613
                       nBufferSizePerBandCached * m_nIndexAsPansharpenedBand +
53✔
1614
                       static_cast<size_t>(nYOff -
53✔
1615
                                           poGDS->m_nLastBandRasterIOYOff) *
53✔
1616
                           nXSize * nDataTypeSize,
53✔
1617
                   nBufferSizePerBand);
1618
            return CE_None;
53✔
1619
        }
1620

1621
        int nYSizeToCache = nYSize;
71✔
1622
        if (nYSize == 1 && nXSize == nRasterXSize)
71✔
1623
        {
1624
            //{static int bDone = 0; if (!bDone) printf("(7)\n"); bDone = 1; }
1625
            // For efficiency, try to cache at leak 256 K
1626
            nYSizeToCache = (256 * 1024) / nXSize / nDataTypeSize;
×
1627
            if (nYSizeToCache == 0)
×
1628
                nYSizeToCache = 1;
×
1629
            else if (nYOff + nYSizeToCache > nRasterYSize)
×
1630
                nYSizeToCache = nRasterYSize - nYOff;
×
1631
        }
1632
        const GUIntBig nBufferSize = static_cast<GUIntBig>(nXSize) *
71✔
1633
                                     nYSizeToCache * nDataTypeSize *
71✔
1634
                                     psOptions->nOutPansharpenedBands;
71✔
1635
        // Check the we don't overflow (for 32 bit platforms)
1636
        if (nBufferSize > std::numeric_limits<size_t>::max() / 2)
71✔
1637
        {
1638
            CPLError(CE_Failure, CPLE_OutOfMemory,
×
1639
                     "Out of memory error while allocating working buffers");
1640
            return CE_Failure;
×
1641
        }
1642
        GByte *pabyTemp = static_cast<GByte *>(
1643
            VSI_REALLOC_VERBOSE(poGDS->m_pabyLastBufferBandRasterIO,
71✔
1644
                                static_cast<size_t>(nBufferSize)));
1645
        if (pabyTemp == nullptr)
71✔
1646
        {
1647
            return CE_Failure;
×
1648
        }
1649
        poGDS->m_nLastBandRasterIOXOff = nXOff;
71✔
1650
        poGDS->m_nLastBandRasterIOYOff = nYOff;
71✔
1651
        poGDS->m_nLastBandRasterIOXSize = nXSize;
71✔
1652
        poGDS->m_nLastBandRasterIOYSize = nYSizeToCache;
71✔
1653
        poGDS->m_eLastBandRasterIODataType = eBufType;
71✔
1654
        poGDS->m_pabyLastBufferBandRasterIO = pabyTemp;
71✔
1655

1656
        CPLErr eErr = poGDS->m_poPansharpener->ProcessRegion(
71✔
1657
            nXOff, nYOff, nXSize, nYSizeToCache,
1658
            poGDS->m_pabyLastBufferBandRasterIO, eBufType);
71✔
1659
        if (eErr == CE_None)
71✔
1660
        {
1661
            //{static int bDone = 0; if (!bDone) printf("(8)\n"); bDone = 1; }
1662
            size_t nBufferSizePerBandCached = static_cast<size_t>(nXSize) *
71✔
1663
                                              poGDS->m_nLastBandRasterIOYSize *
71✔
1664
                                              nDataTypeSize;
71✔
1665
            memcpy(pData,
71✔
1666
                   poGDS->m_pabyLastBufferBandRasterIO +
71✔
1667
                       nBufferSizePerBandCached * m_nIndexAsPansharpenedBand,
71✔
1668
                   nBufferSizePerBand);
1669
        }
1670
        else
1671
        {
1672
            VSIFree(poGDS->m_pabyLastBufferBandRasterIO);
×
1673
            poGDS->m_pabyLastBufferBandRasterIO = nullptr;
×
1674
        }
1675
        return eErr;
71✔
1676
    }
1677

1678
    //{static int bDone = 0; if (!bDone) printf("(9)\n"); bDone = 1; }
1679
    CPLErr eErr = VRTRasterBand::IRasterIO(
3✔
1680
        eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
1681
        eBufType, nPixelSpace, nLineSpace, psExtraArg);
1682

1683
    return eErr;
3✔
1684
}
1685

1686
/************************************************************************/
1687
/*                           SerializeToXML()                           */
1688
/************************************************************************/
1689

1690
CPLXMLNode *
1691
VRTPansharpenedRasterBand::SerializeToXML(const char *pszVRTPathIn,
20✔
1692
                                          bool &bHasWarnedAboutRAMUsage,
1693
                                          size_t &nAccRAMUsage)
1694

1695
{
1696
    CPLXMLNode *psTree = VRTRasterBand::SerializeToXML(
20✔
1697
        pszVRTPathIn, bHasWarnedAboutRAMUsage, nAccRAMUsage);
1698

1699
    /* -------------------------------------------------------------------- */
1700
    /*      Set subclass.                                                   */
1701
    /* -------------------------------------------------------------------- */
1702
    CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
20✔
1703
                     CXT_Text, "VRTPansharpenedRasterBand");
1704

1705
    return psTree;
20✔
1706
}
1707

1708
/************************************************************************/
1709
/*                         GetOverviewCount()                           */
1710
/************************************************************************/
1711

1712
int VRTPansharpenedRasterBand::GetOverviewCount()
23✔
1713
{
1714
    VRTPansharpenedDataset *poGDS = static_cast<VRTPansharpenedDataset *>(poDS);
23✔
1715

1716
    // Build on-the-fly overviews from overviews of pan and spectral bands
1717
    if (poGDS->m_poPansharpener != nullptr &&
46✔
1718
        poGDS->m_apoOverviewDatasets.empty() && poGDS->m_poMainDataset == poGDS)
46✔
1719
    {
1720
        const GDALPansharpenOptions *psOptions =
1721
            poGDS->m_poPansharpener->GetOptions();
19✔
1722

1723
        GDALRasterBand *poPanBand =
1724
            GDALRasterBand::FromHandle(psOptions->hPanchroBand);
19✔
1725
        const int nPanOvrCount = poPanBand->GetOverviewCount();
19✔
1726
        if (nPanOvrCount > 0)
19✔
1727
        {
1728
            for (int i = 0; i < poGDS->GetRasterCount(); i++)
14✔
1729
            {
1730
                if (!static_cast<VRTRasterBand *>(poGDS->GetRasterBand(i + 1))
10✔
1731
                         ->IsPansharpenRasterBand())
10✔
1732
                {
1733
                    return 0;
×
1734
                }
1735
            }
1736

1737
            int nSpectralOvrCount =
1738
                GDALRasterBand::FromHandle(psOptions->pahInputSpectralBands[0])
4✔
1739
                    ->GetOverviewCount();
4✔
1740
            for (int i = 1; i < psOptions->nInputSpectralBands; i++)
10✔
1741
            {
1742
                auto poSpectralBand = GDALRasterBand::FromHandle(
6✔
1743
                    psOptions->pahInputSpectralBands[i]);
6✔
1744
                if (poSpectralBand->GetOverviewCount() != nSpectralOvrCount)
6✔
1745
                {
1746
                    return 0;
×
1747
                }
1748
            }
1749
            auto poPanBandDS = poPanBand->GetDataset();
4✔
1750
            for (int j = 0; j < std::min(nPanOvrCount, nSpectralOvrCount); j++)
7✔
1751
            {
1752
                std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>
1753
                    poPanOvrDS(GDALCreateOverviewDataset(poPanBandDS, j, true));
3✔
1754
                if (!poPanOvrDS)
3✔
1755
                {
1756
                    CPLError(CE_Warning, CPLE_AppDefined,
×
1757
                             "GDALCreateOverviewDataset(poPanBandDS, %d, true) "
1758
                             "failed",
1759
                             j);
1760
                    break;
×
1761
                }
1762
                GDALRasterBand *poPanOvrBand =
1763
                    poPanOvrDS->GetRasterBand(poPanBand->GetBand());
3✔
1764
                auto poOvrDS = std::make_unique<VRTPansharpenedDataset>(
1765
                    poPanOvrBand->GetXSize(), poPanOvrBand->GetYSize());
3✔
1766
                poOvrDS->m_apoDatasetsToReleaseRef.push_back(
6✔
1767
                    std::move(poPanOvrDS));
3✔
1768
                for (int i = 0; i < poGDS->GetRasterCount(); i++)
10✔
1769
                {
1770
                    GDALRasterBand *poSrcBand = poGDS->GetRasterBand(i + 1);
7✔
1771
                    GDALRasterBand *poBand = new VRTPansharpenedRasterBand(
1772
                        poOvrDS.get(), i + 1, poSrcBand->GetRasterDataType());
7✔
1773
                    const char *pszNBITS =
1774
                        poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
7✔
1775
                    if (pszNBITS)
7✔
1776
                        poBand->SetMetadataItem("NBITS", pszNBITS,
×
1777
                                                "IMAGE_STRUCTURE");
×
1778
                    poOvrDS->SetBand(i + 1, poBand);
7✔
1779
                }
1780

1781
                std::unique_ptr<GDALPansharpenOptions,
1782
                                decltype(&GDALDestroyPansharpenOptions)>
1783
                    psPanOvrOptions(GDALClonePansharpenOptions(psOptions),
1784
                                    GDALDestroyPansharpenOptions);
3✔
1785
                psPanOvrOptions->hPanchroBand = poPanOvrBand;
3✔
1786
                for (int i = 0; i < psOptions->nInputSpectralBands; i++)
10✔
1787
                {
1788
                    auto poSpectralBand = GDALRasterBand::FromHandle(
7✔
1789
                        psOptions->pahInputSpectralBands[i]);
7✔
1790
                    std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>
1791
                        poSpectralOvrDS(GDALCreateOverviewDataset(
1792
                            poSpectralBand->GetDataset(), j, true));
7✔
1793
                    if (!poSpectralOvrDS)
7✔
1794
                    {
1795
                        CPLError(CE_Warning, CPLE_AppDefined,
×
1796
                                 "GDALCreateOverviewDataset(poSpectralBand->"
1797
                                 "GetDataset(), %d, true) failed",
1798
                                 j);
1799
                        return 0;
×
1800
                    }
1801
                    psPanOvrOptions->pahInputSpectralBands[i] =
14✔
1802
                        poSpectralOvrDS->GetRasterBand(
7✔
1803
                            poSpectralBand->GetBand());
1804
                    poOvrDS->m_apoDatasetsToReleaseRef.push_back(
14✔
1805
                        std::move(poSpectralOvrDS));
7✔
1806
                }
1807
                poOvrDS->m_poPansharpener =
3✔
1808
                    std::make_unique<GDALPansharpenOperation>();
6✔
1809
                if (poOvrDS->m_poPansharpener->Initialize(
6✔
1810
                        psPanOvrOptions.get()) != CE_None)
6✔
1811
                {
1812
                    CPLError(CE_Warning, CPLE_AppDefined,
×
1813
                             "Unable to initialize pansharpener.");
1814
                    return 0;
×
1815
                }
1816

1817
                poOvrDS->m_poMainDataset = poGDS;
3✔
1818
                poOvrDS->SetMetadataItem("INTERLEAVE", "PIXEL",
3✔
1819
                                         "IMAGE_STRUCTURE");
3✔
1820

1821
                poGDS->m_apoOverviewDatasets.push_back(std::move(poOvrDS));
3✔
1822
            }
1823
        }
1824
    }
1825
    return static_cast<int>(poGDS->m_apoOverviewDatasets.size());
23✔
1826
}
1827

1828
/************************************************************************/
1829
/*                            GetOverview()                             */
1830
/************************************************************************/
1831

1832
GDALRasterBand *VRTPansharpenedRasterBand::GetOverview(int iOvr)
6✔
1833
{
1834
    if (iOvr < 0 || iOvr >= GetOverviewCount())
6✔
1835
        return nullptr;
2✔
1836

1837
    VRTPansharpenedDataset *poGDS = static_cast<VRTPansharpenedDataset *>(poDS);
4✔
1838

1839
    return poGDS->m_apoOverviewDatasets[iOvr]->GetRasterBand(nBand);
4✔
1840
}
1841

1842
/*! @endcond */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc