Coverage Report

Created: 2025-11-16 06:25

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
0
{
59
0
    VRTRasterBand::Initialize(poDSIn->GetRasterXSize(),
60
0
                              poDSIn->GetRasterYSize());
61
62
0
    poDS = poDSIn;
63
0
    nBand = nBandIn;
64
0
}
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
0
    : VRTSourcedRasterBand(poDSIn, nBandIn, eType, nXSize, nYSize, 0, 0)
86
0
{
87
0
}
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
0
{
98
0
    VRTRasterBand::Initialize(nXSize, nYSize);
99
100
0
    poDS = poDSIn;
101
0
    nBand = nBandIn;
102
103
0
    eDataType = eType;
104
0
    if (nBlockXSizeIn > 0)
105
0
        nBlockXSize = nBlockXSizeIn;
106
0
    if (nBlockYSizeIn > 0)
107
0
        nBlockYSize = nBlockYSizeIn;
108
0
}
109
110
/************************************************************************/
111
/*                       ~VRTSourcedRasterBand()                        */
112
/************************************************************************/
113
114
VRTSourcedRasterBand::~VRTSourcedRasterBand()
115
116
0
{
117
0
    VRTSourcedRasterBand::CloseDependentDatasets();
118
0
}
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
0
{
128
0
    const auto IsNonNearestInvolved = [this, psExtraArg]
129
0
    {
130
0
        if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
131
0
        {
132
0
            return true;
133
0
        }
134
0
        for (auto &poSource : m_papoSources)
135
0
        {
136
0
            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
0
        }
147
0
        return false;
148
0
    };
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
0
    if (eRWFlag == GF_Read && (nXSize != nBufXSize || nYSize != nBufYSize) &&
157
0
        !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
0
    return true;
272
0
}
273
274
/************************************************************************/
275
/*                      CanMultiThreadRasterIO()                        */
276
/************************************************************************/
277
278
bool VRTSourcedRasterBand::CanMultiThreadRasterIO(
279
    double dfXOff, double dfYOff, double dfXSize, double dfYSize,
280
    int &nContributingSources) const
281
0
{
282
0
    int iLastSource = 0;
283
0
    CPLRectObj sSourceBounds;
284
0
    CPLQuadTree *hQuadTree = nullptr;
285
0
    bool bRet = true;
286
0
    std::set<std::string> oSetDSName;
287
288
0
    nContributingSources = 0;
289
0
    for (int iSource = 0; iSource < static_cast<int>(m_papoSources.size());
290
0
         iSource++)
291
0
    {
292
0
        const auto &poSource = m_papoSources[iSource];
293
0
        if (!poSource->IsSimpleSource())
294
0
        {
295
0
            bRet = false;
296
0
            break;
297
0
        }
298
0
        const auto poSimpleSource =
299
0
            cpl::down_cast<VRTSimpleSource *>(poSource.get());
300
0
        if (poSimpleSource->DstWindowIntersects(dfXOff, dfYOff, dfXSize,
301
0
                                                dfYSize))
302
0
        {
303
            // Only build hQuadTree if there are 2 or more sources
304
0
            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
0
            if (oSetDSName.find(poSimpleSource->m_osSrcDSName) !=
332
0
                oSetDSName.end())
333
0
            {
334
0
                bRet = false;
335
0
                break;
336
0
            }
337
0
            oSetDSName.insert(poSimpleSource->m_osSrcDSName);
338
339
0
            double dfSourceXOff;
340
0
            double dfSourceYOff;
341
0
            double dfSourceXSize;
342
0
            double dfSourceYSize;
343
0
            poSimpleSource->GetDstWindow(dfSourceXOff, dfSourceYOff,
344
0
                                         dfSourceXSize, dfSourceYSize);
345
0
            constexpr double EPSILON = 1e-1;
346
0
            sSourceBounds.minx = dfSourceXOff + EPSILON;
347
0
            sSourceBounds.miny = dfSourceYOff + EPSILON;
348
0
            sSourceBounds.maxx = dfSourceXOff + dfSourceXSize - EPSILON;
349
0
            sSourceBounds.maxy = dfSourceYOff + dfSourceYSize - EPSILON;
350
0
            iLastSource = iSource;
351
352
0
            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
0
            ++nContributingSources;
368
0
        }
369
0
    }
370
371
0
    if (hQuadTree)
372
0
        CPLQuadTreeDestroy(hQuadTree);
373
374
0
    return bRet;
375
0
}
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
0
{
463
0
    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
0
    const std::string osFctId("VRTSourcedRasterBand::IRasterIO");
471
0
    GDALAntiRecursionGuard oGuard(osFctId);
472
0
    if (oGuard.GetCallDepth() >= 32)
473
0
    {
474
0
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
475
0
        return CE_Failure;
476
0
    }
477
478
0
    GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
479
    // Allow 2 recursion depths on the same dataset for non-nearest resampling
480
0
    if (oGuard2.GetCallDepth() > 2)
481
0
    {
482
0
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
483
0
        return CE_Failure;
484
0
    }
485
486
    /* ==================================================================== */
487
    /*      Do we have overviews that would be appropriate to satisfy       */
488
    /*      this request?                                                   */
489
    /* ==================================================================== */
490
0
    auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
491
0
    if (l_poDS &&
492
0
        l_poDS->m_apoOverviews.empty() &&  // do not use virtual overviews
493
0
        (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
0
    if (l_poDS && !CanIRasterIOBeForwardedToEachSource(
508
0
                      eRWFlag, nXOff, nYOff, nXSize, nYSize, nBufXSize,
509
0
                      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
0
    if (SkipBufferInitialization())
561
0
    {
562
        // Do nothing
563
0
    }
564
0
    else if (nPixelSpace == GDALGetDataTypeSizeBytes(eBufType) &&
565
0
             !(m_bNoDataValueSet && m_dfNoDataValue != 0.0) &&
566
0
             !(m_bNoDataSetAsInt64 && m_nNoDataValueInt64 != 0) &&
567
0
             !(m_bNoDataSetAsUInt64 && m_nNoDataValueUInt64 != 0))
568
0
    {
569
0
        if (nLineSpace == nBufXSize * nPixelSpace)
570
0
        {
571
0
            memset(pData, 0, static_cast<size_t>(nBufYSize * nLineSpace));
572
0
        }
573
0
        else
574
0
        {
575
0
            for (int iLine = 0; iLine < nBufYSize; iLine++)
576
0
            {
577
0
                memset(static_cast<GByte *>(pData) +
578
0
                           static_cast<GIntBig>(iLine) * nLineSpace,
579
0
                       0, static_cast<size_t>(nBufXSize * nPixelSpace));
580
0
            }
581
0
        }
582
0
    }
583
0
    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
0
    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
0
    else
604
0
    {
605
0
        double dfWriteValue = 0.0;
606
0
        if (m_bNoDataValueSet)
607
0
            dfWriteValue = m_dfNoDataValue;
608
609
0
        for (int iLine = 0; iLine < nBufYSize; iLine++)
610
0
        {
611
0
            GDALCopyWords(&dfWriteValue, GDT_Float64, 0,
612
0
                          static_cast<GByte *>(pData) +
613
0
                              static_cast<GIntBig>(nLineSpace) * iLine,
614
0
                          eBufType, static_cast<int>(nPixelSpace), nBufXSize);
615
0
        }
616
0
    }
617
618
    /* -------------------------------------------------------------------- */
619
    /*      Overlay each source in turn over top this.                      */
620
    /* -------------------------------------------------------------------- */
621
0
    CPLErr eErr = CE_None;
622
623
0
    double dfXOff = nXOff;
624
0
    double dfYOff = nYOff;
625
0
    double dfXSize = nXSize;
626
0
    double dfYSize = nYSize;
627
0
    if (psExtraArg->bFloatingPointWindowValidity)
628
0
    {
629
0
        dfXOff = psExtraArg->dfXOff;
630
0
        dfYOff = psExtraArg->dfYOff;
631
0
        dfXSize = psExtraArg->dfXSize;
632
0
        dfYSize = psExtraArg->dfYSize;
633
0
    }
634
635
0
    if (l_poDS)
636
0
        l_poDS->m_bMultiThreadedRasterIOLastUsed = false;
637
638
0
    int nContributingSources = 0;
639
0
    int nMaxThreads = 0;
640
0
    constexpr int MINIMUM_PIXEL_COUNT_FOR_THREADED_IO = 1000 * 1000;
641
0
    if (l_poDS &&
642
0
        (static_cast<int64_t>(nBufXSize) * nBufYSize >=
643
0
             MINIMUM_PIXEL_COUNT_FOR_THREADED_IO ||
644
0
         static_cast<int64_t>(nXSize) * nYSize >=
645
0
             MINIMUM_PIXEL_COUNT_FOR_THREADED_IO) &&
646
0
        CanMultiThreadRasterIO(dfXOff, dfYOff, dfXSize, dfYSize,
647
0
                               nContributingSources) &&
648
0
        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
0
    else
737
0
    {
738
0
        GDALProgressFunc const pfnProgressGlobal = psExtraArg->pfnProgress;
739
0
        void *const pProgressDataGlobal = psExtraArg->pProgressData;
740
741
0
        VRTSource::WorkingState oWorkingState;
742
0
        const int nSources = static_cast<int>(m_papoSources.size());
743
0
        for (int iSource = 0; eErr == CE_None && iSource < nSources; iSource++)
744
0
        {
745
0
            psExtraArg->pfnProgress = GDALScaledProgress;
746
0
            psExtraArg->pProgressData = GDALCreateScaledProgress(
747
0
                1.0 * iSource / nSources, 1.0 * (iSource + 1) / nSources,
748
0
                pfnProgressGlobal, pProgressDataGlobal);
749
0
            if (psExtraArg->pProgressData == nullptr)
750
0
                psExtraArg->pfnProgress = nullptr;
751
752
0
            eErr = m_papoSources[iSource]->RasterIO(
753
0
                eDataType, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
754
0
                nBufYSize, eBufType, nPixelSpace, nLineSpace, psExtraArg,
755
0
                l_poDS ? l_poDS->m_oWorkingState : oWorkingState);
756
757
0
            GDALDestroyScaledProgress(psExtraArg->pProgressData);
758
0
        }
759
760
0
        psExtraArg->pfnProgress = pfnProgressGlobal;
761
0
        psExtraArg->pProgressData = pProgressDataGlobal;
762
0
    }
763
764
0
    if (eErr == CE_None && psExtraArg->pfnProgress)
765
0
    {
766
0
        psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
767
0
    }
768
769
0
    return eErr;
770
0
}
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
0
{
781
0
    if (pdfDataPct)
782
0
        *pdfDataPct = -1.0;
783
784
    // Particular case for a single simple source covering the whole dataset
785
0
    if (m_papoSources.size() == 1 && m_papoSources[0]->IsSimpleSource() &&
786
0
        m_papoSources[0]->GetType() == VRTSimpleSource::GetTypeStatic())
787
0
    {
788
0
        VRTSimpleSource *poSource =
789
0
            static_cast<VRTSimpleSource *>(m_papoSources[0].get());
790
791
0
        GDALRasterBand *poBand = poSource->GetRasterBand();
792
0
        if (!poBand)
793
0
            poBand = poSource->GetMaskBandMainBand();
794
0
        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
0
        double dfReqXOff = 0.0;
802
0
        double dfReqYOff = 0.0;
803
0
        double dfReqXSize = 0.0;
804
0
        double dfReqYSize = 0.0;
805
0
        int nReqXOff = 0;
806
0
        int nReqYOff = 0;
807
0
        int nReqXSize = 0;
808
0
        int nReqYSize = 0;
809
0
        int nOutXOff = 0;
810
0
        int nOutYOff = 0;
811
0
        int nOutXSize = 0;
812
0
        int nOutYSize = 0;
813
0
        bool bError = false;
814
0
        if (poSource->GetSrcDstWindow(
815
0
                0, 0, GetXSize(), GetYSize(), GetXSize(), GetYSize(),
816
0
                &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, &nReqXOff,
817
0
                &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
818
0
                &nOutXSize, &nOutYSize, bError) &&
819
0
            nReqXOff == 0 && nReqYOff == 0 && nReqXSize == GetXSize() &&
820
0
            nReqXSize == poBand->GetXSize() && nReqYSize == GetYSize() &&
821
0
            nReqYSize == poBand->GetYSize() && nOutXOff == 0 && nOutYOff == 0 &&
822
0
            nOutXSize == GetXSize() && nOutYSize == GetYSize())
823
0
        {
824
0
            return poBand->GetDataCoverageStatus(nXOff, nYOff, nXSize, nYSize,
825
0
                                                 nMaskFlagStop, pdfDataPct);
826
0
        }
827
0
    }
828
829
0
#ifndef HAVE_GEOS
830
0
    return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
831
0
           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
0
}
925
926
/************************************************************************/
927
/*                             IReadBlock()                             */
928
/************************************************************************/
929
930
CPLErr VRTSourcedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
931
                                        void *pImage)
932
933
0
{
934
0
    const int nPixelSize = GDALGetDataTypeSizeBytes(eDataType);
935
936
0
    int nReadXSize = 0;
937
0
    if ((nBlockXOff + 1) * nBlockXSize > GetXSize())
938
0
        nReadXSize = GetXSize() - nBlockXOff * nBlockXSize;
939
0
    else
940
0
        nReadXSize = nBlockXSize;
941
942
0
    int nReadYSize = 0;
943
0
    if ((nBlockYOff + 1) * nBlockYSize > GetYSize())
944
0
        nReadYSize = GetYSize() - nBlockYOff * nBlockYSize;
945
0
    else
946
0
        nReadYSize = nBlockYSize;
947
948
0
    GDALRasterIOExtraArg sExtraArg;
949
0
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
950
951
0
    return IRasterIO(
952
0
        GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, nReadXSize,
953
0
        nReadYSize, pImage, nReadXSize, nReadYSize, eDataType, nPixelSize,
954
0
        static_cast<GSpacing>(nPixelSize) * nBlockXSize, &sExtraArg);
955
0
}
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
0
{
994
0
    const char *pszUseSources =
995
0
        CPLGetConfigOption("VRT_MIN_MAX_FROM_SOURCES", nullptr);
996
0
    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
0
    struct CPLTimeVal tvStart;
1006
0
    memset(&tvStart, 0, sizeof(CPLTimeVal));
1007
0
    if (m_papoSources.size() > 1)
1008
0
        CPLGettimeofday(&tvStart, nullptr);
1009
0
    for (auto &poSource : m_papoSources)
1010
0
    {
1011
0
        if (!(poSource->IsSimpleSource()))
1012
0
            return false;
1013
0
        VRTSimpleSource *const poSimpleSource =
1014
0
            static_cast<VRTSimpleSource *>(poSource.get());
1015
0
        const char *pszFilename = poSimpleSource->m_osSrcDSName.c_str();
1016
        // /vsimem/ should be fast.
1017
0
        if (STARTS_WITH(pszFilename, "/vsimem/"))
1018
0
            continue;
1019
        // but not other /vsi filesystems
1020
0
        if (STARTS_WITH(pszFilename, "/vsi"))
1021
0
            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
0
}
1051
1052
/************************************************************************/
1053
/*                             GetMinimum()                             */
1054
/************************************************************************/
1055
1056
double VRTSourcedRasterBand::GetMinimum(int *pbSuccess)
1057
0
{
1058
0
    const char *const pszValue = GetMetadataItem("STATISTICS_MINIMUM");
1059
0
    if (pszValue != nullptr)
1060
0
    {
1061
0
        if (pbSuccess != nullptr)
1062
0
            *pbSuccess = TRUE;
1063
1064
0
        return CPLAtofM(pszValue);
1065
0
    }
1066
1067
0
    if (!CanUseSourcesMinMaxImplementations())
1068
0
        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
0
{
1136
0
    const char *const pszValue = GetMetadataItem("STATISTICS_MAXIMUM");
1137
0
    if (pszValue != nullptr)
1138
0
    {
1139
0
        if (pbSuccess != nullptr)
1140
0
            *pbSuccess = TRUE;
1141
1142
0
        return CPLAtofM(pszValue);
1143
0
    }
1144
1145
0
    if (!CanUseSourcesMinMaxImplementations())
1146
0
        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
0
{
1220
0
    bool bRet = true;
1221
0
    CPLRectObj sGlobalBounds;
1222
0
    sGlobalBounds.minx = 0;
1223
0
    sGlobalBounds.miny = 0;
1224
0
    sGlobalBounds.maxx = nRasterXSize;
1225
0
    sGlobalBounds.maxy = nRasterYSize;
1226
0
    CPLQuadTree *hQuadTree = CPLQuadTreeCreate(&sGlobalBounds, nullptr);
1227
0
    for (int i = 0; i < static_cast<int>(m_papoSources.size()); ++i)
1228
0
    {
1229
0
        if (!m_papoSources[i]->IsSimpleSource())
1230
0
        {
1231
0
            bRet = false;
1232
0
            break;
1233
0
        }
1234
1235
0
        auto poSimpleSource =
1236
0
            cpl::down_cast<VRTSimpleSource *>(m_papoSources[i].get());
1237
0
        const char *pszType = poSimpleSource->GetType();
1238
0
        if (pszType == VRTSimpleSource::GetTypeStatic())
1239
0
        {
1240
            // ok
1241
0
        }
1242
0
        else if (pszType == VRTComplexSource::GetTypeStatic())
1243
0
        {
1244
0
            auto poComplexSource =
1245
0
                cpl::down_cast<VRTComplexSource *>(poSimpleSource);
1246
0
            if (!poComplexSource->AreValuesUnchanged())
1247
0
            {
1248
0
                bRet = false;
1249
0
                break;
1250
0
            }
1251
0
        }
1252
0
        else
1253
0
        {
1254
0
            bRet = false;
1255
0
            break;
1256
0
        }
1257
1258
0
        if (!bAllowMaxValAdjustment && poSimpleSource->NeedMaxValAdjustment())
1259
0
        {
1260
0
            bRet = false;
1261
0
            break;
1262
0
        }
1263
1264
0
        auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
1265
0
        if (poSimpleSourceBand == nullptr ||
1266
0
            poSimpleSourceBand->GetRasterDataType() != eDataType)
1267
0
        {
1268
0
            bRet = false;
1269
0
            break;
1270
0
        }
1271
1272
0
        double dfReqXOff = 0.0;
1273
0
        double dfReqYOff = 0.0;
1274
0
        double dfReqXSize = 0.0;
1275
0
        double dfReqYSize = 0.0;
1276
0
        int nReqXOff = 0;
1277
0
        int nReqYOff = 0;
1278
0
        int nReqXSize = 0;
1279
0
        int nReqYSize = 0;
1280
0
        int nOutXOff = 0;
1281
0
        int nOutYOff = 0;
1282
0
        int nOutXSize = 0;
1283
0
        int nOutYSize = 0;
1284
1285
0
        bool bError = false;
1286
0
        if (!poSimpleSource->GetSrcDstWindow(
1287
0
                0, 0, nRasterXSize, nRasterYSize, nRasterXSize, nRasterYSize,
1288
0
                &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, &nReqXOff,
1289
0
                &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
1290
0
                &nOutXSize, &nOutYSize, bError) ||
1291
0
            nReqXOff != 0 || nReqYOff != 0 ||
1292
0
            nReqXSize != poSimpleSourceBand->GetXSize() ||
1293
0
            nReqYSize != poSimpleSourceBand->GetYSize() ||
1294
0
            nOutXSize != nReqXSize || nOutYSize != nReqYSize)
1295
0
        {
1296
0
            bRet = false;
1297
0
            break;
1298
0
        }
1299
1300
0
        CPLRectObj sBounds;
1301
0
        constexpr double EPSILON = 1e-1;
1302
0
        sBounds.minx = nOutXOff + EPSILON;
1303
0
        sBounds.miny = nOutYOff + EPSILON;
1304
0
        sBounds.maxx = nOutXOff + nOutXSize - EPSILON;
1305
0
        sBounds.maxy = nOutYOff + nOutYSize - EPSILON;
1306
1307
        // Check that the new source doesn't overlap an existing one.
1308
0
        if (CPLQuadTreeHasMatch(hQuadTree, &sBounds))
1309
0
        {
1310
0
            bRet = false;
1311
0
            break;
1312
0
        }
1313
1314
0
        CPLQuadTreeInsertWithBounds(
1315
0
            hQuadTree, reinterpret_cast<void *>(static_cast<uintptr_t>(i)),
1316
0
            &sBounds);
1317
0
    }
1318
0
    CPLQuadTreeDestroy(hQuadTree);
1319
1320
0
    return bRet;
1321
0
}
1322
1323
/************************************************************************/
1324
/*                       ComputeRasterMinMax()                          */
1325
/************************************************************************/
1326
1327
CPLErr VRTSourcedRasterBand::ComputeRasterMinMax(int bApproxOK,
1328
                                                 double *adfMinMax)
1329
0
{
1330
    /* -------------------------------------------------------------------- */
1331
    /*      Does the driver already know the min/max?                       */
1332
    /* -------------------------------------------------------------------- */
1333
0
    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
0
    const std::string osFctId("VRTSourcedRasterBand::ComputeRasterMinMax");
1350
0
    GDALAntiRecursionGuard oGuard(osFctId);
1351
0
    if (oGuard.GetCallDepth() >= 32)
1352
0
    {
1353
0
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1354
0
        return CE_Failure;
1355
0
    }
1356
1357
0
    GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
1358
0
    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
0
    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
0
    if (IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange(
1393
0
            /*bAllowMaxValAdjustment = */ true))
1394
0
    {
1395
0
        CPLDebugOnly(
1396
0
            "VRT", "ComputeRasterMinMax(): use optimized code path for mosaic");
1397
1398
0
        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
0
        bool bUseComputeStatistics = false;
1405
0
        for (auto &poSource : m_papoSources)
1406
0
        {
1407
0
            auto poSimpleSource =
1408
0
                cpl::down_cast<VRTSimpleSource *>(poSource.get());
1409
0
            auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
1410
            // already checked by IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange
1411
0
            assert(poSimpleSourceBand);
1412
0
            int bHasNoData = FALSE;
1413
0
            CPL_IGNORE_RET_VAL(poSimpleSourceBand->GetNoDataValue(&bHasNoData));
1414
0
            if (bHasNoData)
1415
0
            {
1416
0
                bUseComputeStatistics = true;
1417
0
                break;
1418
0
            }
1419
0
            nCoveredArea +=
1420
0
                static_cast<uint64_t>(poSimpleSourceBand->GetXSize()) *
1421
0
                poSimpleSourceBand->GetYSize();
1422
0
        }
1423
1424
0
        if (bUseComputeStatistics)
1425
0
        {
1426
0
            CPLErr eErr;
1427
0
            std::string osLastErrorMsg;
1428
0
            {
1429
0
                CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1430
0
                CPLErrorReset();
1431
0
                eErr =
1432
0
                    ComputeStatistics(bApproxOK, &adfMinMax[0], &adfMinMax[1],
1433
0
                                      nullptr, nullptr, nullptr, nullptr);
1434
0
                if (eErr == CE_Failure)
1435
0
                {
1436
0
                    osLastErrorMsg = CPLGetLastErrorMsg();
1437
0
                }
1438
0
            }
1439
0
            if (eErr == CE_Failure)
1440
0
            {
1441
0
                if (strstr(osLastErrorMsg.c_str(), "no valid pixels found") !=
1442
0
                    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
0
                else
1449
0
                {
1450
0
                    ReportError(CE_Failure, CPLE_AppDefined, "%s",
1451
0
                                osLastErrorMsg.c_str());
1452
0
                }
1453
0
            }
1454
0
            return eErr;
1455
0
        }
1456
1457
0
        bool bSignedByte = false;
1458
0
        if (eDataType == GDT_Byte)
1459
0
        {
1460
0
            EnablePixelTypeSignedByteWarning(false);
1461
0
            const char *pszPixelType =
1462
0
                GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
1463
0
            EnablePixelTypeSignedByteWarning(true);
1464
0
            bSignedByte =
1465
0
                pszPixelType != nullptr && EQUAL(pszPixelType, "SIGNEDBYTE");
1466
0
        }
1467
1468
0
        double dfGlobalMin = std::numeric_limits<double>::max();
1469
0
        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
0
        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
0
        for (auto &poSource : m_papoSources)
1491
0
        {
1492
0
            auto poSimpleSource =
1493
0
                cpl::down_cast<VRTSimpleSource *>(poSource.get());
1494
0
            double adfMinMaxSource[2] = {0};
1495
1496
0
            auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
1497
            // already checked by IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange
1498
0
            assert(poSimpleSourceBand);
1499
0
            CPLErr eErr = poSimpleSourceBand->ComputeRasterMinMax(
1500
0
                bApproxOK, adfMinMaxSource);
1501
0
            if (eErr == CE_Failure)
1502
0
            {
1503
0
                return CE_Failure;
1504
0
            }
1505
0
            else
1506
0
            {
1507
0
                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
0
                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
0
                dfGlobalMin = std::min(dfGlobalMin, adfMinMaxSource[0]);
1526
0
                dfGlobalMax = std::max(dfGlobalMax, adfMinMaxSource[1]);
1527
0
            }
1528
1529
            // Early exit if we know we reached theoretical bounds
1530
0
            if (eDataType == GDT_Byte && !bSignedByte && dfGlobalMin == 0.0 &&
1531
0
                dfGlobalMax == 255.0)
1532
0
            {
1533
0
                break;
1534
0
            }
1535
0
        }
1536
1537
0
        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
0
        adfMinMax[0] = dfGlobalMin;
1548
0
        adfMinMax[1] = dfGlobalMax;
1549
0
        return CE_None;
1550
0
    }
1551
0
    else
1552
0
    {
1553
0
        return GDALRasterBand::ComputeRasterMinMax(bApproxOK, adfMinMax);
1554
0
    }
1555
0
}
1556
1557
// #define naive_update_not_used
1558
1559
namespace
1560
{
1561
struct Context
1562
{
1563
    CPL_DISALLOW_COPY_ASSIGN(Context)
1564
0
    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
0
{
1615
0
    sContext.bUpdateStatsWithConstantValueRun = true;
1616
0
    sContext.dfGlobalMin = std::min(sContext.dfGlobalMin, dfVal);
1617
0
    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
0
    const auto nNewGlobalValidPixels =
1623
0
        sContext.nGlobalValidPixels + nPixelCount;
1624
0
    const double dfDelta = dfVal - sContext.dfGlobalMean;
1625
0
    sContext.dfGlobalMean += nPixelCount * dfDelta / nNewGlobalValidPixels;
1626
0
    sContext.dfGlobalM2 += dfDelta * dfDelta * nPixelCount *
1627
0
                           sContext.nGlobalValidPixels / nNewGlobalValidPixels;
1628
0
#endif
1629
0
    sContext.nGlobalValidPixels += nPixelCount;
1630
0
}
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
0
{
1643
0
    const std::string osFctId("VRTSourcedRasterBand::ComputeStatistics");
1644
0
    GDALAntiRecursionGuard oGuard(osFctId);
1645
0
    if (oGuard.GetCallDepth() >= 32)
1646
0
    {
1647
0
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1648
0
        return CE_Failure;
1649
0
    }
1650
1651
0
    GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
1652
0
    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
0
    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
0
    if (IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange(
1699
0
            /*bAllowMaxValAdjustment = */ false))
1700
0
    {
1701
0
        Context sContext;
1702
0
        sContext.bApproxOK = bApproxOK;
1703
0
        sContext.dfNoDataValue = m_dfNoDataValue;
1704
0
        sContext.bNoDataValueSet = m_bNoDataValueSet;
1705
0
        sContext.bHideNoDataValue = m_bHideNoDataValue;
1706
0
        sContext.pfnProgress = pfnProgress;
1707
0
        sContext.pProgressData = pProgressData;
1708
0
        sContext.nSources = static_cast<int>(m_papoSources.size());
1709
1710
0
        struct Job
1711
0
        {
1712
0
            Context *psContext = nullptr;
1713
0
            GDALRasterBand *poRasterBand = nullptr;
1714
0
            uint64_t nPixelCount = 0;
1715
0
            uint64_t nLastAddedPixels = 0;
1716
0
            uint64_t nValidPixels = 0;
1717
0
            double dfMin = 0;
1718
0
            double dfMax = 0;
1719
0
            double dfMean = 0;
1720
0
            double dfStdDev = 0;
1721
1722
0
            static int CPL_STDCALL ProgressFunc(double dfComplete,
1723
0
                                                const char *pszMessage,
1724
0
                                                void *pProgressArg)
1725
0
            {
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
0
            static void UpdateStats(const Job *psJob)
1763
0
            {
1764
0
                const auto nValidPixels = psJob->nValidPixels;
1765
0
                auto psContext = psJob->psContext;
1766
0
                if (nValidPixels > 0)
1767
0
                {
1768
0
                    psContext->dfGlobalMin =
1769
0
                        std::min(psContext->dfGlobalMin, psJob->dfMin);
1770
0
                    psContext->dfGlobalMax =
1771
0
                        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
0
                    const auto nNewGlobalValidPixels =
1780
0
                        psContext->nGlobalValidPixels + nValidPixels;
1781
0
                    const double dfDelta =
1782
0
                        psJob->dfMean - psContext->dfGlobalMean;
1783
0
                    psContext->dfGlobalMean +=
1784
0
                        nValidPixels * dfDelta / nNewGlobalValidPixels;
1785
0
                    psContext->dfGlobalM2 +=
1786
0
                        nValidPixels * psJob->dfStdDev * psJob->dfStdDev +
1787
0
                        dfDelta * dfDelta * nValidPixels *
1788
0
                            psContext->nGlobalValidPixels /
1789
0
                            nNewGlobalValidPixels;
1790
0
                    psContext->nGlobalValidPixels = nNewGlobalValidPixels;
1791
0
#endif
1792
0
                }
1793
0
                int bHasNoData = FALSE;
1794
0
                const double dfNoDataValue =
1795
0
                    psJob->poRasterBand->GetNoDataValue(&bHasNoData);
1796
0
                if (nValidPixels < psJob->nPixelCount && bHasNoData &&
1797
0
                    !std::isnan(dfNoDataValue) &&
1798
0
                    (!psContext->bNoDataValueSet ||
1799
0
                     dfNoDataValue != psContext->dfNoDataValue))
1800
0
                {
1801
0
                    const auto eBandDT =
1802
0
                        psJob->poRasterBand->GetRasterDataType();
1803
                    // Check that the band nodata value is in the range of the
1804
                    // original raster type
1805
0
                    GByte abyTempBuffer[2 * sizeof(double)];
1806
0
                    CPLAssert(GDALGetDataTypeSizeBytes(eBandDT) <=
1807
0
                              static_cast<int>(sizeof(abyTempBuffer)));
1808
0
                    GDALCopyWords(&dfNoDataValue, GDT_Float64, 0,
1809
0
                                  &abyTempBuffer[0], eBandDT, 0, 1);
1810
0
                    double dfNoDataValueAfter = dfNoDataValue;
1811
0
                    GDALCopyWords(&abyTempBuffer[0], eBandDT, 0,
1812
0
                                  &dfNoDataValueAfter, GDT_Float64, 0, 1);
1813
0
                    if (!std::isfinite(dfNoDataValue) ||
1814
0
                        std::fabs(dfNoDataValueAfter - dfNoDataValue) < 1.0)
1815
0
                    {
1816
0
                        UpdateStatsWithConstantValue(
1817
0
                            *psContext, dfNoDataValueAfter,
1818
0
                            psJob->nPixelCount - nValidPixels);
1819
0
                    }
1820
0
                }
1821
1822
0
                if (psContext->nSources == 1)
1823
0
                {
1824
0
                    psContext->dfSingleSourceMin = psJob->dfMin;
1825
0
                    psContext->dfSingleSourceMax = psJob->dfMax;
1826
0
                    psContext->dfSingleSourceMean = psJob->dfMean;
1827
0
                    psContext->dfSingleSourceStdDev = psJob->dfStdDev;
1828
0
                }
1829
0
            }
1830
0
        };
1831
1832
0
        const auto JobRunner = [](void *pData)
1833
0
        {
1834
0
            auto psJob = static_cast<Job *>(pData);
1835
0
            auto psContext = psJob->psContext;
1836
0
            {
1837
0
                std::lock_guard<std::mutex> oLock(psContext->oMutex);
1838
0
                if (psContext->bFallbackToBase || psContext->bFailure)
1839
0
                    return;
1840
0
            }
1841
1842
0
            auto poSimpleSourceBand = psJob->poRasterBand;
1843
0
            psJob->nPixelCount =
1844
0
                static_cast<uint64_t>(poSimpleSourceBand->GetXSize()) *
1845
0
                poSimpleSourceBand->GetYSize();
1846
1847
0
            CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1848
0
            CPLErr eErr = poSimpleSourceBand->ComputeStatistics(
1849
0
                psContext->bApproxOK, &psJob->dfMin, &psJob->dfMax,
1850
0
                &psJob->dfMean, &psJob->dfStdDev,
1851
0
                psContext->pfnProgress == nullptr ||
1852
0
                        psContext->pfnProgress == GDALDummyProgress
1853
0
                    ? GDALDummyProgress
1854
0
                    : Job::ProgressFunc,
1855
0
                psJob);
1856
0
            const char *pszValidPercent =
1857
0
                poSimpleSourceBand->GetMetadataItem("STATISTICS_VALID_PERCENT");
1858
0
            psJob->nValidPixels =
1859
0
                pszValidPercent
1860
0
                    ? static_cast<uint64_t>(CPLAtof(pszValidPercent) *
1861
0
                                            psJob->nPixelCount / 100.0)
1862
0
                    : psJob->nPixelCount;
1863
0
            if (eErr == CE_Failure)
1864
0
            {
1865
0
                if (pszValidPercent != nullptr &&
1866
0
                    CPLAtof(pszValidPercent) == 0.0)
1867
0
                {
1868
                    // ok: no valid sample
1869
0
                }
1870
0
                else
1871
0
                {
1872
0
                    std::lock_guard<std::mutex> oLock(psContext->oMutex);
1873
0
                    psContext->bFailure = true;
1874
0
                }
1875
0
            }
1876
0
            else
1877
0
            {
1878
0
                int bHasNoData = FALSE;
1879
0
                CPL_IGNORE_RET_VAL(
1880
0
                    psJob->poRasterBand->GetNoDataValue(&bHasNoData));
1881
0
                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
0
            }
1891
0
        };
1892
1893
0
        CPLWorkerThreadPool *poThreadPool = nullptr;
1894
0
        int nThreads =
1895
0
            m_papoSources.size() > 1
1896
0
                ? VRTDataset::GetNumThreads(dynamic_cast<VRTDataset *>(poDS))
1897
0
                : 0;
1898
0
        if (nThreads > 1024)
1899
0
            nThreads = 1024;  // to please Coverity
1900
0
        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
0
        for (auto &poSource : m_papoSources)
1952
0
        {
1953
0
            auto poSimpleSource =
1954
0
                static_cast<VRTSimpleSource *>(poSource.get());
1955
0
            assert(poSimpleSource);
1956
0
            auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
1957
0
            assert(poSimpleSourceBand);
1958
0
            sContext.nTotalPixelsOfSources +=
1959
0
                static_cast<uint64_t>(poSimpleSourceBand->GetXSize()) *
1960
0
                poSimpleSourceBand->GetYSize();
1961
0
        }
1962
1963
0
        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
0
        else
1994
0
        {
1995
0
            CPLDebugOnly(
1996
0
                "VRT",
1997
0
                "ComputeStatistics(): use optimized code path for mosaic");
1998
0
            for (auto &poSource : m_papoSources)
1999
0
            {
2000
0
                auto poSimpleSource =
2001
0
                    static_cast<VRTSimpleSource *>(poSource.get());
2002
0
                assert(poSimpleSource);
2003
0
                auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
2004
0
                assert(poSimpleSourceBand);
2005
0
                Job sJob;
2006
0
                sJob.psContext = &sContext;
2007
0
                sJob.poRasterBand = poSimpleSourceBand;
2008
0
                JobRunner(&sJob);
2009
0
                if (sContext.bFailure || sContext.bFallbackToBase)
2010
0
                    break;
2011
0
                Job::UpdateStats(&sJob);
2012
0
            }
2013
0
        }
2014
2015
0
        if (sContext.bFailure)
2016
0
            return CE_Failure;
2017
0
        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
0
        const uint64_t nTotalPixels =
2031
0
            static_cast<uint64_t>(nRasterXSize) * nRasterYSize;
2032
0
        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
0
        else if (!m_bNoDataValueSet)
2040
0
        {
2041
0
            sContext.nGlobalValidPixels = nTotalPixels;
2042
0
        }
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
0
        double dfGlobalMean = sContext.dfGlobalMean;
2056
0
        double dfGlobalStdDev =
2057
0
            sContext.nGlobalValidPixels > 0
2058
0
                ? sqrt(sContext.dfGlobalM2 / sContext.nGlobalValidPixels)
2059
0
                : 0;
2060
0
#endif
2061
0
        if (m_papoSources.size() == 1 &&
2062
0
            !sContext.bUpdateStatsWithConstantValueRun)
2063
0
        {
2064
0
            sContext.dfGlobalMin = sContext.dfSingleSourceMin;
2065
0
            sContext.dfGlobalMax = sContext.dfSingleSourceMax;
2066
0
            dfGlobalMean = sContext.dfSingleSourceMean;
2067
0
            dfGlobalStdDev = sContext.dfSingleSourceStdDev;
2068
0
        }
2069
2070
0
        if (sContext.nGlobalValidPixels > 0)
2071
0
        {
2072
0
            if (bApproxOK)
2073
0
            {
2074
0
                SetMetadataItem("STATISTICS_APPROXIMATE", "YES");
2075
0
            }
2076
0
            else if (GetMetadataItem("STATISTICS_APPROXIMATE"))
2077
0
            {
2078
0
                SetMetadataItem("STATISTICS_APPROXIMATE", nullptr);
2079
0
            }
2080
0
            SetStatistics(sContext.dfGlobalMin, sContext.dfGlobalMax,
2081
0
                          dfGlobalMean, dfGlobalStdDev);
2082
0
        }
2083
0
        else
2084
0
        {
2085
0
            sContext.dfGlobalMin = 0.0;
2086
0
            sContext.dfGlobalMax = 0.0;
2087
0
        }
2088
2089
0
        SetValidPercent(nTotalPixels, sContext.nGlobalValidPixels);
2090
2091
0
        if (pdfMin)
2092
0
            *pdfMin = sContext.dfGlobalMin;
2093
0
        if (pdfMax)
2094
0
            *pdfMax = sContext.dfGlobalMax;
2095
0
        if (pdfMean)
2096
0
            *pdfMean = dfGlobalMean;
2097
0
        if (pdfStdDev)
2098
0
            *pdfStdDev = dfGlobalStdDev;
2099
2100
0
        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
0
        return sContext.nGlobalValidPixels > 0 ? CE_None : CE_Failure;
2108
0
    }
2109
0
    else
2110
0
    {
2111
0
        return GDALRasterBand::ComputeStatistics(bApproxOK, pdfMin, pdfMax,
2112
0
                                                 pdfMean, pdfStdDev,
2113
0
                                                 pfnProgress, pProgressData);
2114
0
    }
2115
0
}
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
0
{
2128
    /* -------------------------------------------------------------------- */
2129
    /*      If we have overviews, use them for the histogram.               */
2130
    /* -------------------------------------------------------------------- */
2131
0
    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
0
    if (m_papoSources.size() != 1)
2162
0
        return VRTRasterBand::GetHistogram(dfMin, dfMax, nBuckets, panHistogram,
2163
0
                                           bIncludeOutOfRange, bApproxOK,
2164
0
                                           pfnProgress, pProgressData);
2165
2166
0
    if (pfnProgress == nullptr)
2167
0
        pfnProgress = GDALDummyProgress;
2168
2169
0
    const std::string osFctId("VRTSourcedRasterBand::GetHistogram");
2170
0
    GDALAntiRecursionGuard oGuard(osFctId);
2171
0
    if (oGuard.GetCallDepth() >= 32)
2172
0
    {
2173
0
        CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
2174
0
        return CE_Failure;
2175
0
    }
2176
2177
0
    GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
2178
0
    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
0
    const CPLErr eErr = m_papoSources[0]->GetHistogram(
2188
0
        GetXSize(), GetYSize(), dfMin, dfMax, nBuckets, panHistogram,
2189
0
        bIncludeOutOfRange, bApproxOK, pfnProgress, pProgressData);
2190
0
    if (eErr != CE_None)
2191
0
    {
2192
0
        const CPLErr eErr2 = GDALRasterBand::GetHistogram(
2193
0
            dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange, bApproxOK,
2194
0
            pfnProgress, pProgressData);
2195
0
        return eErr2;
2196
0
    }
2197
2198
0
    SetDefaultHistogram(dfMin, dfMax, nBuckets, panHistogram);
2199
2200
0
    return CE_None;
2201
0
}
2202
2203
/************************************************************************/
2204
/*                             AddSource()                              */
2205
/************************************************************************/
2206
2207
CPLErr VRTSourcedRasterBand::AddSource(VRTSource *poNewSource)
2208
2209
0
{
2210
0
    return AddSource(std::unique_ptr<VRTSource>(poNewSource));
2211
0
}
2212
2213
/************************************************************************/
2214
/*                             AddSource()                              */
2215
/************************************************************************/
2216
2217
CPLErr VRTSourcedRasterBand::AddSource(std::unique_ptr<VRTSource> poNewSource)
2218
2219
0
{
2220
0
    auto l_poDS = static_cast<VRTDataset *>(poDS);
2221
0
    l_poDS->SetNeedsFlush();
2222
0
    l_poDS->SourceAdded();
2223
2224
0
    if (poNewSource->IsSimpleSource())
2225
0
    {
2226
0
        VRTSimpleSource *poSS =
2227
0
            static_cast<VRTSimpleSource *>(poNewSource.get());
2228
0
        if (GetMetadataItem("NBITS", "IMAGE_STRUCTURE") != nullptr)
2229
0
        {
2230
0
            int nBits = atoi(GetMetadataItem("NBITS", "IMAGE_STRUCTURE"));
2231
0
            if (nBits >= 1 && nBits <= 31)
2232
0
            {
2233
0
                poSS->SetMaxValue(static_cast<int>((1U << nBits) - 1));
2234
0
            }
2235
0
        }
2236
0
    }
2237
2238
0
    m_papoSources.push_back(std::move(poNewSource));
2239
2240
0
    return CE_None;
2241
0
}
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
0
{
2273
0
    {
2274
0
        const CPLErr eErr =
2275
0
            VRTRasterBand::XMLInit(psTree, pszVRTPath, oMapSharedSources);
2276
0
        if (eErr != CE_None)
2277
0
            return eErr;
2278
0
    }
2279
2280
    /* -------------------------------------------------------------------- */
2281
    /*      Process sources.                                                */
2282
    /* -------------------------------------------------------------------- */
2283
0
    VRTDriver *const poDriver = dynamic_cast<VRTDriver *>(
2284
0
        GetGDALDriverManager()->GetDriverByName("VRT"));
2285
2286
0
    for (const CPLXMLNode *psChild = psTree->psChild;
2287
0
         psChild != nullptr && poDriver != nullptr; psChild = psChild->psNext)
2288
0
    {
2289
0
        if (psChild->eType != CXT_Element)
2290
0
            continue;
2291
2292
0
        CPLErrorReset();
2293
0
        VRTSource *const poSource =
2294
0
            poDriver->ParseSource(psChild, pszVRTPath, oMapSharedSources);
2295
0
        if (poSource != nullptr)
2296
0
            AddSource(poSource);
2297
0
        else if (CPLGetLastErrorType() != CE_None)
2298
0
            return CE_Failure;
2299
0
    }
2300
2301
    /* -------------------------------------------------------------------- */
2302
    /*      Done.                                                           */
2303
    /* -------------------------------------------------------------------- */
2304
0
    const char *pszSubclass =
2305
0
        CPLGetXMLValue(psTree, "subclass", "VRTSourcedRasterBand");
2306
0
    if (m_papoSources.empty() && !EQUAL(pszSubclass, "VRTDerivedRasterBand"))
2307
0
        CPLDebug("VRT", "No valid sources found for band in VRT file %s",
2308
0
                 GetDataset() ? GetDataset()->GetDescription() : "");
2309
2310
0
    return CE_None;
2311
0
}
2312
2313
/************************************************************************/
2314
/*                           SerializeToXML()                           */
2315
/************************************************************************/
2316
2317
CPLXMLNode *VRTSourcedRasterBand::SerializeToXML(const char *pszVRTPath,
2318
                                                 bool &bHasWarnedAboutRAMUsage,
2319
                                                 size_t &nAccRAMUsage)
2320
2321
0
{
2322
0
    CPLXMLNode *psTree = VRTRasterBand::SerializeToXML(
2323
0
        pszVRTPath, bHasWarnedAboutRAMUsage, nAccRAMUsage);
2324
0
    CPLXMLNode *psLastChild = psTree->psChild;
2325
0
    while (psLastChild != nullptr && psLastChild->psNext != nullptr)
2326
0
        psLastChild = psLastChild->psNext;
2327
2328
    /* -------------------------------------------------------------------- */
2329
    /*      Process Sources.                                                */
2330
    /* -------------------------------------------------------------------- */
2331
2332
0
    GIntBig nUsableRAM = -1;
2333
2334
0
    for (const auto &poSource : m_papoSources)
2335
0
    {
2336
0
        CPLXMLNode *const psXMLSrc = poSource->SerializeToXML(pszVRTPath);
2337
2338
0
        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
0
        nAccRAMUsage += 2 * CPLXMLNodeGetRAMUsageEstimate(psXMLSrc);
2347
0
        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
0
        if (psLastChild == nullptr)
2365
0
            psTree->psChild = psXMLSrc;
2366
0
        else
2367
0
            psLastChild->psNext = psXMLSrc;
2368
0
        psLastChild = psXMLSrc;
2369
0
    }
2370
2371
0
    return psTree;
2372
0
}
2373
2374
/************************************************************************/
2375
/*                     SkipBufferInitialization()                       */
2376
/************************************************************************/
2377
2378
bool VRTSourcedRasterBand::SkipBufferInitialization()
2379
0
{
2380
0
    if (m_nSkipBufferInitialization >= 0)
2381
0
        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
0
    m_nSkipBufferInitialization = FALSE;
2388
0
    if (m_papoSources.size() != 1 || !m_papoSources[0]->IsSimpleSource())
2389
0
    {
2390
0
        return false;
2391
0
    }
2392
0
    VRTSimpleSource *poSS =
2393
0
        static_cast<VRTSimpleSource *>(m_papoSources[0].get());
2394
0
    if (poSS->GetType() == VRTSimpleSource::GetTypeStatic())
2395
0
    {
2396
0
        auto l_poBand = poSS->GetRasterBand();
2397
0
        if (l_poBand != nullptr && poSS->m_dfSrcXOff >= 0.0 &&
2398
0
            poSS->m_dfSrcYOff >= 0.0 &&
2399
0
            poSS->m_dfSrcXOff + poSS->m_dfSrcXSize <= l_poBand->GetXSize() &&
2400
0
            poSS->m_dfSrcYOff + poSS->m_dfSrcYSize <= l_poBand->GetYSize() &&
2401
0
            poSS->m_dfDstXOff <= 0.0 && poSS->m_dfDstYOff <= 0.0 &&
2402
0
            poSS->m_dfDstXOff + poSS->m_dfDstXSize >= nRasterXSize &&
2403
0
            poSS->m_dfDstYOff + poSS->m_dfDstYSize >= nRasterYSize)
2404
0
        {
2405
0
            m_nSkipBufferInitialization = TRUE;
2406
0
        }
2407
0
    }
2408
0
    return m_nSkipBufferInitialization != 0;
2409
0
}
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
0
{
2423
    /* -------------------------------------------------------------------- */
2424
    /*      Default source and dest rectangles.                             */
2425
    /* -------------------------------------------------------------------- */
2426
0
    if (dfSrcYSize == -1)
2427
0
    {
2428
0
        dfSrcXOff = 0;
2429
0
        dfSrcYOff = 0;
2430
0
        dfSrcXSize = poSrcBand->GetXSize();
2431
0
        dfSrcYSize = poSrcBand->GetYSize();
2432
0
    }
2433
2434
0
    if (dfDstYSize == -1)
2435
0
    {
2436
0
        dfDstXOff = 0;
2437
0
        dfDstYOff = 0;
2438
0
        dfDstXSize = nRasterXSize;
2439
0
        dfDstYSize = nRasterYSize;
2440
0
    }
2441
2442
0
    if (bAddAsMaskBand)
2443
0
        poSimpleSource->SetSrcMaskBand(poSrcBand);
2444
0
    else
2445
0
        poSimpleSource->SetSrcBand(poSrcBand);
2446
2447
0
    poSimpleSource->SetSrcWindow(dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize);
2448
0
    poSimpleSource->SetDstWindow(dfDstXOff, dfDstYOff, dfDstXSize, dfDstYSize);
2449
2450
    /* -------------------------------------------------------------------- */
2451
    /*      If we can get the associated GDALDataset, add a reference to it.*/
2452
    /* -------------------------------------------------------------------- */
2453
0
    GDALDataset *poSrcBandDataset = poSrcBand->GetDataset();
2454
0
    if (poSrcBandDataset != nullptr)
2455
0
    {
2456
0
        VRTDataset *poVRTSrcBandDataset =
2457
0
            dynamic_cast<VRTDataset *>(poSrcBandDataset);
2458
0
        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
0
        else
2468
0
        {
2469
0
            poSrcBandDataset->Reference();
2470
0
        }
2471
0
    }
2472
0
}
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
0
{
2485
    /* -------------------------------------------------------------------- */
2486
    /*      Create source.                                                  */
2487
    /* -------------------------------------------------------------------- */
2488
0
    VRTSimpleSource *poSimpleSource = nullptr;
2489
2490
0
    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
0
    else
2498
0
    {
2499
0
        poSimpleSource = new VRTSimpleSource();
2500
0
        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
0
    }
2506
2507
0
    poSimpleSource->SetSrcBand(pszFilename, nBandIn);
2508
2509
0
    poSimpleSource->SetSrcWindow(dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize);
2510
0
    poSimpleSource->SetDstWindow(dfDstXOff, dfDstYOff, dfDstXSize, dfDstYSize);
2511
2512
    /* -------------------------------------------------------------------- */
2513
    /*      add to list.                                                    */
2514
    /* -------------------------------------------------------------------- */
2515
0
    return AddSource(poSimpleSource);
2516
0
}
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
0
{
2529
    /* -------------------------------------------------------------------- */
2530
    /*      Create source.                                                  */
2531
    /* -------------------------------------------------------------------- */
2532
0
    VRTSimpleSource *poSimpleSource = nullptr;
2533
2534
0
    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
0
    else
2542
0
    {
2543
0
        poSimpleSource = new VRTSimpleSource();
2544
0
        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
0
    }
2550
2551
0
    ConfigureSource(poSimpleSource, poSrcBand, FALSE, dfSrcXOff, dfSrcYOff,
2552
0
                    dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
2553
0
                    dfDstYSize);
2554
2555
    /* -------------------------------------------------------------------- */
2556
    /*      add to list.                                                    */
2557
    /* -------------------------------------------------------------------- */
2558
0
    return AddSource(poSimpleSource);
2559
0
}
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
0
{
2572
    /* -------------------------------------------------------------------- */
2573
    /*      Create source.                                                  */
2574
    /* -------------------------------------------------------------------- */
2575
0
    VRTSimpleSource *poSimpleSource = new VRTSimpleSource();
2576
2577
0
    ConfigureSource(poSimpleSource, poSrcBand, TRUE, dfSrcXOff, dfSrcYOff,
2578
0
                    dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
2579
0
                    dfDstYSize);
2580
2581
    /* -------------------------------------------------------------------- */
2582
    /*      add to list.                                                    */
2583
    /* -------------------------------------------------------------------- */
2584
0
    return AddSource(poSimpleSource);
2585
0
}
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
0
{
2605
0
    VALIDATE_POINTER1(hVRTBand, "VRTAddSimpleSource", CE_Failure);
2606
2607
0
    return reinterpret_cast<VRTSourcedRasterBand *>(hVRTBand)->AddSimpleSource(
2608
0
        reinterpret_cast<GDALRasterBand *>(hSrcBand), nSrcXOff, nSrcYOff,
2609
0
        nSrcXSize, nSrcYSize, nDstXOff, nDstYOff, nDstXSize, nDstYSize,
2610
0
        pszResampling, dfNoDataValue);
2611
0
}
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
0
{
2781
    /* ==================================================================== */
2782
    /*      LocationInfo handling.                                          */
2783
    /* ==================================================================== */
2784
0
    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
0
    return GDALRasterBand::GetMetadataItem(pszName, pszDomain);
2914
0
}
2915
2916
/************************************************************************/
2917
/*                            GetMetadata()                             */
2918
/************************************************************************/
2919
2920
char **VRTSourcedRasterBand::GetMetadata(const char *pszDomain)
2921
2922
0
{
2923
    /* ==================================================================== */
2924
    /*      vrt_sources domain handling.                                    */
2925
    /* ==================================================================== */
2926
0
    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
0
    return GDALRasterBand::GetMetadata(pszDomain);
2957
0
}
2958
2959
/************************************************************************/
2960
/*                          SetMetadataItem()                           */
2961
/************************************************************************/
2962
2963
CPLErr VRTSourcedRasterBand::SetMetadataItem(const char *pszName,
2964
                                             const char *pszValue,
2965
                                             const char *pszDomain)
2966
2967
0
{
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
0
    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
0
    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
0
    return VRTRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
3042
0
}
3043
3044
/************************************************************************/
3045
/*                            SetMetadata()                             */
3046
/************************************************************************/
3047
3048
CPLErr VRTSourcedRasterBand::SetMetadata(char **papszNewMD,
3049
                                         const char *pszDomain)
3050
3051
0
{
3052
0
    if (pszDomain != nullptr && (EQUAL(pszDomain, "new_vrt_sources") ||
3053
0
                                 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
0
    return VRTRasterBand::SetMetadata(papszNewMD, pszDomain);
3093
0
}
3094
3095
/************************************************************************/
3096
/*                             GetFileList()                            */
3097
/************************************************************************/
3098
3099
void VRTSourcedRasterBand::GetFileList(char ***ppapszFileList, int *pnSize,
3100
                                       int *pnMaxSize, CPLHashSet *hSetFiles)
3101
0
{
3102
0
    for (auto &poSource : m_papoSources)
3103
0
    {
3104
0
        poSource->GetFileList(ppapszFileList, pnSize, pnMaxSize, hSetFiles);
3105
0
    }
3106
3107
0
    VRTRasterBand::GetFileList(ppapszFileList, pnSize, pnMaxSize, hSetFiles);
3108
0
}
3109
3110
/************************************************************************/
3111
/*                        CloseDependentDatasets()                      */
3112
/************************************************************************/
3113
3114
int VRTSourcedRasterBand::CloseDependentDatasets()
3115
0
{
3116
0
    int ret = VRTRasterBand::CloseDependentDatasets();
3117
3118
0
    if (m_papoSources.empty())
3119
0
        return ret;
3120
3121
0
    m_papoSources.clear();
3122
3123
0
    return TRUE;
3124
0
}
3125
3126
/************************************************************************/
3127
/*                               FlushCache()                           */
3128
/************************************************************************/
3129
3130
CPLErr VRTSourcedRasterBand::FlushCache(bool bAtClosing)
3131
0
{
3132
0
    CPLErr eErr = VRTRasterBand::FlushCache(bAtClosing);
3133
0
    for (size_t i = 0; i < m_papoSources.size() && eErr == CE_None; i++)
3134
0
    {
3135
0
        eErr = m_papoSources[i]->FlushCache(bAtClosing);
3136
0
    }
3137
0
    return eErr;
3138
0
}
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 */