Coverage Report

Created: 2026-09-14 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/vrt/vrtdriver.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  Virtual GDAL Datasets
4
 * Purpose:  Implementation of VRTDriver
5
 * Author:   Frank Warmerdam <warmerdam@pobox.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "vrtdataset.h"
15
16
#include "cpl_minixml.h"
17
#include "cpl_string.h"
18
#include "gdal_alg_priv.h"
19
#include "gdal_frmts.h"
20
#include "gdal_priv.h"
21
#include "vrtexpression.h"
22
23
#include <mutex>
24
25
/*! @cond Doxygen_Suppress */
26
27
/************************************************************************/
28
/*                             VRTDriver()                              */
29
/************************************************************************/
30
31
1
VRTDriver::VRTDriver() : papszSourceParsers(nullptr)
32
1
{
33
#if 0
34
    pDeserializerData = GDALRegisterTransformDeserializer(
35
        "WarpedOverviewTransformer",
36
        VRTWarpedOverviewTransform,
37
        VRTDeserializeWarpedOverviewTransformer );
38
#endif
39
1
}
40
41
/************************************************************************/
42
/*                             ~VRTDriver()                             */
43
/************************************************************************/
44
45
VRTDriver::~VRTDriver()
46
47
0
{
48
0
    CSLDestroy(papszSourceParsers);
49
0
    VRTDerivedRasterBand::Cleanup();
50
#if 0
51
    if(  pDeserializerData )
52
    {
53
        GDALUnregisterTransformDeserializer( pDeserializerData );
54
    }
55
#endif
56
0
}
57
58
/************************************************************************/
59
/*                       GetMetadataDomainList()                        */
60
/************************************************************************/
61
62
char **VRTDriver::GetMetadataDomainList()
63
0
{
64
0
    return BuildMetadataDomainList(GDALDriver::GetMetadataDomainList(), TRUE,
65
0
                                   "SourceParsers", nullptr);
66
0
}
67
68
/************************************************************************/
69
/*                            GetMetadata()                             */
70
/************************************************************************/
71
72
CSLConstList VRTDriver::GetMetadata(const char *pszDomain)
73
74
0
{
75
0
    std::lock_guard oLock(m_oMutex);
76
0
    if (pszDomain && EQUAL(pszDomain, "SourceParsers"))
77
0
        return papszSourceParsers;
78
79
0
    return GDALDriver::GetMetadata(pszDomain);
80
0
}
81
82
/************************************************************************/
83
/*                            SetMetadata()                             */
84
/************************************************************************/
85
86
CPLErr VRTDriver::SetMetadata(CSLConstList papszMetadata, const char *pszDomain)
87
88
0
{
89
0
    std::lock_guard oLock(m_oMutex);
90
0
    if (pszDomain && EQUAL(pszDomain, "SourceParsers"))
91
0
    {
92
0
        m_oMapSourceParser.clear();
93
0
        CSLDestroy(papszSourceParsers);
94
0
        papszSourceParsers = CSLDuplicate(papszMetadata);
95
0
        return CE_None;
96
0
    }
97
98
0
    return GDALDriver::SetMetadata(papszMetadata, pszDomain);
99
0
}
100
101
/************************************************************************/
102
/*                          AddSourceParser()                           */
103
/************************************************************************/
104
105
void VRTDriver::AddSourceParser(const char *pszElementName,
106
                                VRTSourceParser pfnParser)
107
108
6
{
109
6
    m_oMapSourceParser[pszElementName] = pfnParser;
110
111
    // Below won't work on architectures with "capability pointers"
112
113
6
    char szPtrValue[128] = {'\0'};
114
6
    void *ptr;
115
6
    CPL_STATIC_ASSERT(sizeof(pfnParser) == sizeof(void *));
116
6
    memcpy(&ptr, &pfnParser, sizeof(void *));
117
6
    int nRet = CPLPrintPointer(szPtrValue, ptr, sizeof(szPtrValue));
118
6
    szPtrValue[nRet] = 0;
119
120
6
    papszSourceParsers =
121
6
        CSLSetNameValue(papszSourceParsers, pszElementName, szPtrValue);
122
6
}
123
124
/************************************************************************/
125
/*                            ParseSource()                             */
126
/************************************************************************/
127
128
VRTSource *VRTDriver::ParseSource(const CPLXMLNode *psSrc,
129
                                  const char *pszVRTPath,
130
                                  VRTMapSharedResources &oMapSharedSources)
