Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/vrt/vrtsourcedrasterband.cpp
Line
Count
Source
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 "gdalantirecursion.h"
41
#include "gdal_priv.h"
42
#include "gdal_thread_pool.h"
43
#include "ogr_geometry.h"
44
45
/*! @cond Doxygen_Suppress */
46
47
/************************************************************************/
48
/* ==================================================================== */
49
/*                          VRTSourcedRasterBand                        */
50
/* ==================================================================== */
51
/************************************************************************/
52
53
/************************************************************************/
54
/*                        VRTSourcedRasterBand()                        */
55
/************************************************************************/
56
57
VRTSourcedRasterBand::VRTSourcedRasterBand(GDALDataset *poDSIn, int nBandIn)
58
21.9k
{
59
21.9k
    VRTRasterBand::Initialize(poDSIn->GetRasterXSize(),
60
21.9k
                              poDSIn->GetRasterYSize());
61
62
21.9k
    poDS = poDSIn;
63
21.9k
    nBand = nBandIn;
64
21.9k
}
65
66
/************************************************************************/
67
/*                        VRTSourcedRasterBand()                        */
68
/************************************************************************/
69
70
VRTSourcedRasterBand::VRTSourcedRasterBand(GDALDataType eType, int nXSize,
71
                                           int nYSize)
72
0
{
73
0
    VRTRasterBand::Initialize(nXSize, nYSize);
74
75
0
    eDataType = eType;
76
0
}
77
78
/************************************************************************/
79
/*                        VRTSourcedRasterBand()                        */
80
/************************************************************************/
81
82
VRTSourcedRasterBand::VRTSourcedRasterBand(GDALDataset *poDSIn, int nBandIn,
83
                                           GDALDataType eType, int nXSize,
84
                                           int nYSize)
85
279
    : VRTSourcedRasterBand(poDSIn, nBandIn, eType, nXSize, nYSize, 0, 0)
86
279
{
87
279
}
88
89
/************************************************************************/
90
/*                        VRTSourcedRasterBand()                        */
91
/************************************************************************/
92
93
VRTSourcedRasterBand::VRTSourcedRasterBand(GDALDataset *poDSIn, int nBandIn,
94
                                           GDALDataType eType, int nXSize,
95
                                           int nYSize, int nBlockXSizeIn,
96
                                           int nBlockYSizeIn)
97
150k
{
98
150k
    VRTRasterBand::Initialize(nXSize, nYSize);
99
100
150k
    poDS = poDSIn;
101
150k
    nBand = nBandIn;
102
103
150k
    eDataType = eType;
104
150k
    if (nBlockXSizeIn > 0)
105
149k
        nBlockXSize = nBlockXSizeIn;
106
150k
    if (nBlockYSizeIn > 0)
107
149k
        nBlockYSize = nBlockYSizeIn;
108
150k
}
109
110
/************************************************************************/
111
/*                       ~VRTSourcedRasterBand()                        */
112
/************************************************************************/
113
114
VRTSourcedRasterBand::~VRTSourcedRasterBand()
115
116
172k
{
117
172k
    VRTSourcedRasterBand::CloseDependentDatasets();
118
172k
}
119
120
/************************************************************************/
121
/*                  CanIRasterIOBeForwardedToEachSource()               */
122
/************************************************************************/
123
124
bool VRTSourcedRasterBand::CanIRasterIOBeForwardedToEachSource(
125
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
126
    int nBufXSize, int nBufYSize, GDALRasterIOExtraArg *psExtraArg) const
127
569k
{
128
569k
    const auto IsNonNearestInvolved = [this, psExtraArg]
129
569k
    {
130
22
        if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
131
0
        {
132
0
            return true;
133
0
        }
134
22
        for (auto &poSource : m_papoSources)
135
22
        {
136
22
            if (poSource->GetType() == VRTComplexSource::GetTypeStatic())
137
0
            {
138
0
                auto *const poComplexSource =
139
0
                    static_cast<VRTComplexSource *>(poSource.get());
140
0
                const auto &osSourceResampling =
141
0
                    poComplexSource->GetResampling();
142
0
                if (!osSourceResampling.empty() &&
143
0
                    osSourceResampling != "nearest")
144
0
                    return true;
145
0
            }
146
22
        }
147
22
        return false;
148
22
    };
149
150
    // If resampling with non-nearest neighbour, we need to be careful
151
    // if the VRT band exposes a nodata value, but the sources do not have it.
152
    // To also avoid edge effects on sources when downsampling, use the
153
    // base implementation of IRasterIO() (that is acquiring sources at their
154
    // nominal resolution, and then downsampling), but only if none of the
155
    // contributing sources have overviews.
156
569k
    if (eRWFlag == GF_Read && (nXSize != nBufXSize || nYSize != nBufYSize) &&
157
25
        !m_papoSources.empty() && IsNonNearestInvolved())
158
0
    {
159
0
        bool bSourceHasOverviews = false;
160
0
        const bool bIsDownsampling = (nBufXSize < nXSize && nBufYSize < nYSize);
161
0
        int nContributingSources = 0;
162
0
        bool bSourceFullySatisfiesRequest = true;
163
0
        for (auto &poSource : m_papoSources)
164
0
        {
165
0
            if (!poSource->IsSimpleSource())
166
0
            {
167
0
                return false;
168
0
            }
169
0
            else
170
0
            {
171
0
                VRTSimpleSource *const poSimpleSource =
172
0
                    static_cast<VRTSimpleSource *>(poSource.get());
173
174
0
                if (poSimpleSource->GetType() ==
175
0
                    VRTComplexSource::GetTypeStatic())
176
0
                {
177
0
                    auto *const poComplexSource =
178
0
                        static_cast<VRTComplexSource *>(poSimpleSource);
179
0
                    const auto &osSourceResampling =
180
0
                        poComplexSource->GetResampling();
181
0
                    if (!osSourceResampling.empty() &&
182
0
                        osSourceResampling != "nearest")
183
0
                    {
184
0
                        const int lMaskFlags =
185
0
                            const_cast<VRTSourcedRasterBand *>(this)
186
0
                                ->GetMaskFlags();
187
0
                        if ((lMaskFlags != GMF_ALL_VALID &&
188
0
                             lMaskFlags != GMF_NODATA) ||
189
0
                            IsMaskBand())
190
0
                        {
191
                            // Unfortunately this will prevent using overviews
192
                            // of the sources, but it is unpractical to use
193
                            // them without serious implementation complications
194
0
                            return false;
195
0
                        }
196
0
                    }
197
0
                }
198
199
0
                double dfXOff = nXOff;
200
0
                double dfYOff = nYOff;
201
0
                double dfXSize = nXSize;
202
0
                double dfYSize = nYSize;
203
0
                if (psExtraArg->bFloatingPointWindowValidity)
204
0
                {
205
0
                    dfXOff = psExtraArg->dfXOff;
206
0
                    dfYOff = psExtraArg->dfYOff;
207
0
                    dfXSize = psExtraArg->dfXSize;
208
0
                    dfYSize = psExtraArg->dfYSize;
209
0
                }
210
211
                // The window we will actually request from the source raster
212
                // band.
213
0
                double dfReqXOff = 0.0;
214
0
                double dfReqYOff = 0.0;
215
0
                double dfReqXSize = 0.0;
216
0
                double dfReqYSize = 0.0;
217
0
                int nReqXOff = 0;
218
0
                int nReqYOff = 0;
219
0
                int nReqXSize = 0;
220
0
                int nReqYSize = 0;
221
222
                // The window we will actual set _within_ the pData buffer.
223
0
                int nOutXOff = 0;
224
0
                int nOutYOff = 0;
225
0
                int nOutXSize = 0;
226
0
                int nOutYSize = 0;
227
228
0
                bool bError = false;
229
0
                if (!poSimpleSource->GetSrcDstWindow(
230
0
                        dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
231
0
                        &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize,
232
0
                        &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff,
233
0
                        &nOutYOff, &nOutXSize, &nOutYSize, bError))
234
0
                {
235
0
                    continue;
236
0
                }
237
0
                auto poBand = poSimpleSource->GetRasterBand();
238
0
                if (poBand == nullptr)
239
0
                {
240
0
                    return false;
241
0
                }
242
0
                ++nContributingSources;
243
0
                if (!(nOutXOff == 0 && nOutYOff == 0 &&
244
0
                      nOutXSize == nBufXSize && nOutYSize == nBufYSize))
245
0
                    bSourceFullySatisfiesRequest = false;
246
0
                if (m_bNoDataValueSet)
247
0
                {
248
0
                    int bSrcHasNoData = FALSE;
249
0
                    const double dfSrcNoData =
250
0
                        poBand->GetNoDataValue(&bSrcHasNoData);
251
0
                    if (!bSrcHasNoData || dfSrcNoData != m_dfNoDataValue)
252
0
                    {
253
0
                        return false;
254
0
                    }
255
0
                }
256
0
                if (bIsDownsampling)
257
0
                {
258
0
                    if (poBand->GetOverviewCount() != 0)
259
0
                    {
260
0
                        bSourceHasOverviews = true;
261
0
                    }
262
0
                }
263
0
            }
264
0
        }
265
0
        if (bIsDownsampling && !bSourceHasOverviews &&
266
0
            (nContributingSources > 1 || !bSourceFullySatisfiesRequest))
267
0
        {
268
0
            return false;
269
0
        }
270
0
    }
271
569k
    return true;
272
569k
}
273
274
/************************************************************************/
275
/*                      CanMultiThreadRasterIO()                        */
276
/************************************************************************/
277
278
bool VRTSourcedRasterBand::CanMultiThreadRasterIO(
279
    double dfXOff, double dfYOff, double dfXSize, double dfYSize,
280
    int &nContributingSources) const
281
48
{
282
48
    int iLastSource = 0;
283
48
    CPLRectObj sSourceBounds;
284
48
    CPLQuadTree *hQuadTree = nullptr;
285
48
    bool bRet = true;
286
48
    std::set<std::string> oSetDSName;
287
288
48
    nContributingSources = 0;
289
87
    for (int iSource = 0; iSource < static_cast<int>(m_papoSources.size());
290
48
         iSource++)
291
39
    {
292
39
        const auto &poSource = m_papoSources[iSource];
293
39
        if (!poSource->IsSimpleSource())
294
0
        {
295
0
            bRet = false;
296
0
            break;
297
0
        }
298
39
        const auto poSimpleSource =
299
39
            cpl::down_cast<VRTSimpleSource *>(poSource.get());
300
39
        if (poSimpleSource->DstWindowIntersects(dfXOff, dfYOff, dfXSize,
301
39
                                                dfYSize))
302
39
        {
303
            // Only build hQuadTree if there are 2 or more sources
304
39
            if (nContributingSources == 1)
305
0
            {
306
0
                std::string &oFirstSrcDSName =
307
0
                    cpl::down_cast<VRTSimpleSource *>(
308
0
                        m_papoSources[iLastSource].get())
309
0
                        ->m_osSrcDSName;
310
0
                oSetDSName.insert(oFirstSrcDSName);
311
312
0
                CPLRectObj sGlobalBounds;
313
0
                sGlobalBounds.minx = dfXOff;
314
0
                sGlobalBounds.miny = dfYOff;
315
0
                sGlobalBounds.maxx = dfXOff + dfXSize;
316
0
                sGlobalBounds.maxy = dfYOff + dfYSize;
317
0
                hQuadTree = CPLQuadTreeCreate(&sGlobalBounds, nullptr);
318
319
0
                CPLQuadTreeInsertWithBounds(
320
0
                    hQuadTree,
321
0
                    reinterpret_cast<void *>(
322
0
                        static_cast<uintptr_t>(iLastSource)),
323
0
                    &sSourceBounds);
324
0
            }
325
326
            // Check there are not several sources with the same name, to avoid
327
            // the same GDALDataset* to be used from multiple threads. We may
328
            // be a bit too pessimistic, for example if working with unnamed
329
            // Memory datasets, but that would involve comparing
330
            // poSource->GetRasterBandNoOpen()->GetDataset()
331
39
            if (oSetDSName.find(poSimpleSource->m_osSrcDSName) !=
332
39
                oSetDSName.end())
333
0
            {
334
0
                bRet = false;
335
0
                break;
336
0
            }
337
39
            oSetDSName.insert(poSimpleSource->m_osSrcDSName);
338
339
39
            double dfSourceXOff;
340
39
            double dfSourceYOff;
341
39
            double dfSourceXSize;
342
39
            double dfSourceYSize;
343
39
            poSimpleSource->GetDstWindow(dfSourceXOff, dfSourceYOff,
344
39
                                         dfSourceXSize, dfSourceYSize);
345
39
            constexpr double EPSILON = 1e-1;
346
39
            sSourceBounds.minx = dfSourceXOff + EPSILON;
347
39
            sSourceBounds.miny = dfSourceYOff + EPSILON;
348
39
            sSourceBounds.maxx = dfSourceXOff + dfSourceXSize - EPSILON;
349
39
            sSourceBounds.maxy = dfSourceYOff + dfSourceYSize - EPSILON;
350
39
            iLastSource = iSource;
351
352
39
            if (hQuadTree)
353
0
            {
354
                // Check that the new source doesn't overlap an existing one.
355
0
                if (CPLQuadTreeHasMatch(hQuadTree, &sSourceBounds))
356
0
                {
357
0
                    bRet = false;
358
0
                    break;
359
0
                }
360
361
0
                CPLQuadTreeInsertWithBounds(
362
0
                    hQuadTree,
363
0
                    reinterpret_cast<void *>(static_cast<uintptr_t>(iSource)),
364
0
                    &sSourceBounds);
365
0
            }
366
367
39
            ++nContributingSources;
368
39
        }
369
39
    }
370
371
48
    if (hQuadTree)
372
0
        CPLQuadTreeDestroy(hQuadTree);
373
374
48
    return bRet;
375
48
}
376
377
/************************************************************************/
378
/*                 VRTSourcedRasterBandRasterIOJob                      */
379
/************************************************************************/
380
381
/** Structure used to declare a threaded job to satisfy IRasterIO()
382
 * on a given source.
383
 */
