Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/vrt/vrtdataset.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  Virtual GDAL Datasets
4
 * Purpose:  Implementation of VRTDataset
5
 * Author:   Frank Warmerdam <warmerdam@pobox.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "vrtdataset.h"
15
16
#include "cpl_error_internal.h"
17
#include "cpl_minixml.h"
18
#include "cpl_string.h"
19
#include "cpl_worker_thread_pool.h"
20
#include "gdal_frmts.h"
21
#include "ogr_spatialref.h"
22
#include "gdal_thread_pool.h"
23
#include "gdal_utils.h"
24
25
#include <algorithm>
26
#include <cassert>
27
#include <cmath>
28
#include <set>
29
#include <typeinfo>
30
#include "gdal_proxy.h"
31
32
/*! @cond Doxygen_Suppress */
33
34
0
#define VRT_PROTOCOL_PREFIX "vrt://"
35
36
constexpr int DEFAULT_BLOCK_SIZE = 128;
37
38
/************************************************************************/
39
/*                             VRTDataset()                             */
40
/************************************************************************/
41
42
VRTDataset::VRTDataset(int nXSize, int nYSize, int nBlockXSize, int nBlockYSize)
43
0
{
44
0
    nRasterXSize = nXSize;
45
0
    nRasterYSize = nYSize;
46
47
0
    m_bBlockSizeSpecified = nBlockXSize > 0 && nBlockYSize > 0;
48
0
    m_nBlockXSize =
49
0
        nBlockXSize > 0 ? nBlockXSize : std::min(DEFAULT_BLOCK_SIZE, nXSize);
50
0
    m_nBlockYSize =
51
0
        nBlockYSize > 0 ? nBlockYSize : std::min(DEFAULT_BLOCK_SIZE, nYSize);
52
53
0
    GDALRegister_VRT();
54
55
0
    poDriver = static_cast<GDALDriver *>(GDALGetDriverByName("VRT"));
56
0
}
57
58
/************************************************************************/
59
/*                         IsDefaultBlockSize()                         */
60
/************************************************************************/
61
62
/* static */ bool VRTDataset::IsDefaultBlockSize(int nBlockSize, int nDimension)
63
0
{
64
0
    return nBlockSize == DEFAULT_BLOCK_SIZE ||
65
0
           (nBlockSize < DEFAULT_BLOCK_SIZE && nBlockSize == nDimension);
66
0
}
67
68
/*! @endcond */
69
70
/************************************************************************/
71
/*                             VRTCreate()                              */
72
/************************************************************************/
73
74
/**
75
 * @see VRTDataset::VRTDataset()
76
 */
77
78
VRTDatasetH CPL_STDCALL VRTCreate(int nXSize, int nYSize)
79
80
0
{
81
0
    auto poDS = new VRTDataset(nXSize, nYSize);
82
0
    poDS->eAccess = GA_Update;
83
0
    return poDS;
84
0
}
85
86
/*! @cond Doxygen_Suppress */
87
88
/************************************************************************/
89
/*                            ~VRTDataset()                             */
90
/************************************************************************/
91
92
VRTDataset::~VRTDataset()
93
94
0
{
95
0
    VRTDataset::FlushCache(true);
96
0
    CPLFree(m_pszVRTPath);
97
98
0
    for (size_t i = 0; i < m_apoOverviews.size(); i++)
99
0
        delete m_apoOverviews[i];
100
0
    for (size_t i = 0; i < m_apoOverviewsBak.size(); i++)
101
0
        delete m_apoOverviewsBak[i];
102
0
    CSLDestroy(m_papszXMLVRTMetadata);
103
0
}
104
105
/************************************************************************/
106
/*                             FlushCache()                             */
107
/************************************************************************/
108
109
CPLErr VRTDataset::FlushCache(bool bAtClosing)
110
111
0
{
112
0
    if (m_poRootGroup)
113
0
    {
114
0
        m_poRootGroup->SetVRTPath(CPLGetPathSafe(GetDescription()));
115
0
        return m_poRootGroup->Serialize() ? CE_None : CE_Failure;
116
0
    }
117
0
    else
118
0
        return VRTFlushCacheStruct<VRTDataset>::FlushCache(*this, bAtClosing);
119
0
}
120
121
/************************************************************************/
122
/*                             FlushCache()                             */
123
/************************************************************************/
124
125
CPLErr VRTWarpedDataset::FlushCache(bool bAtClosing)
126
127
0
{
128
0
    return VRTFlushCacheStruct<VRTWarpedDataset>::FlushCache(*this, bAtClosing);
129
0
}
130
131
/************************************************************************/
132
/*                             FlushCache()                             */
133
/************************************************************************/
134
135
CPLErr VRTPansharpenedDataset::FlushCache(bool bAtClosing)
136
137
0
{
138
0
    return VRTFlushCacheStruct<VRTPansharpenedDataset>::FlushCache(*this,
139
0
                                                                   bAtClosing);
140
0
}
141
142
/************************************************************************/
143
/*                             FlushCache()                             */
144
/************************************************************************/
145
146
CPLErr VRTProcessedDataset::FlushCache(bool bAtClosing)
147
148
0
{
149
0
    return VRTFlushCacheStruct<VRTProcessedDataset>::FlushCache(*this,
150
0
                                                                bAtClosing);
151
0
}
152
153
/************************************************************************/
154
/*                             FlushCache()                             */
155
/************************************************************************/
156
157
template <class T>
158
CPLErr VRTFlushCacheStruct<T>::FlushCache(T &obj, bool bAtClosing)
159
0
{
160
0
    CPLErr eErr = obj.GDALDataset::FlushCache(bAtClosing);
161
162
0
    if (!obj.m_bNeedsFlush || !obj.m_bWritable)
163
0
        return eErr;
164
165
    // We don't write to disk if there is no filename.  This is a
166
    // memory only dataset.
167
0
    if (strlen(obj.GetDescription()) == 0 ||
168
0
        STARTS_WITH_CI(obj.GetDescription(), "<VRTDataset"))
169
0
        return eErr;
170
171
0
    obj.m_bNeedsFlush = false;
172
173
    // Serialize XML representation to disk
174
0
    const std::string osVRTPath(CPLGetPathSafe(obj.GetDescription()));
175
0
    CPLXMLNode *psDSTree = obj.T::SerializeToXML(osVRTPath.c_str());
176
0
    if (!CPLSerializeXMLTreeToFile(psDSTree, obj.GetDescription()))
177
0
        eErr = CE_Failure;
178
0
    CPLDestroyXMLNode(psDSTree);
179
0
    return eErr;
180
0
}
Unexecuted instantiation: VRTFlushCacheStruct<VRTDataset>::FlushCache(VRTDataset&, bool)
Unexecuted instantiation: VRTFlushCacheStruct<VRTWarpedDataset>::FlushCache(VRTWarpedDataset&, bool)
Unexecuted instantiation: VRTFlushCacheStruct<VRTPansharpenedDataset>::FlushCache(VRTPansharpenedDataset&, bool)
Unexecuted instantiation: VRTFlushCacheStruct<VRTProcessedDataset>::FlushCache(VRTProcessedDataset&, bool)
181
182
/************************************************************************/
183
/*                            GetMetadata()                             */
184
/************************************************************************/
185
186
CSLConstList VRTDataset::GetMetadata(const char *pszDomain)
187
0
{
188
0
    if (pszDomain != nullptr && EQUAL(pszDomain, "xml:VRT"))
189
0
    {
190
        /* ------------------------------------------------------------------ */
191
        /*      Convert tree to a single block of XML text.                   */
192
        /* ------------------------------------------------------------------ */
193
0
        const char *pszDescription = GetDescription();
194
0
        char *l_pszVRTPath = CPLStrdup(
195
0
            pszDescription[0] && !STARTS_WITH(pszDescription, "<VRTDataset")
196
0
                ? CPLGetPathSafe(pszDescription).c_str()
197
0
                : "");
198
0
        CPLXMLNode *psDSTree = SerializeToXML(l_pszVRTPath);
199
0
        char *pszXML = CPLSerializeXMLTree(psDSTree);
200
201
0
        CPLDestroyXMLNode(psDSTree);
202
203
0
        CPLFree(l_pszVRTPath);
204
205
0
        CSLDestroy(m_papszXMLVRTMetadata);
206
0
        m_papszXMLVRTMetadata =
207
0
            static_cast<char **>(CPLMalloc(2 * sizeof(char *)));
208
0
        m_papszXMLVRTMetadata[0] = pszXML;
209
0
        m_papszXMLVRTMetadata[1] = nullptr;
210
0
        return m_papszXMLVRTMetadata;
211
0
    }
212
213
0
    return GDALDataset::GetMetadata(pszDomain);
214
0
}
215
216
/************************************************************************/
217
/*                          GetMetadataItem()                           */
218
/************************************************************************/
219
220
const char *VRTDataset::GetMetadataItem(const char *pszName,
221
                                        const char *pszDomain)
222
223
0
{
224
0
    if (pszName && pszDomain && EQUAL(pszDomain, "__DEBUG__"))
225
0
    {
226
0
        if (EQUAL(pszName, "MULTI_THREADED_RASTERIO_LAST_USED"))
227
0
            return m_bMultiThreadedRasterIOLastUsed ? "1" : "0";
228
0
        else if (EQUAL(pszName, "CheckCompatibleForDatasetIO()"))
229
0
            return CheckCompatibleForDatasetIO() ? "1" : "0";
230
0
    }
231
0
    return GDALDataset::GetMetadataItem(pszName, pszDomain);
232
0
}
233
234
/*! @endcond */
235
236
/************************************************************************/
237
/*                    VRTFlushCache(bool bAtClosing)                    */
238
/************************************************************************/
239
240
/**
241
 * @see VRTDataset::FlushCache(bool bAtClosing)
242
 */
243
244
void CPL_STDCALL VRTFlushCache(VRTDatasetH hDataset)
245
0
{
246
0
    VALIDATE_POINTER0(hDataset, "VRTFlushCache");
247
248
0
    static_cast<VRTDataset *>(GDALDataset::FromHandle(hDataset))
249
0
        ->FlushCache(false);
250
0
}
251
252
/*! @cond Doxygen_Suppress */
253
254
/************************************************************************/
255
/*                           SerializeToXML()                           */
256
/************************************************************************/
257
258
CPLXMLNode *VRTDataset::SerializeToXML(const char *pszVRTPathIn)
259
260
0
{
261
0
    if (m_poRootGroup)
262
0
        return m_poRootGroup->SerializeToXML(pszVRTPathIn);
263
264
    /* -------------------------------------------------------------------- */
265
    /*      Setup root node and attributes.                                 */
266
    /* -------------------------------------------------------------------- */
267
0
    CPLXMLNode *psDSTree = CPLCreateXMLNode(nullptr, CXT_Element, "VRTDataset");
268
269
0
    char szNumber[128] = {'\0'};
270
0
    snprintf(szNumber, sizeof(szNumber), "%d", GetRasterXSize());
271
0
    CPLSetXMLValue(psDSTree, "#rasterXSize", szNumber);
272
273
0
    snprintf(szNumber, sizeof(szNumber), "%d", GetRasterYSize());
274
0
    CPLSetXMLValue(psDSTree, "#rasterYSize", szNumber);
275
276
    /* -------------------------------------------------------------------- */
277
    /*      SRS                                                             */
278
    /* -------------------------------------------------------------------- */
279
0
    if (m_poSRS && !m_poSRS->IsEmpty())
280
0
    {
281
0
        char *pszWKT = nullptr;
282
0
        m_poSRS->exportToWkt(&pszWKT);
283
0
        CPLXMLNode *psSRSNode =
284
0
            CPLCreateXMLElementAndValue(psDSTree, "SRS", pszWKT);
285
0
        CPLFree(pszWKT);
286
0
        const auto &mapping = m_poSRS->GetDataAxisToSRSAxisMapping();
287
0
        CPLString osMapping;
288
0
        for (size_t i = 0; i < mapping.size(); ++i)
289
0
        {
290
0
            if (!osMapping.empty())
291
0
                osMapping += ",";
292
0
            osMapping += CPLSPrintf("%d", mapping[i]);
293
0
        }
294
0
        CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
295
0
                                   osMapping.c_str());
296
0
        const double dfCoordinateEpoch = m_poSRS->GetCoordinateEpoch();
297
0
        if (dfCoordinateEpoch > 0)
298
0
        {
299
0
            std::string osCoordinateEpoch = CPLSPrintf("%f", dfCoordinateEpoch);
300
0
            if (osCoordinateEpoch.find('.') != std::string::npos)
301
0
            {
302
0
                while (osCoordinateEpoch.back() == '0')
303
0
                    osCoordinateEpoch.pop_back();
304
0
            }
305
0
            CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
306
0
                                       osCoordinateEpoch.c_str());
307
0
        }
308
0
    }
309
310
    /* -------------------------------------------------------------------- */
311
    /*      Geotransform.                                                   */
312
    /* -------------------------------------------------------------------- */
313
0
    if (m_bGeoTransformSet)
314
0
    {
315
0
        CPLSetXMLValue(
316
0
            psDSTree, "GeoTransform",
317
0
            CPLSPrintf("%24.16e,%24.16e,%24.16e,%24.16e,%24.16e,%24.16e",
318
0
                       m_gt.xorig, m_gt.xscale, m_gt.xrot, m_gt.yorig,
319
0
                       m_gt.yrot, m_gt.yscale));
320
0
    }
321
322
    /* -------------------------------------------------------------------- */
323
    /*      Metadata                                                        */
324
    /* -------------------------------------------------------------------- */
325
0
    CPLXMLNode *psMD = oMDMD.Serialize();
326
0
    if (psMD != nullptr)
327
0
    {
328
0
        CPLAddXMLChild(psDSTree, psMD);
329
0
    }
330
331
    /* -------------------------------------------------------------------- */
332
    /*      GCPs                                                            */
333
    /* -------------------------------------------------------------------- */
334
0
    if (!m_asGCPs.empty())
335
0
    {
336
0
        GDALSerializeGCPListToXML(psDSTree, m_asGCPs, m_poGCP_SRS.get());
337
0
    }
338
339
    /* -------------------------------------------------------------------- */
340
    /*      Serialize bands.                                                */
341
    /* -------------------------------------------------------------------- */
342
0
    CPLXMLNode *psLastChild = psDSTree->psChild;
343
0
    for (; psLastChild != nullptr && psLastChild->psNext;
344
0
         psLastChild = psLastChild->psNext)
345
0
    {
346
0
    }
347
0
    CPLAssert(psLastChild);  // we have at least rasterXSize
348
0
    bool bHasWarnedAboutRAMUsage = false;
349
0
    size_t nAccRAMUsage = 0;
350
0
    for (int iBand = 0; iBand < nBands; iBand++)
351
0
    {
352
0
        CPLXMLNode *psBandTree =
353
0
            static_cast<VRTRasterBand *>(papoBands[iBand])
354
0
                ->SerializeToXML(pszVRTPathIn, bHasWarnedAboutRAMUsage,
355
0
                                 nAccRAMUsage);
356
357
0
        if (psBandTree != nullptr)
358
0
        {
359
0
            psLastChild->psNext = psBandTree;
360
0
            psLastChild = psBandTree;
361
0
        }
362
0
    }
363
364
    /* -------------------------------------------------------------------- */
365
    /*      Serialize dataset mask band.                                    */
366
    /* -------------------------------------------------------------------- */
367
0
    if (m_poMaskBand)
368
0
    {
369
0
        CPLXMLNode *psBandTree = m_poMaskBand->SerializeToXML(
370
0
            pszVRTPathIn, bHasWarnedAboutRAMUsage, nAccRAMUsage);
371
372
0
        if (psBandTree != nullptr)
373
0
        {
374
0
            CPLXMLNode *psMaskBandElement =
375
0
                CPLCreateXMLNode(psDSTree, CXT_Element, "MaskBand");
376
0
            CPLAddXMLChild(psMaskBandElement, psBandTree);
377
0
        }
378
0
    }
379
380
    /* -------------------------------------------------------------------- */
381
    /*      Overview factors.                                               */
382
    /* -------------------------------------------------------------------- */
383
0
    if (!m_anOverviewFactors.empty())
384
0
    {
385
0
        CPLString osOverviewList;
386
0
        for (int nOvFactor : m_anOverviewFactors)
387
0
        {
388
0
            if (!osOverviewList.empty())
389
0
                osOverviewList += " ";
390
0
            osOverviewList += CPLSPrintf("%d", nOvFactor);
391
0
        }
392
0
        CPLXMLNode *psOverviewList = CPLCreateXMLElementAndValue(
393
0
            psDSTree, "OverviewList", osOverviewList);
394
0
        if (!m_osOverviewResampling.empty())
395
0
        {
396
0
            CPLAddXMLAttributeAndValue(psOverviewList, "resampling",
397
0
                                       m_osOverviewResampling);
398
0
        }
399
0
    }
400
401
0
    return psDSTree;
402
0
}
403
404
/*! @endcond */
405
/************************************************************************/
406
/*                         VRTSerializeToXML()                          */
407
/************************************************************************/
408
409
/**
410
 * @see VRTDataset::SerializeToXML()
411
 */
412
413
CPLXMLNode *CPL_STDCALL VRTSerializeToXML(VRTDatasetH hDataset,
414
                                          const char *pszVRTPath)
415
0
{
416
0
    VALIDATE_POINTER1(hDataset, "VRTSerializeToXML", nullptr);
417
418
0
    return static_cast<VRTDataset *>(GDALDataset::FromHandle(hDataset))
419
0
        ->SerializeToXML(pszVRTPath);
420
0
}
421
422
/*! @cond Doxygen_Suppress */
423
424
/************************************************************************/
425
/*                       IsRawRasterBandEnabled()                       */
426
/************************************************************************/
427
428
/** Return whether VRTRawRasterBand support is enabled */
429
430
/* static */
431
bool VRTDataset::IsRawRasterBandEnabled()
432
0
{
433
0
#ifdef GDAL_VRT_ENABLE_RAWRASTERBAND
434
0
    if (CPLTestBool(CPLGetConfigOption("GDAL_VRT_ENABLE_RAWRASTERBAND", "YES")))
435
0
    {
436
0
        return true;
437
0
    }
438
0
    else
439
0
    {
440
0
        CPLError(CE_Failure, CPLE_NotSupported,
441
0
                 "VRTRawRasterBand support has been disabled at run-time.");
442
0
    }
443
0
    return false;
444
#else
445
    CPLError(CE_Failure, CPLE_NotSupported,
446
             "VRTRawRasterBand is disabled in this GDAL build");
447
    return false;
448
#endif
449
0
}
450
451
/************************************************************************/
452
/*                              InitBand()                              */
453
/************************************************************************/
454
455
std::unique_ptr<VRTRasterBand>
456
VRTDataset::InitBand(const char *pszSubclass, int nBand,
457
                     bool bAllowPansharpenedOrProcessed)