131
132
0
{
133
134
0
    if (psSrc == nullptr || psSrc->eType != CXT_Element)
135
0
    {
136
0
        CPLError(CE_Failure, CPLE_AppDefined,
137
0
                 "Corrupt or empty VRT source XML document.");
138
0
        return nullptr;
139
0
    }
140
141
0
    if (!m_oMapSourceParser.empty())
142
0
    {
143
0
        auto oIter = m_oMapSourceParser.find(psSrc->pszValue);
144
0
        if (oIter != m_oMapSourceParser.end())
145
0
        {
146
0
            return oIter->second(psSrc, pszVRTPath, oMapSharedSources);
147
0
        }
148
0
        return nullptr;
149
0
    }
150
151
    // Below won't work on architectures with "capability pointers"
152
153
0
    const char *pszParserFunc =
154
0
        CSLFetchNameValue(papszSourceParsers, psSrc->pszValue);
155
0
    if (pszParserFunc == nullptr)
156
0
        return nullptr;
157
158
0
    VRTSourceParser pfnParser;
159
0
    CPL_STATIC_ASSERT(sizeof(pfnParser) == sizeof(void *));
160
0
    void *ptr =
161
0
        CPLScanPointer(pszParserFunc, static_cast<int>(strlen(pszParserFunc)));
162
0
    memcpy(&pfnParser, &ptr, sizeof(void *));
163
164
0
    if (pfnParser == nullptr)
165
0
        return nullptr;
166
167
0
    return pfnParser(psSrc, pszVRTPath, oMapSharedSources);
168
0
}
169
170
/************************************************************************/
171
/*                           VRTCreateCopy()                            */
172
/************************************************************************/
173
174
static GDALDataset *VRTCreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
175
                                  int /* bStrict */, CSLConstList papszOptions,
176
                                  GDALProgressFunc pfnProgress,
177
                                  void *pProgressData)
178
0
{
179
0
    CPLAssert(nullptr != poSrcDS);
180
181
0
    VRTDataset *poSrcVRTDS = nullptr;
182
183
0
    void *pHandle = poSrcDS->GetInternalHandle("VRT_DATASET");
184
0
    if (pHandle && poSrcDS->GetInternalHandle(nullptr) == nullptr)
185
0
    {
186
0
        poSrcVRTDS = static_cast<VRTDataset *>(pHandle);
187
0
    }
188
0
    else
189
0
    {
190
0
        poSrcVRTDS = dynamic_cast<VRTDataset *>(poSrcDS);
191
0
    }
192
193
    /* -------------------------------------------------------------------- */
194
    /*      If the source dataset is a virtual dataset then just write      */
195
    /*      it to disk as a special case to avoid extra layers of           */
196
    /*      indirection.                                                    */
197
    /* -------------------------------------------------------------------- */
198
0
    if (poSrcVRTDS)
199
0
    {
200
201
        /* --------------------------------------------------------------------
202
         */
203
        /*      Convert tree to a single block of XML text. */
204
        /* --------------------------------------------------------------------
205
         */
206
0
        char *pszVRTPath = CPLStrdup(CPLGetPathSafe(pszFilename).c_str());
207
0
        poSrcVRTDS->UnsetPreservedRelativeFilenames();
208
0
        CPLXMLNode *psDSTree = poSrcVRTDS->SerializeToXML(pszVRTPath);
209
210
0
        char *pszXML = CPLSerializeXMLTree(psDSTree);
211
212
0
        CPLDestroyXMLNode(psDSTree);
213
214
0
        CPLFree(pszVRTPath);
215
216
        /* --------------------------------------------------------------------
217
         */
218
        /*      Write to disk. */
219
        /* --------------------------------------------------------------------
220
         */
221
0
        GDALDataset *pCopyDS = nullptr;
222
223
0
        if (0 != strlen(pszFilename))
224
0
        {
225
0
            VSILFILE *fpVRT = VSIFOpenL(pszFilename, "wb");
226
0
            if (fpVRT == nullptr)
227
0
            {
228
0
                CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s",
229
0
                         pszFilename);
230
0
                CPLFree(pszXML);
231
0
                return nullptr;
232
0
            }
233
234
0
            bool bRet = VSIFWriteL(pszXML, strlen(pszXML), 1, fpVRT) > 0;
235
0
            if (VSIFCloseL(fpVRT) != 0)
236
0
                bRet = false;
237
238
0
            if (bRet)
239
0
                pCopyDS = GDALDataset::Open(
240
0
                    pszFilename,
241
0
                    GDAL_OF_RASTER | GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE);
242
0
        }
243
0
        else
244
0
        {
245
            /* No destination file is given, so pass serialized XML directly. */
246
0
            pCopyDS = GDALDataset::Open(pszXML, GDAL_OF_RASTER |
247
0
                                                    GDAL_OF_MULTIDIM_RASTER |
248
0
                                                    GDAL_OF_UPDATE);
249
0
        }
250
251
0
        CPLFree(pszXML);
252
253
0
        return pCopyDS;
254
0
    }
