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

OSGeo / gdal / 15143862414

20 May 2025 05:20PM UTC coverage: 70.927% (+0.006%) from 70.921%
15143862414

Pull #12392

github

web-flow
Merge 6e44293c3 into 36cf82678
Pull Request #12392: GDALOverviews: Limit external file size in GDALRegenerateOverviewsMultiBand

182 of 202 new or added lines in 3 files covered. (90.1%)

21204 existing lines in 67 files now uncovered.

567713 of 800420 relevant lines covered (70.93%)

235755.72 hits per line

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

87.11
/frmts/vrt/vrtsourcedrasterband.cpp
1
/******************************************************************************
2
 *
3
 * Project:  Virtual GDAL Datasets
4
 * Purpose:  Implementation of VRTSourcedRasterBand
5
 * Author:   Frank Warmerdam <warmerdam@pobox.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13

14
#include "cpl_port.h"
15
#include "gdal_vrt.h"
16
#include "vrtdataset.h"
17

18
#include <algorithm>
19
#include <cassert>
20
#include <cmath>
21
#include <cstddef>
22
#include <cstdio>
23
#include <cstdlib>
24
#include <cstring>
25
#include <limits>
26
#include <mutex>
27
#include <set>
28
#include <string>
29

30
#include "cpl_conv.h"
31
#include "cpl_error.h"
32
#include "cpl_error_internal.h"
33
#include "cpl_hash_set.h"
34
#include "cpl_minixml.h"
35
#include "cpl_progress.h"
36
#include "cpl_quad_tree.h"
37
#include "cpl_string.h"
38
#include "cpl_vsi.h"
39
#include "gdal.h"
40
#include "gdal_priv.h"
41
#include "gdal_thread_pool.h"
42
#include "ogr_geometry.h"
43

44
/*! @cond Doxygen_Suppress */
45

46
/************************************************************************/
47
/* ==================================================================== */
48
/*                          VRTSourcedRasterBand                        */
49
/* ==================================================================== */
50
/************************************************************************/
51

52
/************************************************************************/
53
/*                        VRTSourcedRasterBand()                        */
54
/************************************************************************/
55

56
VRTSourcedRasterBand::VRTSourcedRasterBand(GDALDataset *poDSIn, int nBandIn)
1,544✔
57
{
58
    VRTRasterBand::Initialize(poDSIn->GetRasterXSize(),
1,544✔
59
                              poDSIn->GetRasterYSize());
60

61
    poDS = poDSIn;
1,544✔
62
    nBand = nBandIn;
1,544✔
63
}
1,544✔
64

65
/************************************************************************/
66
/*                        VRTSourcedRasterBand()                        */
67
/************************************************************************/
68

69
VRTSourcedRasterBand::VRTSourcedRasterBand(GDALDataType eType, int nXSize,
×
70
                                           int nYSize)
×
71
{
72
    VRTRasterBand::Initialize(nXSize, nYSize);
×
73

74
    eDataType = eType;
×
75
}
×
76

77
/************************************************************************/
78
/*                        VRTSourcedRasterBand()                        */
79
/************************************************************************/
80

81
VRTSourcedRasterBand::VRTSourcedRasterBand(GDALDataset *poDSIn, int nBandIn,
317✔
82
                                           GDALDataType eType, int nXSize,
83
                                           int nYSize)
317✔
84
    : VRTSourcedRasterBand(poDSIn, nBandIn, eType, nXSize, nYSize, 0, 0)
317✔
85
{
86
}
317✔
87

88
/************************************************************************/
89
/*                        VRTSourcedRasterBand()                        */
90
/************************************************************************/
91

92
VRTSourcedRasterBand::VRTSourcedRasterBand(GDALDataset *poDSIn, int nBandIn,
137,826✔
93
                                           GDALDataType eType, int nXSize,
94
                                           int nYSize, int nBlockXSizeIn,
95
                                           int nBlockYSizeIn)
137,826✔
96
{
97
    VRTRasterBand::Initialize(nXSize, nYSize);
137,826✔
98

99
    poDS = poDSIn;
137,826✔
100
    nBand = nBandIn;
137,826✔
101

102
    eDataType = eType;
137,826✔
103
    if (nBlockXSizeIn > 0)
137,826✔
104
        nBlockXSize = nBlockXSizeIn;
137,476✔
105
    if (nBlockYSizeIn > 0)
137,826✔
106
        nBlockYSize = nBlockYSizeIn;
137,491✔
107
}
137,826✔
108

109
/************************************************************************/
110
/*                       ~VRTSourcedRasterBand()                        */
111
/************************************************************************/
112

113
VRTSourcedRasterBand::~VRTSourcedRasterBand()
278,337✔
114

115
{
116
    VRTSourcedRasterBand::CloseDependentDatasets();
139,370✔
117
    CSLDestroy(m_papszSourceList);
139,370✔
118
}
278,337✔
119

120
/************************************************************************/
121
/*                  CanIRasterIOBeForwardedToEachSource()               */
122
/************************************************************************/
123

124
bool VRTSourcedRasterBand::CanIRasterIOBeForwardedToEachSource(
258,113✔
125
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
126
    int nBufXSize, int nBufYSize, GDALRasterIOExtraArg *psExtraArg) const
127
{
128
    const auto IsNonNearestInvolved = [this, psExtraArg]
32,439✔
129
    {
130
        if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
5,184✔
131
        {
132
            return true;
15✔
133
        }
134
        for (int i = 0; i < nSources; i++)
14,496✔
135
        {
136
            if (papoSources[i]->GetType() == VRTComplexSource::GetTypeStatic())
9,335✔
137
            {
138
                auto *const poSource =
3,424✔
139
                    static_cast<VRTComplexSource *>(papoSources[i]);
3,424✔
140
                if (!poSource->GetResampling().empty())
3,424✔
141
                    return true;
8✔
142
            }
143
        }
144
        return false;
5,161✔
145
    };
258,113✔
146

147
    // If resampling with non-nearest neighbour, we need to be careful
148
    // if the VRT band exposes a nodata value, but the sources do not have it.
149
    // To also avoid edge effects on sources when downsampling, use the
150
    // base implementation of IRasterIO() (that is acquiring sources at their
151
    // nominal resolution, and then downsampling), but only if none of the
152
    // contributing sources have overviews.
153
    if (eRWFlag == GF_Read && (nXSize != nBufXSize || nYSize != nBufYSize) &&
258,113✔
154
        nSources != 0 && IsNonNearestInvolved())
516,226✔
155
    {
156
        bool bSourceHasOverviews = false;
23✔
157
        const bool bIsDownsampling = (nBufXSize < nXSize && nBufYSize < nYSize);
23✔
158
        int nContributingSources = 0;
23✔
159
        bool bSourceFullySatisfiesRequest = true;
23✔
160
        for (int i = 0; i < nSources; i++)
37✔
161
        {
162
            if (!papoSources[i]->IsSimpleSource())
26✔
163
            {
164
                return false;
×
165
            }
166
            else
167
            {
168
                VRTSimpleSource *const poSource =
26✔
169
                    static_cast<VRTSimpleSource *>(papoSources[i]);
26✔
170

171
                if (poSource->GetType() == VRTComplexSource::GetTypeStatic())
26✔
172
                {
173
                    auto *const poComplexSource =
16✔
174
                        static_cast<VRTComplexSource *>(poSource);
175
                    if (!poComplexSource->GetResampling().empty())
16✔
176
                    {
177
                        const int lMaskFlags =
178
                            const_cast<VRTSourcedRasterBand *>(this)
179
                                ->GetMaskFlags();
8✔
180
                        if ((lMaskFlags != GMF_ALL_VALID &&
6✔
181
                             lMaskFlags != GMF_NODATA) ||
16✔
182
                            IsMaskBand())
2✔
183
                        {
184
                            // Unfortunately this will prevent using overviews
185
                            // of the sources, but it is unpractical to use
186
                            // them without serious implementation complications
187
                            return false;
12✔
188
                        }
189
                    }
190
                }
191

192
                double dfXOff = nXOff;
18✔
193
                double dfYOff = nYOff;
18✔
194
                double dfXSize = nXSize;
18✔
195
                double dfYSize = nYSize;
18✔
196
                if (psExtraArg->bFloatingPointWindowValidity)
18✔
197
                {
198
                    dfXOff = psExtraArg->dfXOff;
1✔
199
                    dfYOff = psExtraArg->dfYOff;
1✔
200
                    dfXSize = psExtraArg->dfXSize;
1✔
201
                    dfYSize = psExtraArg->dfYSize;
1✔
202
                }
203

204
                // The window we will actually request from the source raster
205
                // band.
206
                double dfReqXOff = 0.0;
18✔
207
                double dfReqYOff = 0.0;
18✔
208
                double dfReqXSize = 0.0;
18✔
209
                double dfReqYSize = 0.0;
18✔
210
                int nReqXOff = 0;
18✔
211
                int nReqYOff = 0;
18✔
212
                int nReqXSize = 0;
18✔
213
                int nReqYSize = 0;
18✔
214

215
                // The window we will actual set _within_ the pData buffer.
216
                int nOutXOff = 0;
18✔
217
                int nOutYOff = 0;
18✔
218
                int nOutXSize = 0;
18✔
219
                int nOutYSize = 0;
18✔
220

221
                bool bError = false;
18✔
222
                if (!poSource->GetSrcDstWindow(
18✔
223
                        dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
224
                        &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize,
225
                        &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff,
226
                        &nOutYOff, &nOutXSize, &nOutYSize, bError))
227
                {
228
                    continue;
×
229
                }
230
                auto poBand = poSource->GetRasterBand();
18✔
231
                if (poBand == nullptr)
18✔
232
                {
233
                    return false;
×
234
                }
235
                ++nContributingSources;
18✔
236
                if (!(nOutXOff == 0 && nOutYOff == 0 &&
18✔
237
                      nOutXSize == nBufXSize && nOutYSize == nBufYSize))
17✔
238
                    bSourceFullySatisfiesRequest = false;
4✔
239
                if (m_bNoDataValueSet)
18✔
240
                {
241
                    int bSrcHasNoData = FALSE;
7✔
242
                    const double dfSrcNoData =
243
                        poBand->GetNoDataValue(&bSrcHasNoData);
7✔
244
                    if (!bSrcHasNoData || dfSrcNoData != m_dfNoDataValue)
7✔
245
                    {
246
                        return false;
4✔
247
                    }
248
                }
249
                if (bIsDownsampling)
14✔
250
                {
251
                    if (poBand->GetOverviewCount() != 0)
11✔
252
                    {
253
                        bSourceHasOverviews = true;
4✔
254
                    }
255
                }
256
            }
257
        }
258
        if (bIsDownsampling && !bSourceHasOverviews &&
11✔
259
            (nContributingSources > 1 || !bSourceFullySatisfiesRequest))
3✔
260
        {
261
            return false;
2✔
262
        }
263
    }
264
    return true;
258,099✔
265
}
266

267
/************************************************************************/
268
/*                      CanMultiThreadRasterIO()                        */
269
/************************************************************************/
270

271
bool VRTSourcedRasterBand::CanMultiThreadRasterIO(
471✔
272
    double dfXOff, double dfYOff, double dfXSize, double dfYSize,
273
    int &nContributingSources) const
274
{
275
    int iLastSource = 0;
471✔
276
    CPLRectObj sSourceBounds;
277
    CPLQuadTree *hQuadTree = nullptr;
471✔
278
    bool bRet = true;
471✔
279
    std::set<std::string> oSetDSName;
471✔
280

281
    nContributingSources = 0;
471✔
282
    for (int iSource = 0; iSource < nSources; iSource++)
1,303✔
283
    {
284
        const auto poSource = papoSources[iSource];
834✔
285
        if (!poSource->IsSimpleSource())
834✔
286
        {
287
            bRet = false;
×
288
            break;
×
289
        }
290
        const auto poSimpleSource = cpl::down_cast<VRTSimpleSource *>(poSource);
834✔
291
        if (poSimpleSource->DstWindowIntersects(dfXOff, dfYOff, dfXSize,
834✔
292
                                                dfYSize))
293
        {
294
            // Only build hQuadTree if there are 2 or more sources
295
            if (nContributingSources == 1)
523✔
296
            {
297
                std::string &oFirstSrcDSName =
298
                    cpl::down_cast<VRTSimpleSource *>(papoSources[iLastSource])
26✔
299
                        ->m_osSrcDSName;
26✔
300
                oSetDSName.insert(oFirstSrcDSName);
26✔
301

302
                CPLRectObj sGlobalBounds;
303
                sGlobalBounds.minx = dfXOff;
26✔
304
                sGlobalBounds.miny = dfYOff;
26✔
305
                sGlobalBounds.maxx = dfXOff + dfXSize;
26✔
306
                sGlobalBounds.maxy = dfYOff + dfYSize;
26✔
307
                hQuadTree = CPLQuadTreeCreate(&sGlobalBounds, nullptr);
26✔
308

309
                CPLQuadTreeInsertWithBounds(
26✔
310
                    hQuadTree,
311
                    reinterpret_cast<void *>(
312
                        static_cast<uintptr_t>(iLastSource)),
26✔
313
                    &sSourceBounds);
314
            }
315

316
            // Check there are not several sources with the same name, to avoid
317
            // the same GDALDataset* to be used from multiple threads. We may
318
            // be a bit too pessimistic, for example if working with unnamed
319
            // Memory datasets, but that would involve comparing
320
            // poSource->GetRasterBandNoOpen()->GetDataset()
321
            if (oSetDSName.find(poSimpleSource->m_osSrcDSName) !=
523✔
322
                oSetDSName.end())
1,046✔
323
            {
324
                bRet = false;
×
325
                break;
2✔
326
            }
327
            oSetDSName.insert(poSimpleSource->m_osSrcDSName);
523✔
328

329
            double dfSourceXOff;
330
            double dfSourceYOff;
331
            double dfSourceXSize;
332
            double dfSourceYSize;
333
            poSimpleSource->GetDstWindow(dfSourceXOff, dfSourceYOff,
523✔
334
                                         dfSourceXSize, dfSourceYSize);
335
            constexpr double EPSILON = 1e-1;
523✔
336
            sSourceBounds.minx = dfSourceXOff + EPSILON;
523✔
337
            sSourceBounds.miny = dfSourceYOff + EPSILON;
523✔
338
            sSourceBounds.maxx = dfSourceXOff + dfSourceXSize - EPSILON;
523✔
339
            sSourceBounds.maxy = dfSourceYOff + dfSourceYSize - EPSILON;
523✔
340
            iLastSource = iSource;
523✔
341

342
            if (hQuadTree)
523✔
343
            {
344
                // Check that the new source doesn't overlap an existing one.
345
                if (CPLQuadTreeHasMatch(hQuadTree, &sSourceBounds))
278✔
346
                {
347
                    bRet = false;
2✔
348
                    break;
2✔
349
                }
350

351
                CPLQuadTreeInsertWithBounds(
276✔
352
                    hQuadTree,
353
                    reinterpret_cast<void *>(static_cast<uintptr_t>(iSource)),
276✔
354
                    &sSourceBounds);
355
            }
356

357
            ++nContributingSources;
521✔
358
        }
359
    }
360

361
    if (hQuadTree)
471✔
362
        CPLQuadTreeDestroy(hQuadTree);
26✔
363

364
    return bRet;
942✔
365
}
366

367
/************************************************************************/
368
/*                 VRTSourcedRasterBandRasterIOJob                      */
369
/************************************************************************/
370

371
/** Structure used to declare a threaded job to satisfy IRasterIO()
372
 * on a given source.
373
 */
374
struct VRTSourcedRasterBandRasterIOJob
375
{
376
    std::atomic<int> *pnCompletedJobs = nullptr;
377
    std::atomic<bool> *pbSuccess = nullptr;
378
    VRTDataset::QueueWorkingStates *poQueueWorkingStates = nullptr;
379
    CPLErrorAccumulator *poErrorAccumulator = nullptr;
380

381
    GDALDataType eVRTBandDataType = GDT_Unknown;
382
    int nXOff = 0;
383
    int nYOff = 0;
384
    int nXSize = 0;
385
    int nYSize = 0;
386
    void *pData = nullptr;
387
    int nBufXSize = 0;
388
    int nBufYSize = 0;
389
    GDALDataType eBufType = GDT_Unknown;
390
    GSpacing nPixelSpace = 0;
391
    GSpacing nLineSpace = 0;
392
    GDALRasterIOExtraArg *psExtraArg = nullptr;
393
    VRTSimpleSource *poSource = nullptr;
394

395
    static void Func(void *pData);
396
};
397

398
/************************************************************************/
399
/*                 VRTSourcedRasterBandRasterIOJob::Func()              */
400
/************************************************************************/
401

402
void VRTSourcedRasterBandRasterIOJob::Func(void *pData)
98✔
403
{
404
    auto psJob = std::unique_ptr<VRTSourcedRasterBandRasterIOJob>(
405
        static_cast<VRTSourcedRasterBandRasterIOJob *>(pData));
197✔
406
    if (*psJob->pbSuccess)
99✔
407
    {
408
        GDALRasterIOExtraArg sArg = *(psJob->psExtraArg);
99✔
409
        sArg.pfnProgress = nullptr;
99✔
410
        sArg.pProgressData = nullptr;
99✔
411

412
        std::unique_ptr<VRTSource::WorkingState> poWorkingState;
99✔
413
        {
414
            std::lock_guard oLock(psJob->poQueueWorkingStates->oMutex);
198✔
415
            poWorkingState =
416
                std::move(psJob->poQueueWorkingStates->oStates.back());
99✔
417
            psJob->poQueueWorkingStates->oStates.pop_back();
99✔
418
            CPLAssert(poWorkingState.get());
99✔
419
        }
420

421
        auto oAccumulator = psJob->poErrorAccumulator->InstallForCurrentScope();
198✔
422
        CPL_IGNORE_RET_VAL(oAccumulator);
99✔
423

424
        if (psJob->poSource->RasterIO(
198✔
425
                psJob->eVRTBandDataType, psJob->nXOff, psJob->nYOff,
99✔
426
                psJob->nXSize, psJob->nYSize, psJob->pData, psJob->nBufXSize,
99✔
427
                psJob->nBufYSize, psJob->eBufType, psJob->nPixelSpace,
99✔
428
                psJob->nLineSpace, &sArg, *(poWorkingState.get())) != CE_None)
198✔
429
        {
430
            *psJob->pbSuccess = false;
×
431
        }
432

433
        {
434
            std::lock_guard oLock(psJob->poQueueWorkingStates->oMutex);
198✔
435
            psJob->poQueueWorkingStates->oStates.push_back(
198✔
436
                std::move(poWorkingState));
99✔
437
        }
438
    }
439

440
    ++(*psJob->pnCompletedJobs);
99✔
441
}
99✔
442

443
/************************************************************************/
444
/*                             IRasterIO()                              */
445
/************************************************************************/
446