384
struct VRTSourcedRasterBandRasterIOJob
385
{
386
    std::atomic<int> *pnCompletedJobs = nullptr;
387
    std::atomic<bool> *pbSuccess = nullptr;
388
    VRTDataset::QueueWorkingStates *poQueueWorkingStates = nullptr;
389
    CPLErrorAccumulator *poErrorAccumulator = nullptr;
390
391
    GDALDataType eVRTBandDataType = GDT_Unknown;
392
    int nXOff = 0;
393
    int nYOff = 0;
394
    int nXSize = 0;
395
    int nYSize = 0;
396
    void *pData = nullptr;
397
    int nBufXSize = 0;
398
    int nBufYSize = 0;
399
    GDALDataType eBufType = GDT_Unknown;
400
    GSpacing nPixelSpace = 0;
401
    GSpacing nLineSpace = 0;
402
    GDALRasterIOExtraArg *psExtraArg = nullptr;
403
    VRTSimpleSource *poSource = nullptr;
404
405
    static void Func(void *pData);
406
};
407
408
/************************************************************************/
409
/*                 VRTSourcedRasterBandRasterIOJob::Func()              */
410
/************************************************************************/
411
412
void VRTSourcedRasterBandRasterIOJob::Func(void *pData)
413
0
{
414
0
    auto psJob = std::unique_ptr<VRTSourcedRasterBandRasterIOJob>(
415
0
        static_cast<VRTSourcedRasterBandRasterIOJob *>(pData));
416
0
    if (*psJob->pbSuccess)
417
0
    {
418
0
        GDALRasterIOExtraArg sArg = *(psJob->psExtraArg);
419
0
        sArg.pfnProgress = nullptr;
420
0
        sArg.pProgressData = nullptr;
421
422
0
        std::unique_ptr<VRTSource::WorkingState> poWorkingState;
423
0
        {
424
0
            std::lock_guard oLock(psJob->poQueueWorkingStates->oMutex);
425
0
            poWorkingState =
426
0
                std::move(psJob->poQueueWorkingStates->oStates.back());
427
0
            psJob->poQueueWorkingStates->oStates.pop_back();
428
0
            CPLAssert(poWorkingState.get());
429
0
        }
430
431
0
        auto oAccumulator = psJob->poErrorAccumulator->InstallForCurrentScope();
432
0
        CPL_IGNORE_RET_VAL(oAccumulator);
433
434
0
        if (psJob->poSource->RasterIO(
435
0
                psJob->eVRTBandDataType, psJob->nXOff, psJob->nYOff,
436
0
                psJob->nXSize, psJob->nYSize, psJob->pData, psJob->nBufXSize,
437
0
                psJob->nBufYSize, psJob->eBufType, psJob->nPixelSpace,
438
0
                psJob->nLineSpace, &sArg, *(poWorkingState.get())) != CE_None)
439
0
        {
440
0
            *psJob->pbSuccess = false;
441
0
        }
442
443
0
        {
444
0
            std::lock_guard oLock(psJob->poQueueWorkingStates->oMutex);
445
0
            psJob->poQueueWorkingStates->oStates.push_back(
446
0
                std::move(poWorkingState));
447
0
        }
448
0
    }
449
450
0
    ++(*psJob->pnCompletedJobs);
451
0
}
452
453
/************************************************************************/
454
/*                             IRasterIO()                              */
455
/************************************************************************/
456
457
CPLErr VRTSourcedRasterBand::IRasterIO(
458
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
459
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
460
    GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
461
462
569k
{
463
569k
    if (eRWFlag == GF_Write)
464
0
    {
465
0
        CPLError(CE_Failure, CPLE_AppDefined,
466
0
                 "Writing through VRTSourcedRasterBand is not supported.");
467
0
        return CE_Failure;
468
0
    }
469
470
569k
    const std::string osFctId("VRTSourcedRasterBand::IRasterIO");
471
569k
    GDALAntiRecursionGuard oGuard(osFctId);
472
569k
    if (oGuard.GetCallDepth() >= 32)
473
0
    {
474
0
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
475
0
        return CE_Failure;
476
0
    }
477
478
569k
    GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
479
    // Allow 2 recursion depths on the same dataset for non-nearest resampling
480
569k
    if (oGuard2.GetCallDepth() > 2)
481
1
    {
482
1
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
483
1
        return CE_Failure;
484
1
    }
485
486
    /* ==================================================================== */
487
    /*      Do we have overviews that would be appropriate to satisfy       */
488
    /*      this request?                                                   */
489
    /* ==================================================================== */
490
569k
    auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
491
569k
    if (l_poDS &&
492
569k
        l_poDS->m_apoOverviews.empty() &&  // do not use virtual overviews
493
569k
        (nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0)
494
0
    {
495
0
        if (OverviewRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
496
0
                             nBufXSize, nBufYSize, eBufType, nPixelSpace,
497
0
                             nLineSpace, psExtraArg) == CE_None)
498
0
            return CE_None;
499
0
    }
500
501
    // If resampling with non-nearest neighbour, we need to be careful
502
    // if the VRT band exposes a nodata value, but the sources do not have it.
503
    // To also avoid edge effects on sources when downsampling, use the
504
    // base implementation of IRasterIO() (that is acquiring sources at their
505
    // nominal resolution, and then downsampling), but only if none of the
506
    // contributing sources have overviews.
507
569k
    if (l_poDS && !CanIRasterIOBeForwardedToEachSource(
508
569k
                      eRWFlag, nXOff, nYOff, nXSize, nYSize, nBufXSize,
509
569k
                      nBufYSize, psExtraArg))
510
0
    {
511
0
        const bool bBackupEnabledOverviews = l_poDS->AreOverviewsEnabled();
512
0
        if (!l_poDS->m_apoOverviews.empty() && l_poDS->AreOverviewsEnabled())
513
0
        {
514
            // Disable use of implicit overviews to avoid infinite
515
            // recursion
516
0
            l_poDS->SetEnableOverviews(false);
517
0
        }
518
519
0
        const auto eResampleAlgBackup = psExtraArg->eResampleAlg;
520
0
        if (psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
521
0
        {
522
0
            std::string osResampling;
523
0
            for (auto &poSource : m_papoSources)
524
0
            {
525
0
                if (poSource->GetType() == VRTComplexSource::GetTypeStatic())
526
0
                {
527
0
                    auto *const poComplexSource =
528
0
                        static_cast<VRTComplexSource *>(poSource.get());
529
0
                    if (!poComplexSource->GetResampling().empty())
530
0
                    {
531
0
                        if (osResampling.empty())
532
0
                            osResampling = poComplexSource->GetResampling();
533
0
                        else if (osResampling !=
534
0
                                 poComplexSource->GetResampling())
535
0
                        {
536
0
                            osResampling.clear();
537
0
                            break;
538
0
                        }
539
0
                    }
540
0
                }
541
0
            }
542
0
            if (!osResampling.empty())
543
0
                psExtraArg->eResampleAlg =
544
0
                    GDALRasterIOGetResampleAlg(osResampling.c_str());
545
0
        }
546
547
0
        const auto eErr = GDALRasterBand::IRasterIO(
548
0
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
549
0
            eBufType, nPixelSpace, nLineSpace, psExtraArg);
550
551
0
        psExtraArg->eResampleAlg = eResampleAlgBackup;
552
0
        l_poDS->SetEnableOverviews(bBackupEnabledOverviews);
553
0
        return eErr;
554
0
    }
555
556
    /* -------------------------------------------------------------------- */
557
    /*      Initialize the buffer to some background value. Use the         */
558
    /*      nodata value if available.                                      */
559
    /* -------------------------------------------------------------------- */
560
569k
    if (SkipBufferInitialization())
561
405k
    {
562
        // Do nothing
563
405k
    }
564
164k
    else if (nPixelSpace == GDALGetDataTypeSizeBytes(eBufType) &&
565
164k
             !(m_bNoDataValueSet && m_dfNoDataValue != 0.0) &&
566
132k
             !(m_bNoDataSetAsInt64 && m_nNoDataValueInt64 != 0) &&
567
132k
             !(m_bNoDataSetAsUInt64 && m_nNoDataValueUInt64 != 0))
568
132k
    {
569
132k
        if (nLineSpace == nBufXSize * nPixelSpace)
570
130k
        {
571
130k
            memset(pData, 0, static_cast<size_t>(nBufYSize * nLineSpace));
572
130k
        }
573
2.11k
        else
574
2.11k
        {
575
8.91k
            for (int iLine = 0; iLine < nBufYSize; iLine++)
576
6.80k
            {
577
6.80k
                memset(static_cast<GByte *>(pData) +
578
6.80k
                           static_cast<GIntBig>(iLine) * nLineSpace,
579
6.80k
                       0, static_cast<size_t>(nBufXSize * nPixelSpace));
580
6.80k
            }
581
2.11k
        }
582
132k
    }
583
31.8k
    else if (m_bNoDataSetAsInt64)
584
0
    {
585
0
        for (int iLine = 0; iLine < nBufYSize; iLine++)
586
0
        {
587
0
            GDALCopyWords(&m_nNoDataValueInt64, GDT_Int64, 0,
588
0
                          static_cast<GByte *>(pData) +
589
0
                              static_cast<GIntBig>(nLineSpace) * iLine,
590
0
                          eBufType, static_cast<int>(nPixelSpace), nBufXSize);
591
0
        }
592
0
    }
593
31.8k
    else if (m_bNoDataSetAsUInt64)
594
0
    {
595
0
        for (int iLine = 0; iLine < nBufYSize; iLine++)
596
0
        {
597
0
            GDALCopyWords(&m_nNoDataValueUInt64, GDT_UInt64, 0,
598
0
                          static_cast<GByte *>(pData) +
599
0
                              static_cast<GIntBig>(nLineSpace) * iLine,
600
0
                          eBufType, static_cast<int>(nPixelSpace), nBufXSize);
601
0
        }
602
0
    }
603
31.8k
    else
604
31.8k
    {
605
31.8k
        double dfWriteValue = 0.0;
606
31.8k
        if (m_bNoDataValueSet)
607
31.8k
            dfWriteValue = m_dfNoDataValue;
608
609
146k
        for (int iLine = 0; iLine < nBufYSize; iLine++)
610
114k
        {
611
114k
            GDALCopyWords(&dfWriteValue, GDT_Float64, 0,
612
114k
                          static_cast<GByte *>(pData) +
613
114k
                              static_cast<GIntBig>(nLineSpace) * iLine,
614
114k
                          eBufType, static_cast<int>(nPixelSpace), nBufXSize);
615
114k
        }
616
31.8k
    }
617
618
    /* -------------------------------------------------------------------- */
619
    /*      Overlay each source in turn over top this.                      */
620
    /* -------------------------------------------------------------------- */
621
569k
    CPLErr eErr = CE_None;
622
623
569k
    double dfXOff = nXOff;
624
569k
    double dfYOff = nYOff;
625
569k
    double dfXSize = nXSize;
626
569k
    double dfYSize = nYSize;
627
569k
    if (psExtraArg->bFloatingPointWindowValidity)
628
137
    {
629
137
        dfXOff = psExtraArg->dfXOff;
630
137
        dfYOff = psExtraArg->dfYOff;
631
137
        dfXSize = psExtraArg->dfXSize;
632
137
        dfYSize = psExtraArg->dfYSize;
633
137
    }
634
635
569k
    if (l_poDS)
636
569k
        l_poDS->m_bMultiThreadedRasterIOLastUsed = false;
637
638
569k
    int nContributingSources = 0;
639
569k
    int nMaxThreads = 0;
640
569k
    constexpr int MINIMUM_PIXEL_COUNT_FOR_THREADED_IO = 1000 * 1000;
641
569k
    if (l_poDS &&
642
569k
        (static_cast<int64_t>(nBufXSize) * nBufYSize >=
643
569k
             MINIMUM_PIXEL_COUNT_FOR_THREADED_IO ||
644
569k
         static_cast<int64_t>(nXSize) * nYSize >=
645
569k
             MINIMUM_PIXEL_COUNT_FOR_THREADED_IO) &&
646
48
        CanMultiThreadRasterIO(dfXOff, dfYOff, dfXSize, dfYSize,
647
48
                               nContributingSources) &&
648
48
        nContributingSources > 1 &&
649
0
        (nMaxThreads = VRTDataset::GetNumThreads(l_poDS)) > 1)
650
0
    {
651
0
        l_poDS->m_bMultiThreadedRasterIOLastUsed = true;
652
0
        l_poDS->m_oMapSharedSources.InitMutex();
653
654
0
        CPLErrorAccumulator errorAccumulator;
655
0
        std::atomic<bool> bSuccess = true;
656
0
        CPLWorkerThreadPool *psThreadPool = GDALGetGlobalThreadPool(
657
0
            std::min(nContributingSources, nMaxThreads));
658
0
        const int nThreads =
659
0
            std::min(nContributingSources, psThreadPool->GetThreadCount());
660
0
        CPLDebugOnly("VRT",
661
0
                     "IRasterIO(): use optimized "
662
0
                     "multi-threaded code path for mosaic. "
663
0
                     "Using %d threads",
664
0
                     nThreads);
665
666
0
        {
667
0
            std::lock_guard oLock(l_poDS->m_oQueueWorkingStates.oMutex);
668
0
            if (l_poDS->m_oQueueWorkingStates.oStates.size() <
669
0
                static_cast<size_t>(nThreads))
670
0
            {
671
0
                l_poDS->m_oQueueWorkingStates.oStates.resize(nThreads);
672
0
            }
673
0
            for (int i = 0; i < nThreads; ++i)
674
0
            {
675
0
                if (!l_poDS->m_oQueueWorkingStates.oStates[i])
676
0
                    l_poDS->m_oQueueWorkingStates.oStates[i] =
677
0
                        std::make_unique<VRTSource::WorkingState>();
678
0
            }
679
0
        }
680
681
0
        auto oQueue = psThreadPool->CreateJobQueue();
682
0
        std::atomic<int> nCompletedJobs = 0;
683
0
        for (auto &poSource : m_papoSources)
684
0
        {
685
0
            if (!poSource->IsSimpleSource())
686
0
                continue;
687
0
            auto poSimpleSource =
688
0
                cpl::down_cast<VRTSimpleSource *>(poSource.get());
689
0
            if (poSimpleSource->DstWindowIntersects(dfXOff, dfYOff, dfXSize,
690
0
                                                    dfYSize))
691
0
            {
692
0
                auto psJob = new VRTSourcedRasterBandRasterIOJob();
693
0
                psJob->pbSuccess = &bSuccess;
694
0
                psJob->pnCompletedJobs = &nCompletedJobs;
695
0
                psJob->poQueueWorkingStates = &(l_poDS->m_oQueueWorkingStates);
696
0
                psJob->poErrorAccumulator = &errorAccumulator;
697
0
                psJob->eVRTBandDataType = eDataType;
698
0
                psJob->nXOff = nXOff;
699
0
                psJob->nYOff = nYOff;
700
0
                psJob->nXSize = nXSize;
701
0
                psJob->nYSize = nYSize;
702
0
                psJob->pData = pData;
703
0
                psJob->nBufXSize = nBufXSize;
704
0
                psJob->nBufYSize = nBufYSize;
705
0
                psJob->eBufType = eBufType;
706
0
                psJob->nPixelSpace = nPixelSpace;
707
0
                psJob->nLineSpace = nLineSpace;
708
0
                psJob->psExtraArg = psExtraArg;
709
0
                psJob->poSource = poSimpleSource;
710
711
0
                if (!oQueue->SubmitJob(VRTSourcedRasterBandRasterIOJob::Func,
712
0
                                       psJob))
713
0
                {
714
0
                    delete psJob;
715
0
                    bSuccess = false;
716
0
                    break;
717
0
                }
718
0
            }
719
0
        }
720
721
0
        while (oQueue->WaitEvent())
722
0
        {
723
            // Quite rough progress callback. We could do better by counting
724
            // the number of contributing pixels.
725
0
            if (psExtraArg->pfnProgress)
726
0
            {
727
0
                psExtraArg->pfnProgress(double(nCompletedJobs.load()) /
728
0
                                            nContributingSources,
729
0
                                        "", psExtraArg->pProgressData);
730
0
            }
731
0
        }
732
733
0
        errorAccumulator.ReplayErrors();
734
0
        eErr = bSuccess ? CE_None : CE_Failure;
735
0
    }
736
569k
    else
737
569k
    {
738
569k
        GDALProgressFunc const pfnProgressGlobal = psExtraArg->pfnProgress;
739
569k
        void *const pProgressDataGlobal = psExtraArg->pProgressData;
740
741
569k
        VRTSource::WorkingState oWorkingState;
742
569k
        const int nSources = static_cast<int>(m_papoSources.size());
743
1.13M
        for (int iSource = 0; eErr == CE_None && iSource < nSources; iSource++)
744
569k
        {
745
569k
            psExtraArg->pfnProgress = GDALScaledProgress;
746
569k
            psExtraArg->pProgressData = GDALCreateScaledProgress(
747
569k
                1.0 * iSource / nSources, 1.0 * (iSource + 1) / nSources,
748
569k
                pfnProgressGlobal, pProgressDataGlobal);
749
569k
            if (psExtraArg->pProgressData == nullptr)
750
551k
                psExtraArg->pfnProgress = nullptr;
751
752
569k
            eErr = m_papoSources[iSource]->RasterIO(
753
569k
                eDataType, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
754
569k
                nBufYSize, eBufType, nPixelSpace, nLineSpace, psExtraArg,
755
569k
                l_poDS ? l_poDS->m_oWorkingState : oWorkingState);
756
757
569k
            GDALDestroyScaledProgress(psExtraArg->pProgressData);
758
569k
        }
759
760
569k
        psExtraArg->pfnProgress = pfnProgressGlobal;
761
569k
        psExtraArg->pProgressData = pProgressDataGlobal;
762
569k
    }
763
764
569k
    if (eErr == CE_None && psExtraArg->pfnProgress)
765
17.8k
    {
766
17.8k
        psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
767
17.8k
    }
768
769
569k
    return eErr;
770
569k
}
771
772
/************************************************************************/
773
/*                         IGetDataCoverageStatus()                     */
774
/************************************************************************/
775
776
int VRTSourcedRasterBand::IGetDataCoverageStatus(int nXOff, int nYOff,
777
                                                 int nXSize, int nYSize,
778
                                                 int nMaskFlagStop,
779
                                                 double *pdfDataPct)
780
1.49k
{
781
1.49k
    if (pdfDataPct)
782
0
        *pdfDataPct = -1.0;
783
784
    // Particular case for a single simple source covering the whole dataset
785
1.49k
    if (m_papoSources.size() == 1 && m_papoSources[0]->IsSimpleSource() &&
786
1.49k
        m_papoSources[0]->GetType() == VRTSimpleSource::GetTypeStatic())
787
711
    {
788
711
        VRTSimpleSource *poSource =
789
711
            static_cast<VRTSimpleSource *>(m_papoSources[0].get());
790
791
711
        GDALRasterBand *poBand = poSource->GetRasterBand();
792
711
        if (!poBand)
793
0
            poBand = poSource->GetMaskBandMainBand();
794
711
        if (!poBand)
795
0
        {
796
0
            return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
797
0
                   GDAL_DATA_COVERAGE_STATUS_DATA;
798
0
        }
799
800
        /* Check that it uses the full source dataset */
801
711
        double dfReqXOff = 0.0;
802
711
        double dfReqYOff = 0.0;
803
711
        double dfReqXSize = 0.0;
804
711
        double dfReqYSize = 0.0;
805
711
        int nReqXOff = 0;
806
711
        int nReqYOff = 0;
807
711
        int nReqXSize = 0;
808
711
        int nReqYSize = 0;
809
711
        int nOutXOff = 0;
810
711
        int nOutYOff = 0;
811
711
        int nOutXSize = 0;
812
711
        int nOutYSize = 0;
813
711
        bool bError = false;
814
711
        if (poSource->GetSrcDstWindow(
815
711
                0, 0, GetXSize(), GetYSize(), GetXSize(), GetYSize(),
816
711
                &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, &nReqXOff,
817
711
                &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
818
711
                &nOutXSize, &nOutYSize, bError) &&
819
711
            nReqXOff == 0 && nReqYOff == 0 && nReqXSize == GetXSize() &&
820
176
            nReqXSize == poBand->GetXSize() && nReqYSize == GetYSize() &&
821
176
            nReqYSize == poBand->GetYSize() && nOutXOff == 0 && nOutYOff == 0 &&
822
176
            nOutXSize == GetXSize() && nOutYSize == GetYSize())
823
176
        {
824
176
            return poBand->GetDataCoverageStatus(nXOff, nYOff, nXSize, nYSize,
825
176
                                                 nMaskFlagStop, pdfDataPct);
826
176
        }
827
711
    }
828
829
1.32k
#ifndef HAVE_GEOS
830
1.32k
    return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
831
1.32k
           GDAL_DATA_COVERAGE_STATUS_DATA;
832
#else
833
    int nStatus = 0;
834
835
    auto poPolyNonCoveredBySources = std::make_unique<OGRPolygon>();
836
    {
837
        auto poLR = std::make_unique<OGRLinearRing>();
838
        poLR->addPoint(nXOff, nYOff);
839
        poLR->addPoint(nXOff, nYOff + nYSize);
840
        poLR->addPoint(nXOff + nXSize, nYOff + nYSize);
841
        poLR->addPoint(nXOff + nXSize, nYOff);
842
        poLR->addPoint(nXOff, nYOff);
843
        poPolyNonCoveredBySources->addRingDirectly(poLR.release());
844
    }
845
846
    for (auto &poSource : m_papoSources)
847
    {
848
        if (!poSource->IsSimpleSource())
849
        {
850
            return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
851
                   GDAL_DATA_COVERAGE_STATUS_DATA;
852
        }
853
        VRTSimpleSource *poSS = static_cast<VRTSimpleSource *>(poSource.get());
854
        // Check if the AOI is fully inside the source
855
        double dfDstXOff = std::max(0.0, poSS->m_dfDstXOff);
856
        double dfDstYOff = std::max(0.0, poSS->m_dfDstYOff);
857
        double dfDstXSize = poSS->m_dfDstXSize;
858
        double dfDstYSize = poSS->m_dfDstYSize;
859
        auto l_poBand = poSS->GetRasterBand();
860
        if (!l_poBand)
861
            continue;
862
        if (dfDstXSize == -1)
863
            dfDstXSize = l_poBand->GetXSize() - dfDstXOff;
864
        if (dfDstYSize == -1)
865
            dfDstYSize = l_poBand->GetYSize() - dfDstYOff;
866
867
        if (nXOff >= dfDstXOff && nYOff >= dfDstYOff &&
868
            nXOff + nXSize <= dfDstXOff + dfDstXSize &&
869
            nYOff + nYSize <= dfDstYOff + dfDstYSize)
870
        {
871
            if (pdfDataPct)
872
                *pdfDataPct = 100.0;
873
            return GDAL_DATA_COVERAGE_STATUS_DATA;
874
        }
875
        // Check intersection of bounding boxes.
876
        if (dfDstXOff + dfDstXSize > nXOff && dfDstYOff + dfDstYSize > nYOff &&
877
            dfDstXOff < nXOff + nXSize && dfDstYOff < nYOff + nYSize)
878
        {
879
            nStatus |= GDAL_DATA_COVERAGE_STATUS_DATA;
880
            if (poPolyNonCoveredBySources)
881
            {
882
                OGRPolygon oPolySource;
883
                auto poLR = std::make_unique<OGRLinearRing>();
884
                poLR->addPoint(dfDstXOff, dfDstYOff);
885
                poLR->addPoint(dfDstXOff, dfDstYOff + dfDstYSize);
886
                poLR->addPoint(dfDstXOff + dfDstXSize, dfDstYOff + dfDstYSize);
887
                poLR->addPoint(dfDstXOff + dfDstXSize, dfDstYOff);
888
                poLR->addPoint(dfDstXOff, dfDstYOff);
889
                oPolySource.addRingDirectly(poLR.release());
890
                auto poRes = std::unique_ptr<OGRGeometry>(
891
                    poPolyNonCoveredBySources->Difference(&oPolySource));
892
                if (poRes && poRes->IsEmpty())
893
                {
894
                    if (pdfDataPct)
895
                        *pdfDataPct = 100.0;
896
                    return GDAL_DATA_COVERAGE_STATUS_DATA;
897
                }
898
                else if (poRes && poRes->getGeometryType() == wkbPolygon)
899
                {
900
                    poPolyNonCoveredBySources.reset(
901
                        poRes.release()->toPolygon());
902
                }
903
                else
904
                {
905
                    poPolyNonCoveredBySources.reset();
906
                }
907
            }
908
        }
909
        if (nMaskFlagStop != 0 && (nStatus & nMaskFlagStop) != 0)
910
        {
911
            return nStatus;
912
        }
913
    }
914
    if (poPolyNonCoveredBySources)
915
    {
916
        if (!poPolyNonCoveredBySources->IsEmpty())
917
            nStatus |= GDAL_DATA_COVERAGE_STATUS_EMPTY;
918
        if (pdfDataPct)
919
            *pdfDataPct = 100.0 * (1.0 - poPolyNonCoveredBySources->get_Area() /
920
                                             nXSize / nYSize);
921
    }
922
    return nStatus;
923
#endif  // HAVE_GEOS
924
1.49k
}
925
926
/************************************************************************/
927
/*                             IReadBlock()                             */
928
/************************************************************************/
929
930
CPLErr VRTSourcedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
931
                                        void *pImage)
932
933
51.9k
{
934
51.9k
    const int nPixelSize = GDALGetDataTypeSizeBytes(eDataType);
935
936
51.9k
    int nReadXSize = 0;
937
51.9k
    if ((nBlockXOff + 1) * nBlockXSize > GetXSize())
938
3.37k
        nReadXSize = GetXSize() - nBlockXOff * nBlockXSize;
939
48.5k
    else
940
48.5k
        nReadXSize = nBlockXSize;
941
942
51.9k
    int nReadYSize = 0;
943
51.9k
    if ((nBlockYOff + 1) * nBlockYSize > GetYSize())
944
2.76k
        nReadYSize = GetYSize() - nBlockYOff * nBlockYSize;
945
49.1k
    else
946
49.1k
        nReadYSize = nBlockYSize;
947
948
51.9k
    GDALRasterIOExtraArg sExtraArg;
949
51.9k
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
950
951
51.9k
    return IRasterIO(
952
51.9k
        GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, nReadXSize,
953
51.9k
        nReadYSize, pImage, nReadXSize, nReadYSize, eDataType, nPixelSize,
954
51.9k
        static_cast<GSpacing>(nPixelSize) * nBlockXSize, &sExtraArg);
955
51.9k
}
956
957
/************************************************************************/
958
/*                        CPLGettimeofday()                             */
959
/************************************************************************/
960
961
#if defined(_WIN32) && !defined(__CYGWIN__)
962
#include <sys/timeb.h>
963
964
namespace
965
{
966
struct CPLTimeVal
967
{
968
    time_t tv_sec; /* seconds */
969
    long tv_usec;  /* and microseconds */
970
};
971
}  // namespace
972
973
static int CPLGettimeofday(struct CPLTimeVal *tp, void * /* timezonep*/)
974
{
975
    struct _timeb theTime;
976
977
    _ftime(&theTime);
978
    tp->tv_sec = static_cast<time_t>(theTime.time);
979
    tp->tv_usec = theTime.millitm * 1000;
980
    return 0;
981
}
982
#else
983
#include <sys/time.h> /* for gettimeofday() */
984
#define CPLTimeVal timeval
985
0
#define CPLGettimeofday(t, u) gettimeofday(t, u)
986
#endif
987
988
/************************************************************************/
989
/*                    CanUseSourcesMinMaxImplementations()              */
990
/************************************************************************/
991
992
bool VRTSourcedRasterBand::CanUseSourcesMinMaxImplementations()
993
17.3k
{
994
17.3k
    const char *pszUseSources =
995
17.3k
        CPLGetConfigOption("VRT_MIN_MAX_FROM_SOURCES", nullptr);
996
17.3k
    if (pszUseSources)
997
0
        return CPLTestBool(pszUseSources);
998
999
    // Use heuristics to determine if we are going to use the source
1000
    // GetMinimum() or GetMaximum() implementation: all the sources must be
1001
    // "simple" sources with a dataset description that match a "regular" file
1002
    // on the filesystem, whose open time and GetMinimum()/GetMaximum()
1003
    // implementations we hope to be fast enough.
1004
    // In case of doubt return FALSE.
1005
17.3k
    struct CPLTimeVal tvStart;
1006
17.3k
    memset(&tvStart, 0, sizeof(CPLTimeVal));
1007
17.3k
    if (m_papoSources.size() > 1)
1008
0
        CPLGettimeofday(&tvStart, nullptr);
1009
17.3k
    for (auto &poSource : m_papoSources)
1010
17.3k
    {
1011
17.3k
        if (!(poSource->IsSimpleSource()))
1012
0
            return false;
1013
17.3k
        VRTSimpleSource *const poSimpleSource =
1014
17.3k
            static_cast<VRTSimpleSource *>(poSource.get());
1015
17.3k
        const char *pszFilename = poSimpleSource->m_osSrcDSName.c_str();
1016
        // /vsimem/ should be fast.
1017
17.3k
        if (STARTS_WITH(pszFilename, "/vsimem/"))
1018
0
            continue;
1019
        // but not other /vsi filesystems
1020
17.3k
        if (STARTS_WITH(pszFilename, "/vsi"))
1021
17.3k
            return false;
1022
0
        char ch = '\0';
1023
        // We will assume that filenames that are only with ascii characters
1024
        // are real filenames and so we will not try to 'stat' them.
1025
0
        for (int i = 0; (ch = pszFilename[i]) != '\0'; i++)
1026
0
        {
1027
0
            if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
1028
0
                  (ch >= '0' && ch <= '9') || ch == ':' || ch == '/' ||
1029
0
                  ch == '\\' || ch == ' ' || ch == '.' || ch == '_'))
1030
0
                break;
1031
0
        }
1032
0
        if (ch != '\0')
1033
0
        {
1034
            // Otherwise do a real filesystem check.
1035
0
            VSIStatBuf sStat;
1036
0
            if (VSIStat(pszFilename, &sStat) != 0)
1037
0
                return false;
1038
0
            if (m_papoSources.size() > 1)
1039
0
            {
1040
0
                struct CPLTimeVal tvCur;
1041
0
                CPLGettimeofday(&tvCur, nullptr);
1042
0
                if (tvCur.tv_sec - tvStart.tv_sec +
1043
0
                        (tvCur.tv_usec - tvStart.tv_usec) * 1e-6 >
1044
0
                    1)
1045
0
                    return false;
1046
0
            }
1047
0
        }
1048
0
    }
