Coverage Report

Created: 2026-02-14 06:52

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