255
256
    /* -------------------------------------------------------------------- */
257
    /*      Multidimensional raster ?                                       */
258
    /* -------------------------------------------------------------------- */
259
0
    auto poSrcGroup = poSrcDS->GetRootGroup();
260
0
    if (poSrcGroup != nullptr)
261
0
    {
262
0
        auto poDstDS = VRTDataset::CreateVRTMultiDimensional(pszFilename,
263
0
                                                             nullptr, nullptr);
264
0
        if (!poDstDS)
265
0
            return nullptr;
266
0
        auto poDstGroup = poDstDS->GetRootVRTGroup();
267
0
        if (!poDstGroup)
268
0
            return nullptr;
269
0
        poDstGroup->SetGuessRegularlySpacedArrays(
270
0
            CPLTestBool(CSLFetchNameValueDef(
271
0
                papszOptions, "GUESS_REGULARLY_SPACED_ARRAYS", "YES")));
272
0
        if (GDALDriver::DefaultCreateCopyMultiDimensional(
273
0
                poSrcDS, poDstDS.get(), false, nullptr, nullptr, nullptr) !=
274
0
            CE_None)
275
0
            return nullptr;
276
277
0
        if (strcmp(pszFilename, "") != 0)
278
0
        {
279
0
            if (poDstDS->FlushCache(true) != CE_None)
280
0
            {
281
0
                poDstDS.reset();
282
0
            }
283
0
        }
284
285
0
        if (pfnProgress)
286
0
            pfnProgress(1.0, "", pProgressData);
287
0
        return poDstDS.release();
288
0
    }
289
290
    /* -------------------------------------------------------------------- */
291
    /*      Create the virtual dataset.                                     */
292
    /* -------------------------------------------------------------------- */
293
0
    auto poVRTDS = VRTDataset::CreateVRTDataset(
294
0
        pszFilename, poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(), 0,
295
0
        GDT_UInt8, papszOptions);
296
0
    if (poVRTDS == nullptr)
297
0
        return nullptr;
298
299
    /* -------------------------------------------------------------------- */
300
    /*      Do we have a geotransform?                                      */
301
    /* -------------------------------------------------------------------- */
302
0
    GDALGeoTransform gt;
303
0
    if (poSrcDS->GetGeoTransform(gt) == CE_None)
304
0
    {
305
0
        poVRTDS->SetGeoTransform(gt);
306
0
    }
307
308
    /* -------------------------------------------------------------------- */
309
    /*      Copy projection                                                 */
310
    /* -------------------------------------------------------------------- */
311
0
    poVRTDS->SetSpatialRef(poSrcDS->GetSpatialRef());
312
313
    /* -------------------------------------------------------------------- */
314
    /*      Emit dataset level metadata.                                    */
315
    /* -------------------------------------------------------------------- */
316
0
    const char *pszCopySrcMDD =
317
0
        CSLFetchNameValueDef(papszOptions, "COPY_SRC_MDD", "AUTO");
318
0
    char **papszSrcMDD = CSLFetchNameValueMultiple(papszOptions, "SRC_MDD");
319
0
    if (EQUAL(pszCopySrcMDD, "AUTO") || CPLTestBool(pszCopySrcMDD) ||
320
0
        papszSrcMDD)