1049
0
    return true;
1050
17.3k
}
1051
1052
/************************************************************************/
1053
/*                             GetMinimum()                             */
1054
/************************************************************************/
1055
1056
double VRTSourcedRasterBand::GetMinimum(int *pbSuccess)
1057
8.66k
{
1058
8.66k
    const char *const pszValue = GetMetadataItem("STATISTICS_MINIMUM");
1059
8.66k
    if (pszValue != nullptr)
1060
0
    {
1061
0
        if (pbSuccess != nullptr)
1062
0
            *pbSuccess = TRUE;
1063
1064
0
        return CPLAtofM(pszValue);
1065
0
    }
1066
1067
8.66k
    if (!CanUseSourcesMinMaxImplementations())
1068
8.66k
        return GDALRasterBand::GetMinimum(pbSuccess);
1069
1070
0
    const std::string osFctId("VRTSourcedRasterBand::GetMinimum");
1071
0
    GDALAntiRecursionGuard oGuard(osFctId);
1072
0
    if (oGuard.GetCallDepth() >= 32)
1073
0
    {
1074
0
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1075
0
        if (pbSuccess != nullptr)
1076
0
            *pbSuccess = FALSE;
1077
0
        return 0;
1078
0
    }
1079
1080
0
    GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
1081
0
    if (oGuard2.GetCallDepth() >= 2)
1082
0
    {
1083
0
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1084
0
        if (pbSuccess != nullptr)
1085
0
            *pbSuccess = FALSE;
1086
0
        return 0;
1087
0
    }
1088
1089
0
    struct CPLTimeVal tvStart;
1090
0
    memset(&tvStart, 0, sizeof(CPLTimeVal));
1091
0
    if (m_papoSources.size() > 1)
1092
0
        CPLGettimeofday(&tvStart, nullptr);
1093
0
    double dfMin = 0;
1094
0
    for (size_t iSource = 0; iSource < m_papoSources.size(); iSource++)
1095
0
    {
1096
0
        int bSuccess = FALSE;
1097
0
        double dfSourceMin = m_papoSources[iSource]->GetMinimum(
1098
0
            GetXSize(), GetYSize(), &bSuccess);
1099
0
        if (!bSuccess)
1100
0
        {
1101
0
            dfMin = GDALRasterBand::GetMinimum(pbSuccess);
1102
0
            return dfMin;
1103
0
        }
1104
1105
0
        if (iSource == 0 || dfSourceMin < dfMin)
1106
0
        {
1107
0
            dfMin = dfSourceMin;
1108
0
            if (dfMin == 0 && eDataType == GDT_Byte)
1109
0
                break;
1110
0
        }
1111
0
        if (m_papoSources.size() > 1)
1112
0
        {
1113
0
            struct CPLTimeVal tvCur;
1114
0
            CPLGettimeofday(&tvCur, nullptr);
1115
0
            if (tvCur.tv_sec - tvStart.tv_sec +
1116
0
                    (tvCur.tv_usec - tvStart.tv_usec) * 1e-6 >
1117
0
                1)
1118
0
            {
1119
0
                return GDALRasterBand::GetMinimum(pbSuccess);
1120
0
            }
1121
0
        }
1122
0
    }
1123
1124
0
    if (pbSuccess != nullptr)
1125
0
        *pbSuccess = TRUE;
1126
1127
0
    return dfMin;
1128
0
}
1129
1130
/************************************************************************/
1131
/*                             GetMaximum()                             */
1132
/************************************************************************/
1133
1134
double VRTSourcedRasterBand::GetMaximum(int *pbSuccess)
1135
8.66k
{
1136
8.66k
    const char *const pszValue = GetMetadataItem("STATISTICS_MAXIMUM");
1137
8.66k
    if (pszValue != nullptr)
1138
0
    {
1139
0
        if (pbSuccess != nullptr)
1140
0
            *pbSuccess = TRUE;
1141
1142
0
        return CPLAtofM(pszValue);
1143
0
    }
1144
1145
8.66k
    if (!CanUseSourcesMinMaxImplementations())
1146
8.66k
        return GDALRasterBand::GetMaximum(pbSuccess);
1147
1148
0
    const std::string osFctId("VRTSourcedRasterBand::GetMaximum");
1149
0
    GDALAntiRecursionGuard oGuard(osFctId);
1150
0
    if (oGuard.GetCallDepth() >= 32)
1151
0
    {
1152
0
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1153
0
        if (pbSuccess != nullptr)
1154
0
            *pbSuccess = FALSE;
1155
0
        return 0;
1156
0
    }
1157
1158
0
    GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
1159
0
    if (oGuard2.GetCallDepth() >= 2)
1160
0
    {
1161
0
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1162
0
        if (pbSuccess != nullptr)
1163
0
            *pbSuccess = FALSE;
1164
0
        return 0;
1165
0
    }
1166
1167
0
    struct CPLTimeVal tvStart;
1168
0
    memset(&tvStart, 0, sizeof(CPLTimeVal));
1169
0
    if (m_papoSources.size() > 1)
1170
0
        CPLGettimeofday(&tvStart, nullptr);
1171
0
    double dfMax = 0;
1172
0
    for (size_t iSource = 0; iSource < m_papoSources.size(); iSource++)
1173
0
    {
1174
0
        int bSuccess = FALSE;
1175
0
        const double dfSourceMax = m_papoSources[iSource]->GetMaximum(
1176
0
            GetXSize(), GetYSize(), &bSuccess);
1177
0
        if (!bSuccess)
1178
0
        {
1179
0
            dfMax = GDALRasterBand::GetMaximum(pbSuccess);
1180
0
            return dfMax;
1181
0
        }
1182
1183
0
        if (iSource == 0 || dfSourceMax > dfMax)
1184
0
        {
1185
0
            dfMax = dfSourceMax;
1186
0
            if (dfMax == 255.0 && eDataType == GDT_Byte)
1187
0
                break;
1188
0
        }
1189
0
        if (m_papoSources.size() > 1)
1190
0
        {
1191
0
            struct CPLTimeVal tvCur;
1192
0
            CPLGettimeofday(&tvCur, nullptr);
1193
0
            if (tvCur.tv_sec - tvStart.tv_sec +
1194
0
                    (tvCur.tv_usec - tvStart.tv_usec) * 1e-6 >
1195
0
                1)
1196
0
            {
1197
0
                return GDALRasterBand::GetMaximum(pbSuccess);
1198
0
            }
1199
0
        }
1200
0
    }
1201
1202
0
    if (pbSuccess != nullptr)
1203
0
        *pbSuccess = TRUE;
1204
1205
0
    return dfMax;
1206
0
}
1207
1208
/************************************************************************/
1209
/* IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange() */
1210
/************************************************************************/
1211
1212
/* Returns true if the VRT raster band consists of non-overlapping simple
1213
 * sources or complex sources that don't change values, and use the full extent
1214
 * of the source band.
1215
 */
1216
bool VRTSourcedRasterBand::
1217
    IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange(
1218
        bool bAllowMaxValAdjustment) const
1219
31.3k
{
1220
31.3k
    bool bRet = true;
1221
31.3k
    CPLRectObj sGlobalBounds;
1222
31.3k
    sGlobalBounds.minx = 0;
1223
31.3k
    sGlobalBounds.miny = 0;
1224
31.3k
    sGlobalBounds.maxx = nRasterXSize;
1225
31.3k
    sGlobalBounds.maxy = nRasterYSize;
1226
31.3k
    CPLQuadTree *hQuadTree = CPLQuadTreeCreate(&sGlobalBounds, nullptr);
1227
34.5k
    for (int i = 0; i < static_cast<int>(m_papoSources.size()); ++i)
1228
31.3k
    {
1229
31.3k
        if (!m_papoSources[i]->IsSimpleSource())
1230
0
        {
1231
0
            bRet = false;
1232
0
            break;
1233
0
        }
1234
1235
31.3k
        auto poSimpleSource =
1236
31.3k
            cpl::down_cast<VRTSimpleSource *>(m_papoSources[i].get());
1237
31.3k
        const char *pszType = poSimpleSource->GetType();
1238
31.3k
        if (pszType == VRTSimpleSource::GetTypeStatic())
1239
21.0k
        {
1240
            // ok
1241
21.0k
        }
1242
10.3k
        else if (pszType == VRTComplexSource::GetTypeStatic())
1243
10.3k
        {
1244
10.3k
            auto poComplexSource =
1245
10.3k
                cpl::down_cast<VRTComplexSource *>(poSimpleSource);
1246
10.3k
            if (!poComplexSource->AreValuesUnchanged())
1247
10.0k
            {
1248
10.0k
                bRet = false;
1249
10.0k
                break;
1250
10.0k
            }
1251
10.3k
        }
1252
0
        else
1253
0
        {
1254
0
            bRet = false;
1255
0
            break;
1256
0
        }
1257
1258
21.3k
        if (!bAllowMaxValAdjustment && poSimpleSource->NeedMaxValAdjustment())
1259
0
        {
1260
0
            bRet = false;
1261
0
            break;
1262
0
        }
1263
1264
21.3k
        auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
1265
21.3k
        if (poSimpleSourceBand == nullptr ||
1266
21.3k
            poSimpleSourceBand->GetRasterDataType() != eDataType)
1267
0
        {
1268
0
            bRet = false;
1269
0
            break;
1270
0
        }
1271
1272
21.3k
        double dfReqXOff = 0.0;
1273
21.3k
        double dfReqYOff = 0.0;
1274
21.3k
        double dfReqXSize = 0.0;
1275
21.3k
        double dfReqYSize = 0.0;
1276
21.3k
        int nReqXOff = 0;
1277
21.3k
        int nReqYOff = 0;
1278
21.3k
        int nReqXSize = 0;
1279
21.3k
        int nReqYSize = 0;
1280
21.3k
        int nOutXOff = 0;
1281
21.3k
        int nOutYOff = 0;
1282
21.3k
        int nOutXSize = 0;
1283
21.3k
        int nOutYSize = 0;
1284
1285
21.3k
        bool bError = false;
1286
21.3k
        if (!poSimpleSource->GetSrcDstWindow(
1287
21.3k
                0, 0, nRasterXSize, nRasterYSize, nRasterXSize, nRasterYSize,
1288
21.3k
                &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, &nReqXOff,
1289
21.3k
                &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
1290
21.3k
                &nOutXSize, &nOutYSize, bError) ||
1291
21.2k
            nReqXOff != 0 || nReqYOff != 0 ||
1292
20.8k
            nReqXSize != poSimpleSourceBand->GetXSize() ||
1293
20.8k
            nReqYSize != poSimpleSourceBand->GetYSize() ||
1294
20.8k
            nOutXSize != nReqXSize || nOutYSize != nReqYSize)
1295
18.1k
        {
1296
18.1k
            bRet = false;
1297
18.1k
            break;
1298
18.1k
        }
1299
1300
3.21k
        CPLRectObj sBounds;
1301
3.21k
        constexpr double EPSILON = 1e-1;
1302
3.21k
        sBounds.minx = nOutXOff + EPSILON;
1303
3.21k
        sBounds.miny = nOutYOff + EPSILON;
1304
3.21k
        sBounds.maxx = nOutXOff + nOutXSize - EPSILON;
1305
3.21k
        sBounds.maxy = nOutYOff + nOutYSize - EPSILON;
1306
1307
        // Check that the new source doesn't overlap an existing one.
1308
3.21k
        if (CPLQuadTreeHasMatch(hQuadTree, &sBounds))
1309
0
        {
1310
0
            bRet = false;
1311
0
            break;
1312
0
        }
1313
1314
3.21k
        CPLQuadTreeInsertWithBounds(
1315
3.21k
            hQuadTree, reinterpret_cast<void *>(static_cast<uintptr_t>(i)),
1316
3.21k
            &sBounds);
1317
3.21k
    }
1318
31.3k
    CPLQuadTreeDestroy(hQuadTree);
1319
1320
31.3k
    return bRet;
1321
31.3k
}
1322
1323
/************************************************************************/
1324
/*                       ComputeRasterMinMax()                          */
1325
/************************************************************************/
1326
1327
CPLErr VRTSourcedRasterBand::ComputeRasterMinMax(int bApproxOK,
1328
                                                 double *adfMinMax)
