Coverage Report

Created: 2025-06-13 06:29

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