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