447
CPLErr VRTSourcedRasterBand::IRasterIO(
258,115✔
448
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
449
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
450
    GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
451

452
{
453
    if (eRWFlag == GF_Write)
258,115✔
454
    {
455
        CPLError(CE_Failure, CPLE_AppDefined,
×
456
                 "Writing through VRTSourcedRasterBand is not supported.");
457
        return CE_Failure;
×
458
    }
459

460
    const std::string osFctId("VRTSourcedRasterBand::IRasterIO");
516,230✔
461
    GDALAntiRecursionGuard oGuard(osFctId);
516,230✔
462
    if (oGuard.GetCallDepth() >= 32)
258,115✔
463
    {
464
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
×
465
        return CE_Failure;
×
466
    }
467

468
    GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
774,345✔
469
    // Allow 2 recursion depths on the same dataset for non-nearest resampling
470
    if (oGuard2.GetCallDepth() > 2)
258,115✔
471
    {
472
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1✔
473
        return CE_Failure;
1✔
474
    }
475

476
    /* ==================================================================== */
477
    /*      Do we have overviews that would be appropriate to satisfy       */
478
    /*      this request?                                                   */
479
    /* ==================================================================== */
480
    auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
258,114✔
481
    if (l_poDS &&
258,114✔
482
        l_poDS->m_apoOverviews.empty() &&  // do not use virtual overviews
516,143✔
483
        (nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0)
516,228✔
484
    {
485
        if (OverviewRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
2✔
486
                             nBufXSize, nBufYSize, eBufType, nPixelSpace,
487
                             nLineSpace, psExtraArg) == CE_None)
2✔
488
            return CE_None;
1✔
489
    }
490

491
    // If resampling with non-nearest neighbour, we need to be careful
492
    // if the VRT band exposes a nodata value, but the sources do not have it.
493
    // To also avoid edge effects on sources when downsampling, use the
494
    // base implementation of IRasterIO() (that is acquiring sources at their
495
    // nominal resolution, and then downsampling), but only if none of the
496
    // contributing sources have overviews.
497
    if (l_poDS && !CanIRasterIOBeForwardedToEachSource(
258,113✔
498
                      eRWFlag, nXOff, nYOff, nXSize, nYSize, nBufXSize,
499
                      nBufYSize, psExtraArg))
500
    {
501
        const bool bBackupEnabledOverviews = l_poDS->AreOverviewsEnabled();
14✔
502
        if (!l_poDS->m_apoOverviews.empty() && l_poDS->AreOverviewsEnabled())
14✔
503
        {
504
            // Disable use of implicit overviews to avoid infinite
505
            // recursion
506
            l_poDS->SetEnableOverviews(false);
1✔
507
        }
508

509
        const auto eResampleAlgBackup = psExtraArg->eResampleAlg;
14✔
510
        if (psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
14✔
511
        {
512
            std::string osResampling;
16✔
513
            for (int i = 0; i < nSources; i++)
24✔
514
            {
515
                if (papoSources[i]->GetType() ==
16✔
516
                    VRTComplexSource::GetTypeStatic())
16✔
517
                {
518
                    auto *const poComplexSource =
16✔
519
                        static_cast<VRTComplexSource *>(papoSources[i]);
16✔
520
                    if (!poComplexSource->GetResampling().empty())
16✔
521
                    {
522
                        if (i == 0)
16✔
523
                            osResampling = poComplexSource->GetResampling();
8✔
524
                        else if (osResampling !=
8✔
525
                                 poComplexSource->GetResampling())
8✔
526
                        {
527
                            osResampling.clear();
×
528
                            break;
×
529
                        }
530
                    }
531
                }
532
            }
533
            if (!osResampling.empty())
8✔
534
                psExtraArg->eResampleAlg =
8✔
535
                    GDALRasterIOGetResampleAlg(osResampling.c_str());
8✔
536
        }
537

538
        const auto eErr = GDALRasterBand::IRasterIO(
14✔
539
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
540
            eBufType, nPixelSpace, nLineSpace, psExtraArg);
541

542
        psExtraArg->eResampleAlg = eResampleAlgBackup;
14✔
543
        l_poDS->SetEnableOverviews(bBackupEnabledOverviews);
14✔
544
        return eErr;
14✔
545
    }
546

547
    /* -------------------------------------------------------------------- */
548
    /*      Initialize the buffer to some background value. Use the         */
549
    /*      nodata value if available.                                      */
550
    /* -------------------------------------------------------------------- */
551
    if (SkipBufferInitialization())
258,099✔
552
    {
553
        // Do nothing
554
    }
555
    else if (nPixelSpace == GDALGetDataTypeSizeBytes(eBufType) &&
26,513✔
556
             !(m_bNoDataValueSet && m_dfNoDataValue != 0.0) &&
18,102✔
557
             !(m_bNoDataSetAsInt64 && m_nNoDataValueInt64 != 0) &&
62,548✔
558
             !(m_bNoDataSetAsUInt64 && m_nNoDataValueUInt64 != 0))
17,933✔
559
    {
560
        if (nLineSpace == nBufXSize * nPixelSpace)
17,932✔
561
        {
562
            memset(pData, 0, static_cast<size_t>(nBufYSize * nLineSpace));
17,722✔
563
        }
564
        else
565
        {
566
            for (int iLine = 0; iLine < nBufYSize; iLine++)
28,188✔
567
            {
568
                memset(static_cast<GByte *>(pData) +
27,978✔
569
                           static_cast<GIntBig>(iLine) * nLineSpace,
27,978✔
570
                       0, static_cast<size_t>(nBufXSize * nPixelSpace));
27,978✔
571
            }
572
        }
573
    }
574
    else if (m_bNoDataSetAsInt64)
8,581✔
575
    {
576
        for (int iLine = 0; iLine < nBufYSize; iLine++)
7✔
577
        {
578
            GDALCopyWords(&m_nNoDataValueInt64, GDT_Int64, 0,
6✔
579
                          static_cast<GByte *>(pData) +
580
                              static_cast<GIntBig>(nLineSpace) * iLine,
6✔
581
                          eBufType, static_cast<int>(nPixelSpace), nBufXSize);
582
        }
583
    }
584
    else if (m_bNoDataSetAsUInt64)
8,580✔
585
    {
586
        for (int iLine = 0; iLine < nBufYSize; iLine++)
7✔
587
        {
588
            GDALCopyWords(&m_nNoDataValueUInt64, GDT_UInt64, 0,
6✔
589
                          static_cast<GByte *>(pData) +
590
                              static_cast<GIntBig>(nLineSpace) * iLine,
6✔
591
                          eBufType, static_cast<int>(nPixelSpace), nBufXSize);
592
        }
593
    }
594
    else
595
    {
596
        double dfWriteValue = 0.0;
8,579✔
597
        if (m_bNoDataValueSet)
8,579✔
598
            dfWriteValue = m_dfNoDataValue;
180✔
599

600
        for (int iLine = 0; iLine < nBufYSize; iLine++)
58,958✔
601
        {
602
            GDALCopyWords(&dfWriteValue, GDT_Float64, 0,
50,379✔
603
                          static_cast<GByte *>(pData) +
604
                              static_cast<GIntBig>(nLineSpace) * iLine,
50,379✔
605
                          eBufType, static_cast<int>(nPixelSpace), nBufXSize);
606
        }
607
    }
608

609
    /* -------------------------------------------------------------------- */
610
    /*      Overlay each source in turn over top this.                      */
611
    /* -------------------------------------------------------------------- */
612
    CPLErr eErr = CE_None;
258,099✔
613

614
    double dfXOff = nXOff;
258,099✔
615
    double dfYOff = nYOff;
258,099✔
616
    double dfXSize = nXSize;
258,099✔
617
    double dfYSize = nYSize;
258,099✔
618
    if (psExtraArg->bFloatingPointWindowValidity)
258,099✔
619
    {
620
        dfXOff = psExtraArg->dfXOff;
260✔
621
        dfYOff = psExtraArg->dfYOff;
260✔
622
        dfXSize = psExtraArg->dfXSize;
260✔
623
        dfYSize = psExtraArg->dfYSize;
260✔
624
    }
625

626
    if (l_poDS)
258,099✔
627
        l_poDS->m_bMultiThreadedRasterIOLastUsed = false;
258,099✔
628

629
    int nContributingSources = 0;
258,099✔
630
    int nMaxThreads = 0;
258,099✔
631
    constexpr int MINIMUM_PIXEL_COUNT_FOR_THREADED_IO = 1000 * 1000;
258,099✔
632
    if (l_poDS &&
516,198✔
633
        (static_cast<int64_t>(nBufXSize) * nBufYSize >=
258,099✔
634
             MINIMUM_PIXEL_COUNT_FOR_THREADED_IO ||
257,737✔
635
         static_cast<int64_t>(nXSize) * nYSize >=
257,737✔
636
             MINIMUM_PIXEL_COUNT_FOR_THREADED_IO) &&
369✔
637
        CanMultiThreadRasterIO(dfXOff, dfYOff, dfXSize, dfYSize,
369✔
638
                               nContributingSources) &&
368✔
639
        nContributingSources > 1 &&
516,218✔
640
        (nMaxThreads = VRTDataset::GetNumThreads(l_poDS)) > 1)
20✔
641
    {
642
        l_poDS->m_bMultiThreadedRasterIOLastUsed = true;
18✔
643
        l_poDS->m_oMapSharedSources.InitMutex();
18✔
644

645
        CPLErrorAccumulator errorAccumulator;
36✔
646
        std::atomic<bool> bSuccess = true;
18✔
647
        CPLWorkerThreadPool *psThreadPool = GDALGetGlobalThreadPool(
18✔
648
            std::min(nContributingSources, nMaxThreads));
18✔
649
        const int nThreads =
650
            std::min(nContributingSources, psThreadPool->GetThreadCount());
18✔
651
        CPLDebugOnly("VRT",
18✔
652
                     "IRasterIO(): use optimized "
653
                     "multi-threaded code path for mosaic. "
654
                     "Using %d threads",
655
                     nThreads);
656

657
        {
658
            std::lock_guard oLock(l_poDS->m_oQueueWorkingStates.oMutex);
36✔
659
            if (l_poDS->m_oQueueWorkingStates.oStates.size() <
18✔
660
                static_cast<size_t>(nThreads))
18✔
661
            {
662
                l_poDS->m_oQueueWorkingStates.oStates.resize(nThreads);
6✔
663
            }
664
            for (int i = 0; i < nThreads; ++i)
117✔
665
            {
666
                if (!l_poDS->m_oQueueWorkingStates.oStates[i])
99✔
667
                    l_poDS->m_oQueueWorkingStates.oStates[i] =
75✔
668
                        std::make_unique<VRTSource::WorkingState>();
150✔
669
            }
670
        }
671

672
        auto oQueue = psThreadPool->CreateJobQueue();
18✔
673
        std::atomic<int> nCompletedJobs = 0;
18✔
674
        for (int iSource = 0; iSource < nSources; iSource++)
180✔
675
        {
676
            auto poSource = papoSources[iSource];
162✔
677
            if (!poSource->IsSimpleSource())
162✔
678
                continue;
×
679
            auto poSimpleSource = cpl::down_cast<VRTSimpleSource *>(poSource);
162✔
680
            if (poSimpleSource->DstWindowIntersects(dfXOff, dfYOff, dfXSize,
162✔
681
                                                    dfYSize))
682
            {
683
                auto psJob = new VRTSourcedRasterBandRasterIOJob();
99✔
684
                psJob->pbSuccess = &bSuccess;
99✔
685
                psJob->pnCompletedJobs = &nCompletedJobs;
99✔
686
                psJob->poQueueWorkingStates = &(l_poDS->m_oQueueWorkingStates);
99✔
687
                psJob->poErrorAccumulator = &errorAccumulator;
99✔
688
                psJob->eVRTBandDataType = eDataType;
99✔
689
                psJob->nXOff = nXOff;
99✔
690
                psJob->nYOff = nYOff;
99✔
691
                psJob->nXSize = nXSize;
99✔
692
                psJob->nYSize = nYSize;
99✔
693
                psJob->pData = pData;
99✔
694
                psJob->nBufXSize = nBufXSize;
99✔
695
                psJob->nBufYSize = nBufYSize;
99✔
696
                psJob->eBufType = eBufType;
99✔
697
                psJob->nPixelSpace = nPixelSpace;
99✔
698
                psJob->nLineSpace = nLineSpace;
99✔
699
                psJob->psExtraArg = psExtraArg;
99✔
700
                psJob->poSource = poSimpleSource;
99✔
701

702
                if (!oQueue->SubmitJob(VRTSourcedRasterBandRasterIOJob::Func,
99✔
703
                                       psJob))
704
                {
705
                    delete psJob;
×
706
                    bSuccess = false;
×
707
                    break;
×
708
                }
709
            }
710
        }
711

712
        while (oQueue->WaitEvent())
83✔
713
        {
714
            // Quite rough progress callback. We could do better by counting
715
            // the number of contributing pixels.
716
            if (psExtraArg->pfnProgress)
65✔
717
            {
718
                psExtraArg->pfnProgress(double(nCompletedJobs.load()) /
108✔
719
                                            nContributingSources,
720
                                        "", psExtraArg->pProgressData);
721
            }
722
        }
723

724
        errorAccumulator.ReplayErrors();
18✔
725
        eErr = bSuccess ? CE_None : CE_Failure;
18✔
726
    }
727
    else
728
    {
729
        GDALProgressFunc const pfnProgressGlobal = psExtraArg->pfnProgress;
258,081✔
730
        void *const pProgressDataGlobal = psExtraArg->pProgressData;
258,081✔
731

732
        VRTSource::WorkingState oWorkingState;
258,081✔
733
        for (int iSource = 0; eErr == CE_None && iSource < nSources; iSource++)
533,439✔
734
        {
735
            psExtraArg->pfnProgress = GDALScaledProgress;
275,358✔
736
            psExtraArg->pProgressData = GDALCreateScaledProgress(
550,716✔
737
                1.0 * iSource / nSources, 1.0 * (iSource + 1) / nSources,
275,358✔
738
                pfnProgressGlobal, pProgressDataGlobal);
739
            if (psExtraArg->pProgressData == nullptr)
275,358✔
740
                psExtraArg->pfnProgress = nullptr;
272,261✔
741

742
            eErr = papoSources[iSource]->RasterIO(
275,358✔
743
                eDataType, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
744
                nBufYSize, eBufType, nPixelSpace, nLineSpace, psExtraArg,
745
                l_poDS ? l_poDS->m_oWorkingState : oWorkingState);
275,358✔
746

747
            GDALDestroyScaledProgress(psExtraArg->pProgressData);
275,358✔
748
        }
749

750
        psExtraArg->pfnProgress = pfnProgressGlobal;
258,081✔
751
        psExtraArg->pProgressData = pProgressDataGlobal;
258,081✔
752
    }
753

754
    if (eErr == CE_None && psExtraArg->pfnProgress)
258,099✔
755
    {
756
        psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
2,964✔
757
    }
758

759
    return eErr;
258,099✔
760
}
761

762
/************************************************************************/
763
/*                         IGetDataCoverageStatus()                     */
764
/************************************************************************/
765

766
int VRTSourcedRasterBand::IGetDataCoverageStatus(int nXOff, int nYOff,
2,972✔
767
                                                 int nXSize, int nYSize,
768
                                                 int nMaskFlagStop,
769
                                                 double *pdfDataPct)
770
{
771
    if (pdfDataPct)
2,972✔
772
        *pdfDataPct = -1.0;
8✔
773

774
    // Particular case for a single simple source covering the whole dataset
775
    if (nSources == 1 && papoSources[0]->IsSimpleSource() &&
5,929✔
776
        papoSources[0]->GetType() == VRTSimpleSource::GetTypeStatic())
2,957✔
777
    {
778
        VRTSimpleSource *poSource =
2,931✔
779
            static_cast<VRTSimpleSource *>(papoSources[0]);
2,931✔
780

781
        GDALRasterBand *poBand = poSource->GetRasterBand();
2,931✔
782
        if (!poBand)
2,931✔
783
            poBand = poSource->GetMaskBandMainBand();
×
784
        if (!poBand)
2,931✔
785
        {
786
            return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
787
                   GDAL_DATA_COVERAGE_STATUS_DATA;
336✔
788
        }
789

790
        /* Check that it uses the full source dataset */
791
        double dfReqXOff = 0.0;
2,931✔
792
        double dfReqYOff = 0.0;
2,931✔
793
        double dfReqXSize = 0.0;
2,931✔
794
        double dfReqYSize = 0.0;
2,931✔
795
        int nReqXOff = 0;
2,931✔
796
        int nReqYOff = 0;
2,931✔
797
        int nReqXSize = 0;
2,931✔
798
        int nReqYSize = 0;
2,931✔
799
        int nOutXOff = 0;
2,931✔
800
        int nOutYOff = 0;
2,931✔
801
        int nOutXSize = 0;
2,931✔
802
        int nOutYSize = 0;
2,931✔
803
        bool bError = false;
2,931✔
804
        if (poSource->GetSrcDstWindow(
2,931✔
805
                0, 0, GetXSize(), GetYSize(), GetXSize(), GetYSize(),
2,931✔
806
                &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, &nReqXOff,
807
                &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
808
                &nOutXSize, &nOutYSize, bError) &&
2,931✔
809
            nReqXOff == 0 && nReqYOff == 0 && nReqXSize == GetXSize() &&
2,931✔
810
            nReqXSize == poBand->GetXSize() && nReqYSize == GetYSize() &&
445✔
811
            nReqYSize == poBand->GetYSize() && nOutXOff == 0 && nOutYOff == 0 &&
345✔
812
            nOutXSize == GetXSize() && nOutYSize == GetYSize())
5,862✔
813
        {
814
            return poBand->GetDataCoverageStatus(nXOff, nYOff, nXSize, nYSize,
336✔
815
                                                 nMaskFlagStop, pdfDataPct);
336✔
816
        }
817
    }
818

819
#ifndef HAVE_GEOS
820
    return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
821
           GDAL_DATA_COVERAGE_STATUS_DATA;
822
#else
823
    int nStatus = 0;
2,636✔
824

825
    auto poPolyNonCoveredBySources = std::make_unique<OGRPolygon>();
5,272✔
826
    {
827
        auto poLR = std::make_unique<OGRLinearRing>();
5,272✔
828
        poLR->addPoint(nXOff, nYOff);
2,636✔
829
        poLR->addPoint(nXOff, nYOff + nYSize);
2,636✔
830
        poLR->addPoint(nXOff + nXSize, nYOff + nYSize);
2,636✔
831
        poLR->addPoint(nXOff + nXSize, nYOff);
2,636✔
832
        poLR->addPoint(nXOff, nYOff);
2,636✔
833
        poPolyNonCoveredBySources->addRingDirectly(poLR.release());
2,636✔
834
    }
835

836
    for (int iSource = 0; iSource < nSources; iSource++)
2,646✔
837
    {
838
        if (!papoSources[iSource]->IsSimpleSource())
2,641✔
839
        {
840
            return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
841
                   GDAL_DATA_COVERAGE_STATUS_DATA;
4✔
842
        }
843
        VRTSimpleSource *poSS =
2,637✔
844
            static_cast<VRTSimpleSource *>(papoSources[iSource]);
2,637✔
845
        // Check if the AOI is fully inside the source
846
        double dfDstXOff = std::max(0.0, poSS->m_dfDstXOff);
2,637✔
847
        double dfDstYOff = std::max(0.0, poSS->m_dfDstYOff);
2,637✔
848
        double dfDstXSize = poSS->m_dfDstXSize;
2,637✔
849
        double dfDstYSize = poSS->m_dfDstYSize;
2,637✔
850
        auto l_poBand = poSS->GetRasterBand();
2,637✔
851
        if (!l_poBand)
2,637✔
852
            continue;
×
853
        if (dfDstXSize == -1)
2,637✔
854
            dfDstXSize = l_poBand->GetXSize() - dfDstXOff;
8✔
855
        if (dfDstYSize == -1)
2,637✔
856
            dfDstYSize = l_poBand->GetYSize() - dfDstYOff;
8✔
857

858
        if (nXOff >= dfDstXOff && nYOff >= dfDstYOff &&
2,637✔
859
            nXOff + nXSize <= dfDstXOff + dfDstXSize &&
2,628✔
860
            nYOff + nYSize <= dfDstYOff + dfDstYSize)
2,604✔
861
        {
862
            if (pdfDataPct)
2,604✔
863
                *pdfDataPct = 100.0;
1✔
864
            return GDAL_DATA_COVERAGE_STATUS_DATA;
2,604✔
865
        }
866
        // Check intersection of bounding boxes.
867
        if (dfDstXOff + dfDstXSize > nXOff && dfDstYOff + dfDstYSize > nYOff &&
33✔
868
            dfDstXOff < nXOff + nXSize && dfDstYOff < nYOff + nYSize)
27✔
869
        {
870
            nStatus |= GDAL_DATA_COVERAGE_STATUS_DATA;
25✔
871
            if (poPolyNonCoveredBySources)
25✔
872
            {
873
                OGRPolygon oPolySource;
25✔
874
                auto poLR = std::make_unique<OGRLinearRing>();
25✔
875
                poLR->addPoint(dfDstXOff, dfDstYOff);
25✔
876
                poLR->addPoint(dfDstXOff, dfDstYOff + dfDstYSize);
25✔
877
                poLR->addPoint(dfDstXOff + dfDstXSize, dfDstYOff + dfDstYSize);
25✔
878
                poLR->addPoint(dfDstXOff + dfDstXSize, dfDstYOff);
25✔
879
                poLR->addPoint(dfDstXOff, dfDstYOff);
25✔
880
                oPolySource.addRingDirectly(poLR.release());
25✔
881
                auto poRes = std::unique_ptr<OGRGeometry>(
882
                    poPolyNonCoveredBySources->Difference(&oPolySource));
25✔
883
                if (poRes && poRes->IsEmpty())
25✔
884
                {
885
                    if (pdfDataPct)
1✔
886
                        *pdfDataPct = 100.0;
1✔
887
                    return GDAL_DATA_COVERAGE_STATUS_DATA;
1✔
888
                }
889
                else if (poRes && poRes->getGeometryType() == wkbPolygon)
24✔
890
                {
891
                    poPolyNonCoveredBySources.reset(
24✔
892
                        poRes.release()->toPolygon());
893
                }
894
                else
895
                {
896
                    poPolyNonCoveredBySources.reset();
×
897
                }
898
            }
899
        }
900
        if (nMaskFlagStop != 0 && (nStatus & nMaskFlagStop) != 0)
32✔
901
        {
902
            return nStatus;
22✔
903
        }
904
    }
905
    if (poPolyNonCoveredBySources)
5✔
906
    {
907
        if (!poPolyNonCoveredBySources->IsEmpty())
5✔
908
            nStatus |= GDAL_DATA_COVERAGE_STATUS_EMPTY;
5✔
909
        if (pdfDataPct)
5✔
910
            *pdfDataPct = 100.0 * (1.0 - poPolyNonCoveredBySources->get_Area() /
2✔
911
                                             nXSize / nYSize);
2✔
912
    }
913
    return nStatus;
5✔
914
#endif  // HAVE_GEOS
915
}
916

917
/************************************************************************/
918
/*                             IReadBlock()                             */
919
/************************************************************************/
920

921
CPLErr VRTSourcedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
680✔
922
                                        void *pImage)
923

924
{
925
    const int nPixelSize = GDALGetDataTypeSizeBytes(eDataType);
680✔
926

927
    int nReadXSize = 0;
680✔
928
    if ((nBlockXOff + 1) * nBlockXSize > GetXSize())
680✔
929
        nReadXSize = GetXSize() - nBlockXOff * nBlockXSize;
16✔
930
    else
931
        nReadXSize = nBlockXSize;
664✔
932

933
    int nReadYSize = 0;
680✔
934
    if ((nBlockYOff + 1) * nBlockYSize > GetYSize())
680✔
935
        nReadYSize = GetYSize() - nBlockYOff * nBlockYSize;
×
936
    else
937
        nReadYSize = nBlockYSize;
680✔
938

939
    GDALRasterIOExtraArg sExtraArg;
940
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
680✔
941

942
    return IRasterIO(
680✔
943
        GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, nReadXSize,
680✔
944
        nReadYSize, pImage, nReadXSize, nReadYSize, eDataType, nPixelSize,
945
        static_cast<GSpacing>(nPixelSize) * nBlockXSize, &sExtraArg);
1,360✔
946
}
947

948
/************************************************************************/
949
/*                        CPLGettimeofday()                             */
950
/************************************************************************/
951

952
#if defined(_WIN32) && !defined(__CYGWIN__)
953
#include <sys/timeb.h>
954

955
namespace
956
{
957
struct CPLTimeVal
958
{
959
    time_t tv_sec; /* seconds */
960
    long tv_usec;  /* and microseconds */
961
};
962
}  // namespace
963

964
static int CPLGettimeofday(struct CPLTimeVal *tp, void * /* timezonep*/)
965
{
966
    struct _timeb theTime;
967

968
    _ftime(&theTime);
969
    tp->tv_sec = static_cast<time_t>(theTime.time);
970
    tp->tv_usec = theTime.millitm * 1000;
971
    return 0;
972
}
973
#else
974
#include <sys/time.h> /* for gettimeofday() */
975
#define CPLTimeVal timeval
976
#define CPLGettimeofday(t, u) gettimeofday(t, u)
977
#endif
978

979
/************************************************************************/
980
/*                    CanUseSourcesMinMaxImplementations()              */
981
/************************************************************************/
982

983
bool VRTSourcedRasterBand::CanUseSourcesMinMaxImplementations()
107✔
984
{
985
    const char *pszUseSources =
986
        CPLGetConfigOption("VRT_MIN_MAX_FROM_SOURCES", nullptr);
107✔
987
    if (pszUseSources)
107✔
988
        return CPLTestBool(pszUseSources);
×
989

990
    // Use heuristics to determine if we are going to use the source
991
    // GetMinimum() or GetMaximum() implementation: all the sources must be
992
    // "simple" sources with a dataset description that match a "regular" file
993
    // on the filesystem, whose open time and GetMinimum()/GetMaximum()
994
    // implementations we hope to be fast enough.
995
    // In case of doubt return FALSE.
996
    struct CPLTimeVal tvStart;
997
    memset(&tvStart, 0, sizeof(CPLTimeVal));
107✔
998
    if (nSources > 1)
107✔
999
        CPLGettimeofday(&tvStart, nullptr);
10✔
1000
    for (int iSource = 0; iSource < nSources; iSource++)
235✔
1001
    {
1002
        if (!(papoSources[iSource]->IsSimpleSource()))
131✔
1003
            return false;
2✔
1004
        VRTSimpleSource *const poSimpleSource =
129✔
1005
            static_cast<VRTSimpleSource *>(papoSources[iSource]);
129✔
1006
        const char *pszFilename = poSimpleSource->m_osSrcDSName.c_str();
129✔
1007
        // /vsimem/ should be fast.
1008
        if (STARTS_WITH(pszFilename, "/vsimem/"))
129✔
1009
            continue;
10✔
1010
        // but not other /vsi filesystems
1011
        if (STARTS_WITH(pszFilename, "/vsi"))
119✔
1012
            return false;
×
1013
        char ch = '\0';
119✔
1014
        // We will assume that filenames that are only with ascii characters
1015
        // are real filenames and so we will not try to 'stat' them.
1016
        for (int i = 0; (ch = pszFilename[i]) != '\0'; i++)
1,815✔
1017
        {
1018
            if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
1,718✔
1019
                  (ch >= '0' && ch <= '9') || ch == ':' || ch == '/' ||
409✔
1020
                  ch == '\\' || ch == ' ' || ch == '.' || ch == '_'))
143✔
1021
                break;
5✔
1022
        }
1023
        if (ch != '\0')
119✔
1024
        {
1025
            // Otherwise do a real filesystem check.
1026
            VSIStatBuf sStat;
1027
            if (VSIStat(pszFilename, &sStat) != 0)
5✔
1028
                return false;
1✔
1029
            if (nSources > 1)
4✔
1030
            {
1031
                struct CPLTimeVal tvCur;
1032
                CPLGettimeofday(&tvCur, nullptr);
4✔
1033
                if (tvCur.tv_sec - tvStart.tv_sec +
4✔
1034
                        (tvCur.tv_usec - tvStart.tv_usec) * 1e-6 >
4✔
1035
                    1)
1036
                    return false;
×
1037
            }
1038
        }
1039
    }