458
0
{
459
0
    if (auto poProcessedDS = dynamic_cast<VRTProcessedDataset *>(this))
460
0
    {
461
0
        if (bAllowPansharpenedOrProcessed &&
462
0
            EQUAL(pszSubclass, "VRTProcessedRasterBand"))
463
0
        {
464
0
            return std::make_unique<VRTProcessedRasterBand>(poProcessedDS,
465
0
                                                            nBand);
466
0
        }
467
0
    }
468
0
    else if (EQUAL(pszSubclass, "VRTSourcedRasterBand"))
469
0
        return std::make_unique<VRTSourcedRasterBand>(this, nBand);
470
0
    else if (EQUAL(pszSubclass, "VRTDerivedRasterBand"))
471
0
        return std::make_unique<VRTDerivedRasterBand>(this, nBand);
472
0
    else if (EQUAL(pszSubclass, "VRTRawRasterBand"))
473
0
    {
474
0
#ifdef GDAL_VRT_ENABLE_RAWRASTERBAND
475
0
        if (!VRTDataset::IsRawRasterBandEnabled())
476
0
        {
477
0
            return nullptr;
478
0
        }
479
0
        return std::make_unique<VRTRawRasterBand>(this, nBand);
480
#else
481
        CPLError(CE_Failure, CPLE_NotSupported,
482
                 "VRTDataset::InitBand(): cannot instantiate VRTRawRasterBand, "
483
                 "because disabled in this GDAL build");
484
        return nullptr;
485
#endif
486
0
    }
487
0
    else if (EQUAL(pszSubclass, "VRTWarpedRasterBand") &&
488
0
             dynamic_cast<VRTWarpedDataset *>(this) != nullptr)
489
0
    {
490
0
        return std::make_unique<VRTWarpedRasterBand>(this, nBand);
491
0
    }
492
0
    else if (bAllowPansharpenedOrProcessed &&
493
0
             EQUAL(pszSubclass, "VRTPansharpenedRasterBand") &&
494
0
             dynamic_cast<VRTPansharpenedDataset *>(this) != nullptr)
495
0
    {
496
0
        return std::make_unique<VRTPansharpenedRasterBand>(this, nBand);
497
0
    }
498
499
0
    CPLError(CE_Failure, CPLE_AppDefined,
500
0
             "VRTRasterBand of unrecognized subclass '%s'.", pszSubclass);
501
0
    return nullptr;
502
0
}
503
504
/************************************************************************/
505
/*                              XMLInit()                               */
506
/************************************************************************/
507
508
CPLErr VRTDataset::XMLInit(const CPLXMLNode *psTree, const char *pszVRTPathIn)
509
510
0
{
511
0
    if (pszVRTPathIn != nullptr)
512
0
        m_pszVRTPath = CPLStrdup(pszVRTPathIn);
513
514
    /* -------------------------------------------------------------------- */
515
    /*      Check for an SRS node.                                          */
516
    /* -------------------------------------------------------------------- */
517
0
    const CPLXMLNode *psSRSNode = CPLGetXMLNode(psTree, "SRS");
518
0
    if (psSRSNode)
519
0
    {
520
0
        m_poSRS.reset(new OGRSpatialReference());
521
0
        m_poSRS->SetFromUserInput(
522
0
            CPLGetXMLValue(psSRSNode, nullptr, ""),
523
0
            OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
524
0
        const char *pszMapping =
525
0
            CPLGetXMLValue(psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
526
0
        if (pszMapping)
527
0
        {
528
0
            char **papszTokens =
529
0
                CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
530
0
            std::vector<int> anMapping;
531
0
            for (int i = 0; papszTokens && papszTokens[i]; i++)
532
0
            {
533
0
                anMapping.push_back(atoi(papszTokens[i]));
534
0
            }
535
0
            CSLDestroy(papszTokens);
536
0
            m_poSRS->SetDataAxisToSRSAxisMapping(anMapping);
537
0
        }
538
0
        else
539
0
        {
540
0
            m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
541
0
        }
542
543
0
        const char *pszCoordinateEpoch =
544
0
            CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
545
0
        if (pszCoordinateEpoch)
546
0
            m_poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
547
0
    }
548
549
    /* -------------------------------------------------------------------- */
550
    /*      Check for a GeoTransform node.                                  */
551
    /* -------------------------------------------------------------------- */
552
0
    const char *pszGT = CPLGetXMLValue(psTree, "GeoTransform", "");
553
0
    if (strlen(pszGT) > 0)
554
0
    {
555
0
        if (m_gt.Init(pszGT, ","))
556
0
        {
557
0
            m_bGeoTransformSet = true;
558
0
        }
559
0
        else
560
0
        {
561
0
            CPLError(CE_Warning, CPLE_AppDefined, "Invalid GeoTransform");
562
0
        }
563
0
    }
564
565
    /* -------------------------------------------------------------------- */
566
    /*      Check for GCPs.                                                 */
567
    /* -------------------------------------------------------------------- */
568
0
    if (const CPLXMLNode *psGCPList = CPLGetXMLNode(psTree, "GCPList"))
569
0
    {
570
0
        OGRSpatialReference *poSRS = nullptr;
571
0
        GDALDeserializeGCPListFromXML(psGCPList, m_asGCPs, &poSRS);
572
0
        m_poGCP_SRS.reset(poSRS);
573
0
    }
574
575
    /* -------------------------------------------------------------------- */
576
    /*      Apply any dataset level metadata.                               */
577
    /* -------------------------------------------------------------------- */
578
0
    oMDMD.XMLInit(psTree, TRUE);
579
580
    /* -------------------------------------------------------------------- */
581
    /*      Create dataset mask band.                                       */
582
    /* -------------------------------------------------------------------- */
583
584
    /* Parse dataset mask band first */
585
0
    const CPLXMLNode *psMaskBandNode = CPLGetXMLNode(psTree, "MaskBand");
586
587
0
    const CPLXMLNode *psChild = nullptr;
588
0
    if (psMaskBandNode)
589
0
        psChild = psMaskBandNode->psChild;
590
0
    else
591
0
        psChild = nullptr;
592
593
0
    for (; psChild != nullptr; psChild = psChild->psNext)
594
0
    {
595
0
        if (psChild->eType == CXT_Element &&
596
0
            EQUAL(psChild->pszValue, "VRTRasterBand"))
597
0
        {
598
0
            const char *pszSubclass =
599
0
                CPLGetXMLValue(psChild, "subclass", "VRTSourcedRasterBand");
600
601
0
            auto poBand = InitBand(pszSubclass, 0, false);
602
0
            if (poBand != nullptr &&
603
0
                poBand->XMLInit(psChild, pszVRTPathIn, m_oMapSharedSources) ==
604
0
                    CE_None)
605
0
            {
606
0
                SetMaskBand(std::move(poBand));
607
0
                break;
608
0
            }
609
0
            else
610
0
            {
611
0
                return CE_Failure;
612
0
            }
613
0
        }
614
0
    }
615
616
    /* -------------------------------------------------------------------- */
617
    /*      Create band information objects.                                */
618
    /* -------------------------------------------------------------------- */
619
0
    int l_nBands = 0;
620
0
    for (psChild = psTree->psChild; psChild != nullptr;
621
0
         psChild = psChild->psNext)
622
0
    {
623
0
        if (psChild->eType == CXT_Element &&
624
0
            EQUAL(psChild->pszValue, "VRTRasterBand"))
625
0
        {
626
0
            const char *pszSubclass =
627
0
                CPLGetXMLValue(psChild, "subclass", "VRTSourcedRasterBand");
628
0
            if (dynamic_cast<VRTProcessedDataset *>(this) &&
629
0
                !EQUAL(pszSubclass, "VRTProcessedRasterBand"))
630
0
            {
631
0
                CPLError(CE_Failure, CPLE_NotSupported,
632
0
                         "Only subClass=VRTProcessedRasterBand supported");
633
0
                return CE_Failure;
634
0
            }
635
636
0
            if (CPLGetXMLNode(psChild, "PixelFunctionType") != nullptr &&
637
0
                !EQUAL(pszSubclass, "VRTDerivedRasterBand"))
638
0
            {
639
0
                CPLError(CE_Failure, CPLE_NotSupported,
640
0
                         "Pixel functions may only be used with "
641
0
                         "subClass=VRTDerivedRasterBand");
642
0
                return CE_Failure;
643
0
            }
644
645
0
            auto poBand = InitBand(pszSubclass, l_nBands + 1, true);
646
0
            if (poBand != nullptr &&
647
0
                poBand->XMLInit(psChild, pszVRTPathIn, m_oMapSharedSources) ==
648
0
                    CE_None)
649
0
            {
650
0
                l_nBands++;
651
0
                SetBand(l_nBands, std::move(poBand));
652
0
            }
653
0
            else
654
0
            {
655
0
                return CE_Failure;
656
0
            }
657
0
        }
658
0
    }
659
660
0
    if (const CPLXMLNode *psGroup = CPLGetXMLNode(psTree, "Group"))
661
0
    {
662
0
        const char *pszName = CPLGetXMLValue(psGroup, "name", nullptr);
663
0
        if (pszName == nullptr || !EQUAL(pszName, "/"))
664
0
        {
665
0
            CPLError(CE_Failure, CPLE_AppDefined,
666
0
                     "Missing name or not equal to '/'");
667
0
            return CE_Failure;
668
0
        }
669
670
0
        m_poRootGroup = VRTGroup::Create(std::string(), "/");
671
0
        m_poRootGroup->SetIsRootGroup();
672
0
        if (!m_poRootGroup->XMLInit(m_poRootGroup, m_poRootGroup, psGroup,
673
0
                                    pszVRTPathIn))
674
0
        {
675
0
            return CE_Failure;
676
0
        }
677
0
    }
678
679
    /* -------------------------------------------------------------------- */
680
    /*      Create virtual overviews.                                       */
681
    /* -------------------------------------------------------------------- */
682
0
    const char *pszSubClass = CPLGetXMLValue(psTree, "subClass", "");
683
0
    if (EQUAL(pszSubClass, ""))
684
0
    {
685
0
        m_aosOverviewList =
686
0
            CSLTokenizeString(CPLGetXMLValue(psTree, "OverviewList", ""));
687
0
        m_osOverviewResampling =
688
0
            CPLGetXMLValue(psTree, "OverviewList.resampling", "");
689
0
    }
690
691
0
    return CE_None;
692
0
}
693
694
/************************************************************************/
695
/*                            GetGCPCount()                             */
696
/************************************************************************/
697
698
int VRTDataset::GetGCPCount()
699
700
0
{
701
0
    return static_cast<int>(m_asGCPs.size());
702
0
}
703
704
/************************************************************************/
705
/*                              GetGCPs()                               */
706
/************************************************************************/
707
708
const GDAL_GCP *VRTDataset::GetGCPs()
709
710
0
{
711
0
    return gdal::GCP::c_ptr(m_asGCPs);
712
0
}
713
714
/************************************************************************/
715
/*                              SetGCPs()                               */
716
/************************************************************************/
717
718
CPLErr VRTDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
719
                           const OGRSpatialReference *poGCP_SRS)
720
721
0
{
722
0
    m_poGCP_SRS.reset(poGCP_SRS ? poGCP_SRS->Clone() : nullptr);
723
0
    m_asGCPs = gdal::GCP::fromC(pasGCPListIn, nGCPCountIn);
724
725
0
    SetNeedsFlush();
726
727
0
    return CE_None;
728
0
}
729
730
/************************************************************************/
731
/*                           SetSpatialRef()                            */
732
/************************************************************************/
733
734
CPLErr VRTDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
735
736
0
{
737
0
    m_poSRS.reset(poSRS ? poSRS->Clone() : nullptr);
738
739
0
    SetNeedsFlush();
740
741
0
    return CE_None;
742
0
}
743
744
/************************************************************************/
745
/*                          SetGeoTransform()                           */
746
/************************************************************************/
747
748
CPLErr VRTDataset::SetGeoTransform(const GDALGeoTransform &gt)
749
750
0
{
751
0
    m_gt = gt;
752
0
    m_bGeoTransformSet = TRUE;
753
754
0
    SetNeedsFlush();
755
756
0
    return CE_None;
757
0
}
758
759
/************************************************************************/
760
/*                          GetGeoTransform()                           */
761
/************************************************************************/
762
763
CPLErr VRTDataset::GetGeoTransform(GDALGeoTransform &gt) const
764
765
0
{
766
0
    gt = m_gt;
767
768
0
    return m_bGeoTransformSet ? CE_None : CE_Failure;
769
0
}
770
771
/************************************************************************/
772
/*                            SetMetadata()                             */
773
/************************************************************************/
774
775
CPLErr VRTDataset::SetMetadata(CSLConstList papszMetadata,
776
                               const char *pszDomain)
777
778
0
{
779
0
    SetNeedsFlush();
780
781
0
    return GDALDataset::SetMetadata(papszMetadata, pszDomain);
782
0
}
783
784
/************************************************************************/
785
/*                          SetMetadataItem()                           */
786
/************************************************************************/
787
788
CPLErr VRTDataset::SetMetadataItem(const char *pszName, const char *pszValue,
789
                                   const char *pszDomain)
790
791
0
{
792
0
    SetNeedsFlush();
793
794
0
    return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
795
0
}
796
797
/************************************************************************/
798
/*                              Identify()                              */
799
/************************************************************************/
800
801
int VRTDataset::Identify(GDALOpenInfo *poOpenInfo)
802
803
0
{
804
0
    if (poOpenInfo->nHeaderBytes > 20 &&
805
0
        strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
806
0
               "<VRTDataset") != nullptr)
807
0
        return TRUE;
808
809
0
    if (strstr(poOpenInfo->pszFilename, "<VRTDataset") != nullptr)
810
0
        return TRUE;
811
812
0
    if (STARTS_WITH_CI(poOpenInfo->pszFilename, VRT_PROTOCOL_PREFIX))
813
0
        return TRUE;
814
815
0
    return FALSE;
816
0
}
817
818
/************************************************************************/
819
/*                                Open()                                */
820
/************************************************************************/
821
822
GDALDataset *VRTDataset::Open(GDALOpenInfo *poOpenInfo)
823
824
0
{
825
    /* -------------------------------------------------------------------- */
826
    /*      Does this appear to be a virtual dataset definition XML         */
827
    /*      file?                                                           */
828
    /* -------------------------------------------------------------------- */
829
0
    if (!Identify(poOpenInfo))
830
0
        return nullptr;
831
832
0
    if (STARTS_WITH_CI(poOpenInfo->pszFilename, VRT_PROTOCOL_PREFIX))
833
0
        return OpenVRTProtocol(poOpenInfo->pszFilename);
834
835
    /* -------------------------------------------------------------------- */
836
    /*      Try to read the whole file into memory.                         */
837
    /* -------------------------------------------------------------------- */
838
0
    char *pszXML = nullptr;
839
0
    VSILFILE *fp = poOpenInfo->fpL;
840
841
0
    char *pszVRTPath = nullptr;
842
0
    if (fp != nullptr)
843
0
    {
844
0
        poOpenInfo->fpL = nullptr;
845
846
0
        GByte *pabyOut = nullptr;
847
0
        if (!VSIIngestFile(fp, poOpenInfo->pszFilename, &pabyOut, nullptr,
848
0
                           INT_MAX - 1))
849
0
        {
850
0
            CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
851
0
            return nullptr;
852
0
        }
853
0
        pszXML = reinterpret_cast<char *>(pabyOut);
854
855
0
        char *pszCurDir = CPLGetCurrentDir();
856
0
        std::string currentVrtFilename =
857
0
            CPLProjectRelativeFilenameSafe(pszCurDir, poOpenInfo->pszFilename);
858
0
        CPLString osInitialCurrentVrtFilename(currentVrtFilename);
859
0
        CPLFree(pszCurDir);
860
861
0
#if !defined(_WIN32)
862
0
        char filenameBuffer[2048];
863
864
0
        while (true)
865
0
        {
866
0
            VSIStatBuf statBuffer;
867
0
            int lstatCode = lstat(currentVrtFilename.c_str(), &statBuffer);
868
0
            if (lstatCode == -1)
869
0
            {
870
0
                if (errno == ENOENT)
871
0
                {
872
                    // File could be a virtual file, let later checks handle it.
873
0
                    break;
874
0
                }
875
0
                else
876
0
                {
877
0
                    CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
878
0
                    CPLFree(pszXML);
879
0
                    CPLError(CE_Failure, CPLE_FileIO, "Failed to lstat %s: %s",
880
0
                             currentVrtFilename.c_str(), VSIStrerror(errno));
881
0
                    return nullptr;
882
0
                }
883
0
            }
884
885
0
            if (!VSI_ISLNK(statBuffer.st_mode))
886
0
            {
887
0
                break;
888
0
            }
889
890
0
            const int bufferSize = static_cast<int>(
891
0
                readlink(currentVrtFilename.c_str(), filenameBuffer,
892
0
                         sizeof(filenameBuffer)));
893
0
            if (bufferSize != -1)
894
0
            {
895
0
                filenameBuffer[std::min(
896
0
                    bufferSize, static_cast<int>(sizeof(filenameBuffer)) - 1)] =
897
0
                    0;
898
                // The filename in filenameBuffer might be a relative path
899
                // from the linkfile resolve it before looping
900
0
                currentVrtFilename = CPLProjectRelativeFilenameSafe(
901
0
                    CPLGetDirnameSafe(currentVrtFilename.c_str()).c_str(),
902
0
                    filenameBuffer);
903
0
            }
904
0
            else
905
0
            {
906
0
                CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
907
0
                CPLFree(pszXML);
908
0
                CPLError(CE_Failure, CPLE_FileIO,
909
0
                         "Failed to read filename from symlink %s: %s",
910
0
                         currentVrtFilename.c_str(), VSIStrerror(errno));
911
0
                return nullptr;
912
0
            }
913
0
        }
914
0
#endif  // !defined(__WIN32)
915
916
0
        if (osInitialCurrentVrtFilename == currentVrtFilename)
917
0
            pszVRTPath =
918
0
                CPLStrdup(CPLGetPathSafe(poOpenInfo->pszFilename).c_str());
919
0
        else
920
0
            pszVRTPath =
921
0
                CPLStrdup(CPLGetPathSafe(currentVrtFilename.c_str()).c_str());
922
923
0
        CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
924
0
    }
925
    /* -------------------------------------------------------------------- */
926
    /*      Or use the filename as the XML input.                           */
927
    /* -------------------------------------------------------------------- */
928
0
    else
929
0
    {
930
0
        pszXML = CPLStrdup(poOpenInfo->pszFilename);
931
0
    }
932
933
0
    if (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "ROOT_PATH") != nullptr)
