Coverage Report

Created: 2025-06-13 06:18

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