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