934
0
    {
935
0
        CPLFree(pszVRTPath);
936
0
        pszVRTPath = CPLStrdup(
937
0
            CSLFetchNameValue(poOpenInfo->papszOpenOptions, "ROOT_PATH"));
938
0
    }
939
940
    /* -------------------------------------------------------------------- */
941
    /*      Turn the XML representation into a VRTDataset.                  */
942
    /* -------------------------------------------------------------------- */
943
0
    auto poDS = OpenXML(pszXML, pszVRTPath, poOpenInfo->eAccess);
944
945
0
    if (poDS != nullptr)
946
0
        poDS->m_bNeedsFlush = false;
947
948
0
    if (poDS != nullptr)
949
0
    {
950
0
        if (poDS->GetRasterCount() == 0 &&
951
0
            (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER) == 0 &&
952
0
            strstr(pszXML, "VRTPansharpenedDataset") == nullptr)
953
0
        {
954
0
            poDS.reset();
955
0
        }
956
0
        else if (poDS->GetRootGroup() == nullptr &&
957
0
                 (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
958
0
                 (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER) != 0)
959
0
        {
960
0
            poDS.reset();
961
0
        }
962
0
    }
963
964
0
    CPLFree(pszXML);
965
0
    CPLFree(pszVRTPath);
966
967
    /* -------------------------------------------------------------------- */
968
    /*      Initialize info for later overview discovery.                   */
969
    /* -------------------------------------------------------------------- */
970
971
0
    if (poDS != nullptr)
972
0
    {
973
0
        if (fp != nullptr)
974
0
        {
975
0
            poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
976
0
            if (poOpenInfo->AreSiblingFilesLoaded())
977
0
                poDS->oOvManager.TransferSiblingFiles(
978
0
                    poOpenInfo->StealSiblingFiles());
979
0
        }
980
981
        // Creating virtual overviews, but only if there is no higher priority
982
        // overview source, ie. a Overview element at VRT band level,
983
        // or external .vrt.ovr
984
0
        if (!poDS->m_aosOverviewList.empty())
985
0
        {
986
0
            if (poDS->nBands > 0)
987
0
            {
988
0
                auto poBand = dynamic_cast<VRTRasterBand *>(poDS->papoBands[0]);
989
0
                if (poBand && !poBand->m_aoOverviewInfos.empty())
990
0
                {
991
0
                    poDS->m_aosOverviewList.Clear();
992
0
                    CPLDebug("VRT",
993
0
                             "Ignoring virtual overviews of OverviewList "
994
0
                             "because Overview element is present on VRT band");
995
0
                }
996
0
                else if (poBand &&
997
0
                         poBand->GDALRasterBand::GetOverviewCount() > 0)
998
0
                {
999
0
                    poDS->m_aosOverviewList.Clear();
1000
0
                    CPLDebug("VRT",
1001
0
                             "Ignoring virtual overviews of OverviewList "
1002
0
                             "because external .vrt.ovr is available");
1003
0
                }
1004
0
            }
1005
0
            for (int iOverview = 0; iOverview < poDS->m_aosOverviewList.size();
1006
0
                 iOverview++)
1007
0
            {
1008
0
                const int nOvFactor = atoi(poDS->m_aosOverviewList[iOverview]);
1009
0
                if (nOvFactor <= 1)
1010
0
                {
1011
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1012
0
                             "Invalid overview factor");
1013
0
                    return nullptr;
1014
0
                }
1015
1016
0
                poDS->AddVirtualOverview(
1017
0
                    nOvFactor, poDS->m_osOverviewResampling.empty()
1018
0
                                   ? "nearest"
1019
0
                                   : poDS->m_osOverviewResampling.c_str());
1020
0
            }
1021
0
            poDS->m_aosOverviewList.Clear();
1022
0
        }
1023
1024
0
        if (poDS->eAccess == GA_Update && poDS->m_poRootGroup &&
1025
0
            !STARTS_WITH_CI(poOpenInfo->pszFilename, "<VRT"))
1026
0
        {
1027
0
            poDS->m_poRootGroup->SetFilename(poOpenInfo->pszFilename);
1028
0
        }
1029
0
    }
1030
1031
0
    return poDS.release();
1032
0
}
1033
1034
/************************************************************************/
1035
/*                         OpenVRTProtocol()                            */
1036
/*                                                                      */
1037
/*      Create an open VRTDataset from a vrt:// string.                 */
1038
/************************************************************************/
1039
1040
GDALDataset *VRTDataset::OpenVRTProtocol(const char *pszSpec)
1041
1042
0
{
1043
0
    CPLAssert(STARTS_WITH_CI(pszSpec, VRT_PROTOCOL_PREFIX));
1044
0
    CPLString osFilename(pszSpec + strlen(VRT_PROTOCOL_PREFIX));
1045
0
    const auto nPosQuotationMark = osFilename.find('?');
1046
0
    CPLString osQueryString;
1047
0
    if (nPosQuotationMark != std::string::npos)
1048
0
    {
1049
0
        osQueryString = osFilename.substr(nPosQuotationMark + 1);
1050
0
        osFilename.resize(nPosQuotationMark);
1051
0
    }
1052
1053
    // Parse query string, get args required for initial Open()
1054
0
    const CPLStringList aosTokens(CSLTokenizeString2(osQueryString, "&", 0));
1055
0
    CPLStringList aosAllowedDrivers;
1056
0
    CPLStringList aosOpenOptions;
1057
1058
0
    for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(
1059
0
             aosTokens, /* bReturnNullKeyIfNotNameValue = */ true))
1060
0
    {
1061
0
        if (!pszKey)
1062
0
        {
1063
0
            CPLError(CE_Failure, CPLE_NotSupported,
1064
0
                     "Invalid option specification: %s\n"
1065
0
                     "must be in the form 'key=value'",
1066
0
                     pszValue);
1067
0
            return nullptr;
1068
0
        }
1069
0
        else if (EQUAL(pszKey, "if"))
1070
0
        {
1071
0
            if (!aosAllowedDrivers.empty())
1072
0
            {
1073
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1074
0
                         "'if' option should be specified once, use commas "
1075
0
                         "to input multiple values.");
1076
0
                return nullptr;
1077
0
            }
1078
0
            aosAllowedDrivers = CSLTokenizeString2(pszValue, ",", 0);
1079
0
        }
1080
0
        else if (EQUAL(pszKey, "oo"))
1081
0
        {
1082
0
            if (!aosOpenOptions.empty())
1083
0
            {
1084
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1085
0
                         "'oo' option should be specified once, use commas "
1086
0
                         "to input multiple values.");
1087
0
                return nullptr;
1088
0
            }
1089
0
            aosOpenOptions = CSLTokenizeString2(pszValue, ",", 0);
1090
0
        }
1091
0
    }
1092
1093
    // We don't open in GDAL_OF_SHARED mode to avoid issues when we open a
1094
    // http://.jp2 file with the JP2OpenJPEG driver through the HTTP driver,
1095
    // which returns a /vsimem/ file
1096
0
    auto poSrcDS = std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>(
1097
0
        GDALDataset::Open(osFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
1098
0
                          aosAllowedDrivers.List(), aosOpenOptions.List(),
1099
0
                          nullptr));
1100
0
    if (poSrcDS == nullptr)
1101
0
    {
1102
0
        return nullptr;
1103
0
    }
1104
1105
0
    bool bFound_transpose = false;
1106
0
    for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(aosTokens))
1107
0
    {
1108
0
        if (EQUAL(pszKey, "transpose"))
1109
0
        {
1110
0
            bFound_transpose = true;
1111
0
            const CPLStringList aosTransposeTokens(
1112
0
                CSLTokenizeString2(pszValue, ":", 0));
1113
0
            if (aosTransposeTokens.size() != 2)
1114
0
            {
1115
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1116
0
                         "Invalid transpose option: %s", pszValue);
1117
0
                return nullptr;
1118
0
            }
1119
0
            const CPLStringList aosTransposeIndex(
1120
0
                CSLTokenizeString2(aosTransposeTokens[1], ",", 0));
1121
            // fail if not two values
1122
0
            if (aosTransposeIndex.size() != 2)
1123
0
            {
1124
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1125
0
                         "Invalid transpose option: %s", pszValue);
1126
0
                return nullptr;
1127
0
            }
1128
0
            int index_x = atoi(aosTransposeIndex[0]);
1129
0
            int index_y = atoi(aosTransposeIndex[1]);
1130
1131
0
            auto poMDimDS = std::unique_ptr<GDALDataset>(
1132
0
                GDALDataset::Open(osFilename, GDAL_OF_MULTIDIM_RASTER));
1133
0
            if (!poMDimDS)
1134
0
                return nullptr;
1135
0
            auto poMdimGroup = poMDimDS->GetRootGroup();
1136
0
            if (!poMdimGroup)
1137
0
            {
1138
0
                return nullptr;
1139
0
            }
1140
0
            auto poArray =
1141
0
                poMdimGroup->OpenMDArrayFromFullname(aosTransposeTokens[0]);
1142
0
            if (!poArray)
1143
0
            {
1144
0
                return nullptr;
1145
0
            }
1146
1147
0
            auto poClassicDS = poArray->AsClassicDataset(index_x, index_y);
1148
1149
0
            if (!poClassicDS)
1150
0
                return nullptr;
1151
0
            poSrcDS =
1152
0
                std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>(
1153
0
                    poClassicDS);
1154
0
        }
1155
0
    }
1156
    // scan for sd_name/sd in tokens, close the source dataset and reopen if found/valid
1157
0
    bool bFound_subdataset = false;
1158
0
    for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(aosTokens))
1159
0
    {
1160
0
        if (EQUAL(pszKey, "sd_name"))
1161
0
        {
1162
0
            if (bFound_transpose)
1163
0
            {
1164
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1165
0
                         "'sd_name' is mutually exclusive with option "
1166
0
                         "'transpose'");
1167
0
                return nullptr;
1168
0
            }
1169
0
            if (bFound_subdataset)
1170
0
            {
1171
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1172
0
                         "'sd_name' is mutually exclusive with option "
1173
0
                         "'sd'");
1174
0
                return nullptr;
1175
0
            }
1176
0
            CSLConstList papszSubdatasets = poSrcDS->GetMetadata("SUBDATASETS");
1177
0
            int nSubdatasets = CSLCount(papszSubdatasets);
1178
1179
0
            if (nSubdatasets > 0)
1180
0
            {
1181
0
                bool bFound = false;
1182
0
                for (int j = 0; j < nSubdatasets && papszSubdatasets[j]; j += 2)
1183
0
                {
1184
0
                    const char *pszEqual = strchr(papszSubdatasets[j], '=');
1185
0
                    if (!pszEqual)
1186
0
                    {
1187
0
                        CPLError(CE_Failure, CPLE_IllegalArg,
1188
0
                                 "'sd_name:' failed to obtain "
1189
0
                                 "subdataset string ");
1190
0
                        return nullptr;
1191
0
                    }
1192
0
                    const char *pszSubdatasetSource = pszEqual + 1;
1193
0
                    GDALSubdatasetInfoH info =
1194
0
                        GDALGetSubdatasetInfo(pszSubdatasetSource);
1195
0
                    char *component =
1196
0
                        info ? GDALSubdatasetInfoGetSubdatasetComponent(info)
1197
0
                             : nullptr;
1198
1199
0
                    bFound = component && EQUAL(pszValue, component);
1200
0
                    bFound_subdataset = true;
1201
0
                    CPLFree(component);
1202
0
                    GDALDestroySubdatasetInfo(info);
1203
0
                    if (bFound)
1204
0
                    {
1205
0
                        poSrcDS.reset(GDALDataset::Open(
1206
0
                            pszSubdatasetSource,
1207
0
                            GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
1208
0
                            aosAllowedDrivers.List(), aosOpenOptions.List(),
1209
0
                            nullptr));
1210
0
                        if (poSrcDS == nullptr)
1211
0
                        {
1212
0
                            return nullptr;
1213
0
                        }
1214
1215
0
                        break;
1216
0
                    }
1217
0
                }
1218
1219
0
                if (!bFound)
1220
0
                {
1221
0
                    CPLError(CE_Failure, CPLE_IllegalArg,
1222
0
                             "'sd_name' option should be be a valid "
1223
0
                             "subdataset component name");
1224
0
                    return nullptr;
1225
0
                }
1226
0
            }
1227
0
        }
1228
1229
0
        if (EQUAL(pszKey, "sd"))
1230
0
        {
1231
0
            if (bFound_transpose)
1232
0
            {
1233
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1234
0
                         "'sd' is mutually exclusive with option "
1235
0
                         "'transpose'");
1236
0
                return nullptr;
1237
0
            }
1238
0
            if (bFound_subdataset)
1239
0
            {
1240
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1241
0
                         "'sd' is mutually exclusive with option "
1242
0
                         "'sd_name'");
1243
0
                return nullptr;
1244
0
            }
1245
0
            CSLConstList papszSubdatasets = poSrcDS->GetMetadata("SUBDATASETS");
1246
0
            int nSubdatasets = CSLCount(papszSubdatasets);
1247
1248
0
            if (nSubdatasets > 0)
1249
0
            {
1250
0
                int iSubdataset = atoi(pszValue);
1251
0
                if (iSubdataset < 1 || iSubdataset > (nSubdatasets) / 2)
1252
0
                {
1253
0
                    CPLError(CE_Failure, CPLE_IllegalArg,
1254
0
                             "'sd' option should indicate a valid "
1255
0
                             "subdataset component number (starting with 1)");
1256
0
                    return nullptr;
1257
0
                }
1258
0
                const std::string osSubdatasetSource(
1259
0
                    strstr(papszSubdatasets[(iSubdataset - 1) * 2], "=") + 1);
1260
0
                if (osSubdatasetSource.empty())
1261
0
                {
1262
0
                    CPLError(CE_Failure, CPLE_IllegalArg,
1263
0
                             "'sd:' failed to obtain subdataset "
1264
0
                             "string ");
1265
0
                    return nullptr;
1266
0
                }
1267
1268
0
                poSrcDS.reset(GDALDataset::Open(
1269
0
                    osSubdatasetSource.c_str(),
1270
0
                    GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
1271
0
                    aosAllowedDrivers.List(), aosOpenOptions.List(), nullptr));
1272
0
                if (poSrcDS == nullptr)
1273
0
                {
1274
0
                    return nullptr;
1275
0
                }
1276
0
                bFound_subdataset = true;
1277
0
            }
1278
0
        }
1279
0
    }
1280
1281
0
    std::vector<int> anBands;
1282
1283
    // Check for 'block' option and validate/convert to srcwin
1284
0
    int nBlockXOff = -1;
1285
0
    int nBlockYOff = -1;
1286
0
    bool bFoundBlock = false;
1287
0
    for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(aosTokens))
1288
0
    {
1289
0
        if (EQUAL(pszKey, "block"))
1290
0
        {
1291
0
            const CPLStringList aosBlockParams(
1292
0
                CSLTokenizeString2(pszValue, ",", 0));
1293
0
            if (aosBlockParams.size() != 2)
1294
0
            {
1295
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1296
0
                         "Invalid block option: %s, must be two "
1297
0
                         "values separated by comma nXBlockOff,nYBlockOff",
1298
0
                         pszValue);
1299
0
                return nullptr;
1300
0
            }
1301
1302
            // Convert using CPLStrtod to handle scientific notation (e.g., 1e0)
1303
            // and detect non-numeric values via endPtr
1304
0
            char *pszEnd = nullptr;
1305
0
            const double dfBlockXOff = CPLStrtod(aosBlockParams[0], &pszEnd);
1306
0
            if (pszEnd == aosBlockParams[0] || *pszEnd != '\0' ||
1307
0
                dfBlockXOff < 0 || dfBlockXOff != static_cast<int>(dfBlockXOff))
1308
0
            {
1309
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1310
0
                         "Invalid block option: nXBlockOff '%s' is not a valid "
1311
0
                         "non-negative integer",
1312
0
                         aosBlockParams[0]);
1313
0
                return nullptr;
1314
0
            }
1315
1316
0
            pszEnd = nullptr;
1317
0
            const double dfBlockYOff = CPLStrtod(aosBlockParams[1], &pszEnd);
1318
0
            if (pszEnd == aosBlockParams[1] || *pszEnd != '\0' ||
1319
0
                dfBlockYOff < 0 || dfBlockYOff != static_cast<int>(dfBlockYOff))
1320
0
            {
1321
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1322
0
                         "Invalid block option: nYBlockOff '%s' is not a valid "
1323
0
                         "non-negative integer",
1324
0
                         aosBlockParams[1]);
1325
0
                return nullptr;
1326
0
            }
1327
1328
0
            nBlockXOff = static_cast<int>(dfBlockXOff);
1329
0
            nBlockYOff = static_cast<int>(dfBlockYOff);
1330
0
            bFoundBlock = true;
1331
0
        }
1332
0
    }
1333
1334
    // Validate block indices and convert to pixel coordinates
1335
0
    int nBlockSrcWinXOff = 0;
1336
0
    int nBlockSrcWinYOff = 0;
1337
0
    int nBlockSrcWinXSize = 0;
1338
0
    int nBlockSrcWinYSize = 0;
1339
0
    if (bFoundBlock)
1340
0
    {
1341
        // Check mutual exclusivity with other spatial options
1342
0
        for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(aosTokens))
1343
0
        {
1344
0
            if (EQUAL(pszKey, "srcwin") || EQUAL(pszKey, "projwin") ||
1345
0
                EQUAL(pszKey, "outsize") || EQUAL(pszKey, "tr") ||
1346
0
                EQUAL(pszKey, "r") || EQUAL(pszKey, "ovr"))
1347
0
            {
1348
0
                CPLError(
1349
0
                    CE_Failure, CPLE_IllegalArg,
1350
0
                    "'block' is mutually exclusive with options "
1351
0
                    "'srcwin', 'projwin', 'outsize', 'tr', 'r', and 'ovr'");
1352
0
                return nullptr;
1353
0
            }
1354
0
        }
1355
1356
        // Check that the dataset has at least one band
1357
0
        if (poSrcDS->GetRasterCount() == 0)
1358
0
        {
1359
0
            CPLError(CE_Failure, CPLE_NotSupported,
1360
0
                     "'block' option requires a raster with at least one band");
1361
0
            return nullptr;
1362
0
        }
1363
1364
        // Validate using GetActualBlockSize which handles bounds checking
1365
0
        int nXValid = 0;
1366
0
        int nYValid = 0;
1367
0
        int nBlockXSize = 0;
1368
0
        int nBlockYSize = 0;
1369
0
        poSrcDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
1370
0
        if (poSrcDS->GetRasterBand(1)->GetActualBlockSize(
1371
0
                nBlockXOff, nBlockYOff, &nXValid, &nYValid) != CE_None)