1040
    return true;
104✔
1041
}
1042

1043
/************************************************************************/
1044
/*                             GetMinimum()                             */
1045
/************************************************************************/
1046

1047
double VRTSourcedRasterBand::GetMinimum(int *pbSuccess)
59✔
1048
{
1049
    const char *const pszValue = GetMetadataItem("STATISTICS_MINIMUM");
59✔
1050
    if (pszValue != nullptr)
59✔
1051
    {
1052
        if (pbSuccess != nullptr)
5✔
1053
            *pbSuccess = TRUE;
5✔
1054

1055
        return CPLAtofM(pszValue);
5✔
1056
    }
1057

1058
    if (!CanUseSourcesMinMaxImplementations())
54✔
1059
        return GDALRasterBand::GetMinimum(pbSuccess);
2✔
1060

1061
    const std::string osFctId("VRTSourcedRasterBand::GetMinimum");
104✔
1062
    GDALAntiRecursionGuard oGuard(osFctId);
104✔
1063
    if (oGuard.GetCallDepth() >= 32)
52✔
1064
    {
1065
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
×
1066
        if (pbSuccess != nullptr)
×
1067
            *pbSuccess = FALSE;
×
1068
        return 0;
×
1069
    }
1070

1071
    GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
156✔
1072
    if (oGuard2.GetCallDepth() >= 2)
52✔
1073
    {
1074
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
×
1075
        if (pbSuccess != nullptr)
×
1076
            *pbSuccess = FALSE;
×
1077
        return 0;
×
1078
    }
1079

1080
    struct CPLTimeVal tvStart;
1081
    memset(&tvStart, 0, sizeof(CPLTimeVal));
52✔
1082
    if (nSources > 1)
52✔
1083
        CPLGettimeofday(&tvStart, nullptr);
5✔
1084
    double dfMin = 0;
52✔
1085
    for (int iSource = 0; iSource < nSources; iSource++)
57✔
1086
    {
1087
        int bSuccess = FALSE;
53✔
1088
        double dfSourceMin =
1089
            papoSources[iSource]->GetMinimum(GetXSize(), GetYSize(), &bSuccess);
53✔
1090
        if (!bSuccess)
53✔
1091
        {
1092
            dfMin = GDALRasterBand::GetMinimum(pbSuccess);
48✔
1093
            return dfMin;
48✔
1094
        }
1095

1096
        if (iSource == 0 || dfSourceMin < dfMin)
5✔
1097
        {
1098
            dfMin = dfSourceMin;
4✔
1099
            if (dfMin == 0 && eDataType == GDT_Byte)
4✔
1100
                break;
×
1101
        }
1102
        if (nSources > 1)
5✔
1103
        {
1104
            struct CPLTimeVal tvCur;
1105
            CPLGettimeofday(&tvCur, nullptr);
2✔
1106
            if (tvCur.tv_sec - tvStart.tv_sec +
2✔
1107
                    (tvCur.tv_usec - tvStart.tv_usec) * 1e-6 >
2✔
1108
                1)
1109
            {
1110
                return GDALRasterBand::GetMinimum(pbSuccess);
×
1111
            }
1112
        }
1113
    }
1114

1115
    if (pbSuccess != nullptr)
4✔
1116
        *pbSuccess = TRUE;
4✔
1117

1118
    return dfMin;
4✔
1119
}
1120

1121
/************************************************************************/
1122
/*                             GetMaximum()                             */
1123
/************************************************************************/
1124

1125
double VRTSourcedRasterBand::GetMaximum(int *pbSuccess)
57✔
1126
{
1127
    const char *const pszValue = GetMetadataItem("STATISTICS_MAXIMUM");
57✔
1128
    if (pszValue != nullptr)
57✔
1129
    {
1130
        if (pbSuccess != nullptr)
4✔
1131
            *pbSuccess = TRUE;
4✔
1132

1133
        return CPLAtofM(pszValue);
4✔
1134
    }
1135

1136
    if (!CanUseSourcesMinMaxImplementations())
53✔
1137
        return GDALRasterBand::GetMaximum(pbSuccess);
1✔
1138

1139
    const std::string osFctId("VRTSourcedRasterBand::GetMaximum");
104✔
1140
    GDALAntiRecursionGuard oGuard(osFctId);
104✔
1141
    if (oGuard.GetCallDepth() >= 32)
52✔
1142
    {
1143
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
×
1144
        if (pbSuccess != nullptr)
×
1145
            *pbSuccess = FALSE;
×
1146
        return 0;
×
1147
    }
1148

1149
    GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
156✔
1150
    if (oGuard2.GetCallDepth() >= 2)
52✔
1151
    {
1152
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
×
1153
        if (pbSuccess != nullptr)
×
1154
            *pbSuccess = FALSE;
×
1155
        return 0;
×
1156
    }
1157

1158
    struct CPLTimeVal tvStart;
1159
    memset(&tvStart, 0, sizeof(CPLTimeVal));
52✔
1160
    if (nSources > 1)
52✔
1161
        CPLGettimeofday(&tvStart, nullptr);
5✔
1162
    double dfMax = 0;
52✔
1163
    for (int iSource = 0; iSource < nSources; iSource++)
54✔
1164
    {
1165
        int bSuccess = FALSE;
52✔
1166
        const double dfSourceMax =
1167
            papoSources[iSource]->GetMaximum(GetXSize(), GetYSize(), &bSuccess);
52✔
1168
        if (!bSuccess)
52✔
1169
        {
1170
            dfMax = GDALRasterBand::GetMaximum(pbSuccess);
48✔
1171
            return dfMax;
48✔
1172
        }
1173

1174
        if (iSource == 0 || dfSourceMax > dfMax)
4✔
1175
        {
1176
            dfMax = dfSourceMax;
4✔
1177
            if (dfMax == 255.0 && eDataType == GDT_Byte)
4✔
1178
                break;
2✔
1179
        }
1180
        if (nSources > 1)
2✔
1181
        {
1182
            struct CPLTimeVal tvCur;
1183
            CPLGettimeofday(&tvCur, nullptr);
×
1184
            if (tvCur.tv_sec - tvStart.tv_sec +
×
1185
                    (tvCur.tv_usec - tvStart.tv_usec) * 1e-6 >
×
1186
                1)
1187
            {
1188
                return GDALRasterBand::GetMaximum(pbSuccess);
×
1189
            }
1190
        }
1191
    }
1192

1193
    if (pbSuccess != nullptr)
4✔
1194
        *pbSuccess = TRUE;
4✔
1195

1196
    return dfMax;
4✔
1197
}
1198

1199
/************************************************************************/
1200
/* IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange() */
1201
/************************************************************************/
1202

1203
/* Returns true if the VRT raster band consists of non-overlapping simple
1204
 * sources or complex sources that don't change values, and use the full extent
1205
 * of the source band.
1206
 */
1207
bool VRTSourcedRasterBand::
49✔
1208
    IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange(
1209
        bool bAllowMaxValAdjustment) const
1210
{
1211
    bool bRet = true;
49✔
1212
    CPLRectObj sGlobalBounds;
1213
    sGlobalBounds.minx = 0;
49✔
1214
    sGlobalBounds.miny = 0;
49✔
1215
    sGlobalBounds.maxx = nRasterXSize;
49✔
1216
    sGlobalBounds.maxy = nRasterYSize;
49✔
1217
    CPLQuadTree *hQuadTree = CPLQuadTreeCreate(&sGlobalBounds, nullptr);
49✔
1218
    for (int i = 0; i < nSources; ++i)
114✔
1219
    {
1220
        if (!papoSources[i]->IsSimpleSource())
74✔
1221
        {
1222
            bRet = false;
1✔
1223
            break;
9✔
1224
        }
1225

1226
        auto poSimpleSource = cpl::down_cast<VRTSimpleSource *>(papoSources[i]);
73✔
1227
        const char *pszType = poSimpleSource->GetType();
73✔
1228
        if (pszType == VRTSimpleSource::GetTypeStatic())
73✔
1229
        {
1230
            // ok
1231
        }
1232
        else if (pszType == VRTComplexSource::GetTypeStatic())
5✔
1233
        {
1234
            auto poComplexSource =
1235
                cpl::down_cast<VRTComplexSource *>(papoSources[i]);
5✔
1236
            if (!poComplexSource->AreValuesUnchanged())
5✔
1237
            {
1238
                bRet = false;
2✔
1239
                break;
2✔
1240
            }
1241
        }
1242
        else
1243
        {
1244
            bRet = false;
×
1245
            break;
×
1246
        }
1247

1248
        if (!bAllowMaxValAdjustment && poSimpleSource->NeedMaxValAdjustment())
71✔
1249
        {
1250
            bRet = false;
2✔
1251
            break;
2✔
1252
        }
1253

1254
        auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
69✔
1255
        if (poSimpleSourceBand == nullptr ||
138✔
1256
            poSimpleSourceBand->GetRasterDataType() != eDataType)
69✔
1257
        {
1258
            bRet = false;
×
1259
            break;
×
1260
        }
1261

1262
        double dfReqXOff = 0.0;
69✔
1263
        double dfReqYOff = 0.0;
69✔
1264
        double dfReqXSize = 0.0;
69✔
1265
        double dfReqYSize = 0.0;
69✔
1266
        int nReqXOff = 0;
69✔
1267
        int nReqYOff = 0;
69✔
1268
        int nReqXSize = 0;
69✔
1269
        int nReqYSize = 0;
69✔
1270
        int nOutXOff = 0;
69✔
1271
        int nOutYOff = 0;
69✔
1272
        int nOutXSize = 0;
69✔
1273
        int nOutYSize = 0;
69✔
1274

1275
        bool bError = false;
69✔
1276
        if (!poSimpleSource->GetSrcDstWindow(
207✔
1277
                0, 0, nRasterXSize, nRasterYSize, nRasterXSize, nRasterYSize,
69✔
1278
                &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, &nReqXOff,
1279
                &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
1280
                &nOutXSize, &nOutYSize, bError) ||
69✔
1281
            nReqXOff != 0 || nReqYOff != 0 ||
69✔
1282
            nReqXSize != poSimpleSourceBand->GetXSize() ||
68✔
1283
            nReqYSize != poSimpleSourceBand->GetYSize() ||
68✔
1284
            nOutXSize != nReqXSize || nOutYSize != nReqYSize)
138✔
1285
        {
1286
            bRet = false;
1✔
1287
            break;
1✔
1288
        }
1289

1290
        CPLRectObj sBounds;
1291
        constexpr double EPSILON = 1e-1;
68✔
1292
        sBounds.minx = nOutXOff + EPSILON;
68✔
1293
        sBounds.miny = nOutYOff + EPSILON;
68✔
1294
        sBounds.maxx = nOutXOff + nOutXSize - EPSILON;
68✔
1295
        sBounds.maxy = nOutYOff + nOutYSize - EPSILON;
68✔
1296

1297
        // Check that the new source doesn't overlap an existing one.
1298
        if (CPLQuadTreeHasMatch(hQuadTree, &sBounds))
68✔
1299
        {
1300
            bRet = false;
3✔
1301
            break;
3✔
1302
        }
1303

1304
        CPLQuadTreeInsertWithBounds(
65✔
1305
            hQuadTree, reinterpret_cast<void *>(static_cast<uintptr_t>(i)),
65✔
1306
            &sBounds);
1307
    }
1308
    CPLQuadTreeDestroy(hQuadTree);
49✔
1309

1310
    return bRet;
49✔
1311
}
1312

