Coverage Report

Created: 2026-04-01 06:20

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