1372
0
        {
1373
1374
0
            const int nMaxXBlock =
1375
0
                cpl::div_round_up(poSrcDS->GetRasterXSize(), nBlockXSize);
1376
0
            const int nMaxYBlock =
1377
0
                cpl::div_round_up(poSrcDS->GetRasterYSize(), nBlockYSize);
1378
0
            CPLError(CE_Failure, CPLE_IllegalArg,
1379
0
                     "Invalid block indices: %d,%d. "
1380
0
                     "Valid range is 0-%d for X and 0-%d for Y",
1381
0
                     nBlockXOff, nBlockYOff, nMaxXBlock - 1, nMaxYBlock - 1);
1382
0
            return nullptr;
1383
0
        }
1384
1385
0
        nBlockSrcWinXOff = nBlockXOff * nBlockXSize;
1386
0
        nBlockSrcWinYOff = nBlockYOff * nBlockYSize;
1387
0
        nBlockSrcWinXSize = nXValid;
1388
0
        nBlockSrcWinYSize = nYValid;
1389
0
    }
1390
1391
0
    CPLStringList argv;
1392
0
    argv.AddString("-of");
1393
0
    argv.AddString("VRT");
1394
1395
    // Add srcwin from block option before processing other options
1396
0
    if (bFoundBlock)
1397
0
    {
1398
0
        argv.AddString("-srcwin");
1399
0
        argv.AddString(CPLSPrintf("%d", nBlockSrcWinXOff));
1400
0
        argv.AddString(CPLSPrintf("%d", nBlockSrcWinYOff));
1401
0
        argv.AddString(CPLSPrintf("%d", nBlockSrcWinXSize));
1402
0
        argv.AddString(CPLSPrintf("%d", nBlockSrcWinYSize));
1403
0
    }
1404
1405
0
    for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(aosTokens))
1406
0
    {
1407
0
        if (EQUAL(pszKey, "bands"))
1408
0
        {
1409
0
            const CPLStringList aosBands(CSLTokenizeString2(pszValue, ",", 0));
1410
0
            for (int j = 0; j < aosBands.size(); j++)
1411
0
            {
1412
0
                if (EQUAL(aosBands[j], "mask"))
1413
0
                {
1414
0
                    anBands.push_back(0);
1415
0
                }
1416
0
                else
1417
0
                {
1418
0
                    const int nBand = atoi(aosBands[j]);
1419
0
                    if (nBand <= 0 || nBand > poSrcDS->GetRasterCount())
1420
0
                    {
1421
0
                        CPLError(CE_Failure, CPLE_IllegalArg,
1422
0
                                 "Invalid band number: %s", aosBands[j]);
1423
0
                        return nullptr;
1424
0
                    }
1425
0
                    anBands.push_back(nBand);
1426
0
                }
1427
0
            }
1428
1429
0
            for (const int nBand : anBands)
1430
0
            {
1431
0
                argv.AddString("-b");
1432
0
                argv.AddString(nBand == 0 ? "mask" : CPLSPrintf("%d", nBand));
1433
0
            }
1434
0
        }
1435
1436
0
        else if (EQUAL(pszKey, "a_nodata"))
1437
0
        {
1438
0
            argv.AddString("-a_nodata");
1439
0
            argv.AddString(pszValue);
1440
0
        }
1441
1442
0
        else if (EQUAL(pszKey, "a_srs"))
1443
0
        {
1444
0
            argv.AddString("-a_srs");
1445
0
            argv.AddString(pszValue);
1446
0
        }
1447
1448
0
        else if (EQUAL(pszKey, "a_ullr"))
1449
0
        {
1450
            // Parse the limits
1451
0
            const CPLStringList aosUllr(CSLTokenizeString2(pszValue, ",", 0));
1452
            // fail if not four values
1453
0
            if (aosUllr.size() != 4)
1454
0
            {
1455
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1456
0
                         "Invalid a_ullr option: %s", pszValue);
1457
0
                return nullptr;
1458
0
            }
1459
1460
0
            argv.AddString("-a_ullr");
1461
0
            argv.AddString(aosUllr[0]);
1462
0
            argv.AddString(aosUllr[1]);
1463
0
            argv.AddString(aosUllr[2]);
1464
0
            argv.AddString(aosUllr[3]);
1465
0
        }
1466
1467
0
        else if (EQUAL(pszKey, "ovr"))
1468
0
        {
1469
0
            argv.AddString("-ovr");
1470
0
            argv.AddString(pszValue);
1471
0
        }
1472
0
        else if (EQUAL(pszKey, "expand"))
1473
0
        {
1474
0
            argv.AddString("-expand");
1475
0
            argv.AddString(pszValue);
1476
0
        }
1477
0
        else if (EQUAL(pszKey, "a_scale"))
1478
0
        {
1479
0
            argv.AddString("-a_scale");
1480
0
            argv.AddString(pszValue);
1481
0
        }
1482
0
        else if (EQUAL(pszKey, "a_offset"))
1483
0
        {
1484
0
            argv.AddString("-a_offset");
1485
0
            argv.AddString(pszValue);
1486
0
        }
1487
0
        else if (EQUAL(pszKey, "ot"))
1488
0
        {
1489
0
            argv.AddString("-ot");
1490
0
            argv.AddString(pszValue);
1491
0
        }
1492
0
        else if (EQUAL(pszKey, "gcp"))
1493
0
        {
1494
0
            const CPLStringList aosGCP(CSLTokenizeString2(pszValue, ",", 0));
1495
1496
0
            if (aosGCP.size() < 4 || aosGCP.size() > 5)
1497
0
            {
1498
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1499
0
                         "Invalid value for GCP: %s\n  need 4, or 5 "
1500
0
                         "numbers, comma separated: "
1501
0
                         "'gcp=<pixel>,<line>,<easting>,<northing>[,<"
1502
0
                         "elevation>]'",
1503
0
                         pszValue);
1504
0
                return nullptr;
1505
0
            }
1506
0
            argv.AddString("-gcp");
1507
0
            for (int j = 0; j < aosGCP.size(); j++)
1508
0
            {
1509
0
                argv.AddString(aosGCP[j]);
1510
0
            }
1511
0
        }
1512
0
        else if (EQUAL(pszKey, "scale") || STARTS_WITH_CI(pszKey, "scale_"))
1513
0
        {
1514
0
            const CPLStringList aosScaleParams(
1515
0
                CSLTokenizeString2(pszValue, ",", 0));
1516
1517
0
            if (!(aosScaleParams.size() == 2) &&
1518
0
                !(aosScaleParams.size() == 4) && !(aosScaleParams.size() == 1))
1519
0
            {
1520
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1521
0
                         "Invalid value for scale, (or scale_bn): "
1522
0
                         "%s\n  need 'scale=true', or 2 or 4 "
1523
0
                         "numbers, comma separated: "
1524
0
                         "'scale=src_min,src_max[,dst_min,dst_max]' or "
1525
0
                         "'scale_bn=src_min,src_max[,dst_min,dst_max]'",
1526
0
                         pszValue);
1527
0
                return nullptr;
1528
0
            }
1529
1530
            // -scale because scale=true or scale=min,max or scale=min,max,dstmin,dstmax
1531
0
            if (aosScaleParams.size() == 1 && CPLTestBool(aosScaleParams[0]))
1532
0
            {
1533
0
                argv.AddString(CPLSPrintf("-%s", pszKey));
1534
0
            }
1535
            // add remaining params (length 2 or 4)
1536
0
            if (aosScaleParams.size() > 1)
1537
0
            {
1538
0
                argv.AddString(CPLSPrintf("-%s", pszKey));
1539
0
                for (int j = 0; j < aosScaleParams.size(); j++)
1540
0
                {
1541
0
                    argv.AddString(aosScaleParams[j]);
1542
0
                }
1543
0
            }
1544
0
        }
1545
0
        else if (EQUAL(pszKey, "exponent") ||
1546
0
                 STARTS_WITH_CI(pszKey, "exponent_"))
1547
0
        {
1548
0
            argv.AddString(CPLSPrintf("-%s", pszKey));
1549
0
            argv.AddString(pszValue);
1550
0
        }
1551
0
        else if (EQUAL(pszKey, "outsize"))
1552
0
        {
1553
0
            const CPLStringList aosOutSize(
1554
0
                CSLTokenizeString2(pszValue, ",", 0));
1555
0
            if (aosOutSize.size() != 2)
1556
0
            {
1557
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1558
0
                         "Invalid outsize option: %s, must be two"
1559
0
                         "values separated by comma pixel,line or two "
1560
0
                         "fraction values with percent symbol",
1561
0
                         pszValue);
1562
0
                return nullptr;
1563
0
            }
1564
0
            argv.AddString("-outsize");
1565
0
            argv.AddString(aosOutSize[0]);
1566
0
            argv.AddString(aosOutSize[1]);
1567
0
        }
1568
0
        else if (EQUAL(pszKey, "projwin"))
1569
0
        {
1570
            // Parse the limits
1571
0
            const CPLStringList aosProjWin(
1572
0
                CSLTokenizeString2(pszValue, ",", 0));
1573
            // fail if not four values
1574
0
            if (aosProjWin.size() != 4)
1575
0
            {
1576
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1577
0
                         "Invalid projwin option: %s", pszValue);
1578
0
                return nullptr;
1579
0
            }
1580
1581
0
            argv.AddString("-projwin");
1582
0
            argv.AddString(aosProjWin[0]);
1583
0
            argv.AddString(aosProjWin[1]);
1584
0
            argv.AddString(aosProjWin[2]);
1585
0
            argv.AddString(aosProjWin[3]);
1586
0
        }
1587
0
        else if (EQUAL(pszKey, "projwin_srs"))
1588
0
        {
1589
0
            argv.AddString("-projwin_srs");
1590
0
            argv.AddString(pszValue);
1591
0
        }
1592
0
        else if (EQUAL(pszKey, "tr"))
1593
0
        {
1594
0
            const CPLStringList aosTargetResolution(
1595
0
                CSLTokenizeString2(pszValue, ",", 0));
1596
0
            if (aosTargetResolution.size() != 2)
1597
0
            {
1598
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1599
0
                         "Invalid tr option: %s, must be two "
1600
0
                         "values separated by comma xres,yres",
1601
0
                         pszValue);
1602
0
                return nullptr;
1603
0
            }
1604
0
            argv.AddString("-tr");
1605
0
            argv.AddString(aosTargetResolution[0]);
1606
0
            argv.AddString(aosTargetResolution[1]);
1607
0
        }
1608
0
        else if (EQUAL(pszKey, "r"))
1609
0
        {
1610
0
            argv.AddString("-r");
1611
0
            argv.AddString(pszValue);
1612
0
        }
1613
1614
0
        else if (EQUAL(pszKey, "srcwin"))
1615
0
        {
1616
            // Parse the limits
1617
0
            const CPLStringList aosSrcWin(CSLTokenizeString2(pszValue, ",", 0));
1618
            // fail if not four values
1619
0
            if (aosSrcWin.size() != 4)
1620
0
            {
1621
0
                CPLError(CE_Failure, CPLE_IllegalArg,
1622
0
                         "Invalid srcwin option: %s, must be four "
1623
0
                         "values separated by comma xoff,yoff,xsize,ysize",
1624
0
                         pszValue);
1625
0
                return nullptr;
1626
0
            }
1627
1628
0
            argv.AddString("-srcwin");
1629
0
            argv.AddString(aosSrcWin[0]);
1630
0
            argv.AddString(aosSrcWin[1]);
1631
0
            argv.AddString(aosSrcWin[2]);
1632
0
            argv.AddString(aosSrcWin[3]);
1633
0
        }
1634
1635
0
        else if (EQUAL(pszKey, "a_gt"))
1636
0
        {
1637
            // Parse the limits
1638
0
            const CPLStringList aosAGeoTransform(
1639
0
                CSLTokenizeString2(pszValue, ",", 0));
1640
            // fail if not six values
1641
0
            if (aosAGeoTransform.size() != 6)
1642
0
            {
1643
0
                CPLError(CE_Failure, CPLE_IllegalArg, "Invalid a_gt option: %s",
1644
0
                         pszValue);
1645
0
                return nullptr;
1646
0
            }
1647
1648
0
            argv.AddString("-a_gt");
1649
0
            argv.AddString(aosAGeoTransform[0]);
1650
0
            argv.AddString(aosAGeoTransform[1]);
1651
0
            argv.AddString(aosAGeoTransform[2]);
1652
0
            argv.AddString(aosAGeoTransform[3]);
1653
0
            argv.AddString(aosAGeoTransform[4]);
1654
0
            argv.AddString(aosAGeoTransform[5]);
1655
0
        }
1656
0
        else if (EQUAL(pszKey, "oo"))
1657
0
        {
1658
            // do nothing, we passed this in earlier
1659
0
        }
1660
0
        else if (EQUAL(pszKey, "if"))
1661
0
        {
1662
            // do nothing, we passed this in earlier
1663
0
        }
1664
0
        else if (EQUAL(pszKey, "sd_name"))
1665
0
        {
1666
            // do nothing, we passed this in earlier
1667
0
        }
1668
0
        else if (EQUAL(pszKey, "sd"))
1669
0
        {
1670
            // do nothing, we passed this in earlier
1671
0
        }
1672
0
        else if (EQUAL(pszKey, "transpose"))
1673
0
        {
1674
            // do nothing, we passed this in earlier
1675
0
        }
1676
0
        else if (EQUAL(pszKey, "block"))
1677
0
        {
1678
            // do nothing, we passed this in earlier
1679
0
        }
1680
0
        else if (EQUAL(pszKey, "unscale"))
1681
0
        {
1682
0
            if (CPLTestBool(pszValue))
1683
0
            {
1684
0
                argv.AddString("-unscale");
1685
0
            }
1686
0
        }
1687
0
        else if (EQUAL(pszKey, "a_coord_epoch"))
1688
0
        {
1689
0
            argv.AddString("-a_coord_epoch");
1690
0
            argv.AddString(pszValue);
1691
0
        }
1692
0
        else if (EQUAL(pszKey, "nogcp"))
1693
0
        {
1694
0
            if (CPLTestBool(pszValue))
1695
0
            {
1696
0
                argv.AddString("-nogcp");
1697
0
            }
1698
0
        }
1699
0
        else if (EQUAL(pszKey, "epo"))
1700
0
        {
1701
0
            if (CPLTestBool(pszValue))
1702
0
            {
1703
0
                argv.AddString("-epo");
1704
0
            }
1705
0
        }
1706
0
        else if (EQUAL(pszKey, "eco"))
1707
0
        {
1708
0
            if (CPLTestBool(pszValue))
1709
0
            {
1710
0
                argv.AddString("-eco");
1711
0
            }
1712
0
        }
1713
1714
0
        else
1715
0
        {
1716
0
            CPLError(CE_Failure, CPLE_NotSupported, "Unknown option: %s",
1717
0
                     pszKey);
1718
0
            return nullptr;
1719
0
        }
1720
0
    }
1721
1722
0
    GDALTranslateOptions *psOptions =
1723
0
        GDALTranslateOptionsNew(argv.List(), nullptr);
1724
1725
0
    auto hRet = GDALTranslate("", GDALDataset::ToHandle(poSrcDS.get()),
1726
0
                              psOptions, nullptr);
1727
1728
0
    GDALTranslateOptionsFree(psOptions);
1729
1730
    // Situation where we open a http://.jp2 file with the JP2OpenJPEG driver
1731
    // through the HTTP driver, which returns a /vsimem/ file
1732
0
    const bool bPatchSourceFilename =
1733
0
        (STARTS_WITH(osFilename.c_str(), "http://") ||
1734
0
         STARTS_WITH(osFilename.c_str(), "https://")) &&
1735
0
        osFilename != poSrcDS->GetDescription();
1736
1737
0
    poSrcDS.reset();
1738
1739
0
    auto poDS = dynamic_cast<VRTDataset *>(GDALDataset::FromHandle(hRet));
1740
0
    if (poDS)
1741
0
    {
1742
0
        if (bPatchSourceFilename)
1743
0
        {
1744
0
            for (int i = 0; i < poDS->nBands; ++i)
1745
0
            {
1746
0
                auto poBand =
1747
0
                    dynamic_cast<VRTSourcedRasterBand *>(poDS->papoBands[i]);
1748
0
                if (poBand && poBand->m_papoSources.size() == 1 &&
1749
0
                    poBand->m_papoSources[0]->IsSimpleSource())
1750
0
                {
1751
0
                    auto poSource = cpl::down_cast<VRTSimpleSource *>(
1752
0
                        poBand->m_papoSources[0].get());
1753
0
                    poSource->m_bRelativeToVRTOri = 0;
1754
0
                    poSource->m_osSourceFileNameOri = osFilename;
1755
0
                }
1756
0
            }
1757
0
        }
1758
0
        poDS->SetDescription(pszSpec);
1759
0
        poDS->SetWritable(false);
1760
0
    }
1761
0
    return poDS;
1762
0
}
1763
1764
/************************************************************************/
1765
/*                              OpenXML()                               */
1766
/*                                                                      */
1767
/*      Create an open VRTDataset from a supplied XML representation    */
1768
/*      of the dataset.                                                 */
1769
/************************************************************************/
1770
1771
std::unique_ptr<VRTDataset> VRTDataset::OpenXML(const char *pszXML,
1772
                                                const char *pszVRTPath,
1773
                                                GDALAccess eAccessIn)