321
0
    {
322
0
        if (!papszSrcMDD || CSLFindString(papszSrcMDD, "") >= 0 ||
323
0
            CSLFindString(papszSrcMDD, "_DEFAULT_") >= 0)
324
0
        {
325
0
            poVRTDS->SetMetadata(poSrcDS->GetMetadata());
326
0
        }
327
328
        /* -------------------------------------------------------------------- */
329
        /*      Copy any special domains that should be transportable.          */
330
        /* -------------------------------------------------------------------- */
331
0
        constexpr const char *apszDefaultDomains[] = {
332
0
            GDAL_MDD_RPC, GDAL_MDD_IMD, GDAL_MDD_GEOLOCATION};
333
0
        for (const char *pszDomain : apszDefaultDomains)
334
0
        {
335
0
            if (!papszSrcMDD || CSLFindString(papszSrcMDD, pszDomain) >= 0)
336
0
            {
337
0
                CSLConstList papszMD = poSrcDS->GetMetadata(pszDomain);
338
0
                if (papszMD)
339
0
                    poVRTDS->SetMetadata(papszMD, pszDomain);
340
0
            }
341
0
        }
342
343
0
        if ((!EQUAL(pszCopySrcMDD, "AUTO") && CPLTestBool(pszCopySrcMDD)) ||
344
0
            papszSrcMDD)
345
0
        {
346
0
            char **papszDomainList = poSrcDS->GetMetadataDomainList();
347
0
            constexpr const char *apszReservedDomains[] = {
348
0
                GDAL_MDD_IMAGE_STRUCTURE, "DERIVED_SUBDATASETS"};
349
0
            for (char **papszIter = papszDomainList; papszIter && *papszIter;
350
0
                 ++papszIter)
351
0
            {
352
0
                const char *pszDomain = *papszIter;
353
0
                if (pszDomain[0] != 0 &&
354
0
                    (!papszSrcMDD ||
355
0
                     CSLFindString(papszSrcMDD, pszDomain) >= 0))
356
0
                {
357
0
                    bool bCanCopy = true;
358
0
                    for (const char *pszOtherDomain : apszDefaultDomains)
359
0
                    {
360
0
                        if (EQUAL(pszDomain, pszOtherDomain))
361
0
                        {
362
0
                            bCanCopy = false;
363
0
                            break;
364
0
                        }
365
0
                    }
366
0
                    if (!papszSrcMDD)
367
0
                    {
368
0
                        for (const char *pszOtherDomain : apszReservedDomains)
369
0
                        {
370
0
                            if (EQUAL(pszDomain, pszOtherDomain))
371
0
                            {
372
0
                                bCanCopy = false;
373
0
                                break;
374
0
                            }
375
0
                        }
376
0
                    }
377
0
                    if (bCanCopy)
378
0
                    {
379
0
                        poVRTDS->SetMetadata(poSrcDS->GetMetadata(pszDomain),
380
0
                                             pszDomain);
381
0
                    }
382
0
                }
383
0
            }
384
0
            CSLDestroy(papszDomainList);
385
0
        }
386
0
    }
387
0
    CSLDestroy(papszSrcMDD);
388
389
0
    {
390
0
        const char *pszInterleave = poSrcDS->GetMetadataItem(
391
0
            GDALMD_INTERLEAVE, GDAL_MDD_IMAGE_STRUCTURE);
392
0
        if (pszInterleave)
393
0
        {
394
0
            poVRTDS->SetMetadataItem(GDALMD_INTERLEAVE, pszInterleave,
395
0
                                     GDAL_MDD_IMAGE_STRUCTURE);
396
0
        }
397
0
    }
398
0
    {
399
0
        const char *pszCompression = poSrcDS->GetMetadataItem(
400
0
            GDALMD_COMPRESSION, GDAL_MDD_IMAGE_STRUCTURE);
401
0
        if (pszCompression)
402
0
        {
403
0
            poVRTDS->SetMetadataItem(GDALMD_COMPRESSION, pszCompression,
404
0
                                     GDAL_MDD_IMAGE_STRUCTURE);
405
0
        }
406
0
    }
407
408
    /* -------------------------------------------------------------------- */
