Coverage Report

Created: 2025-08-28 06:57

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