1774
1775
0
{
1776
    /* -------------------------------------------------------------------- */
1777
    /*      Parse the XML.                                                  */
1778
    /* -------------------------------------------------------------------- */
1779
0
    CPLXMLTreeCloser psTree(CPLParseXMLString(pszXML));
1780
0
    if (psTree == nullptr)
1781
0
        return nullptr;
1782
1783
0
    CPLXMLNode *psRoot = CPLGetXMLNode(psTree.get(), "=VRTDataset");
1784
0
    if (psRoot == nullptr)
1785
0
    {
1786
0
        CPLError(CE_Failure, CPLE_AppDefined, "Missing VRTDataset element.");
1787
0
        return nullptr;
1788
0
    }
1789
1790
0
    const char *pszSubClass = CPLGetXMLValue(psRoot, "subClass", "");
1791
1792
0
    const bool bIsPansharpened =
1793
0
        strcmp(pszSubClass, "VRTPansharpenedDataset") == 0;
1794
0
    const bool bIsProcessed = strcmp(pszSubClass, "VRTProcessedDataset") == 0;
1795
1796
0
    if (!bIsPansharpened && !bIsProcessed &&
1797
0
        CPLGetXMLNode(psRoot, "Group") == nullptr &&
1798
0
        (CPLGetXMLNode(psRoot, "rasterXSize") == nullptr ||
1799
0
         CPLGetXMLNode(psRoot, "rasterYSize") == nullptr ||
1800
0
         CPLGetXMLNode(psRoot, "VRTRasterBand") == nullptr))
1801
0
    {
1802
0
        CPLError(CE_Failure, CPLE_AppDefined,
1803
0
                 "Missing one of rasterXSize, rasterYSize or bands on"
1804
0
                 " VRTDataset.");
1805
0
        return nullptr;
1806
0
    }
1807
1808
    /* -------------------------------------------------------------------- */
1809
    /*      Create the new virtual dataset object.                          */
1810
    /* -------------------------------------------------------------------- */
1811
0
    const int nXSize = atoi(CPLGetXMLValue(psRoot, "rasterXSize", "0"));
1812
0
    const int nYSize = atoi(CPLGetXMLValue(psRoot, "rasterYSize", "0"));
1813
1814
0
    if (!bIsPansharpened && !bIsProcessed &&
1815
0
        CPLGetXMLNode(psRoot, "VRTRasterBand") != nullptr &&
1816
0
        !GDALCheckDatasetDimensions(nXSize, nYSize))
1817
0
    {
1818
0
        return nullptr;
1819
0
    }
1820
1821
0
    std::unique_ptr<VRTDataset> poDS;
1822
0
    if (strcmp(pszSubClass, "VRTWarpedDataset") == 0)
1823
0
        poDS = std::make_unique<VRTWarpedDataset>(nXSize, nYSize);
1824
0
    else if (bIsPansharpened)
1825
0
        poDS = std::make_unique<VRTPansharpenedDataset>(nXSize, nYSize);
1826
0
    else if (bIsProcessed)
1827
0
        poDS = std::make_unique<VRTProcessedDataset>(nXSize, nYSize);
1828
0
    else
1829
0
    {
1830
0
        poDS = std::make_unique<VRTDataset>(nXSize, nYSize);
1831
0
        poDS->eAccess = eAccessIn;
1832
0
    }
1833
1834
0
    if (poDS->XMLInit(psRoot, pszVRTPath) != CE_None)
1835
0
    {
1836
0
        poDS.reset();
1837
0
    }
1838
1839
    /* -------------------------------------------------------------------- */
1840
    /*      Try to return a regular handle on the file.                     */
1841
    /* -------------------------------------------------------------------- */
1842
1843
0
    return poDS;
1844
0
}
1845
1846
/************************************************************************/
1847
/*                              AddBand()                               */
1848
/************************************************************************/
1849
1850
CPLErr VRTDataset::AddBand(GDALDataType eType, CSLConstList papszOptions)
1851
1852
0
{
1853
0
    if (eType == GDT_Unknown || eType == GDT_TypeCount)
1854
0
    {
1855
0
        ReportError(CE_Failure, CPLE_IllegalArg,
1856
0
                    "Illegal GDT_Unknown/GDT_TypeCount argument");
1857
0
        return CE_Failure;
1858
0
    }
1859
1860
0
    SetNeedsFlush();
1861
1862
    /* ==================================================================== */
1863
    /*      Handle a new raw band.                                          */
1864
    /* ==================================================================== */
1865
0
    const char *pszSubClass = CSLFetchNameValue(papszOptions, "subclass");
1866
1867
0
    if (pszSubClass != nullptr && EQUAL(pszSubClass, "VRTRawRasterBand"))
1868
0
    {
1869
0
#ifdef GDAL_VRT_ENABLE_RAWRASTERBAND
1870
0
        if (!VRTDataset::IsRawRasterBandEnabled())
1871
0
        {
1872
0
            return CE_Failure;
1873
0
        }
1874
0
        const int nWordDataSize = GDALGetDataTypeSizeBytes(eType);
1875
1876
        /* ---------------------------------------------------------------- */
1877
        /*      Collect required information.                               */
1878
        /* ---------------------------------------------------------------- */
1879
0
        const char *pszImageOffset =
1880
0
            CSLFetchNameValueDef(papszOptions, "ImageOffset", "0");
1881
0
        vsi_l_offset nImageOffset = CPLScanUIntBig(
1882
0
            pszImageOffset, static_cast<int>(strlen(pszImageOffset)));
1883
1884
0
        int nPixelOffset = nWordDataSize;
1885
0
        const char *pszPixelOffset =
1886
0
            CSLFetchNameValue(papszOptions, "PixelOffset");
1887
0
        if (pszPixelOffset != nullptr)
1888
0
            nPixelOffset = atoi(pszPixelOffset);
1889
1890
0
        int nLineOffset;
1891
0
        const char *pszLineOffset =
1892
0
            CSLFetchNameValue(papszOptions, "LineOffset");
1893
0
        if (pszLineOffset != nullptr)
1894
0
            nLineOffset = atoi(pszLineOffset);
1895
0
        else
1896
0
        {
1897
0
            if (nPixelOffset > INT_MAX / GetRasterXSize() ||
1898
0
                nPixelOffset < INT_MIN / GetRasterXSize())
1899
0
            {
1900
0
                CPLError(CE_Failure, CPLE_AppDefined, "Int overflow");
1901
0
                return CE_Failure;
1902
0
            }
1903
0
            nLineOffset = nPixelOffset * GetRasterXSize();
1904
0
        }
1905
1906
0
        const char *pszByteOrder = CSLFetchNameValue(papszOptions, "ByteOrder");
1907
1908
0
        const char *pszFilename =
1909
0
            CSLFetchNameValue(papszOptions, "SourceFilename");
1910
0
        if (pszFilename == nullptr)
1911
0
        {
1912
0
            CPLError(CE_Failure, CPLE_AppDefined,
1913
0
                     "AddBand() requires a SourceFilename option for "
1914
0
                     "VRTRawRasterBands.");
1915
0
            return CE_Failure;
1916
0
        }
1917
1918
0
        const bool bRelativeToVRT =
1919
0
            CPLFetchBool(papszOptions, "relativeToVRT", false);
1920
1921
        /* --------------------------------------------------------------- */
1922
        /*      Create and initialize the band.                            */
1923
        /* --------------------------------------------------------------- */
1924
1925
0
        auto poBand = std::make_unique<VRTRawRasterBand>(
1926
0
            this, GetRasterCount() + 1, eType);
1927
1928
0
        const std::string osPath = CPLGetPathSafe(GetDescription());
1929
0
        const CPLErr eErr = poBand->SetRawLink(
1930
0
            pszFilename, osPath.empty() ? nullptr : osPath.c_str(),
1931
0
            bRelativeToVRT, nImageOffset, nPixelOffset, nLineOffset,
1932
0
            pszByteOrder);
1933
0
        if (eErr == CE_None)
1934
0
            SetBand(GetRasterCount() + 1, std::move(poBand));
1935
1936
0
        return eErr;
1937
#else
1938
        CPLError(CE_Failure, CPLE_NotSupported,
1939
                 "VRTDataset::AddBand(): cannot instantiate VRTRawRasterBand, "
1940
                 "because disabled in this GDAL build");
1941
        return CE_Failure;
1942
#endif
1943
0
    }
1944
1945
    /* ==================================================================== */
1946
    /*      Handle a new "sourced" band.                                    */
1947
    /* ==================================================================== */
1948
0
    else
1949
0
    {
1950
0
        VRTSourcedRasterBand *poBand = nullptr;
1951
1952
0
        int nBlockXSizeIn =
1953
0
            atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "0"));
1954
0
        int nBlockYSizeIn =
1955
0
            atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "0"));
1956
0
        if (nBlockXSizeIn == 0 && nBlockYSizeIn == 0)
1957
0
        {
1958
0
            nBlockXSizeIn = m_nBlockXSize;
1959
0
            nBlockYSizeIn = m_nBlockYSize;
1960
0
        }
1961
1962
        /* ---- Check for our sourced band 'derived' subclass ---- */
1963
0
        if (pszSubClass != nullptr &&
1964
0
            EQUAL(pszSubClass, "VRTDerivedRasterBand"))
1965
0
        {
1966
1967
            /* We'll need a pointer to the subclass in case we need */
1968
            /* to set the new band's pixel function below. */
1969
0
            VRTDerivedRasterBand *poDerivedBand = new VRTDerivedRasterBand(
1970
0
                this, GetRasterCount() + 1, eType, GetRasterXSize(),
1971
0
                GetRasterYSize(), nBlockXSizeIn, nBlockYSizeIn);
1972
1973
            /* Set the pixel function options it provided. */
1974
0
            const char *pszFuncName =
1975
0
                CSLFetchNameValue(papszOptions, "PixelFunctionType");
1976
0
            if (pszFuncName != nullptr)
1977
0
                poDerivedBand->SetPixelFunctionName(pszFuncName);
1978
1979
0
            const char *pszLanguage =
1980
0
                CSLFetchNameValue(papszOptions, "PixelFunctionLanguage");
1981
0
            if (pszLanguage != nullptr)
1982
0
                poDerivedBand->SetPixelFunctionLanguage(pszLanguage);
1983
1984
0
            const char *pszSkipNonContributingSources =
1985
0
                CSLFetchNameValue(papszOptions, "SkipNonContributingSources");
1986
0
            if (pszSkipNonContributingSources != nullptr)
1987
0
            {
1988
0
                poDerivedBand->SetSkipNonContributingSources(
1989
0
                    CPLTestBool(pszSkipNonContributingSources));
1990
0
            }
1991
0
            for (const auto &[pszKey, pszValue] :
1992
0
                 cpl::IterateNameValue(static_cast<CSLConstList>(papszOptions)))
1993
0
            {
1994
0
                if (STARTS_WITH(pszKey, "_PIXELFN_ARG_"))
1995
0
                {
1996
0
                    poDerivedBand->AddPixelFunctionArgument(pszKey + 13,
1997
0
                                                            pszValue);
1998
0
                }
1999
0
            }
2000
2001
0
            const char *pszTransferTypeName =
2002
0
                CSLFetchNameValue(papszOptions, "SourceTransferType");
2003
0
            if (pszTransferTypeName != nullptr)
2004
0
            {
2005
0
                const GDALDataType eTransferType =
2006
0
                    GDALGetDataTypeByName(pszTransferTypeName);
2007
0
                if (eTransferType == GDT_Unknown)
2008
0
                {
2009
0
                    CPLError(CE_Failure, CPLE_AppDefined,
2010
0
                             "invalid SourceTransferType: \"%s\".",
2011
0
                             pszTransferTypeName);
2012
0
                    delete poDerivedBand;
2013
0
                    return CE_Failure;
2014
0
                }
2015
0
                poDerivedBand->SetSourceTransferType(eTransferType);
2016
0
            }
2017
2018
            /* We're done with the derived band specific stuff, so */
2019
            /* we can assign the base class pointer now. */
2020
0
            poBand = poDerivedBand;
2021
0
        }
2022
0
        else
2023
0
        {
2024
            /* ---- Standard sourced band ---- */
2025
0
            poBand = new VRTSourcedRasterBand(
2026
0
                this, GetRasterCount() + 1, eType, GetRasterXSize(),
2027
0
                GetRasterYSize(), nBlockXSizeIn, nBlockYSizeIn);
2028
0
        }
2029
2030
0
        SetBand(GetRasterCount() + 1, poBand);
2031
2032
0
        for (int i = 0; papszOptions != nullptr && papszOptions[i] != nullptr;
2033
0
             i++)
2034
0
        {
2035
0
            if (STARTS_WITH_CI(papszOptions[i], "AddFuncSource="))
2036
0
            {
2037
0
                char **papszTokens = CSLTokenizeStringComplex(
2038
0
                    papszOptions[i] + 14, ",", TRUE, FALSE);
2039
0
                if (CSLCount(papszTokens) < 1)
2040
0
                {
2041
0
                    CPLError(CE_Failure, CPLE_AppDefined,
2042
0
                             "AddFuncSource(): required argument missing.");
2043
                    // TODO: How should this error be handled?  Return
2044
                    // CE_Failure?
2045
0
                }
2046
2047
0
                VRTImageReadFunc pfnReadFunc = nullptr;
2048
0
                sscanf(papszTokens[0], "%p", &pfnReadFunc);
2049
2050
0
                void *pCBData = nullptr;
2051
0
                if (CSLCount(papszTokens) > 1)
2052
0
                    sscanf(papszTokens[1], "%p", &pCBData);
2053
2054
0
                const double dfNoDataValue = (CSLCount(papszTokens) > 2)
2055
0
                                                 ? CPLAtof(papszTokens[2])
2056
0
                                                 : VRT_NODATA_UNSET;
2057
2058
0
                poBand->AddFuncSource(pfnReadFunc, pCBData, dfNoDataValue);
2059
2060
0
                CSLDestroy(papszTokens);
2061
0
            }
2062
0
        }
2063
2064
0
        return CE_None;
2065
0
    }
2066
0
}
2067
2068
/*! @endcond */
2069
/************************************************************************/
2070
/*                             VRTAddBand()                             */
2071
/************************************************************************/
2072
2073
/**
2074
 * @see VRTDataset::VRTAddBand().
2075
 *
2076
 * @note The return type of this function is int, but the actual values
2077
 * returned are of type CPLErr.
2078
 */
2079
2080
int CPL_STDCALL VRTAddBand(VRTDatasetH hDataset, GDALDataType eType,
2081
                           CSLConstList papszOptions)
2082
2083
0
{
2084
0
    VALIDATE_POINTER1(hDataset, "VRTAddBand", 0);
2085
2086
0
    return static_cast<VRTDataset *>(GDALDataset::FromHandle(hDataset))
2087
0
        ->AddBand(eType, papszOptions);
2088
0
}
2089
2090
/*! @cond Doxygen_Suppress */
2091
2092
/************************************************************************/
2093
/*                               Create()                               */
2094
/************************************************************************/
2095
2096
GDALDataset *VRTDataset::Create(const char *pszName, int nXSize, int nYSize,
2097
                                int nBandsIn, GDALDataType eType,
2098
                                CSLConstList papszOptions)
2099
2100
0
{
2101
0
    return CreateVRTDataset(pszName, nXSize, nYSize, nBandsIn, eType,
2102
0
                            const_cast<CSLConstList>(papszOptions))
2103
0
        .release();
2104
0
}
2105
2106
/************************************************************************/
2107
/*                          CreateVRTDataset()                          */
2108
/************************************************************************/
2109
2110
std::unique_ptr<VRTDataset>
2111
VRTDataset::CreateVRTDataset(const char *pszName, int nXSize, int nYSize,
2112
                             int nBandsIn, GDALDataType eType,
2113
                             CSLConstList papszOptions)
2114
2115
0
{
2116
0
    if (STARTS_WITH_CI(pszName, "<VRTDataset"))
2117
0
    {
2118
0
        auto poDS = OpenXML(pszName, nullptr, GA_Update);
2119
0
        if (poDS != nullptr)
2120
0
            poDS->SetDescription("<FromXML>");
2121
0
        return poDS;
2122
0
    }
2123
2124
0
    const char *pszSubclass = CSLFetchNameValue(papszOptions, "SUBCLASS");
2125
2126
0
    std::unique_ptr<VRTDataset> poDS;
2127
2128
0
    const int nBlockXSize =
2129
0
        atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "0"));
2130
0
    const int nBlockYSize =
2131
0
        atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "0"));
2132
0
    if (pszSubclass == nullptr || EQUAL(pszSubclass, "VRTDataset"))
2133
0
        poDS = std::make_unique<VRTDataset>(nXSize, nYSize, nBlockXSize,
2134
0
                                            nBlockYSize);
2135
0
    else if (EQUAL(pszSubclass, "VRTWarpedDataset"))
2136
0
    {
2137
0
        poDS = std::make_unique<VRTWarpedDataset>(nXSize, nYSize, nBlockXSize,
2138
0
                                                  nBlockYSize);
2139
0
    }
2140
0
    else
2141
0
    {
2142
0
        CPLError(CE_Failure, CPLE_AppDefined, "SUBCLASS=%s not recognised.",
2143
0
                 pszSubclass);
2144
0
        return nullptr;
2145
0
    }
2146
0
    poDS->eAccess = GA_Update;
2147
2148
0
    poDS->SetDescription(pszName);
2149
2150
0
    for (int iBand = 0; iBand < nBandsIn; iBand++)
2151
0
        poDS->AddBand(eType, nullptr);
2152
2153
0
    poDS->SetNeedsFlush();
2154
2155
0
    poDS->oOvManager.Initialize(poDS.get(), pszName);
2156
2157
0
    return poDS;
2158
0
}
2159
2160
/************************************************************************/
2161
/*                     CreateVRTMultiDimensional()                      */
2162
/************************************************************************/
2163
2164
std::unique_ptr<VRTDataset>
2165
VRTDataset::CreateVRTMultiDimensional(const char *pszFilename,
2166
                                      CSLConstList /*papszRootGroupOptions*/,
2167
                                      CSLConstList /*papszOptions*/)
2168
0
{
2169
0
    auto poDS = std::make_unique<VRTDataset>(0, 0);
2170
0
    poDS->eAccess = GA_Update;
2171
0
    poDS->SetDescription(pszFilename);
2172
0
    poDS->m_poRootGroup = VRTGroup::Create(std::string(), "/");
2173
0
    poDS->m_poRootGroup->SetIsRootGroup();
2174
0
    poDS->m_poRootGroup->SetFilename(pszFilename);
2175
0
    poDS->m_poRootGroup->SetDirty();
2176
2177
0
    return poDS;
2178
0
}
2179
2180
/************************************************************************/
2181
/*                       CreateMultiDimensional()                       */
2182
/************************************************************************/
2183
2184
GDALDataset *
2185
VRTDataset::CreateMultiDimensional(const char *pszFilename,
2186
                                   CSLConstList papszRootGroupOptions,
2187
                                   CSLConstList papszOptions)
2188
0
{
2189
0
    return CreateVRTMultiDimensional(pszFilename, papszRootGroupOptions,
2190
0
                                     papszOptions)
2191
0
        .release();
2192
0
}
2193
2194
/************************************************************************/
2195
/*                            GetFileList()                             */
2196
/************************************************************************/
2197
2198
char **VRTDataset::GetFileList()
2199
0
{
2200
0
    char **papszFileList = GDALDataset::GetFileList();
2201
2202
0
    int nSize = CSLCount(papszFileList);
2203
0
    int nMaxSize = nSize;
2204
2205
    // Do not need an element deallocator as each string points to an
2206
    // element of the papszFileList.
2207
0
    CPLHashSet *hSetFiles =
2208
0
        CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, nullptr);
2209
2210
0
    for (int iBand = 0; iBand < nBands; iBand++)
2211
0
    {
2212
0
        static_cast<VRTRasterBand *>(papoBands[iBand])
2213
0
            ->GetFileList(&papszFileList, &nSize, &nMaxSize, hSetFiles);
2214
0
    }