1329
7.95k
{
1330
    /* -------------------------------------------------------------------- */
1331
    /*      Does the driver already know the min/max?                       */
1332
    /* -------------------------------------------------------------------- */
1333
7.95k
    if (bApproxOK)
1334
0
    {
1335
0
        int bSuccessMin = FALSE;
1336
0
        int bSuccessMax = FALSE;
1337
1338
0
        const double dfMin = GetMinimum(&bSuccessMin);
1339
0
        const double dfMax = GetMaximum(&bSuccessMax);
1340
1341
0
        if (bSuccessMin && bSuccessMax)
1342
0
        {
1343
0
            adfMinMax[0] = dfMin;
1344
0
            adfMinMax[1] = dfMax;
1345
0
            return CE_None;
1346
0
        }
1347
0
    }
1348
1349
7.95k
    const std::string osFctId("VRTSourcedRasterBand::ComputeRasterMinMax");
1350
7.95k
    GDALAntiRecursionGuard oGuard(osFctId);
1351
7.95k
    if (oGuard.GetCallDepth() >= 32)
1352
0
    {
1353
0
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1354
0
        return CE_Failure;
1355
0
    }
1356
1357
7.95k
    GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
1358
7.95k
    if (oGuard2.GetCallDepth() >= 2)
1359
0
    {
1360
0
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1361
0
        return CE_Failure;
1362
0
    }
1363
1364
    /* -------------------------------------------------------------------- */
1365
    /*      If we have overview bands, use them for min/max.                */
1366
    /* -------------------------------------------------------------------- */
1367
7.95k
    if (bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews())
1368
0
    {
1369
0
        GDALRasterBand *const poBand =
1370
0
            GetRasterSampleOverview(GDALSTAT_APPROX_NUMSAMPLES);
1371
1372
0
        if (poBand != nullptr && poBand != this)
1373
0
        {
1374
0
            auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
1375
0
            if (l_poDS && !l_poDS->m_apoOverviews.empty() &&
1376
0
                dynamic_cast<VRTSourcedRasterBand *>(poBand) != nullptr)
1377
0
            {
1378
0
                auto apoTmpOverviews = std::move(l_poDS->m_apoOverviews);
1379
0
                l_poDS->m_apoOverviews.clear();
1380
0
                auto eErr = poBand->GDALRasterBand::ComputeRasterMinMax(
1381
0
                    TRUE, adfMinMax);
1382
0
                l_poDS->m_apoOverviews = std::move(apoTmpOverviews);
1383
0
                return eErr;
1384
0
            }
1385
0
            else
1386
0
            {
1387
0
                return poBand->ComputeRasterMinMax(TRUE, adfMinMax);
1388
0
            }
1389
0
        }
1390
0
    }
1391
1392
7.95k
    if (IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange(
1393
7.95k
            /*bAllowMaxValAdjustment = */ true))
1394
383
    {
1395
383
        CPLDebugOnly(
1396
383
            "VRT", "ComputeRasterMinMax(): use optimized code path for mosaic");
1397
1398
383
        uint64_t nCoveredArea = 0;
1399
1400
        // If source bands have nodata value, we can't use source band's
1401
        // ComputeRasterMinMax() as we don't know if there are pixels actually
1402
        // at the nodata value, so use ComputeStatistics() instead that takes
1403
        // into account that aspect.
1404
383
        bool bUseComputeStatistics = false;
1405
383
        for (auto &poSource : m_papoSources)
1406
383
        {
1407
383
            auto poSimpleSource =
1408
383
                cpl::down_cast<VRTSimpleSource *>(poSource.get());
1409
383
            auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
1410
            // already checked by IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange
1411
383
            assert(poSimpleSourceBand);
1412
383
            int bHasNoData = FALSE;
1413
383
            CPL_IGNORE_RET_VAL(poSimpleSourceBand->GetNoDataValue(&bHasNoData));
1414
383
            if (bHasNoData)
1415
21
            {
1416
21
                bUseComputeStatistics = true;
1417
21
                break;
1418
21
            }
1419
362
            nCoveredArea +=
1420
362
                static_cast<uint64_t>(poSimpleSourceBand->GetXSize()) *
1421
362
                poSimpleSourceBand->GetYSize();
1422
362
        }
1423
1424
383
        if (bUseComputeStatistics)
1425
21
        {
1426
21
            CPLErr eErr;
1427
21
            std::string osLastErrorMsg;
1428
21
            {
1429
21
                CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1430
21
                CPLErrorReset();
1431
21
                eErr =
1432
21
                    ComputeStatistics(bApproxOK, &adfMinMax[0], &adfMinMax[1],
1433
21
                                      nullptr, nullptr, nullptr, nullptr);
1434
21
                if (eErr == CE_Failure)
1435
21
                {
1436
21
                    osLastErrorMsg = CPLGetLastErrorMsg();
1437
21
                }
1438
21
            }
1439
21
            if (eErr == CE_Failure)
1440
21
            {
1441
21
                if (strstr(osLastErrorMsg.c_str(), "no valid pixels found") !=
1442
21
                    nullptr)
1443
0
                {
1444
0
                    ReportError(CE_Failure, CPLE_AppDefined,
1445
0
                                "Failed to compute min/max, no valid pixels "
1446
0
                                "found in sampling.");
1447
0
                }
1448
21
                else
1449
21
                {
1450
21
                    ReportError(CE_Failure, CPLE_AppDefined, "%s",
1451
21
                                osLastErrorMsg.c_str());
1452
21
                }
1453
21
            }
1454
21
            return eErr;
1455
21
        }
1456
1457
362
        bool bSignedByte = false;
1458
362
        if (eDataType == GDT_Byte)
1459
362
        {
1460
362
            EnablePixelTypeSignedByteWarning(false);
1461
362
            const char *pszPixelType =
1462
362
                GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
1463
362
            EnablePixelTypeSignedByteWarning(true);
1464
362
            bSignedByte =
1465
362
                pszPixelType != nullptr && EQUAL(pszPixelType, "SIGNEDBYTE");
1466
362
        }
1467
1468
362
        double dfGlobalMin = std::numeric_limits<double>::max();
1469
362
        double dfGlobalMax = -std::numeric_limits<double>::max();
1470
1471
        // If the mosaic doesn't cover the whole VRT raster, take into account
1472
        // VRT nodata value.
1473
362
        if (nCoveredArea < static_cast<uint64_t>(nRasterXSize) * nRasterYSize)
1474
0
        {
1475
0
            if (m_bNoDataValueSet && m_bHideNoDataValue)
1476
0
            {
1477
0
                if (IsNoDataValueInDataTypeRange())
1478
0
                {
1479
0
                    dfGlobalMin = std::min(dfGlobalMin, m_dfNoDataValue);
1480
0
                    dfGlobalMax = std::max(dfGlobalMax, m_dfNoDataValue);
1481
0
                }
1482
0
            }
1483
0
            else if (!m_bNoDataValueSet)
1484
0
            {
1485
0
                dfGlobalMin = std::min(dfGlobalMin, 0.0);
1486
0
                dfGlobalMax = std::max(dfGlobalMax, 0.0);
1487
0
            }
1488
0
        }
1489
1490
362
        for (auto &poSource : m_papoSources)
1491
362
        {
1492
362
            auto poSimpleSource =
1493
362
                cpl::down_cast<VRTSimpleSource *>(poSource.get());
1494
362
            double adfMinMaxSource[2] = {0};
1495
1496
362
            auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
1497
            // already checked by IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange
1498
362
            assert(poSimpleSourceBand);
1499
362
            CPLErr eErr = poSimpleSourceBand->ComputeRasterMinMax(
1500
362
                bApproxOK, adfMinMaxSource);
1501
362
            if (eErr == CE_Failure)
1502
1
            {
1503
1
                return CE_Failure;
1504
1
            }
1505
361
            else
1506
361
            {
1507
361
                if (poSimpleSource->NeedMaxValAdjustment())
1508
0
                {
1509
0
                    const double dfMaxValue =
1510
0
                        static_cast<double>(poSimpleSource->m_nMaxValue);
1511
0
                    adfMinMaxSource[0] =
1512
0
                        std::min(adfMinMaxSource[0], dfMaxValue);
1513
0
                    adfMinMaxSource[1] =
1514
0
                        std::min(adfMinMaxSource[1], dfMaxValue);
1515
0
                }
1516
1517
361
                if (m_bNoDataValueSet && !m_bHideNoDataValue &&
1518
0
                    m_dfNoDataValue >= adfMinMaxSource[0] &&
1519
0
                    m_dfNoDataValue <= adfMinMaxSource[1])
1520
0
                {
1521
0
                    return GDALRasterBand::ComputeRasterMinMax(bApproxOK,
1522
0
                                                               adfMinMax);
1523
0
                }
1524
1525
361
                dfGlobalMin = std::min(dfGlobalMin, adfMinMaxSource[0]);
1526
361
                dfGlobalMax = std::max(dfGlobalMax, adfMinMaxSource[1]);
1527
361
            }
1528
1529
            // Early exit if we know we reached theoretical bounds
1530
361
            if (eDataType == GDT_Byte && !bSignedByte && dfGlobalMin == 0.0 &&
1531
355
                dfGlobalMax == 255.0)
1532
355
            {
1533
355
                break;
1534
355
            }
1535
361
        }
1536
1537
361
        if (dfGlobalMin > dfGlobalMax)
1538
0
        {
1539
0
            adfMinMax[0] = 0.0;
1540
0
            adfMinMax[1] = 0.0;
1541
0
            ReportError(CE_Failure, CPLE_AppDefined,
1542
0
                        "Failed to compute min/max, no valid pixels found in "
1543
0
                        "sampling.");
1544
0
            return CE_Failure;
1545
0
        }
1546
1547
361
        adfMinMax[0] = dfGlobalMin;
1548
361
        adfMinMax[1] = dfGlobalMax;
1549
361
        return CE_None;
1550
361
    }
1551
7.57k
    else
1552
7.57k
    {
1553
7.57k
        return GDALRasterBand::ComputeRasterMinMax(bApproxOK, adfMinMax);
1554
7.57k
    }
1555
7.95k
}
1556
1557
// #define naive_update_not_used
1558
1559
namespace
1560
{
1561
struct Context
1562
{
1563
    CPL_DISALLOW_COPY_ASSIGN(Context)
1564
2.83k
    Context() = default;
1565
1566
    // Protected by mutex
1567
    std::mutex oMutex{};
1568
    uint64_t nTotalIteratedPixels = 0;
1569
    uint64_t nLastReportedPixels = 0;
1570
    bool bFailure = false;
1571
    bool bFallbackToBase = false;
1572
    // End of protected by mutex
1573
1574
    int nSources = 0;
1575
1576
    bool bApproxOK = false;
1577
    GDALProgressFunc pfnProgress = nullptr;
1578
    void *pProgressData = nullptr;
1579
1580
    // VRTSourcedRasterBand parameters
1581
    double dfNoDataValue = 0;
1582
    bool bNoDataValueSet = false;
1583
    bool bHideNoDataValue = false;
1584
1585
    double dfGlobalMin = std::numeric_limits<double>::max();
1586
    double dfGlobalMax = -std::numeric_limits<double>::max();
1587
#ifdef naive_update_not_used
1588
    // This native method uses the fact that stddev = sqrt(sum_of_squares/N -
1589
    // mean^2) and that thus sum_of_squares = N * (stddev^2 + mean^2)
1590
    double dfGlobalSum = 0;
1591
    double dfGlobalSumSquare = 0;
1592
#else
1593
    // This method uses
1594
    // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Parallel_algorithm
1595
    // which is more numerically robust
1596
    double dfGlobalMean = 0;
1597
    double dfGlobalM2 = 0;
1598
#endif
1599
    uint64_t nGlobalValidPixels = 0;
1600
    uint64_t nTotalPixelsOfSources = 0;
1601
1602
    // Keep original values from single source to avoid slight changes
1603
    // due to recomputation. Cf https://github.com/OSGeo/gdal/issues/12650
1604
    bool bUpdateStatsWithConstantValueRun = false;
1605
    double dfSingleSourceMin = 0;
1606
    double dfSingleSourceMax = 0;
1607
    double dfSingleSourceMean = 0;
1608
    double dfSingleSourceStdDev = 0;
1609
};
1610
}  // namespace
1611
1612
static void UpdateStatsWithConstantValue(Context &sContext, double dfVal,
1613
                                         uint64_t nPixelCount)
1614
391
{
1615
391
    sContext.bUpdateStatsWithConstantValueRun = true;
1616
391
    sContext.dfGlobalMin = std::min(sContext.dfGlobalMin, dfVal);
1617
391
    sContext.dfGlobalMax = std::max(sContext.dfGlobalMax, dfVal);
1618
#ifdef naive_update_not_used
1619
    sContext.dfGlobalSum += dfVal * nPixelCount;
1620
    sContext.dfGlobalSumSquare += dfVal * dfVal * nPixelCount;
1621
#else
1622
391
    const auto nNewGlobalValidPixels =
1623
391
        sContext.nGlobalValidPixels + nPixelCount;
1624
391
    const double dfDelta = dfVal - sContext.dfGlobalMean;
1625
391
    sContext.dfGlobalMean += nPixelCount * dfDelta / nNewGlobalValidPixels;
1626
391
    sContext.dfGlobalM2 += dfDelta * dfDelta * nPixelCount *
1627
391
                           sContext.nGlobalValidPixels / nNewGlobalValidPixels;
1628
391
#endif
1629
391
    sContext.nGlobalValidPixels += nPixelCount;
1630
391
}
1631
1632
/************************************************************************/
1633
/*                         ComputeStatistics()                          */
1634
/************************************************************************/
1635
1636
CPLErr VRTSourcedRasterBand::ComputeStatistics(int bApproxOK, double *pdfMin,
1637
                                               double *pdfMax, double *pdfMean,
1638
                                               double *pdfStdDev,
1639
                                               GDALProgressFunc pfnProgress,
1640
                                               void *pProgressData)