409
    /*      GCPs                                                            */
410
    /* -------------------------------------------------------------------- */
411
0
    if (poSrcDS->GetGCPCount() > 0)
412
0
    {
413
0
        poVRTDS->SetGCPs(poSrcDS->GetGCPCount(), poSrcDS->GetGCPs(),
414
0
                         poSrcDS->GetGCPSpatialRef());
415
0
    }
416
417
    /* -------------------------------------------------------------------- */
418
    /*      Loop over all the bands.                                        */
419
    /* -------------------------------------------------------------------- */
420
0
    for (int iBand = 0; iBand < poSrcDS->GetRasterCount(); iBand++)
421
0
    {
422
0
        GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
423
424
        /* --------------------------------------------------------------------
425
         */
426
        /*      Create the band with the appropriate band type. */
427
        /* --------------------------------------------------------------------
428
         */
429
0
        CPLStringList aosAddBandOptions;
430
0
        int nBlockXSize = poVRTDS->GetBlockXSize();
431
0
        int nBlockYSize = poVRTDS->GetBlockYSize();
432
0
        if (!poVRTDS->IsBlockSizeSpecified())
433
0
        {
434
0
            poSrcBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
435
0
        }
436
0
        aosAddBandOptions.SetNameValue("BLOCKXSIZE",
437
0
                                       CPLSPrintf("%d", nBlockXSize));
438
0
        aosAddBandOptions.SetNameValue("BLOCKYSIZE",
439
0
                                       CPLSPrintf("%d", nBlockYSize));
440
0
        poVRTDS->AddBand(poSrcBand->GetRasterDataType(), aosAddBandOptions);
441
442
0
        VRTSourcedRasterBand *poVRTBand = static_cast<VRTSourcedRasterBand *>(
443
0
            poVRTDS->GetRasterBand(iBand + 1));
444
445
        /* --------------------------------------------------------------------
446
         */
447
        /*      Setup source mapping. */
448
        /* --------------------------------------------------------------------
449
         */
450
0
        poVRTBand->AddSimpleSource(poSrcBand);
451
452
        /* --------------------------------------------------------------------
453
         */
454
        /*      Emit various band level metadata. */
455
        /* --------------------------------------------------------------------
456
         */
457
0
        poVRTBand->CopyCommonInfoFrom(poSrcBand);
458
459
0
        const char *pszCompression = poSrcBand->GetMetadataItem(
460
0
            GDALMD_COMPRESSION, GDAL_MDD_IMAGE_STRUCTURE);
461
0
        if (pszCompression)
462
0
        {
463
0
            poVRTBand->SetMetadataItem(GDALMD_COMPRESSION, pszCompression,
464
0
                                       GDAL_MDD_IMAGE_STRUCTURE);
465
0
        }
466
467
        /* --------------------------------------------------------------------
468
         */
469
        /*      Add specific mask band. */
470
        /* --------------------------------------------------------------------
471
         */
472
0
        if ((poSrcBand->GetMaskFlags() &
473
0
             (GMF_PER_DATASET | GMF_ALL_VALID | GMF_NODATA)) == 0)
474
0
        {
475
0
            auto poVRTMaskBand = std::make_unique<VRTSourcedRasterBand>(
476
0
                poVRTDS.get(), 0, poSrcBand->GetMaskBand()->GetRasterDataType(),
477
0
                poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize());
478
0
            poVRTMaskBand->AddMaskBandSource(poSrcBand);
479
0
            poVRTBand->SetMaskBand(std::move(poVRTMaskBand));
480
0
        }
481
0
    }
482
483
    /* -------------------------------------------------------------------- */
484
    /*      Add dataset mask band                                           */
485
    /* -------------------------------------------------------------------- */
486
0
    if (poSrcDS->GetRasterCount() != 0 &&
487
0
        poSrcDS->GetRasterBand(1) != nullptr &&
488
0
        poSrcDS->GetRasterBand(1)->GetMaskFlags() == GMF_PER_DATASET)