2215
2216
0
    CPLHashSetDestroy(hSetFiles);
2217
2218
0
    return papszFileList;
2219
0
}
2220
2221
/************************************************************************/
2222
/*                               Delete()                               */
2223
/************************************************************************/
2224
2225
/* We implement Delete() to avoid that the default implementation */
2226
/* in GDALDriver::Delete() destroys the source files listed by GetFileList(),*/
2227
/* which would be an undesired effect... */
2228
CPLErr VRTDataset::Delete(const char *pszFilename)
2229
0
{
2230
0
    GDALDriverH hDriver = GDALIdentifyDriver(pszFilename, nullptr);
2231
2232
0
    if (!hDriver || !EQUAL(GDALGetDriverShortName(hDriver), "VRT"))
2233
0
        return CE_Failure;
2234
2235
0
    if (strstr(pszFilename, "<VRTDataset") == nullptr &&
2236
0
        VSIUnlink(pszFilename) != 0)
2237
0
    {
2238
0
        CPLError(CE_Failure, CPLE_AppDefined, "Deleting %s failed:\n%s",
2239
0
                 pszFilename, VSIStrerror(errno));
2240
0
        return CE_Failure;
2241
0
    }
2242
2243
0
    return CE_None;
2244
0
}
2245
2246
/************************************************************************/
2247
/*                           CreateMaskBand()                           */
2248
/************************************************************************/
2249
2250
CPLErr VRTDataset::CreateMaskBand(int)
2251
0
{
2252
0
    if (m_poMaskBand != nullptr)
2253
0
    {
2254
0
        CPLError(CE_Failure, CPLE_AppDefined,
2255
0
                 "This VRT dataset has already a mask band");
2256
0
        return CE_Failure;
2257
0
    }
2258
2259
0
    SetMaskBand(std::make_unique<VRTSourcedRasterBand>(this, 0));
2260
2261
0
    return CE_None;
2262
0
}
2263
2264
/************************************************************************/
2265
/*                            SetMaskBand()                             */
2266
/************************************************************************/
2267
2268
void VRTDataset::SetMaskBand(std::unique_ptr<VRTRasterBand> poMaskBandIn)
2269
0
{
2270
0
    m_poMaskBand = std::move(poMaskBandIn);
2271
0
    m_poMaskBand->SetIsMaskBand();
2272
0
}
2273
2274
/************************************************************************/
2275
/*                       CloseDependentDatasets()                       */
2276
/************************************************************************/
2277
2278
int VRTDataset::CloseDependentDatasets()
2279
0
{
2280
    /* We need to call it before removing the sources, otherwise */
2281
    /* we would remove them from the serizalized VRT */
2282
0
    FlushCache(true);
2283
2284
0
    int bHasDroppedRef = GDALDataset::CloseDependentDatasets();
2285
2286
0
    for (int iBand = 0; iBand < nBands; iBand++)
2287
0
    {
2288
0
        bHasDroppedRef |= static_cast<VRTRasterBand *>(papoBands[iBand])
2289
0
                              ->CloseDependentDatasets();
2290
0
    }
2291
2292
0
    return bHasDroppedRef;
2293
0
}
2294
2295
/************************************************************************/
2296
/*                    CheckCompatibleForDatasetIO()                     */
2297
/************************************************************************/
2298
2299
/* We will return TRUE only if all the bands are VRTSourcedRasterBands */
2300
/* made of identical sources, that are strictly VRTSimpleSource, and that */
2301
/* the band number of each source is the band number of the */
2302
/* VRTSourcedRasterBand. */
2303
2304
bool VRTDataset::CheckCompatibleForDatasetIO() const
2305
0
{
2306
0
    size_t nSources = 0;
2307
0
    const std::unique_ptr<VRTSource> *papoSources = nullptr;
2308
0
    CPLString osResampling;
2309
2310
0
    if (m_nCompatibleForDatasetIO >= 0)
2311
0
    {
2312
0
        return CPL_TO_BOOL(m_nCompatibleForDatasetIO);
2313
0
    }
2314
2315
0
    m_nCompatibleForDatasetIO = false;
2316
2317
0
    GDALDataset *poFirstBandSourceDS = nullptr;
2318
0
    for (int iBand = 0; iBand < nBands; iBand++)
2319
0
    {
2320
0
        auto poVRTBand = static_cast<VRTRasterBand *>(papoBands[iBand]);
2321
0
        assert(poVRTBand);
2322
0
        if (!poVRTBand->IsSourcedRasterBand())
2323
0
            return false;
2324
2325
0
        const VRTSourcedRasterBand *poBand =
2326
0
            static_cast<const VRTSourcedRasterBand *>(poVRTBand);
2327
2328
        // Do not allow VRTDerivedRasterBand for example
2329
0
        if (typeid(*poBand) != typeid(VRTSourcedRasterBand))
2330
0
            return false;
2331
2332
0
        if (iBand == 0)
2333
0
        {
2334
0
            nSources = poBand->m_papoSources.size();
2335
0
            papoSources = poBand->m_papoSources.data();
2336
0
            for (auto &poSource : poBand->m_papoSources)
2337
0
            {
2338
0
                if (!poSource->IsSimpleSource())
2339
0
                    return false;
2340
2341
0
                const VRTSimpleSource *poSimpleSource =
2342
0
                    static_cast<const VRTSimpleSource *>(poSource.get());
2343
0
                if (poSimpleSource->GetType() !=
2344
0
                    VRTSimpleSource::GetTypeStatic())
2345
0
                    return false;
2346
2347
0
                if (poSimpleSource->m_nBand != iBand + 1 ||
2348
0
                    poSimpleSource->m_bGetMaskBand ||
2349
0
                    (nSources > 1 && poSimpleSource->m_osSrcDSName.empty()))
2350
0
                {
2351
0
                    return false;
2352
0
                }
2353
0
                if (nSources == 1 && poSimpleSource->m_osSrcDSName.empty())
2354
0
                {
2355
0
                    if (auto poSourceBand = poSimpleSource->GetRasterBand())
2356
0
                    {
2357
0
                        poFirstBandSourceDS = poSourceBand->GetDataset();
2358
0
                    }
2359
0
                    else
2360
0
                    {
2361
0
                        return false;
2362
0
                    }
2363
0
                }
2364
0
                osResampling = poSimpleSource->GetResampling();
2365
0
            }
2366
0
        }
2367
0
        else if (nSources != poBand->m_papoSources.size())
2368
0
        {
2369
0
            return false;
2370
0
        }
2371
0
        else
2372
0
        {
2373
0
            for (size_t iSource = 0; iSource < nSources; iSource++)
2374
0
            {
2375
0
                if (!poBand->m_papoSources[iSource]->IsSimpleSource())
2376
0
                    return false;
2377
0
                const VRTSimpleSource *poRefSource =
2378
0
                    static_cast<const VRTSimpleSource *>(
2379
0
                        papoSources[iSource].get());
2380
2381
0
                const VRTSimpleSource *poSource =
2382
0
                    static_cast<const VRTSimpleSource *>(
2383
0
                        poBand->m_papoSources[iSource].get());
2384
0
                if (poSource->GetType() != VRTSimpleSource::GetTypeStatic())
2385
0
                    return false;
2386
0
                if (poSource->m_nBand != iBand + 1 ||
2387
0
                    poSource->m_bGetMaskBand ||
2388
0
                    (nSources > 1 && poSource->m_osSrcDSName.empty()))
2389
0
                    return false;
2390
0
                if (!poSource->IsSameExceptBandNumber(poRefSource))
2391
0
                    return false;
2392
0
                if (osResampling.compare(poSource->GetResampling()) != 0)
2393
0
                    return false;
2394
0
                if (nSources == 1 && poSource->m_osSrcDSName.empty())
2395
0
                {
2396
0
                    auto poSourceBand = poSource->GetRasterBand();
2397
0
                    if (!poSourceBand ||
2398
0
                        poFirstBandSourceDS != poSourceBand->GetDataset())
2399
0
                    {
2400
0
                        return false;
2401
0
                    }
2402
0
                }
2403
0
            }
2404
0
        }
2405
0
    }
2406
2407
0
    m_nCompatibleForDatasetIO = nSources != 0;
2408
0
    return CPL_TO_BOOL(m_nCompatibleForDatasetIO);
2409
0
}
2410
2411
/************************************************************************/
2412
/*                         GetSingleSimpleSource()                      */
2413
/*                                                                      */
2414
/* Returns a non-NULL dataset if the VRT is made of a single source     */
2415
/* that is a simple source, in its full extent, and with all of its     */
2416
/* bands. Basically something produced by :                             */
2417
/*   gdal_translate src dst.vrt -of VRT (-a_srs / -a_ullr)              */
2418
/************************************************************************/
2419
2420
GDALDataset *VRTDataset::GetSingleSimpleSource()
2421
0
{
2422
0
    if (!CheckCompatibleForDatasetIO())
2423
0
        return nullptr;
2424
2425
0
    VRTSourcedRasterBand *poVRTBand =
2426
0
        static_cast<VRTSourcedRasterBand *>(papoBands[0]);
2427
0
    if (poVRTBand->m_papoSources.size() != 1)
2428
0
        return nullptr;
2429
2430
0
    VRTSimpleSource *poSource =
2431
0
        static_cast<VRTSimpleSource *>(poVRTBand->m_papoSources[0].get());
2432
2433
0
    GDALRasterBand *poBand = poSource->GetRasterBand();
2434
0
    if (poBand == nullptr || poSource->GetMaskBandMainBand() != nullptr)
2435
0
        return nullptr;
2436
2437
0
    GDALDataset *poSrcDS = poBand->GetDataset();
2438
0
    if (poSrcDS == nullptr)
2439
0
        return nullptr;
2440
2441
    /* Check that it uses the full source dataset */
2442
0
    double dfReqXOff = 0.0;
2443
0
    double dfReqYOff = 0.0;
2444
0
    double dfReqXSize = 0.0;
2445
0
    double dfReqYSize = 0.0;
2446
0
    int nReqXOff = 0;
2447
0
    int nReqYOff = 0;
2448
0
    int nReqXSize = 0;
2449
0
    int nReqYSize = 0;
2450
0
    int nOutXOff = 0;
2451
0
    int nOutYOff = 0;
2452
0
    int nOutXSize = 0;
2453
0
    int nOutYSize = 0;
2454
0
    bool bError = false;
2455
0
    if (!poSource->GetSrcDstWindow(
2456
0
            0, 0, poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(),
2457
0
            poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(),
2458
0
            GRIORA_NearestNeighbour, &dfReqXOff, &dfReqYOff, &dfReqXSize,
2459
0
            &dfReqYSize, &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
2460
0
            &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize, bError))
2461
0
        return nullptr;
2462
2463
0
    if (nReqXOff != 0 || nReqYOff != 0 ||
2464
0
        nReqXSize != poSrcDS->GetRasterXSize() ||
2465
0
        nReqYSize != poSrcDS->GetRasterYSize())
2466
0
        return nullptr;
2467
2468
0
    if (nOutXOff != 0 || nOutYOff != 0 ||
2469
0
        nOutXSize != poSrcDS->GetRasterXSize() ||
2470
0
        nOutYSize != poSrcDS->GetRasterYSize())
2471
0
        return nullptr;
2472
2473
0
    return poSrcDS;
2474
0
}
2475
2476
/************************************************************************/
2477
/*                             AdviseRead()                             */
2478
/************************************************************************/
2479
2480
CPLErr VRTDataset::AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
2481
                              int nBufXSize, int nBufYSize, GDALDataType eDT,
2482
                              int nBandCount, int *panBandList,
2483
                              CSLConstList papszOptions)
2484
0
{
2485
0
    if (!CheckCompatibleForDatasetIO())
2486
0
        return CE_None;
2487
2488
0
    VRTSourcedRasterBand *poVRTBand =
2489
0
        static_cast<VRTSourcedRasterBand *>(papoBands[0]);
2490
0
    if (poVRTBand->m_papoSources.size() != 1)
2491
0
        return CE_None;
2492
2493
0
    VRTSimpleSource *poSource =
2494
0
        static_cast<VRTSimpleSource *>(poVRTBand->m_papoSources[0].get());
2495
2496
    /* Find source window and buffer size */
2497
0
    double dfReqXOff = 0.0;
2498
0
    double dfReqYOff = 0.0;
2499
0
    double dfReqXSize = 0.0;
2500
0
    double dfReqYSize = 0.0;
2501
0
    int nReqXOff = 0;
2502
0
    int nReqYOff = 0;
2503
0
    int nReqXSize = 0;
2504
0
    int nReqYSize = 0;
2505
0
    int nOutXOff = 0;
2506
0
    int nOutYOff = 0;
2507
0
    int nOutXSize = 0;
2508
0
    int nOutYSize = 0;
2509
0
    bool bError = false;
2510
0
    if (!poSource->GetSrcDstWindow(
2511
0
            nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
2512
0
            GRIORA_NearestNeighbour, &dfReqXOff, &dfReqYOff, &dfReqXSize,
2513
0
            &dfReqYSize, &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
2514
0
            &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize, bError))
2515
0
    {
2516
0
        return bError ? CE_Failure : CE_None;
2517
0
    }
2518
2519
0
    GDALRasterBand *poBand = poSource->GetRasterBand();
2520
0
    if (poBand == nullptr || poSource->GetMaskBandMainBand() != nullptr)
2521
0
        return CE_None;
2522
2523
0
    GDALDataset *poSrcDS = poBand->GetDataset();
2524
0
    if (poSrcDS == nullptr)
2525
0
        return CE_None;
2526
2527
0
    return poSrcDS->AdviseRead(nReqXOff, nReqYOff, nReqXSize, nReqYSize,
2528
0
                               nOutXSize, nOutYSize, eDT, nBandCount,
2529
0
                               panBandList, papszOptions);
2530
0
}
2531
2532
/************************************************************************/
2533
/*                           GetNumThreads()                            */
2534
/************************************************************************/
2535
2536
/* static */ int VRTDataset::GetNumThreads(GDALDataset *poDS)
2537
0
{
2538
0
    const char *pszNumThreads = nullptr;
2539
0
    if (poDS)
2540
0
        pszNumThreads = CSLFetchNameValueDef(poDS->GetOpenOptions(),
2541
0
                                             "NUM_THREADS", nullptr);
2542
0
    if (!pszNumThreads)
2543
0
        pszNumThreads = CPLGetConfigOption("VRT_NUM_THREADS", nullptr);
2544
0
    return GDALGetNumThreads(pszNumThreads, GDALGetMaxDatasetPoolSize(),
2545
0
                             /* bDefaultAllCPUs = */ true);
2546
0
}
2547
2548
/************************************************************************/
2549
/*                        VRTDatasetRasterIOJob                         */
2550
/************************************************************************/
2551
2552
/** Structure used to declare a threaded job to satisfy IRasterIO()
2553
 * on a given source.
2554
 */
2555
struct VRTDatasetRasterIOJob
2556
{
2557
    std::atomic<int> *pnCompletedJobs = nullptr;
2558
    std::atomic<bool> *pbSuccess = nullptr;
2559
    CPLErrorAccumulator *poErrorAccumulator = nullptr;
2560
2561
    GDALDataType eVRTBandDataType = GDT_Unknown;
2562
    int nXOff = 0;
2563
    int nYOff = 0;
2564
    int nXSize = 0;
2565
    int nYSize = 0;
2566
    void *pData = nullptr;
2567
    int nBufXSize = 0;
2568
    int nBufYSize = 0;
2569
    int nBandCount = 0;
2570
    BANDMAP_TYPE panBandMap = nullptr;
2571
    GDALDataType eBufType = GDT_Unknown;
2572
    GSpacing nPixelSpace = 0;
2573
    GSpacing nLineSpace = 0;
2574
    GSpacing nBandSpace = 0;
2575
    GDALRasterIOExtraArg *psExtraArg = nullptr;
2576
    VRTSimpleSource *poSource = nullptr;
2577
2578
    static void Func(void *pData);
2579
};
2580
2581
/************************************************************************/
2582
/*                    VRTDatasetRasterIOJob::Func()                     */
2583
/************************************************************************/
2584
2585
void VRTDatasetRasterIOJob::Func(void *pData)
2586
0
{
2587
0
    auto psJob = std::unique_ptr<VRTDatasetRasterIOJob>(
2588
0
        static_cast<VRTDatasetRasterIOJob *>(pData));
2589
0
    if (*psJob->pbSuccess)
2590
0
    {
2591
0
        GDALRasterIOExtraArg sArg = *(psJob->psExtraArg);
2592
0
        sArg.pfnProgress = nullptr;
2593
0
        sArg.pProgressData = nullptr;
2594
2595
0
        auto oAccumulator = psJob->poErrorAccumulator->InstallForCurrentScope();
2596
0
        CPL_IGNORE_RET_VAL(oAccumulator);
2597
2598
0
        if (psJob->poSource->DatasetRasterIO(
2599
0
                psJob->eVRTBandDataType, psJob->nXOff, psJob->nYOff,
2600
0
                psJob->nXSize, psJob->nYSize, psJob->pData, psJob->nBufXSize,
2601
0
                psJob->nBufYSize, psJob->eBufType, psJob->nBandCount,
2602
0
                psJob->panBandMap, psJob->nPixelSpace, psJob->nLineSpace,
2603
0
                psJob->nBandSpace, &sArg) != CE_None)
2604
0
        {
2605
0
            *psJob->pbSuccess = false;
2606
0
        }
2607
0
    }
2608
2609
0
    ++(*psJob->pnCompletedJobs);
2610
0
}
2611
2612
/************************************************************************/
2613
/*                             IRasterIO()                              */
2614
/************************************************************************/
2615
2616
CPLErr VRTDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
2617
                             int nXSize, int nYSize, void *pData, int nBufXSize,
2618
                             int nBufYSize, GDALDataType eBufType,
2619
                             int nBandCount, BANDMAP_TYPE panBandMap,
2620
                             GSpacing nPixelSpace, GSpacing nLineSpace,
2621
                             GSpacing nBandSpace,
2622
                             GDALRasterIOExtraArg *psExtraArg)
2623
0
{
2624
0
    m_bMultiThreadedRasterIOLastUsed = false;
2625
2626
0
    if (nBands == 1 && nBandCount == 1)
2627
0
    {
2628
0
        VRTSourcedRasterBand *poBand =
2629
0
            dynamic_cast<VRTSourcedRasterBand *>(papoBands[0]);
2630
0
        if (poBand)
2631
0
        {
2632
0
            return poBand->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2633
0
                                     pData, nBufXSize, nBufYSize, eBufType,
2634
0
                                     nPixelSpace, nLineSpace, psExtraArg);
2635
0
        }
2636
0
    }
