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