1313
/************************************************************************/
1314
/*                       ComputeRasterMinMax()                          */
1315
/************************************************************************/
1316

1317
CPLErr VRTSourcedRasterBand::ComputeRasterMinMax(int bApproxOK,
22✔
1318
                                                 double *adfMinMax)
1319
{
1320
    /* -------------------------------------------------------------------- */
1321
    /*      Does the driver already know the min/max?                       */
1322
    /* -------------------------------------------------------------------- */
1323
    if (bApproxOK)
22✔
1324
    {
1325
        int bSuccessMin = FALSE;
3✔
1326
        int bSuccessMax = FALSE;
3✔
1327

1328
        const double dfMin = GetMinimum(&bSuccessMin);
3✔
1329
        const double dfMax = GetMaximum(&bSuccessMax);
3✔
1330

1331
        if (bSuccessMin && bSuccessMax)
3✔
1332
        {
1333
            adfMinMax[0] = dfMin;
1✔
1334
            adfMinMax[1] = dfMax;
1✔
1335
            return CE_None;
1✔
1336
        }
1337
    }
1338

1339
    const std::string osFctId("VRTSourcedRasterBand::ComputeRasterMinMax");
42✔
1340
    GDALAntiRecursionGuard oGuard(osFctId);
42✔
1341
    if (oGuard.GetCallDepth() >= 32)
21✔
1342
    {
1343
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
×
1344
        return CE_Failure;
×
1345
    }
1346

1347
    GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
63✔
1348
    if (oGuard2.GetCallDepth() >= 2)
21✔
1349
    {
1350
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
×
1351
        return CE_Failure;
×
1352
    }
1353

1354
    /* -------------------------------------------------------------------- */
1355
    /*      If we have overview bands, use them for min/max.                */
1356
    /* -------------------------------------------------------------------- */
1357
    if (bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews())
21✔
1358
    {
1359
        GDALRasterBand *const poBand =
1360
            GetRasterSampleOverview(GDALSTAT_APPROX_NUMSAMPLES);
×
1361

1362
        if (poBand != nullptr && poBand != this)
×
1363
        {
1364
            auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
×
1365
            if (l_poDS && !l_poDS->m_apoOverviews.empty() &&
×
1366
                dynamic_cast<VRTSourcedRasterBand *>(poBand) != nullptr)
×
1367
            {
1368
                auto apoTmpOverviews = std::move(l_poDS->m_apoOverviews);
×
1369
                l_poDS->m_apoOverviews.clear();
×
1370
                auto eErr = poBand->GDALRasterBand::ComputeRasterMinMax(
×
1371
                    TRUE, adfMinMax);
1372
                l_poDS->m_apoOverviews = std::move(apoTmpOverviews);
×
1373
                return eErr;
×
1374
            }
1375
            else
1376
            {
1377
                return poBand->ComputeRasterMinMax(TRUE, adfMinMax);
×
1378
            }
1379
        }
1380
    }
1381

1382
    if (IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange(
21✔
1383
            /*bAllowMaxValAdjustment = */ true))
1384
    {
1385
        CPLDebugOnly(
18✔
1386
            "VRT", "ComputeRasterMinMax(): use optimized code path for mosaic");
1387

1388
        uint64_t nCoveredArea = 0;
18✔
1389

1390
        // If source bands have nodata value, we can't use source band's
1391
        // ComputeRasterMinMax() as we don't know if there are pixels actually
1392
        // at the nodata value, so use ComputeStatistics() instead that takes
1393
        // into account that aspect.
1394
        bool bUseComputeStatistics = false;
18✔
1395
        for (int i = 0; i < nSources; ++i)
37✔
1396
        {
1397
            auto poSimpleSource =
1398
                cpl::down_cast<VRTSimpleSource *>(papoSources[i]);
23✔
1399
            auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
23✔
1400
            int bHasNoData = FALSE;
23✔
1401
            CPL_IGNORE_RET_VAL(poSimpleSourceBand->GetNoDataValue(&bHasNoData));
23✔
1402
            if (bHasNoData)
23✔
1403
            {
1404
                bUseComputeStatistics = true;
4✔
1405
                break;
4✔
1406
            }
1407
            nCoveredArea +=
19✔
1408
                static_cast<uint64_t>(poSimpleSourceBand->GetXSize()) *
19✔
1409
                poSimpleSourceBand->GetYSize();
19✔
1410
        }
1411

1412
        if (bUseComputeStatistics)
18✔
1413
        {
1414
            CPLErr eErr;
1415
            std::string osLastErrorMsg;
4✔
1416
            {
1417
                CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
8✔
1418
                CPLErrorReset();
4✔
1419
                eErr =
1420
                    ComputeStatistics(bApproxOK, &adfMinMax[0], &adfMinMax[1],
8✔
1421
                                      nullptr, nullptr, nullptr, nullptr);
4✔
1422
                if (eErr == CE_Failure)
4✔
1423
                {
1424
                    osLastErrorMsg = CPLGetLastErrorMsg();
1✔
1425
                }
1426
            }
1427
            if (eErr == CE_Failure)
4✔
1428
            {
1429
                if (strstr(osLastErrorMsg.c_str(), "no valid pixels found") !=
1✔
1430
                    nullptr)
1431
                {
1432
                    ReportError(CE_Failure, CPLE_AppDefined,
1✔
1433
                                "Failed to compute min/max, no valid pixels "
1434
                                "found in sampling.");
1435
                }
1436
                else
1437
                {
1438
                    ReportError(CE_Failure, CPLE_AppDefined, "%s",
×
1439
                                osLastErrorMsg.c_str());
1440
                }
1441
            }
1442
            return eErr;
4✔
1443
        }
1444

1445
        bool bSignedByte = false;
14✔
1446
        if (eDataType == GDT_Byte)
14✔
1447
        {
1448
            EnablePixelTypeSignedByteWarning(false);
8✔
1449
            const char *pszPixelType =
1450
                GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
8✔
1451
            EnablePixelTypeSignedByteWarning(true);
8✔
1452
            bSignedByte =
8✔
1453
                pszPixelType != nullptr && EQUAL(pszPixelType, "SIGNEDBYTE");
8✔
1454
        }
1455

1456
        double dfGlobalMin = std::numeric_limits<double>::max();
14✔
1457
        double dfGlobalMax = -std::numeric_limits<double>::max();
14✔
1458

1459
        // If the mosaic doesn't cover the whole VRT raster, take into account
1460
        // VRT nodata value.
1461
        if (nCoveredArea < static_cast<uint64_t>(nRasterXSize) * nRasterYSize)
14✔
1462
        {
1463
            if (m_bNoDataValueSet && m_bHideNoDataValue)
2✔
1464
            {
1465
                if (IsNoDataValueInDataTypeRange())
1✔
1466
                {
1467
                    dfGlobalMin = std::min(dfGlobalMin, m_dfNoDataValue);
1✔
1468
                    dfGlobalMax = std::max(dfGlobalMax, m_dfNoDataValue);
1✔
1469
                }
1470
            }
1471
            else if (!m_bNoDataValueSet)
1✔
1472
            {
1473
                dfGlobalMin = std::min(dfGlobalMin, 0.0);
×
1474
                dfGlobalMax = std::max(dfGlobalMax, 0.0);
×
1475
            }
1476
        }
1477

1478
        for (int i = 0; i < nSources; ++i)
31✔
1479
        {
1480
            auto poSimpleSource =
1481
                cpl::down_cast<VRTSimpleSource *>(papoSources[i]);
18✔
1482
            double adfMinMaxSource[2] = {0};
18✔
1483

1484
            auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
18✔
1485
            CPLErr eErr = poSimpleSourceBand->ComputeRasterMinMax(
36✔
1486
                bApproxOK, adfMinMaxSource);
18✔
1487
            if (eErr == CE_Failure)
18✔
1488
            {
1489
                return CE_Failure;
1✔
1490
            }
1491
            else
1492
            {
1493
                if (poSimpleSource->NeedMaxValAdjustment())
18✔
1494
                {
1495
                    const double dfMaxValue =
2✔
1496
                        static_cast<double>(poSimpleSource->m_nMaxValue);
2✔
1497
                    adfMinMaxSource[0] =
2✔
1498
                        std::min(adfMinMaxSource[0], dfMaxValue);
2✔
1499
                    adfMinMaxSource[1] =
2✔
1500
                        std::min(adfMinMaxSource[1], dfMaxValue);
2✔
1501
                }
1502

1503
                if (m_bNoDataValueSet && !m_bHideNoDataValue &&
18✔
1504
                    m_dfNoDataValue >= adfMinMaxSource[0] &&
3✔
1505
                    m_dfNoDataValue <= adfMinMaxSource[1])
1✔
1506
                {
1507
                    return GDALRasterBand::ComputeRasterMinMax(bApproxOK,
1✔
1508
                                                               adfMinMax);
1✔
1509
                }
1510

1511
                dfGlobalMin = std::min(dfGlobalMin, adfMinMaxSource[0]);
17✔
1512
                dfGlobalMax = std::max(dfGlobalMax, adfMinMaxSource[1]);
17✔
1513
            }
1514

1515
            // Early exit if we know we reached theoretical bounds
1516
            if (eDataType == GDT_Byte && !bSignedByte && dfGlobalMin == 0.0 &&
17✔
1517
                dfGlobalMax == 255.0)
×
1518
            {
1519
                break;
×
1520
            }
1521
        }
1522

1523
        if (dfGlobalMin > dfGlobalMax)
13✔
1524
        {
1525
            adfMinMax[0] = 0.0;
×
1526
            adfMinMax[1] = 0.0;
×
1527
            ReportError(CE_Failure, CPLE_AppDefined,
×
1528
                        "Failed to compute min/max, no valid pixels found in "
1529
                        "sampling.");
1530
            return CE_Failure;
×
1531
        }
1532

1533
        adfMinMax[0] = dfGlobalMin;
13✔
1534
        adfMinMax[1] = dfGlobalMax;
13✔
1535
        return CE_None;
13✔
1536
    }
1537
    else
1538
    {
1539
        return GDALRasterBand::ComputeRasterMinMax(bApproxOK, adfMinMax);
3✔
1540
    }
1541
}
1542

1543
// #define naive_update_not_used
1544

1545
namespace
1546
{
1547
struct Context
1548
{
1549
    CPL_DISALLOW_COPY_ASSIGN(Context)
1550
    Context() = default;
1551

1552
    // Protected by mutex
1553
    std::mutex oMutex{};
1554
    uint64_t nTotalIteratedPixels = 0;
1555
    uint64_t nLastReportedPixels = 0;
1556
    bool bFailure = false;
1557
    bool bFallbackToBase = false;
1558
    // End of protected by mutex
1559

1560
    bool bApproxOK = false;
1561
    GDALProgressFunc pfnProgress = nullptr;
1562
    void *pProgressData = nullptr;
1563

1564
    // VRTSourcedRasterBand parameters
1565
    double dfNoDataValue = 0;
1566
    bool bNoDataValueSet = false;
1567
    bool bHideNoDataValue = false;
1568

1569
    double dfGlobalMin = std::numeric_limits<double>::max();
1570
    double dfGlobalMax = -std::numeric_limits<double>::max();
1571
#ifdef naive_update_not_used
1572
    // This native method uses the fact that stddev = sqrt(sum_of_squares/N -
1573
    // mean^2) and that thus sum_of_squares = N * (stddev^2 + mean^2)
1574
    double dfGlobalSum = 0;
1575
    double dfGlobalSumSquare = 0;
1576
#else
1577
    // This method uses
1578
    // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Parallel_algorithm
1579
    // which is more numerically robust
1580
    double dfGlobalMean = 0;
1581
    double dfGlobalM2 = 0;
1582
#endif
1583
    uint64_t nGlobalValidPixels = 0;
1584
    uint64_t nTotalPixelsOfSources = 0;
1585
};
1586
}  // namespace
1587

1588
static void UpdateStatsWithConstantValue(Context &sContext, double dfVal,
3✔
1589
                                         uint64_t nPixelCount)
1590
{
1591
    sContext.dfGlobalMin = std::min(sContext.dfGlobalMin, dfVal);
3✔
1592
    sContext.dfGlobalMax = std::max(sContext.dfGlobalMax, dfVal);
3✔
1593
#ifdef naive_update_not_used
1594
    sContext.dfGlobalSum += dfVal * nPixelCount;
1595
    sContext.dfGlobalSumSquare += dfVal * dfVal * nPixelCount;
1596
#else
1597
    const auto nNewGlobalValidPixels =
3✔
1598
        sContext.nGlobalValidPixels + nPixelCount;
3✔
1599
    const double dfDelta = dfVal - sContext.dfGlobalMean;
3✔
1600
    sContext.dfGlobalMean += nPixelCount * dfDelta / nNewGlobalValidPixels;
3✔
1601
    sContext.dfGlobalM2 += dfDelta * dfDelta * nPixelCount *
3✔
1602
                           sContext.nGlobalValidPixels / nNewGlobalValidPixels;
3✔
1603
#endif
1604
    sContext.nGlobalValidPixels += nPixelCount;
3✔
1605
}
3✔
1606

1607
/************************************************************************/
1608
/*                         ComputeStatistics()                          */
1609
/************************************************************************/
1610

1611
CPLErr VRTSourcedRasterBand::ComputeStatistics(int bApproxOK, double *pdfMin,
30✔
1612
                                               double *pdfMax, double *pdfMean,
1613
                                               double *pdfStdDev,
1614
                                               GDALProgressFunc pfnProgress,
1615
                                               void *pProgressData)
1616