2637
2638
0
    bool bLocalCompatibleForDatasetIO =
2639
0
        CPL_TO_BOOL(CheckCompatibleForDatasetIO());
2640
0
    if (bLocalCompatibleForDatasetIO && eRWFlag == GF_Read &&
2641
0
        (nBufXSize < nXSize || nBufYSize < nYSize) && m_apoOverviews.empty())
2642
0
    {
2643
0
        int bTried = FALSE;
2644
0
        const CPLErr eErr = TryOverviewRasterIO(
2645
0
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2646
0
            eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
2647
0
            nBandSpace, psExtraArg, &bTried);
2648
2649
0
        if (bTried)
2650
0
        {
2651
0
            return eErr;
2652
0
        }
2653
2654
0
        for (int iBand = 0; iBand < nBands; iBand++)
2655
0
        {
2656
0
            VRTSourcedRasterBand *poBand =
2657
0
                static_cast<VRTSourcedRasterBand *>(papoBands[iBand]);
2658
2659
            // If there are overviews, let VRTSourcedRasterBand::IRasterIO()
2660
            // do the job.
2661
0
            if (poBand->GetOverviewCount() != 0)
2662
0
            {
2663
0
                bLocalCompatibleForDatasetIO = false;
2664
0
                break;
2665
0
            }
2666
0
        }
2667
0
    }
2668
2669
    // If resampling with non-nearest neighbour, we need to be careful
2670
    // if the VRT band exposes a nodata value, but the sources do not have it.
2671
    // To also avoid edge effects on sources when downsampling, use the
2672
    // base implementation of IRasterIO() (that is acquiring sources at their
2673
    // nominal resolution, and then downsampling), but only if none of the
2674
    // contributing sources have overviews.
2675
0
    if (bLocalCompatibleForDatasetIO && eRWFlag == GF_Read &&
2676
0
        (nXSize != nBufXSize || nYSize != nBufYSize) &&
2677
0
        psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
2678
0
    {
2679
0
        for (int iBandIndex = 0; iBandIndex < nBandCount; iBandIndex++)
2680
0
        {
2681
0
            VRTSourcedRasterBand *poBand = static_cast<VRTSourcedRasterBand *>(
2682
0
                GetRasterBand(panBandMap[iBandIndex]));
2683
0
            if (!poBand->CanIRasterIOBeForwardedToEachSource(
2684
0
                    eRWFlag, nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
2685
0
                    psExtraArg))
2686
0
            {
2687
0
                bLocalCompatibleForDatasetIO = false;
2688
0
                break;
2689
0
            }
2690
0
        }
2691
0
    }
2692
2693
0
    if (bLocalCompatibleForDatasetIO && eRWFlag == GF_Read)
2694
0
    {
2695
0
        for (int iBandIndex = 0; iBandIndex < nBandCount; iBandIndex++)
2696
0
        {
2697
0
            VRTSourcedRasterBand *poBand = static_cast<VRTSourcedRasterBand *>(
2698
0
                GetRasterBand(panBandMap[iBandIndex]));
2699
2700
            /* Dirty little trick to initialize the buffer without doing */
2701
            /* any real I/O */
2702
0
            std::vector<std::unique_ptr<VRTSource>> papoSavedSources;
2703
0
            std::swap(papoSavedSources, poBand->m_papoSources);
2704
2705
0
            GDALProgressFunc pfnProgressGlobal = psExtraArg->pfnProgress;
2706
0
            psExtraArg->pfnProgress = nullptr;
2707
2708
0
            GByte *pabyBandData =
2709
0
                static_cast<GByte *>(pData) + iBandIndex * nBandSpace;
2710
2711
0
            poBand->IRasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize,
2712
0
                              pabyBandData, nBufXSize, nBufYSize, eBufType,
2713
0
                              nPixelSpace, nLineSpace, psExtraArg);
2714
2715
0
            psExtraArg->pfnProgress = pfnProgressGlobal;
2716
2717
0
            std::swap(papoSavedSources, poBand->m_papoSources);
2718
0
        }
2719
2720
0
        CPLErr eErr = CE_None;
2721
2722
        // Use the last band, because when sources reference a GDALProxyDataset,
2723
        // they don't necessary instantiate all underlying rasterbands.
2724
0
        VRTSourcedRasterBand *poBand =
2725
0
            static_cast<VRTSourcedRasterBand *>(papoBands[nBands - 1]);
2726
2727
0
        double dfXOff = nXOff;
2728
0
        double dfYOff = nYOff;
2729
0
        double dfXSize = nXSize;
2730
0
        double dfYSize = nYSize;
2731
0
        if (psExtraArg->bFloatingPointWindowValidity)
2732
0
        {
2733
0
            dfXOff = psExtraArg->dfXOff;
2734
0
            dfYOff = psExtraArg->dfYOff;
2735
0
            dfXSize = psExtraArg->dfXSize;
2736
0
            dfYSize = psExtraArg->dfYSize;
2737
0
        }
2738
2739
0
        int nContributingSources = 0;
2740
0
        int nMaxThreads = 0;
2741
0
        constexpr int MINIMUM_PIXEL_COUNT_FOR_THREADED_IO = 1000 * 1000;
2742
0
        if ((static_cast<int64_t>(nBufXSize) * nBufYSize >=
2743
0
                 MINIMUM_PIXEL_COUNT_FOR_THREADED_IO ||
2744
0
             static_cast<int64_t>(nXSize) * nYSize >=
2745
0
                 MINIMUM_PIXEL_COUNT_FOR_THREADED_IO) &&
2746
0
            poBand->CanMultiThreadRasterIO(dfXOff, dfYOff, dfXSize, dfYSize,
2747
0
                                           nContributingSources) &&
2748
0
            nContributingSources > 1 &&
2749
0
            (nMaxThreads = VRTDataset::GetNumThreads(this)) > 1)
2750
0
        {
2751
0
            m_bMultiThreadedRasterIOLastUsed = true;
2752
0
            m_oMapSharedSources.InitMutex();
2753
2754
0
            CPLErrorAccumulator errorAccumulator;
2755
0
            std::atomic<bool> bSuccess = true;
2756
0
            CPLWorkerThreadPool *psThreadPool = GDALGetGlobalThreadPool(
2757
0
                std::min(nContributingSources, nMaxThreads));
2758
2759
0
            CPLDebugOnly(
2760
0
                "VRT",
2761
0
                "IRasterIO(): use optimized "
2762
0
                "multi-threaded code path for mosaic. "
2763
0
                "Using %d threads",
2764
0
                std::min(nContributingSources, psThreadPool->GetThreadCount()));
2765
2766
0
            auto oQueue = psThreadPool->CreateJobQueue();
2767
0
            std::atomic<int> nCompletedJobs = 0;
2768
0
            for (auto &poSource : poBand->m_papoSources)
2769
0
            {
2770
0
                if (!poSource->IsSimpleSource())
2771
0
                    continue;
2772
0
                auto poSimpleSource =
2773
0
                    cpl::down_cast<VRTSimpleSource *>(poSource.get());
2774
0
                if (poSimpleSource->DstWindowIntersects(dfXOff, dfYOff, dfXSize,
2775
0
                                                        dfYSize))
2776
0
                {
2777
0
                    auto psJob = new VRTDatasetRasterIOJob();
2778
0
                    psJob->pbSuccess = &bSuccess;
2779
0
                    psJob->poErrorAccumulator = &errorAccumulator;
2780
0
                    psJob->pnCompletedJobs = &nCompletedJobs;
2781
0
                    psJob->eVRTBandDataType = poBand->GetRasterDataType();
2782
0
                    psJob->nXOff = nXOff;
2783
0
                    psJob->nYOff = nYOff;
2784
0
                    psJob->nXSize = nXSize;
2785
0
                    psJob->nYSize = nYSize;
2786
0
                    psJob->pData = pData;
2787
0
                    psJob->nBufXSize = nBufXSize;
2788
0
                    psJob->nBufYSize = nBufYSize;
2789
0
                    psJob->eBufType = eBufType;
2790
0
                    psJob->nBandCount = nBandCount;
2791
0
                    psJob->panBandMap = panBandMap;
2792
0
                    psJob->nPixelSpace = nPixelSpace;
2793
0
                    psJob->nLineSpace = nLineSpace;
2794
0
                    psJob->nBandSpace = nBandSpace;
2795
0
                    psJob->psExtraArg = psExtraArg;
2796
0
                    psJob->poSource = poSimpleSource;
2797
2798
0
                    if (!oQueue->SubmitJob(VRTDatasetRasterIOJob::Func, psJob))
2799
0
                    {
2800
0
                        delete psJob;
2801
0
                        bSuccess = false;
2802
0
                        break;
2803
0
                    }
2804
0
                }
2805
0
            }
2806
2807
0
            while (oQueue->WaitEvent())
2808
0
            {
2809
                // Quite rough progress callback. We could do better by counting
2810
                // the number of contributing pixels.
2811
0
                if (psExtraArg->pfnProgress)
2812
0
                {
2813
0
                    psExtraArg->pfnProgress(double(nCompletedJobs.load()) /
2814
0
                                                nContributingSources,
2815
0
                                            "", psExtraArg->pProgressData);
2816
0
                }
2817
0
            }
2818
2819
0
            errorAccumulator.ReplayErrors();
2820
0
            eErr = bSuccess ? CE_None : CE_Failure;
2821
0
        }
2822
0
        else
2823
0
        {
2824
0
            GDALProgressFunc pfnProgressGlobal = psExtraArg->pfnProgress;
2825
0
            void *pProgressDataGlobal = psExtraArg->pProgressData;
2826
2827
0
            const int nSources = static_cast<int>(poBand->m_papoSources.size());
2828
0
            for (int iSource = 0; eErr == CE_None && iSource < nSources;
2829
0
                 iSource++)
2830
0
            {
2831
0
                psExtraArg->pfnProgress = GDALScaledProgress;
2832
0
                psExtraArg->pProgressData = GDALCreateScaledProgress(
2833
0
                    1.0 * iSource / nSources, 1.0 * (iSource + 1) / nSources,
2834
0
                    pfnProgressGlobal, pProgressDataGlobal);
2835
2836
0
                VRTSimpleSource *poSource = static_cast<VRTSimpleSource *>(
2837
0
                    poBand->m_papoSources[iSource].get());
2838
2839
0
                eErr = poSource->DatasetRasterIO(
2840
0
                    poBand->GetRasterDataType(), nXOff, nYOff, nXSize, nYSize,
2841
0
                    pData, nBufXSize, nBufYSize, eBufType, nBandCount,
2842
0
                    panBandMap, nPixelSpace, nLineSpace, nBandSpace,
2843
0
                    psExtraArg);
2844
2845
0
                GDALDestroyScaledProgress(psExtraArg->pProgressData);
2846
0
            }
2847
2848
0
            psExtraArg->pfnProgress = pfnProgressGlobal;
2849
0
            psExtraArg->pProgressData = pProgressDataGlobal;
2850
0
        }
2851
2852
0
        if (eErr == CE_None && psExtraArg->pfnProgress)
2853
0
        {
2854
0
            psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
2855
0
        }
2856
2857
0
        return eErr;
2858
0
    }
2859
2860
0
    CPLErr eErr;
2861
0
    if (eRWFlag == GF_Read &&
2862
0
        psExtraArg->eResampleAlg != GRIORA_NearestNeighbour &&
2863
0
        nBufXSize < nXSize && nBufYSize < nYSize && nBandCount > 1)
2864
0
    {
2865
        // Force going through VRTSourcedRasterBand::IRasterIO(), otherwise
2866
        // GDALDataset::IRasterIOResampled() would be used without source
2867
        // overviews being potentially used.
2868
0
        eErr = GDALDataset::BandBasedRasterIO(
2869
0
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2870
0
            eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
2871
0
            nBandSpace, psExtraArg);
2872
0
    }
2873
0
    else
2874
0
    {
2875
0
        eErr = GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2876
0
                                      pData, nBufXSize, nBufYSize, eBufType,
2877
0
                                      nBandCount, panBandMap, nPixelSpace,
2878
0
                                      nLineSpace, nBandSpace, psExtraArg);
2879
0
    }
2880
0
    return eErr;
2881
0
}
2882
2883
/************************************************************************/
2884
/*                  UnsetPreservedRelativeFilenames()                   */
2885
/************************************************************************/
2886
2887
void VRTDataset::UnsetPreservedRelativeFilenames()
2888
0
{
2889
0
    for (int iBand = 0; iBand < nBands; iBand++)
2890
0
    {
2891
0
        if (!static_cast<VRTRasterBand *>(papoBands[iBand])
2892
0
                 ->IsSourcedRasterBand())
2893
0
            continue;
2894
2895
0
        VRTSourcedRasterBand *poBand =
2896
0
            static_cast<VRTSourcedRasterBand *>(papoBands[iBand]);
2897
0
        for (auto &poSource : poBand->m_papoSources)
2898
0
        {
2899
0
            if (!poSource->IsSimpleSource())
2900
0
                continue;
2901
2902
0
            VRTSimpleSource *poSimpleSource =
2903
0
                static_cast<VRTSimpleSource *>(poSource.get());
2904
0
            poSimpleSource->UnsetPreservedRelativeFilenames();
2905
0
        }
2906
0
    }
2907
0
}
2908
2909
/************************************************************************/
2910
/*                       BuildVirtualOverviews()                        */
2911
/************************************************************************/
2912
2913
static bool CheckBandForOverview(GDALRasterBand *poBand,
2914
                                 GDALRasterBand *&poFirstBand, int &nOverviews,
2915
                                 std::set<std::pair<int, int>> &oSetOvrSizes,
2916
                                 std::vector<GDALDataset *> &apoOverviewsBak)
