Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Virtual GDAL Datasets
4 : * Purpose: Implementation of VRTDataset
5 : * Author: Frank Warmerdam <warmerdam@pobox.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "vrtdataset.h"
15 :
16 : #include "cpl_error_internal.h"
17 : #include "cpl_minixml.h"
18 : #include "cpl_string.h"
19 : #include "cpl_worker_thread_pool.h"
20 : #include "gdal_frmts.h"
21 : #include "ogr_spatialref.h"
22 : #include "gdal_thread_pool.h"
23 : #include "gdal_utils.h"
24 :
25 : #include <algorithm>
26 : #include <cassert>
27 : #include <cmath>
28 : #include <set>
29 : #include <typeinfo>
30 : #include "gdal_proxy.h"
31 :
32 : /*! @cond Doxygen_Suppress */
33 :
34 : #define VRT_PROTOCOL_PREFIX "vrt://"
35 :
36 : constexpr int DEFAULT_BLOCK_SIZE = 128;
37 :
38 : /************************************************************************/
39 : /* VRTDataset() */
40 : /************************************************************************/
41 :
42 6728 : VRTDataset::VRTDataset(int nXSize, int nYSize, int nBlockXSize, int nBlockYSize)
43 : {
44 6728 : nRasterXSize = nXSize;
45 6728 : nRasterYSize = nYSize;
46 :
47 6728 : m_bBlockSizeSpecified = nBlockXSize > 0 && nBlockYSize > 0;
48 6728 : m_nBlockXSize =
49 6728 : nBlockXSize > 0 ? nBlockXSize : std::min(DEFAULT_BLOCK_SIZE, nXSize);
50 6728 : m_nBlockYSize =
51 6728 : nBlockYSize > 0 ? nBlockYSize : std::min(DEFAULT_BLOCK_SIZE, nYSize);
52 :
53 6728 : GDALRegister_VRT();
54 :
55 6728 : poDriver = static_cast<GDALDriver *>(GDALGetDriverByName("VRT"));
56 6728 : }
57 :
58 : /************************************************************************/
59 : /* IsDefaultBlockSize() */
60 : /************************************************************************/
61 :
62 1516 : /* static */ bool VRTDataset::IsDefaultBlockSize(int nBlockSize, int nDimension)
63 : {
64 2512 : return nBlockSize == DEFAULT_BLOCK_SIZE ||
65 2512 : (nBlockSize < DEFAULT_BLOCK_SIZE && nBlockSize == nDimension);
66 : }
67 :
68 : /*! @endcond */
69 :
70 : /************************************************************************/
71 : /* VRTCreate() */
72 : /************************************************************************/
73 :
74 : /**
75 : * @see VRTDataset::VRTDataset()
76 : */
77 :
78 2347 : VRTDatasetH CPL_STDCALL VRTCreate(int nXSize, int nYSize)
79 :
80 : {
81 2347 : auto poDS = new VRTDataset(nXSize, nYSize);
82 2347 : poDS->eAccess = GA_Update;
83 2347 : return poDS;
84 : }
85 :
86 : /*! @cond Doxygen_Suppress */
87 :
88 : /************************************************************************/
89 : /* ~VRTDataset() */
90 : /************************************************************************/
91 :
92 12417 : VRTDataset::~VRTDataset()
93 :
94 : {
95 6728 : VRTDataset::FlushCache(true);
96 6728 : CPLFree(m_pszVRTPath);
97 :
98 6791 : for (size_t i = 0; i < m_apoOverviews.size(); i++)
99 63 : delete m_apoOverviews[i];
100 6737 : for (size_t i = 0; i < m_apoOverviewsBak.size(); i++)
101 9 : delete m_apoOverviewsBak[i];
102 6728 : CSLDestroy(m_papszXMLVRTMetadata);
103 12417 : }
104 :
105 : /************************************************************************/
106 : /* FlushCache() */
107 : /************************************************************************/
108 :
109 7191 : CPLErr VRTDataset::FlushCache(bool bAtClosing)
110 :
111 : {
112 7191 : if (m_poRootGroup)
113 : {
114 380 : m_poRootGroup->SetVRTPath(CPLGetPathSafe(GetDescription()));
115 380 : return m_poRootGroup->Serialize() ? CE_None : CE_Failure;
116 : }
117 : else
118 6811 : return VRTFlushCacheStruct<VRTDataset>::FlushCache(*this, bAtClosing);
119 : }
120 :
121 : /************************************************************************/
122 : /* FlushCache() */
123 : /************************************************************************/
124 :
125 818 : CPLErr VRTWarpedDataset::FlushCache(bool bAtClosing)
126 :
127 : {
128 818 : return VRTFlushCacheStruct<VRTWarpedDataset>::FlushCache(*this, bAtClosing);
129 : }
130 :
131 : /************************************************************************/
132 : /* FlushCache() */
133 : /************************************************************************/
134 :
135 199 : CPLErr VRTPansharpenedDataset::FlushCache(bool bAtClosing)
136 :
137 : {
138 199 : return VRTFlushCacheStruct<VRTPansharpenedDataset>::FlushCache(*this,
139 199 : bAtClosing);
140 : }
141 :
142 : /************************************************************************/
143 : /* FlushCache() */
144 : /************************************************************************/
145 :
146 204 : CPLErr VRTProcessedDataset::FlushCache(bool bAtClosing)
147 :
148 : {
149 204 : return VRTFlushCacheStruct<VRTProcessedDataset>::FlushCache(*this,
150 204 : bAtClosing);
151 : }
152 :
153 : /************************************************************************/
154 : /* FlushCache() */
155 : /************************************************************************/
156 :
157 : template <class T>
158 8032 : CPLErr VRTFlushCacheStruct<T>::FlushCache(T &obj, bool bAtClosing)
159 : {
160 8032 : CPLErr eErr = obj.GDALDataset::FlushCache(bAtClosing);
161 :
162 8032 : if (!obj.m_bNeedsFlush || !obj.m_bWritable)
163 3638 : return eErr;
164 :
165 : // We don't write to disk if there is no filename. This is a
166 : // memory only dataset.
167 4836 : if (strlen(obj.GetDescription()) == 0 ||
168 442 : STARTS_WITH_CI(obj.GetDescription(), "<VRTDataset"))
169 3966 : return eErr;
170 :
171 428 : obj.m_bNeedsFlush = false;
172 :
173 : // Serialize XML representation to disk
174 428 : const std::string osVRTPath(CPLGetPathSafe(obj.GetDescription()));
175 428 : CPLXMLNode *psDSTree = obj.T::SerializeToXML(osVRTPath.c_str());
176 428 : if (!CPLSerializeXMLTreeToFile(psDSTree, obj.GetDescription()))
177 13 : eErr = CE_Failure;
178 428 : CPLDestroyXMLNode(psDSTree);
179 428 : return eErr;
180 : }
181 :
182 : /************************************************************************/
183 : /* GetMetadata() */
184 : /************************************************************************/
185 :
186 18151 : CSLConstList VRTDataset::GetMetadata(const char *pszDomain)
187 : {
188 18151 : if (pszDomain != nullptr && EQUAL(pszDomain, "xml:VRT"))
189 : {
190 : /* ------------------------------------------------------------------ */
191 : /* Convert tree to a single block of XML text. */
192 : /* ------------------------------------------------------------------ */
193 33 : const char *pszDescription = GetDescription();
194 33 : char *l_pszVRTPath = CPLStrdup(
195 33 : pszDescription[0] && !STARTS_WITH(pszDescription, "<VRTDataset")
196 53 : ? CPLGetPathSafe(pszDescription).c_str()
197 : : "");
198 33 : CPLXMLNode *psDSTree = SerializeToXML(l_pszVRTPath);
199 33 : char *pszXML = CPLSerializeXMLTree(psDSTree);
200 :
201 33 : CPLDestroyXMLNode(psDSTree);
202 :
203 33 : CPLFree(l_pszVRTPath);
204 :
205 33 : CSLDestroy(m_papszXMLVRTMetadata);
206 33 : m_papszXMLVRTMetadata =
207 33 : static_cast<char **>(CPLMalloc(2 * sizeof(char *)));
208 33 : m_papszXMLVRTMetadata[0] = pszXML;
209 33 : m_papszXMLVRTMetadata[1] = nullptr;
210 33 : return m_papszXMLVRTMetadata;
211 : }
212 :
213 18118 : return GDALDataset::GetMetadata(pszDomain);
214 : }
215 :
216 : /************************************************************************/
217 : /* GetMetadataItem() */
218 : /************************************************************************/
219 :
220 28421 : const char *VRTDataset::GetMetadataItem(const char *pszName,
221 : const char *pszDomain)
222 :
223 : {
224 28421 : if (pszName && pszDomain && EQUAL(pszDomain, "__DEBUG__"))
225 : {
226 11 : if (EQUAL(pszName, "MULTI_THREADED_RASTERIO_LAST_USED"))
227 10 : return m_bMultiThreadedRasterIOLastUsed ? "1" : "0";
228 1 : else if (EQUAL(pszName, "CheckCompatibleForDatasetIO()"))
229 1 : return CheckCompatibleForDatasetIO() ? "1" : "0";
230 : }
231 28410 : return GDALDataset::GetMetadataItem(pszName, pszDomain);
232 : }
233 :
234 : /*! @endcond */
235 :
236 : /************************************************************************/
237 : /* VRTFlushCache(bool bAtClosing) */
238 : /************************************************************************/
239 :
240 : /**
241 : * @see VRTDataset::FlushCache(bool bAtClosing)
242 : */
243 :
244 0 : void CPL_STDCALL VRTFlushCache(VRTDatasetH hDataset)
245 : {
246 0 : VALIDATE_POINTER0(hDataset, "VRTFlushCache");
247 :
248 0 : static_cast<VRTDataset *>(GDALDataset::FromHandle(hDataset))
249 0 : ->FlushCache(false);
250 : }
251 :
252 : /*! @cond Doxygen_Suppress */
253 :
254 : /************************************************************************/
255 : /* SerializeToXML() */
256 : /************************************************************************/
257 :
258 593 : CPLXMLNode *VRTDataset::SerializeToXML(const char *pszVRTPathIn)
259 :
260 : {
261 593 : if (m_poRootGroup)
262 87 : return m_poRootGroup->SerializeToXML(pszVRTPathIn);
263 :
264 : /* -------------------------------------------------------------------- */
265 : /* Setup root node and attributes. */
266 : /* -------------------------------------------------------------------- */
267 506 : CPLXMLNode *psDSTree = CPLCreateXMLNode(nullptr, CXT_Element, "VRTDataset");
268 :
269 506 : char szNumber[128] = {'\0'};
270 506 : snprintf(szNumber, sizeof(szNumber), "%d", GetRasterXSize());
271 506 : CPLSetXMLValue(psDSTree, "#rasterXSize", szNumber);
272 :
273 506 : snprintf(szNumber, sizeof(szNumber), "%d", GetRasterYSize());
274 506 : CPLSetXMLValue(psDSTree, "#rasterYSize", szNumber);
275 :
276 : /* -------------------------------------------------------------------- */
277 : /* SRS */
278 : /* -------------------------------------------------------------------- */
279 506 : if (m_poSRS && !m_poSRS->IsEmpty())
280 : {
281 354 : char *pszWKT = nullptr;
282 354 : m_poSRS->exportToWkt(&pszWKT);
283 : CPLXMLNode *psSRSNode =
284 354 : CPLCreateXMLElementAndValue(psDSTree, "SRS", pszWKT);
285 354 : CPLFree(pszWKT);
286 354 : const auto &mapping = m_poSRS->GetDataAxisToSRSAxisMapping();
287 708 : CPLString osMapping;
288 1064 : for (size_t i = 0; i < mapping.size(); ++i)
289 : {
290 710 : if (!osMapping.empty())
291 356 : osMapping += ",";
292 710 : osMapping += CPLSPrintf("%d", mapping[i]);
293 : }
294 354 : CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
295 : osMapping.c_str());
296 354 : const double dfCoordinateEpoch = m_poSRS->GetCoordinateEpoch();
297 354 : if (dfCoordinateEpoch > 0)
298 : {
299 2 : std::string osCoordinateEpoch = CPLSPrintf("%f", dfCoordinateEpoch);
300 1 : if (osCoordinateEpoch.find('.') != std::string::npos)
301 : {
302 6 : while (osCoordinateEpoch.back() == '0')
303 5 : osCoordinateEpoch.pop_back();
304 : }
305 1 : CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
306 : osCoordinateEpoch.c_str());
307 : }
308 : }
309 :
310 : /* -------------------------------------------------------------------- */
311 : /* Geotransform. */
312 : /* -------------------------------------------------------------------- */
313 506 : if (m_bGeoTransformSet)
314 : {
315 416 : CPLSetXMLValue(
316 : psDSTree, "GeoTransform",
317 : CPLSPrintf("%24.16e,%24.16e,%24.16e,%24.16e,%24.16e,%24.16e",
318 : m_gt.xorig, m_gt.xscale, m_gt.xrot, m_gt.yorig,
319 : m_gt.yrot, m_gt.yscale));
320 : }
321 :
322 : /* -------------------------------------------------------------------- */
323 : /* Metadata */
324 : /* -------------------------------------------------------------------- */
325 506 : CPLXMLNode *psMD = oMDMD.Serialize();
326 506 : if (psMD != nullptr)
327 : {
328 264 : CPLAddXMLChild(psDSTree, psMD);
329 : }
330 :
331 : /* -------------------------------------------------------------------- */
332 : /* GCPs */
333 : /* -------------------------------------------------------------------- */
334 506 : if (!m_asGCPs.empty())
335 : {
336 5 : GDALSerializeGCPListToXML(psDSTree, m_asGCPs, m_poGCP_SRS.get());
337 : }
338 :
339 : /* -------------------------------------------------------------------- */
340 : /* Serialize bands. */
341 : /* -------------------------------------------------------------------- */
342 506 : CPLXMLNode *psLastChild = psDSTree->psChild;
343 2203 : for (; psLastChild != nullptr && psLastChild->psNext;
344 1697 : psLastChild = psLastChild->psNext)
345 : {
346 : }
347 506 : CPLAssert(psLastChild); // we have at least rasterXSize
348 506 : bool bHasWarnedAboutRAMUsage = false;
349 506 : size_t nAccRAMUsage = 0;
350 1243 : for (int iBand = 0; iBand < nBands; iBand++)
351 : {
352 : CPLXMLNode *psBandTree =
353 737 : static_cast<VRTRasterBand *>(papoBands[iBand])
354 1474 : ->SerializeToXML(pszVRTPathIn, bHasWarnedAboutRAMUsage,
355 737 : nAccRAMUsage);
356 :
357 737 : if (psBandTree != nullptr)
358 : {
359 737 : psLastChild->psNext = psBandTree;
360 737 : psLastChild = psBandTree;
361 : }
362 : }
363 :
364 : /* -------------------------------------------------------------------- */
365 : /* Serialize dataset mask band. */
366 : /* -------------------------------------------------------------------- */
367 506 : if (m_poMaskBand)
368 : {
369 19 : CPLXMLNode *psBandTree = m_poMaskBand->SerializeToXML(
370 19 : pszVRTPathIn, bHasWarnedAboutRAMUsage, nAccRAMUsage);
371 :
372 19 : if (psBandTree != nullptr)
373 : {
374 : CPLXMLNode *psMaskBandElement =
375 19 : CPLCreateXMLNode(psDSTree, CXT_Element, "MaskBand");
376 19 : CPLAddXMLChild(psMaskBandElement, psBandTree);
377 : }
378 : }
379 :
380 : /* -------------------------------------------------------------------- */
381 : /* Overview factors. */
382 : /* -------------------------------------------------------------------- */
383 506 : if (!m_anOverviewFactors.empty())
384 : {
385 10 : CPLString osOverviewList;
386 16 : for (int nOvFactor : m_anOverviewFactors)
387 : {
388 11 : if (!osOverviewList.empty())
389 6 : osOverviewList += " ";
390 11 : osOverviewList += CPLSPrintf("%d", nOvFactor);
391 : }
392 5 : CPLXMLNode *psOverviewList = CPLCreateXMLElementAndValue(
393 : psDSTree, "OverviewList", osOverviewList);
394 5 : if (!m_osOverviewResampling.empty())
395 : {
396 5 : CPLAddXMLAttributeAndValue(psOverviewList, "resampling",
397 : m_osOverviewResampling);
398 : }
399 : }
400 :
401 506 : return psDSTree;
402 : }
403 :
404 : /*! @endcond */
405 : /************************************************************************/
406 : /* VRTSerializeToXML() */
407 : /************************************************************************/
408 :
409 : /**
410 : * @see VRTDataset::SerializeToXML()
411 : */
412 :
413 0 : CPLXMLNode *CPL_STDCALL VRTSerializeToXML(VRTDatasetH hDataset,
414 : const char *pszVRTPath)
415 : {
416 0 : VALIDATE_POINTER1(hDataset, "VRTSerializeToXML", nullptr);
417 :
418 0 : return static_cast<VRTDataset *>(GDALDataset::FromHandle(hDataset))
419 0 : ->SerializeToXML(pszVRTPath);
420 : }
421 :
422 : /*! @cond Doxygen_Suppress */
423 :
424 : /************************************************************************/
425 : /* IsRawRasterBandEnabled() */
426 : /************************************************************************/
427 :
428 : /** Return whether VRTRawRasterBand support is enabled */
429 :
430 : /* static */
431 78 : bool VRTDataset::IsRawRasterBandEnabled()
432 : {
433 : #ifdef GDAL_VRT_ENABLE_RAWRASTERBAND
434 78 : if (CPLTestBool(CPLGetConfigOption("GDAL_VRT_ENABLE_RAWRASTERBAND", "YES")))
435 : {
436 76 : return true;
437 : }
438 : else
439 : {
440 2 : CPLError(CE_Failure, CPLE_NotSupported,
441 : "VRTRawRasterBand support has been disabled at run-time.");
442 : }
443 2 : return false;
444 : #else
445 : CPLError(CE_Failure, CPLE_NotSupported,
446 : "VRTRawRasterBand is disabled in this GDAL build");
447 : return false;
448 : #endif
449 : }
450 :
451 : /************************************************************************/
452 : /* InitBand() */
453 : /************************************************************************/
454 :
455 : std::unique_ptr<VRTRasterBand>
456 2954 : VRTDataset::InitBand(const char *pszSubclass, int nBand,
457 : bool bAllowPansharpenedOrProcessed)
458 : {
459 2954 : if (auto poProcessedDS = dynamic_cast<VRTProcessedDataset *>(this))
460 : {
461 39 : if (bAllowPansharpenedOrProcessed &&
462 39 : EQUAL(pszSubclass, "VRTProcessedRasterBand"))
463 : {
464 78 : return std::make_unique<VRTProcessedRasterBand>(poProcessedDS,
465 39 : nBand);
466 : }
467 : }
468 2915 : else if (EQUAL(pszSubclass, "VRTSourcedRasterBand"))
469 1045 : return std::make_unique<VRTSourcedRasterBand>(this, nBand);
470 1870 : else if (EQUAL(pszSubclass, "VRTDerivedRasterBand"))
471 1498 : return std::make_unique<VRTDerivedRasterBand>(this, nBand);
472 372 : else if (EQUAL(pszSubclass, "VRTRawRasterBand"))
473 : {
474 : #ifdef GDAL_VRT_ENABLE_RAWRASTERBAND
475 33 : if (!VRTDataset::IsRawRasterBandEnabled())
476 : {
477 1 : return nullptr;
478 : }
479 32 : return std::make_unique<VRTRawRasterBand>(this, nBand);
480 : #else
481 : CPLError(CE_Failure, CPLE_NotSupported,
482 : "VRTDataset::InitBand(): cannot instantiate VRTRawRasterBand, "
483 : "because disabled in this GDAL build");
484 : return nullptr;
485 : #endif
486 : }
487 597 : else if (EQUAL(pszSubclass, "VRTWarpedRasterBand") &&
488 258 : dynamic_cast<VRTWarpedDataset *>(this) != nullptr)
489 : {
490 258 : return std::make_unique<VRTWarpedRasterBand>(this, nBand);
491 : }
492 81 : else if (bAllowPansharpenedOrProcessed &&
493 161 : EQUAL(pszSubclass, "VRTPansharpenedRasterBand") &&
494 80 : dynamic_cast<VRTPansharpenedDataset *>(this) != nullptr)
495 : {
496 80 : return std::make_unique<VRTPansharpenedRasterBand>(this, nBand);
497 : }
498 :
499 1 : CPLError(CE_Failure, CPLE_AppDefined,
500 : "VRTRasterBand of unrecognized subclass '%s'.", pszSubclass);
501 1 : return nullptr;
502 : }
503 :
504 : /************************************************************************/
505 : /* XMLInit() */
506 : /************************************************************************/
507 :
508 2794 : CPLErr VRTDataset::XMLInit(const CPLXMLNode *psTree, const char *pszVRTPathIn)
509 :
510 : {
511 2794 : if (pszVRTPathIn != nullptr)
512 1173 : m_pszVRTPath = CPLStrdup(pszVRTPathIn);
513 :
514 : /* -------------------------------------------------------------------- */
515 : /* Check for an SRS node. */
516 : /* -------------------------------------------------------------------- */
517 2794 : const CPLXMLNode *psSRSNode = CPLGetXMLNode(psTree, "SRS");
518 2794 : if (psSRSNode)
519 : {
520 544 : m_poSRS = OGRSpatialReferenceRefCountedPtr::makeInstance();
521 544 : m_poSRS->SetFromUserInput(
522 : CPLGetXMLValue(psSRSNode, nullptr, ""),
523 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
524 : const char *pszMapping =
525 544 : CPLGetXMLValue(psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
526 544 : if (pszMapping)
527 : {
528 : char **papszTokens =
529 314 : CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
530 628 : std::vector<int> anMapping;
531 945 : for (int i = 0; papszTokens && papszTokens[i]; i++)
532 : {
533 631 : anMapping.push_back(atoi(papszTokens[i]));
534 : }
535 314 : CSLDestroy(papszTokens);
536 314 : m_poSRS->SetDataAxisToSRSAxisMapping(anMapping);
537 : }
538 : else
539 : {
540 230 : m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
541 : }
542 :
543 : const char *pszCoordinateEpoch =
544 544 : CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
545 544 : if (pszCoordinateEpoch)
546 1 : m_poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
547 : }
548 :
549 : /* -------------------------------------------------------------------- */
550 : /* Check for a GeoTransform node. */
551 : /* -------------------------------------------------------------------- */
552 2794 : const char *pszGT = CPLGetXMLValue(psTree, "GeoTransform", "");
553 2794 : if (strlen(pszGT) > 0)
554 : {
555 621 : if (m_gt.Init(pszGT, ","))
556 : {
557 621 : m_bGeoTransformSet = true;
558 : }
559 : else
560 : {
561 0 : CPLError(CE_Warning, CPLE_AppDefined, "Invalid GeoTransform");
562 : }
563 : }
564 :
565 : /* -------------------------------------------------------------------- */
566 : /* Check for GCPs. */
567 : /* -------------------------------------------------------------------- */
568 2794 : if (const CPLXMLNode *psGCPList = CPLGetXMLNode(psTree, "GCPList"))
569 : {
570 46 : OGRSpatialReference *poSRS = nullptr;
571 46 : GDALDeserializeGCPListFromXML(psGCPList, m_asGCPs, &poSRS);
572 46 : m_poGCP_SRS.reset(poSRS, /* add_ref = */ false);
573 : }
574 :
575 : /* -------------------------------------------------------------------- */
576 : /* Apply any dataset level metadata. */
577 : /* -------------------------------------------------------------------- */
578 2794 : oMDMD.XMLInit(psTree, TRUE);
579 :
580 : /* -------------------------------------------------------------------- */
581 : /* Create dataset mask band. */
582 : /* -------------------------------------------------------------------- */
583 :
584 : /* Parse dataset mask band first */
585 2794 : const CPLXMLNode *psMaskBandNode = CPLGetXMLNode(psTree, "MaskBand");
586 :
587 2794 : const CPLXMLNode *psChild = nullptr;
588 2794 : if (psMaskBandNode)
589 28 : psChild = psMaskBandNode->psChild;
590 : else
591 2766 : psChild = nullptr;
592 :
593 2794 : for (; psChild != nullptr; psChild = psChild->psNext)
594 : {
595 28 : if (psChild->eType == CXT_Element &&
596 28 : EQUAL(psChild->pszValue, "VRTRasterBand"))
597 : {
598 : const char *pszSubclass =
599 28 : CPLGetXMLValue(psChild, "subclass", "VRTSourcedRasterBand");
600 :
601 28 : auto poBand = InitBand(pszSubclass, 0, false);
602 56 : if (poBand != nullptr &&
603 28 : poBand->XMLInit(psChild, pszVRTPathIn, m_oMapSharedSources) ==
604 : CE_None)
605 : {
606 28 : SetMaskBand(std::move(poBand));
607 28 : break;
608 : }
609 : else
610 : {
611 0 : return CE_Failure;
612 : }
613 : }
614 : }
615 :
616 : /* -------------------------------------------------------------------- */
617 : /* Create band information objects. */
618 : /* -------------------------------------------------------------------- */
619 2794 : int l_nBands = 0;
620 13218 : for (psChild = psTree->psChild; psChild != nullptr;
621 10424 : psChild = psChild->psNext)
622 : {
623 10468 : if (psChild->eType == CXT_Element &&
624 5596 : EQUAL(psChild->pszValue, "VRTRasterBand"))
625 : {
626 : const char *pszSubclass =
627 2927 : CPLGetXMLValue(psChild, "subclass", "VRTSourcedRasterBand");
628 2927 : if (dynamic_cast<VRTProcessedDataset *>(this) &&
629 39 : !EQUAL(pszSubclass, "VRTProcessedRasterBand"))
630 : {
631 0 : CPLError(CE_Failure, CPLE_NotSupported,
632 : "Only subClass=VRTProcessedRasterBand supported");
633 44 : return CE_Failure;
634 : }
635 :
636 4424 : if (CPLGetXMLNode(psChild, "PixelFunctionType") != nullptr &&
637 1497 : !EQUAL(pszSubclass, "VRTDerivedRasterBand"))
638 : {
639 1 : CPLError(CE_Failure, CPLE_NotSupported,
640 : "Pixel functions may only be used with "
641 : "subClass=VRTDerivedRasterBand");
642 1 : return CE_Failure;
643 : }
644 :
645 2926 : auto poBand = InitBand(pszSubclass, l_nBands + 1, true);
646 5850 : if (poBand != nullptr &&
647 2924 : poBand->XMLInit(psChild, pszVRTPathIn, m_oMapSharedSources) ==
648 : CE_None)
649 : {
650 2883 : l_nBands++;
651 2883 : SetBand(l_nBands, std::move(poBand));
652 : }
653 : else
654 : {
655 43 : return CE_Failure;
656 : }
657 : }
658 : }
659 :
660 2750 : if (const CPLXMLNode *psGroup = CPLGetXMLNode(psTree, "Group"))
661 : {
662 255 : const char *pszName = CPLGetXMLValue(psGroup, "name", nullptr);
663 255 : if (pszName == nullptr || !EQUAL(pszName, "/"))
664 : {
665 2 : CPLError(CE_Failure, CPLE_AppDefined,
666 : "Missing name or not equal to '/'");
667 2 : return CE_Failure;
668 : }
669 :
670 253 : m_poRootGroup = VRTGroup::Create(std::string(), "/");
671 253 : m_poRootGroup->SetIsRootGroup();
672 253 : if (!m_poRootGroup->XMLInit(m_poRootGroup, m_poRootGroup, psGroup,
673 : pszVRTPathIn))
674 : {
675 22 : return CE_Failure;
676 : }
677 : }
678 :
679 : /* -------------------------------------------------------------------- */
680 : /* Create virtual overviews. */
681 : /* -------------------------------------------------------------------- */
682 2726 : const char *pszSubClass = CPLGetXMLValue(psTree, "subClass", "");
683 2726 : if (EQUAL(pszSubClass, ""))
684 : {
685 : m_aosOverviewList =
686 2404 : CSLTokenizeString(CPLGetXMLValue(psTree, "OverviewList", ""));
687 : m_osOverviewResampling =
688 2404 : CPLGetXMLValue(psTree, "OverviewList.resampling", "");
689 : }
690 :
691 2726 : return CE_None;
692 : }
693 :
694 : /************************************************************************/
695 : /* GetGCPCount() */
696 : /************************************************************************/
697 :
698 2425 : int VRTDataset::GetGCPCount()
699 :
700 : {
701 2425 : return static_cast<int>(m_asGCPs.size());
702 : }
703 :
704 : /************************************************************************/
705 : /* GetGCPs() */
706 : /************************************************************************/
707 :
708 77 : const GDAL_GCP *VRTDataset::GetGCPs()
709 :
710 : {
711 77 : return gdal::GCP::c_ptr(m_asGCPs);
712 : }
713 :
714 : /************************************************************************/
715 : /* SetGCPs() */
716 : /************************************************************************/
717 :
718 35 : CPLErr VRTDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
719 : const OGRSpatialReference *poGCP_SRS)
720 :
721 : {
722 35 : m_poGCP_SRS = OGRSpatialReferenceRefCountedPtr::makeClone(poGCP_SRS);
723 35 : m_asGCPs = gdal::GCP::fromC(pasGCPListIn, nGCPCountIn);
724 :
725 35 : SetNeedsFlush();
726 :
727 35 : return CE_None;
728 : }
729 :
730 : /************************************************************************/
731 : /* SetSpatialRef() */
732 : /************************************************************************/
733 :
734 2821 : CPLErr VRTDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
735 :
736 : {
737 2821 : m_poSRS = OGRSpatialReferenceRefCountedPtr::makeClone(poSRS);
738 :
739 2821 : SetNeedsFlush();
740 :
741 2821 : return CE_None;
742 : }
743 :
744 : /************************************************************************/
745 : /* SetGeoTransform() */
746 : /************************************************************************/
747 :
748 3005 : CPLErr VRTDataset::SetGeoTransform(const GDALGeoTransform >)
749 :
750 : {
751 3005 : m_gt = gt;
752 3005 : m_bGeoTransformSet = TRUE;
753 :
754 3005 : SetNeedsFlush();
755 :
756 3005 : return CE_None;
757 : }
758 :
759 : /************************************************************************/
760 : /* GetGeoTransform() */
761 : /************************************************************************/
762 :
763 8586 : CPLErr VRTDataset::GetGeoTransform(GDALGeoTransform >) const
764 :
765 : {
766 8586 : gt = m_gt;
767 :
768 8586 : return m_bGeoTransformSet ? CE_None : CE_Failure;
769 : }
770 :
771 : /************************************************************************/
772 : /* SetMetadata() */
773 : /************************************************************************/
774 :
775 2529 : CPLErr VRTDataset::SetMetadata(CSLConstList papszMetadata,
776 : const char *pszDomain)
777 :
778 : {
779 2529 : SetNeedsFlush();
780 :
781 2529 : return GDALDataset::SetMetadata(papszMetadata, pszDomain);
782 : }
783 :
784 : /************************************************************************/
785 : /* SetMetadataItem() */
786 : /************************************************************************/
787 :
788 2583 : CPLErr VRTDataset::SetMetadataItem(const char *pszName, const char *pszValue,
789 : const char *pszDomain)
790 :
791 : {
792 2583 : SetNeedsFlush();
793 :
794 2583 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
795 : }
796 :
797 : /************************************************************************/
798 : /* Identify() */
799 : /************************************************************************/
800 :
801 77011 : int VRTDataset::Identify(GDALOpenInfo *poOpenInfo)
802 :
803 : {
804 77011 : if (poOpenInfo->nHeaderBytes > 20 &&
805 12676 : strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
806 : "<VRTDataset") != nullptr)
807 2135 : return TRUE;
808 :
809 74876 : if (strstr(poOpenInfo->pszFilename, "<VRTDataset") != nullptr)
810 3255 : return TRUE;
811 :
812 71621 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, VRT_PROTOCOL_PREFIX))
813 202 : return TRUE;
814 :
815 71419 : return FALSE;
816 : }
817 :
818 : /************************************************************************/
819 : /* Open() */
820 : /************************************************************************/
821 :
822 2779 : GDALDataset *VRTDataset::Open(GDALOpenInfo *poOpenInfo)
823 :
824 : {
825 : /* -------------------------------------------------------------------- */
826 : /* Does this appear to be a virtual dataset definition XML */
827 : /* file? */
828 : /* -------------------------------------------------------------------- */
829 2779 : if (!Identify(poOpenInfo))
830 0 : return nullptr;
831 :
832 2779 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, VRT_PROTOCOL_PREFIX))
833 101 : return OpenVRTProtocol(poOpenInfo->pszFilename);
834 :
835 : /* -------------------------------------------------------------------- */
836 : /* Try to read the whole file into memory. */
837 : /* -------------------------------------------------------------------- */
838 2678 : char *pszXML = nullptr;
839 2678 : VSILFILE *fp = poOpenInfo->fpL;
840 :
841 2678 : char *pszVRTPath = nullptr;
842 2678 : if (fp != nullptr)
843 : {
844 1051 : poOpenInfo->fpL = nullptr;
845 :
846 1051 : GByte *pabyOut = nullptr;
847 1051 : if (!VSIIngestFile(fp, poOpenInfo->pszFilename, &pabyOut, nullptr,
848 : INT_MAX - 1))
849 : {
850 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
851 0 : return nullptr;
852 : }
853 1051 : pszXML = reinterpret_cast<char *>(pabyOut);
854 :
855 1051 : char *pszCurDir = CPLGetCurrentDir();
856 : std::string currentVrtFilename =
857 1051 : CPLProjectRelativeFilenameSafe(pszCurDir, poOpenInfo->pszFilename);
858 1051 : CPLString osInitialCurrentVrtFilename(currentVrtFilename);
859 1051 : CPLFree(pszCurDir);
860 :
861 : #if !defined(_WIN32)
862 : char filenameBuffer[2048];
863 :
864 : while (true)
865 : {
866 : VSIStatBuf statBuffer;
867 1055 : int lstatCode = lstat(currentVrtFilename.c_str(), &statBuffer);
868 1055 : if (lstatCode == -1)
869 : {
870 305 : if (errno == ENOENT)
871 : {
872 : // File could be a virtual file, let later checks handle it.
873 305 : break;
874 : }
875 : else
876 : {
877 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
878 0 : CPLFree(pszXML);
879 0 : CPLError(CE_Failure, CPLE_FileIO, "Failed to lstat %s: %s",
880 0 : currentVrtFilename.c_str(), VSIStrerror(errno));
881 0 : return nullptr;
882 : }
883 : }
884 :
885 750 : if (!VSI_ISLNK(statBuffer.st_mode))
886 : {
887 746 : break;
888 : }
889 :
890 : const int bufferSize = static_cast<int>(
891 4 : readlink(currentVrtFilename.c_str(), filenameBuffer,
892 4 : sizeof(filenameBuffer)));
893 4 : if (bufferSize != -1)
894 : {
895 4 : filenameBuffer[std::min(
896 4 : bufferSize, static_cast<int>(sizeof(filenameBuffer)) - 1)] =
897 : 0;
898 : // The filename in filenameBuffer might be a relative path
899 : // from the linkfile resolve it before looping
900 8 : currentVrtFilename = CPLProjectRelativeFilenameSafe(
901 8 : CPLGetDirnameSafe(currentVrtFilename.c_str()).c_str(),
902 4 : filenameBuffer);
903 : }
904 : else
905 : {
906 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
907 0 : CPLFree(pszXML);
908 0 : CPLError(CE_Failure, CPLE_FileIO,
909 : "Failed to read filename from symlink %s: %s",
910 0 : currentVrtFilename.c_str(), VSIStrerror(errno));
911 0 : return nullptr;
912 : }
913 4 : }
914 : #endif // !defined(__WIN32)
915 :
916 1051 : if (osInitialCurrentVrtFilename == currentVrtFilename)
917 : pszVRTPath =
918 1048 : CPLStrdup(CPLGetPathSafe(poOpenInfo->pszFilename).c_str());
919 : else
920 : pszVRTPath =
921 3 : CPLStrdup(CPLGetPathSafe(currentVrtFilename.c_str()).c_str());
922 :
923 1051 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
924 : }
925 : /* -------------------------------------------------------------------- */
926 : /* Or use the filename as the XML input. */
927 : /* -------------------------------------------------------------------- */
928 : else
929 : {
930 1627 : pszXML = CPLStrdup(poOpenInfo->pszFilename);
931 : }
932 :
933 2678 : if (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "ROOT_PATH") != nullptr)
934 : {
935 5 : CPLFree(pszVRTPath);
936 5 : pszVRTPath = CPLStrdup(
937 5 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "ROOT_PATH"));
938 : }
939 :
940 : /* -------------------------------------------------------------------- */
941 : /* Turn the XML representation into a VRTDataset. */
942 : /* -------------------------------------------------------------------- */
943 5356 : auto poDS = OpenXML(pszXML, pszVRTPath, poOpenInfo->eAccess);
944 :
945 2678 : if (poDS != nullptr)
946 2542 : poDS->m_bNeedsFlush = false;
947 :
948 2678 : if (poDS != nullptr)
949 : {
950 2542 : if (poDS->GetRasterCount() == 0 &&
951 2543 : (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER) == 0 &&
952 1 : strstr(pszXML, "VRTPansharpenedDataset") == nullptr)
953 : {
954 0 : poDS.reset();
955 : }
956 5084 : else if (poDS->GetRootGroup() == nullptr &&
957 5084 : (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
958 0 : (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER) != 0)
959 : {
960 0 : poDS.reset();
961 : }
962 : }
963 :
964 2678 : CPLFree(pszXML);
965 2678 : CPLFree(pszVRTPath);
966 :
967 : /* -------------------------------------------------------------------- */
968 : /* Initialize info for later overview discovery. */
969 : /* -------------------------------------------------------------------- */
970 :
971 2678 : if (poDS != nullptr)
972 : {
973 2542 : if (fp != nullptr)
974 : {
975 1038 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
976 1038 : if (poOpenInfo->AreSiblingFilesLoaded())
977 2 : poDS->oOvManager.TransferSiblingFiles(
978 : poOpenInfo->StealSiblingFiles());
979 : }
980 :
981 : // Creating virtual overviews, but only if there is no higher priority
982 : // overview source, ie. a Overview element at VRT band level,
983 : // or external .vrt.ovr
984 2542 : if (!poDS->m_aosOverviewList.empty())
985 : {
986 8 : if (poDS->nBands > 0)
987 : {
988 8 : auto poBand = dynamic_cast<VRTRasterBand *>(poDS->papoBands[0]);
989 8 : if (poBand && !poBand->m_aoOverviewInfos.empty())
990 : {
991 0 : poDS->m_aosOverviewList.Clear();
992 0 : CPLDebug("VRT",
993 : "Ignoring virtual overviews of OverviewList "
994 : "because Overview element is present on VRT band");
995 : }
996 16 : else if (poBand &&
997 8 : poBand->GDALRasterBand::GetOverviewCount() > 0)
998 : {
999 1 : poDS->m_aosOverviewList.Clear();
1000 1 : CPLDebug("VRT",
1001 : "Ignoring virtual overviews of OverviewList "
1002 : "because external .vrt.ovr is available");
1003 : }
1004 : }
1005 21 : for (int iOverview = 0; iOverview < poDS->m_aosOverviewList.size();
1006 : iOverview++)
1007 : {
1008 13 : const int nOvFactor = atoi(poDS->m_aosOverviewList[iOverview]);
1009 13 : if (nOvFactor <= 1)
1010 : {
1011 0 : CPLError(CE_Failure, CPLE_AppDefined,
1012 : "Invalid overview factor");
1013 0 : return nullptr;
1014 : }
1015 :
1016 38 : poDS->AddVirtualOverview(
1017 13 : nOvFactor, poDS->m_osOverviewResampling.empty()
1018 : ? "nearest"
1019 12 : : poDS->m_osOverviewResampling.c_str());
1020 : }
1021 8 : poDS->m_aosOverviewList.Clear();
1022 : }
1023 :
1024 2630 : if (poDS->eAccess == GA_Update && poDS->m_poRootGroup &&
1025 88 : !STARTS_WITH_CI(poOpenInfo->pszFilename, "<VRT"))
1026 : {
1027 87 : poDS->m_poRootGroup->SetFilename(poOpenInfo->pszFilename);
1028 : }
1029 : }
1030 :
1031 2678 : return poDS.release();
1032 : }
1033 :
1034 : /************************************************************************/
1035 : /* OpenVRTProtocol() */
1036 : /* */
1037 : /* Create an open VRTDataset from a vrt:// string. */
1038 : /************************************************************************/
1039 :
1040 101 : GDALDataset *VRTDataset::OpenVRTProtocol(const char *pszSpec)
1041 :
1042 : {
1043 101 : CPLAssert(STARTS_WITH_CI(pszSpec, VRT_PROTOCOL_PREFIX));
1044 202 : CPLString osFilename(pszSpec + strlen(VRT_PROTOCOL_PREFIX));
1045 101 : const auto nPosQuotationMark = osFilename.find('?');
1046 202 : CPLString osQueryString;
1047 101 : if (nPosQuotationMark != std::string::npos)
1048 : {
1049 91 : osQueryString = osFilename.substr(nPosQuotationMark + 1);
1050 91 : osFilename.resize(nPosQuotationMark);
1051 : }
1052 :
1053 : // Parse query string, get args required for initial Open()
1054 202 : const CPLStringList aosTokens(CSLTokenizeString2(osQueryString, "&", 0));
1055 202 : CPLStringList aosAllowedDrivers;
1056 202 : CPLStringList aosOpenOptions;
1057 :
1058 232 : for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(
1059 329 : aosTokens, /* bReturnNullKeyIfNotNameValue = */ true))
1060 : {
1061 118 : if (!pszKey)
1062 : {
1063 2 : CPLError(CE_Failure, CPLE_NotSupported,
1064 : "Invalid option specification: %s\n"
1065 : "must be in the form 'key=value'",
1066 : pszValue);
1067 4 : return nullptr;
1068 : }
1069 116 : else if (EQUAL(pszKey, "if"))
1070 : {
1071 4 : if (!aosAllowedDrivers.empty())
1072 : {
1073 1 : CPLError(CE_Failure, CPLE_IllegalArg,
1074 : "'if' option should be specified once, use commas "
1075 : "to input multiple values.");
1076 1 : return nullptr;
1077 : }
1078 3 : aosAllowedDrivers = CSLTokenizeString2(pszValue, ",", 0);
1079 : }
1080 112 : else if (EQUAL(pszKey, "oo"))
1081 : {
1082 3 : if (!aosOpenOptions.empty())
1083 : {
1084 1 : CPLError(CE_Failure, CPLE_IllegalArg,
1085 : "'oo' option should be specified once, use commas "
1086 : "to input multiple values.");
1087 1 : return nullptr;
1088 : }
1089 2 : aosOpenOptions = CSLTokenizeString2(pszValue, ",", 0);
1090 : }
1091 : }
1092 :
1093 : // We don't open in GDAL_OF_SHARED mode to avoid issues when we open a
1094 : // http://.jp2 file with the JP2OpenJPEG driver through the HTTP driver,
1095 : // which returns a /vsimem/ file
1096 : auto poSrcDS = std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>(
1097 : GDALDataset::Open(osFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
1098 97 : aosAllowedDrivers.List(), aosOpenOptions.List(),
1099 194 : nullptr));
1100 97 : if (poSrcDS == nullptr)
1101 : {
1102 4 : return nullptr;
1103 : }
1104 :
1105 93 : bool bFound_transpose = false;
1106 203 : for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(aosTokens))
1107 : {
1108 110 : if (EQUAL(pszKey, "transpose"))
1109 : {
1110 4 : bFound_transpose = true;
1111 : const CPLStringList aosTransposeTokens(
1112 4 : CSLTokenizeString2(pszValue, ":", 0));
1113 4 : if (aosTransposeTokens.size() != 2)
1114 : {
1115 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1116 : "Invalid transpose option: %s", pszValue);
1117 0 : return nullptr;
1118 : }
1119 : const CPLStringList aosTransposeIndex(
1120 4 : CSLTokenizeString2(aosTransposeTokens[1], ",", 0));
1121 : // fail if not two values
1122 4 : if (aosTransposeIndex.size() != 2)
1123 : {
1124 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1125 : "Invalid transpose option: %s", pszValue);
1126 0 : return nullptr;
1127 : }
1128 4 : int index_x = atoi(aosTransposeIndex[0]);
1129 4 : int index_y = atoi(aosTransposeIndex[1]);
1130 :
1131 : auto poMDimDS = std::unique_ptr<GDALDataset>(
1132 4 : GDALDataset::Open(osFilename, GDAL_OF_MULTIDIM_RASTER));
1133 4 : if (!poMDimDS)
1134 0 : return nullptr;
1135 4 : auto poMdimGroup = poMDimDS->GetRootGroup();
1136 4 : if (!poMdimGroup)
1137 : {
1138 0 : return nullptr;
1139 : }
1140 : auto poArray =
1141 8 : poMdimGroup->OpenMDArrayFromFullname(aosTransposeTokens[0]);
1142 4 : if (!poArray)
1143 : {
1144 0 : return nullptr;
1145 : }
1146 :
1147 4 : auto poClassicDS = poArray->AsClassicDataset(index_x, index_y);
1148 :
1149 4 : if (!poClassicDS)
1150 0 : return nullptr;
1151 : poSrcDS =
1152 8 : std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>(
1153 4 : poClassicDS);
1154 : }
1155 : }
1156 : // scan for sd_name/sd in tokens, close the source dataset and reopen if found/valid
1157 93 : bool bFound_subdataset = false;
1158 196 : for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(aosTokens))
1159 : {
1160 108 : if (EQUAL(pszKey, "sd_name"))
1161 : {
1162 5 : if (bFound_transpose)
1163 : {
1164 1 : CPLError(CE_Failure, CPLE_IllegalArg,
1165 : "'sd_name' is mutually exclusive with option "
1166 : "'transpose'");
1167 5 : return nullptr;
1168 : }
1169 4 : if (bFound_subdataset)
1170 : {
1171 1 : CPLError(CE_Failure, CPLE_IllegalArg,
1172 : "'sd_name' is mutually exclusive with option "
1173 : "'sd'");
1174 1 : return nullptr;
1175 : }
1176 : CSLConstList papszSubdatasets =
1177 3 : poSrcDS->GetMetadata(GDAL_MDD_SUBDATASETS);
1178 3 : int nSubdatasets = CSLCount(papszSubdatasets);
1179 :
1180 3 : if (nSubdatasets > 0)
1181 : {
1182 3 : bool bFound = false;
1183 25 : for (int j = 0; j < nSubdatasets && papszSubdatasets[j]; j += 2)
1184 : {
1185 24 : const char *pszEqual = strchr(papszSubdatasets[j], '=');
1186 24 : if (!pszEqual)
1187 : {
1188 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1189 : "'sd_name:' failed to obtain "
1190 : "subdataset string ");
1191 0 : return nullptr;
1192 : }
1193 24 : const char *pszSubdatasetSource = pszEqual + 1;
1194 : GDALSubdatasetInfoH info =
1195 24 : GDALGetSubdatasetInfo(pszSubdatasetSource);
1196 : char *component =
1197 24 : info ? GDALSubdatasetInfoGetSubdatasetComponent(info)
1198 24 : : nullptr;
1199 :
1200 24 : bFound = component && EQUAL(pszValue, component);
1201 24 : bFound_subdataset = true;
1202 24 : CPLFree(component);
1203 24 : GDALDestroySubdatasetInfo(info);
1204 24 : if (bFound)
1205 : {
1206 2 : poSrcDS.reset(GDALDataset::Open(
1207 : pszSubdatasetSource,
1208 : GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
1209 2 : aosAllowedDrivers.List(), aosOpenOptions.List(),
1210 : nullptr));
1211 2 : if (poSrcDS == nullptr)
1212 : {
1213 0 : return nullptr;
1214 : }
1215 :
1216 2 : break;
1217 : }
1218 : }
1219 :
1220 3 : if (!bFound)
1221 : {
1222 1 : CPLError(CE_Failure, CPLE_IllegalArg,
1223 : "'sd_name' option should be be a valid "
1224 : "subdataset component name");
1225 1 : return nullptr;
1226 : }
1227 : }
1228 : }
1229 :
1230 105 : if (EQUAL(pszKey, "sd"))
1231 : {
1232 4 : if (bFound_transpose)
1233 : {
1234 1 : CPLError(CE_Failure, CPLE_IllegalArg,
1235 : "'sd' is mutually exclusive with option "
1236 : "'transpose'");
1237 1 : return nullptr;
1238 : }
1239 3 : if (bFound_subdataset)
1240 : {
1241 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1242 : "'sd' is mutually exclusive with option "
1243 : "'sd_name'");
1244 0 : return nullptr;
1245 : }
1246 : CSLConstList papszSubdatasets =
1247 3 : poSrcDS->GetMetadata(GDAL_MDD_SUBDATASETS);
1248 3 : int nSubdatasets = CSLCount(papszSubdatasets);
1249 :
1250 3 : if (nSubdatasets > 0)
1251 : {
1252 3 : int iSubdataset = atoi(pszValue);
1253 3 : if (iSubdataset < 1 || iSubdataset > (nSubdatasets) / 2)
1254 : {
1255 1 : CPLError(CE_Failure, CPLE_IllegalArg,
1256 : "'sd' option should indicate a valid "
1257 : "subdataset component number (starting with 1)");
1258 1 : return nullptr;
1259 : }
1260 : const std::string osSubdatasetSource(
1261 2 : strstr(papszSubdatasets[(iSubdataset - 1) * 2], "=") + 1);
1262 2 : if (osSubdatasetSource.empty())
1263 : {
1264 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1265 : "'sd:' failed to obtain subdataset "
1266 : "string ");
1267 0 : return nullptr;
1268 : }
1269 :
1270 2 : poSrcDS.reset(GDALDataset::Open(
1271 : osSubdatasetSource.c_str(),
1272 : GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
1273 2 : aosAllowedDrivers.List(), aosOpenOptions.List(), nullptr));
1274 2 : if (poSrcDS == nullptr)
1275 : {
1276 0 : return nullptr;
1277 : }
1278 2 : bFound_subdataset = true;
1279 : }
1280 : }
1281 : }
1282 :
1283 176 : std::vector<int> anBands;
1284 :
1285 : // Check for 'block' option and validate/convert to srcwin
1286 88 : int nBlockXOff = -1;
1287 88 : int nBlockYOff = -1;
1288 88 : bool bFoundBlock = false;
1289 182 : for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(aosTokens))
1290 : {
1291 102 : if (EQUAL(pszKey, "block"))
1292 : {
1293 : const CPLStringList aosBlockParams(
1294 23 : CSLTokenizeString2(pszValue, ",", 0));
1295 23 : if (aosBlockParams.size() != 2)
1296 : {
1297 2 : CPLError(CE_Failure, CPLE_IllegalArg,
1298 : "Invalid block option: %s, must be two "
1299 : "values separated by comma nXBlockOff,nYBlockOff",
1300 : pszValue);
1301 2 : return nullptr;
1302 : }
1303 :
1304 21 : nBlockXOff = cpl::strict_parse<int>(aosBlockParams[0]).value_or(-1);
1305 21 : if (nBlockXOff < 0)
1306 : {
1307 3 : CPLError(CE_Failure, CPLE_IllegalArg,
1308 : "Invalid block option: nXBlockOff '%s' is not a valid "
1309 : "non-negative integer",
1310 : aosBlockParams[0]);
1311 3 : return nullptr;
1312 : }
1313 :
1314 18 : nBlockYOff = cpl::strict_parse<int>(aosBlockParams[1]).value_or(-1);
1315 18 : if (nBlockYOff < 0)
1316 : {
1317 3 : CPLError(CE_Failure, CPLE_IllegalArg,
1318 : "Invalid block option: nYBlockOff '%s' is not a valid "
1319 : "non-negative integer",
1320 : aosBlockParams[1]);
1321 3 : return nullptr;
1322 : }
1323 :
1324 15 : bFoundBlock = true;
1325 : }
1326 : }
1327 :
1328 : // Validate block indices and convert to pixel coordinates
1329 80 : int nBlockSrcWinXOff = 0;
1330 80 : int nBlockSrcWinYOff = 0;
1331 80 : int nBlockSrcWinXSize = 0;
1332 80 : int nBlockSrcWinYSize = 0;
1333 80 : if (bFoundBlock)
1334 : {
1335 : // Check mutual exclusivity with other spatial options
1336 31 : for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(aosTokens))
1337 : {
1338 22 : if (EQUAL(pszKey, "srcwin") || EQUAL(pszKey, "projwin") ||
1339 20 : EQUAL(pszKey, "outsize") || EQUAL(pszKey, "tr") ||
1340 18 : EQUAL(pszKey, "r") || EQUAL(pszKey, "ovr"))
1341 : {
1342 6 : CPLError(
1343 : CE_Failure, CPLE_IllegalArg,
1344 : "'block' is mutually exclusive with options "
1345 : "'srcwin', 'projwin', 'outsize', 'tr', 'r', and 'ovr'");
1346 6 : return nullptr;
1347 : }
1348 : }
1349 :
1350 : // Check that the dataset has at least one band
1351 9 : if (poSrcDS->GetRasterCount() == 0)
1352 : {
1353 0 : CPLError(CE_Failure, CPLE_NotSupported,
1354 : "'block' option requires a raster with at least one band");
1355 0 : return nullptr;
1356 : }
1357 :
1358 : // Validate using GetActualBlockSize which handles bounds checking
1359 9 : int nXValid = 0;
1360 9 : int nYValid = 0;
1361 9 : int nBlockXSize = 0;
1362 9 : int nBlockYSize = 0;
1363 9 : poSrcDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
1364 9 : if (poSrcDS->GetRasterBand(1)->GetActualBlockSize(
1365 9 : nBlockXOff, nBlockYOff, &nXValid, &nYValid) != CE_None)
1366 : {
1367 :
1368 : const int nMaxXBlock =
1369 2 : cpl::div_round_up(poSrcDS->GetRasterXSize(), nBlockXSize);
1370 : const int nMaxYBlock =
1371 2 : cpl::div_round_up(poSrcDS->GetRasterYSize(), nBlockYSize);
1372 2 : CPLError(CE_Failure, CPLE_IllegalArg,
1373 : "Invalid block indices: %d,%d. "
1374 : "Valid range is 0-%d for X and 0-%d for Y",
1375 : nBlockXOff, nBlockYOff, nMaxXBlock - 1, nMaxYBlock - 1);
1376 2 : return nullptr;
1377 : }
1378 :
1379 7 : nBlockSrcWinXOff = nBlockXOff * nBlockXSize;
1380 7 : nBlockSrcWinYOff = nBlockYOff * nBlockYSize;
1381 7 : nBlockSrcWinXSize = nXValid;
1382 7 : nBlockSrcWinYSize = nYValid;
1383 : }
1384 :
1385 144 : CPLStringList argv;
1386 72 : argv.AddString("-of");
1387 72 : argv.AddString("VRT");
1388 :
1389 : // Add srcwin from block option before processing other options
1390 72 : if (bFoundBlock)
1391 : {
1392 7 : argv.AddString("-srcwin");
1393 7 : argv.AddString(CPLSPrintf("%d", nBlockSrcWinXOff));
1394 7 : argv.AddString(CPLSPrintf("%d", nBlockSrcWinYOff));
1395 7 : argv.AddString(CPLSPrintf("%d", nBlockSrcWinXSize));
1396 7 : argv.AddString(CPLSPrintf("%d", nBlockSrcWinYSize));
1397 : }
1398 :
1399 142 : for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(aosTokens))
1400 : {
1401 80 : if (EQUAL(pszKey, "bands"))
1402 : {
1403 10 : const CPLStringList aosBands(CSLTokenizeString2(pszValue, ",", 0));
1404 23 : for (int j = 0; j < aosBands.size(); j++)
1405 : {
1406 16 : if (EQUAL(aosBands[j], "mask"))
1407 : {
1408 1 : anBands.push_back(0);
1409 : }
1410 : else
1411 : {
1412 15 : const int nBand = atoi(aosBands[j]);
1413 15 : if (nBand <= 0 || nBand > poSrcDS->GetRasterCount())
1414 : {
1415 3 : CPLError(CE_Failure, CPLE_IllegalArg,
1416 : "Invalid band number: %s", aosBands[j]);
1417 3 : return nullptr;
1418 : }
1419 12 : anBands.push_back(nBand);
1420 : }
1421 : }
1422 :
1423 20 : for (const int nBand : anBands)
1424 : {
1425 13 : argv.AddString("-b");
1426 13 : argv.AddString(nBand == 0 ? "mask" : CPLSPrintf("%d", nBand));
1427 : }
1428 : }
1429 :
1430 70 : else if (EQUAL(pszKey, "a_nodata"))
1431 : {
1432 1 : argv.AddString("-a_nodata");
1433 1 : argv.AddString(pszValue);
1434 : }
1435 :
1436 69 : else if (EQUAL(pszKey, "a_srs"))
1437 : {
1438 1 : argv.AddString("-a_srs");
1439 1 : argv.AddString(pszValue);
1440 : }
1441 :
1442 68 : else if (EQUAL(pszKey, "a_ullr"))
1443 : {
1444 : // Parse the limits
1445 1 : const CPLStringList aosUllr(CSLTokenizeString2(pszValue, ",", 0));
1446 : // fail if not four values
1447 1 : if (aosUllr.size() != 4)
1448 : {
1449 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1450 : "Invalid a_ullr option: %s", pszValue);
1451 0 : return nullptr;
1452 : }
1453 :
1454 1 : argv.AddString("-a_ullr");
1455 1 : argv.AddString(aosUllr[0]);
1456 1 : argv.AddString(aosUllr[1]);
1457 1 : argv.AddString(aosUllr[2]);
1458 1 : argv.AddString(aosUllr[3]);
1459 : }
1460 :
1461 67 : else if (EQUAL(pszKey, "ovr"))
1462 : {
1463 1 : argv.AddString("-ovr");
1464 1 : argv.AddString(pszValue);
1465 : }
1466 66 : else if (EQUAL(pszKey, "expand"))
1467 : {
1468 1 : argv.AddString("-expand");
1469 1 : argv.AddString(pszValue);
1470 : }
1471 65 : else if (EQUAL(pszKey, "a_scale"))
1472 : {
1473 2 : argv.AddString("-a_scale");
1474 2 : argv.AddString(pszValue);
1475 : }
1476 63 : else if (EQUAL(pszKey, "a_offset"))
1477 : {
1478 1 : argv.AddString("-a_offset");
1479 1 : argv.AddString(pszValue);
1480 : }
1481 62 : else if (EQUAL(pszKey, "ot"))
1482 : {
1483 2 : argv.AddString("-ot");
1484 2 : argv.AddString(pszValue);
1485 : }
1486 60 : else if (EQUAL(pszKey, "gcp"))
1487 : {
1488 6 : const CPLStringList aosGCP(CSLTokenizeString2(pszValue, ",", 0));
1489 :
1490 6 : if (aosGCP.size() < 4 || aosGCP.size() > 5)
1491 : {
1492 1 : CPLError(CE_Failure, CPLE_IllegalArg,
1493 : "Invalid value for GCP: %s\n need 4, or 5 "
1494 : "numbers, comma separated: "
1495 : "'gcp=<pixel>,<line>,<easting>,<northing>[,<"
1496 : "elevation>]'",
1497 : pszValue);
1498 1 : return nullptr;
1499 : }
1500 5 : argv.AddString("-gcp");
1501 28 : for (int j = 0; j < aosGCP.size(); j++)
1502 : {
1503 23 : argv.AddString(aosGCP[j]);
1504 : }
1505 : }
1506 54 : else if (EQUAL(pszKey, "scale") || STARTS_WITH_CI(pszKey, "scale_"))
1507 : {
1508 : const CPLStringList aosScaleParams(
1509 7 : CSLTokenizeString2(pszValue, ",", 0));
1510 :
1511 7 : if (!(aosScaleParams.size() == 2) &&
1512 7 : !(aosScaleParams.size() == 4) && !(aosScaleParams.size() == 1))
1513 : {
1514 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1515 : "Invalid value for scale, (or scale_bn): "
1516 : "%s\n need 'scale=true', or 2 or 4 "
1517 : "numbers, comma separated: "
1518 : "'scale=src_min,src_max[,dst_min,dst_max]' or "
1519 : "'scale_bn=src_min,src_max[,dst_min,dst_max]'",
1520 : pszValue);
1521 0 : return nullptr;
1522 : }
1523 :
1524 : // -scale because scale=true or scale=min,max or scale=min,max,dstmin,dstmax
1525 7 : if (aosScaleParams.size() == 1 && CPLTestBool(aosScaleParams[0]))
1526 : {
1527 2 : argv.AddString(CPLSPrintf("-%s", pszKey));
1528 : }
1529 : // add remaining params (length 2 or 4)
1530 7 : if (aosScaleParams.size() > 1)
1531 : {
1532 5 : argv.AddString(CPLSPrintf("-%s", pszKey));
1533 21 : for (int j = 0; j < aosScaleParams.size(); j++)
1534 : {
1535 16 : argv.AddString(aosScaleParams[j]);
1536 : }
1537 7 : }
1538 : }
1539 47 : else if (EQUAL(pszKey, "exponent") ||
1540 45 : STARTS_WITH_CI(pszKey, "exponent_"))
1541 : {
1542 3 : argv.AddString(CPLSPrintf("-%s", pszKey));
1543 3 : argv.AddString(pszValue);
1544 : }
1545 44 : else if (EQUAL(pszKey, "outsize"))
1546 : {
1547 : const CPLStringList aosOutSize(
1548 4 : CSLTokenizeString2(pszValue, ",", 0));
1549 4 : if (aosOutSize.size() != 2)
1550 : {
1551 1 : CPLError(CE_Failure, CPLE_IllegalArg,
1552 : "Invalid outsize option: %s, must be two"
1553 : "values separated by comma pixel,line or two "
1554 : "fraction values with percent symbol",
1555 : pszValue);
1556 1 : return nullptr;
1557 : }
1558 3 : argv.AddString("-outsize");
1559 3 : argv.AddString(aosOutSize[0]);
1560 3 : argv.AddString(aosOutSize[1]);
1561 : }
1562 40 : else if (EQUAL(pszKey, "projwin"))
1563 : {
1564 : // Parse the limits
1565 : const CPLStringList aosProjWin(
1566 3 : CSLTokenizeString2(pszValue, ",", 0));
1567 : // fail if not four values
1568 3 : if (aosProjWin.size() != 4)
1569 : {
1570 1 : CPLError(CE_Failure, CPLE_IllegalArg,
1571 : "Invalid projwin option: %s", pszValue);
1572 1 : return nullptr;
1573 : }
1574 :
1575 2 : argv.AddString("-projwin");
1576 2 : argv.AddString(aosProjWin[0]);
1577 2 : argv.AddString(aosProjWin[1]);
1578 2 : argv.AddString(aosProjWin[2]);
1579 2 : argv.AddString(aosProjWin[3]);
1580 : }
1581 37 : else if (EQUAL(pszKey, "projwin_srs"))
1582 : {
1583 2 : argv.AddString("-projwin_srs");
1584 2 : argv.AddString(pszValue);
1585 : }
1586 35 : else if (EQUAL(pszKey, "tr"))
1587 : {
1588 : const CPLStringList aosTargetResolution(
1589 3 : CSLTokenizeString2(pszValue, ",", 0));
1590 3 : if (aosTargetResolution.size() != 2)
1591 : {
1592 1 : CPLError(CE_Failure, CPLE_IllegalArg,
1593 : "Invalid tr option: %s, must be two "
1594 : "values separated by comma xres,yres",
1595 : pszValue);
1596 1 : return nullptr;
1597 : }
1598 2 : argv.AddString("-tr");
1599 2 : argv.AddString(aosTargetResolution[0]);
1600 2 : argv.AddString(aosTargetResolution[1]);
1601 : }
1602 32 : else if (EQUAL(pszKey, "r"))
1603 : {
1604 1 : argv.AddString("-r");
1605 1 : argv.AddString(pszValue);
1606 : }
1607 :
1608 31 : else if (EQUAL(pszKey, "srcwin"))
1609 : {
1610 : // Parse the limits
1611 6 : const CPLStringList aosSrcWin(CSLTokenizeString2(pszValue, ",", 0));
1612 : // fail if not four values
1613 6 : if (aosSrcWin.size() != 4)
1614 : {
1615 1 : CPLError(CE_Failure, CPLE_IllegalArg,
1616 : "Invalid srcwin option: %s, must be four "
1617 : "values separated by comma xoff,yoff,xsize,ysize",
1618 : pszValue);
1619 1 : return nullptr;
1620 : }
1621 :
1622 5 : argv.AddString("-srcwin");
1623 5 : argv.AddString(aosSrcWin[0]);
1624 5 : argv.AddString(aosSrcWin[1]);
1625 5 : argv.AddString(aosSrcWin[2]);
1626 5 : argv.AddString(aosSrcWin[3]);
1627 : }
1628 :
1629 25 : else if (EQUAL(pszKey, "a_gt"))
1630 : {
1631 : // Parse the limits
1632 : const CPLStringList aosAGeoTransform(
1633 2 : CSLTokenizeString2(pszValue, ",", 0));
1634 : // fail if not six values
1635 2 : if (aosAGeoTransform.size() != 6)
1636 : {
1637 1 : CPLError(CE_Failure, CPLE_IllegalArg, "Invalid a_gt option: %s",
1638 : pszValue);
1639 1 : return nullptr;
1640 : }
1641 :
1642 1 : argv.AddString("-a_gt");
1643 1 : argv.AddString(aosAGeoTransform[0]);
1644 1 : argv.AddString(aosAGeoTransform[1]);
1645 1 : argv.AddString(aosAGeoTransform[2]);
1646 1 : argv.AddString(aosAGeoTransform[3]);
1647 1 : argv.AddString(aosAGeoTransform[4]);
1648 1 : argv.AddString(aosAGeoTransform[5]);
1649 : }
1650 23 : else if (EQUAL(pszKey, "oo"))
1651 : {
1652 : // do nothing, we passed this in earlier
1653 : }
1654 22 : else if (EQUAL(pszKey, "if"))
1655 : {
1656 : // do nothing, we passed this in earlier
1657 : }
1658 21 : else if (EQUAL(pszKey, "sd_name"))
1659 : {
1660 : // do nothing, we passed this in earlier
1661 : }
1662 19 : else if (EQUAL(pszKey, "sd"))
1663 : {
1664 : // do nothing, we passed this in earlier
1665 : }
1666 18 : else if (EQUAL(pszKey, "transpose"))
1667 : {
1668 : // do nothing, we passed this in earlier
1669 : }
1670 16 : else if (EQUAL(pszKey, "block"))
1671 : {
1672 : // do nothing, we passed this in earlier
1673 : }
1674 9 : else if (EQUAL(pszKey, "unscale"))
1675 : {
1676 1 : if (CPLTestBool(pszValue))
1677 : {
1678 1 : argv.AddString("-unscale");
1679 : }
1680 : }
1681 8 : else if (EQUAL(pszKey, "a_coord_epoch"))
1682 : {
1683 0 : argv.AddString("-a_coord_epoch");
1684 0 : argv.AddString(pszValue);
1685 : }
1686 8 : else if (EQUAL(pszKey, "nogcp"))
1687 : {
1688 1 : if (CPLTestBool(pszValue))
1689 : {
1690 1 : argv.AddString("-nogcp");
1691 : }
1692 : }
1693 7 : else if (EQUAL(pszKey, "epo"))
1694 : {
1695 3 : if (CPLTestBool(pszValue))
1696 : {
1697 2 : argv.AddString("-epo");
1698 : }
1699 : }
1700 4 : else if (EQUAL(pszKey, "eco"))
1701 : {
1702 3 : if (CPLTestBool(pszValue))
1703 : {
1704 3 : argv.AddString("-eco");
1705 : }
1706 : }
1707 :
1708 : else
1709 : {
1710 1 : CPLError(CE_Failure, CPLE_NotSupported, "Unknown option: %s",
1711 : pszKey);
1712 1 : return nullptr;
1713 : }
1714 : }
1715 :
1716 : GDALTranslateOptions *psOptions =
1717 62 : GDALTranslateOptionsNew(argv.List(), nullptr);
1718 :
1719 62 : auto hRet = GDALTranslate("", GDALDataset::ToHandle(poSrcDS.get()),
1720 : psOptions, nullptr);
1721 :
1722 62 : GDALTranslateOptionsFree(psOptions);
1723 :
1724 : // Situation where we open a http://.jp2 file with the JP2OpenJPEG driver
1725 : // through the HTTP driver, which returns a /vsimem/ file
1726 : const bool bPatchSourceFilename =
1727 62 : (STARTS_WITH(osFilename.c_str(), "http://") ||
1728 63 : STARTS_WITH(osFilename.c_str(), "https://")) &&
1729 1 : osFilename != poSrcDS->GetDescription();
1730 :
1731 62 : poSrcDS.reset();
1732 :
1733 62 : auto poDS = dynamic_cast<VRTDataset *>(GDALDataset::FromHandle(hRet));
1734 62 : if (poDS)
1735 : {
1736 59 : if (bPatchSourceFilename)
1737 : {
1738 2 : for (int i = 0; i < poDS->nBands; ++i)
1739 : {
1740 : auto poBand =
1741 1 : dynamic_cast<VRTSourcedRasterBand *>(poDS->papoBands[i]);
1742 2 : if (poBand && poBand->m_papoSources.size() == 1 &&
1743 1 : poBand->m_papoSources[0]->IsSimpleSource())
1744 : {
1745 1 : auto poSource = cpl::down_cast<VRTSimpleSource *>(
1746 1 : poBand->m_papoSources[0].get());
1747 1 : poSource->m_bRelativeToVRTOri = 0;
1748 1 : poSource->m_osSourceFileNameOri = osFilename;
1749 : }
1750 : }
1751 : }
1752 59 : poDS->SetDescription(pszSpec);
1753 59 : poDS->SetWritable(false);
1754 : }
1755 62 : return poDS;
1756 : }
1757 :
1758 : /************************************************************************/
1759 : /* OpenXML() */
1760 : /* */
1761 : /* Create an open VRTDataset from a supplied XML representation */
1762 : /* of the dataset. */
1763 : /************************************************************************/
1764 :
1765 2679 : std::unique_ptr<VRTDataset> VRTDataset::OpenXML(const char *pszXML,
1766 : const char *pszVRTPath,
1767 : GDALAccess eAccessIn)
1768 :
1769 : {
1770 : /* -------------------------------------------------------------------- */
1771 : /* Parse the XML. */
1772 : /* -------------------------------------------------------------------- */
1773 5358 : CPLXMLTreeCloser psTree(CPLParseXMLString(pszXML));
1774 2679 : if (psTree == nullptr)
1775 0 : return nullptr;
1776 :
1777 2679 : CPLXMLNode *psRoot = CPLGetXMLNode(psTree.get(), "=VRTDataset");
1778 2679 : if (psRoot == nullptr)
1779 : {
1780 0 : CPLError(CE_Failure, CPLE_AppDefined, "Missing VRTDataset element.");
1781 0 : return nullptr;
1782 : }
1783 :
1784 2679 : const char *pszSubClass = CPLGetXMLValue(psRoot, "subClass", "");
1785 :
1786 2679 : const bool bIsPansharpened =
1787 2679 : strcmp(pszSubClass, "VRTPansharpenedDataset") == 0;
1788 2679 : const bool bIsProcessed = strcmp(pszSubClass, "VRTProcessedDataset") == 0;
1789 :
1790 2603 : if (!bIsPansharpened && !bIsProcessed &&
1791 7531 : CPLGetXMLNode(psRoot, "Group") == nullptr &&
1792 2249 : (CPLGetXMLNode(psRoot, "rasterXSize") == nullptr ||
1793 2248 : CPLGetXMLNode(psRoot, "rasterYSize") == nullptr ||
1794 2248 : CPLGetXMLNode(psRoot, "VRTRasterBand") == nullptr))
1795 : {
1796 2 : CPLError(CE_Failure, CPLE_AppDefined,
1797 : "Missing one of rasterXSize, rasterYSize or bands on"
1798 : " VRTDataset.");
1799 2 : return nullptr;
1800 : }
1801 :
1802 : /* -------------------------------------------------------------------- */
1803 : /* Create the new virtual dataset object. */
1804 : /* -------------------------------------------------------------------- */
1805 2677 : const int nXSize = atoi(CPLGetXMLValue(psRoot, "rasterXSize", "0"));
1806 2677 : const int nYSize = atoi(CPLGetXMLValue(psRoot, "rasterYSize", "0"));
1807 :
1808 2601 : if (!bIsPansharpened && !bIsProcessed &&
1809 7525 : CPLGetXMLNode(psRoot, "VRTRasterBand") != nullptr &&
1810 2247 : !GDALCheckDatasetDimensions(nXSize, nYSize))
1811 : {
1812 0 : return nullptr;
1813 : }
1814 :
1815 2677 : std::unique_ptr<VRTDataset> poDS;
1816 2677 : if (strcmp(pszSubClass, "VRTWarpedDataset") == 0)
1817 149 : poDS = std::make_unique<VRTWarpedDataset>(nXSize, nYSize);
1818 2528 : else if (bIsPansharpened)
1819 76 : poDS = std::make_unique<VRTPansharpenedDataset>(nXSize, nYSize);
1820 2452 : else if (bIsProcessed)
1821 99 : poDS = std::make_unique<VRTProcessedDataset>(nXSize, nYSize);
1822 : else
1823 : {
1824 2353 : poDS = std::make_unique<VRTDataset>(nXSize, nYSize);
1825 2353 : poDS->eAccess = eAccessIn;
1826 : }
1827 :
1828 2677 : if (poDS->XMLInit(psRoot, pszVRTPath) != CE_None)
1829 : {
1830 135 : poDS.reset();
1831 : }
1832 :
1833 : /* -------------------------------------------------------------------- */
1834 : /* Try to return a regular handle on the file. */
1835 : /* -------------------------------------------------------------------- */
1836 :
1837 2677 : return poDS;
1838 : }
1839 :
1840 : /************************************************************************/
1841 : /* AddBand() */
1842 : /************************************************************************/
1843 :
1844 138822 : CPLErr VRTDataset::AddBand(GDALDataType eType, CSLConstList papszOptions)
1845 :
1846 : {
1847 138822 : if (eType == GDT_Unknown || eType == GDT_TypeCount)
1848 : {
1849 1 : ReportError(CE_Failure, CPLE_IllegalArg,
1850 : "Illegal GDT_Unknown/GDT_TypeCount argument");
1851 1 : return CE_Failure;
1852 : }
1853 :
1854 138821 : SetNeedsFlush();
1855 :
1856 : /* ==================================================================== */
1857 : /* Handle a new raw band. */
1858 : /* ==================================================================== */
1859 138821 : const char *pszSubClass = CSLFetchNameValue(papszOptions, "subclass");
1860 :
1861 138821 : if (pszSubClass != nullptr && EQUAL(pszSubClass, "VRTRawRasterBand"))
1862 : {
1863 : #ifdef GDAL_VRT_ENABLE_RAWRASTERBAND
1864 7 : if (!VRTDataset::IsRawRasterBandEnabled())
1865 : {
1866 1 : return CE_Failure;
1867 : }
1868 6 : const int nWordDataSize = GDALGetDataTypeSizeBytes(eType);
1869 :
1870 : /* ---------------------------------------------------------------- */
1871 : /* Collect required information. */
1872 : /* ---------------------------------------------------------------- */
1873 : const char *pszImageOffset =
1874 6 : CSLFetchNameValueDef(papszOptions, "ImageOffset", "0");
1875 12 : vsi_l_offset nImageOffset = CPLScanUIntBig(
1876 6 : pszImageOffset, static_cast<int>(strlen(pszImageOffset)));
1877 :
1878 6 : int nPixelOffset = nWordDataSize;
1879 : const char *pszPixelOffset =
1880 6 : CSLFetchNameValue(papszOptions, "PixelOffset");
1881 6 : if (pszPixelOffset != nullptr)
1882 4 : nPixelOffset = atoi(pszPixelOffset);
1883 :
1884 : int nLineOffset;
1885 : const char *pszLineOffset =
1886 6 : CSLFetchNameValue(papszOptions, "LineOffset");
1887 6 : if (pszLineOffset != nullptr)
1888 4 : nLineOffset = atoi(pszLineOffset);
1889 : else
1890 : {
1891 4 : if (nPixelOffset > INT_MAX / GetRasterXSize() ||
1892 2 : nPixelOffset < INT_MIN / GetRasterXSize())
1893 : {
1894 0 : CPLError(CE_Failure, CPLE_AppDefined, "Int overflow");
1895 0 : return CE_Failure;
1896 : }
1897 2 : nLineOffset = nPixelOffset * GetRasterXSize();
1898 : }
1899 :
1900 6 : const char *pszByteOrder = CSLFetchNameValue(papszOptions, "ByteOrder");
1901 :
1902 : const char *pszFilename =
1903 6 : CSLFetchNameValue(papszOptions, "SourceFilename");
1904 6 : if (pszFilename == nullptr)
1905 : {
1906 0 : CPLError(CE_Failure, CPLE_AppDefined,
1907 : "AddBand() requires a SourceFilename option for "
1908 : "VRTRawRasterBands.");
1909 0 : return CE_Failure;
1910 : }
1911 :
1912 : const bool bRelativeToVRT =
1913 6 : CPLFetchBool(papszOptions, "relativeToVRT", false);
1914 :
1915 : /* --------------------------------------------------------------- */
1916 : /* Create and initialize the band. */
1917 : /* --------------------------------------------------------------- */
1918 :
1919 : auto poBand = std::make_unique<VRTRawRasterBand>(
1920 12 : this, GetRasterCount() + 1, eType);
1921 :
1922 6 : const std::string osPath = CPLGetPathSafe(GetDescription());
1923 18 : const CPLErr eErr = poBand->SetRawLink(
1924 12 : pszFilename, osPath.empty() ? nullptr : osPath.c_str(),
1925 : bRelativeToVRT, nImageOffset, nPixelOffset, nLineOffset,
1926 : pszByteOrder);
1927 6 : if (eErr == CE_None)
1928 6 : SetBand(GetRasterCount() + 1, std::move(poBand));
1929 :
1930 6 : return eErr;
1931 : #else
1932 : CPLError(CE_Failure, CPLE_NotSupported,
1933 : "VRTDataset::AddBand(): cannot instantiate VRTRawRasterBand, "
1934 : "because disabled in this GDAL build");
1935 : return CE_Failure;
1936 : #endif
1937 : }
1938 :
1939 : /* ==================================================================== */
1940 : /* Handle a new "sourced" band. */
1941 : /* ==================================================================== */
1942 : else
1943 : {
1944 138814 : VRTSourcedRasterBand *poBand = nullptr;
1945 :
1946 : int nBlockXSizeIn =
1947 138814 : atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "0"));
1948 : int nBlockYSizeIn =
1949 138814 : atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "0"));
1950 138814 : if (nBlockXSizeIn == 0 && nBlockYSizeIn == 0)
1951 : {
1952 71446 : nBlockXSizeIn = m_nBlockXSize;
1953 71446 : nBlockYSizeIn = m_nBlockYSize;
1954 : }
1955 :
1956 : /* ---- Check for our sourced band 'derived' subclass ---- */
1957 138814 : if (pszSubClass != nullptr &&
1958 337 : EQUAL(pszSubClass, "VRTDerivedRasterBand"))
1959 : {
1960 :
1961 : /* We'll need a pointer to the subclass in case we need */
1962 : /* to set the new band's pixel function below. */
1963 : VRTDerivedRasterBand *poDerivedBand = new VRTDerivedRasterBand(
1964 317 : this, GetRasterCount() + 1, eType, GetRasterXSize(),
1965 317 : GetRasterYSize(), nBlockXSizeIn, nBlockYSizeIn);
1966 :
1967 : /* Set the pixel function options it provided. */
1968 : const char *pszFuncName =
1969 317 : CSLFetchNameValue(papszOptions, "PixelFunctionType");
1970 317 : if (pszFuncName != nullptr)
1971 316 : poDerivedBand->SetPixelFunctionName(pszFuncName);
1972 :
1973 : const char *pszLanguage =
1974 317 : CSLFetchNameValue(papszOptions, "PixelFunctionLanguage");
1975 317 : if (pszLanguage != nullptr)
1976 1 : poDerivedBand->SetPixelFunctionLanguage(pszLanguage);
1977 :
1978 : const char *pszSkipNonContributingSources =
1979 317 : CSLFetchNameValue(papszOptions, "SkipNonContributingSources");
1980 317 : if (pszSkipNonContributingSources != nullptr)
1981 : {
1982 3 : poDerivedBand->SetSkipNonContributingSources(
1983 3 : CPLTestBool(pszSkipNonContributingSources));
1984 : }
1985 2414 : for (const auto &[pszKey, pszValue] :
1986 2731 : cpl::IterateNameValue(static_cast<CSLConstList>(papszOptions)))
1987 : {
1988 1207 : if (STARTS_WITH(pszKey, "_PIXELFN_ARG_"))
1989 : {
1990 565 : poDerivedBand->AddPixelFunctionArgument(pszKey + 13,
1991 : pszValue);
1992 : }
1993 : }
1994 :
1995 : const char *pszTransferTypeName =
1996 317 : CSLFetchNameValue(papszOptions, "SourceTransferType");
1997 317 : if (pszTransferTypeName != nullptr)
1998 : {
1999 : const GDALDataType eTransferType =
2000 5 : GDALGetDataTypeByName(pszTransferTypeName);
2001 5 : if (eTransferType == GDT_Unknown)
2002 : {
2003 1 : CPLError(CE_Failure, CPLE_AppDefined,
2004 : "invalid SourceTransferType: \"%s\".",
2005 : pszTransferTypeName);
2006 1 : delete poDerivedBand;
2007 1 : return CE_Failure;
2008 : }
2009 4 : poDerivedBand->SetSourceTransferType(eTransferType);
2010 : }
2011 :
2012 : /* We're done with the derived band specific stuff, so */
2013 : /* we can assign the base class pointer now. */
2014 316 : poBand = poDerivedBand;
2015 : }
2016 : else
2017 : {
2018 : /* ---- Standard sourced band ---- */
2019 138497 : poBand = new VRTSourcedRasterBand(
2020 138497 : this, GetRasterCount() + 1, eType, GetRasterXSize(),
2021 138497 : GetRasterYSize(), nBlockXSizeIn, nBlockYSizeIn);
2022 : }
2023 :
2024 138813 : SetBand(GetRasterCount() + 1, poBand);
2025 :
2026 295340 : for (int i = 0; papszOptions != nullptr && papszOptions[i] != nullptr;
2027 : i++)
2028 : {
2029 156527 : if (STARTS_WITH_CI(papszOptions[i], "AddFuncSource="))
2030 : {
2031 0 : char **papszTokens = CSLTokenizeStringComplex(
2032 0 : papszOptions[i] + 14, ",", TRUE, FALSE);
2033 0 : if (CSLCount(papszTokens) < 1)
2034 : {
2035 0 : CPLError(CE_Failure, CPLE_AppDefined,
2036 : "AddFuncSource(): required argument missing.");
2037 : // TODO: How should this error be handled? Return
2038 : // CE_Failure?
2039 : }
2040 :
2041 0 : VRTImageReadFunc pfnReadFunc = nullptr;
2042 0 : sscanf(papszTokens[0], "%p", &pfnReadFunc);
2043 :
2044 0 : void *pCBData = nullptr;
2045 0 : if (CSLCount(papszTokens) > 1)
2046 0 : sscanf(papszTokens[1], "%p", &pCBData);
2047 :
2048 0 : const double dfNoDataValue = (CSLCount(papszTokens) > 2)
2049 0 : ? CPLAtof(papszTokens[2])
2050 0 : : VRT_NODATA_UNSET;
2051 :
2052 0 : poBand->AddFuncSource(pfnReadFunc, pCBData, dfNoDataValue);
2053 :
2054 0 : CSLDestroy(papszTokens);
2055 : }
2056 : }
2057 :
2058 138813 : return CE_None;
2059 : }
2060 : }
2061 :
2062 : /*! @endcond */
2063 : /************************************************************************/
2064 : /* VRTAddBand() */
2065 : /************************************************************************/
2066 :
2067 : /**
2068 : * @see VRTDataset::VRTAddBand().
2069 : *
2070 : * @note The return type of this function is int, but the actual values
2071 : * returned are of type CPLErr.
2072 : */
2073 :
2074 960 : int CPL_STDCALL VRTAddBand(VRTDatasetH hDataset, GDALDataType eType,
2075 : CSLConstList papszOptions)
2076 :
2077 : {
2078 960 : VALIDATE_POINTER1(hDataset, "VRTAddBand", 0);
2079 :
2080 960 : return static_cast<VRTDataset *>(GDALDataset::FromHandle(hDataset))
2081 960 : ->AddBand(eType, papszOptions);
2082 : }
2083 :
2084 : /*! @cond Doxygen_Suppress */
2085 :
2086 : /************************************************************************/
2087 : /* Create() */
2088 : /************************************************************************/
2089 :
2090 231 : GDALDataset *VRTDataset::Create(const char *pszName, int nXSize, int nYSize,
2091 : int nBandsIn, GDALDataType eType,
2092 : CSLConstList papszOptions)
2093 :
2094 : {
2095 462 : return CreateVRTDataset(pszName, nXSize, nYSize, nBandsIn, eType,
2096 : const_cast<CSLConstList>(papszOptions))
2097 231 : .release();
2098 : }
2099 :
2100 : /************************************************************************/
2101 : /* CreateVRTDataset() */
2102 : /************************************************************************/
2103 :
2104 : std::unique_ptr<VRTDataset>
2105 724 : VRTDataset::CreateVRTDataset(const char *pszName, int nXSize, int nYSize,
2106 : int nBandsIn, GDALDataType eType,
2107 : CSLConstList papszOptions)
2108 :
2109 : {
2110 724 : if (STARTS_WITH_CI(pszName, "<VRTDataset"))
2111 : {
2112 0 : auto poDS = OpenXML(pszName, nullptr, GA_Update);
2113 0 : if (poDS != nullptr)
2114 0 : poDS->SetDescription("<FromXML>");
2115 0 : return poDS;
2116 : }
2117 :
2118 724 : const char *pszSubclass = CSLFetchNameValue(papszOptions, "SUBCLASS");
2119 :
2120 724 : std::unique_ptr<VRTDataset> poDS;
2121 :
2122 : const int nBlockXSize =
2123 724 : atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "0"));
2124 : const int nBlockYSize =
2125 724 : atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "0"));
2126 724 : if (pszSubclass == nullptr || EQUAL(pszSubclass, "VRTDataset"))
2127 1088 : poDS = std::make_unique<VRTDataset>(nXSize, nYSize, nBlockXSize,
2128 544 : nBlockYSize);
2129 180 : else if (EQUAL(pszSubclass, "VRTWarpedDataset"))
2130 : {
2131 360 : poDS = std::make_unique<VRTWarpedDataset>(nXSize, nYSize, nBlockXSize,
2132 180 : nBlockYSize);
2133 : }
2134 : else
2135 : {
2136 0 : CPLError(CE_Failure, CPLE_AppDefined, "SUBCLASS=%s not recognised.",
2137 : pszSubclass);
2138 0 : return nullptr;
2139 : }
2140 724 : poDS->eAccess = GA_Update;
2141 :
2142 724 : poDS->SetDescription(pszName);
2143 :
2144 1125 : for (int iBand = 0; iBand < nBandsIn; iBand++)
2145 401 : poDS->AddBand(eType, nullptr);
2146 :
2147 724 : poDS->SetNeedsFlush();
2148 :
2149 724 : poDS->oOvManager.Initialize(poDS.get(), pszName);
2150 :
2151 724 : return poDS;
2152 : }
2153 :
2154 : /************************************************************************/
2155 : /* CreateVRTMultiDimensional() */
2156 : /************************************************************************/
2157 :
2158 : std::unique_ptr<VRTDataset>
2159 126 : VRTDataset::CreateVRTMultiDimensional(const char *pszFilename,
2160 : CSLConstList /*papszRootGroupOptions*/,
2161 : CSLConstList /*papszOptions*/)
2162 : {
2163 126 : auto poDS = std::make_unique<VRTDataset>(0, 0);
2164 126 : poDS->eAccess = GA_Update;
2165 126 : poDS->SetDescription(pszFilename);
2166 126 : poDS->m_poRootGroup = VRTGroup::Create(std::string(), "/");
2167 126 : poDS->m_poRootGroup->SetIsRootGroup();
2168 126 : poDS->m_poRootGroup->SetFilename(pszFilename);
2169 126 : poDS->m_poRootGroup->SetDirty();
2170 :
2171 126 : return poDS;
2172 : }
2173 :
2174 : /************************************************************************/
2175 : /* CreateMultiDimensional() */
2176 : /************************************************************************/
2177 :
2178 : GDALDataset *
2179 2 : VRTDataset::CreateMultiDimensional(const char *pszFilename,
2180 : CSLConstList papszRootGroupOptions,
2181 : CSLConstList papszOptions)
2182 : {
2183 4 : return CreateVRTMultiDimensional(pszFilename, papszRootGroupOptions,
2184 : papszOptions)
2185 2 : .release();
2186 : }
2187 :
2188 : /************************************************************************/
2189 : /* GetFileList() */
2190 : /************************************************************************/
2191 :
2192 60 : char **VRTDataset::GetFileList()
2193 : {
2194 60 : char **papszFileList = GDALDataset::GetFileList();
2195 :
2196 60 : int nSize = CSLCount(papszFileList);
2197 60 : int nMaxSize = nSize;
2198 :
2199 : // Do not need an element deallocator as each string points to an
2200 : // element of the papszFileList.
2201 : CPLHashSet *hSetFiles =
2202 60 : CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, nullptr);
2203 :
2204 131 : for (int iBand = 0; iBand < nBands; iBand++)
2205 : {
2206 71 : static_cast<VRTRasterBand *>(papoBands[iBand])
2207 71 : ->GetFileList(&papszFileList, &nSize, &nMaxSize, hSetFiles);
2208 : }
2209 :
2210 60 : CPLHashSetDestroy(hSetFiles);
2211 :
2212 60 : return papszFileList;
2213 : }
2214 :
2215 : /************************************************************************/
2216 : /* Delete() */
2217 : /************************************************************************/
2218 :
2219 : /* We implement Delete() to avoid that the default implementation */
2220 : /* in GDALDriver::Delete() destroys the source files listed by GetFileList(),*/
2221 : /* which would be an undesired effect... */
2222 13 : CPLErr VRTDataset::Delete(const char *pszFilename)
2223 : {
2224 13 : GDALDriverH hDriver = GDALIdentifyDriver(pszFilename, nullptr);
2225 :
2226 13 : if (!hDriver || !EQUAL(GDALGetDriverShortName(hDriver), "VRT"))
2227 0 : return CE_Failure;
2228 :
2229 25 : if (strstr(pszFilename, "<VRTDataset") == nullptr &&
2230 12 : VSIUnlink(pszFilename) != 0)
2231 : {
2232 0 : CPLError(CE_Failure, CPLE_AppDefined, "Deleting %s failed:\n%s",
2233 0 : pszFilename, VSIStrerror(errno));
2234 0 : return CE_Failure;
2235 : }
2236 :
2237 13 : return CE_None;
2238 : }
2239 :
2240 : /************************************************************************/
2241 : /* CreateMaskBand() */
2242 : /************************************************************************/
2243 :
2244 46 : CPLErr VRTDataset::CreateMaskBand(int)
2245 : {
2246 46 : if (m_poMaskBand != nullptr)
2247 : {
2248 1 : CPLError(CE_Failure, CPLE_AppDefined,
2249 : "This VRT dataset has already a mask band");
2250 1 : return CE_Failure;
2251 : }
2252 :
2253 45 : SetMaskBand(std::make_unique<VRTSourcedRasterBand>(this, 0));
2254 :
2255 45 : return CE_None;
2256 : }
2257 :
2258 : /************************************************************************/
2259 : /* SetMaskBand() */
2260 : /************************************************************************/
2261 :
2262 83 : void VRTDataset::SetMaskBand(std::unique_ptr<VRTRasterBand> poMaskBandIn)
2263 : {
2264 83 : m_poMaskBand = std::move(poMaskBandIn);
2265 83 : m_poMaskBand->SetIsMaskBand();
2266 83 : }
2267 :
2268 : /************************************************************************/
2269 : /* CloseDependentDatasets() */
2270 : /************************************************************************/
2271 :
2272 656 : int VRTDataset::CloseDependentDatasets()
2273 : {
2274 : /* We need to call it before removing the sources, otherwise */
2275 : /* we would remove them from the serizalized VRT */
2276 656 : FlushCache(true);
2277 :
2278 656 : int bHasDroppedRef = GDALDataset::CloseDependentDatasets();
2279 :
2280 1707 : for (int iBand = 0; iBand < nBands; iBand++)
2281 : {
2282 2102 : bHasDroppedRef |= static_cast<VRTRasterBand *>(papoBands[iBand])
2283 1051 : ->CloseDependentDatasets();
2284 : }
2285 :
2286 656 : return bHasDroppedRef;
2287 : }
2288 :
2289 : /************************************************************************/
2290 : /* CheckCompatibleForDatasetIO() */
2291 : /************************************************************************/
2292 :
2293 : /* We will return TRUE only if all the bands are VRTSourcedRasterBands */
2294 : /* made of identical sources, that are strictly VRTSimpleSource, and that */
2295 : /* the band number of each source is the band number of the */
2296 : /* VRTSourcedRasterBand. */
2297 :
2298 18305 : bool VRTDataset::CheckCompatibleForDatasetIO() const
2299 : {
2300 18305 : size_t nSources = 0;
2301 18305 : const std::unique_ptr<VRTSource> *papoSources = nullptr;
2302 36610 : CPLString osResampling;
2303 :
2304 18305 : if (m_nCompatibleForDatasetIO >= 0)
2305 : {
2306 16036 : return CPL_TO_BOOL(m_nCompatibleForDatasetIO);
2307 : }
2308 :
2309 2269 : m_nCompatibleForDatasetIO = false;
2310 :
2311 2269 : GDALDataset *poFirstBandSourceDS = nullptr;
2312 6543 : for (int iBand = 0; iBand < nBands; iBand++)
2313 : {
2314 4692 : auto poVRTBand = static_cast<VRTRasterBand *>(papoBands[iBand]);
2315 4692 : assert(poVRTBand);
2316 4692 : if (!poVRTBand->IsSourcedRasterBand())
2317 65 : return false;
2318 :
2319 4627 : const VRTSourcedRasterBand *poBand =
2320 : static_cast<const VRTSourcedRasterBand *>(poVRTBand);
2321 :
2322 : // Do not allow VRTDerivedRasterBand for example
2323 4627 : if (typeid(*poBand) != typeid(VRTSourcedRasterBand))
2324 68 : return false;
2325 :
2326 4559 : if (iBand == 0)
2327 : {
2328 2035 : nSources = poBand->m_papoSources.size();
2329 2035 : papoSources = poBand->m_papoSources.data();
2330 4217 : for (auto &poSource : poBand->m_papoSources)
2331 : {
2332 2360 : if (!poSource->IsSimpleSource())
2333 178 : return false;
2334 :
2335 : const VRTSimpleSource *poSimpleSource =
2336 2356 : static_cast<const VRTSimpleSource *>(poSource.get());
2337 2356 : if (poSimpleSource->GetType() !=
2338 2356 : VRTSimpleSource::GetTypeStatic())
2339 143 : return false;
2340 :
2341 6615 : if (poSimpleSource->m_nBand != iBand + 1 ||
2342 2603 : poSimpleSource->m_bGetMaskBand ||
2343 390 : (nSources > 1 && poSimpleSource->m_osSrcDSName.empty()))
2344 : {
2345 31 : return false;
2346 : }
2347 2182 : if (nSources == 1 && poSimpleSource->m_osSrcDSName.empty())
2348 : {
2349 1009 : if (auto poSourceBand = poSimpleSource->GetRasterBand())
2350 : {
2351 1009 : poFirstBandSourceDS = poSourceBand->GetDataset();
2352 : }
2353 : else
2354 : {
2355 0 : return false;
2356 : }
2357 : }
2358 2182 : osResampling = poSimpleSource->GetResampling();
2359 : }
2360 : }
2361 2524 : else if (nSources != poBand->m_papoSources.size())
2362 : {
2363 0 : return false;
2364 : }
2365 : else
2366 : {
2367 5636 : for (size_t iSource = 0; iSource < nSources; iSource++)
2368 : {
2369 3219 : if (!poBand->m_papoSources[iSource]->IsSimpleSource())
2370 0 : return false;
2371 : const VRTSimpleSource *poRefSource =
2372 : static_cast<const VRTSimpleSource *>(
2373 3219 : papoSources[iSource].get());
2374 :
2375 : const VRTSimpleSource *poSource =
2376 : static_cast<const VRTSimpleSource *>(
2377 3219 : poBand->m_papoSources[iSource].get());
2378 3219 : if (poSource->GetType() != VRTSimpleSource::GetTypeStatic())
2379 20 : return false;
2380 9510 : if (poSource->m_nBand != iBand + 1 ||
2381 4013 : poSource->m_bGetMaskBand ||
2382 814 : (nSources > 1 && poSource->m_osSrcDSName.empty()))
2383 87 : return false;
2384 3112 : if (!poSource->IsSameExceptBandNumber(poRefSource))
2385 0 : return false;
2386 3112 : if (osResampling.compare(poSource->GetResampling()) != 0)
2387 0 : return false;
2388 3112 : if (nSources == 1 && poSource->m_osSrcDSName.empty())
2389 : {
2390 1946 : auto poSourceBand = poSource->GetRasterBand();
2391 3892 : if (!poSourceBand ||
2392 1946 : poFirstBandSourceDS != poSourceBand->GetDataset())
2393 : {
2394 0 : return false;
2395 : }
2396 : }
2397 : }
2398 : }
2399 : }
2400 :
2401 1851 : m_nCompatibleForDatasetIO = nSources != 0;
2402 1851 : return CPL_TO_BOOL(m_nCompatibleForDatasetIO);
2403 : }
2404 :
2405 : /************************************************************************/
2406 : /* GetSingleSimpleSource() */
2407 : /* */
2408 : /* Returns a non-NULL dataset if the VRT is made of a single source */
2409 : /* that is a simple source, in its full extent, and with all of its */
2410 : /* bands. Basically something produced by : */
2411 : /* gdal_translate src dst.vrt -of VRT (-a_srs / -a_ullr) */
2412 : /************************************************************************/
2413 :
2414 1277 : GDALDataset *VRTDataset::GetSingleSimpleSource()
2415 : {
2416 1277 : if (!CheckCompatibleForDatasetIO())
2417 102 : return nullptr;
2418 :
2419 1175 : VRTSourcedRasterBand *poVRTBand =
2420 1175 : static_cast<VRTSourcedRasterBand *>(papoBands[0]);
2421 1175 : if (poVRTBand->m_papoSources.size() != 1)
2422 1 : return nullptr;
2423 :
2424 : VRTSimpleSource *poSource =
2425 1174 : static_cast<VRTSimpleSource *>(poVRTBand->m_papoSources[0].get());
2426 :
2427 1174 : GDALRasterBand *poBand = poSource->GetRasterBand();
2428 1174 : if (poBand == nullptr || poSource->GetMaskBandMainBand() != nullptr)
2429 3 : return nullptr;
2430 :
2431 1171 : GDALDataset *poSrcDS = poBand->GetDataset();
2432 1171 : if (poSrcDS == nullptr)
2433 0 : return nullptr;
2434 :
2435 : /* Check that it uses the full source dataset */
2436 1171 : double dfReqXOff = 0.0;
2437 1171 : double dfReqYOff = 0.0;
2438 1171 : double dfReqXSize = 0.0;
2439 1171 : double dfReqYSize = 0.0;
2440 1171 : int nReqXOff = 0;
2441 1171 : int nReqYOff = 0;
2442 1171 : int nReqXSize = 0;
2443 1171 : int nReqYSize = 0;
2444 1171 : int nOutXOff = 0;
2445 1171 : int nOutYOff = 0;
2446 1171 : int nOutXSize = 0;
2447 1171 : int nOutYSize = 0;
2448 1171 : bool bError = false;
2449 2342 : if (!poSource->GetSrcDstWindow(
2450 1171 : 0, 0, poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(),
2451 : poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(),
2452 : GRIORA_NearestNeighbour, &dfReqXOff, &dfReqYOff, &dfReqXSize,
2453 : &dfReqYSize, &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
2454 : &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize, bError))
2455 0 : return nullptr;
2456 :
2457 355 : if (nReqXOff != 0 || nReqYOff != 0 ||
2458 1762 : nReqXSize != poSrcDS->GetRasterXSize() ||
2459 236 : nReqYSize != poSrcDS->GetRasterYSize())
2460 938 : return nullptr;
2461 :
2462 233 : if (nOutXOff != 0 || nOutYOff != 0 ||
2463 669 : nOutXSize != poSrcDS->GetRasterXSize() ||
2464 203 : nOutYSize != poSrcDS->GetRasterYSize())
2465 30 : return nullptr;
2466 :
2467 203 : return poSrcDS;
2468 : }
2469 :
2470 : /************************************************************************/
2471 : /* AdviseRead() */
2472 : /************************************************************************/
2473 :
2474 3959 : CPLErr VRTDataset::AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
2475 : int nBufXSize, int nBufYSize, GDALDataType eDT,
2476 : int nBandCount, int *panBandList,
2477 : CSLConstList papszOptions)
2478 : {
2479 3959 : if (!CheckCompatibleForDatasetIO())
2480 802 : return CE_None;
2481 :
2482 3157 : VRTSourcedRasterBand *poVRTBand =
2483 3157 : static_cast<VRTSourcedRasterBand *>(papoBands[0]);
2484 3157 : if (poVRTBand->m_papoSources.size() != 1)
2485 10 : return CE_None;
2486 :
2487 : VRTSimpleSource *poSource =
2488 3147 : static_cast<VRTSimpleSource *>(poVRTBand->m_papoSources[0].get());
2489 :
2490 : /* Find source window and buffer size */
2491 3147 : double dfReqXOff = 0.0;
2492 3147 : double dfReqYOff = 0.0;
2493 3147 : double dfReqXSize = 0.0;
2494 3147 : double dfReqYSize = 0.0;
2495 3147 : int nReqXOff = 0;
2496 3147 : int nReqYOff = 0;
2497 3147 : int nReqXSize = 0;
2498 3147 : int nReqYSize = 0;
2499 3147 : int nOutXOff = 0;
2500 3147 : int nOutYOff = 0;
2501 3147 : int nOutXSize = 0;
2502 3147 : int nOutYSize = 0;
2503 3147 : bool bError = false;
2504 3147 : if (!poSource->GetSrcDstWindow(
2505 : nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
2506 : GRIORA_NearestNeighbour, &dfReqXOff, &dfReqYOff, &dfReqXSize,
2507 : &dfReqYSize, &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
2508 : &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize, bError))
2509 : {
2510 81 : return bError ? CE_Failure : CE_None;
2511 : }
2512 :
2513 3066 : GDALRasterBand *poBand = poSource->GetRasterBand();
2514 3066 : if (poBand == nullptr || poSource->GetMaskBandMainBand() != nullptr)
2515 0 : return CE_None;
2516 :
2517 3066 : GDALDataset *poSrcDS = poBand->GetDataset();
2518 3066 : if (poSrcDS == nullptr)
2519 0 : return CE_None;
2520 :
2521 3066 : return poSrcDS->AdviseRead(nReqXOff, nReqYOff, nReqXSize, nReqYSize,
2522 : nOutXSize, nOutYSize, eDT, nBandCount,
2523 3066 : panBandList, papszOptions);
2524 : }
2525 :
2526 : /************************************************************************/
2527 : /* GetNumThreads() */
2528 : /************************************************************************/
2529 :
2530 38 : /* static */ int VRTDataset::GetNumThreads(GDALDataset *poDS)
2531 : {
2532 38 : const char *pszNumThreads = nullptr;
2533 38 : if (poDS)
2534 38 : pszNumThreads = CSLFetchNameValueDef(poDS->GetOpenOptions(),
2535 : "NUM_THREADS", nullptr);
2536 38 : if (!pszNumThreads)
2537 38 : pszNumThreads = CPLGetConfigOption("VRT_NUM_THREADS", nullptr);
2538 38 : return GDALGetNumThreads(pszNumThreads, GDALGetMaxDatasetPoolSize(),
2539 38 : /* bDefaultAllCPUs = */ true);
2540 : }
2541 :
2542 : /************************************************************************/
2543 : /* VRTDatasetRasterIOJob */
2544 : /************************************************************************/
2545 :
2546 : /** Structure used to declare a threaded job to satisfy IRasterIO()
2547 : * on a given source.
2548 : */
2549 : struct VRTDatasetRasterIOJob
2550 : {
2551 : std::atomic<int> *pnCompletedJobs = nullptr;
2552 : std::atomic<bool> *pbSuccess = nullptr;
2553 : CPLErrorAccumulator *poErrorAccumulator = nullptr;
2554 :
2555 : GDALDataType eVRTBandDataType = GDT_Unknown;
2556 : int nXOff = 0;
2557 : int nYOff = 0;
2558 : int nXSize = 0;
2559 : int nYSize = 0;
2560 : void *pData = nullptr;
2561 : int nBufXSize = 0;
2562 : int nBufYSize = 0;
2563 : int nBandCount = 0;
2564 : BANDMAP_TYPE panBandMap = nullptr;
2565 : GDALDataType eBufType = GDT_Unknown;
2566 : GSpacing nPixelSpace = 0;
2567 : GSpacing nLineSpace = 0;
2568 : GSpacing nBandSpace = 0;
2569 : GDALRasterIOExtraArg *psExtraArg = nullptr;
2570 : VRTSimpleSource *poSource = nullptr;
2571 :
2572 : static void Func(void *pData);
2573 : };
2574 :
2575 : /************************************************************************/
2576 : /* VRTDatasetRasterIOJob::Func() */
2577 : /************************************************************************/
2578 :
2579 67 : void VRTDatasetRasterIOJob::Func(void *pData)
2580 : {
2581 : auto psJob = std::unique_ptr<VRTDatasetRasterIOJob>(
2582 134 : static_cast<VRTDatasetRasterIOJob *>(pData));
2583 67 : if (*psJob->pbSuccess)
2584 : {
2585 67 : GDALRasterIOExtraArg sArg = *(psJob->psExtraArg);
2586 67 : sArg.pfnProgress = nullptr;
2587 67 : sArg.pProgressData = nullptr;
2588 :
2589 134 : auto oAccumulator = psJob->poErrorAccumulator->InstallForCurrentScope();
2590 67 : CPL_IGNORE_RET_VAL(oAccumulator);
2591 :
2592 134 : if (psJob->poSource->DatasetRasterIO(
2593 67 : psJob->eVRTBandDataType, psJob->nXOff, psJob->nYOff,
2594 67 : psJob->nXSize, psJob->nYSize, psJob->pData, psJob->nBufXSize,
2595 67 : psJob->nBufYSize, psJob->eBufType, psJob->nBandCount,
2596 67 : psJob->panBandMap, psJob->nPixelSpace, psJob->nLineSpace,
2597 134 : psJob->nBandSpace, &sArg) != CE_None)
2598 : {
2599 0 : *psJob->pbSuccess = false;
2600 : }
2601 : }
2602 :
2603 67 : ++(*psJob->pnCompletedJobs);
2604 67 : }
2605 :
2606 : /************************************************************************/
2607 : /* IRasterIO() */
2608 : /************************************************************************/
2609 :
2610 14841 : CPLErr VRTDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
2611 : int nXSize, int nYSize, void *pData, int nBufXSize,
2612 : int nBufYSize, GDALDataType eBufType,
2613 : int nBandCount, BANDMAP_TYPE panBandMap,
2614 : GSpacing nPixelSpace, GSpacing nLineSpace,
2615 : GSpacing nBandSpace,
2616 : GDALRasterIOExtraArg *psExtraArg)
2617 : {
2618 14841 : m_bMultiThreadedRasterIOLastUsed = false;
2619 :
2620 14841 : if (nBands == 1 && nBandCount == 1)
2621 : {
2622 : VRTSourcedRasterBand *poBand =
2623 1829 : dynamic_cast<VRTSourcedRasterBand *>(papoBands[0]);
2624 1829 : if (poBand)
2625 : {
2626 1827 : return poBand->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2627 : pData, nBufXSize, nBufYSize, eBufType,
2628 1827 : nPixelSpace, nLineSpace, psExtraArg);
2629 : }
2630 : }
2631 :
2632 13014 : bool bLocalCompatibleForDatasetIO = CheckCompatibleForDatasetIO();
2633 12445 : if (bLocalCompatibleForDatasetIO && eRWFlag == GF_Read &&
2634 25459 : (nBufXSize < nXSize || nBufYSize < nYSize) && m_apoOverviews.empty())
2635 : {
2636 3 : int bTried = FALSE;
2637 3 : const CPLErr eErr = TryOverviewRasterIO(
2638 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2639 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
2640 : nBandSpace, psExtraArg, &bTried);
2641 :
2642 3 : if (bTried)
2643 : {
2644 1 : return eErr;
2645 : }
2646 :
2647 9 : for (int iBand = 0; iBand < nBands; iBand++)
2648 : {
2649 7 : VRTSourcedRasterBand *poBand =
2650 7 : static_cast<VRTSourcedRasterBand *>(papoBands[iBand]);
2651 :
2652 : // If there are overviews, let VRTSourcedRasterBand::IRasterIO()
2653 : // do the job.
2654 7 : if (poBand->GetOverviewCount() != 0)
2655 : {
2656 0 : bLocalCompatibleForDatasetIO = false;
2657 0 : break;
2658 : }
2659 : }
2660 : }
2661 :
2662 : // If resampling with non-nearest neighbour, we need to be careful
2663 : // if the VRT band exposes a nodata value, but the sources do not have it.
2664 : // To also avoid edge effects on sources when downsampling, use the
2665 : // base implementation of IRasterIO() (that is acquiring sources at their
2666 : // nominal resolution, and then downsampling), but only if none of the
2667 : // contributing sources have overviews.
2668 13013 : if (bLocalCompatibleForDatasetIO && eRWFlag == GF_Read &&
2669 12431 : (nXSize != nBufXSize || nYSize != nBufYSize) &&
2670 13 : psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
2671 : {
2672 0 : for (int iBandIndex = 0; iBandIndex < nBandCount; iBandIndex++)
2673 : {
2674 : VRTSourcedRasterBand *poBand = static_cast<VRTSourcedRasterBand *>(
2675 0 : GetRasterBand(panBandMap[iBandIndex]));
2676 0 : if (!poBand->CanIRasterIOBeForwardedToEachSource(
2677 : eRWFlag, nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
2678 : psExtraArg))
2679 : {
2680 0 : bLocalCompatibleForDatasetIO = false;
2681 0 : break;
2682 : }
2683 : }
2684 : }
2685 :
2686 13013 : if (bLocalCompatibleForDatasetIO && eRWFlag == GF_Read)
2687 : {
2688 47822 : for (int iBandIndex = 0; iBandIndex < nBandCount; iBandIndex++)
2689 : {
2690 : VRTSourcedRasterBand *poBand = static_cast<VRTSourcedRasterBand *>(
2691 35378 : GetRasterBand(panBandMap[iBandIndex]));
2692 :
2693 : /* Dirty little trick to initialize the buffer without doing */
2694 : /* any real I/O */
2695 70756 : std::vector<std::unique_ptr<VRTSource>> papoSavedSources;
2696 35378 : std::swap(papoSavedSources, poBand->m_papoSources);
2697 :
2698 35378 : GDALProgressFunc pfnProgressGlobal = psExtraArg->pfnProgress;
2699 35378 : psExtraArg->pfnProgress = nullptr;
2700 :
2701 35378 : GByte *pabyBandData =
2702 35378 : static_cast<GByte *>(pData) + iBandIndex * nBandSpace;
2703 :
2704 35378 : poBand->IRasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize,
2705 : pabyBandData, nBufXSize, nBufYSize, eBufType,
2706 35378 : nPixelSpace, nLineSpace, psExtraArg);
2707 :
2708 35378 : psExtraArg->pfnProgress = pfnProgressGlobal;
2709 :
2710 35378 : std::swap(papoSavedSources, poBand->m_papoSources);
2711 : }
2712 :
2713 12444 : CPLErr eErr = CE_None;
2714 :
2715 : // Use the last band, because when sources reference a GDALProxyDataset,
2716 : // they don't necessary instantiate all underlying rasterbands.
2717 12444 : VRTSourcedRasterBand *poBand =
2718 12444 : static_cast<VRTSourcedRasterBand *>(papoBands[nBands - 1]);
2719 :
2720 12444 : double dfXOff = nXOff;
2721 12444 : double dfYOff = nYOff;
2722 12444 : double dfXSize = nXSize;
2723 12444 : double dfYSize = nYSize;
2724 12444 : if (psExtraArg->bFloatingPointWindowValidity)
2725 : {
2726 11 : dfXOff = psExtraArg->dfXOff;
2727 11 : dfYOff = psExtraArg->dfYOff;
2728 11 : dfXSize = psExtraArg->dfXSize;
2729 11 : dfYSize = psExtraArg->dfYSize;
2730 : }
2731 :
2732 12444 : int nContributingSources = 0;
2733 12444 : int nMaxThreads = 0;
2734 12444 : constexpr int MINIMUM_PIXEL_COUNT_FOR_THREADED_IO = 1000 * 1000;
2735 24888 : if ((static_cast<int64_t>(nBufXSize) * nBufYSize >=
2736 12271 : MINIMUM_PIXEL_COUNT_FOR_THREADED_IO ||
2737 12271 : static_cast<int64_t>(nXSize) * nYSize >=
2738 173 : MINIMUM_PIXEL_COUNT_FOR_THREADED_IO) &&
2739 173 : poBand->CanMultiThreadRasterIO(dfXOff, dfYOff, dfXSize, dfYSize,
2740 172 : nContributingSources) &&
2741 24892 : nContributingSources > 1 &&
2742 4 : (nMaxThreads = VRTDataset::GetNumThreads(this)) > 1)
2743 : {
2744 2 : m_bMultiThreadedRasterIOLastUsed = true;
2745 2 : m_oMapSharedSources.InitMutex();
2746 :
2747 4 : CPLErrorAccumulator errorAccumulator;
2748 2 : std::atomic<bool> bSuccess = true;
2749 2 : CPLWorkerThreadPool *psThreadPool = GDALGetGlobalThreadPool(
2750 2 : std::min(nContributingSources, nMaxThreads));
2751 :
2752 2 : CPLDebugOnly(
2753 : "VRT",
2754 : "IRasterIO(): use optimized "
2755 : "multi-threaded code path for mosaic. "
2756 : "Using %d threads",
2757 : std::min(nContributingSources, psThreadPool->GetThreadCount()));
2758 :
2759 2 : auto oQueue = psThreadPool->CreateJobQueue();
2760 2 : std::atomic<int> nCompletedJobs = 0;
2761 132 : for (auto &poSource : poBand->m_papoSources)
2762 : {
2763 130 : if (!poSource->IsSimpleSource())
2764 0 : continue;
2765 : auto poSimpleSource =
2766 130 : cpl::down_cast<VRTSimpleSource *>(poSource.get());
2767 130 : if (poSimpleSource->DstWindowIntersects(dfXOff, dfYOff, dfXSize,
2768 : dfYSize))
2769 : {
2770 67 : auto psJob = new VRTDatasetRasterIOJob();
2771 67 : psJob->pbSuccess = &bSuccess;
2772 67 : psJob->poErrorAccumulator = &errorAccumulator;
2773 67 : psJob->pnCompletedJobs = &nCompletedJobs;
2774 67 : psJob->eVRTBandDataType = poBand->GetRasterDataType();
2775 67 : psJob->nXOff = nXOff;
2776 67 : psJob->nYOff = nYOff;
2777 67 : psJob->nXSize = nXSize;
2778 67 : psJob->nYSize = nYSize;
2779 67 : psJob->pData = pData;
2780 67 : psJob->nBufXSize = nBufXSize;
2781 67 : psJob->nBufYSize = nBufYSize;
2782 67 : psJob->eBufType = eBufType;
2783 67 : psJob->nBandCount = nBandCount;
2784 67 : psJob->panBandMap = panBandMap;
2785 67 : psJob->nPixelSpace = nPixelSpace;
2786 67 : psJob->nLineSpace = nLineSpace;
2787 67 : psJob->nBandSpace = nBandSpace;
2788 67 : psJob->psExtraArg = psExtraArg;
2789 67 : psJob->poSource = poSimpleSource;
2790 :
2791 67 : if (!oQueue->SubmitJob(VRTDatasetRasterIOJob::Func, psJob))
2792 : {
2793 0 : delete psJob;
2794 0 : bSuccess = false;
2795 0 : break;
2796 : }
2797 : }
2798 : }
2799 :
2800 64 : while (oQueue->WaitEvent())
2801 : {
2802 : // Quite rough progress callback. We could do better by counting
2803 : // the number of contributing pixels.
2804 62 : if (psExtraArg->pfnProgress)
2805 : {
2806 124 : psExtraArg->pfnProgress(double(nCompletedJobs.load()) /
2807 : nContributingSources,
2808 : "", psExtraArg->pProgressData);
2809 : }
2810 : }
2811 :
2812 2 : errorAccumulator.ReplayErrors();
2813 2 : eErr = bSuccess ? CE_None : CE_Failure;
2814 : }
2815 : else
2816 : {
2817 12442 : GDALProgressFunc pfnProgressGlobal = psExtraArg->pfnProgress;
2818 12442 : void *pProgressDataGlobal = psExtraArg->pProgressData;
2819 :
2820 12442 : const int nSources = static_cast<int>(poBand->m_papoSources.size());
2821 25067 : for (int iSource = 0; eErr == CE_None && iSource < nSources;
2822 : iSource++)
2823 : {
2824 12625 : psExtraArg->pfnProgress = GDALScaledProgress;
2825 25250 : psExtraArg->pProgressData = GDALCreateScaledProgress(
2826 12625 : 1.0 * iSource / nSources, 1.0 * (iSource + 1) / nSources,
2827 : pfnProgressGlobal, pProgressDataGlobal);
2828 :
2829 : VRTSimpleSource *poSource = static_cast<VRTSimpleSource *>(
2830 12625 : poBand->m_papoSources[iSource].get());
2831 :
2832 12625 : eErr = poSource->DatasetRasterIO(
2833 : poBand->GetRasterDataType(), nXOff, nYOff, nXSize, nYSize,
2834 : pData, nBufXSize, nBufYSize, eBufType, nBandCount,
2835 : panBandMap, nPixelSpace, nLineSpace, nBandSpace,
2836 : psExtraArg);
2837 :
2838 12625 : GDALDestroyScaledProgress(psExtraArg->pProgressData);
2839 : }
2840 :
2841 12442 : psExtraArg->pfnProgress = pfnProgressGlobal;
2842 12442 : psExtraArg->pProgressData = pProgressDataGlobal;
2843 : }
2844 :
2845 12444 : if (eErr == CE_None && psExtraArg->pfnProgress)
2846 : {
2847 2673 : psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
2848 : }
2849 :
2850 12444 : return eErr;
2851 : }
2852 :
2853 : CPLErr eErr;
2854 569 : if (eRWFlag == GF_Read &&
2855 569 : psExtraArg->eResampleAlg != GRIORA_NearestNeighbour &&
2856 2 : nBufXSize < nXSize && nBufYSize < nYSize && nBandCount > 1)
2857 : {
2858 : // Force going through VRTSourcedRasterBand::IRasterIO(), otherwise
2859 : // GDALDataset::IRasterIOResampled() would be used without source
2860 : // overviews being potentially used.
2861 2 : eErr = GDALDataset::BandBasedRasterIO(
2862 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2863 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
2864 : nBandSpace, psExtraArg);
2865 : }
2866 : else
2867 : {
2868 567 : eErr = GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2869 : pData, nBufXSize, nBufYSize, eBufType,
2870 : nBandCount, panBandMap, nPixelSpace,
2871 : nLineSpace, nBandSpace, psExtraArg);
2872 : }
2873 569 : return eErr;
2874 : }
2875 :
2876 : /************************************************************************/
2877 : /* UnsetPreservedRelativeFilenames() */
2878 : /************************************************************************/
2879 :
2880 133 : void VRTDataset::UnsetPreservedRelativeFilenames()
2881 : {
2882 208 : for (int iBand = 0; iBand < nBands; iBand++)
2883 : {
2884 185 : if (!static_cast<VRTRasterBand *>(papoBands[iBand])
2885 75 : ->IsSourcedRasterBand())
2886 35 : continue;
2887 :
2888 40 : VRTSourcedRasterBand *poBand =
2889 40 : static_cast<VRTSourcedRasterBand *>(papoBands[iBand]);
2890 80 : for (auto &poSource : poBand->m_papoSources)
2891 : {
2892 40 : if (!poSource->IsSimpleSource())
2893 0 : continue;
2894 :
2895 : VRTSimpleSource *poSimpleSource =
2896 40 : static_cast<VRTSimpleSource *>(poSource.get());
2897 40 : poSimpleSource->UnsetPreservedRelativeFilenames();
2898 : }
2899 : }
2900 133 : }
2901 :
2902 : /************************************************************************/
2903 : /* BuildVirtualOverviews() */
2904 : /************************************************************************/
2905 :
2906 5797 : static bool CheckBandForOverview(GDALRasterBand *poBand,
2907 : GDALRasterBand *&poFirstBand, int &nOverviews,
2908 : std::set<std::pair<int, int>> &oSetOvrSizes,
2909 : std::vector<GDALDataset *> &apoOverviewsBak)
2910 : {
2911 5797 : if (!cpl::down_cast<VRTRasterBand *>(poBand)->IsSourcedRasterBand())
2912 0 : return false;
2913 :
2914 : VRTSourcedRasterBand *poVRTBand =
2915 5797 : cpl::down_cast<VRTSourcedRasterBand *>(poBand);
2916 5797 : if (poVRTBand->m_papoSources.size() != 1)
2917 4285 : return false;
2918 1512 : if (!poVRTBand->m_papoSources[0]->IsSimpleSource())
2919 0 : return false;
2920 :
2921 : VRTSimpleSource *poSource =
2922 1512 : cpl::down_cast<VRTSimpleSource *>(poVRTBand->m_papoSources[0].get());
2923 1512 : const char *pszType = poSource->GetType();
2924 1637 : if (pszType != VRTSimpleSource::GetTypeStatic() &&
2925 125 : pszType != VRTComplexSource::GetTypeStatic())
2926 : {
2927 26 : return false;
2928 : }
2929 1486 : GDALRasterBand *poSrcBand = poSource->GetMaskBandMainBand();
2930 1486 : if (!poSrcBand)
2931 1483 : poSrcBand = poSource->GetRasterBand();
2932 1486 : if (poSrcBand == nullptr)
2933 81 : return false;
2934 :
2935 : // To prevent recursion
2936 1405 : apoOverviewsBak.push_back(nullptr);
2937 1405 : const int nOvrCount = poSrcBand->GetOverviewCount();
2938 : oSetOvrSizes.insert(
2939 1405 : std::pair<int, int>(poSrcBand->GetXSize(), poSrcBand->GetYSize()));
2940 1557 : for (int i = 0; i < nOvrCount; ++i)
2941 : {
2942 152 : auto poSrcOvrBand = poSrcBand->GetOverview(i);
2943 152 : if (poSrcOvrBand)
2944 : {
2945 152 : oSetOvrSizes.insert(std::pair<int, int>(poSrcOvrBand->GetXSize(),
2946 304 : poSrcOvrBand->GetYSize()));
2947 : }
2948 : }
2949 1405 : apoOverviewsBak.resize(0);
2950 :
2951 1405 : if (nOvrCount == 0)
2952 1316 : return false;
2953 89 : if (poFirstBand == nullptr)
2954 : {
2955 31 : if (poSrcBand->GetXSize() == 0 || poSrcBand->GetYSize() == 0)
2956 0 : return false;
2957 31 : poFirstBand = poSrcBand;
2958 31 : nOverviews = nOvrCount;
2959 : }
2960 58 : else if (nOvrCount < nOverviews)
2961 0 : nOverviews = nOvrCount;
2962 89 : return true;
2963 : }
2964 :
2965 5871 : void VRTDataset::BuildVirtualOverviews()
2966 : {
2967 : // Currently we expose virtual overviews only if the dataset is made of
2968 : // a single SimpleSource/ComplexSource, in each band.
2969 : // And if the underlying sources have overviews of course
2970 5871 : if (!m_apoOverviews.empty() || !m_apoOverviewsBak.empty())
2971 5843 : return;
2972 :
2973 5736 : int nOverviews = 0;
2974 5736 : GDALRasterBand *poFirstBand = nullptr;
2975 5736 : std::set<std::pair<int, int>> oSetOvrSizes;
2976 :
2977 5819 : for (int iBand = 0; iBand < nBands; iBand++)
2978 : {
2979 5791 : if (!CheckBandForOverview(papoBands[iBand], poFirstBand, nOverviews,
2980 5791 : oSetOvrSizes, m_apoOverviewsBak))
2981 5708 : return;
2982 : }
2983 :
2984 28 : if (m_poMaskBand)
2985 : {
2986 6 : if (!CheckBandForOverview(m_poMaskBand.get(), poFirstBand, nOverviews,
2987 6 : oSetOvrSizes, m_apoOverviewsBak))
2988 0 : return;
2989 : }
2990 28 : if (poFirstBand == nullptr)
2991 : {
2992 : // to make cppcheck happy
2993 0 : CPLAssert(false);
2994 : return;
2995 : }
2996 :
2997 : VRTSourcedRasterBand *l_poVRTBand =
2998 28 : cpl::down_cast<VRTSourcedRasterBand *>(papoBands[0]);
2999 : VRTSimpleSource *poSource =
3000 28 : cpl::down_cast<VRTSimpleSource *>(l_poVRTBand->m_papoSources[0].get());
3001 28 : const double dfDstToSrcXRatio =
3002 28 : poSource->m_dfDstXSize / poSource->m_dfSrcXSize;
3003 28 : const double dfDstToSrcYRatio =
3004 28 : poSource->m_dfDstYSize / poSource->m_dfSrcYSize;
3005 :
3006 68 : for (int j = 0; j < nOverviews; j++)
3007 : {
3008 43 : auto poSrcOvrBand = poFirstBand->GetOverview(j);
3009 43 : if (!poSrcOvrBand)
3010 0 : return;
3011 43 : const double dfXRatio = static_cast<double>(poSrcOvrBand->GetXSize()) /
3012 43 : poFirstBand->GetXSize();
3013 43 : const double dfYRatio = static_cast<double>(poSrcOvrBand->GetYSize()) /
3014 43 : poFirstBand->GetYSize();
3015 43 : if (dfXRatio >= dfDstToSrcXRatio || dfYRatio >= dfDstToSrcYRatio)
3016 : {
3017 5 : continue;
3018 : }
3019 38 : int nOvrXSize = static_cast<int>(0.5 + nRasterXSize * dfXRatio);
3020 38 : int nOvrYSize = static_cast<int>(0.5 + nRasterYSize * dfYRatio);
3021 :
3022 : // Look for a source overview whose size is very close to the
3023 : // theoretical computed one.
3024 38 : bool bSrcOvrMatchFound = false;
3025 127 : for (const auto &ovrSize : oSetOvrSizes)
3026 : {
3027 113 : if (std::abs(ovrSize.first - nOvrXSize) <= 1 &&
3028 24 : std::abs(ovrSize.second - nOvrYSize) <= 1)
3029 : {
3030 24 : bSrcOvrMatchFound = true;
3031 24 : nOvrXSize = ovrSize.first;
3032 24 : nOvrYSize = ovrSize.second;
3033 24 : break;
3034 : }
3035 : }
3036 :
3037 38 : if (!bSrcOvrMatchFound &&
3038 14 : (nOvrXSize < DEFAULT_BLOCK_SIZE || nOvrYSize < DEFAULT_BLOCK_SIZE))
3039 : {
3040 : break;
3041 : }
3042 :
3043 35 : int nBlockXSize = 0;
3044 35 : int nBlockYSize = 0;
3045 35 : l_poVRTBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
3046 35 : if (VRTDataset::IsDefaultBlockSize(nBlockXSize, nRasterXSize))
3047 13 : nBlockXSize = 0;
3048 35 : if (VRTDataset::IsDefaultBlockSize(nBlockYSize, nRasterYSize))
3049 17 : nBlockYSize = 0;
3050 :
3051 : auto poOvrVDS = std::make_unique<VRTDataset>(nOvrXSize, nOvrYSize,
3052 35 : nBlockXSize, nBlockYSize);
3053 :
3054 : const auto CreateOverviewBand =
3055 102 : [&poOvrVDS, nOvrXSize, nOvrYSize, dfXRatio,
3056 : dfYRatio](VRTSourcedRasterBand *poVRTBand)
3057 102 : -> std::unique_ptr<VRTSourcedRasterBand>
3058 : {
3059 : auto poOvrVRTBand = poVRTBand->CloneWithoutSources(
3060 204 : poOvrVDS.get(), nOvrXSize, nOvrYSize);
3061 102 : auto &oOvrVRTBand = *poOvrVRTBand;
3062 102 : auto &oVRTBand = *poVRTBand;
3063 102 : if (typeid(oOvrVRTBand) != typeid(oVRTBand))
3064 : {
3065 0 : CPLError(CE_Failure, CPLE_AppDefined,
3066 : "Lack of CloneWithoutSources() implementation for "
3067 : "subclass %s of VRTSourcedRasterBand",
3068 0 : typeid(oVRTBand).name());
3069 0 : return nullptr;
3070 : }
3071 :
3072 102 : VRTSimpleSource *poSrcSource = cpl::down_cast<VRTSimpleSource *>(
3073 102 : poVRTBand->m_papoSources[0].get());
3074 102 : std::unique_ptr<VRTSimpleSource> poNewSource;
3075 102 : const char *pszType = poSrcSource->GetType();
3076 102 : if (pszType == VRTSimpleSource::GetTypeStatic())
3077 : {
3078 97 : poNewSource = std::make_unique<VRTSimpleSource>(
3079 97 : poSrcSource, dfXRatio, dfYRatio);
3080 : }
3081 5 : else if (pszType == VRTComplexSource::GetTypeStatic())
3082 : {
3083 5 : poNewSource = std::make_unique<VRTComplexSource>(
3084 10 : cpl::down_cast<VRTComplexSource *>(poSrcSource), dfXRatio,
3085 10 : dfYRatio);
3086 : }
3087 : else
3088 : {
3089 0 : CPLAssert(false);
3090 : }
3091 102 : if (poNewSource)
3092 : {
3093 102 : poOvrVRTBand->AddSource(std::move(poNewSource));
3094 : }
3095 :
3096 102 : return poOvrVRTBand;
3097 35 : };
3098 :
3099 129 : for (int i = 0; i < nBands; i++)
3100 : {
3101 : VRTSourcedRasterBand *poSrcBand =
3102 94 : cpl::down_cast<VRTSourcedRasterBand *>(GetRasterBand(i + 1));
3103 94 : auto poOvrBand = CreateOverviewBand(poSrcBand);
3104 94 : if (!poOvrBand)
3105 0 : return;
3106 188 : poOvrVDS->SetBand(poOvrVDS->GetRasterCount() + 1,
3107 94 : std::move(poOvrBand));
3108 : }
3109 :
3110 35 : if (m_poMaskBand)
3111 : {
3112 : VRTSourcedRasterBand *poSrcBand =
3113 8 : cpl::down_cast<VRTSourcedRasterBand *>(m_poMaskBand.get());
3114 8 : auto poOvrBand = CreateOverviewBand(poSrcBand);
3115 8 : if (!poOvrBand)
3116 0 : return;
3117 8 : poOvrVDS->SetMaskBand(std::move(poOvrBand));
3118 : }
3119 :
3120 35 : m_apoOverviews.push_back(poOvrVDS.release());
3121 : }
3122 : }
3123 :
3124 : /************************************************************************/
3125 : /* AddVirtualOverview() */
3126 : /************************************************************************/
3127 :
3128 38 : bool VRTDataset::AddVirtualOverview(int nOvFactor, const char *pszResampling)
3129 : {
3130 38 : if (nRasterXSize / nOvFactor == 0 || nRasterYSize / nOvFactor == 0)
3131 : {
3132 1 : return false;
3133 : }
3134 :
3135 74 : CPLStringList argv;
3136 37 : argv.AddString("-of");
3137 37 : argv.AddString("VRT");
3138 37 : argv.AddString("-outsize");
3139 37 : argv.AddString(CPLSPrintf("%d", nRasterXSize / nOvFactor));
3140 37 : argv.AddString(CPLSPrintf("%d", nRasterYSize / nOvFactor));
3141 37 : argv.AddString("-r");
3142 37 : argv.AddString(pszResampling);
3143 :
3144 37 : int nBlockXSize = 0;
3145 37 : int nBlockYSize = 0;
3146 37 : GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
3147 37 : if (!VRTDataset::IsDefaultBlockSize(nBlockXSize, nRasterXSize))
3148 : {
3149 6 : argv.AddString("-co");
3150 6 : argv.AddString(CPLSPrintf("BLOCKXSIZE=%d", nBlockXSize));
3151 : }
3152 37 : if (!VRTDataset::IsDefaultBlockSize(nBlockYSize, nRasterYSize))
3153 : {
3154 11 : argv.AddString("-co");
3155 11 : argv.AddString(CPLSPrintf("BLOCKYSIZE=%d", nBlockYSize));
3156 : }
3157 :
3158 : GDALTranslateOptions *psOptions =
3159 37 : GDALTranslateOptionsNew(argv.List(), nullptr);
3160 :
3161 : // Add a dummy overview so that BuildVirtualOverviews() doesn't trigger
3162 37 : m_apoOverviews.push_back(nullptr);
3163 37 : CPLAssert(m_bCanTakeRef);
3164 37 : m_bCanTakeRef =
3165 : false; // we don't want hOverviewDS to take a reference on ourselves.
3166 : GDALDatasetH hOverviewDS =
3167 37 : GDALTranslate("", GDALDataset::ToHandle(this), psOptions, nullptr);
3168 37 : m_bCanTakeRef = true;
3169 37 : m_apoOverviews.pop_back();
3170 :
3171 37 : GDALTranslateOptionsFree(psOptions);
3172 37 : if (hOverviewDS == nullptr)
3173 0 : return false;
3174 :
3175 37 : m_anOverviewFactors.push_back(nOvFactor);
3176 37 : m_apoOverviews.push_back(GDALDataset::FromHandle(hOverviewDS));
3177 37 : return true;
3178 : }
3179 :
3180 : /************************************************************************/
3181 : /* IBuildOverviews() */
3182 : /************************************************************************/
3183 :
3184 37 : CPLErr VRTDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
3185 : const int *panOverviewList, int nListBands,
3186 : const int *panBandList,
3187 : GDALProgressFunc pfnProgress,
3188 : void *pProgressData,
3189 : CSLConstList papszOptions)
3190 : {
3191 37 : if (CPLTestBool(CSLFetchNameValueDef(
3192 : papszOptions, "VIRTUAL",
3193 : CPLGetConfigOption("VRT_VIRTUAL_OVERVIEWS", "NO"))))
3194 : {
3195 15 : SetNeedsFlush();
3196 29 : if (nOverviews == 0 ||
3197 14 : (!m_apoOverviews.empty() && m_anOverviewFactors.empty()))
3198 : {
3199 3 : m_anOverviewFactors.clear();
3200 3 : m_apoOverviewsBak.insert(m_apoOverviewsBak.end(),
3201 : m_apoOverviews.begin(),
3202 6 : m_apoOverviews.end());
3203 3 : m_apoOverviews.clear();
3204 : }
3205 15 : m_osOverviewResampling = pszResampling;
3206 40 : for (int i = 0; i < nOverviews; i++)
3207 : {
3208 25 : if (std::find(m_anOverviewFactors.begin(),
3209 : m_anOverviewFactors.end(),
3210 25 : panOverviewList[i]) == m_anOverviewFactors.end())
3211 : {
3212 25 : AddVirtualOverview(panOverviewList[i], pszResampling);
3213 : }
3214 : }
3215 15 : return CE_None;
3216 : }
3217 :
3218 22 : if (!oOvManager.IsInitialized())
3219 : {
3220 0 : const char *pszDesc = GetDescription();
3221 0 : if (pszDesc[0])
3222 : {
3223 0 : oOvManager.Initialize(this, pszDesc);
3224 : }
3225 : }
3226 :
3227 : // Make implicit overviews invisible, but do not destroy them in case they
3228 : // are already used. Should the client do that? Behavior might undefined
3229 : // in GDAL API?
3230 22 : if (!m_apoOverviews.empty())
3231 : {
3232 2 : m_apoOverviewsBak.insert(m_apoOverviewsBak.end(),
3233 4 : m_apoOverviews.begin(), m_apoOverviews.end());
3234 2 : m_apoOverviews.clear();
3235 : }
3236 : else
3237 : {
3238 : // Add a dummy overview so that GDALDataset::IBuildOverviews()
3239 : // doesn't manage to get a virtual implicit overview.
3240 20 : m_apoOverviews.push_back(nullptr);
3241 : }
3242 :
3243 22 : CPLErr eErr = GDALDataset::IBuildOverviews(
3244 : pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
3245 : pfnProgress, pProgressData, papszOptions);
3246 :
3247 22 : m_apoOverviews.clear();
3248 22 : return eErr;
3249 : }
3250 :
3251 : /************************************************************************/
3252 : /* GetShiftedDataset() */
3253 : /* */
3254 : /* Returns true if the VRT is made of a single source that is a simple */
3255 : /* in its full resolution. */
3256 : /************************************************************************/
3257 :
3258 54 : bool VRTDataset::GetShiftedDataset(int nXOff, int nYOff, int nXSize, int nYSize,
3259 : GDALDataset *&poSrcDataset, int &nSrcXOff,
3260 : int &nSrcYOff)
3261 : {
3262 54 : if (!CheckCompatibleForDatasetIO())
3263 0 : return false;
3264 :
3265 54 : VRTSourcedRasterBand *poVRTBand =
3266 54 : static_cast<VRTSourcedRasterBand *>(papoBands[0]);
3267 54 : if (poVRTBand->m_papoSources.size() != 1)
3268 0 : return false;
3269 :
3270 : VRTSimpleSource *poSource =
3271 54 : static_cast<VRTSimpleSource *>(poVRTBand->m_papoSources[0].get());
3272 :
3273 54 : GDALRasterBand *poBand = poSource->GetRasterBand();
3274 104 : if (!poBand || poSource->GetMaskBandMainBand() ||
3275 50 : poBand->GetRasterDataType() != poVRTBand->GetRasterDataType())
3276 27 : return false;
3277 :
3278 27 : poSrcDataset = poBand->GetDataset();
3279 27 : if (!poSrcDataset)
3280 0 : return false;
3281 :
3282 27 : double dfReqXOff = 0.0;
3283 27 : double dfReqYOff = 0.0;
3284 27 : double dfReqXSize = 0.0;
3285 27 : double dfReqYSize = 0.0;
3286 27 : int nReqXOff = 0;
3287 27 : int nReqYOff = 0;
3288 27 : int nReqXSize = 0;
3289 27 : int nReqYSize = 0;
3290 27 : int nOutXOff = 0;
3291 27 : int nOutYOff = 0;
3292 27 : int nOutXSize = 0;
3293 27 : int nOutYSize = 0;
3294 27 : bool bError = false;
3295 27 : if (!poSource->GetSrcDstWindow(
3296 : nXOff, nYOff, nXSize, nYSize, nXSize, nYSize,
3297 : GRIORA_NearestNeighbour, &dfReqXOff, &dfReqYOff, &dfReqXSize,
3298 : &dfReqYSize, &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
3299 : &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize, bError))
3300 0 : return false;
3301 :
3302 27 : if (nReqXSize != nXSize || nReqYSize != nYSize || nReqXSize != nOutXSize ||
3303 25 : nReqYSize != nOutYSize)
3304 2 : return false;
3305 :
3306 25 : nSrcXOff = nReqXOff;
3307 25 : nSrcYOff = nReqYOff;
3308 25 : return true;
3309 : }
3310 :
3311 : /************************************************************************/
3312 : /* GetCompressionFormats() */
3313 : /************************************************************************/
3314 :
3315 0 : CPLStringList VRTDataset::GetCompressionFormats(int nXOff, int nYOff,
3316 : int nXSize, int nYSize,
3317 : int nBandCount,
3318 : const int *panBandList)
3319 : {
3320 : GDALDataset *poSrcDataset;
3321 : int nSrcXOff;
3322 : int nSrcYOff;
3323 0 : if (!GetShiftedDataset(nXOff, nYOff, nXSize, nYSize, poSrcDataset, nSrcXOff,
3324 : nSrcYOff))
3325 0 : return CPLStringList();
3326 : return poSrcDataset->GetCompressionFormats(nSrcXOff, nSrcYOff, nXSize,
3327 0 : nYSize, nBandCount, panBandList);
3328 : }
3329 :
3330 : /************************************************************************/
3331 : /* ReadCompressedData() */
3332 : /************************************************************************/
3333 :
3334 54 : CPLErr VRTDataset::ReadCompressedData(const char *pszFormat, int nXOff,
3335 : int nYOff, int nXSize, int nYSize,
3336 : int nBandCount, const int *panBandList,
3337 : void **ppBuffer, size_t *pnBufferSize,
3338 : char **ppszDetailedFormat)
3339 : {
3340 : GDALDataset *poSrcDataset;
3341 : int nSrcXOff;
3342 : int nSrcYOff;
3343 54 : if (!GetShiftedDataset(nXOff, nYOff, nXSize, nYSize, poSrcDataset, nSrcXOff,
3344 : nSrcYOff))
3345 29 : return CE_Failure;
3346 25 : return poSrcDataset->ReadCompressedData(
3347 : pszFormat, nSrcXOff, nSrcYOff, nXSize, nYSize, nBandCount, panBandList,
3348 25 : ppBuffer, pnBufferSize, ppszDetailedFormat);
3349 : }
3350 :
3351 : /************************************************************************/
3352 : /* ClearStatistics() */
3353 : /************************************************************************/
3354 :
3355 6 : void VRTDataset::ClearStatistics()
3356 : {
3357 18 : for (int i = 1; i <= nBands; ++i)
3358 : {
3359 12 : bool bChanged = false;
3360 12 : GDALRasterBand *poBand = GetRasterBand(i);
3361 12 : CSLConstList papszOldMD = poBand->GetMetadata();
3362 24 : CPLStringList aosNewMD;
3363 32 : for (const char *pszMDItem : cpl::Iterate(papszOldMD))
3364 : {
3365 20 : if (STARTS_WITH_CI(pszMDItem, "STATISTICS_"))
3366 : {
3367 20 : bChanged = true;
3368 : }
3369 : else
3370 : {
3371 0 : aosNewMD.AddString(pszMDItem);
3372 : }
3373 : }
3374 12 : if (bChanged)
3375 : {
3376 4 : poBand->SetMetadata(aosNewMD.List());
3377 : }
3378 : }
3379 :
3380 6 : GDALDataset::ClearStatistics();
3381 6 : }
3382 :
3383 : /************************************************************************/
3384 : /* VRTMapSharedResources::LockGuard() */
3385 : /************************************************************************/
3386 :
3387 : std::unique_ptr<std::lock_guard<std::mutex>>
3388 204239 : VRTMapSharedResources::LockGuard() const
3389 : {
3390 204239 : std::unique_ptr<std::lock_guard<std::mutex>> poLockGuard;
3391 204239 : if (m_bUseMutex)
3392 : {
3393 268 : poLockGuard = std::make_unique<std::lock_guard<std::mutex>>(m_oMutex);
3394 : }
3395 204239 : return poLockGuard;
3396 : }
3397 :
3398 : /************************************************************************/
3399 : /* VRTMapSharedResources::Get() */
3400 : /************************************************************************/
3401 :
3402 102257 : GDALDataset *VRTMapSharedResources::Get(const std::string &osKey) const
3403 : {
3404 102257 : auto poLockGuard = LockGuard();
3405 102257 : CPL_IGNORE_RET_VAL(poLockGuard);
3406 102257 : auto oIter = m_oMap.find(osKey);
3407 102257 : GDALDataset *poRet = nullptr;
3408 102257 : if (oIter != m_oMap.end())
3409 98980 : poRet = oIter->second;
3410 204514 : return poRet;
3411 : }
3412 :
3413 : /************************************************************************/
3414 : /* VRTMapSharedResources::Insert() */
3415 : /************************************************************************/
3416 :
3417 101982 : void VRTMapSharedResources::Insert(const std::string &osKey, GDALDataset *poDS)
3418 : {
3419 101982 : auto poLockGuard = LockGuard();
3420 101982 : CPL_IGNORE_RET_VAL(poLockGuard);
3421 101982 : m_oMap[osKey] = poDS;
3422 101982 : }
3423 :
3424 : /************************************************************************/
3425 : /* VRTMapSharedResources::InitMutex() */
3426 : /************************************************************************/
3427 :
3428 20 : void VRTMapSharedResources::InitMutex()
3429 : {
3430 20 : m_bUseMutex = true;
3431 20 : }
3432 :
3433 : /*! @endcond */
|