1617
{
1618
    const std::string osFctId("VRTSourcedRasterBand::ComputeStatistics");
60✔
1619
    GDALAntiRecursionGuard oGuard(osFctId);
60✔
1620
    if (oGuard.GetCallDepth() >= 32)
30✔
1621
    {
1622
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
×
1623
        return CE_Failure;
×
1624
    }
1625

1626
    GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
90✔
1627
    if (oGuard2.GetCallDepth() >= 2)
30✔
1628
    {
1629
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
×
1630
        return CE_Failure;
×
1631
    }
1632

1633
    /* -------------------------------------------------------------------- */
1634
    /*      If we have overview bands, use them for statistics.             */
1635
    /* -------------------------------------------------------------------- */
1636
    if (bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews())
30✔
1637
    {
1638
        GDALRasterBand *const poBand =
1639
            GetRasterSampleOverview(GDALSTAT_APPROX_NUMSAMPLES);
2✔
1640

1641
        if (poBand != nullptr && poBand != this)
2✔
1642
        {
1643
            auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
2✔
1644
            CPLErr eErr;
1645
            if (l_poDS && !l_poDS->m_apoOverviews.empty() &&
3✔
1646
                dynamic_cast<VRTSourcedRasterBand *>(poBand) != nullptr)
1✔
1647
            {
1648
                auto apoTmpOverviews = std::move(l_poDS->m_apoOverviews);
2✔
1649
                l_poDS->m_apoOverviews.clear();
1✔
1650
                eErr = poBand->GDALRasterBand::ComputeStatistics(
1✔
1651
                    TRUE, pdfMin, pdfMax, pdfMean, pdfStdDev, pfnProgress,
1652
                    pProgressData);
1653
                l_poDS->m_apoOverviews = std::move(apoTmpOverviews);
1✔
1654
            }
1655
            else
1656
            {
1657
                eErr = poBand->ComputeStatistics(TRUE, pdfMin, pdfMax, pdfMean,
1✔
1658
                                                 pdfStdDev, pfnProgress,
1659
                                                 pProgressData);
1✔
1660
            }
1661
            if (eErr == CE_None && pdfMin && pdfMax && pdfMean && pdfStdDev)
2✔
1662
            {
1663
                SetMetadataItem("STATISTICS_APPROXIMATE", "YES");
2✔
1664
                SetMetadataItem(
2✔
1665
                    "STATISTICS_VALID_PERCENT",
1666
                    poBand->GetMetadataItem("STATISTICS_VALID_PERCENT"));
2✔
1667
                SetStatistics(*pdfMin, *pdfMax, *pdfMean, *pdfStdDev);
2✔
1668
            }
1669
            return eErr;
2✔
1670
        }
1671
    }
1672

1673
    if (IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange(
28✔
1674
            /*bAllowMaxValAdjustment = */ false))
1675
    {
1676
        Context sContext;
22✔
1677
        sContext.bApproxOK = bApproxOK;
22✔
1678
        sContext.dfNoDataValue = m_dfNoDataValue;
22✔
1679
        sContext.bNoDataValueSet = m_bNoDataValueSet;
22✔
1680
        sContext.bHideNoDataValue = m_bHideNoDataValue;
22✔
1681
        sContext.pfnProgress = pfnProgress;
22✔
1682
        sContext.pProgressData = pProgressData;
22✔
1683

1684
        struct Job
1685
        {
1686
            Context *psContext = nullptr;
1687
            GDALRasterBand *poRasterBand = nullptr;
1688
            uint64_t nPixelCount = 0;
1689
            uint64_t nLastAddedPixels = 0;
1690
            uint64_t nValidPixels = 0;
1691
            double dfMin = 0;
1692
            double dfMax = 0;
1693
            double dfMean = 0;
1694
            double dfStdDev = 0;
1695

1696
            static int CPL_STDCALL ProgressFunc(double dfComplete,
58✔
1697
                                                const char *pszMessage,
1698
                                                void *pProgressArg)
1699
            {
1700
                auto psJob = static_cast<Job *>(pProgressArg);
58✔
1701
                auto psContext = psJob->psContext;
58✔
1702
                const uint64_t nNewAddedPixels =
58✔
1703
                    dfComplete == 1.0
1704
                        ? psJob->nPixelCount
58✔
1705
                        : static_cast<uint64_t>(
1706
                              dfComplete * psJob->nPixelCount + 0.5);
54✔
1707
                const auto nUpdateThreshold =
1708
                    std::min(psContext->nTotalPixelsOfSources / 1000,
116✔
1709
                             static_cast<uint64_t>(1000 * 1000));
58✔
1710
                std::lock_guard<std::mutex> oLock(psContext->oMutex);
116✔
1711
                psContext->nTotalIteratedPixels +=
58✔
1712
                    (nNewAddedPixels - psJob->nLastAddedPixels);
58✔
1713
                psJob->nLastAddedPixels = nNewAddedPixels;
58✔
1714
                if (psContext->nTotalIteratedPixels ==
58✔
1715
                    psContext->nTotalPixelsOfSources)
58✔
1716
                {
1717
                    psContext->nLastReportedPixels =
2✔
1718
                        psContext->nTotalIteratedPixels;
2✔
1719
                    return psContext->pfnProgress(1.0, pszMessage,
2✔
1720
                                                  psContext->pProgressData);
2✔
1721
                }
1722
                else if (psContext->nTotalIteratedPixels -
56✔
1723
                             psContext->nLastReportedPixels >
56✔
1724
                         nUpdateThreshold)
1725
                {
1726
                    psContext->nLastReportedPixels =
48✔
1727
                        psContext->nTotalIteratedPixels;
48✔
1728
                    return psContext->pfnProgress(
96✔
1729
                        static_cast<double>(psContext->nTotalIteratedPixels) /
48✔
1730
                            psContext->nTotalPixelsOfSources,
48✔
1731
                        pszMessage, psContext->pProgressData);
48✔
1732
                }
1733
                return 1;
8✔
1734
            }
1735

1736
            static void UpdateStats(const Job *psJob)
32✔
1737
            {
1738
                const auto nValidPixels = psJob->nValidPixels;
32✔
1739
                auto psContext = psJob->psContext;
32✔
1740
                if (nValidPixels > 0)
32✔
1741
                {
1742
                    psContext->dfGlobalMin =
24✔
1743
                        std::min(psContext->dfGlobalMin, psJob->dfMin);
24✔
1744
                    psContext->dfGlobalMax =
24✔
1745
                        std::max(psContext->dfGlobalMax, psJob->dfMax);
24✔
1746
#ifdef naive_update_not_used
1747
                    psContext->dfGlobalSum += nValidPixels * psJob->dfMean;
1748
                    psContext->dfGlobalSumSquare +=
1749
                        nValidPixels * (psJob->dfStdDev * psJob->dfStdDev +
1750
                                        psJob->dfMean * psJob->dfMean);
1751
                    psContext->nGlobalValidPixels += nValidPixels;
1752
#else
1753
                    const auto nNewGlobalValidPixels =
24✔
1754
                        psContext->nGlobalValidPixels + nValidPixels;
24✔
1755
                    const double dfDelta =
24✔
1756
                        psJob->dfMean - psContext->dfGlobalMean;
24✔
1757
                    psContext->dfGlobalMean +=
24✔
1758
                        nValidPixels * dfDelta / nNewGlobalValidPixels;
24✔
1759
                    psContext->dfGlobalM2 +=
24✔
1760
                        nValidPixels * psJob->dfStdDev * psJob->dfStdDev +
24✔
1761
                        dfDelta * dfDelta * nValidPixels *
24✔
1762
                            psContext->nGlobalValidPixels /
24✔
1763
                            nNewGlobalValidPixels;
1764
                    psContext->nGlobalValidPixels = nNewGlobalValidPixels;
24✔
1765
#endif
1766
                }
1767
                int bHasNoData = FALSE;
32✔
1768
                const double dfNoDataValue =
1769
                    psJob->poRasterBand->GetNoDataValue(&bHasNoData);
32✔
1770
                if (nValidPixels < psJob->nPixelCount && bHasNoData &&
9✔
1771
                    !std::isnan(dfNoDataValue) &&
50✔
1772
                    (!psContext->bNoDataValueSet ||
9✔
1773
                     dfNoDataValue != psContext->dfNoDataValue))
7✔
1774
                {
1775
                    const auto eBandDT =
1776
                        psJob->poRasterBand->GetRasterDataType();
2✔
1777
                    // Check that the band nodata value is in the range of the
1778
                    // original raster type
1779
                    GByte abyTempBuffer[2 * sizeof(double)];
1780
                    CPLAssert(GDALGetDataTypeSizeBytes(eBandDT) <=
2✔
1781
                              static_cast<int>(sizeof(abyTempBuffer)));
1782
                    GDALCopyWords(&dfNoDataValue, GDT_Float64, 0,
2✔
1783
                                  &abyTempBuffer[0], eBandDT, 0, 1);
1784
                    double dfNoDataValueAfter = dfNoDataValue;
2✔
1785
                    GDALCopyWords(&abyTempBuffer[0], eBandDT, 0,
2✔
1786
                                  &dfNoDataValueAfter, GDT_Float64, 0, 1);
1787
                    if (!std::isfinite(dfNoDataValue) ||
4✔
1788
                        std::fabs(dfNoDataValueAfter - dfNoDataValue) < 1.0)
2✔
1789
                    {
1790
                        UpdateStatsWithConstantValue(
2✔
1791
                            *psContext, dfNoDataValueAfter,
1792
                            psJob->nPixelCount - nValidPixels);
2✔
1793
                    }
1794
                }
1795
            }
32✔
1796
        };
1797

1798
        const auto JobRunner = [](void *pData)
35✔
1799
        {
1800
            auto psJob = static_cast<Job *>(pData);
35✔
1801
            auto psContext = psJob->psContext;
35✔
1802
            {
1803
                std::lock_guard<std::mutex> oLock(psContext->oMutex);
35✔
1804
                if (psContext->bFallbackToBase || psContext->bFailure)
35✔
UNCOV
1805
                    return;
×
1806
            }
1807

1808
            auto poSimpleSourceBand = psJob->poRasterBand;
35✔
1809
            psJob->nPixelCount =
35✔
1810
                static_cast<uint64_t>(poSimpleSourceBand->GetXSize()) *
35✔
1811
                poSimpleSourceBand->GetYSize();
35✔
1812

1813
            CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
35✔
1814
            CPLErr eErr = poSimpleSourceBand->ComputeStatistics(
35✔
1815
                psContext->bApproxOK, &psJob->dfMin, &psJob->dfMax,
35✔
1816
                &psJob->dfMean, &psJob->dfStdDev,
1817
                psContext->pfnProgress == nullptr ||
41✔
1818
                        psContext->pfnProgress == GDALDummyProgress
6✔
1819
                    ? GDALDummyProgress
1820
                    : Job::ProgressFunc,
1821
                psJob);
35✔
1822
            const char *pszValidPercent =
1823
                poSimpleSourceBand->GetMetadataItem("STATISTICS_VALID_PERCENT");
35✔
1824
            psJob->nValidPixels =
32✔
1825
                pszValidPercent
1826
                    ? static_cast<uint64_t>(CPLAtof(pszValidPercent) *
34✔
1827
                                            psJob->nPixelCount / 100.0)
32✔
1828
                    : psJob->nPixelCount;
1829
            if (eErr == CE_Failure)
32✔
1830
            {
1831
                if (pszValidPercent != nullptr &&
15✔
1832
                    CPLAtof(pszValidPercent) == 0.0)
7✔
1833
                {
1834
                    // ok: no valid sample
1835
                }
1836
                else
1837
                {
1838
                    std::lock_guard<std::mutex> oLock(psContext->oMutex);
×
1839
                    psContext->bFailure = true;
×
1840
                }
1841
            }
1842
            else
1843
            {
1844
                int bHasNoData = FALSE;
25✔
1845
                CPL_IGNORE_RET_VAL(
26✔
1846
                    psJob->poRasterBand->GetNoDataValue(&bHasNoData));
25✔
1847
                if (!bHasNoData && psContext->bNoDataValueSet &&
25✔
1848
                    !psContext->bHideNoDataValue &&
8✔
1849
                    psContext->dfNoDataValue >= psJob->dfMin &&
7✔
1850
                    psContext->dfNoDataValue <= psJob->dfMax)
2✔
1851
                {
1852
                    std::lock_guard<std::mutex> oLock(psContext->oMutex);
2✔
1853
                    psJob->psContext->bFallbackToBase = true;
2✔
1854
                    return;
2✔
1855
                }
1856
            }
1857
        };
1858

1859
        CPLWorkerThreadPool *poThreadPool = nullptr;
22✔
1860
        int nThreads =
1861
            nSources > 1
22✔
1862
                ? VRTDataset::GetNumThreads(dynamic_cast<VRTDataset *>(poDS))
22✔
1863
                : 0;
22✔
1864
        if (nThreads > 1024)
22✔
1865
            nThreads = 1024;  // to please Coverity
×
1866
        if (nThreads > 1)
22✔
1867
        {
1868
            // Check that all sources refer to different datasets
1869
            // before allowing multithreaded access
1870
            // If the datasets belong to the MEM driver, check GDALDataset*
1871
            // pointer values. Otherwise use dataset name.
1872
            std::set<std::string> oSetDatasetNames;
24✔
1873
            std::set<GDALDataset *> oSetDatasetPointers;
24✔
1874
            for (int i = 0; i < nSources; ++i)
36✔
1875
            {
1876
                auto poSimpleSource =
1877
                    cpl::down_cast<VRTSimpleSource *>(papoSources[i]);
24✔
1878
                assert(poSimpleSource);
24✔
1879
                auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
24✔
1880
                assert(poSimpleSourceBand);
24✔
1881
                auto poSourceDataset = poSimpleSourceBand->GetDataset();
24✔
1882
                if (poSourceDataset == nullptr)
24✔
1883
                {
1884
                    nThreads = 0;
×
1885
                    break;
×
1886
                }
1887
                auto poDriver = poSourceDataset->GetDriver();
24✔
1888
                if (poDriver && EQUAL(poDriver->GetDescription(), "MEM"))
24✔
1889
                {
1890
                    if (oSetDatasetPointers.find(poSourceDataset) !=
24✔
1891
                        oSetDatasetPointers.end())
48✔
1892
                    {
1893
                        nThreads = 0;
×
1894
                        break;
×
1895
                    }
1896
                    oSetDatasetPointers.insert(poSourceDataset);
24✔
1897
                }
1898
                else
1899
                {
1900
                    if (oSetDatasetNames.find(
×
1901
                            poSourceDataset->GetDescription()) !=
×
1902
                        oSetDatasetNames.end())
×
1903
                    {
1904
                        nThreads = 0;
×
1905
                        break;
×
1906
                    }
1907
                    oSetDatasetNames.insert(poSourceDataset->GetDescription());
×
1908
                }
1909
            }
1910
            if (nThreads > 1)
12✔
1911
            {
1912
                poThreadPool = GDALGetGlobalThreadPool(nThreads);
12✔
1913
            }
1914
        }
1915

1916
        // Compute total number of pixels of sources
1917
        for (int i = 0; i < nSources; ++i)
57✔
1918
        {
1919
            auto poSimpleSource =
35✔
1920
                static_cast<VRTSimpleSource *>(papoSources[i]);
35✔
1921
            assert(poSimpleSource);
35✔
1922
            auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
35✔
1923
            assert(poSimpleSourceBand);
35✔
1924
            sContext.nTotalPixelsOfSources +=
35✔
1925
                static_cast<uint64_t>(poSimpleSourceBand->GetXSize()) *
35✔
1926
                poSimpleSourceBand->GetYSize();
35✔
1927
        }
1928

1929
        if (poThreadPool)
22✔
1930
        {
1931
            CPLDebugOnly("VRT", "ComputeStatistics(): use optimized "
12✔
1932
                                "multi-threaded code path for mosaic");
1933
            std::vector<Job> asJobs(nSources);
24✔
1934
            auto poQueue = poThreadPool->CreateJobQueue();
24✔
1935
            for (int i = 0; i < nSources; ++i)
36✔
1936
            {
1937
                auto poSimpleSource =
24✔
1938
                    static_cast<VRTSimpleSource *>(papoSources[i]);
24✔
1939
                assert(poSimpleSource);
24✔
1940
                auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
24✔
1941
                assert(poSimpleSourceBand);
24✔
1942
                asJobs[i].psContext = &sContext;
24✔
1943
                asJobs[i].poRasterBand = poSimpleSourceBand;
24✔
1944
                if (!poQueue->SubmitJob(JobRunner, &asJobs[i]))
24✔
1945
                {
1946
                    sContext.bFailure = true;
×
1947
                    break;
×
1948
                }
1949
            }
1950
            poQueue->WaitCompletion();
12✔
1951
            if (!(sContext.bFailure || sContext.bFallbackToBase))
12✔
1952
            {
1953
                for (int i = 0; i < nSources; ++i)
33✔
1954
                {
1955
                    Job::UpdateStats(&asJobs[i]);
22✔
1956
                }
1957
            }
1958
        }
1959
        else
1960
        {
1961
            CPLDebugOnly(
10✔
1962
                "VRT",
1963
                "ComputeStatistics(): use optimized code path for mosaic");
1964
            for (int i = 0; i < nSources; ++i)
20✔
1965
            {
1966
                auto poSimpleSource =
11✔
1967
                    static_cast<VRTSimpleSource *>(papoSources[i]);
11✔
1968
                assert(poSimpleSource);
11✔
1969
                auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
11✔
1970
                assert(poSimpleSourceBand);
11✔
1971
                Job sJob;
11✔
1972
                sJob.psContext = &sContext;
11✔
1973
                sJob.poRasterBand = poSimpleSourceBand;
11✔
1974
                JobRunner(&sJob);
11✔
1975
                if (sContext.bFailure || sContext.bFallbackToBase)
11✔
1976
                    break;
1977
                Job::UpdateStats(&sJob);
10✔
1978
            }
1979
        }
1980

1981
        if (sContext.bFailure)
22✔
1982
            return CE_Failure;
×
1983
        if (sContext.bFallbackToBase)
22✔
1984
        {
1985
            // If the VRT band nodata value is in the [min, max] range
1986
            // of the source and that the source has no nodata value set,
1987
            // then we can't use the optimization.
1988
            CPLDebugOnly("VRT", "ComputeStatistics(): revert back to "
2✔
1989
                                "generic case because of nodata value in range "
1990
                                "of source raster");
1991
            return GDALRasterBand::ComputeStatistics(
2✔
1992
                bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev, pfnProgress,
1993
                pProgressData);
2✔
1994
        }
1995

1996
        const uint64_t nTotalPixels =
20✔
1997
            static_cast<uint64_t>(nRasterXSize) * nRasterYSize;
20✔
1998
        if (m_bNoDataValueSet && m_bHideNoDataValue &&
7✔
1999
            !std::isnan(m_dfNoDataValue) && IsNoDataValueInDataTypeRange())
27✔
2000
        {
2001
            UpdateStatsWithConstantValue(sContext, m_dfNoDataValue,
1✔
2002
                                         nTotalPixels -
2003
                                             sContext.nGlobalValidPixels);
1✔
2004
        }
2005
        else if (!m_bNoDataValueSet)
19✔
2006
        {
2007
            sContext.nGlobalValidPixels = nTotalPixels;
13✔
2008
        }
2009

2010
#ifdef naive_update_not_used
2011
        const double dfGlobalMean =
2012
            sContext.nGlobalValidPixels > 0
2013
                ? sContext.dfGlobalSum / sContext.nGlobalValidPixels
2014
                : 0;
2015
        const double dfGlobalStdDev =
2016
            sContext.nGlobalValidPixels > 0
2017
                ? sqrt(sContext.dfGlobalSumSquare /
2018
                           sContext.nGlobalValidPixels -
2019
                       dfGlobalMean * dfGlobalMean)
2020
                : 0;
2021
#else
2022
        const double dfGlobalMean = sContext.dfGlobalMean;
20✔
2023
        const double dfGlobalStdDev =
2024
            sContext.nGlobalValidPixels > 0
20✔
2025
                ? sqrt(sContext.dfGlobalM2 / sContext.nGlobalValidPixels)
20✔
2026
                : 0;
20✔
2027
#endif
2028
        if (sContext.nGlobalValidPixels > 0)
20✔
2029
        {
2030
            if (bApproxOK)
18✔
2031
            {
2032
                SetMetadataItem("STATISTICS_APPROXIMATE", "YES");
1✔
2033
            }
2034
            else if (GetMetadataItem("STATISTICS_APPROXIMATE"))
17✔
2035
            {
2036
                SetMetadataItem("STATISTICS_APPROXIMATE", nullptr);
×
2037
            }
2038
            SetStatistics(sContext.dfGlobalMin, sContext.dfGlobalMax,
18✔
2039
                          dfGlobalMean, dfGlobalStdDev);
18✔
2040
        }
2041
        else
2042
        {
2043
            sContext.dfGlobalMin = 0.0;
2✔
2044
            sContext.dfGlobalMax = 0.0;
2✔
2045
        }
2046

2047
        SetValidPercent(nTotalPixels, sContext.nGlobalValidPixels);
20✔
2048

2049
        if (pdfMin)
20✔
2050
            *pdfMin = sContext.dfGlobalMin;
16✔
2051
        if (pdfMax)
20✔
2052
            *pdfMax = sContext.dfGlobalMax;
16✔
2053
        if (pdfMean)
20✔
2054
            *pdfMean = dfGlobalMean;
12✔
2055
        if (pdfStdDev)
20✔
2056
            *pdfStdDev = dfGlobalStdDev;
12✔
2057

2058
        if (sContext.nGlobalValidPixels == 0)
20✔
2059
        {
2060
            ReportError(CE_Failure, CPLE_AppDefined,
2✔
2061
                        "Failed to compute statistics, no valid pixels found "
2062
                        "in sampling.");
2063
        }
2064

2065
        return sContext.nGlobalValidPixels > 0 ? CE_None : CE_Failure;
20✔
2066
    }
2067
    else
2068
    {
2069
        return GDALRasterBand::ComputeStatistics(bApproxOK, pdfMin, pdfMax,
6✔
2070
                                                 pdfMean, pdfStdDev,
2071
                                                 pfnProgress, pProgressData);
6✔
2072
    }
2073
}
2074