2917
0
{
2918
0
    if (!cpl::down_cast<VRTRasterBand *>(poBand)->IsSourcedRasterBand())
2919
0
        return false;
2920
2921
0
    VRTSourcedRasterBand *poVRTBand =
2922
0
        cpl::down_cast<VRTSourcedRasterBand *>(poBand);
2923
0
    if (poVRTBand->m_papoSources.size() != 1)
2924
0
        return false;
2925
0
    if (!poVRTBand->m_papoSources[0]->IsSimpleSource())
2926
0
        return false;
2927
2928
0
    VRTSimpleSource *poSource =
2929
0
        cpl::down_cast<VRTSimpleSource *>(poVRTBand->m_papoSources[0].get());
2930
0
    const char *pszType = poSource->GetType();
2931
0
    if (pszType != VRTSimpleSource::GetTypeStatic() &&
2932
0
        pszType != VRTComplexSource::GetTypeStatic())
2933
0
    {
2934
0
        return false;
2935
0
    }
2936
0
    GDALRasterBand *poSrcBand = poSource->GetMaskBandMainBand();
2937
0
    if (!poSrcBand)
2938
0
        poSrcBand = poSource->GetRasterBand();
2939
0
    if (poSrcBand == nullptr)
2940
0
        return false;
2941
2942
    // To prevent recursion
2943
0
    apoOverviewsBak.push_back(nullptr);
2944
0
    const int nOvrCount = poSrcBand->GetOverviewCount();
2945
0
    oSetOvrSizes.insert(
2946
0
        std::pair<int, int>(poSrcBand->GetXSize(), poSrcBand->GetYSize()));
2947
0
    for (int i = 0; i < nOvrCount; ++i)
2948
0
    {
2949
0
        auto poSrcOvrBand = poSrcBand->GetOverview(i);
2950
0
        if (poSrcOvrBand)
2951
0
        {
2952
0
            oSetOvrSizes.insert(std::pair<int, int>(poSrcOvrBand->GetXSize(),
2953
0
                                                    poSrcOvrBand->GetYSize()));
2954
0
        }
2955
0
    }
2956
0
    apoOverviewsBak.resize(0);
2957
2958
0
    if (nOvrCount == 0)
2959
0
        return false;
2960
0
    if (poFirstBand == nullptr)
2961
0
    {
2962
0
        if (poSrcBand->GetXSize() == 0 || poSrcBand->GetYSize() == 0)
2963
0
            return false;
2964
0
        poFirstBand = poSrcBand;
2965
0
        nOverviews = nOvrCount;
2966
0
    }
2967
0
    else if (nOvrCount < nOverviews)
2968
0
        nOverviews = nOvrCount;
2969
0
    return true;
2970
0
}
2971
2972
void VRTDataset::BuildVirtualOverviews()
2973
0
{
2974
    // Currently we expose virtual overviews only if the dataset is made of
2975
    // a single SimpleSource/ComplexSource, in each band.
2976
    // And if the underlying sources have overviews of course
2977
0
    if (!m_apoOverviews.empty() || !m_apoOverviewsBak.empty())
2978
0
        return;
2979
2980
0
    int nOverviews = 0;
2981
0
    GDALRasterBand *poFirstBand = nullptr;
2982
0
    std::set<std::pair<int, int>> oSetOvrSizes;
2983
2984
0
    for (int iBand = 0; iBand < nBands; iBand++)
2985
0
    {
2986
0
        if (!CheckBandForOverview(papoBands[iBand], poFirstBand, nOverviews,
2987
0
                                  oSetOvrSizes, m_apoOverviewsBak))
2988
0
            return;
2989
0
    }
2990
2991
0
    if (m_poMaskBand)
2992
0
    {
2993
0
        if (!CheckBandForOverview(m_poMaskBand.get(), poFirstBand, nOverviews,
2994
0
                                  oSetOvrSizes, m_apoOverviewsBak))
2995
0
            return;
2996
0
    }
2997
0
    if (poFirstBand == nullptr)
2998
0
    {
2999
        // to make cppcheck happy
3000
0
        CPLAssert(false);
3001
0
        return;
3002
0
    }
3003
3004
0
    VRTSourcedRasterBand *l_poVRTBand =
3005
0
        cpl::down_cast<VRTSourcedRasterBand *>(papoBands[0]);
3006
0
    VRTSimpleSource *poSource =
3007
0
        cpl::down_cast<VRTSimpleSource *>(l_poVRTBand->m_papoSources[0].get());
3008
0
    const double dfDstToSrcXRatio =
3009
0
        poSource->m_dfDstXSize / poSource->m_dfSrcXSize;
3010
0
    const double dfDstToSrcYRatio =
3011
0
        poSource->m_dfDstYSize / poSource->m_dfSrcYSize;
3012
3013
0
    for (int j = 0; j < nOverviews; j++)
3014
0
    {
3015
0
        auto poOvrBand = poFirstBand->GetOverview(j);
3016
0
        if (!poOvrBand)
3017
0
            return;
3018
0
        const double dfXRatio = static_cast<double>(poOvrBand->GetXSize()) /
3019
0
                                poFirstBand->GetXSize();
3020
0
        const double dfYRatio = static_cast<double>(poOvrBand->GetYSize()) /
3021
0
                                poFirstBand->GetYSize();
3022
0
        if (dfXRatio >= dfDstToSrcXRatio || dfYRatio >= dfDstToSrcYRatio)
3023
0
        {
3024
0
            continue;
3025
0
        }
3026
0
        int nOvrXSize = static_cast<int>(0.5 + nRasterXSize * dfXRatio);
3027
0
        int nOvrYSize = static_cast<int>(0.5 + nRasterYSize * dfYRatio);
3028
3029
        // Look for a source overview whose size is very close to the
3030
        // theoretical computed one.
3031
0
        bool bSrcOvrMatchFound = false;
3032
0
        for (const auto &ovrSize : oSetOvrSizes)
3033
0
        {
3034
0
            if (std::abs(ovrSize.first - nOvrXSize) <= 1 &&
3035
0
                std::abs(ovrSize.second - nOvrYSize) <= 1)
3036
0
            {
3037
0
                bSrcOvrMatchFound = true;
3038
0
                nOvrXSize = ovrSize.first;
3039
0
                nOvrYSize = ovrSize.second;
3040
0
                break;
3041
0
            }
3042
0
        }
3043
3044
0
        if (!bSrcOvrMatchFound &&
3045
0
            (nOvrXSize < DEFAULT_BLOCK_SIZE || nOvrYSize < DEFAULT_BLOCK_SIZE))
3046
0
        {
3047
0
            break;
3048
0
        }
3049
3050
0
        int nBlockXSize = 0;
3051
0
        int nBlockYSize = 0;
3052
0
        l_poVRTBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
3053
0
        if (VRTDataset::IsDefaultBlockSize(nBlockXSize, nRasterXSize))
3054
0
            nBlockXSize = 0;
3055
0
        if (VRTDataset::IsDefaultBlockSize(nBlockYSize, nRasterYSize))
3056
0
            nBlockYSize = 0;
3057
3058
0
        VRTDataset *poOvrVDS =
3059
0
            new VRTDataset(nOvrXSize, nOvrYSize, nBlockXSize, nBlockYSize);
3060
0
        m_apoOverviews.push_back(poOvrVDS);
3061
3062
0
        const auto CreateOverviewBand =
3063
0
            [&poOvrVDS, nOvrXSize, nOvrYSize, dfXRatio,
3064
0
             dfYRatio](VRTSourcedRasterBand *poVRTBand)
3065
0
        {
3066
0
            auto poOvrVRTBand = std::make_unique<VRTSourcedRasterBand>(
3067
0
                poOvrVDS, poVRTBand->GetBand(), poVRTBand->GetRasterDataType(),
3068
0
                nOvrXSize, nOvrYSize);
3069
0
            poOvrVRTBand->CopyCommonInfoFrom(poVRTBand);
3070
0
            poOvrVRTBand->m_bNoDataValueSet = poVRTBand->m_bNoDataValueSet;
3071
0
            poOvrVRTBand->m_dfNoDataValue = poVRTBand->m_dfNoDataValue;
3072
0
            poOvrVRTBand->m_bHideNoDataValue = poVRTBand->m_bHideNoDataValue;
3073
3074
0
            VRTSimpleSource *poSrcSource = cpl::down_cast<VRTSimpleSource *>(
3075
0
                poVRTBand->m_papoSources[0].get());
3076
0
            std::unique_ptr<VRTSimpleSource> poNewSource;
3077
0
            const char *pszType = poSrcSource->GetType();
3078
0
            if (pszType == VRTSimpleSource::GetTypeStatic())
3079
0
            {
3080
0
                poNewSource = std::make_unique<VRTSimpleSource>(
3081
0
                    poSrcSource, dfXRatio, dfYRatio);
3082
0
            }
3083
0
            else if (pszType == VRTComplexSource::GetTypeStatic())
3084
0
            {
3085
0
                poNewSource = std::make_unique<VRTComplexSource>(
3086
0
                    cpl::down_cast<VRTComplexSource *>(poSrcSource), dfXRatio,
3087
0
                    dfYRatio);
3088
0
            }
3089
0
            else
3090
0
            {
3091
0
                CPLAssert(false);
3092
0
            }
3093
0
            if (poNewSource)
3094
0
            {
3095
0
                poOvrVRTBand->AddSource(std::move(poNewSource));
3096
0
            }
3097
3098
0
            return poOvrVRTBand;
3099
0
        };
3100
3101
0
        for (int i = 0; i < nBands; i++)
3102
0
        {
3103
0
            VRTSourcedRasterBand *poSrcBand =
3104
0
                cpl::down_cast<VRTSourcedRasterBand *>(GetRasterBand(i + 1));
3105
0
            poOvrVDS->SetBand(poOvrVDS->GetRasterCount() + 1,
3106
0
                              CreateOverviewBand(poSrcBand));
3107
0
        }
3108
3109
0
        if (m_poMaskBand)
3110
0
        {
3111
0
            VRTSourcedRasterBand *poSrcBand =
3112
0
                cpl::down_cast<VRTSourcedRasterBand *>(m_poMaskBand.get());
3113
0
            poOvrVDS->SetMaskBand(CreateOverviewBand(poSrcBand));
3114
0
        }
3115
0
    }
3116
0
}
3117
3118
/************************************************************************/
3119
/*                         AddVirtualOverview()                         */
3120
/************************************************************************/
3121
3122
bool VRTDataset::AddVirtualOverview(int nOvFactor, const char *pszResampling)
3123
0
{
3124
0
    if (nRasterXSize / nOvFactor == 0 || nRasterYSize / nOvFactor == 0)
3125
0
    {
3126
0
        return false;
3127
0
    }
3128
3129
0
    CPLStringList argv;
3130
0
    argv.AddString("-of");
3131
0
    argv.AddString("VRT");
3132
0
    argv.AddString("-outsize");
3133
0
    argv.AddString(CPLSPrintf("%d", nRasterXSize / nOvFactor));
3134
0
    argv.AddString(CPLSPrintf("%d", nRasterYSize / nOvFactor));
3135
0
    argv.AddString("-r");
3136
0
    argv.AddString(pszResampling);
3137
3138
0
    int nBlockXSize = 0;
3139
0
    int nBlockYSize = 0;
3140
0
    GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
3141
0
    if (!VRTDataset::IsDefaultBlockSize(nBlockXSize, nRasterXSize))
3142
0
    {
3143
0
        argv.AddString("-co");
3144
0
        argv.AddString(CPLSPrintf("BLOCKXSIZE=%d", nBlockXSize));
3145
0
    }
3146
0
    if (!VRTDataset::IsDefaultBlockSize(nBlockYSize, nRasterYSize))
3147
0
    {
3148
0
        argv.AddString("-co");
3149
0
        argv.AddString(CPLSPrintf("BLOCKYSIZE=%d", nBlockYSize));
3150
0
    }
3151
3152
0
    GDALTranslateOptions *psOptions =
3153
0
        GDALTranslateOptionsNew(argv.List(), nullptr);
3154
3155
    // Add a dummy overview so that BuildVirtualOverviews() doesn't trigger
3156
0
    m_apoOverviews.push_back(nullptr);
3157
0
    CPLAssert(m_bCanTakeRef);
3158
0
    m_bCanTakeRef =
3159
0
        false;  // we don't want hOverviewDS to take a reference on ourselves.
3160
0
    GDALDatasetH hOverviewDS =
3161
0
        GDALTranslate("", GDALDataset::ToHandle(this), psOptions, nullptr);
3162
0
    m_bCanTakeRef = true;
3163
0
    m_apoOverviews.pop_back();
3164
3165
0
    GDALTranslateOptionsFree(psOptions);
3166
0
    if (hOverviewDS == nullptr)
3167
0
        return false;
3168
3169
0
    m_anOverviewFactors.push_back(nOvFactor);
3170
0
    m_apoOverviews.push_back(GDALDataset::FromHandle(hOverviewDS));
3171
0
    return true;
3172
0
}
3173
3174
/************************************************************************/
3175
/*                          IBuildOverviews()                           */
3176
/************************************************************************/
3177
3178
CPLErr VRTDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
3179
                                   const int *panOverviewList, int nListBands,
3180
                                   const int *panBandList,
3181
                                   GDALProgressFunc pfnProgress,
3182
                                   void *pProgressData,
3183
                                   CSLConstList papszOptions)
3184
0
{
3185
0
    if (CPLTestBool(CSLFetchNameValueDef(
3186
0
            papszOptions, "VIRTUAL",
3187
0
            CPLGetConfigOption("VRT_VIRTUAL_OVERVIEWS", "NO"))))
3188
0
    {
3189
0
        SetNeedsFlush();
3190
0
        if (nOverviews == 0 ||
3191
0
            (!m_apoOverviews.empty() && m_anOverviewFactors.empty()))
3192
0
        {
3193
0
            m_anOverviewFactors.clear();
3194
0
            m_apoOverviewsBak.insert(m_apoOverviewsBak.end(),
3195
0
                                     m_apoOverviews.begin(),
3196
0
                                     m_apoOverviews.end());
3197
0
            m_apoOverviews.clear();
3198
0
        }
3199
0
        m_osOverviewResampling = pszResampling;
3200
0
        for (int i = 0; i < nOverviews; i++)
3201
0
        {
3202
0
            if (std::find(m_anOverviewFactors.begin(),
3203
0
                          m_anOverviewFactors.end(),
3204
0
                          panOverviewList[i]) == m_anOverviewFactors.end())
3205
0
            {
3206
0
                AddVirtualOverview(panOverviewList[i], pszResampling);
3207
0
            }
3208
0
        }
3209
0
        return CE_None;
3210
0
    }
3211
3212
0
    if (!oOvManager.IsInitialized())
3213
0
    {
3214
0
        const char *pszDesc = GetDescription();
3215
0
        if (pszDesc[0])
3216
0
        {
3217
0
            oOvManager.Initialize(this, pszDesc);
3218
0
        }
3219
0
    }
3220
3221
    // Make implicit overviews invisible, but do not destroy them in case they
3222
    // are already used.  Should the client do that?  Behavior might undefined
3223
    // in GDAL API?
3224
0
    if (!m_apoOverviews.empty())
3225
0
    {
3226
0
        m_apoOverviewsBak.insert(m_apoOverviewsBak.end(),
3227
0
                                 m_apoOverviews.begin(), m_apoOverviews.end());
3228
0
        m_apoOverviews.clear();
3229
0
    }
3230
0
    else
3231
0
    {
3232
        // Add a dummy overview so that GDALDataset::IBuildOverviews()
3233
        // doesn't manage to get a virtual implicit overview.
3234
0
        m_apoOverviews.push_back(nullptr);
3235
0
    }
3236
3237
0
    CPLErr eErr = GDALDataset::IBuildOverviews(
3238
0
        pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
3239
0
        pfnProgress, pProgressData, papszOptions);
3240
3241
0
    m_apoOverviews.clear();
3242
0
    return eErr;
3243
0
}
3244
3245
/************************************************************************/
3246
/*                         GetShiftedDataset()                          */
3247
/*                                                                      */
3248
/* Returns true if the VRT is made of a single source that is a simple  */
3249
/* in its full resolution.                                              */
3250
/************************************************************************/
3251
3252
bool VRTDataset::GetShiftedDataset(int nXOff, int nYOff, int nXSize, int nYSize,
3253
                                   GDALDataset *&poSrcDataset, int &nSrcXOff,
3254
                                   int &nSrcYOff)
3255
0
{
3256
0
    if (!CheckCompatibleForDatasetIO())
3257
0
        return false;
3258
3259
0
    VRTSourcedRasterBand *poVRTBand =
3260
0
        static_cast<VRTSourcedRasterBand *>(papoBands[0]);
3261
0
    if (poVRTBand->m_papoSources.size() != 1)
3262
0
        return false;
3263
3264
0
    VRTSimpleSource *poSource =
3265
0
        static_cast<VRTSimpleSource *>(poVRTBand->m_papoSources[0].get());
3266
3267
0
    GDALRasterBand *poBand = poSource->GetRasterBand();
3268
0
    if (!poBand || poSource->GetMaskBandMainBand() ||
3269
0
        poBand->GetRasterDataType() != poVRTBand->GetRasterDataType())
3270
0
        return false;
3271
3272
0
    poSrcDataset = poBand->GetDataset();
3273
0
    if (!poSrcDataset)
3274
0
        return false;
3275
3276
0
    double dfReqXOff = 0.0;
3277
0
    double dfReqYOff = 0.0;
3278
0
    double dfReqXSize = 0.0;
3279
0
    double dfReqYSize = 0.0;
3280
0
    int nReqXOff = 0;
3281
0
    int nReqYOff = 0;
3282
0
    int nReqXSize = 0;
3283
0
    int nReqYSize = 0;
3284
0
    int nOutXOff = 0;
3285
0
    int nOutYOff = 0;
3286
0
    int nOutXSize = 0;
3287
0
    int nOutYSize = 0;
3288
0
    bool bError = false;
3289
0
    if (!poSource->GetSrcDstWindow(
3290
0
            nXOff, nYOff, nXSize, nYSize, nXSize, nYSize,
3291
0
            GRIORA_NearestNeighbour, &dfReqXOff, &dfReqYOff, &dfReqXSize,
3292
0
            &dfReqYSize, &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
3293
0
            &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize, bError))
3294
0
        return false;
3295
3296
0
    if (nReqXSize != nXSize || nReqYSize != nYSize || nReqXSize != nOutXSize ||
3297
0
        nReqYSize != nOutYSize)
3298
0
        return false;
3299
3300
0
    nSrcXOff = nReqXOff;
3301
0
    nSrcYOff = nReqYOff;
3302
0
    return true;
3303
0
}
3304
3305
/************************************************************************/
3306
/*                       GetCompressionFormats()                        */
3307
/************************************************************************/
3308
3309
CPLStringList VRTDataset::GetCompressionFormats(int nXOff, int nYOff,
3310
                                                int nXSize, int nYSize,
3311
                                                int nBandCount,
3312
                                                const int *panBandList)
3313
0
{
3314
0
    GDALDataset *poSrcDataset;
3315
0
    int nSrcXOff;
3316
0
    int nSrcYOff;
3317
0
    if (!GetShiftedDataset(nXOff, nYOff, nXSize, nYSize, poSrcDataset, nSrcXOff,
3318
0
                           nSrcYOff))
3319
0
        return CPLStringList();
3320
0
    return poSrcDataset->GetCompressionFormats(nSrcXOff, nSrcYOff, nXSize,
3321
0
                                               nYSize, nBandCount, panBandList);
3322
0
}
3323
3324
/************************************************************************/
3325
/*                         ReadCompressedData()                         */
3326
/************************************************************************/
3327
3328
CPLErr VRTDataset::ReadCompressedData(const char *pszFormat, int nXOff,
3329
                                      int nYOff, int nXSize, int nYSize,
3330
                                      int nBandCount, const int *panBandList,
3331
                                      void **ppBuffer, size_t *pnBufferSize,
3332
                                      char **ppszDetailedFormat)
3333
0
{
3334
0
    GDALDataset *poSrcDataset;
3335
0
    int nSrcXOff;
3336
0
    int nSrcYOff;
3337
0
    if (!GetShiftedDataset(nXOff, nYOff, nXSize, nYSize, poSrcDataset, nSrcXOff,
3338
0
                           nSrcYOff))
3339
0
        return CE_Failure;
3340
0
    return poSrcDataset->ReadCompressedData(
3341
0
        pszFormat, nSrcXOff, nSrcYOff, nXSize, nYSize, nBandCount, panBandList,
3342
0
        ppBuffer, pnBufferSize, ppszDetailedFormat);
3343
0
}
3344
3345
/************************************************************************/
3346
/*                          ClearStatistics()                           */
3347
/************************************************************************/
3348
3349
void VRTDataset::ClearStatistics()
3350
0
{
3351
0
    for (int i = 1; i <= nBands; ++i)
3352
0
    {
3353
0
        bool bChanged = false;
3354
0
        GDALRasterBand *poBand = GetRasterBand(i);
3355
0
        CSLConstList papszOldMD = poBand->GetMetadata();
3356
0
        CPLStringList aosNewMD;
3357
0
        for (const char *pszMDItem : cpl::Iterate(papszOldMD))
3358
0
        {
3359
0
            if (STARTS_WITH_CI(pszMDItem, "STATISTICS_"))
3360
0
            {
3361
0
                bChanged = true;
3362
0
            }
3363
0
            else
3364
0
            {
3365
0
                aosNewMD.AddString(pszMDItem);
3366
0
            }
3367
0
        }
3368
0
        if (bChanged)
3369
0
        {
3370
0
            poBand->SetMetadata(aosNewMD.List());
3371
0
        }
3372
0
    }
3373
3374
0
    GDALDataset::ClearStatistics();
3375
0
}
3376
3377
/************************************************************************/
3378
/*                  VRTMapSharedResources::LockGuard()                  */
3379
/************************************************************************/
3380
3381
std::unique_ptr<std::lock_guard<std::mutex>>
3382
VRTMapSharedResources::LockGuard() const
3383
0
{
3384
0
    std::unique_ptr<std::lock_guard<std::mutex>> poLockGuard;
3385
0
    if (m_bUseMutex)
3386
0
    {
3387
0
        poLockGuard = std::make_unique<std::lock_guard<std::mutex>>(m_oMutex);
3388
0
    }
3389
0
    return poLockGuard;
3390
0
}
3391
3392
/************************************************************************/
3393
/*                     VRTMapSharedResources::Get()                     */
3394
/************************************************************************/
3395
3396
GDALDataset *VRTMapSharedResources::Get(const std::string &osKey) const
3397
0
{
3398
0
    auto poLockGuard = LockGuard();
3399
0
    CPL_IGNORE_RET_VAL(poLockGuard);
3400
0
    auto oIter = m_oMap.find(osKey);
3401
0
    GDALDataset *poRet = nullptr;
3402
0
    if (oIter != m_oMap.end())
3403
0
        poRet = oIter->second;
3404
0
    return poRet;
3405
0
}
3406
3407
/************************************************************************/
3408
/*                   VRTMapSharedResources::Insert()                    */
3409
/************************************************************************/
3410
3411
void VRTMapSharedResources::Insert(const std::string &osKey, GDALDataset *poDS)
3412
0
{
3413
0
    auto poLockGuard = LockGuard();
3414
0
    CPL_IGNORE_RET_VAL(poLockGuard);
3415
0
    m_oMap[osKey] = poDS;
3416
0
}
3417
3418
/************************************************************************/
3419
/*                  VRTMapSharedResources::InitMutex()                  */
3420
/************************************************************************/
3421
3422
void VRTMapSharedResources::InitMutex()
3423
0
{
3424
0
    m_bUseMutex = true;
3425
0
}
3426
3427
/*! @endcond */