489
0
    {
490
0
        GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(1);
491
0
        auto poVRTMaskBand = std::make_unique<VRTSourcedRasterBand>(
492
0
            poVRTDS.get(), 0, poSrcBand->GetMaskBand()->GetRasterDataType(),
493
0
            poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize());
494
0
        poVRTMaskBand->AddMaskBandSource(poSrcBand);
495
0
        poVRTDS->SetMaskBand(std::move(poVRTMaskBand));
496
0
    }
497
498
0
    if (strcmp(pszFilename, "") != 0)
499
0
    {
500
0
        if (poVRTDS->FlushCache(true) != CE_None)
501
0
        {
502
0
            poVRTDS.reset();
503
0
        }
504
0
    }
505
506
0
    if (pfnProgress)
507
0
        pfnProgress(1.0, "", pProgressData);
508
509
0
    return poVRTDS.release();
510
0
}
511
512
/************************************************************************/
513
/*                          GDALRegister_VRT()                          */
514
/************************************************************************/
515
516
void GDALRegister_VRT()
517
518
1
{
519
1
    auto poDM = GetGDALDriverManager();
520
1
    if (poDM->GetDriverByName("VRT") != nullptr)
521
0
        return;
522
523
1
    static std::once_flag flag;
524
1
    std::call_once(flag,
525
1
                   []()
526
1
                   {
527
                       // First register the pixel functions
528
1
                       GDALRegisterDefaultPixelFunc();
529
530
                       // Register functions for VRTProcessedDataset
531
1
                       GDALVRTRegisterDefaultProcessedDatasetFuncs();
532
1
                   });
533
534
1
    VRTDriver *poDriver = new VRTDriver();
535
536
1
    poDriver->SetDescription("VRT");
537
1
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
538
1
    poDriver->SetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER, "YES");
539
1
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Virtual Raster");
540
1
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "vrt");
541
1
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/vrt.html");
542
1
    poDriver->SetMetadataItem(
543
1
        GDAL_DMD_CREATIONDATATYPES,
544
1
        "Byte Int8 Int16 UInt16 Int32 UInt32 Int64 UInt64 "
545
1
        "Float16 Float32 Float64 "
546
1
        "CInt16 CInt32 CFloat16 CFloat32 CFloat64");
547
1
    poDriver->SetMetadataItem(
548
1
        GDAL_DMD_CREATIONOPTIONLIST,
549
1
        "<CreationOptionList>\n"
550
1
        "   <Option name='SUBCLASS' type='string-select' "
551
1
        "default='VRTDataset'>\n"
552
1
        "       <Value>VRTDataset</Value>\n"
553
1
        "       <Value>VRTWarpedDataset</Value>\n"
554
1
        "   </Option>\n"
555
1
        "   <Option name='BLOCKXSIZE' type='int' description='Block width'/>\n"
556
1
        "   <Option name='BLOCKYSIZE' type='int' description='Block height'/>\n"
557
1
        "</CreationOptionList>\n");
558
559
1
    auto poGTiffDrv = poDM->GetDriverByName("GTiff");
560
1
    if (poGTiffDrv)
561
1
    {
562
1
        const char *pszGTiffOvrCO =
563
1
            poGTiffDrv->GetMetadataItem(GDAL_DMD_OVERVIEW_CREATIONOPTIONLIST);
564
1
        if (pszGTiffOvrCO &&
565
1
            STARTS_WITH(pszGTiffOvrCO, "<OverviewCreationOptionList>"))
566
1
        {
567
1
            std::string ocoList =
568
1
                "<OverviewCreationOptionList>"
569
1
                "   <Option name='VIRTUAL' type='boolean' "
570
1
                "default='NO' "
571
1
                "description='Whether virtual overviews rather than "
572
1
                "materialized external GeoTIFF .ovr should be created'/>";
573
1
            ocoList += (pszGTiffOvrCO + strlen("<OverviewCreationOptionList>"));
574
1
            poDriver->SetMetadataItem(GDAL_DMD_OVERVIEW_CREATIONOPTIONLIST,
575
1
                                      ocoList.c_str());
576
1
        }
577
1
    }
578
579
1
    poDriver->SetMetadataItem(
580
1
        GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST,
581
1
        "<MultiDimDatasetCreationOptionList>"
582
1
        "   <Option name='GUESS_REGULARLY_SPACED_ARRAYS' type='boolean' "
583
1
        "description='Whether content of 1D-arrays should be read to deduce "
584
1
        "if they are regularly spaced. Can be slow on huge remote datasets' "
585
1
        "default='YES'/>"
586
1
        "</MultiDimDatasetCreationOptionList>");