2075
/************************************************************************/
2076
/*                            GetHistogram()                            */
2077
/************************************************************************/
2078

2079
CPLErr VRTSourcedRasterBand::GetHistogram(double dfMin, double dfMax,
7✔
2080
                                          int nBuckets, GUIntBig *panHistogram,
2081
                                          int bIncludeOutOfRange, int bApproxOK,
2082
                                          GDALProgressFunc pfnProgress,
2083
                                          void *pProgressData)
2084

2085
{
2086
    /* -------------------------------------------------------------------- */
2087
    /*      If we have overviews, use them for the histogram.               */
2088
    /* -------------------------------------------------------------------- */
2089
    if (bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews())
7✔
2090
    {
2091
        // FIXME: Should we use the most reduced overview here or use some
2092
        // minimum number of samples like GDALRasterBand::ComputeStatistics()
2093
        // does?
2094
        GDALRasterBand *poBand = GetRasterSampleOverview(0);
1✔
2095

2096
        if (poBand != nullptr && poBand != this)
1✔
2097
        {
2098
            auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
1✔
2099
            if (l_poDS && !l_poDS->m_apoOverviews.empty() &&
2✔
2100
                dynamic_cast<VRTSourcedRasterBand *>(poBand) != nullptr)
1✔
2101
            {
2102
                auto apoTmpOverviews = std::move(l_poDS->m_apoOverviews);
1✔
2103
                l_poDS->m_apoOverviews.clear();
1✔
2104
                auto eErr = poBand->GDALRasterBand::GetHistogram(
1✔
2105
                    dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange,
2106
                    bApproxOK, pfnProgress, pProgressData);
2107
                l_poDS->m_apoOverviews = std::move(apoTmpOverviews);
1✔
2108
                return eErr;
1✔
2109
            }
2110
            else
2111
            {
2112
                return poBand->GetHistogram(
×
2113
                    dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange,
2114
                    bApproxOK, pfnProgress, pProgressData);
×
2115
            }
2116
        }
2117
    }
2118

2119
    if (nSources != 1)
6✔
2120
        return VRTRasterBand::GetHistogram(dfMin, dfMax, nBuckets, panHistogram,
2✔
2121
                                           bIncludeOutOfRange, bApproxOK,
2122
                                           pfnProgress, pProgressData);
2✔
2123

2124
    if (pfnProgress == nullptr)
4✔
2125
        pfnProgress = GDALDummyProgress;
4✔
2126

2127
    const std::string osFctId("VRTSourcedRasterBand::GetHistogram");
8✔
2128
    GDALAntiRecursionGuard oGuard(osFctId);
8✔
2129
    if (oGuard.GetCallDepth() >= 32)
4✔
2130
    {
2131
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
×
2132
        return CE_Failure;
×
2133
    }
2134

2135
    GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
12✔
2136
    if (oGuard2.GetCallDepth() >= 2)
4✔
2137
    {
2138
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
×
2139
        return CE_Failure;
×
2140
    }
2141

2142
    /* -------------------------------------------------------------------- */
2143
    /*      Try with source bands.                                          */
2144
    /* -------------------------------------------------------------------- */
2145
    const CPLErr eErr = papoSources[0]->GetHistogram(
4✔
2146
        GetXSize(), GetYSize(), dfMin, dfMax, nBuckets, panHistogram,
2147
        bIncludeOutOfRange, bApproxOK, pfnProgress, pProgressData);
4✔
2148
    if (eErr != CE_None)
4✔
2149
    {
2150
        const CPLErr eErr2 = GDALRasterBand::GetHistogram(
×
2151
            dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange, bApproxOK,
2152
            pfnProgress, pProgressData);
2153
        return eErr2;
×
2154
    }
2155

2156
    SetDefaultHistogram(dfMin, dfMax, nBuckets, panHistogram);
4✔
2157

2158
    return CE_None;
4✔
2159
}
2160

2161
/************************************************************************/
2162
/*                             AddSource()                              */
2163
/************************************************************************/
2164

2165
CPLErr VRTSourcedRasterBand::AddSource(VRTSource *poNewSource)
144,819✔
2166

2167
{
2168
    nSources++;
144,819✔
2169

2170
    papoSources = static_cast<VRTSource **>(
144,819✔
2171
        CPLRealloc(papoSources, sizeof(void *) * nSources));
144,819✔
2172
    papoSources[nSources - 1] = poNewSource;
144,819✔
2173

2174
    auto l_poDS = static_cast<VRTDataset *>(poDS);
144,819✔
2175
    l_poDS->SetNeedsFlush();
144,819✔
2176
    l_poDS->SourceAdded();
144,819✔
2177

2178
    if (poNewSource->IsSimpleSource())
144,819✔
2179
    {
2180
        VRTSimpleSource *poSS = static_cast<VRTSimpleSource *>(poNewSource);
144,793✔
2181
        if (GetMetadataItem("NBITS", "IMAGE_STRUCTURE") != nullptr)
144,793✔
2182
        {
2183
            int nBits = atoi(GetMetadataItem("NBITS", "IMAGE_STRUCTURE"));
47✔
2184
            if (nBits >= 1 && nBits <= 31)
47✔
2185
            {
2186
                poSS->SetMaxValue(static_cast<int>((1U << nBits) - 1));
47✔
2187
            }
2188
        }
2189
    }
2190

2191
    return CE_None;
144,819✔
2192
}
2193

2194
/*! @endcond */
2195

2196
/************************************************************************/
2197
/*                              VRTAddSource()                          */
2198
/************************************************************************/
2199

2200
/**
2201
 * @see VRTSourcedRasterBand::AddSource().
2202
 */
2203

2204
CPLErr CPL_STDCALL VRTAddSource(VRTSourcedRasterBandH hVRTBand,
×
2205
                                VRTSourceH hNewSource)
2206
{
2207
    VALIDATE_POINTER1(hVRTBand, "VRTAddSource", CE_Failure);
×
2208

2209
    return reinterpret_cast<VRTSourcedRasterBand *>(hVRTBand)->AddSource(
×
2210
        reinterpret_cast<VRTSource *>(hNewSource));
×
2211
}
2212

2213
/*! @cond Doxygen_Suppress */
2214

2215
/************************************************************************/
2216
/*                              XMLInit()                               */
2217
/************************************************************************/
2218

2219
CPLErr VRTSourcedRasterBand::XMLInit(const CPLXMLNode *psTree,
1,494✔
2220
                                     const char *pszVRTPath,
2221
                                     VRTMapSharedResources &oMapSharedSources)
2222

2223
{
2224
    {
2225
        const CPLErr eErr =
2226
            VRTRasterBand::XMLInit(psTree, pszVRTPath, oMapSharedSources);
1,494✔
2227
        if (eErr != CE_None)
1,494✔
2228
            return eErr;
×
2229
    }
2230

2231
    /* -------------------------------------------------------------------- */
2232
    /*      Process sources.                                                */
2233
    /* -------------------------------------------------------------------- */
2234
    VRTDriver *const poDriver =
2235
        static_cast<VRTDriver *>(GDALGetDriverByName("VRT"));
1,494✔
2236

2237
    for (const CPLXMLNode *psChild = psTree->psChild;
1,494✔
2238
         psChild != nullptr && poDriver != nullptr; psChild = psChild->psNext)
10,835✔
2239
    {
2240
        if (psChild->eType != CXT_Element)
9,362✔
2241
            continue;
3,567✔
2242

2243
        CPLErrorReset();
5,795✔
2244
        VRTSource *const poSource =
2245
            poDriver->ParseSource(psChild, pszVRTPath, oMapSharedSources);
5,795✔
2246
        if (poSource != nullptr)
5,795✔
2247
            AddSource(poSource);
3,509✔
2248
        else if (CPLGetLastErrorType() != CE_None)
2,286✔
2249
            return CE_Failure;
21✔
2250
    }
2251

2252
    /* -------------------------------------------------------------------- */
2253
    /*      Done.                                                           */
2254
    /* -------------------------------------------------------------------- */
2255
    const char *pszSubclass =
2256
        CPLGetXMLValue(psTree, "subclass", "VRTSourcedRasterBand");
1,473✔
2257
    if (nSources == 0 && !EQUAL(pszSubclass, "VRTDerivedRasterBand"))
1,473✔
2258
        CPLDebug("VRT", "No valid sources found for band in VRT file %s",
159✔
2259
                 GetDataset() ? GetDataset()->GetDescription() : "");
159✔
2260

2261
    return CE_None;
1,473✔
2262
}
2263

2264
/************************************************************************/
2265
/*                           SerializeToXML()                           */
2266
/************************************************************************/
2267

2268
CPLXMLNode *VRTSourcedRasterBand::SerializeToXML(const char *pszVRTPath,
661✔
2269
                                                 bool &bHasWarnedAboutRAMUsage,
2270
                                                 size_t &nAccRAMUsage)
2271

2272
{
2273
    CPLXMLNode *psTree = VRTRasterBand::SerializeToXML(
661✔
2274
        pszVRTPath, bHasWarnedAboutRAMUsage, nAccRAMUsage);
2275
    CPLXMLNode *psLastChild = psTree->psChild;
661✔
2276
    while (psLastChild != nullptr && psLastChild->psNext != nullptr)
2,472✔
2277
        psLastChild = psLastChild->psNext;
1,811✔
2278

2279
    /* -------------------------------------------------------------------- */
2280
    /*      Process Sources.                                                */
2281
    /* -------------------------------------------------------------------- */
2282

2283
    GIntBig nUsableRAM = -1;
661✔
2284

2285
    for (int iSource = 0; iSource < nSources; iSource++)
3,246✔
2286
    {
2287
        CPLXMLNode *const psXMLSrc =
2288
            papoSources[iSource]->SerializeToXML(pszVRTPath);
2,585✔
2289

2290
        if (psXMLSrc == nullptr)
2,585✔
2291
            break;
×
2292

2293
        // Creating the CPLXMLNode tree representation of a VRT can easily
2294
        // take several times RAM usage than its string serialization, or its
2295
        // internal representation in the driver.
2296
        // We multiply the estimate by a factor of 2, experimentally found to
2297
        // be more realistic than the conservative raw estimate.
2298
        nAccRAMUsage += 2 * CPLXMLNodeGetRAMUsageEstimate(psXMLSrc);
2,585✔
2299
        if (!bHasWarnedAboutRAMUsage && nAccRAMUsage > 512 * 1024 * 1024)
2,585✔
2300
        {
2301
            if (nUsableRAM < 0)
×
2302
                nUsableRAM = CPLGetUsablePhysicalRAM();
×
2303
            if (nUsableRAM > 0 &&
×
2304
                nAccRAMUsage > static_cast<uint64_t>(nUsableRAM) / 10 * 8)
×
2305
            {
2306
                bHasWarnedAboutRAMUsage = true;
×
2307
                CPLError(CE_Warning, CPLE_AppDefined,
×
2308
                         "Serialization of this VRT file has already consumed "
2309
                         "at least %.02f GB of RAM over a total of %.02f. This "
2310
                         "process may abort",
2311
                         double(nAccRAMUsage) / (1024 * 1024 * 1024),
×
2312
                         double(nUsableRAM) / (1024 * 1024 * 1024));
×
2313
            }
2314
        }
2315

2316
        if (psLastChild == nullptr)
2,585✔
2317
            psTree->psChild = psXMLSrc;
×
2318
        else
2319
            psLastChild->psNext = psXMLSrc;
2,585✔
2320
        psLastChild = psXMLSrc;
2,585✔
2321
    }
2322

2323
    return psTree;
661✔
2324
}
2325

2326
/************************************************************************/
2327
/*                     SkipBufferInitialization()                       */
2328
/************************************************************************/
2329

2330
bool VRTSourcedRasterBand::SkipBufferInitialization()
258,790✔
2331
{
2332
    if (m_nSkipBufferInitialization >= 0)
258,790✔
2333
        return m_nSkipBufferInitialization != 0;
253,739✔
2334
    /* -------------------------------------------------------------------- */
2335
    /*      Check if we can avoid buffer initialization.                    */
2336
    /* -------------------------------------------------------------------- */
2337

2338
    // Note: if one day we do alpha compositing, we will need to check that.
2339
    m_nSkipBufferInitialization = FALSE;
5,051✔
2340
    if (nSources != 1 || !papoSources[0]->IsSimpleSource())
5,051✔
2341
    {
2342
        return false;
794✔
2343
    }
2344
    VRTSimpleSource *poSS = static_cast<VRTSimpleSource *>(papoSources[0]);
4,257✔
2345
    if (poSS->GetType() == VRTSimpleSource::GetTypeStatic())
4,257✔
2346
    {
2347
        auto l_poBand = poSS->GetRasterBand();
3,905✔
2348
        if (l_poBand != nullptr && poSS->m_dfSrcXOff >= 0.0 &&
3,894✔
2349
            poSS->m_dfSrcYOff >= 0.0 &&
3,819✔
2350
            poSS->m_dfSrcXOff + poSS->m_dfSrcXSize <= l_poBand->GetXSize() &&
3,819✔
2351
            poSS->m_dfSrcYOff + poSS->m_dfSrcYSize <= l_poBand->GetYSize() &&
3,813✔
2352
            poSS->m_dfDstXOff <= 0.0 && poSS->m_dfDstYOff <= 0.0 &&
3,813✔
2353
            poSS->m_dfDstXOff + poSS->m_dfDstXSize >= nRasterXSize &&
11,568✔
2354
            poSS->m_dfDstYOff + poSS->m_dfDstYSize >= nRasterYSize)
3,769✔
2355
        {
2356
            m_nSkipBufferInitialization = TRUE;
3,763✔
2357
        }
2358
    }
2359
    return m_nSkipBufferInitialization != 0;
4,257✔
2360
}
2361

2362
/************************************************************************/
2363
/*                          ConfigureSource()                           */
2364
/************************************************************************/
2365

2366
void VRTSourcedRasterBand::ConfigureSource(VRTSimpleSource *poSimpleSource,
140,768✔
2367
                                           GDALRasterBand *poSrcBand,
2368
                                           int bAddAsMaskBand, double dfSrcXOff,
2369
                                           double dfSrcYOff, double dfSrcXSize,
2370
                                           double dfSrcYSize, double dfDstXOff,
2371
                                           double dfDstYOff, double dfDstXSize,
2372
                                           double dfDstYSize)
2373
{
2374
    /* -------------------------------------------------------------------- */
2375
    /*      Default source and dest rectangles.                             */
2376
    /* -------------------------------------------------------------------- */
2377
    if (dfSrcYSize == -1)
140,768✔
2378
    {
2379
        dfSrcXOff = 0;
65,872✔
2380
        dfSrcYOff = 0;
65,872✔
2381
        dfSrcXSize = poSrcBand->GetXSize();
65,872✔
2382
        dfSrcYSize = poSrcBand->GetYSize();
65,872✔
2383
    }
2384

2385
    if (dfDstYSize == -1)
140,768✔
2386
    {
2387
        dfDstXOff = 0;
65,872✔
2388
        dfDstYOff = 0;
65,872✔
2389
        dfDstXSize = nRasterXSize;
65,872✔
2390
        dfDstYSize = nRasterYSize;
65,872✔
2391
    }
2392

2393
    if (bAddAsMaskBand)
140,768✔
2394
        poSimpleSource->SetSrcMaskBand(poSrcBand);
39✔
2395
    else
2396
        poSimpleSource->SetSrcBand(poSrcBand);
140,729✔
2397

2398
    poSimpleSource->SetSrcWindow(dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize);
140,768✔
2399
    poSimpleSource->SetDstWindow(dfDstXOff, dfDstYOff, dfDstXSize, dfDstYSize);
140,768✔
2400

2401
    /* -------------------------------------------------------------------- */
2402
    /*      If we can get the associated GDALDataset, add a reference to it.*/
2403
    /* -------------------------------------------------------------------- */
2404
    GDALDataset *poSrcBandDataset = poSrcBand->GetDataset();
140,768✔
2405
    if (poSrcBandDataset != nullptr)
140,768✔
2406
    {
2407
        VRTDataset *poVRTSrcBandDataset =
2408
            dynamic_cast<VRTDataset *>(poSrcBandDataset);
140,768✔
2409
        if (poVRTSrcBandDataset && !poVRTSrcBandDataset->m_bCanTakeRef)
140,768✔
2410
        {
2411
            // Situation triggered by VRTDataset::AddVirtualOverview()
2412
            // We create an overview dataset that is a VRT of a reduction of
2413
            // ourselves. But we don't want to take a reference on ourselves,
2414
            // otherwise this will prevent us to be closed in number of
2415
            // circumstances
2416
            poSimpleSource->m_bDropRefOnSrcBand = false;
62✔
2417
        }
2418
        else
2419
        {
2420
            poSrcBandDataset->Reference();
140,706✔
2421
        }
2422
    }
2423
}
140,768✔
2424

2425
/************************************************************************/
2426
/*                          AddSimpleSource()                           */
2427
/************************************************************************/
2428

2429
CPLErr VRTSourcedRasterBand::AddSimpleSource(
362✔
2430
    const char *pszFilename, int nBandIn, double dfSrcXOff, double dfSrcYOff,
2431
    double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
2432
    double dfDstXSize, double dfDstYSize, const char *pszResampling,
2433
    double dfNoDataValueIn)
2434

2435
{
2436
    /* -------------------------------------------------------------------- */
2437
    /*      Create source.                                                  */
2438
    /* -------------------------------------------------------------------- */
2439
    VRTSimpleSource *poSimpleSource = nullptr;
362✔
2440

2441
    if (pszResampling != nullptr && STARTS_WITH_CI(pszResampling, "aver"))
362✔
2442
    {
2443
        auto poAveragedSource = new VRTAveragedSource();
×
2444
        poSimpleSource = poAveragedSource;
×
2445
        if (dfNoDataValueIn != VRT_NODATA_UNSET)
×
2446
            poAveragedSource->SetNoDataValue(dfNoDataValueIn);
×
2447
    }
2448
    else
2449
    {
2450
        poSimpleSource = new VRTSimpleSource();
362✔
2451
        if (dfNoDataValueIn != VRT_NODATA_UNSET)
362✔
2452
            CPLError(
×
2453
                CE_Warning, CPLE_AppDefined,
2454
                "NODATA setting not currently supported for nearest  "
2455
                "neighbour sampled simple sources on Virtual Datasources.");
2456
    }
2457

2458
    poSimpleSource->SetSrcBand(pszFilename, nBandIn);
362✔
2459

2460
    poSimpleSource->SetSrcWindow(dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize);
362✔
2461
    poSimpleSource->SetDstWindow(dfDstXOff, dfDstYOff, dfDstXSize, dfDstYSize);
362✔
2462

2463
    /* -------------------------------------------------------------------- */
2464
    /*      add to list.                                                    */
2465
    /* -------------------------------------------------------------------- */
2466
    return AddSource(poSimpleSource);
362✔
2467
}
2468

2469
/************************************************************************/
2470
/*                          AddSimpleSource()                           */
2471
/************************************************************************/
2472