1641
1642
23.4k
{
1643
23.4k
    const std::string osFctId("VRTSourcedRasterBand::ComputeStatistics");
1644
23.4k
    GDALAntiRecursionGuard oGuard(osFctId);
1645
23.4k
    if (oGuard.GetCallDepth() >= 32)
1646
0
    {
1647
0
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1648
0
        return CE_Failure;
1649
0
    }
1650
1651
23.4k
    GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
1652
23.4k
    if (oGuard2.GetCallDepth() >= 2)
1653
0
    {
1654
0
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1655
0
        return CE_Failure;
1656
0
    }
1657
1658
    /* -------------------------------------------------------------------- */
1659
    /*      If we have overview bands, use them for statistics.             */
1660
    /* -------------------------------------------------------------------- */
1661
23.4k
    if (bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews())
1662
0
    {
1663
0
        GDALRasterBand *const poBand =
1664
0
            GetRasterSampleOverview(GDALSTAT_APPROX_NUMSAMPLES);
1665
1666
0
        if (poBand != nullptr && poBand != this)
1667
0
        {
1668
0
            auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
1669
0
            CPLErr eErr;
1670
0
            if (l_poDS && !l_poDS->m_apoOverviews.empty() &&
1671
0
                dynamic_cast<VRTSourcedRasterBand *>(poBand) != nullptr)
1672
0
            {
1673
0
                auto apoTmpOverviews = std::move(l_poDS->m_apoOverviews);
1674
0
                l_poDS->m_apoOverviews.clear();
1675
0
                eErr = poBand->GDALRasterBand::ComputeStatistics(
1676
0
                    TRUE, pdfMin, pdfMax, pdfMean, pdfStdDev, pfnProgress,
1677
0
                    pProgressData);
1678
0
                l_poDS->m_apoOverviews = std::move(apoTmpOverviews);
1679
0
            }
1680
0
            else
1681
0
            {
1682
0
                eErr = poBand->ComputeStatistics(TRUE, pdfMin, pdfMax, pdfMean,
1683
0
                                                 pdfStdDev, pfnProgress,
1684
0
                                                 pProgressData);
1685
0
            }
1686
0
            if (eErr == CE_None && pdfMin && pdfMax && pdfMean && pdfStdDev)
1687
0
            {
1688
0
                SetMetadataItem("STATISTICS_APPROXIMATE", "YES");
1689
0
                SetMetadataItem(
1690
0
                    "STATISTICS_VALID_PERCENT",
1691
0
                    poBand->GetMetadataItem("STATISTICS_VALID_PERCENT"));
1692
0
                SetStatistics(*pdfMin, *pdfMax, *pdfMean, *pdfStdDev);
1693
0
            }
1694
0
            return eErr;
1695
0
        }
1696
0
    }
1697
1698
23.4k
    if (IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange(
1699
23.4k
            /*bAllowMaxValAdjustment = */ false))
1700
2.83k
    {
1701
2.83k
        Context sContext;
1702
2.83k
        sContext.bApproxOK = bApproxOK;
1703
2.83k
        sContext.dfNoDataValue = m_dfNoDataValue;
1704
2.83k
        sContext.bNoDataValueSet = m_bNoDataValueSet;
1705
2.83k
        sContext.bHideNoDataValue = m_bHideNoDataValue;
1706
2.83k
        sContext.pfnProgress = pfnProgress;
1707
2.83k
        sContext.pProgressData = pProgressData;
1708
2.83k
        sContext.nSources = static_cast<int>(m_papoSources.size());
1709
1710
2.83k
        struct Job
1711
2.83k
        {
1712
2.83k
            Context *psContext = nullptr;
1713
2.83k
            GDALRasterBand *poRasterBand = nullptr;
1714
2.83k
            uint64_t nPixelCount = 0;
1715
2.83k
            uint64_t nLastAddedPixels = 0;
1716
2.83k
            uint64_t nValidPixels = 0;
1717
2.83k
            double dfMin = 0;
1718
2.83k
            double dfMax = 0;
1719
2.83k
            double dfMean = 0;
1720
2.83k
            double dfStdDev = 0;
1721
1722
2.83k
            static int CPL_STDCALL ProgressFunc(double dfComplete,
1723
2.83k
                                                const char *pszMessage,
1724
2.83k
                                                void *pProgressArg)
1725
2.83k
            {
1726
0
                auto psJob = static_cast<Job *>(pProgressArg);
1727
0
                auto psContext = psJob->psContext;
1728
0
                const uint64_t nNewAddedPixels =
1729
0
                    dfComplete == 1.0
1730
0
                        ? psJob->nPixelCount
1731
0
                        : static_cast<uint64_t>(
1732
0
                              dfComplete * psJob->nPixelCount + 0.5);
1733
0
                const auto nUpdateThreshold =
1734
0
                    std::min(psContext->nTotalPixelsOfSources / 1000,
1735
0
                             static_cast<uint64_t>(1000 * 1000));
1736
0
                std::lock_guard<std::mutex> oLock(psContext->oMutex);
1737
0
                psContext->nTotalIteratedPixels +=
1738
0
                    (nNewAddedPixels - psJob->nLastAddedPixels);
1739
0
                psJob->nLastAddedPixels = nNewAddedPixels;
1740
0
                if (psContext->nTotalIteratedPixels ==
1741
0
                    psContext->nTotalPixelsOfSources)
1742
0
                {
1743
0
                    psContext->nLastReportedPixels =
1744
0
                        psContext->nTotalIteratedPixels;
1745
0
                    return psContext->pfnProgress(1.0, pszMessage,
1746
0
                                                  psContext->pProgressData);
1747
0
                }
1748
0
                else if (psContext->nTotalIteratedPixels -
1749
0
                             psContext->nLastReportedPixels >
1750
0
                         nUpdateThreshold)
1751
0
                {
1752
0
                    psContext->nLastReportedPixels =
1753
0
                        psContext->nTotalIteratedPixels;
1754
0
                    return psContext->pfnProgress(
1755
0
                        static_cast<double>(psContext->nTotalIteratedPixels) /
1756
0
                            psContext->nTotalPixelsOfSources,
1757
0
                        pszMessage, psContext->pProgressData);
1758
0
                }
1759
0
                return 1;
1760
0
            }
1761
1762
2.83k
            static void UpdateStats(const Job *psJob)
1763
2.83k
            {
1764
2.27k
                const auto nValidPixels = psJob->nValidPixels;
1765
2.27k
                auto psContext = psJob->psContext;
1766
2.27k
                if (nValidPixels > 0)
1767
2.27k
                {
1768
2.27k
                    psContext->dfGlobalMin =
1769
2.27k
                        std::min(psContext->dfGlobalMin, psJob->dfMin);
1770
2.27k
                    psContext->dfGlobalMax =
1771
2.27k
                        std::max(psContext->dfGlobalMax, psJob->dfMax);
1772
#ifdef naive_update_not_used
1773
                    psContext->dfGlobalSum += nValidPixels * psJob->dfMean;
1774
                    psContext->dfGlobalSumSquare +=
1775
                        nValidPixels * (psJob->dfStdDev * psJob->dfStdDev +
1776
                                        psJob->dfMean * psJob->dfMean);
1777
                    psContext->nGlobalValidPixels += nValidPixels;
1778
#else
1779
2.27k
                    const auto nNewGlobalValidPixels =
1780
2.27k
                        psContext->nGlobalValidPixels + nValidPixels;
1781
2.27k
                    const double dfDelta =
1782
2.27k
                        psJob->dfMean - psContext->dfGlobalMean;
1783
2.27k
                    psContext->dfGlobalMean +=
1784
2.27k
                        nValidPixels * dfDelta / nNewGlobalValidPixels;
1785
2.27k
                    psContext->dfGlobalM2 +=
1786
2.27k
                        nValidPixels * psJob->dfStdDev * psJob->dfStdDev +
1787
2.27k
                        dfDelta * dfDelta * nValidPixels *
1788
2.27k
                            psContext->nGlobalValidPixels /
1789
2.27k
                            nNewGlobalValidPixels;
1790
2.27k
                    psContext->nGlobalValidPixels = nNewGlobalValidPixels;
1791
2.27k
#endif
1792
2.27k
                }
1793
2.27k
                int bHasNoData = FALSE;
1794
2.27k
                const double dfNoDataValue =
1795
2.27k
                    psJob->poRasterBand->GetNoDataValue(&bHasNoData);
1796
2.27k
                if (nValidPixels < psJob->nPixelCount && bHasNoData &&
1797
426
                    !std::isnan(dfNoDataValue) &&
1798
426
                    (!psContext->bNoDataValueSet ||
1799
35
                     dfNoDataValue != psContext->dfNoDataValue))
1800
391
                {
1801
391
                    const auto eBandDT =
1802
391
                        psJob->poRasterBand->GetRasterDataType();
1803
                    // Check that the band nodata value is in the range of the
1804
                    // original raster type
1805
391
                    GByte abyTempBuffer[2 * sizeof(double)];
1806
391
                    CPLAssert(GDALGetDataTypeSizeBytes(eBandDT) <=
1807
391
                              static_cast<int>(sizeof(abyTempBuffer)));
1808
391
                    GDALCopyWords(&dfNoDataValue, GDT_Float64, 0,
1809
391
                                  &abyTempBuffer[0], eBandDT, 0, 1);
1810
391
                    double dfNoDataValueAfter = dfNoDataValue;
1811
391
                    GDALCopyWords(&abyTempBuffer[0], eBandDT, 0,
1812
391
                                  &dfNoDataValueAfter, GDT_Float64, 0, 1);
1813
391
                    if (!std::isfinite(dfNoDataValue) ||
1814
391
                        std::fabs(dfNoDataValueAfter - dfNoDataValue) < 1.0)
1815
391
                    {
1816
391
                        UpdateStatsWithConstantValue(
1817
391
                            *psContext, dfNoDataValueAfter,
1818
391
                            psJob->nPixelCount - nValidPixels);
1819
391
                    }
1820
391
                }
1821
1822
2.27k
                if (psContext->nSources == 1)
1823
2.27k
                {
1824
2.27k
                    psContext->dfSingleSourceMin = psJob->dfMin;
1825
2.27k
                    psContext->dfSingleSourceMax = psJob->dfMax;
1826
2.27k
                    psContext->dfSingleSourceMean = psJob->dfMean;
1827
2.27k
                    psContext->dfSingleSourceStdDev = psJob->dfStdDev;
1828
2.27k
                }
1829
2.27k
            }
1830
2.83k
        };
1831
1832
2.83k
        const auto JobRunner = [](void *pData)
1833
2.83k
        {
1834
2.83k
            auto psJob = static_cast<Job *>(pData);
1835
2.83k
            auto psContext = psJob->psContext;
1836
2.83k
            {
1837
2.83k
                std::lock_guard<std::mutex> oLock(psContext->oMutex);
1838
2.83k
                if (psContext->bFallbackToBase || psContext->bFailure)
1839
0
                    return;
1840
2.83k
            }
1841
1842
2.83k
            auto poSimpleSourceBand = psJob->poRasterBand;
1843
2.83k
            psJob->nPixelCount =
1844
2.83k
                static_cast<uint64_t>(poSimpleSourceBand->GetXSize()) *
1845
2.83k
                poSimpleSourceBand->GetYSize();
1846
1847
2.83k
            CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1848
2.83k
            CPLErr eErr = poSimpleSourceBand->ComputeStatistics(
1849
2.83k
                psContext->bApproxOK, &psJob->dfMin, &psJob->dfMax,
1850
2.83k
                &psJob->dfMean, &psJob->dfStdDev,
1851
2.83k
                psContext->pfnProgress == nullptr ||
1852
2.80k
                        psContext->pfnProgress == GDALDummyProgress
1853
2.83k
                    ? GDALDummyProgress
1854
2.83k
                    : Job::ProgressFunc,
1855
2.83k
                psJob);
1856
2.83k
            const char *pszValidPercent =
1857
2.83k
                poSimpleSourceBand->GetMetadataItem("STATISTICS_VALID_PERCENT");
1858
2.83k
            psJob->nValidPixels =
1859
2.83k
                pszValidPercent
1860
2.83k
                    ? static_cast<uint64_t>(CPLAtof(pszValidPercent) *
1861
2.27k
                                            psJob->nPixelCount / 100.0)
1862
2.83k
                    : psJob->nPixelCount;
1863
2.83k
            if (eErr == CE_Failure)
1864
553
            {
1865
553
                if (pszValidPercent != nullptr &&
1866
2
                    CPLAtof(pszValidPercent) == 0.0)
1867
2
                {
1868
                    // ok: no valid sample
1869
2
                }
1870
551
                else
1871
551
                {
1872
551
                    std::lock_guard<std::mutex> oLock(psContext->oMutex);
1873
551
                    psContext->bFailure = true;
1874
551
                }
1875
553
            }
1876
2.27k
            else
1877
2.27k
            {
1878
2.27k
                int bHasNoData = FALSE;
1879
2.27k
                CPL_IGNORE_RET_VAL(
1880
2.27k
                    psJob->poRasterBand->GetNoDataValue(&bHasNoData));
1881
2.27k
                if (!bHasNoData && psContext->bNoDataValueSet &&
1882
0
                    !psContext->bHideNoDataValue &&
1883
0
                    psContext->dfNoDataValue >= psJob->dfMin &&
1884
0
                    psContext->dfNoDataValue <= psJob->dfMax)
1885
0
                {
1886
0
                    std::lock_guard<std::mutex> oLock(psContext->oMutex);
1887
0
                    psJob->psContext->bFallbackToBase = true;
1888
0
                    return;
1889
0
                }
1890
2.27k
            }
1891
2.83k
        };
1892
1893
2.83k
        CPLWorkerThreadPool *poThreadPool = nullptr;
1894
2.83k
        int nThreads =
1895
2.83k
            m_papoSources.size() > 1
1896
2.83k
                ? VRTDataset::GetNumThreads(dynamic_cast<VRTDataset *>(poDS))
1897
2.83k
                : 0;
1898
2.83k
        if (nThreads > 1024)
1899
0
            nThreads = 1024;  // to please Coverity
1900
2.83k
        if (nThreads > 1)
1901
0
        {
1902
            // Check that all sources refer to different datasets
1903
            // before allowing multithreaded access
1904
            // If the datasets belong to the MEM driver, check GDALDataset*
1905
            // pointer values. Otherwise use dataset name.
1906
0
            std::set<std::string> oSetDatasetNames;
1907
0
            std::set<GDALDataset *> oSetDatasetPointers;
1908
0
            for (auto &poSource : m_papoSources)
1909
0
            {
1910
0
                auto poSimpleSource =
1911
0
                    static_cast<VRTSimpleSource *>(poSource.get());
1912
0
                assert(poSimpleSource);
1913
0
                auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
1914
0
                assert(poSimpleSourceBand);
1915
0
                auto poSourceDataset = poSimpleSourceBand->GetDataset();
1916
0
                if (poSourceDataset == nullptr)
1917
0
                {
1918
0
                    nThreads = 0;
1919
0
                    break;
1920
0
                }
1921
0
                auto poDriver = poSourceDataset->GetDriver();
1922
0
                if (poDriver && EQUAL(poDriver->GetDescription(), "MEM"))
1923
0
                {
1924
0
                    if (oSetDatasetPointers.find(poSourceDataset) !=
1925
0
                        oSetDatasetPointers.end())
1926
0
                    {
1927
0
                        nThreads = 0;
1928
0
                        break;
1929
0
                    }
1930
0
                    oSetDatasetPointers.insert(poSourceDataset);
1931
0
                }
1932
0
                else
1933
0
                {
1934
0
                    if (oSetDatasetNames.find(
1935
0
                            poSourceDataset->GetDescription()) !=
1936
0
                        oSetDatasetNames.end())
1937
0
                    {
1938
0
                        nThreads = 0;
1939
0
                        break;
1940
0
                    }
1941
0
                    oSetDatasetNames.insert(poSourceDataset->GetDescription());
1942
0
                }
1943
0
            }
1944
0
            if (nThreads > 1)
1945
0
            {
1946
0
                poThreadPool = GDALGetGlobalThreadPool(nThreads);
1947
0
            }
1948
0
        }
1949
1950
        // Compute total number of pixels of sources
1951
2.83k
        for (auto &poSource : m_papoSources)
1952
2.83k
        {
1953
2.83k
            auto poSimpleSource =
1954
2.83k
                static_cast<VRTSimpleSource *>(poSource.get());
1955
2.83k
            assert(poSimpleSource);
1956
2.83k
            auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
1957
2.83k
            assert(poSimpleSourceBand);
1958
2.83k
            sContext.nTotalPixelsOfSources +=
1959
2.83k
                static_cast<uint64_t>(poSimpleSourceBand->GetXSize()) *
1960
2.83k
                poSimpleSourceBand->GetYSize();
1961
2.83k
        }
1962
1963
2.83k
        if (poThreadPool)
1964
0
        {
1965
0
            CPLDebugOnly("VRT", "ComputeStatistics(): use optimized "
1966
0
                                "multi-threaded code path for mosaic");
1967
0
            std::vector<Job> asJobs(m_papoSources.size());
1968
0
            auto poQueue = poThreadPool->CreateJobQueue();
1969
0
            for (size_t i = 0; i < m_papoSources.size(); ++i)
1970
0
            {
1971
0
                auto poSimpleSource =
1972
0
                    static_cast<VRTSimpleSource *>(m_papoSources[i].get());
1973
0
                assert(poSimpleSource);
1974
0
                auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
1975
0
                assert(poSimpleSourceBand);
1976
0
                asJobs[i].psContext = &sContext;
1977
0
                asJobs[i].poRasterBand = poSimpleSourceBand;
1978
0
                if (!poQueue->SubmitJob(JobRunner, &asJobs[i]))
1979
0
                {
1980
0
                    sContext.bFailure = true;
1981
0
                    break;
1982
0
                }
1983
0
            }
1984
0
            poQueue->WaitCompletion();
1985
0
            if (!(sContext.bFailure || sContext.bFallbackToBase))
1986
0
            {
1987
0
                for (size_t i = 0; i < m_papoSources.size(); ++i)
1988
0
                {
1989
0
                    Job::UpdateStats(&asJobs[i]);
1990
0
                }
1991
0
            }
1992
0
        }
1993
2.83k
        else
1994
2.83k
        {
1995
2.83k
            CPLDebugOnly(
1996
2.83k
                "VRT",
1997
2.83k
                "ComputeStatistics(): use optimized code path for mosaic");
1998
2.83k
            for (auto &poSource : m_papoSources)
1999
2.83k
            {
2000
2.83k
                auto poSimpleSource =
2001
2.83k
                    static_cast<VRTSimpleSource *>(poSource.get());
2002
2.83k
                assert(poSimpleSource);
2003
2.83k
                auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
2004
2.83k
                assert(poSimpleSourceBand);
2005
2.83k
                Job sJob;
2006
2.83k
                sJob.psContext = &sContext;
2007
2.83k
                sJob.poRasterBand = poSimpleSourceBand;
2008
2.83k
                JobRunner(&sJob);
2009
2.83k
                if (sContext.bFailure || sContext.bFallbackToBase)
2010
551
                    break;
2011
2.27k
                Job::UpdateStats(&sJob);
2012
2.27k
            }
2013
2.83k
        }
2014
2015
2.83k
        if (sContext.bFailure)
2016
551
            return CE_Failure;
2017
2.27k
        if (sContext.bFallbackToBase)
2018
0
        {
2019
            // If the VRT band nodata value is in the [min, max] range
2020
            // of the source and that the source has no nodata value set,
2021
            // then we can't use the optimization.
2022
0
            CPLDebugOnly("VRT", "ComputeStatistics(): revert back to "
2023
0
                                "generic case because of nodata value in range "
2024
0
                                "of source raster");
2025
0
            return GDALRasterBand::ComputeStatistics(
2026
0
                bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev, pfnProgress,
2027
0
                pProgressData);
2028
0
        }
2029
2030
2.27k
        const uint64_t nTotalPixels =
2031
2.27k
            static_cast<uint64_t>(nRasterXSize) * nRasterYSize;
2032
2.27k
        if (m_bNoDataValueSet && m_bHideNoDataValue &&
2033
0
            !std::isnan(m_dfNoDataValue) && IsNoDataValueInDataTypeRange())
2034
0
        {
2035
0
            UpdateStatsWithConstantValue(sContext, m_dfNoDataValue,
2036
0
                                         nTotalPixels -
2037
0
                                             sContext.nGlobalValidPixels);
2038
0
        }
2039
2.27k
        else if (!m_bNoDataValueSet)
2040
2.24k
        {
2041
2.24k
            sContext.nGlobalValidPixels = nTotalPixels;
2042
2.24k
        }
2043
2044
#ifdef naive_update_not_used
2045
        double dfGlobalMean =
2046
            sContext.nGlobalValidPixels > 0
2047
                ? sContext.dfGlobalSum / sContext.nGlobalValidPixels
2048
                : 0;
2049
        double dfGlobalStdDev = sContext.nGlobalValidPixels > 0
2050
                                    ? sqrt(sContext.dfGlobalSumSquare /
2051
                                               sContext.nGlobalValidPixels -
2052
                                           dfGlobalMean * dfGlobalMean)
2053
                                    : 0;
2054
#else
2055
2.27k
        double dfGlobalMean = sContext.dfGlobalMean;
2056
2.27k
        double dfGlobalStdDev =
2057
2.27k
            sContext.nGlobalValidPixels > 0
2058
2.27k
                ? sqrt(sContext.dfGlobalM2 / sContext.nGlobalValidPixels)
2059
2.27k
                : 0;
2060
2.27k
#endif
2061
2.27k
        if (m_papoSources.size() == 1 &&
2062
2.27k
            !sContext.bUpdateStatsWithConstantValueRun)
2063
1.88k
        {
2064
1.88k
            sContext.dfGlobalMin = sContext.dfSingleSourceMin;
2065
1.88k
            sContext.dfGlobalMax = sContext.dfSingleSourceMax;
2066
1.88k
            dfGlobalMean = sContext.dfSingleSourceMean;
2067
1.88k
            dfGlobalStdDev = sContext.dfSingleSourceStdDev;
2068
1.88k
        }
2069
2070
2.27k
        if (sContext.nGlobalValidPixels > 0)
2071
2.27k
        {
2072
2.27k
            if (bApproxOK)
2073
576
            {
2074
576
                SetMetadataItem("STATISTICS_APPROXIMATE", "YES");
2075
576
            }
2076
1.70k
            else if (GetMetadataItem("STATISTICS_APPROXIMATE"))
2077
0
            {
2078
0
                SetMetadataItem("STATISTICS_APPROXIMATE", nullptr);
2079
0
            }
2080
2.27k
            SetStatistics(sContext.dfGlobalMin, sContext.dfGlobalMax,
2081
2.27k
                          dfGlobalMean, dfGlobalStdDev);
2082
2.27k
        }
2083
0
        else
2084
0
        {
2085
0
            sContext.dfGlobalMin = 0.0;
2086
0
            sContext.dfGlobalMax = 0.0;
2087
0
        }
2088
2089
2.27k
        SetValidPercent(nTotalPixels, sContext.nGlobalValidPixels);
2090
2091
2.27k
        if (pdfMin)
2092
2.27k
            *pdfMin = sContext.dfGlobalMin;
2093
2.27k
        if (pdfMax)
2094
2.27k
            *pdfMax = sContext.dfGlobalMax;
2095
2.27k
        if (pdfMean)
2096
2.27k
            *pdfMean = dfGlobalMean;
2097
2.27k
        if (pdfStdDev)
2098
2.27k
            *pdfStdDev = dfGlobalStdDev;
2099
2100
2.27k
        if (sContext.nGlobalValidPixels == 0)
2101
0
        {
2102
0
            ReportError(CE_Failure, CPLE_AppDefined,
2103
0
                        "Failed to compute statistics, no valid pixels found "
2104
0
                        "in sampling.");
2105
0
        }
2106
2107
2.27k
        return sContext.nGlobalValidPixels > 0 ? CE_None : CE_Failure;
2108
2.27k
    }
2109
20.6k
    else
2110
20.6k
    {
2111
20.6k
        return GDALRasterBand::ComputeStatistics(bApproxOK, pdfMin, pdfMax,
2112
20.6k
                                                 pdfMean, pdfStdDev,
2113
20.6k
                                                 pfnProgress, pProgressData);
2114
20.6k
    }
2115
23.4k
}
2116
2117
/************************************************************************/
2118
/*                            GetHistogram()                            */
2119
/************************************************************************/
2120
2121
CPLErr VRTSourcedRasterBand::GetHistogram(double dfMin, double dfMax,
2122
                                          int nBuckets, GUIntBig *panHistogram,
2123
                                          int bIncludeOutOfRange, int bApproxOK,
2124
                                          GDALProgressFunc pfnProgress,
2125
                                          void *pProgressData)