587
588
1
    poDriver->SetMetadataItem(
589
1
        GDAL_DMD_MULTIDIM_ARRAY_CREATIONOPTIONLIST,
590
1
        "<MultiDimArrayCreationOptionList>"
591
1
        "   <Option name='BLOCKSIZE' type='int' description='Block size in "
592
1
        "pixels'/>"
593
1
        "</MultiDimArrayCreationOptionList>");
594
595
1
    poDriver->pfnCreateCopy = VRTCreateCopy;
596
1
    poDriver->pfnCreate = VRTDataset::Create;
597
1
    poDriver->pfnCreateMultiDimensional = VRTDataset::CreateMultiDimensional;
598
599
1
#ifndef NO_OPEN
600
1
    poDriver->pfnOpen = VRTDataset::Open;
601
1
    poDriver->pfnIdentify = VRTDataset::Identify;
602
1
    poDriver->pfnDelete = VRTDataset::Delete;
603
604
1
    poDriver->SetMetadataItem(
605
1
        GDAL_DMD_OPENOPTIONLIST,
606
1
        "<OpenOptionList>"
607
1
        "  <Option name='ROOT_PATH' type='string' description='Root path to "
608
1
        "evaluate "
609
1
        "relative paths inside the VRT. Mainly useful for inlined VRT, or "
610
1
        "in-memory "
611
1
        "VRT, where their own directory does not make sense'/>"
612
1
        "<Option name='NUM_THREADS' type='string' description="
613
1
        "'Number of worker threads for reading. Can be set to ALL_CPUS' "
614
1
        "default='ALL_CPUS'/>"
615
1
        "</OpenOptionList>");
616
1
#endif
617
618
1
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
619
1
    poDriver->SetMetadataItem(GDAL_DCAP_COORDINATE_EPOCH, "YES");
620
621
1
    poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES");
622
1
    poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS,
623
1
                              "GeoTransform SRS GCPs NoData "
624
1
                              "ColorInterpretation "
625
1
                              "DatasetMetadata BandMetadata");
626
627
1
    const char *pszExpressionDialects = "ExpressionDialects";
628
#if defined(GDAL_VRT_ENABLE_MUPARSER) && defined(GDAL_VRT_ENABLE_EXPRTK)
629
    poDriver->SetMetadataItem(pszExpressionDialects, "muparser,exprtk");
630
#elif defined(GDAL_VRT_ENABLE_MUPARSER)
631
    poDriver->SetMetadataItem(pszExpressionDialects, "muparser");
632
#elif defined(GDAL_VRT_ENABLE_EXPRTK)
633
    poDriver->SetMetadataItem(pszExpressionDialects, "exprtk");
634
#else
635
1
    poDriver->SetMetadataItem(pszExpressionDialects, "none");
636
1
#endif
637
638
#ifdef GDAL_VRT_ENABLE_MUPARSER
639
    if (gdal::MuParserHasDefineFunUserData())
640
    {
641
        poDriver->SetMetadataItem("MUPARSER_HAS_DEFINE_FUN_USER_DATA", "YES");
642
    }
643
#endif
644
645
1
#ifdef GDAL_VRT_ENABLE_RAWRASTERBAND
646
1
    poDriver->SetMetadataItem("GDAL_VRT_ENABLE_RAWRASTERBAND", "YES");
647
1
#endif
648
649
1
    poDriver->AddSourceParser("SimpleSource", VRTParseCoreSources);
650
1
    poDriver->AddSourceParser("ComplexSource", VRTParseCoreSources);
651
1
    poDriver->AddSourceParser("AveragedSource", VRTParseCoreSources);
652
1
    poDriver->AddSourceParser("NoDataFromMaskSource", VRTParseCoreSources);
653
1
    poDriver->AddSourceParser("KernelFilteredSource", VRTParseFilterSources);
654
1
    poDriver->AddSourceParser("ArraySource", VRTParseArraySource);
655
656
1
    poDM->RegisterDriver(poDriver);
657
1
}
658
659
/*! @endcond */