Coverage Report

Created: 2025-12-31 06:48

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