2126
2127
9.57k
{
2128
    /* -------------------------------------------------------------------- */
2129
    /*      If we have overviews, use them for the histogram.               */
2130
    /* -------------------------------------------------------------------- */
2131
9.57k
    if (bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews())
2132
0
    {
2133
        // FIXME: Should we use the most reduced overview here or use some
2134
        // minimum number of samples like GDALRasterBand::ComputeStatistics()
2135
        // does?
2136
0
        GDALRasterBand *poBand = GetRasterSampleOverview(0);
2137
2138
0
        if (poBand != nullptr && poBand != this)
2139
0
        {
2140
0
            auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
2141
0
            if (l_poDS && !l_poDS->m_apoOverviews.empty() &&
2142
0
                dynamic_cast<VRTSourcedRasterBand *>(poBand) != nullptr)
2143
0
            {
2144
0
                auto apoTmpOverviews = std::move(l_poDS->m_apoOverviews);
2145
0
                l_poDS->m_apoOverviews.clear();
2146
0
                auto eErr = poBand->GDALRasterBand::GetHistogram(
2147
0
                    dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange,
2148
0
                    bApproxOK, pfnProgress, pProgressData);
2149
0
                l_poDS->m_apoOverviews = std::move(apoTmpOverviews);
2150
0
                return eErr;
2151
0
            }
2152
0
            else
2153
0
            {
2154
0
                return poBand->GetHistogram(
2155
0
                    dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange,
2156
0
                    bApproxOK, pfnProgress, pProgressData);
2157
0
            }
2158
0
        }
2159
0
    }
2160
2161
9.57k
    if (m_papoSources.size() != 1)
2162
0
        return VRTRasterBand::GetHistogram(dfMin, dfMax, nBuckets, panHistogram,
2163
0
                                           bIncludeOutOfRange, bApproxOK,
2164
0
                                           pfnProgress, pProgressData);
2165
2166
9.57k
    if (pfnProgress == nullptr)
2167
0
        pfnProgress = GDALDummyProgress;
2168
2169
9.57k
    const std::string osFctId("VRTSourcedRasterBand::GetHistogram");
2170
9.57k
    GDALAntiRecursionGuard oGuard(osFctId);
2171
9.57k
    if (oGuard.GetCallDepth() >= 32)
2172
0
    {
2173
0
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
2174
0
        return CE_Failure;
2175
0
    }
2176
2177
9.57k
    GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
2178
9.57k
    if (oGuard2.GetCallDepth() >= 2)
2179
0
    {
2180
0
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
2181
0
        return CE_Failure;
2182
0
    }
2183
2184
    /* -------------------------------------------------------------------- */
2185
    /*      Try with source bands.                                          */
2186
    /* -------------------------------------------------------------------- */
2187
9.57k
    const CPLErr eErr = m_papoSources[0]->GetHistogram(
2188
9.57k
        GetXSize(), GetYSize(), dfMin, dfMax, nBuckets, panHistogram,
2189
9.57k
        bIncludeOutOfRange, bApproxOK, pfnProgress, pProgressData);
2190
9.57k
    if (eErr != CE_None)
2191
5.50k
    {
2192
5.50k
        const CPLErr eErr2 = GDALRasterBand::GetHistogram(
2193
5.50k
            dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange, bApproxOK,
2194
5.50k
            pfnProgress, pProgressData);
2195
5.50k
        return eErr2;
2196
5.50k
    }
2197
2198
4.07k
    SetDefaultHistogram(dfMin, dfMax, nBuckets, panHistogram);
2199
2200
4.07k
    return CE_None;
2201
9.57k
}
2202
2203
/************************************************************************/
2204
/*                             AddSource()                              */
2205
/************************************************************************/
2206
2207
CPLErr VRTSourcedRasterBand::AddSource(VRTSource *poNewSource)
2208
2209
91.5k
{
2210
91.5k
    return AddSource(std::unique_ptr<VRTSource>(poNewSource));
2211
91.5k
}
2212
2213
/************************************************************************/
2214
/*                             AddSource()                              */
2215
/************************************************************************/
2216
2217
CPLErr VRTSourcedRasterBand::AddSource(std::unique_ptr<VRTSource> poNewSource)
2218
2219
179k
{
2220
179k
    auto l_poDS = static_cast<VRTDataset *>(poDS);
2221
179k
    l_poDS->SetNeedsFlush();
2222
179k
    l_poDS->SourceAdded();
2223
2224
179k
    if (poNewSource->IsSimpleSource())
2225
179k
    {
2226
179k
        VRTSimpleSource *poSS =
2227
179k
            static_cast<VRTSimpleSource *>(poNewSource.get());
2228
179k
        if (GetMetadataItem("NBITS", "IMAGE_STRUCTURE") != nullptr)
2229
5.59k
        {
2230
5.59k
            int nBits = atoi(GetMetadataItem("NBITS", "IMAGE_STRUCTURE"));
2231
5.59k
            if (nBits >= 1 && nBits <= 31)
2232
5.56k
            {
2233
5.56k
                poSS->SetMaxValue(static_cast<int>((1U << nBits) - 1));
2234
5.56k
            }
2235
5.59k
        }
2236
179k
    }
2237
2238
179k
    m_papoSources.push_back(std::move(poNewSource));
2239
2240
179k
    return CE_None;
2241
179k
}
2242
2243
/*! @endcond */
2244
2245
/************************************************************************/
2246
/*                              VRTAddSource()                          */
2247
/************************************************************************/
2248
2249
/**
2250
 * @see VRTSourcedRasterBand::AddSource().
2251
 */
2252
2253
CPLErr CPL_STDCALL VRTAddSource(VRTSourcedRasterBandH hVRTBand,
2254
                                VRTSourceH hNewSource)
2255
0
{
2256
0
    VALIDATE_POINTER1(hVRTBand, "VRTAddSource", CE_Failure);
2257
2258
0
    return reinterpret_cast<VRTSourcedRasterBand *>(hVRTBand)->AddSource(
2259
0
        reinterpret_cast<VRTSource *>(hNewSource));
2260
0
}
2261
2262
/*! @cond Doxygen_Suppress */
2263
2264
/************************************************************************/
2265
/*                              XMLInit()                               */
2266
/************************************************************************/
2267
2268
CPLErr VRTSourcedRasterBand::XMLInit(const CPLXMLNode *psTree,
2269
                                     const char *pszVRTPath,
2270
                                     VRTMapSharedResources &oMapSharedSources)
2271
2272
21.8k
{
2273
21.8k
    {
2274
21.8k
        const CPLErr eErr =
2275
21.8k
            VRTRasterBand::XMLInit(psTree, pszVRTPath, oMapSharedSources);
2276
21.8k
        if (eErr != CE_None)
2277
19
            return eErr;
2278
21.8k
    }
2279
2280
    /* -------------------------------------------------------------------- */
2281
    /*      Process sources.                                                */
2282
    /* -------------------------------------------------------------------- */
2283
21.8k
    VRTDriver *const poDriver = dynamic_cast<VRTDriver *>(
2284
21.8k
        GetGDALDriverManager()->GetDriverByName("VRT"));
2285
2286
21.8k
    for (const CPLXMLNode *psChild = psTree->psChild;
2287
129k
         psChild != nullptr && poDriver != nullptr; psChild = psChild->psNext)
2288
107k
    {
2289
107k
        if (psChild->eType != CXT_Element)
2290
46.6k
            continue;
2291
2292
61.1k
        CPLErrorReset();
2293
61.1k
        VRTSource *const poSource =
2294
61.1k
            poDriver->ParseSource(psChild, pszVRTPath, oMapSharedSources);
2295
61.1k
        if (poSource != nullptr)
2296
29.0k
            AddSource(poSource);
2297
32.1k
        else if (CPLGetLastErrorType() != CE_None)
2298
64
            return CE_Failure;
2299
61.1k
    }
2300
2301
    /* -------------------------------------------------------------------- */
2302
    /*      Done.                                                           */
2303
    /* -------------------------------------------------------------------- */
2304
21.7k
    const char *pszSubclass =
2305
21.7k
        CPLGetXMLValue(psTree, "subclass", "VRTSourcedRasterBand");
2306
21.7k
    if (m_papoSources.empty() && !EQUAL(pszSubclass, "VRTDerivedRasterBand"))
2307
980
        CPLDebug("VRT", "No valid sources found for band in VRT file %s",
2308
980
                 GetDataset() ? GetDataset()->GetDescription() : "");
2309
2310
21.7k
    return CE_None;
2311
21.8k
}
2312
2313
/************************************************************************/
2314
/*                           SerializeToXML()                           */
2315
/************************************************************************/
2316
2317
CPLXMLNode *VRTSourcedRasterBand::SerializeToXML(const char *pszVRTPath,
2318
                                                 bool &bHasWarnedAboutRAMUsage,
2319
                                                 size_t &nAccRAMUsage)
2320
2321
55.3k
{
2322
55.3k
    CPLXMLNode *psTree = VRTRasterBand::SerializeToXML(
2323
55.3k
        pszVRTPath, bHasWarnedAboutRAMUsage, nAccRAMUsage);
2324
55.3k
    CPLXMLNode *psLastChild = psTree->psChild;
2325
280k
    while (psLastChild != nullptr && psLastChild->psNext != nullptr)
2326
224k
        psLastChild = psLastChild->psNext;
2327
2328
    /* -------------------------------------------------------------------- */
2329
    /*      Process Sources.                                                */
2330
    /* -------------------------------------------------------------------- */
2331
2332
55.3k
    GIntBig nUsableRAM = -1;
2333
2334
55.3k
    for (const auto &poSource : m_papoSources)
2335
55.3k
    {
2336
55.3k
        CPLXMLNode *const psXMLSrc = poSource->SerializeToXML(pszVRTPath);
2337
2338
55.3k
        if (psXMLSrc == nullptr)
2339
0
            break;
2340
2341
        // Creating the CPLXMLNode tree representation of a VRT can easily
2342
        // take several times RAM usage than its string serialization, or its
2343
        // internal representation in the driver.
2344
        // We multiply the estimate by a factor of 2, experimentally found to
2345
        // be more realistic than the conservative raw estimate.
2346
55.3k
        nAccRAMUsage += 2 * CPLXMLNodeGetRAMUsageEstimate(psXMLSrc);
2347
55.3k
        if (!bHasWarnedAboutRAMUsage && nAccRAMUsage > 512 * 1024 * 1024)
2348
0
        {
2349
0
            if (nUsableRAM < 0)
2350
0
                nUsableRAM = CPLGetUsablePhysicalRAM();
2351
0
            if (nUsableRAM > 0 &&
2352
0
                nAccRAMUsage > static_cast<uint64_t>(nUsableRAM) / 10 * 8)
2353
0
            {
2354
0
                bHasWarnedAboutRAMUsage = true;
2355
0
                CPLError(CE_Warning, CPLE_AppDefined,
2356
0
                         "Serialization of this VRT file has already consumed "
2357
0
                         "at least %.02f GB of RAM over a total of %.02f. This "
2358
0
                         "process may abort",
2359
0
                         double(nAccRAMUsage) / (1024 * 1024 * 1024),
2360
0
                         double(nUsableRAM) / (1024 * 1024 * 1024));
2361
0
            }
2362
0
        }
2363
2364
55.3k
        if (psLastChild == nullptr)
2365
0
            psTree->psChild = psXMLSrc;
2366
55.3k
        else
2367
55.3k
            psLastChild->psNext = psXMLSrc;
2368
55.3k
        psLastChild = psXMLSrc;
2369
55.3k
    }
2370
2371
55.3k
    return psTree;
2372
55.3k
}
2373
2374
/************************************************************************/
2375
/*                     SkipBufferInitialization()                       */
2376
/************************************************************************/
2377
2378
bool VRTSourcedRasterBand::SkipBufferInitialization()
2379
569k
{
2380
569k
    if (m_nSkipBufferInitialization >= 0)
2381
493k
        return m_nSkipBufferInitialization != 0;
2382
    /* -------------------------------------------------------------------- */
2383
    /*      Check if we can avoid buffer initialization.                    */
2384
    /* -------------------------------------------------------------------- */
2385
2386
    // Note: if one day we do alpha compositing, we will need to check that.
2387
76.3k
    m_nSkipBufferInitialization = FALSE;
2388
76.3k
    if (m_papoSources.size() != 1 || !m_papoSources[0]->IsSimpleSource())
2389
866
    {
2390
866
        return false;
2391
866
    }
2392
75.4k
    VRTSimpleSource *poSS =
2393
75.4k
        static_cast<VRTSimpleSource *>(m_papoSources[0].get());
2394
75.4k
    if (poSS->GetType() == VRTSimpleSource::GetTypeStatic())
2395
51.4k
    {
2396
51.4k
        auto l_poBand = poSS->GetRasterBand();
2397
51.4k
        if (l_poBand != nullptr && poSS->m_dfSrcXOff >= 0.0 &&
2398
46.3k
            poSS->m_dfSrcYOff >= 0.0 &&
2399
46.3k
            poSS->m_dfSrcXOff + poSS->m_dfSrcXSize <= l_poBand->GetXSize() &&
2400
46.0k
            poSS->m_dfSrcYOff + poSS->m_dfSrcYSize <= l_poBand->GetYSize() &&
2401
45.8k
            poSS->m_dfDstXOff <= 0.0 && poSS->m_dfDstYOff <= 0.0 &&
2402
45.8k
            poSS->m_dfDstXOff + poSS->m_dfDstXSize >= nRasterXSize &&
2403
45.5k
            poSS->m_dfDstYOff + poSS->m_dfDstYSize >= nRasterYSize)
2404
45.5k
        {
2405
45.5k
            m_nSkipBufferInitialization = TRUE;
2406
45.5k
        }
2407
51.4k
    }
2408
75.4k
    return m_nSkipBufferInitialization != 0;
2409
76.3k
}
2410
2411
/************************************************************************/
2412
/*                          ConfigureSource()                           */
2413
/************************************************************************/
2414
2415
void VRTSourcedRasterBand::ConfigureSource(VRTSimpleSource *poSimpleSource,
2416
                                           GDALRasterBand *poSrcBand,
2417
                                           int bAddAsMaskBand, double dfSrcXOff,
2418
                                           double dfSrcYOff, double dfSrcXSize,
2419
                                           double dfSrcYSize, double dfDstXOff,
2420
                                           double dfDstYOff, double dfDstXSize,
2421
                                           double dfDstYSize)
2422
148k
{
2423
    /* -------------------------------------------------------------------- */
2424
    /*      Default source and dest rectangles.                             */
2425
    /* -------------------------------------------------------------------- */
2426
148k
    if (dfSrcYSize == -1)
2427
51.7k
    {
2428
51.7k
        dfSrcXOff = 0;
2429
51.7k
        dfSrcYOff = 0;
2430
51.7k
        dfSrcXSize = poSrcBand->GetXSize();
2431
51.7k
        dfSrcYSize = poSrcBand->GetYSize();
2432
51.7k
    }
2433
2434
148k
    if (dfDstYSize == -1)
2435
51.7k
    {
2436
51.7k
        dfDstXOff = 0;
2437
51.7k
        dfDstYOff = 0;
2438
51.7k
        dfDstXSize = nRasterXSize;
2439
51.7k
        dfDstYSize = nRasterYSize;
2440
51.7k
    }
2441
2442
148k
    if (bAddAsMaskBand)
2443
8.14k
        poSimpleSource->SetSrcMaskBand(poSrcBand);
2444
140k
    else
2445
140k
        poSimpleSource->SetSrcBand(poSrcBand);
2446
2447
148k
    poSimpleSource->SetSrcWindow(dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize);
2448
148k
    poSimpleSource->SetDstWindow(dfDstXOff, dfDstYOff, dfDstXSize, dfDstYSize);
2449
2450
    /* -------------------------------------------------------------------- */
2451
    /*      If we can get the associated GDALDataset, add a reference to it.*/
2452
    /* -------------------------------------------------------------------- */
2453
148k
    GDALDataset *poSrcBandDataset = poSrcBand->GetDataset();
2454
148k
    if (poSrcBandDataset != nullptr)
2455
148k
    {
2456
148k
        VRTDataset *poVRTSrcBandDataset =
2457
148k
            dynamic_cast<VRTDataset *>(poSrcBandDataset);
2458
148k
        if (poVRTSrcBandDataset && !poVRTSrcBandDataset->m_bCanTakeRef)
2459
0
        {
2460
            // Situation triggered by VRTDataset::AddVirtualOverview()
2461
            // We create an overview dataset that is a VRT of a reduction of
2462
            // ourselves. But we don't want to take a reference on ourselves,
2463
            // otherwise this will prevent us to be closed in number of
2464
            // circumstances
2465
0
            poSimpleSource->m_bDropRefOnSrcBand = false;
2466
0
        }
2467
148k
        else
2468
148k
        {
2469
148k
            poSrcBandDataset->Reference();
2470
148k
        }
2471
148k
    }
2472
148k
}
2473
2474
/************************************************************************/
2475
/*                          AddSimpleSource()                           */
2476
/************************************************************************/
2477
2478
CPLErr VRTSourcedRasterBand::AddSimpleSource(
2479
    const char *pszFilename, int nBandIn, double dfSrcXOff, double dfSrcYOff,
2480
    double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
2481
    double dfDstXSize, double dfDstYSize, const char *pszResampling,
2482
    double dfNoDataValueIn)