2473
CPLErr VRTSourcedRasterBand::AddSimpleSource(
67,154✔
2474
    GDALRasterBand *poSrcBand, double dfSrcXOff, double dfSrcYOff,
2475
    double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
2476
    double dfDstXSize, double dfDstYSize, const char *pszResampling,
2477
    double dfNoDataValueIn)
2478

2479
{
2480
    /* -------------------------------------------------------------------- */
2481
    /*      Create source.                                                  */
2482
    /* -------------------------------------------------------------------- */
2483
    VRTSimpleSource *poSimpleSource = nullptr;
67,154✔
2484

2485
    if (pszResampling != nullptr && STARTS_WITH_CI(pszResampling, "aver"))
67,154✔
2486
    {
2487
        auto poAveragedSource = new VRTAveragedSource();
×
2488
        poSimpleSource = poAveragedSource;
×
2489
        if (dfNoDataValueIn != VRT_NODATA_UNSET)
×
2490
            poAveragedSource->SetNoDataValue(dfNoDataValueIn);
×
2491
    }
2492
    else
2493
    {
2494
        poSimpleSource = new VRTSimpleSource();
67,154✔
2495
        if (dfNoDataValueIn != VRT_NODATA_UNSET)
67,154✔
2496
            CPLError(
×
2497
                CE_Warning, CPLE_AppDefined,
2498
                "NODATA setting not currently supported for "
2499
                "neighbour sampled simple sources on Virtual Datasources.");
2500
    }
2501

2502
    ConfigureSource(poSimpleSource, poSrcBand, FALSE, dfSrcXOff, dfSrcYOff,
67,154✔
2503
                    dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
2504
                    dfDstYSize);
2505

2506
    /* -------------------------------------------------------------------- */
2507
    /*      add to list.                                                    */
2508
    /* -------------------------------------------------------------------- */
2509
    return AddSource(poSimpleSource);
67,154✔
2510
}
2511

2512
/************************************************************************/
2513
/*                         AddMaskBandSource()                          */
2514
/************************************************************************/
2515

2516
// poSrcBand is not the mask band, but the band from which the mask band is
2517
// taken.
2518
CPLErr VRTSourcedRasterBand::AddMaskBandSource(
24✔
2519
    GDALRasterBand *poSrcBand, double dfSrcXOff, double dfSrcYOff,
2520
    double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
2521
    double dfDstXSize, double dfDstYSize)
2522
{
2523
    /* -------------------------------------------------------------------- */
2524
    /*      Create source.                                                  */
2525
    /* -------------------------------------------------------------------- */
2526
    VRTSimpleSource *poSimpleSource = new VRTSimpleSource();
24✔
2527

2528
    ConfigureSource(poSimpleSource, poSrcBand, TRUE, dfSrcXOff, dfSrcYOff,
24✔
2529
                    dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
2530
                    dfDstYSize);
2531

2532
    /* -------------------------------------------------------------------- */
2533
    /*      add to list.                                                    */
2534
    /* -------------------------------------------------------------------- */
2535
    return AddSource(poSimpleSource);
24✔
2536
}
2537

2538
/*! @endcond */
2539

2540
/************************************************************************/
2541
/*                         VRTAddSimpleSource()                         */
2542
/************************************************************************/
2543

2544
/**
2545
 * @see VRTSourcedRasterBand::AddSimpleSource().
2546
 */
2547

2548
CPLErr CPL_STDCALL VRTAddSimpleSource(VRTSourcedRasterBandH hVRTBand,
968✔
2549
                                      GDALRasterBandH hSrcBand, int nSrcXOff,
2550
                                      int nSrcYOff, int nSrcXSize,
2551
                                      int nSrcYSize, int nDstXOff, int nDstYOff,
2552
                                      int nDstXSize, int nDstYSize,
2553
                                      const char *pszResampling,
2554
                                      double dfNoDataValue)
2555
{
2556
    VALIDATE_POINTER1(hVRTBand, "VRTAddSimpleSource", CE_Failure);
968✔
2557

2558
    return reinterpret_cast<VRTSourcedRasterBand *>(hVRTBand)->AddSimpleSource(
968✔
2559
        reinterpret_cast<GDALRasterBand *>(hSrcBand), nSrcXOff, nSrcYOff,
2560
        nSrcXSize, nSrcYSize, nDstXOff, nDstYOff, nDstXSize, nDstYSize,
2561
        pszResampling, dfNoDataValue);
968✔
2562
}
2563

2564
/*! @cond Doxygen_Suppress */
2565

2566
/************************************************************************/
2567
/*                          AddComplexSource()                          */
2568
/************************************************************************/
2569

2570
CPLErr VRTSourcedRasterBand::AddComplexSource(
29✔
2571
    const char *pszFilename, int nBandIn, double dfSrcXOff, double dfSrcYOff,
2572
    double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
2573
    double dfDstXSize, double dfDstYSize, double dfScaleOff,
2574
    double dfScaleRatio, double dfNoDataValueIn, int nColorTableComponent)
2575

2576
{
2577
    /* -------------------------------------------------------------------- */
2578
    /*      Create source.                                                  */
2579
    /* -------------------------------------------------------------------- */
2580
    VRTComplexSource *const poSource = new VRTComplexSource();
29✔
2581

2582
    poSource->SetSrcBand(pszFilename, nBandIn);
29✔
2583

2584
    poSource->SetSrcWindow(dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize);
29✔
2585
    poSource->SetDstWindow(dfDstXOff, dfDstYOff, dfDstXSize, dfDstYSize);
29✔
2586

2587
    /* -------------------------------------------------------------------- */
2588
    /*      Set complex parameters.                                         */
2589
    /* -------------------------------------------------------------------- */
2590
    if (dfNoDataValueIn != VRT_NODATA_UNSET)
29✔
2591
        poSource->SetNoDataValue(dfNoDataValueIn);
8✔
2592

2593
    if (dfScaleOff != 0.0 || dfScaleRatio != 1.0)
29✔
2594
        poSource->SetLinearScaling(dfScaleOff, dfScaleRatio);
4✔
2595

2596
    poSource->SetColorTableComponent(nColorTableComponent);
29✔
2597

2598
    /* -------------------------------------------------------------------- */
2599
    /*      add to list.                                                    */
2600
    /* -------------------------------------------------------------------- */
2601
    return AddSource(poSource);
29✔
2602
}
2603

2604
/************************************************************************/
2605
/*                          AddComplexSource()                          */
2606
/************************************************************************/
2607

2608
CPLErr VRTSourcedRasterBand::AddComplexSource(
13✔
2609
    GDALRasterBand *poSrcBand, double dfSrcXOff, double dfSrcYOff,
2610
    double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
2611
    double dfDstXSize, double dfDstYSize, double dfScaleOff,
2612
    double dfScaleRatio, double dfNoDataValueIn, int nColorTableComponent)
2613

2614
{
2615
    /* -------------------------------------------------------------------- */
2616
    /*      Create source.                                                  */
2617
    /* -------------------------------------------------------------------- */
2618
    VRTComplexSource *const poSource = new VRTComplexSource();
13✔
2619

2620
    ConfigureSource(poSource, poSrcBand, FALSE, dfSrcXOff, dfSrcYOff,
13✔
2621
                    dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
2622
                    dfDstYSize);
2623

2624
    /* -------------------------------------------------------------------- */
2625
    /*      Set complex parameters.                                         */
2626
    /* -------------------------------------------------------------------- */
2627
    if (dfNoDataValueIn != VRT_NODATA_UNSET)
13✔
2628
        poSource->SetNoDataValue(dfNoDataValueIn);
2✔
2629

2630
    if (dfScaleOff != 0.0 || dfScaleRatio != 1.0)
13✔
2631
        poSource->SetLinearScaling(dfScaleOff, dfScaleRatio);
13✔
2632

2633
    poSource->SetColorTableComponent(nColorTableComponent);
13✔
2634

2635
    /* -------------------------------------------------------------------- */
2636
    /*      add to list.                                                    */
2637
    /* -------------------------------------------------------------------- */
2638
    return AddSource(poSource);
13✔
2639
}
2640

2641
/*! @endcond */
2642

2643
/************************************************************************/
2644
/*                         VRTAddComplexSource()                        */
2645
/************************************************************************/
2646

2647
/**
2648
 * @see VRTSourcedRasterBand::AddComplexSource().
2649
 */
2650

2651
CPLErr CPL_STDCALL VRTAddComplexSource(
×
2652
    VRTSourcedRasterBandH hVRTBand, GDALRasterBandH hSrcBand, int nSrcXOff,
2653
    int nSrcYOff, int nSrcXSize, int nSrcYSize, int nDstXOff, int nDstYOff,
2654
    int nDstXSize, int nDstYSize, double dfScaleOff, double dfScaleRatio,
2655
    double dfNoDataValue)
2656
{
2657
    VALIDATE_POINTER1(hVRTBand, "VRTAddComplexSource", CE_Failure);
×
2658

2659
    return reinterpret_cast<VRTSourcedRasterBand *>(hVRTBand)->AddComplexSource(
×
2660
        reinterpret_cast<GDALRasterBand *>(hSrcBand), nSrcXOff, nSrcYOff,
2661
        nSrcXSize, nSrcYSize, nDstXOff, nDstYOff, nDstXSize, nDstYSize,
2662
        dfScaleOff, dfScaleRatio, dfNoDataValue);
×
2663
}
2664

2665
/*! @cond Doxygen_Suppress */
2666

2667
/************************************************************************/
2668
/*                           AddFuncSource()                            */
2669
/************************************************************************/
2670

2671
CPLErr VRTSourcedRasterBand::AddFuncSource(VRTImageReadFunc pfnReadFunc,
12✔
2672
                                           void *pCBData,
2673
                                           double dfNoDataValueIn)
2674

2675
{
2676
    /* -------------------------------------------------------------------- */
2677
    /*      Create source.                                                  */
2678
    /* -------------------------------------------------------------------- */
2679
    VRTFuncSource *const poFuncSource = new VRTFuncSource;
12✔
2680

2681
    poFuncSource->fNoDataValue = static_cast<float>(dfNoDataValueIn);
12✔
2682
    poFuncSource->pfnReadFunc = pfnReadFunc;
12✔
2683
    poFuncSource->pCBData = pCBData;
12✔
2684
    poFuncSource->eType = GetRasterDataType();
12✔
2685

2686
    /* -------------------------------------------------------------------- */
2687
    /*      add to list.                                                    */
2688
    /* -------------------------------------------------------------------- */
2689
    return AddSource(poFuncSource);
12✔
2690
}
2691

2692
/*! @endcond */
2693

2694
/************************************************************************/
2695
/*                          VRTAddFuncSource()                          */
2696
/************************************************************************/
2697

2698
/**
2699
 * @see VRTSourcedRasterBand::AddFuncSource().
2700
 */
2701

2702
CPLErr CPL_STDCALL VRTAddFuncSource(VRTSourcedRasterBandH hVRTBand,
×
2703
                                    VRTImageReadFunc pfnReadFunc, void *pCBData,
2704
                                    double dfNoDataValue)
2705
{
2706
    VALIDATE_POINTER1(hVRTBand, "VRTAddFuncSource", CE_Failure);
×
2707

2708
    return reinterpret_cast<VRTSourcedRasterBand *>(hVRTBand)->AddFuncSource(
×
2709
        pfnReadFunc, pCBData, dfNoDataValue);
×
2710
}
2711

2712
/*! @cond Doxygen_Suppress */
2713

2714
/************************************************************************/
2715
/*                      GetMetadataDomainList()                         */
2716
/************************************************************************/
2717

2718
char **VRTSourcedRasterBand::GetMetadataDomainList()
×
2719
{
2720
    return CSLAddString(GDALRasterBand::GetMetadataDomainList(),
×
2721
                        "LocationInfo");
×
2722
}
2723

2724
/************************************************************************/
2725
/*                          GetMetadataItem()                           */
2726
/************************************************************************/
2727

2728
const char *VRTSourcedRasterBand::GetMetadataItem(const char *pszName,
152,743✔
2729
                                                  const char *pszDomain)
2730

2731
{
2732
    /* ==================================================================== */
2733
    /*      LocationInfo handling.                                          */
2734
    /* ==================================================================== */
2735
    if (pszDomain != nullptr && EQUAL(pszDomain, "LocationInfo") &&
152,743✔
2736
        (STARTS_WITH_CI(pszName, "Pixel_") ||
2✔
2737
         STARTS_WITH_CI(pszName, "GeoPixel_")))
×
2738
    {
2739
        /* --------------------------------------------------------------------
2740
         */
2741
        /*      What pixel are we aiming at? */
2742
        /* --------------------------------------------------------------------
2743
         */
2744
        int iPixel = 0;
2✔
2745
        int iLine = 0;
2✔
2746

2747
        if (STARTS_WITH_CI(pszName, "Pixel_"))
2✔
2748
        {
2749
            // TODO(schwehr): Replace sscanf.
2750
            if (sscanf(pszName + 6, "%d_%d", &iPixel, &iLine) != 2)
2✔
2751
                return nullptr;
×
2752
        }
2753
        else if (STARTS_WITH_CI(pszName, "GeoPixel_"))
×
2754
        {
2755
            const double dfGeoX = CPLAtof(pszName + 9);
×
2756
            const char *const pszUnderscore = strchr(pszName + 9, '_');
×
2757
            if (!pszUnderscore)
×
2758
                return nullptr;
×
2759
            const double dfGeoY = CPLAtof(pszUnderscore + 1);
×
2760

2761
            if (GetDataset() == nullptr)
×
2762
                return nullptr;
×
2763

2764
            double adfGeoTransform[6] = {0.0};
×
2765
            if (GetDataset()->GetGeoTransform(adfGeoTransform) != CE_None)
×
2766
                return nullptr;
×
2767

2768
            double adfInvGeoTransform[6] = {0.0};
×
2769
            if (!GDALInvGeoTransform(adfGeoTransform, adfInvGeoTransform))
×
2770
                return nullptr;
×
2771

2772
            iPixel = static_cast<int>(floor(adfInvGeoTransform[0] +
×
2773
                                            adfInvGeoTransform[1] * dfGeoX +
×
2774
                                            adfInvGeoTransform[2] * dfGeoY));
×
2775
            iLine = static_cast<int>(floor(adfInvGeoTransform[3] +
×
2776
                                           adfInvGeoTransform[4] * dfGeoX +
×
2777
                                           adfInvGeoTransform[5] * dfGeoY));
×
2778
        }
2779
        else
2780
        {
2781
            return nullptr;
×
2782
        }
2783

2784
        if (iPixel < 0 || iLine < 0 || iPixel >= GetXSize() ||
4✔
2785
            iLine >= GetYSize())
2✔
2786
            return nullptr;
×
2787

2788
        /* --------------------------------------------------------------------
2789
         */
2790
        /*      Find the file(s) at this location. */
2791
        /* --------------------------------------------------------------------
2792
         */
2793
        char **papszFileList = nullptr;
2✔
2794
        int nListSize = 0;     // keep it in this scope
2✔
2795
        int nListMaxSize = 0;  // keep it in this scope
2✔
2796
        CPLHashSet *const hSetFiles =
2797
            CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, nullptr);
2✔
2798

2799
        for (int iSource = 0; iSource < nSources; iSource++)
4✔
2800
        {
2801
            if (!papoSources[iSource]->IsSimpleSource())
2✔
2802
                continue;
×
2803

2804
            VRTSimpleSource *const poSrc =
2✔
2805
                static_cast<VRTSimpleSource *>(papoSources[iSource]);
2✔
2806

2807
            double dfReqXOff = 0.0;
2✔
2808
            double dfReqYOff = 0.0;
2✔
2809
            double dfReqXSize = 0.0;
2✔
2810
            double dfReqYSize = 0.0;
2✔
2811
            int nReqXOff = 0;
2✔
2812
            int nReqYOff = 0;
2✔
2813
            int nReqXSize = 0;
2✔
2814
            int nReqYSize = 0;
2✔
2815
            int nOutXOff = 0;
2✔
2816
            int nOutYOff = 0;
2✔
2817
            int nOutXSize = 0;
2✔
2818
            int nOutYSize = 0;
2✔
2819

2820
            bool bError = false;
2✔
2821
            if (!poSrc->GetSrcDstWindow(iPixel, iLine, 1, 1, 1, 1, &dfReqXOff,
2✔
2822
                                        &dfReqYOff, &dfReqXSize, &dfReqYSize,
2823
                                        &nReqXOff, &nReqYOff, &nReqXSize,
2824
                                        &nReqYSize, &nOutXOff, &nOutYOff,
2825
                                        &nOutXSize, &nOutYSize, bError))
2826
            {
2827
                if (bError)
×
2828
                {
2829
                    CSLDestroy(papszFileList);
×
2830
                    CPLHashSetDestroy(hSetFiles);
×
2831
                    return nullptr;
×
2832
                }
2833
                continue;
×
2834
            }
2835

2836
            poSrc->GetFileList(&papszFileList, &nListSize, &nListMaxSize,
2✔
2837
                               hSetFiles);
2✔
2838
        }
2839

2840
        /* --------------------------------------------------------------------
2841
         */
2842
        /*      Format into XML. */
2843
        /* --------------------------------------------------------------------
2844
         */
2845
        m_osLastLocationInfo = "<LocationInfo>";
2✔
2846
        for (int i = 0; i < nListSize && papszFileList[i] != nullptr; i++)
4✔
2847
        {
2848
            m_osLastLocationInfo += "<File>";
2✔
2849
            char *const pszXMLEscaped =
2850
                CPLEscapeString(papszFileList[i], -1, CPLES_XML);
2✔
2851
            m_osLastLocationInfo += pszXMLEscaped;
2✔
2852
            CPLFree(pszXMLEscaped);
2✔
2853
            m_osLastLocationInfo += "</File>";
2✔
2854
        }
2855
        m_osLastLocationInfo += "</LocationInfo>";
2✔
2856

2857
        CSLDestroy(papszFileList);
2✔
2858
        CPLHashSetDestroy(hSetFiles);
2✔
2859

2860
        return m_osLastLocationInfo.c_str();
2✔
2861
    }
2862

2863
    /* ==================================================================== */
2864
    /*      Other domains.                                                  */
2865
    /* ==================================================================== */
2866

2867
    return GDALRasterBand::GetMetadataItem(pszName, pszDomain);
152,741✔
2868
}
2869

2870
/************************************************************************/
2871
/*                            GetMetadata()                             */
2872
/************************************************************************/
2873

2874
char **VRTSourcedRasterBand::GetMetadata(const char *pszDomain)
13,951✔
2875