2483
2484
1.93k
{
2485
    /* -------------------------------------------------------------------- */
2486
    /*      Create source.                                                  */
2487
    /* -------------------------------------------------------------------- */
2488
1.93k
    VRTSimpleSource *poSimpleSource = nullptr;
2489
2490
1.93k
    if (pszResampling != nullptr && STARTS_WITH_CI(pszResampling, "aver"))
2491
0
    {
2492
0
        auto poAveragedSource = new VRTAveragedSource();
2493
0
        poSimpleSource = poAveragedSource;
2494
0
        if (dfNoDataValueIn != VRT_NODATA_UNSET)
2495
0
            poAveragedSource->SetNoDataValue(dfNoDataValueIn);
2496
0
    }
2497
1.93k
    else
2498
1.93k
    {
2499
1.93k
        poSimpleSource = new VRTSimpleSource();
2500
1.93k
        if (dfNoDataValueIn != VRT_NODATA_UNSET)
2501
0
            CPLError(
2502
0
                CE_Warning, CPLE_AppDefined,
2503
0
                "NODATA setting not currently supported for nearest  "
2504
0
                "neighbour sampled simple sources on Virtual Datasources.");
2505
1.93k
    }
2506
2507
1.93k
    poSimpleSource->SetSrcBand(pszFilename, nBandIn);
2508
2509
1.93k
    poSimpleSource->SetSrcWindow(dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize);
2510
1.93k
    poSimpleSource->SetDstWindow(dfDstXOff, dfDstYOff, dfDstXSize, dfDstYSize);
2511
2512
    /* -------------------------------------------------------------------- */
2513
    /*      add to list.                                                    */
2514
    /* -------------------------------------------------------------------- */
2515
1.93k
    return AddSource(poSimpleSource);
2516
1.93k
}
2517
2518
/************************************************************************/
2519
/*                          AddSimpleSource()                           */
2520
/************************************************************************/
2521
2522
CPLErr VRTSourcedRasterBand::AddSimpleSource(
2523
    GDALRasterBand *poSrcBand, double dfSrcXOff, double dfSrcYOff,
2524
    double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
2525
    double dfDstXSize, double dfDstYSize, const char *pszResampling,
2526
    double dfNoDataValueIn)
2527
2528
52.4k
{
2529
    /* -------------------------------------------------------------------- */
2530
    /*      Create source.                                                  */
2531
    /* -------------------------------------------------------------------- */
2532
52.4k
    VRTSimpleSource *poSimpleSource = nullptr;
2533
2534
52.4k
    if (pszResampling != nullptr && STARTS_WITH_CI(pszResampling, "aver"))
2535
0
    {
2536
0
        auto poAveragedSource = new VRTAveragedSource();
2537
0
        poSimpleSource = poAveragedSource;
2538
0
        if (dfNoDataValueIn != VRT_NODATA_UNSET)
2539
0
            poAveragedSource->SetNoDataValue(dfNoDataValueIn);
2540
0
    }
2541
52.4k
    else
2542
52.4k
    {
2543
52.4k
        poSimpleSource = new VRTSimpleSource();
2544
52.4k
        if (dfNoDataValueIn != VRT_NODATA_UNSET)
2545
0
            CPLError(
2546
0
                CE_Warning, CPLE_AppDefined,
2547
0
                "NODATA setting not currently supported for "
2548
0
                "neighbour sampled simple sources on Virtual Datasources.");
2549
52.4k
    }
2550
2551
52.4k
    ConfigureSource(poSimpleSource, poSrcBand, FALSE, dfSrcXOff, dfSrcYOff,
2552
52.4k
                    dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
2553
52.4k
                    dfDstYSize);
2554
2555
    /* -------------------------------------------------------------------- */
2556
    /*      add to list.                                                    */
2557
    /* -------------------------------------------------------------------- */
2558
52.4k
    return AddSource(poSimpleSource);
2559
52.4k
}
2560
2561
/************************************************************************/
2562
/*                         AddMaskBandSource()                          */
2563
/************************************************************************/
2564
2565
// poSrcBand is not the mask band, but the band from which the mask band is
2566
// taken.
2567
CPLErr VRTSourcedRasterBand::AddMaskBandSource(
2568
    GDALRasterBand *poSrcBand, double dfSrcXOff, double dfSrcYOff,
2569
    double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
2570
    double dfDstXSize, double dfDstYSize)
2571
8.14k
{
2572
    /* -------------------------------------------------------------------- */
2573
    /*      Create source.                                                  */
2574
    /* -------------------------------------------------------------------- */
2575
8.14k
    VRTSimpleSource *poSimpleSource = new VRTSimpleSource();
2576
2577
8.14k
    ConfigureSource(poSimpleSource, poSrcBand, TRUE, dfSrcXOff, dfSrcYOff,
2578
8.14k
                    dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
2579
8.14k
                    dfDstYSize);
2580
2581
    /* -------------------------------------------------------------------- */
2582
    /*      add to list.                                                    */
2583
    /* -------------------------------------------------------------------- */
2584
8.14k
    return AddSource(poSimpleSource);
2585
8.14k
}
2586
2587
/*! @endcond */
2588
2589
/************************************************************************/
2590
/*                         VRTAddSimpleSource()                         */
2591
/************************************************************************/
2592
2593
/**
2594
 * @see VRTSourcedRasterBand::AddSimpleSource().
2595
 */
2596
2597
CPLErr CPL_STDCALL VRTAddSimpleSource(VRTSourcedRasterBandH hVRTBand,
2598
                                      GDALRasterBandH hSrcBand, int nSrcXOff,
2599
                                      int nSrcYOff, int nSrcXSize,
2600
                                      int nSrcYSize, int nDstXOff, int nDstYOff,
2601
                                      int nDstXSize, int nDstYSize,
2602
                                      const char *pszResampling,
2603
                                      double dfNoDataValue)
2604
1.04k
{
2605
1.04k
    VALIDATE_POINTER1(hVRTBand, "VRTAddSimpleSource", CE_Failure);
2606
2607
1.04k
    return reinterpret_cast<VRTSourcedRasterBand *>(hVRTBand)->AddSimpleSource(
2608
1.04k
        reinterpret_cast<GDALRasterBand *>(hSrcBand), nSrcXOff, nSrcYOff,
2609
1.04k
        nSrcXSize, nSrcYSize, nDstXOff, nDstYOff, nDstXSize, nDstYSize,
2610
1.04k
        pszResampling, dfNoDataValue);
2611
1.04k
}
2612
2613
/*! @cond Doxygen_Suppress */
2614
2615
/************************************************************************/
2616
/*                          AddComplexSource()                          */
2617
/************************************************************************/
2618
2619
CPLErr VRTSourcedRasterBand::AddComplexSource(
2620
    const char *pszFilename, int nBandIn, double dfSrcXOff, double dfSrcYOff,
2621
    double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
2622
    double dfDstXSize, double dfDstYSize, double dfScaleOff,
2623
    double dfScaleRatio, double dfNoDataValueIn, int nColorTableComponent)
2624
2625
0
{
2626
    /* -------------------------------------------------------------------- */
2627
    /*      Create source.                                                  */
2628
    /* -------------------------------------------------------------------- */
2629
0
    VRTComplexSource *const poSource = new VRTComplexSource();
2630
2631
0
    poSource->SetSrcBand(pszFilename, nBandIn);
2632
2633
0
    poSource->SetSrcWindow(dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize);
2634
0
    poSource->SetDstWindow(dfDstXOff, dfDstYOff, dfDstXSize, dfDstYSize);
2635
2636
    /* -------------------------------------------------------------------- */
2637
    /*      Set complex parameters.                                         */
2638
    /* -------------------------------------------------------------------- */
2639
0
    if (dfNoDataValueIn != VRT_NODATA_UNSET)
2640
0
        poSource->SetNoDataValue(dfNoDataValueIn);
2641
2642
0
    if (dfScaleOff != 0.0 || dfScaleRatio != 1.0)
2643
0
        poSource->SetLinearScaling(dfScaleOff, dfScaleRatio);
2644
2645
0
    poSource->SetColorTableComponent(nColorTableComponent);
2646
2647
    /* -------------------------------------------------------------------- */
2648
    /*      add to list.                                                    */
2649
    /* -------------------------------------------------------------------- */
2650
0
    return AddSource(poSource);
2651
0
}
2652
2653
/************************************************************************/
2654
/*                          AddComplexSource()                          */
2655
/************************************************************************/
2656
2657
CPLErr VRTSourcedRasterBand::AddComplexSource(
2658
    GDALRasterBand *poSrcBand, double dfSrcXOff, double dfSrcYOff,
2659
    double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
2660
    double dfDstXSize, double dfDstYSize, double dfScaleOff,
2661
    double dfScaleRatio, double dfNoDataValueIn, int nColorTableComponent)
2662
2663
0
{
2664
    /* -------------------------------------------------------------------- */
2665
    /*      Create source.                                                  */
2666
    /* -------------------------------------------------------------------- */
2667
0
    VRTComplexSource *const poSource = new VRTComplexSource();
2668
2669
0
    ConfigureSource(poSource, poSrcBand, FALSE, dfSrcXOff, dfSrcYOff,
2670
0
                    dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
2671
0
                    dfDstYSize);
2672
2673
    /* -------------------------------------------------------------------- */
2674
    /*      Set complex parameters.                                         */
2675
    /* -------------------------------------------------------------------- */
2676
0
    if (dfNoDataValueIn != VRT_NODATA_UNSET)
2677
0
        poSource->SetNoDataValue(dfNoDataValueIn);
2678
2679
0
    if (dfScaleOff != 0.0 || dfScaleRatio != 1.0)
2680
0
        poSource->SetLinearScaling(dfScaleOff, dfScaleRatio);
2681
2682
0
    poSource->SetColorTableComponent(nColorTableComponent);
2683
2684
    /* -------------------------------------------------------------------- */
2685
    /*      add to list.                                                    */
2686
    /* -------------------------------------------------------------------- */
2687
0
    return AddSource(poSource);
2688
0
}
2689
2690
/*! @endcond */
2691
2692
/************************************************************************/
2693
/*                         VRTAddComplexSource()                        */
2694
/************************************************************************/
2695
2696
/**
2697
 * @see VRTSourcedRasterBand::AddComplexSource().
2698
 */
2699
2700
CPLErr CPL_STDCALL VRTAddComplexSource(
2701
    VRTSourcedRasterBandH hVRTBand, GDALRasterBandH hSrcBand, int nSrcXOff,
2702
    int nSrcYOff, int nSrcXSize, int nSrcYSize, int nDstXOff, int nDstYOff,
2703
    int nDstXSize, int nDstYSize, double dfScaleOff, double dfScaleRatio,
2704
    double dfNoDataValue)
2705
0
{
2706
0
    VALIDATE_POINTER1(hVRTBand, "VRTAddComplexSource", CE_Failure);
2707
2708
0
    return reinterpret_cast<VRTSourcedRasterBand *>(hVRTBand)->AddComplexSource(
2709
0
        reinterpret_cast<GDALRasterBand *>(hSrcBand), nSrcXOff, nSrcYOff,
2710
0
        nSrcXSize, nSrcYSize, nDstXOff, nDstYOff, nDstXSize, nDstYSize,
2711
0
        dfScaleOff, dfScaleRatio, dfNoDataValue);
2712
0
}
2713
2714
/*! @cond Doxygen_Suppress */
2715
2716
/************************************************************************/
2717
/*                           AddFuncSource()                            */
2718
/************************************************************************/
2719
2720
CPLErr VRTSourcedRasterBand::AddFuncSource(VRTImageReadFunc pfnReadFunc,
2721
                                           void *pCBData,
2722
                                           double dfNoDataValueIn)
2723
2724
0
{
2725
    /* -------------------------------------------------------------------- */
2726
    /*      Create source.                                                  */
2727
    /* -------------------------------------------------------------------- */
2728
0
    VRTFuncSource *const poFuncSource = new VRTFuncSource;
2729
2730
0
    poFuncSource->fNoDataValue = static_cast<float>(dfNoDataValueIn);
2731
0
    poFuncSource->pfnReadFunc = pfnReadFunc;
2732
0
    poFuncSource->pCBData = pCBData;
2733
0
    poFuncSource->eType = GetRasterDataType();
2734
2735
    /* -------------------------------------------------------------------- */
2736
    /*      add to list.                                                    */
2737
    /* -------------------------------------------------------------------- */
2738
0
    return AddSource(poFuncSource);
2739
0
}
2740
2741
/*! @endcond */
2742
2743
/************************************************************************/
2744
/*                          VRTAddFuncSource()                          */
2745
/************************************************************************/
2746
2747
/**
2748
 * @see VRTSourcedRasterBand::AddFuncSource().
2749
 */
2750
2751
CPLErr CPL_STDCALL VRTAddFuncSource(VRTSourcedRasterBandH hVRTBand,
2752
                                    VRTImageReadFunc pfnReadFunc, void *pCBData,
2753
                                    double dfNoDataValue)
2754
0
{
2755
0
    VALIDATE_POINTER1(hVRTBand, "VRTAddFuncSource", CE_Failure);
2756
2757
0
    return reinterpret_cast<VRTSourcedRasterBand *>(hVRTBand)->AddFuncSource(
2758
0
        pfnReadFunc, pCBData, dfNoDataValue);
2759
0
}
2760
2761
/*! @cond Doxygen_Suppress */
2762
2763
/************************************************************************/
2764
/*                      GetMetadataDomainList()                         */
2765
/************************************************************************/
2766
2767
char **VRTSourcedRasterBand::GetMetadataDomainList()
2768
0
{
2769
0
    return CSLAddString(GDALRasterBand::GetMetadataDomainList(),
2770
0
                        "LocationInfo");
2771
0
}
2772
2773
/************************************************************************/
2774
/*                          GetMetadataItem()                           */
2775
/************************************************************************/
2776
2777
const char *VRTSourcedRasterBand::GetMetadataItem(const char *pszName,
2778
                                                  const char *pszDomain)
2779
2780
338k
{
2781
    /* ==================================================================== */
2782
    /*      LocationInfo handling.                                          */
2783
    /* ==================================================================== */
2784
338k
    if (pszDomain != nullptr && EQUAL(pszDomain, "LocationInfo") &&
2785
0
        (STARTS_WITH_CI(pszName, "Pixel_") ||
2786
0
         STARTS_WITH_CI(pszName, "GeoPixel_")))
2787
0
    {
2788
        /* --------------------------------------------------------------------
2789
         */
2790
        /*      What pixel are we aiming at? */
2791
        /* --------------------------------------------------------------------
2792
         */
2793
0
        int iPixel = 0;
2794
0
        int iLine = 0;
2795
2796
0
        if (STARTS_WITH_CI(pszName, "Pixel_"))
2797
0
        {
2798
            // TODO(schwehr): Replace sscanf.
2799
0
            if (sscanf(pszName + 6, "%d_%d", &iPixel, &iLine) != 2)
2800
0
                return nullptr;
2801
0
        }
2802
0
        else if (STARTS_WITH_CI(pszName, "GeoPixel_"))
2803
0
        {
2804
0
            const double dfGeoX = CPLAtof(pszName + 9);
2805
0
            const char *const pszUnderscore = strchr(pszName + 9, '_');
2806
0
            if (!pszUnderscore)
2807
0
                return nullptr;
2808
0
            const double dfGeoY = CPLAtof(pszUnderscore + 1);
2809
2810
0
            if (GetDataset() == nullptr)
2811
0
                return nullptr;
2812
2813
0
            GDALGeoTransform gt, invGT;
2814
0
            if (GetDataset()->GetGeoTransform(gt) != CE_None ||
2815
0
                !gt.GetInverse(invGT))
2816
0
            {
2817
0
                return nullptr;
2818
0
            }
2819
2820
0
            iPixel = static_cast<int>(
2821
0
                floor(invGT[0] + invGT[1] * dfGeoX + invGT[2] * dfGeoY));
2822
0
            iLine = static_cast<int>(
2823
0
                floor(invGT[3] + invGT[4] * dfGeoX + invGT[5] * dfGeoY));
2824
0
        }
2825
0
        else
2826
0
        {
2827
0
            return nullptr;
2828
0
        }
2829
2830
0
        if (iPixel < 0 || iLine < 0 || iPixel >= GetXSize() ||
2831
0
            iLine >= GetYSize())
2832
0
            return nullptr;
2833
2834
        /* --------------------------------------------------------------------
2835
         */
2836
        /*      Find the file(s) at this location. */
2837
        /* --------------------------------------------------------------------
2838
         */
2839
0
        char **papszFileList = nullptr;
2840
0
        int nListSize = 0;     // keep it in this scope
2841
0
        int nListMaxSize = 0;  // keep it in this scope
2842
0
        CPLHashSet *const hSetFiles =
2843
0
            CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, nullptr);
2844
2845
0
        for (auto &poSource : m_papoSources)
2846
0
        {
2847
0
            if (!poSource->IsSimpleSource())
2848
0
                continue;
2849
2850
0
            VRTSimpleSource *const poSrc =
2851
0
                static_cast<VRTSimpleSource *>(poSource.get());
2852
2853
0
            double dfReqXOff = 0.0;
2854
0
            double dfReqYOff = 0.0;
2855
0
            double dfReqXSize = 0.0;
2856
0
            double dfReqYSize = 0.0;
2857
0
            int nReqXOff = 0;
2858
0
            int nReqYOff = 0;
2859
0
            int nReqXSize = 0;
2860
0
            int nReqYSize = 0;
2861
0
            int nOutXOff = 0;
2862
0
            int nOutYOff = 0;
2863
0
            int nOutXSize = 0;
2864
0
            int nOutYSize = 0;
2865
2866
0
            bool bError = false;
2867
0
            if (!poSrc->GetSrcDstWindow(iPixel, iLine, 1, 1, 1, 1, &dfReqXOff,
2868
0
                                        &dfReqYOff, &dfReqXSize, &dfReqYSize,
2869
0
                                        &nReqXOff, &nReqYOff, &nReqXSize,
2870
0
                                        &nReqYSize, &nOutXOff, &nOutYOff,
2871
0
                                        &nOutXSize, &nOutYSize, bError))
2872
0
            {
2873
0
                if (bError)
2874
0
                {
2875
0
                    CSLDestroy(papszFileList);
2876
0
                    CPLHashSetDestroy(hSetFiles);
2877
0
                    return nullptr;
2878
0
                }
2879
0
                continue;
2880
0
            }
2881
2882
0
            poSrc->GetFileList(&papszFileList, &nListSize, &nListMaxSize,
2883
0
                               hSetFiles);
2884
0
        }
2885
2886
        /* --------------------------------------------------------------------
2887
         */
2888
        /*      Format into XML. */
2889
        /* --------------------------------------------------------------------
2890
         */
2891
0
        m_osLastLocationInfo = "<LocationInfo>";
2892
0
        for (int i = 0; i < nListSize && papszFileList[i] != nullptr; i++)
2893
0
        {
2894
0
            m_osLastLocationInfo += "<File>";
2895
0
            char *const pszXMLEscaped =
2896
0
                CPLEscapeString(papszFileList[i], -1, CPLES_XML);
2897
0
            m_osLastLocationInfo += pszXMLEscaped;
2898
0
            CPLFree(pszXMLEscaped);
2899
0
            m_osLastLocationInfo += "</File>";
2900
0
        }
2901
0
        m_osLastLocationInfo += "</LocationInfo>";
2902
2903
0
        CSLDestroy(papszFileList);
2904
0
        CPLHashSetDestroy(hSetFiles);
2905
2906
0
        return m_osLastLocationInfo.c_str();
2907
0
    }