2876
{
2877
    /* ==================================================================== */
2878
    /*      vrt_sources domain handling.                                    */
2879
    /* ==================================================================== */
2880
    if (pszDomain != nullptr && EQUAL(pszDomain, "vrt_sources"))
13,951✔
2881
    {
2882
        CSLDestroy(m_papszSourceList);
1✔
2883
        m_papszSourceList = nullptr;
1✔
2884

2885
        /* --------------------------------------------------------------------
2886
         */
2887
        /*      Process SimpleSources. */
2888
        /* --------------------------------------------------------------------
2889
         */
2890
        for (int iSource = 0; iSource < nSources; iSource++)
2✔
2891
        {
2892
            CPLXMLNode *const psXMLSrc =
2893
                papoSources[iSource]->SerializeToXML(nullptr);
1✔
2894
            if (psXMLSrc == nullptr)
1✔
2895
                continue;
×
2896

2897
            char *const pszXML = CPLSerializeXMLTree(psXMLSrc);
1✔
2898

2899
            m_papszSourceList = CSLSetNameValue(
1✔
2900
                m_papszSourceList, CPLSPrintf("source_%d", iSource), pszXML);
2901
            CPLFree(pszXML);
1✔
2902
            CPLDestroyXMLNode(psXMLSrc);
1✔
2903
        }
2904

2905
        return m_papszSourceList;
1✔
2906
    }
2907

2908
    /* ==================================================================== */
2909
    /*      Other domains.                                                  */
2910
    /* ==================================================================== */
2911

2912
    return GDALRasterBand::GetMetadata(pszDomain);
13,950✔
2913
}
2914

2915
/************************************************************************/
2916
/*                          SetMetadataItem()                           */
2917
/************************************************************************/
2918

2919
CPLErr VRTSourcedRasterBand::SetMetadataItem(const char *pszName,
133,480✔
2920
                                             const char *pszValue,
2921
                                             const char *pszDomain)
2922

2923
{
2924
#if DEBUG_VERBOSE
2925
    CPLDebug("VRT", "VRTSourcedRasterBand::SetMetadataItem(%s,%s,%s)\n",
2926
             pszName, pszValue ? pszValue : "(null)",
2927
             pszDomain ? pszDomain : "(null)");
2928
#endif
2929

2930
    if (pszDomain != nullptr && EQUAL(pszDomain, "new_vrt_sources"))
133,480✔
2931
    {
2932
        VRTDriver *const poDriver =
2933
            static_cast<VRTDriver *>(GDALGetDriverByName("VRT"));
1✔
2934

2935
        CPLXMLNode *const psTree = CPLParseXMLString(pszValue);
1✔
2936
        if (psTree == nullptr)
1✔
2937
            return CE_Failure;
×
2938

2939
        auto l_poDS = dynamic_cast<VRTDataset *>(GetDataset());
1✔
2940
        if (l_poDS == nullptr)
1✔
2941
        {
2942
            CPLDestroyXMLNode(psTree);
×
2943
            return CE_Failure;
×
2944
        }
2945
        VRTSource *const poSource =
2946
            poDriver->ParseSource(psTree, nullptr, l_poDS->m_oMapSharedSources);
1✔
2947
        CPLDestroyXMLNode(psTree);
1✔
2948

2949
        if (poSource != nullptr)
1✔
2950
            return AddSource(poSource);
1✔
2951

2952
        return CE_Failure;
×
2953
    }
2954
    else if (pszDomain != nullptr && EQUAL(pszDomain, "vrt_sources"))
133,479✔
2955
    {
2956
        int iSource = 0;
1✔
2957
        // TODO(schwehr): Replace sscanf.
2958
        if (sscanf(pszName, "source_%d", &iSource) != 1 || iSource < 0 ||
2✔
2959
            iSource >= nSources)
1✔
2960
        {
2961
            CPLError(CE_Failure, CPLE_AppDefined,
×
2962
                     "%s metadata item name is not recognized. "
2963
                     "Should be between source_0 and source_%d",
2964
                     pszName, nSources - 1);
×
2965
            return CE_Failure;
×
2966
        }
2967

2968
        VRTDriver *const poDriver =
2969
            static_cast<VRTDriver *>(GDALGetDriverByName("VRT"));
1✔
2970

2971
        CPLXMLNode *const psTree = CPLParseXMLString(pszValue);
1✔
2972
        if (psTree == nullptr)
1✔
2973
            return CE_Failure;
×
2974

2975
        auto l_poDS = dynamic_cast<VRTDataset *>(GetDataset());
1✔
2976
        if (l_poDS == nullptr)
1✔
2977
        {
2978
            CPLDestroyXMLNode(psTree);
×
2979
            return CE_Failure;
×
2980
        }
2981
        VRTSource *const poSource =
2982
            poDriver->ParseSource(psTree, nullptr, l_poDS->m_oMapSharedSources);
1✔
2983
        CPLDestroyXMLNode(psTree);
1✔
2984

2985
        if (poSource != nullptr)
1✔
2986
        {
2987
            delete papoSources[iSource];
1✔
2988
            papoSources[iSource] = poSource;
1✔
2989
            static_cast<VRTDataset *>(poDS)->SetNeedsFlush();
1✔
2990
            return CE_None;
1✔
2991
        }
2992

2993
        return CE_Failure;
×
2994
    }
2995

2996
    return VRTRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
133,478✔
2997
}
2998

2999
/************************************************************************/
3000
/*                            SetMetadata()                             */
3001
/************************************************************************/
3002

3003
CPLErr VRTSourcedRasterBand::SetMetadata(char **papszNewMD,
70,171✔
3004
                                         const char *pszDomain)
3005

3006
{
3007
    if (pszDomain != nullptr && (EQUAL(pszDomain, "new_vrt_sources") ||
70,171✔
3008
                                 EQUAL(pszDomain, "vrt_sources")))
70,171✔
3009
    {
3010
        VRTDriver *const poDriver =
3011
            static_cast<VRTDriver *>(GDALGetDriverByName("VRT"));
8✔
3012

3013
        if (EQUAL(pszDomain, "vrt_sources"))
8✔
3014
        {
3015
            for (int i = 0; i < nSources; i++)
8✔
3016
                delete papoSources[i];
×
3017
            CPLFree(papoSources);
8✔
3018
            papoSources = nullptr;
8✔
3019
            nSources = 0;
8✔
3020
        }
3021

3022
        for (const char *const pszMDItem :
6✔
3023
             cpl::Iterate(CSLConstList(papszNewMD)))
20✔
3024
        {
3025
            const char *const pszXML = CPLParseNameValue(pszMDItem, nullptr);
8✔
3026
            CPLXMLTreeCloser psTree(CPLParseXMLString(pszXML));
8✔
3027
            if (!psTree)
8✔
3028
                return CE_Failure;
×
3029

3030
            auto l_poDS = dynamic_cast<VRTDataset *>(GetDataset());
8✔
3031
            if (l_poDS == nullptr)
8✔
3032
            {
3033
                return CE_Failure;
×
3034
            }
3035
            VRTSource *const poSource = poDriver->ParseSource(
8✔
3036
                psTree.get(), nullptr, l_poDS->m_oMapSharedSources);
8✔
3037
            if (poSource == nullptr)
8✔
3038
                return CE_Failure;
2✔
3039

3040
            const CPLErr eErr = AddSource(poSource);
6✔
3041
            // cppcheck-suppress knownConditionTrueFalse
3042
            if (eErr != CE_None)
6✔
3043
                return eErr;
×
3044
        }
3045

3046
        return CE_None;
6✔
3047
    }
3048

3049
    return VRTRasterBand::SetMetadata(papszNewMD, pszDomain);
70,163✔
3050
}
3051

3052
/************************************************************************/
3053
/*                             GetFileList()                            */
3054
/************************************************************************/
3055

3056
void VRTSourcedRasterBand::GetFileList(char ***ppapszFileList, int *pnSize,
67✔
3057
                                       int *pnMaxSize, CPLHashSet *hSetFiles)
3058
{
3059
    for (int i = 0; i < nSources; i++)
178✔
3060
    {
3061
        papoSources[i]->GetFileList(ppapszFileList, pnSize, pnMaxSize,
111✔
3062
                                    hSetFiles);
111✔
3063
    }
3064

3065
    VRTRasterBand::GetFileList(ppapszFileList, pnSize, pnMaxSize, hSetFiles);
67✔
3066
}
67✔
3067

3068
/************************************************************************/
3069
/*                        CloseDependentDatasets()                      */
3070
/************************************************************************/
3071

3072
int VRTSourcedRasterBand::CloseDependentDatasets()
139,437✔
3073
{
3074
    int ret = VRTRasterBand::CloseDependentDatasets();
139,437✔
3075

3076
    if (nSources == 0)
139,437✔
3077
        return ret;
411✔
3078

3079
    for (int i = 0; i < nSources; i++)
283,838✔
3080
        delete papoSources[i];
144,812✔
3081

3082
    CPLFree(papoSources);
139,026✔
3083
    papoSources = nullptr;
139,026✔
3084
    nSources = 0;
139,026✔
3085

3086
    return TRUE;
139,026✔
3087
}
3088

3089
/************************************************************************/
3090
/*                               FlushCache()                           */
3091
/************************************************************************/
3092

3093
CPLErr VRTSourcedRasterBand::FlushCache(bool bAtClosing)
205,424✔
3094
{
3095
    CPLErr eErr = VRTRasterBand::FlushCache(bAtClosing);
205,424✔
3096
    for (int i = 0; i < nSources && eErr == CE_None; i++)
416,249✔
3097
    {
3098
        eErr = papoSources[i]->FlushCache(bAtClosing);
210,825✔
3099
    }
3100
    return eErr;
205,424✔
3101
}
3102

3103
/************************************************************************/
3104
/*                           RemoveCoveredSources()                     */
3105
/************************************************************************/
3106

3107
/** Remove sources that are covered by other sources.
3108
 *
3109
 * This method removes sources that are covered entirely by (one or several)
3110
 * sources of higher priority (even if they declare a nodata setting).
3111
 * This optimizes the size of the VRT and the rendering time.
3112
 */
3113
void VRTSourcedRasterBand::RemoveCoveredSources(CSLConstList papszOptions)
14✔
3114
{
3115
#ifndef HAVE_GEOS
3116
    if (CPLTestBool(CSLFetchNameValueDef(
3117
            papszOptions, "EMIT_ERROR_IF_GEOS_NOT_AVAILABLE", "TRUE")))
3118
    {
3119
        CPLError(CE_Failure, CPLE_NotSupported,
3120
                 "RemoveCoveredSources() not implemented in builds "
3121
                 "without GEOS support");
3122
    }
3123
#else
3124
    (void)papszOptions;
3125

3126
    CPLRectObj globalBounds;
3127
    globalBounds.minx = 0;
14✔
3128
    globalBounds.miny = 0;
14✔
3129
    globalBounds.maxx = nRasterXSize;
14✔
3130
    globalBounds.maxy = nRasterYSize;
14✔
3131

3132
    // Create an index with the bbox of all sources
3133
    CPLQuadTree *hTree = CPLQuadTreeCreate(&globalBounds, nullptr);
14✔
3134
    for (int i = 0; i < nSources; i++)
38✔
3135
    {
3136
        if (papoSources[i]->IsSimpleSource())
24✔
3137
        {
3138
            VRTSimpleSource *poSS =
3139
                cpl::down_cast<VRTSimpleSource *>(papoSources[i]);
24✔
3140
            void *hFeature =
24✔
3141
                reinterpret_cast<void *>(static_cast<uintptr_t>(i));
24✔
3142
            CPLRectObj rect;
3143
            rect.minx = std::max(0.0, poSS->m_dfDstXOff);
24✔
3144
            rect.miny = std::max(0.0, poSS->m_dfDstYOff);
24✔
3145
            rect.maxx = std::min(double(nRasterXSize),
48✔
3146
                                 poSS->m_dfDstXOff + poSS->m_dfDstXSize);
24✔
3147
            rect.maxy = std::min(double(nRasterYSize),
48✔
3148
                                 poSS->m_dfDstYOff + poSS->m_dfDstYSize);
24✔
3149
            CPLQuadTreeInsertWithBounds(hTree, hFeature, &rect);
24✔
3150
        }
3151
    }
3152

3153
    for (int i = 0; i < nSources; i++)
38✔
3154
    {
3155
        if (papoSources[i]->IsSimpleSource())
24✔
3156
        {
3157
            VRTSimpleSource *poSS =
3158
                cpl::down_cast<VRTSimpleSource *>(papoSources[i]);
24✔
3159
            CPLRectObj rect;
3160
            rect.minx = std::max(0.0, poSS->m_dfDstXOff);
24✔
3161
            rect.miny = std::max(0.0, poSS->m_dfDstYOff);
24✔
3162
            rect.maxx = std::min(double(nRasterXSize),
48✔
3163
                                 poSS->m_dfDstXOff + poSS->m_dfDstXSize);
24✔
3164
            rect.maxy = std::min(double(nRasterYSize),
48✔
3165
                                 poSS->m_dfDstYOff + poSS->m_dfDstYSize);
24✔
3166

3167
            // Find sources whose extent intersect with the current one
3168
            int nFeatureCount = 0;
24✔
3169
            void **pahFeatures =
3170
                CPLQuadTreeSearch(hTree, &rect, &nFeatureCount);
24✔
3171

3172
            // Compute the bounding box of those sources, only if they are
3173
            // on top of the current one
3174
            CPLRectObj rectIntersecting;
3175
            rectIntersecting.minx = std::numeric_limits<double>::max();
24✔
3176
            rectIntersecting.miny = std::numeric_limits<double>::max();
24✔
3177
            rectIntersecting.maxx = -std::numeric_limits<double>::max();
24✔
3178
            rectIntersecting.maxy = -std::numeric_limits<double>::max();
24✔
3179
            for (int j = 0; j < nFeatureCount; j++)
61✔
3180
            {
3181
                const int curFeature = static_cast<int>(
37✔
3182
                    reinterpret_cast<uintptr_t>(pahFeatures[j]));
37✔
3183
                if (curFeature > i)
37✔
3184
                {
3185
                    VRTSimpleSource *poOtherSS =
3186
                        cpl::down_cast<VRTSimpleSource *>(
26✔
3187
                            papoSources[curFeature]);
13✔
3188
                    rectIntersecting.minx =
13✔
3189
                        std::min(rectIntersecting.minx, poOtherSS->m_dfDstXOff);
13✔
3190
                    rectIntersecting.miny =
13✔
3191
                        std::min(rectIntersecting.miny, poOtherSS->m_dfDstYOff);
13✔
3192
                    rectIntersecting.maxx = std::max(
13✔
3193
                        rectIntersecting.maxx,
3194
                        poOtherSS->m_dfDstXOff + poOtherSS->m_dfDstXSize);
13✔
3195
                    rectIntersecting.maxy = std::max(
13✔
3196
                        rectIntersecting.maxy,
3197
                        poOtherSS->m_dfDstYOff + poOtherSS->m_dfDstXSize);
13✔
3198
                }
3199
            }
3200

3201
            // If the boundinx box of those sources overlap the current one,
3202
            // then compute their union, and check if it contains the current
3203
            // source
3204
            if (rectIntersecting.minx <= rect.minx &&
24✔
3205
                rectIntersecting.miny <= rect.miny &&
7✔
3206
                rectIntersecting.maxx >= rect.maxx &&
7✔
3207
                rectIntersecting.maxy >= rect.maxy)
7✔
3208
            {
3209
                OGRPolygon oPoly;
14✔
3210
                {
3211
                    auto poLR = new OGRLinearRing();
7✔
3212
                    poLR->addPoint(rect.minx, rect.miny);
7✔
3213
                    poLR->addPoint(rect.minx, rect.maxy);
7✔
3214
                    poLR->addPoint(rect.maxx, rect.maxy);
7✔
3215
                    poLR->addPoint(rect.maxx, rect.miny);
7✔
3216
                    poLR->addPoint(rect.minx, rect.miny);
7✔
3217
                    oPoly.addRingDirectly(poLR);
7✔
3218
                }
3219

3220
                std::unique_ptr<OGRGeometry> poUnion;
7✔
3221
                for (int j = 0; j < nFeatureCount; j++)
24✔
3222
                {
3223
                    const int curFeature = static_cast<int>(
17✔
3224
                        reinterpret_cast<uintptr_t>(pahFeatures[j]));
17✔
3225
                    if (curFeature > i)
17✔
3226
                    {
3227
                        VRTSimpleSource *poOtherSS =
3228
                            cpl::down_cast<VRTSimpleSource *>(
20✔
3229
                                papoSources[curFeature]);
10✔
3230
                        CPLRectObj otherRect;
3231
                        otherRect.minx = std::max(0.0, poOtherSS->m_dfDstXOff);
10✔
3232
                        otherRect.miny = std::max(0.0, poOtherSS->m_dfDstYOff);
10✔
3233
                        otherRect.maxx = std::min(double(nRasterXSize),
20✔
3234
                                                  poOtherSS->m_dfDstXOff +
20✔
3235
                                                      poOtherSS->m_dfDstXSize);
10✔
3236
                        otherRect.maxy = std::min(double(nRasterYSize),
20✔
3237
                                                  poOtherSS->m_dfDstYOff +
20✔
3238
                                                      poOtherSS->m_dfDstYSize);
10✔
3239
                        OGRPolygon oOtherPoly;
20✔
3240
                        {
3241
                            auto poLR = new OGRLinearRing();
10✔
3242
                            poLR->addPoint(otherRect.minx, otherRect.miny);
10✔
3243
                            poLR->addPoint(otherRect.minx, otherRect.maxy);
10✔
3244
                            poLR->addPoint(otherRect.maxx, otherRect.maxy);
10✔
3245
                            poLR->addPoint(otherRect.maxx, otherRect.miny);
10✔
3246
                            poLR->addPoint(otherRect.minx, otherRect.miny);
10✔
3247
                            oOtherPoly.addRingDirectly(poLR);
10✔
3248
                        }
3249
                        if (poUnion == nullptr)
10✔
3250
                            poUnion.reset(oOtherPoly.clone());
7✔
3251
                        else
3252
                            poUnion.reset(oOtherPoly.Union(poUnion.get()));
3✔
3253
                    }
3254
                }
3255

3256
                if (poUnion != nullptr && poUnion->Contains(&oPoly))
7✔
3257
                {
3258
                    // We can remove the current source
3259
                    delete papoSources[i];
7✔
3260
                    papoSources[i] = nullptr;
7✔
3261
                }
3262
            }
3263
            CPLFree(pahFeatures);
24✔
3264

3265
            void *hFeature =
24✔
3266
                reinterpret_cast<void *>(static_cast<uintptr_t>(i));
24✔
3267
            CPLQuadTreeRemove(hTree, hFeature, &rect);
24✔
3268
        }
3269
    }
3270

3271
    // Compact the papoSources array
3272
    int iDst = 0;
14✔
3273
    for (int iSrc = 0; iSrc < nSources; iSrc++)
38✔
3274
    {
3275
        if (papoSources[iSrc])
24✔
3276
            papoSources[iDst++] = papoSources[iSrc];
17✔
3277
    }
3278
    nSources = iDst;
14✔
3279

3280
    CPLQuadTreeDestroy(hTree);
14✔
3281
#endif
3282
}
14✔
3283

3284
/*! @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