2908
2909
    /* ==================================================================== */
2910
    /*      Other domains.                                                  */
2911
    /* ==================================================================== */
2912
2913
338k
    return GDALRasterBand::GetMetadataItem(pszName, pszDomain);
2914
338k
}
2915
2916
/************************************************************************/
2917
/*                            GetMetadata()                             */
2918
/************************************************************************/
2919
2920
char **VRTSourcedRasterBand::GetMetadata(const char *pszDomain)
2921
2922
278k
{
2923
    /* ==================================================================== */
2924
    /*      vrt_sources domain handling.                                    */
2925
    /* ==================================================================== */
2926
278k
    if (pszDomain != nullptr && EQUAL(pszDomain, "vrt_sources"))
2927
0
    {
2928
0
        if (static_cast<size_t>(m_aosSourceList.size()) != m_papoSources.size())
2929
0
        {
2930
0
            m_aosSourceList.clear();
2931
2932
            // Process SimpleSources
2933
0
            for (int iSource = 0;
2934
0
                 iSource < static_cast<int>(m_papoSources.size()); iSource++)
2935
0
            {
2936
0
                CPLXMLNode *const psXMLSrc =
2937
0
                    m_papoSources[iSource]->SerializeToXML(nullptr);
2938
0
                if (psXMLSrc == nullptr)
2939
0
                    continue;
2940
2941
0
                char *const pszXML = CPLSerializeXMLTree(psXMLSrc);
2942
2943
0
                m_aosSourceList.AddString(
2944
0
                    CPLSPrintf("source_%d=%s", iSource, pszXML));
2945
0
                CPLFree(pszXML);
2946
0
                CPLDestroyXMLNode(psXMLSrc);
2947
0
            }
2948
0
        }
2949
0
        return m_aosSourceList.List();
2950
0
    }
2951
2952
    /* ==================================================================== */
2953
    /*      Other domains.                                                  */
2954
    /* ==================================================================== */
2955
2956
278k
    return GDALRasterBand::GetMetadata(pszDomain);
2957
278k
}
2958
2959
/************************************************************************/
2960
/*                          SetMetadataItem()                           */
2961
/************************************************************************/
2962
2963
CPLErr VRTSourcedRasterBand::SetMetadataItem(const char *pszName,
2964
                                             const char *pszValue,
2965
                                             const char *pszDomain)
2966
2967
197k
{
2968
#if DEBUG_VERBOSE
2969
    CPLDebug("VRT", "VRTSourcedRasterBand::SetMetadataItem(%s,%s,%s)\n",
2970
             pszName, pszValue ? pszValue : "(null)",
2971
             pszDomain ? pszDomain : "(null)");
2972
#endif
2973
2974
197k
    if (pszDomain != nullptr && EQUAL(pszDomain, "new_vrt_sources"))
2975
0
    {
2976
0
        VRTDriver *const poDriver = dynamic_cast<VRTDriver *>(
2977
0
            GetGDALDriverManager()->GetDriverByName("VRT"));
2978
0
        if (poDriver)
2979
0
        {
2980
0
            CPLXMLNode *const psTree = CPLParseXMLString(pszValue);
2981
0
            if (psTree == nullptr)
2982
0
                return CE_Failure;
2983
2984
0
            auto l_poDS = dynamic_cast<VRTDataset *>(GetDataset());
2985
0
            if (l_poDS == nullptr)
2986
0
            {
2987
0
                CPLDestroyXMLNode(psTree);
2988
0
                return CE_Failure;
2989
0
            }
2990
0
            VRTSource *const poSource = poDriver->ParseSource(
2991
0
                psTree, nullptr, l_poDS->m_oMapSharedSources);
2992
0
            CPLDestroyXMLNode(psTree);
2993
2994
0
            if (poSource != nullptr)
2995
0
                return AddSource(poSource);
2996
0
        }
2997
2998
0
        return CE_Failure;
2999
0
    }
3000
197k
    else if (pszDomain != nullptr && EQUAL(pszDomain, "vrt_sources"))
3001
0
    {
3002
0
        int iSource = 0;
3003
        // TODO(schwehr): Replace sscanf.
3004
0
        if (sscanf(pszName, "source_%d", &iSource) != 1 || iSource < 0 ||
3005
0
            iSource >= static_cast<int>(m_papoSources.size()))
3006
0
        {
3007
0
            CPLError(CE_Failure, CPLE_AppDefined,
3008
0
                     "%s metadata item name is not recognized. "
3009
0
                     "Should be between source_0 and source_%d",
3010
0
                     pszName, static_cast<int>(m_papoSources.size()) - 1);
3011
0
            return CE_Failure;
3012
0
        }
3013
0
        VRTDriver *const poDriver = dynamic_cast<VRTDriver *>(
3014
0
            GetGDALDriverManager()->GetDriverByName("VRT"));
3015
0
        if (poDriver)
3016
0
        {
3017
0
            CPLXMLNode *const psTree = CPLParseXMLString(pszValue);
3018
0
            if (psTree == nullptr)
3019
0
                return CE_Failure;
3020
3021
0
            auto l_poDS = dynamic_cast<VRTDataset *>(GetDataset());
3022
0
            if (l_poDS == nullptr)
3023
0
            {
3024
0
                CPLDestroyXMLNode(psTree);
3025
0
                return CE_Failure;
3026
0
            }
3027
0
            auto poSource = std::unique_ptr<VRTSource>(poDriver->ParseSource(
3028
0
                psTree, nullptr, l_poDS->m_oMapSharedSources));
3029
0
            CPLDestroyXMLNode(psTree);
3030
3031
0
            if (poSource != nullptr)
3032
0
            {
3033
0
                m_papoSources[iSource] = std::move(poSource);
3034
0
                static_cast<VRTDataset *>(poDS)->SetNeedsFlush();
3035
0
                return CE_None;
3036
0
            }
3037
0
        }
3038
0
        return CE_Failure;
3039
0
    }
3040
3041
197k
    return VRTRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
3042
197k
}
3043
3044
/************************************************************************/
3045
/*                            SetMetadata()                             */
3046
/************************************************************************/
3047
3048
CPLErr VRTSourcedRasterBand::SetMetadata(char **papszNewMD,
3049
                                         const char *pszDomain)
3050
3051
139k
{
3052
139k
    if (pszDomain != nullptr && (EQUAL(pszDomain, "new_vrt_sources") ||
3053
139k
                                 EQUAL(pszDomain, "vrt_sources")))
3054
0
    {
3055
0
        VRTDriver *const poDriver = dynamic_cast<VRTDriver *>(
3056
0
            GetGDALDriverManager()->GetDriverByName("VRT"));
3057
0
        if (!poDriver)
3058
0
            return CE_Failure;
3059
3060
0
        if (EQUAL(pszDomain, "vrt_sources"))
3061
0
        {
3062
0
            m_papoSources.clear();
3063
0
        }
3064
3065
0
        for (const char *const pszMDItem :
3066
0
             cpl::Iterate(CSLConstList(papszNewMD)))
3067
0
        {
3068
0
            const char *const pszXML = CPLParseNameValue(pszMDItem, nullptr);
3069
0
            CPLXMLTreeCloser psTree(CPLParseXMLString(pszXML));
3070
0
            if (!psTree)
3071
0
                return CE_Failure;
3072
3073
0
            auto l_poDS = dynamic_cast<VRTDataset *>(GetDataset());
3074
0
            if (l_poDS == nullptr)
3075
0
            {
3076
0
                return CE_Failure;
3077
0
            }
3078
0
            VRTSource *const poSource = poDriver->ParseSource(
3079
0
                psTree.get(), nullptr, l_poDS->m_oMapSharedSources);
3080
0
            if (poSource == nullptr)
3081
0
                return CE_Failure;
3082
3083
0
            const CPLErr eErr = AddSource(poSource);
3084
            // cppcheck-suppress knownConditionTrueFalse
3085
0
            if (eErr != CE_None)
3086
0
                return eErr;
3087
0
        }
3088
3089
0
        return CE_None;
3090
0
    }
3091
3092
139k
    return VRTRasterBand::SetMetadata(papszNewMD, pszDomain);
3093
139k
}
3094
3095
/************************************************************************/
3096
/*                             GetFileList()                            */
3097
/************************************************************************/
3098
3099
void VRTSourcedRasterBand::GetFileList(char ***ppapszFileList, int *pnSize,
3100
                                       int *pnMaxSize, CPLHashSet *hSetFiles)
3101
21.9k
{
3102
21.9k
    for (auto &poSource : m_papoSources)
3103
29.2k
    {
3104
29.2k
        poSource->GetFileList(ppapszFileList, pnSize, pnMaxSize, hSetFiles);
3105
29.2k
    }
3106
3107
21.9k
    VRTRasterBand::GetFileList(ppapszFileList, pnSize, pnMaxSize, hSetFiles);
3108
21.9k
}
3109
3110
/************************************************************************/
3111
/*                        CloseDependentDatasets()                      */
3112
/************************************************************************/
3113
3114
int VRTSourcedRasterBand::CloseDependentDatasets()
3115
172k
{
3116
172k
    int ret = VRTRasterBand::CloseDependentDatasets();
3117
3118
172k
    if (m_papoSources.empty())
3119
1.07k
        return ret;
3120
3121
170k
    m_papoSources.clear();
3122
3123
170k
    return TRUE;
3124
172k
}
3125
3126
/************************************************************************/
3127
/*                               FlushCache()                           */
3128
/************************************************************************/
3129
3130
CPLErr VRTSourcedRasterBand::FlushCache(bool bAtClosing)
3131
228k
{
3132
228k
    CPLErr eErr = VRTRasterBand::FlushCache(bAtClosing);
3133
463k
    for (size_t i = 0; i < m_papoSources.size() && eErr == CE_None; i++)
3134
235k
    {
3135
235k
        eErr = m_papoSources[i]->FlushCache(bAtClosing);
3136
235k
    }
3137
228k
    return eErr;
3138
228k
}
3139
3140
/************************************************************************/
3141
/*                           RemoveCoveredSources()                     */
3142
/************************************************************************/
3143
3144
/** Remove sources that are covered by other sources.
3145
 *
3146
 * This method removes sources that are covered entirely by (one or several)
3147
 * sources of higher priority (even if they declare a nodata setting).
3148
 * This optimizes the size of the VRT and the rendering time.
3149
 */
3150
void VRTSourcedRasterBand::RemoveCoveredSources(CSLConstList papszOptions)
3151
0
{
3152
0
#ifndef HAVE_GEOS
3153
0
    if (CPLTestBool(CSLFetchNameValueDef(
3154
0
            papszOptions, "EMIT_ERROR_IF_GEOS_NOT_AVAILABLE", "TRUE")))
3155
0
    {
3156
0
        CPLError(CE_Failure, CPLE_NotSupported,
3157
0
                 "RemoveCoveredSources() not implemented in builds "
3158
0
                 "without GEOS support");
3159
0
    }
3160
#else
3161
    (void)papszOptions;
3162
3163
    CPLRectObj globalBounds;
3164
    globalBounds.minx = 0;
3165
    globalBounds.miny = 0;
3166
    globalBounds.maxx = nRasterXSize;
3167
    globalBounds.maxy = nRasterYSize;
3168
3169
    // Create an index with the bbox of all sources
3170
    CPLQuadTree *hTree = CPLQuadTreeCreate(&globalBounds, nullptr);
3171
    for (int i = 0; i < static_cast<int>(m_papoSources.size()); i++)
3172
    {
3173
        if (m_papoSources[i]->IsSimpleSource())
3174
        {
3175
            VRTSimpleSource *poSS =
3176
                cpl::down_cast<VRTSimpleSource *>(m_papoSources[i].get());
3177
            void *hFeature =
3178
                reinterpret_cast<void *>(static_cast<uintptr_t>(i));
3179
            CPLRectObj rect;
3180
            rect.minx = std::max(0.0, poSS->m_dfDstXOff);
3181
            rect.miny = std::max(0.0, poSS->m_dfDstYOff);
3182
            rect.maxx = std::min(double(nRasterXSize),
3183
                                 poSS->m_dfDstXOff + poSS->m_dfDstXSize);
3184
            rect.maxy = std::min(double(nRasterYSize),
3185
                                 poSS->m_dfDstYOff + poSS->m_dfDstYSize);
3186
            CPLQuadTreeInsertWithBounds(hTree, hFeature, &rect);
3187
        }
3188
    }
3189
3190
    for (int i = 0; i < static_cast<int>(m_papoSources.size()); i++)
3191
    {
3192
        if (m_papoSources[i]->IsSimpleSource())
3193
        {
3194
            VRTSimpleSource *poSS =
3195
                cpl::down_cast<VRTSimpleSource *>(m_papoSources[i].get());
3196
            CPLRectObj rect;
3197
            rect.minx = std::max(0.0, poSS->m_dfDstXOff);
3198
            rect.miny = std::max(0.0, poSS->m_dfDstYOff);
3199
            rect.maxx = std::min(double(nRasterXSize),
3200
                                 poSS->m_dfDstXOff + poSS->m_dfDstXSize);
3201
            rect.maxy = std::min(double(nRasterYSize),
3202
                                 poSS->m_dfDstYOff + poSS->m_dfDstYSize);
3203
3204
            // Find sources whose extent intersect with the current one
3205
            int nFeatureCount = 0;
3206
            void **pahFeatures =
3207
                CPLQuadTreeSearch(hTree, &rect, &nFeatureCount);
3208
3209
            // Compute the bounding box of those sources, only if they are
3210
            // on top of the current one
3211
            CPLRectObj rectIntersecting;
3212
            rectIntersecting.minx = std::numeric_limits<double>::max();
3213
            rectIntersecting.miny = std::numeric_limits<double>::max();
3214
            rectIntersecting.maxx = -std::numeric_limits<double>::max();
3215
            rectIntersecting.maxy = -std::numeric_limits<double>::max();
3216
            for (int j = 0; j < nFeatureCount; j++)
3217
            {
3218
                const int curFeature = static_cast<int>(
3219
                    reinterpret_cast<uintptr_t>(pahFeatures[j]));
3220
                if (curFeature > i)
3221
                {
3222
                    VRTSimpleSource *poOtherSS =
3223
                        cpl::down_cast<VRTSimpleSource *>(
3224
                            m_papoSources[curFeature].get());
3225
                    rectIntersecting.minx =
3226
                        std::min(rectIntersecting.minx, poOtherSS->m_dfDstXOff);
3227
                    rectIntersecting.miny =
3228
                        std::min(rectIntersecting.miny, poOtherSS->m_dfDstYOff);
3229
                    rectIntersecting.maxx = std::max(
3230
                        rectIntersecting.maxx,
3231
                        poOtherSS->m_dfDstXOff + poOtherSS->m_dfDstXSize);
3232
                    rectIntersecting.maxy = std::max(
3233
                        rectIntersecting.maxy,
3234
                        poOtherSS->m_dfDstYOff + poOtherSS->m_dfDstXSize);
3235
                }
3236
            }
3237
3238
            // If the boundinx box of those sources overlap the current one,
3239
            // then compute their union, and check if it contains the current
3240
            // source
3241
            if (rectIntersecting.minx <= rect.minx &&
3242
                rectIntersecting.miny <= rect.miny &&
3243
                rectIntersecting.maxx >= rect.maxx &&
3244
                rectIntersecting.maxy >= rect.maxy)
3245
            {
3246
                OGRPolygon oPoly;
3247
                {
3248
                    auto poLR = new OGRLinearRing();
3249
                    poLR->addPoint(rect.minx, rect.miny);
3250
                    poLR->addPoint(rect.minx, rect.maxy);
3251
                    poLR->addPoint(rect.maxx, rect.maxy);
3252
                    poLR->addPoint(rect.maxx, rect.miny);
3253
                    poLR->addPoint(rect.minx, rect.miny);
3254
                    oPoly.addRingDirectly(poLR);
3255
                }
3256
3257
                std::unique_ptr<OGRGeometry> poUnion;
3258
                for (int j = 0; j < nFeatureCount; j++)
3259
                {
3260
                    const int curFeature = static_cast<int>(
3261
                        reinterpret_cast<uintptr_t>(pahFeatures[j]));
3262
                    if (curFeature > i)
3263
                    {
3264
                        VRTSimpleSource *poOtherSS =
3265
                            cpl::down_cast<VRTSimpleSource *>(
3266
                                m_papoSources[curFeature].get());
3267
                        CPLRectObj otherRect;
3268
                        otherRect.minx = std::max(0.0, poOtherSS->m_dfDstXOff);
3269
                        otherRect.miny = std::max(0.0, poOtherSS->m_dfDstYOff);
3270
                        otherRect.maxx = std::min(double(nRasterXSize),
3271
                                                  poOtherSS->m_dfDstXOff +
3272
                                                      poOtherSS->m_dfDstXSize);
3273
                        otherRect.maxy = std::min(double(nRasterYSize),
3274
                                                  poOtherSS->m_dfDstYOff +
3275
                                                      poOtherSS->m_dfDstYSize);
3276
                        OGRPolygon oOtherPoly;
3277
                        {
3278
                            auto poLR = new OGRLinearRing();
3279
                            poLR->addPoint(otherRect.minx, otherRect.miny);
3280
                            poLR->addPoint(otherRect.minx, otherRect.maxy);
3281
                            poLR->addPoint(otherRect.maxx, otherRect.maxy);
3282
                            poLR->addPoint(otherRect.maxx, otherRect.miny);
3283
                            poLR->addPoint(otherRect.minx, otherRect.miny);
3284
                            oOtherPoly.addRingDirectly(poLR);
3285
                        }
3286
                        if (poUnion == nullptr)
3287
                            poUnion.reset(oOtherPoly.clone());
3288
                        else
3289
                            poUnion.reset(oOtherPoly.Union(poUnion.get()));
3290
                    }
3291
                }
3292
3293
                if (poUnion != nullptr && poUnion->Contains(&oPoly))
3294
                {
3295
                    // We can remove the current source
3296
                    m_papoSources[i].reset();
3297
                }
3298
            }
3299
            CPLFree(pahFeatures);
3300
3301
            void *hFeature =
3302
                reinterpret_cast<void *>(static_cast<uintptr_t>(i));
3303
            CPLQuadTreeRemove(hTree, hFeature, &rect);
3304
        }
3305
    }
3306
3307
    // Compact the papoSources array
3308
    m_papoSources.erase(std::remove_if(m_papoSources.begin(),
3309
                                       m_papoSources.end(),
3310
                                       [](const std::unique_ptr<VRTSource> &src)
3311
                                       { return src.get() == nullptr; }),
3312
                        m_papoSources.end());
3313
3314
    CPLQuadTreeDestroy(hTree);
3315
#endif
3316
0
}
3317
3318
/*! @endcond */