Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Virtual GDAL Datasets
4 : * Purpose: Implementation of VRTSourcedRasterBand
5 : * Author: Frank Warmerdam <warmerdam@pobox.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_port.h"
31 : #include "gdal_vrt.h"
32 : #include "vrtdataset.h"
33 :
34 : #include <algorithm>
35 : #include <cassert>
36 : #include <cmath>
37 : #include <cstddef>
38 : #include <cstdio>
39 : #include <cstdlib>
40 : #include <cstring>
41 : #include <limits>
42 : #include <mutex>
43 : #include <set>
44 : #include <string>
45 :
46 : #include "cpl_conv.h"
47 : #include "cpl_error.h"
48 : #include "cpl_hash_set.h"
49 : #include "cpl_minixml.h"
50 : #include "cpl_progress.h"
51 : #include "cpl_quad_tree.h"
52 : #include "cpl_string.h"
53 : #include "cpl_vsi.h"
54 : #include "gdal.h"
55 : #include "gdal_priv.h"
56 : #include "gdal_thread_pool.h"
57 : #include "ogr_geometry.h"
58 :
59 : /*! @cond Doxygen_Suppress */
60 :
61 : /************************************************************************/
62 : /* ==================================================================== */
63 : /* VRTSourcedRasterBand */
64 : /* ==================================================================== */
65 : /************************************************************************/
66 :
67 : /************************************************************************/
68 : /* VRTSourcedRasterBand() */
69 : /************************************************************************/
70 :
71 1181 : VRTSourcedRasterBand::VRTSourcedRasterBand(GDALDataset *poDSIn, int nBandIn)
72 : {
73 1181 : VRTRasterBand::Initialize(poDSIn->GetRasterXSize(),
74 : poDSIn->GetRasterYSize());
75 :
76 1181 : poDS = poDSIn;
77 1181 : nBand = nBandIn;
78 1181 : }
79 :
80 : /************************************************************************/
81 : /* VRTSourcedRasterBand() */
82 : /************************************************************************/
83 :
84 0 : VRTSourcedRasterBand::VRTSourcedRasterBand(GDALDataType eType, int nXSize,
85 0 : int nYSize)
86 : {
87 0 : VRTRasterBand::Initialize(nXSize, nYSize);
88 :
89 0 : eDataType = eType;
90 0 : }
91 :
92 : /************************************************************************/
93 : /* VRTSourcedRasterBand() */
94 : /************************************************************************/
95 :
96 278 : VRTSourcedRasterBand::VRTSourcedRasterBand(GDALDataset *poDSIn, int nBandIn,
97 : GDALDataType eType, int nXSize,
98 278 : int nYSize)
99 278 : : VRTSourcedRasterBand(poDSIn, nBandIn, eType, nXSize, nYSize, 0, 0)
100 : {
101 278 : }
102 :
103 : /************************************************************************/
104 : /* VRTSourcedRasterBand() */
105 : /************************************************************************/
106 :
107 3691 : VRTSourcedRasterBand::VRTSourcedRasterBand(GDALDataset *poDSIn, int nBandIn,
108 : GDALDataType eType, int nXSize,
109 : int nYSize, int nBlockXSizeIn,
110 3691 : int nBlockYSizeIn)
111 : {
112 3691 : VRTRasterBand::Initialize(nXSize, nYSize);
113 :
114 3691 : poDS = poDSIn;
115 3691 : nBand = nBandIn;
116 :
117 3691 : eDataType = eType;
118 3691 : if (nBlockXSizeIn > 0)
119 3403 : nBlockXSize = nBlockXSizeIn;
120 3691 : if (nBlockYSizeIn > 0)
121 3406 : nBlockYSize = nBlockYSizeIn;
122 3691 : }
123 :
124 : /************************************************************************/
125 : /* ~VRTSourcedRasterBand() */
126 : /************************************************************************/
127 :
128 9553 : VRTSourcedRasterBand::~VRTSourcedRasterBand()
129 :
130 : {
131 4872 : VRTSourcedRasterBand::CloseDependentDatasets();
132 4872 : CSLDestroy(m_papszSourceList);
133 9553 : }
134 :
135 : /************************************************************************/
136 : /* CanIRasterIOBeForwardedToEachSource() */
137 : /************************************************************************/
138 :
139 47050 : bool VRTSourcedRasterBand::CanIRasterIOBeForwardedToEachSource(
140 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
141 : int nBufXSize, int nBufYSize, GDALRasterIOExtraArg *psExtraArg) const
142 : {
143 : // If resampling with non-nearest neighbour, we need to be careful
144 : // if the VRT band exposes a nodata value, but the sources do not have it.
145 : // To also avoid edge effects on sources when downsampling, use the
146 : // base implementation of IRasterIO() (that is acquiring sources at their
147 : // nominal resolution, and then downsampling), but only if none of the
148 : // contributing sources have overviews.
149 47050 : if (eRWFlag == GF_Read && (nXSize != nBufXSize || nYSize != nBufYSize) &&
150 5660 : psExtraArg->eResampleAlg != GRIORA_NearestNeighbour && nSources != 0)
151 : {
152 17 : bool bSourceHasOverviews = false;
153 17 : const bool bIsDownsampling = (nBufXSize < nXSize && nBufYSize < nYSize);
154 17 : int nContributingSources = 0;
155 17 : bool bSourceFullySatisfiesRequest = true;
156 33 : for (int i = 0; i < nSources; i++)
157 : {
158 21 : if (!papoSources[i]->IsSimpleSource())
159 : {
160 0 : return false;
161 : }
162 : else
163 : {
164 21 : VRTSimpleSource *const poSource =
165 21 : static_cast<VRTSimpleSource *>(papoSources[i]);
166 :
167 21 : double dfXOff = nXOff;
168 21 : double dfYOff = nYOff;
169 21 : double dfXSize = nXSize;
170 21 : double dfYSize = nYSize;
171 21 : if (psExtraArg->bFloatingPointWindowValidity)
172 : {
173 1 : dfXOff = psExtraArg->dfXOff;
174 1 : dfYOff = psExtraArg->dfYOff;
175 1 : dfXSize = psExtraArg->dfXSize;
176 1 : dfYSize = psExtraArg->dfYSize;
177 : }
178 :
179 : // The window we will actually request from the source raster
180 : // band.
181 21 : double dfReqXOff = 0.0;
182 21 : double dfReqYOff = 0.0;
183 21 : double dfReqXSize = 0.0;
184 21 : double dfReqYSize = 0.0;
185 21 : int nReqXOff = 0;
186 21 : int nReqYOff = 0;
187 21 : int nReqXSize = 0;
188 21 : int nReqYSize = 0;
189 :
190 : // The window we will actual set _within_ the pData buffer.
191 21 : int nOutXOff = 0;
192 21 : int nOutYOff = 0;
193 21 : int nOutXSize = 0;
194 21 : int nOutYSize = 0;
195 :
196 21 : bool bError = false;
197 21 : if (!poSource->GetSrcDstWindow(
198 : dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
199 : &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize,
200 : &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff,
201 : &nOutYOff, &nOutXSize, &nOutYSize, bError))
202 : {
203 0 : continue;
204 : }
205 21 : auto poBand = poSource->GetRasterBand();
206 21 : if (poBand == nullptr)
207 : {
208 5 : return false;
209 : }
210 21 : ++nContributingSources;
211 21 : if (!(nOutXOff == 0 && nOutYOff == 0 &&
212 20 : nOutXSize == nBufXSize && nOutYSize == nBufYSize))
213 5 : bSourceFullySatisfiesRequest = false;
214 21 : if (m_bNoDataValueSet)
215 : {
216 8 : int bSrcHasNoData = FALSE;
217 : const double dfSrcNoData =
218 8 : poBand->GetNoDataValue(&bSrcHasNoData);
219 8 : if (!bSrcHasNoData || dfSrcNoData != m_dfNoDataValue)
220 : {
221 5 : return false;
222 : }
223 : }
224 16 : if (bIsDownsampling)
225 : {
226 13 : if (poBand->GetOverviewCount() != 0)
227 : {
228 4 : bSourceHasOverviews = true;
229 : }
230 : }
231 : }
232 : }
233 12 : if (bIsDownsampling && !bSourceHasOverviews &&
234 3 : (nContributingSources > 1 || !bSourceFullySatisfiesRequest))
235 : {
236 3 : return false;
237 : }
238 : }
239 47042 : return true;
240 : }
241 :
242 : /************************************************************************/
243 : /* IRasterIO() */
244 : /************************************************************************/
245 :
246 47048 : CPLErr VRTSourcedRasterBand::IRasterIO(
247 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
248 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
249 : GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
250 :
251 : {
252 47048 : if (eRWFlag == GF_Write)
253 : {
254 0 : CPLError(CE_Failure, CPLE_AppDefined,
255 : "Writing through VRTSourcedRasterBand is not supported.");
256 0 : return CE_Failure;
257 : }
258 :
259 94096 : const std::string osFctId("VRTSourcedRasterBand::IRasterIO");
260 94096 : GDALAntiRecursionGuard oGuard(osFctId);
261 47048 : if (oGuard.GetCallDepth() >= 32)
262 : {
263 0 : CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
264 0 : return CE_Failure;
265 : }
266 :
267 141144 : GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
268 : // Allow 2 recursion depths on the same dataset for non-nearest resampling
269 47048 : if (oGuard2.GetCallDepth() > 2)
270 : {
271 1 : CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
272 1 : return CE_Failure;
273 : }
274 :
275 : /* ==================================================================== */
276 : /* Do we have overviews that would be appropriate to satisfy */
277 : /* this request? */
278 : /* ==================================================================== */
279 47047 : auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
280 47047 : if (l_poDS &&
281 94012 : l_poDS->m_apoOverviews.empty() && // do not use virtual overviews
282 94094 : (nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0)
283 : {
284 1 : if (OverviewRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
285 : nBufXSize, nBufYSize, eBufType, nPixelSpace,
286 1 : nLineSpace, psExtraArg) == CE_None)
287 0 : return CE_None;
288 : }
289 :
290 : // If resampling with non-nearest neighbour, we need to be careful
291 : // if the VRT band exposes a nodata value, but the sources do not have it.
292 : // To also avoid edge effects on sources when downsampling, use the
293 : // base implementation of IRasterIO() (that is acquiring sources at their
294 : // nominal resolution, and then downsampling), but only if none of the
295 : // contributing sources have overviews.
296 47047 : if (l_poDS && !CanIRasterIOBeForwardedToEachSource(
297 : eRWFlag, nXOff, nYOff, nXSize, nYSize, nBufXSize,
298 : nBufYSize, psExtraArg))
299 : {
300 6 : const bool bBackupEnabledOverviews = l_poDS->AreOverviewsEnabled();
301 6 : if (!l_poDS->m_apoOverviews.empty() && l_poDS->AreOverviewsEnabled())
302 : {
303 : // Disable use of implicit overviews to avoid infinite
304 : // recursion
305 1 : l_poDS->SetEnableOverviews(false);
306 : }
307 6 : const auto eErr = GDALRasterBand::IRasterIO(
308 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
309 : eBufType, nPixelSpace, nLineSpace, psExtraArg);
310 6 : l_poDS->SetEnableOverviews(bBackupEnabledOverviews);
311 6 : return eErr;
312 : }
313 :
314 : /* -------------------------------------------------------------------- */
315 : /* Initialize the buffer to some background value. Use the */
316 : /* nodata value if available. */
317 : /* -------------------------------------------------------------------- */
318 47041 : if (SkipBufferInitialization())
319 : {
320 : // Do nothing
321 : }
322 37347 : else if (nPixelSpace == GDALGetDataTypeSizeBytes(eBufType) &&
323 17974 : (!m_bNoDataValueSet || m_dfNoDataValue == 0.0))
324 : {
325 17714 : if (nLineSpace == nBufXSize * nPixelSpace)
326 : {
327 17633 : memset(pData, 0, static_cast<size_t>(nBufYSize * nLineSpace));
328 : }
329 : else
330 : {
331 10673 : for (int iLine = 0; iLine < nBufYSize; iLine++)
332 : {
333 10592 : memset(static_cast<GByte *>(pData) +
334 10592 : static_cast<GIntBig>(iLine) * nLineSpace,
335 10592 : 0, static_cast<size_t>(nBufXSize * nPixelSpace));
336 : }
337 : }
338 : }
339 : else
340 : {
341 1659 : double dfWriteValue = 0.0;
342 1659 : if (m_bNoDataValueSet)
343 260 : dfWriteValue = m_dfNoDataValue;
344 :
345 47563 : for (int iLine = 0; iLine < nBufYSize; iLine++)
346 : {
347 45904 : GDALCopyWords(&dfWriteValue, GDT_Float64, 0,
348 : static_cast<GByte *>(pData) +
349 45904 : static_cast<GIntBig>(nLineSpace) * iLine,
350 : eBufType, static_cast<int>(nPixelSpace), nBufXSize);
351 : }
352 : }
353 :
354 47041 : GDALProgressFunc const pfnProgressGlobal = psExtraArg->pfnProgress;
355 47041 : void *const pProgressDataGlobal = psExtraArg->pProgressData;
356 :
357 : /* -------------------------------------------------------------------- */
358 : /* Overlay each source in turn over top this. */
359 : /* -------------------------------------------------------------------- */
360 47041 : CPLErr eErr = CE_None;
361 47041 : VRTSource::WorkingState oWorkingState;
362 109542 : for (int iSource = 0; eErr == CE_None && iSource < nSources; iSource++)
363 : {
364 62501 : psExtraArg->pfnProgress = GDALScaledProgress;
365 125002 : psExtraArg->pProgressData = GDALCreateScaledProgress(
366 62501 : 1.0 * iSource / nSources, 1.0 * (iSource + 1) / nSources,
367 : pfnProgressGlobal, pProgressDataGlobal);
368 62501 : if (psExtraArg->pProgressData == nullptr)
369 62157 : psExtraArg->pfnProgress = nullptr;
370 :
371 62501 : eErr = papoSources[iSource]->RasterIO(
372 : eDataType, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
373 : nBufYSize, eBufType, nPixelSpace, nLineSpace, psExtraArg,
374 62501 : l_poDS ? l_poDS->m_oWorkingState : oWorkingState);
375 :
376 62501 : GDALDestroyScaledProgress(psExtraArg->pProgressData);
377 : }
378 :
379 47041 : psExtraArg->pfnProgress = pfnProgressGlobal;
380 47041 : psExtraArg->pProgressData = pProgressDataGlobal;
381 :
382 47041 : return eErr;
383 : }
384 :
385 : /************************************************************************/
386 : /* IGetDataCoverageStatus() */
387 : /************************************************************************/
388 :
389 : #ifndef HAVE_GEOS
390 : int VRTSourcedRasterBand::IGetDataCoverageStatus(
391 : int /* nXOff */, int /* nYOff */, int /* nXSize */, int /* nYSize */,
392 : int /* nMaskFlagStop */, double *pdfDataPct)
393 : {
394 : // TODO(rouault): Should this set pdfDataPct?
395 : if (pdfDataPct != nullptr)
396 : *pdfDataPct = -1.0;
397 : return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
398 : GDAL_DATA_COVERAGE_STATUS_DATA;
399 : }
400 : #else
401 501 : int VRTSourcedRasterBand::IGetDataCoverageStatus(int nXOff, int nYOff,
402 : int nXSize, int nYSize,
403 : int nMaskFlagStop,
404 : double *pdfDataPct)
405 : {
406 501 : if (pdfDataPct != nullptr)
407 4 : *pdfDataPct = -1.0;
408 501 : int nStatus = 0;
409 :
410 501 : OGRPolygon *poPolyNonCoveredBySources = new OGRPolygon();
411 501 : OGRLinearRing *poLR = new OGRLinearRing();
412 501 : poLR->addPoint(nXOff, nYOff);
413 501 : poLR->addPoint(nXOff, nYOff + nYSize);
414 501 : poLR->addPoint(nXOff + nXSize, nYOff + nYSize);
415 501 : poLR->addPoint(nXOff + nXSize, nYOff);
416 501 : poLR->addPoint(nXOff, nYOff);
417 501 : poPolyNonCoveredBySources->addRingDirectly(poLR);
418 :
419 511 : for (int iSource = 0; iSource < nSources; iSource++)
420 : {
421 506 : if (!papoSources[iSource]->IsSimpleSource())
422 : {
423 0 : delete poPolyNonCoveredBySources;
424 : return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
425 0 : GDAL_DATA_COVERAGE_STATUS_DATA;
426 : }
427 506 : VRTSimpleSource *poSS =
428 506 : static_cast<VRTSimpleSource *>(papoSources[iSource]);
429 : // Check if the AOI is fully inside the source
430 506 : double dfDstXOff = std::max(0.0, poSS->m_dfDstXOff);
431 506 : double dfDstYOff = std::max(0.0, poSS->m_dfDstYOff);
432 506 : double dfDstXSize = poSS->m_dfDstXSize;
433 506 : double dfDstYSize = poSS->m_dfDstYSize;
434 506 : auto l_poBand = poSS->GetRasterBand();
435 506 : if (!l_poBand)
436 0 : continue;
437 506 : if (dfDstXSize == -1)
438 10 : dfDstXSize = l_poBand->GetXSize() - dfDstXOff;
439 506 : if (dfDstYSize == -1)
440 10 : dfDstYSize = l_poBand->GetYSize() - dfDstYOff;
441 :
442 506 : if (nXOff >= dfDstXOff && nYOff >= dfDstYOff &&
443 497 : nXOff + nXSize <= dfDstXOff + dfDstXSize &&
444 481 : nYOff + nYSize <= dfDstYOff + dfDstYSize)
445 : {
446 481 : if (pdfDataPct)
447 1 : *pdfDataPct = 100.0;
448 481 : delete poPolyNonCoveredBySources;
449 481 : return GDAL_DATA_COVERAGE_STATUS_DATA;
450 : }
451 : // Check intersection of bounding boxes.
452 25 : if (dfDstXOff + dfDstXSize > nXOff && dfDstYOff + dfDstYSize > nYOff &&
453 19 : dfDstXOff < nXOff + nXSize && dfDstYOff < nYOff + nYSize)
454 : {
455 17 : nStatus |= GDAL_DATA_COVERAGE_STATUS_DATA;
456 17 : if (poPolyNonCoveredBySources != nullptr)
457 : {
458 17 : OGRPolygon oPolySource;
459 17 : poLR = new OGRLinearRing();
460 17 : poLR->addPoint(dfDstXOff, dfDstYOff);
461 17 : poLR->addPoint(dfDstXOff, dfDstYOff + dfDstYSize);
462 17 : poLR->addPoint(dfDstXOff + dfDstXSize, dfDstYOff + dfDstYSize);
463 17 : poLR->addPoint(dfDstXOff + dfDstXSize, dfDstYOff);
464 17 : poLR->addPoint(dfDstXOff, dfDstYOff);
465 17 : oPolySource.addRingDirectly(poLR);
466 : OGRGeometry *poRes =
467 17 : poPolyNonCoveredBySources->Difference(&oPolySource);
468 17 : if (poRes != nullptr && poRes->IsEmpty())
469 : {
470 1 : delete poRes;
471 1 : if (pdfDataPct)
472 1 : *pdfDataPct = 100.0;
473 1 : delete poPolyNonCoveredBySources;
474 1 : return GDAL_DATA_COVERAGE_STATUS_DATA;
475 : }
476 32 : else if (poRes != nullptr &&
477 16 : poRes->getGeometryType() == wkbPolygon)
478 : {
479 16 : delete poPolyNonCoveredBySources;
480 16 : poPolyNonCoveredBySources = poRes->toPolygon();
481 : }
482 : else
483 : {
484 0 : delete poRes;
485 0 : delete poPolyNonCoveredBySources;
486 0 : poPolyNonCoveredBySources = nullptr;
487 : }
488 : }
489 : }
490 24 : if (nMaskFlagStop != 0 && (nStatus & nMaskFlagStop) != 0)
491 : {
492 14 : delete poPolyNonCoveredBySources;
493 14 : return nStatus;
494 : }
495 : }
496 5 : if (poPolyNonCoveredBySources != nullptr)
497 : {
498 5 : if (!poPolyNonCoveredBySources->IsEmpty())
499 5 : nStatus |= GDAL_DATA_COVERAGE_STATUS_EMPTY;
500 5 : if (pdfDataPct != nullptr)
501 2 : *pdfDataPct = 100.0 * (1.0 - poPolyNonCoveredBySources->get_Area() /
502 2 : nXSize / nYSize);
503 : }
504 5 : delete poPolyNonCoveredBySources;
505 5 : return nStatus;
506 : }
507 : #endif // HAVE_GEOS
508 :
509 : /************************************************************************/
510 : /* IReadBlock() */
511 : /************************************************************************/
512 :
513 1003 : CPLErr VRTSourcedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
514 : void *pImage)
515 :
516 : {
517 1003 : const int nPixelSize = GDALGetDataTypeSizeBytes(eDataType);
518 :
519 1003 : int nReadXSize = 0;
520 1003 : if ((nBlockXOff + 1) * nBlockXSize > GetXSize())
521 8 : nReadXSize = GetXSize() - nBlockXOff * nBlockXSize;
522 : else
523 995 : nReadXSize = nBlockXSize;
524 :
525 1003 : int nReadYSize = 0;
526 1003 : if ((nBlockYOff + 1) * nBlockYSize > GetYSize())
527 32 : nReadYSize = GetYSize() - nBlockYOff * nBlockYSize;
528 : else
529 971 : nReadYSize = nBlockYSize;
530 :
531 : GDALRasterIOExtraArg sExtraArg;
532 1003 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
533 :
534 1003 : return IRasterIO(
535 1003 : GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, nReadXSize,
536 : nReadYSize, pImage, nReadXSize, nReadYSize, eDataType, nPixelSize,
537 2006 : static_cast<GSpacing>(nPixelSize) * nBlockXSize, &sExtraArg);
538 : }
539 :
540 : /************************************************************************/
541 : /* CPLGettimeofday() */
542 : /************************************************************************/
543 :
544 : #if defined(_WIN32) && !defined(__CYGWIN__)
545 : #include <sys/timeb.h>
546 :
547 : namespace
548 : {
549 : struct CPLTimeVal
550 : {
551 : time_t tv_sec; /* seconds */
552 : long tv_usec; /* and microseconds */
553 : };
554 : } // namespace
555 :
556 : static int CPLGettimeofday(struct CPLTimeVal *tp, void * /* timezonep*/)
557 : {
558 : struct _timeb theTime;
559 :
560 : _ftime(&theTime);
561 : tp->tv_sec = static_cast<time_t>(theTime.time);
562 : tp->tv_usec = theTime.millitm * 1000;
563 : return 0;
564 : }
565 : #else
566 : #include <sys/time.h> /* for gettimeofday() */
567 : #define CPLTimeVal timeval
568 : #define CPLGettimeofday(t, u) gettimeofday(t, u)
569 : #endif
570 :
571 : /************************************************************************/
572 : /* CanUseSourcesMinMaxImplementations() */
573 : /************************************************************************/
574 :
575 106 : bool VRTSourcedRasterBand::CanUseSourcesMinMaxImplementations()
576 : {
577 : const char *pszUseSources =
578 106 : CPLGetConfigOption("VRT_MIN_MAX_FROM_SOURCES", nullptr);
579 106 : if (pszUseSources)
580 0 : return CPLTestBool(pszUseSources);
581 :
582 : // Use heuristics to determine if we are going to use the source
583 : // GetMinimum() or GetMaximum() implementation: all the sources must be
584 : // "simple" sources with a dataset description that match a "regular" file
585 : // on the filesystem, whose open time and GetMinimum()/GetMaximum()
586 : // implementations we hope to be fast enough.
587 : // In case of doubt return FALSE.
588 : struct CPLTimeVal tvStart;
589 106 : memset(&tvStart, 0, sizeof(CPLTimeVal));
590 106 : if (nSources > 1)
591 10 : CPLGettimeofday(&tvStart, nullptr);
592 234 : for (int iSource = 0; iSource < nSources; iSource++)
593 : {
594 130 : if (!(papoSources[iSource]->IsSimpleSource()))
595 2 : return false;
596 128 : VRTSimpleSource *const poSimpleSource =
597 128 : static_cast<VRTSimpleSource *>(papoSources[iSource]);
598 128 : const char *pszFilename = poSimpleSource->m_osSrcDSName.c_str();
599 : // /vsimem/ should be fast.
600 128 : if (STARTS_WITH(pszFilename, "/vsimem/"))
601 10 : continue;
602 : // but not other /vsi filesystems
603 118 : if (STARTS_WITH(pszFilename, "/vsi"))
604 0 : return false;
605 118 : char ch = '\0';
606 : // We will assume that filenames that are only with ascii characters
607 : // are real filenames and so we will not try to 'stat' them.
608 1814 : for (int i = 0; (ch = pszFilename[i]) != '\0'; i++)
609 : {
610 1716 : if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
611 408 : (ch >= '0' && ch <= '9') || ch == ':' || ch == '/' ||
612 142 : ch == '\\' || ch == ' ' || ch == '.' || ch == '_'))
613 4 : break;
614 : }
615 118 : if (ch != '\0')
616 : {
617 : // Otherwise do a real filesystem check.
618 : VSIStatBuf sStat;
619 4 : if (VSIStat(pszFilename, &sStat) != 0)
620 0 : return false;
621 4 : if (nSources > 1)
622 : {
623 : struct CPLTimeVal tvCur;
624 4 : CPLGettimeofday(&tvCur, nullptr);
625 4 : if (tvCur.tv_sec - tvStart.tv_sec +
626 4 : (tvCur.tv_usec - tvStart.tv_usec) * 1e-6 >
627 : 1)
628 0 : return false;
629 : }
630 : }
631 : }
632 104 : return true;
633 : }
634 :
635 : /************************************************************************/
636 : /* GetMinimum() */
637 : /************************************************************************/
638 :
639 57 : double VRTSourcedRasterBand::GetMinimum(int *pbSuccess)
640 : {
641 57 : const char *const pszValue = GetMetadataItem("STATISTICS_MINIMUM");
642 57 : if (pszValue != nullptr)
643 : {
644 4 : if (pbSuccess != nullptr)
645 4 : *pbSuccess = TRUE;
646 :
647 4 : return CPLAtofM(pszValue);
648 : }
649 :
650 53 : if (!CanUseSourcesMinMaxImplementations())
651 1 : return GDALRasterBand::GetMinimum(pbSuccess);
652 :
653 104 : const std::string osFctId("VRTSourcedRasterBand::GetMinimum");
654 104 : GDALAntiRecursionGuard oGuard(osFctId);
655 52 : if (oGuard.GetCallDepth() >= 32)
656 : {
657 0 : CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
658 0 : if (pbSuccess != nullptr)
659 0 : *pbSuccess = FALSE;
660 0 : return 0;
661 : }
662 :
663 156 : GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
664 52 : if (oGuard2.GetCallDepth() >= 2)
665 : {
666 0 : CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
667 0 : if (pbSuccess != nullptr)
668 0 : *pbSuccess = FALSE;
669 0 : return 0;
670 : }
671 :
672 : struct CPLTimeVal tvStart;
673 52 : memset(&tvStart, 0, sizeof(CPLTimeVal));
674 52 : if (nSources > 1)
675 5 : CPLGettimeofday(&tvStart, nullptr);
676 52 : double dfMin = 0;
677 57 : for (int iSource = 0; iSource < nSources; iSource++)
678 : {
679 53 : int bSuccess = FALSE;
680 : double dfSourceMin =
681 53 : papoSources[iSource]->GetMinimum(GetXSize(), GetYSize(), &bSuccess);
682 53 : if (!bSuccess)
683 : {
684 48 : dfMin = GDALRasterBand::GetMinimum(pbSuccess);
685 48 : return dfMin;
686 : }
687 :
688 5 : if (iSource == 0 || dfSourceMin < dfMin)
689 : {
690 4 : dfMin = dfSourceMin;
691 4 : if (dfMin == 0 && eDataType == GDT_Byte)
692 0 : break;
693 : }
694 5 : if (nSources > 1)
695 : {
696 : struct CPLTimeVal tvCur;
697 2 : CPLGettimeofday(&tvCur, nullptr);
698 2 : if (tvCur.tv_sec - tvStart.tv_sec +
699 2 : (tvCur.tv_usec - tvStart.tv_usec) * 1e-6 >
700 : 1)
701 : {
702 0 : return GDALRasterBand::GetMinimum(pbSuccess);
703 : }
704 : }
705 : }
706 :
707 4 : if (pbSuccess != nullptr)
708 4 : *pbSuccess = TRUE;
709 :
710 4 : return dfMin;
711 : }
712 :
713 : /************************************************************************/
714 : /* GetMaximum() */
715 : /************************************************************************/
716 :
717 57 : double VRTSourcedRasterBand::GetMaximum(int *pbSuccess)
718 : {
719 57 : const char *const pszValue = GetMetadataItem("STATISTICS_MAXIMUM");
720 57 : if (pszValue != nullptr)
721 : {
722 4 : if (pbSuccess != nullptr)
723 4 : *pbSuccess = TRUE;
724 :
725 4 : return CPLAtofM(pszValue);
726 : }
727 :
728 53 : if (!CanUseSourcesMinMaxImplementations())
729 1 : return GDALRasterBand::GetMaximum(pbSuccess);
730 :
731 104 : const std::string osFctId("VRTSourcedRasterBand::GetMaximum");
732 104 : GDALAntiRecursionGuard oGuard(osFctId);
733 52 : if (oGuard.GetCallDepth() >= 32)
734 : {
735 0 : CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
736 0 : if (pbSuccess != nullptr)
737 0 : *pbSuccess = FALSE;
738 0 : return 0;
739 : }
740 :
741 156 : GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
742 52 : if (oGuard2.GetCallDepth() >= 2)
743 : {
744 0 : CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
745 0 : if (pbSuccess != nullptr)
746 0 : *pbSuccess = FALSE;
747 0 : return 0;
748 : }
749 :
750 : struct CPLTimeVal tvStart;
751 52 : memset(&tvStart, 0, sizeof(CPLTimeVal));
752 52 : if (nSources > 1)
753 5 : CPLGettimeofday(&tvStart, nullptr);
754 52 : double dfMax = 0;
755 54 : for (int iSource = 0; iSource < nSources; iSource++)
756 : {
757 52 : int bSuccess = FALSE;
758 : const double dfSourceMax =
759 52 : papoSources[iSource]->GetMaximum(GetXSize(), GetYSize(), &bSuccess);
760 52 : if (!bSuccess)
761 : {
762 48 : dfMax = GDALRasterBand::GetMaximum(pbSuccess);
763 48 : return dfMax;
764 : }
765 :
766 4 : if (iSource == 0 || dfSourceMax > dfMax)
767 : {
768 4 : dfMax = dfSourceMax;
769 4 : if (dfMax == 255.0 && eDataType == GDT_Byte)
770 2 : break;
771 : }
772 2 : if (nSources > 1)
773 : {
774 : struct CPLTimeVal tvCur;
775 0 : CPLGettimeofday(&tvCur, nullptr);
776 0 : if (tvCur.tv_sec - tvStart.tv_sec +
777 0 : (tvCur.tv_usec - tvStart.tv_usec) * 1e-6 >
778 : 1)
779 : {
780 0 : return GDALRasterBand::GetMaximum(pbSuccess);
781 : }
782 : }
783 : }
784 :
785 4 : if (pbSuccess != nullptr)
786 4 : *pbSuccess = TRUE;
787 :
788 4 : return dfMax;
789 : }
790 :
791 : /************************************************************************/
792 : /* IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange() */
793 : /************************************************************************/
794 :
795 : /* Returns true if the VRT raster band consists of non-overlapping simple
796 : * sources or complex sources that don't change values, and use the full extent
797 : * of the source band.
798 : */
799 47 : bool VRTSourcedRasterBand::
800 : IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange(
801 : bool bAllowMaxValAdjustment) const
802 : {
803 47 : bool bRet = true;
804 : CPLRectObj sGlobalBounds;
805 47 : sGlobalBounds.minx = 0;
806 47 : sGlobalBounds.miny = 0;
807 47 : sGlobalBounds.maxx = nRasterXSize;
808 47 : sGlobalBounds.maxy = nRasterYSize;
809 47 : CPLQuadTree *hQuadTree = CPLQuadTreeCreate(&sGlobalBounds, nullptr);
810 110 : for (int i = 0; i < nSources; ++i)
811 : {
812 72 : if (!papoSources[i]->IsSimpleSource())
813 : {
814 1 : bRet = false;
815 9 : break;
816 : }
817 :
818 71 : auto poSimpleSource = cpl::down_cast<VRTSimpleSource *>(papoSources[i]);
819 71 : auto poComplexSource = dynamic_cast<VRTComplexSource *>(papoSources[i]);
820 71 : if (poComplexSource)
821 : {
822 10 : if (!EQUAL(poComplexSource->GetType(), "ComplexSource") ||
823 5 : !poComplexSource->AreValuesUnchanged())
824 : {
825 2 : bRet = false;
826 2 : break;
827 : }
828 : }
829 : else
830 : {
831 66 : if (!EQUAL(poSimpleSource->GetType(), "SimpleSource"))
832 : {
833 0 : bRet = false;
834 0 : break;
835 : }
836 : }
837 :
838 69 : if (!bAllowMaxValAdjustment && poSimpleSource->NeedMaxValAdjustment())
839 : {
840 2 : bRet = false;
841 2 : break;
842 : }
843 :
844 67 : auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
845 134 : if (poSimpleSourceBand == nullptr ||
846 67 : poSimpleSourceBand->GetRasterDataType() != eDataType)
847 : {
848 0 : bRet = false;
849 0 : break;
850 : }
851 :
852 67 : double dfReqXOff = 0.0;
853 67 : double dfReqYOff = 0.0;
854 67 : double dfReqXSize = 0.0;
855 67 : double dfReqYSize = 0.0;
856 67 : int nReqXOff = 0;
857 67 : int nReqYOff = 0;
858 67 : int nReqXSize = 0;
859 67 : int nReqYSize = 0;
860 67 : int nOutXOff = 0;
861 67 : int nOutYOff = 0;
862 67 : int nOutXSize = 0;
863 67 : int nOutYSize = 0;
864 :
865 67 : bool bError = false;
866 201 : if (!poSimpleSource->GetSrcDstWindow(
867 67 : 0, 0, nRasterXSize, nRasterYSize, nRasterXSize, nRasterYSize,
868 : &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, &nReqXOff,
869 : &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
870 67 : &nOutXSize, &nOutYSize, bError) ||
871 67 : nReqXOff != 0 || nReqYOff != 0 ||
872 66 : nReqXSize != poSimpleSourceBand->GetXSize() ||
873 66 : nReqYSize != poSimpleSourceBand->GetYSize() ||
874 134 : nOutXSize != nReqXSize || nOutYSize != nReqYSize)
875 : {
876 1 : bRet = false;
877 1 : break;
878 : }
879 :
880 : CPLRectObj sBounds;
881 66 : constexpr double EPSILON = 1e-1;
882 66 : sBounds.minx = nOutXOff + EPSILON;
883 66 : sBounds.miny = nOutYOff + EPSILON;
884 66 : sBounds.maxx = nOutXOff + nOutXSize - EPSILON;
885 66 : sBounds.maxy = nOutYOff + nOutYSize - EPSILON;
886 :
887 : // Check that the new source doesn't overlap an existing one.
888 66 : int nFeatureCount = 0;
889 66 : void **pahRet = CPLQuadTreeSearch(hQuadTree, &sBounds, &nFeatureCount);
890 66 : CPLFree(pahRet);
891 66 : if (nFeatureCount != 0)
892 : {
893 3 : bRet = false;
894 3 : break;
895 : }
896 :
897 63 : CPLQuadTreeInsertWithBounds(
898 63 : hQuadTree, reinterpret_cast<void *>(static_cast<uintptr_t>(i)),
899 : &sBounds);
900 : }
901 47 : CPLQuadTreeDestroy(hQuadTree);
902 :
903 47 : return bRet;
904 : }
905 :
906 : /************************************************************************/
907 : /* ComputeRasterMinMax() */
908 : /************************************************************************/
909 :
910 22 : CPLErr VRTSourcedRasterBand::ComputeRasterMinMax(int bApproxOK,
911 : double *adfMinMax)
912 : {
913 : /* -------------------------------------------------------------------- */
914 : /* Does the driver already know the min/max? */
915 : /* -------------------------------------------------------------------- */
916 22 : if (bApproxOK)
917 : {
918 3 : int bSuccessMin = FALSE;
919 3 : int bSuccessMax = FALSE;
920 :
921 3 : const double dfMin = GetMinimum(&bSuccessMin);
922 3 : const double dfMax = GetMaximum(&bSuccessMax);
923 :
924 3 : if (bSuccessMin && bSuccessMax)
925 : {
926 1 : adfMinMax[0] = dfMin;
927 1 : adfMinMax[1] = dfMax;
928 1 : return CE_None;
929 : }
930 : }
931 :
932 42 : const std::string osFctId("VRTSourcedRasterBand::ComputeRasterMinMax");
933 42 : GDALAntiRecursionGuard oGuard(osFctId);
934 21 : if (oGuard.GetCallDepth() >= 32)
935 : {
936 0 : CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
937 0 : return CE_Failure;
938 : }
939 :
940 63 : GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
941 21 : if (oGuard2.GetCallDepth() >= 2)
942 : {
943 0 : CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
944 0 : return CE_Failure;
945 : }
946 :
947 : /* -------------------------------------------------------------------- */
948 : /* If we have overview bands, use them for min/max. */
949 : /* -------------------------------------------------------------------- */
950 21 : if (bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews())
951 : {
952 : GDALRasterBand *const poBand =
953 0 : GetRasterSampleOverview(GDALSTAT_APPROX_NUMSAMPLES);
954 :
955 0 : if (poBand != nullptr && poBand != this)
956 : {
957 0 : auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
958 0 : if (l_poDS && !l_poDS->m_apoOverviews.empty() &&
959 0 : dynamic_cast<VRTSourcedRasterBand *>(poBand) != nullptr)
960 : {
961 0 : auto apoTmpOverviews = std::move(l_poDS->m_apoOverviews);
962 0 : l_poDS->m_apoOverviews.clear();
963 0 : auto eErr = poBand->GDALRasterBand::ComputeRasterMinMax(
964 : TRUE, adfMinMax);
965 0 : l_poDS->m_apoOverviews = std::move(apoTmpOverviews);
966 0 : return eErr;
967 : }
968 : else
969 : {
970 0 : return poBand->ComputeRasterMinMax(TRUE, adfMinMax);
971 : }
972 : }
973 : }
974 :
975 21 : if (IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange(
976 : /*bAllowMaxValAdjustment = */ true))
977 : {
978 18 : CPLDebugOnly(
979 : "VRT", "ComputeRasterMinMax(): use optimized code path for mosaic");
980 :
981 18 : uint64_t nCoveredArea = 0;
982 :
983 : // If source bands have nodata value, we can't use source band's
984 : // ComputeRasterMinMax() as we don't know if there are pixels actually
985 : // at the nodata value, so use ComputeStatistics() instead that takes
986 : // into account that aspect.
987 18 : bool bUseComputeStatistics = false;
988 37 : for (int i = 0; i < nSources; ++i)
989 : {
990 : auto poSimpleSource =
991 23 : cpl::down_cast<VRTSimpleSource *>(papoSources[i]);
992 23 : auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
993 23 : int bHasNoData = FALSE;
994 23 : CPL_IGNORE_RET_VAL(poSimpleSourceBand->GetNoDataValue(&bHasNoData));
995 23 : if (bHasNoData)
996 : {
997 4 : bUseComputeStatistics = true;
998 4 : break;
999 : }
1000 19 : nCoveredArea +=
1001 19 : static_cast<uint64_t>(poSimpleSourceBand->GetXSize()) *
1002 19 : poSimpleSourceBand->GetYSize();
1003 : }
1004 :
1005 18 : if (bUseComputeStatistics)
1006 : {
1007 : CPLErr eErr;
1008 4 : std::string osLastErrorMsg;
1009 : {
1010 8 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1011 4 : CPLErrorReset();
1012 : eErr =
1013 8 : ComputeStatistics(bApproxOK, &adfMinMax[0], &adfMinMax[1],
1014 4 : nullptr, nullptr, nullptr, nullptr);
1015 4 : if (eErr == CE_Failure)
1016 : {
1017 1 : osLastErrorMsg = CPLGetLastErrorMsg();
1018 : }
1019 : }
1020 4 : if (eErr == CE_Failure)
1021 : {
1022 1 : if (strstr(osLastErrorMsg.c_str(), "no valid pixels found") !=
1023 : nullptr)
1024 : {
1025 1 : ReportError(CE_Failure, CPLE_AppDefined,
1026 : "Failed to compute min/max, no valid pixels "
1027 : "found in sampling.");
1028 : }
1029 : else
1030 : {
1031 0 : ReportError(CE_Failure, CPLE_AppDefined, "%s",
1032 : osLastErrorMsg.c_str());
1033 : }
1034 : }
1035 4 : return eErr;
1036 : }
1037 :
1038 14 : bool bSignedByte = false;
1039 14 : if (eDataType == GDT_Byte)
1040 : {
1041 8 : EnablePixelTypeSignedByteWarning(false);
1042 : const char *pszPixelType =
1043 8 : GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
1044 8 : EnablePixelTypeSignedByteWarning(true);
1045 8 : bSignedByte =
1046 8 : pszPixelType != nullptr && EQUAL(pszPixelType, "SIGNEDBYTE");
1047 : }
1048 :
1049 14 : double dfGlobalMin = std::numeric_limits<double>::max();
1050 14 : double dfGlobalMax = -std::numeric_limits<double>::max();
1051 :
1052 : // If the mosaic doesn't cover the whole VRT raster, take into account
1053 : // VRT nodata value.
1054 14 : if (nCoveredArea < static_cast<uint64_t>(nRasterXSize) * nRasterYSize)
1055 : {
1056 2 : if (m_bNoDataValueSet && m_bHideNoDataValue)
1057 : {
1058 1 : if (IsNoDataValueInDataTypeRange())
1059 : {
1060 1 : dfGlobalMin = std::min(dfGlobalMin, m_dfNoDataValue);
1061 1 : dfGlobalMax = std::max(dfGlobalMax, m_dfNoDataValue);
1062 : }
1063 : }
1064 1 : else if (!m_bNoDataValueSet)
1065 : {
1066 0 : dfGlobalMin = std::min(dfGlobalMin, 0.0);
1067 0 : dfGlobalMax = std::max(dfGlobalMax, 0.0);
1068 : }
1069 : }
1070 :
1071 31 : for (int i = 0; i < nSources; ++i)
1072 : {
1073 : auto poSimpleSource =
1074 18 : cpl::down_cast<VRTSimpleSource *>(papoSources[i]);
1075 18 : double adfMinMaxSource[2] = {0};
1076 :
1077 18 : auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
1078 36 : CPLErr eErr = poSimpleSourceBand->ComputeRasterMinMax(
1079 18 : bApproxOK, adfMinMaxSource);
1080 18 : if (eErr == CE_Failure)
1081 : {
1082 1 : return CE_Failure;
1083 : }
1084 : else
1085 : {
1086 18 : if (poSimpleSource->NeedMaxValAdjustment())
1087 : {
1088 2 : const double dfMaxValue =
1089 2 : static_cast<double>(poSimpleSource->m_nMaxValue);
1090 2 : adfMinMaxSource[0] =
1091 2 : std::min(adfMinMaxSource[0], dfMaxValue);
1092 2 : adfMinMaxSource[1] =
1093 2 : std::min(adfMinMaxSource[1], dfMaxValue);
1094 : }
1095 :
1096 18 : if (m_bNoDataValueSet && !m_bHideNoDataValue &&
1097 3 : m_dfNoDataValue >= adfMinMaxSource[0] &&
1098 1 : m_dfNoDataValue <= adfMinMaxSource[1])
1099 : {
1100 1 : return GDALRasterBand::ComputeRasterMinMax(bApproxOK,
1101 1 : adfMinMax);
1102 : }
1103 :
1104 17 : dfGlobalMin = std::min(dfGlobalMin, adfMinMaxSource[0]);
1105 17 : dfGlobalMax = std::max(dfGlobalMax, adfMinMaxSource[1]);
1106 : }
1107 :
1108 : // Early exit if we know we reached theoretical bounds
1109 17 : if (eDataType == GDT_Byte && !bSignedByte && dfGlobalMin == 0.0 &&
1110 0 : dfGlobalMax == 255.0)
1111 : {
1112 0 : break;
1113 : }
1114 : }
1115 :
1116 13 : if (dfGlobalMin > dfGlobalMax)
1117 : {
1118 0 : adfMinMax[0] = 0.0;
1119 0 : adfMinMax[1] = 0.0;
1120 0 : ReportError(CE_Failure, CPLE_AppDefined,
1121 : "Failed to compute min/max, no valid pixels found in "
1122 : "sampling.");
1123 0 : return CE_Failure;
1124 : }
1125 :
1126 13 : adfMinMax[0] = dfGlobalMin;
1127 13 : adfMinMax[1] = dfGlobalMax;
1128 13 : return CE_None;
1129 : }
1130 : else
1131 : {
1132 3 : return GDALRasterBand::ComputeRasterMinMax(bApproxOK, adfMinMax);
1133 : }
1134 : }
1135 :
1136 : // #define naive_update_not_used
1137 :
1138 : namespace
1139 : {
1140 : struct Context
1141 : {
1142 : CPL_DISALLOW_COPY_ASSIGN(Context)
1143 : Context() = default;
1144 :
1145 : // Protected by mutex
1146 : std::mutex oMutex{};
1147 : uint64_t nTotalIteratedPixels = 0;
1148 : uint64_t nLastReportedPixels = 0;
1149 : bool bFailure = false;
1150 : bool bFallbackToBase = false;
1151 : // End of protected by mutex
1152 :
1153 : bool bApproxOK = false;
1154 : GDALProgressFunc pfnProgress = nullptr;
1155 : void *pProgressData = nullptr;
1156 :
1157 : // VRTSourcedRasterBand parameters
1158 : double dfNoDataValue = 0;
1159 : bool bNoDataValueSet = false;
1160 : bool bHideNoDataValue = false;
1161 :
1162 : double dfGlobalMin = std::numeric_limits<double>::max();
1163 : double dfGlobalMax = -std::numeric_limits<double>::max();
1164 : #ifdef naive_update_not_used
1165 : // This native method uses the fact that stddev = sqrt(sum_of_squares/N -
1166 : // mean^2) and that thus sum_of_squares = N * (stddev^2 + mean^2)
1167 : double dfGlobalSum = 0;
1168 : double dfGlobalSumSquare = 0;
1169 : #else
1170 : // This method uses
1171 : // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Parallel_algorithm
1172 : // which is more numerically robust
1173 : double dfGlobalMean = 0;
1174 : double dfGlobalM2 = 0;
1175 : #endif
1176 : uint64_t nGlobalValidPixels = 0;
1177 : uint64_t nTotalPixelsOfSources = 0;
1178 : };
1179 : } // namespace
1180 :
1181 3 : static void UpdateStatsWithConstantValue(Context &sContext, double dfVal,
1182 : uint64_t nPixelCount)
1183 : {
1184 3 : sContext.dfGlobalMin = std::min(sContext.dfGlobalMin, dfVal);
1185 3 : sContext.dfGlobalMax = std::max(sContext.dfGlobalMax, dfVal);
1186 : #ifdef naive_update_not_used
1187 : sContext.dfGlobalSum += dfVal * nPixelCount;
1188 : sContext.dfGlobalSumSquare += dfVal * dfVal * nPixelCount;
1189 : #else
1190 3 : const auto nNewGlobalValidPixels =
1191 3 : sContext.nGlobalValidPixels + nPixelCount;
1192 3 : const double dfDelta = dfVal - sContext.dfGlobalMean;
1193 3 : sContext.dfGlobalMean += nPixelCount * dfDelta / nNewGlobalValidPixels;
1194 3 : sContext.dfGlobalM2 += dfDelta * dfDelta * nPixelCount *
1195 3 : sContext.nGlobalValidPixels / nNewGlobalValidPixels;
1196 : #endif
1197 3 : sContext.nGlobalValidPixels += nPixelCount;
1198 3 : }
1199 :
1200 : /************************************************************************/
1201 : /* ComputeStatistics() */
1202 : /************************************************************************/
1203 :
1204 28 : CPLErr VRTSourcedRasterBand::ComputeStatistics(int bApproxOK, double *pdfMin,
1205 : double *pdfMax, double *pdfMean,
1206 : double *pdfStdDev,
1207 : GDALProgressFunc pfnProgress,
1208 : void *pProgressData)
1209 :
1210 : {
1211 56 : const std::string osFctId("VRTSourcedRasterBand::ComputeStatistics");
1212 56 : GDALAntiRecursionGuard oGuard(osFctId);
1213 28 : if (oGuard.GetCallDepth() >= 32)
1214 : {
1215 0 : CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1216 0 : return CE_Failure;
1217 : }
1218 :
1219 84 : GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
1220 28 : if (oGuard2.GetCallDepth() >= 2)
1221 : {
1222 0 : CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1223 0 : return CE_Failure;
1224 : }
1225 :
1226 : /* -------------------------------------------------------------------- */
1227 : /* If we have overview bands, use them for statistics. */
1228 : /* -------------------------------------------------------------------- */
1229 28 : if (bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews())
1230 : {
1231 : GDALRasterBand *const poBand =
1232 2 : GetRasterSampleOverview(GDALSTAT_APPROX_NUMSAMPLES);
1233 :
1234 2 : if (poBand != nullptr && poBand != this)
1235 : {
1236 2 : auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
1237 : CPLErr eErr;
1238 3 : if (l_poDS && !l_poDS->m_apoOverviews.empty() &&
1239 1 : dynamic_cast<VRTSourcedRasterBand *>(poBand) != nullptr)
1240 : {
1241 2 : auto apoTmpOverviews = std::move(l_poDS->m_apoOverviews);
1242 1 : l_poDS->m_apoOverviews.clear();
1243 1 : eErr = poBand->GDALRasterBand::ComputeStatistics(
1244 : TRUE, pdfMin, pdfMax, pdfMean, pdfStdDev, pfnProgress,
1245 : pProgressData);
1246 1 : l_poDS->m_apoOverviews = std::move(apoTmpOverviews);
1247 : }
1248 : else
1249 : {
1250 1 : eErr = poBand->ComputeStatistics(TRUE, pdfMin, pdfMax, pdfMean,
1251 : pdfStdDev, pfnProgress,
1252 1 : pProgressData);
1253 : }
1254 2 : if (eErr == CE_None && pdfMin && pdfMax && pdfMean && pdfStdDev)
1255 : {
1256 2 : SetMetadataItem("STATISTICS_APPROXIMATE", "YES");
1257 2 : SetMetadataItem(
1258 : "STATISTICS_VALID_PERCENT",
1259 2 : poBand->GetMetadataItem("STATISTICS_VALID_PERCENT"));
1260 2 : SetStatistics(*pdfMin, *pdfMax, *pdfMean, *pdfStdDev);
1261 : }
1262 2 : return eErr;
1263 : }
1264 : }
1265 :
1266 26 : if (IsMosaicOfNonOverlappingSimpleSourcesOfFullRasterNoResAndTypeChange(
1267 : /*bAllowMaxValAdjustment = */ false))
1268 : {
1269 20 : Context sContext;
1270 20 : sContext.bApproxOK = bApproxOK;
1271 20 : sContext.dfNoDataValue = m_dfNoDataValue;
1272 20 : sContext.bNoDataValueSet = m_bNoDataValueSet;
1273 20 : sContext.bHideNoDataValue = m_bHideNoDataValue;
1274 20 : sContext.pfnProgress = pfnProgress;
1275 20 : sContext.pProgressData = pProgressData;
1276 :
1277 : struct Job
1278 : {
1279 : Context *psContext = nullptr;
1280 : GDALRasterBand *poRasterBand = nullptr;
1281 : uint64_t nPixelCount = 0;
1282 : uint64_t nLastAddedPixels = 0;
1283 : uint64_t nValidPixels = 0;
1284 : double dfMin = 0;
1285 : double dfMax = 0;
1286 : double dfMean = 0;
1287 : double dfStdDev = 0;
1288 :
1289 58 : static int CPL_STDCALL ProgressFunc(double dfComplete,
1290 : const char *pszMessage,
1291 : void *pProgressArg)
1292 : {
1293 58 : auto psJob = static_cast<Job *>(pProgressArg);
1294 58 : auto psContext = psJob->psContext;
1295 58 : const uint64_t nNewAddedPixels =
1296 : dfComplete == 1.0
1297 58 : ? psJob->nPixelCount
1298 : : static_cast<uint64_t>(
1299 54 : dfComplete * psJob->nPixelCount + 0.5);
1300 : const auto nUpdateThreshold =
1301 116 : std::min(psContext->nTotalPixelsOfSources / 1000,
1302 58 : static_cast<uint64_t>(1000 * 1000));
1303 116 : std::lock_guard<std::mutex> oLock(psContext->oMutex);
1304 58 : psContext->nTotalIteratedPixels +=
1305 58 : (nNewAddedPixels - psJob->nLastAddedPixels);
1306 58 : psJob->nLastAddedPixels = nNewAddedPixels;
1307 58 : if (psContext->nTotalIteratedPixels ==
1308 58 : psContext->nTotalPixelsOfSources)
1309 : {
1310 2 : psContext->nLastReportedPixels =
1311 2 : psContext->nTotalIteratedPixels;
1312 2 : return psContext->pfnProgress(1.0, pszMessage,
1313 2 : psContext->pProgressData);
1314 : }
1315 56 : else if (psContext->nTotalIteratedPixels -
1316 56 : psContext->nLastReportedPixels >
1317 : nUpdateThreshold)
1318 : {
1319 48 : psContext->nLastReportedPixels =
1320 48 : psContext->nTotalIteratedPixels;
1321 96 : return psContext->pfnProgress(
1322 48 : static_cast<double>(psContext->nTotalIteratedPixels) /
1323 48 : psContext->nTotalPixelsOfSources,
1324 48 : pszMessage, psContext->pProgressData);
1325 : }
1326 8 : return 1;
1327 : }
1328 :
1329 30 : static void UpdateStats(const Job *psJob)
1330 : {
1331 30 : const auto nValidPixels = psJob->nValidPixels;
1332 30 : auto psContext = psJob->psContext;
1333 30 : if (nValidPixels > 0)
1334 : {
1335 22 : psContext->dfGlobalMin =
1336 22 : std::min(psContext->dfGlobalMin, psJob->dfMin);
1337 22 : psContext->dfGlobalMax =
1338 22 : std::max(psContext->dfGlobalMax, psJob->dfMax);
1339 : #ifdef naive_update_not_used
1340 : psContext->dfGlobalSum += nValidPixels * psJob->dfMean;
1341 : psContext->dfGlobalSumSquare +=
1342 : nValidPixels * (psJob->dfStdDev * psJob->dfStdDev +
1343 : psJob->dfMean * psJob->dfMean);
1344 : psContext->nGlobalValidPixels += nValidPixels;
1345 : #else
1346 22 : const auto nNewGlobalValidPixels =
1347 22 : psContext->nGlobalValidPixels + nValidPixels;
1348 22 : const double dfDelta =
1349 22 : psJob->dfMean - psContext->dfGlobalMean;
1350 22 : psContext->dfGlobalMean +=
1351 22 : nValidPixels * dfDelta / nNewGlobalValidPixels;
1352 22 : psContext->dfGlobalM2 +=
1353 22 : nValidPixels * psJob->dfStdDev * psJob->dfStdDev +
1354 22 : dfDelta * dfDelta * nValidPixels *
1355 22 : psContext->nGlobalValidPixels /
1356 : nNewGlobalValidPixels;
1357 22 : psContext->nGlobalValidPixels = nNewGlobalValidPixels;
1358 : #endif
1359 : }
1360 30 : int bHasNoData = FALSE;
1361 : const double dfNoDataValue =
1362 30 : psJob->poRasterBand->GetNoDataValue(&bHasNoData);
1363 9 : if (nValidPixels < psJob->nPixelCount && bHasNoData &&
1364 48 : !std::isnan(dfNoDataValue) &&
1365 9 : (!psContext->bNoDataValueSet ||
1366 7 : dfNoDataValue != psContext->dfNoDataValue))
1367 : {
1368 : const auto eBandDT =
1369 2 : psJob->poRasterBand->GetRasterDataType();
1370 : // Check that the band nodata value is in the range of the
1371 : // original raster type
1372 : GByte abyTempBuffer[2 * sizeof(double)];
1373 2 : CPLAssert(GDALGetDataTypeSizeBytes(eBandDT) <=
1374 : static_cast<int>(sizeof(abyTempBuffer)));
1375 2 : GDALCopyWords(&dfNoDataValue, GDT_Float64, 0,
1376 : &abyTempBuffer[0], eBandDT, 0, 1);
1377 2 : double dfNoDataValueAfter = dfNoDataValue;
1378 2 : GDALCopyWords(&abyTempBuffer[0], eBandDT, 0,
1379 : &dfNoDataValueAfter, GDT_Float64, 0, 1);
1380 4 : if (!std::isfinite(dfNoDataValue) ||
1381 2 : std::fabs(dfNoDataValueAfter - dfNoDataValue) < 1.0)
1382 : {
1383 2 : UpdateStatsWithConstantValue(
1384 : *psContext, dfNoDataValueAfter,
1385 2 : psJob->nPixelCount - nValidPixels);
1386 : }
1387 : }
1388 30 : }
1389 : };
1390 :
1391 32 : const auto JobRunner = [](void *pData)
1392 : {
1393 32 : auto psJob = static_cast<Job *>(pData);
1394 32 : auto psContext = psJob->psContext;
1395 : {
1396 32 : std::lock_guard<std::mutex> oLock(psContext->oMutex);
1397 32 : if (psContext->bFallbackToBase || psContext->bFailure)
1398 0 : return;
1399 : }
1400 :
1401 32 : auto poSimpleSourceBand = psJob->poRasterBand;
1402 32 : psJob->nPixelCount =
1403 32 : static_cast<uint64_t>(poSimpleSourceBand->GetXSize()) *
1404 32 : poSimpleSourceBand->GetYSize();
1405 :
1406 32 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1407 32 : CPLErr eErr = poSimpleSourceBand->ComputeStatistics(
1408 32 : psContext->bApproxOK, &psJob->dfMin, &psJob->dfMax,
1409 : &psJob->dfMean, &psJob->dfStdDev,
1410 37 : psContext->pfnProgress == nullptr ||
1411 5 : psContext->pfnProgress == GDALDummyProgress
1412 : ? GDALDummyProgress
1413 : : Job::ProgressFunc,
1414 32 : psJob);
1415 : const char *pszValidPercent =
1416 32 : poSimpleSourceBand->GetMetadataItem("STATISTICS_VALID_PERCENT");
1417 32 : psJob->nValidPixels =
1418 : pszValidPercent
1419 32 : ? static_cast<uint64_t>(CPLAtof(pszValidPercent) *
1420 32 : psJob->nPixelCount / 100.0)
1421 : : psJob->nPixelCount;
1422 32 : if (eErr == CE_Failure)
1423 : {
1424 16 : if (pszValidPercent != nullptr &&
1425 8 : CPLAtof(pszValidPercent) == 0.0)
1426 : {
1427 : // ok: no valid sample
1428 : }
1429 : else
1430 : {
1431 0 : std::lock_guard<std::mutex> oLock(psContext->oMutex);
1432 0 : psContext->bFailure = true;
1433 : }
1434 : }
1435 : else
1436 : {
1437 24 : int bHasNoData = FALSE;
1438 24 : CPL_IGNORE_RET_VAL(
1439 24 : psJob->poRasterBand->GetNoDataValue(&bHasNoData));
1440 24 : if (!bHasNoData && psContext->bNoDataValueSet &&
1441 8 : !psContext->bHideNoDataValue &&
1442 6 : psContext->dfNoDataValue >= psJob->dfMin &&
1443 2 : psContext->dfNoDataValue <= psJob->dfMax)
1444 : {
1445 2 : std::lock_guard<std::mutex> oLock(psContext->oMutex);
1446 2 : psJob->psContext->bFallbackToBase = true;
1447 2 : return;
1448 : }
1449 : }
1450 : };
1451 :
1452 20 : CPLWorkerThreadPool *poThreadPool = nullptr;
1453 20 : const char *pszValue = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
1454 20 : if (pszValue)
1455 : {
1456 : int nThreads =
1457 1 : EQUAL(pszValue, "ALL_CPUS") ? CPLGetNumCPUs() : atoi(pszValue);
1458 1 : if (nThreads > 1024)
1459 0 : nThreads = 1024; // to please Coverity
1460 1 : if (nThreads > 1)
1461 : {
1462 : // Check that all sources refer to different datasets
1463 : // before allowing multithreaded access
1464 : // If the datasets belong to the MEM driver, check GDALDataset*
1465 : // pointer values. Otherwise use dataset name.
1466 2 : std::set<std::string> oSetDatasetNames;
1467 2 : std::set<GDALDataset *> oSetDatasetPointers;
1468 3 : for (int i = 0; i < nSources; ++i)
1469 : {
1470 : auto poSimpleSource =
1471 2 : cpl::down_cast<VRTSimpleSource *>(papoSources[i]);
1472 2 : assert(poSimpleSource);
1473 2 : auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
1474 2 : assert(poSimpleSourceBand);
1475 2 : auto poSourceDataset = poSimpleSourceBand->GetDataset();
1476 2 : if (poSourceDataset == nullptr)
1477 : {
1478 0 : nThreads = 0;
1479 0 : break;
1480 : }
1481 2 : auto poDriver = poSourceDataset->GetDriver();
1482 2 : if (poDriver && EQUAL(poDriver->GetDescription(), "MEM"))
1483 : {
1484 2 : if (oSetDatasetPointers.find(poSourceDataset) !=
1485 4 : oSetDatasetPointers.end())
1486 : {
1487 0 : nThreads = 0;
1488 0 : break;
1489 : }
1490 2 : oSetDatasetPointers.insert(poSourceDataset);
1491 : }
1492 : else
1493 : {
1494 0 : if (oSetDatasetNames.find(
1495 0 : poSourceDataset->GetDescription()) !=
1496 0 : oSetDatasetNames.end())
1497 : {
1498 0 : nThreads = 0;
1499 0 : break;
1500 : }
1501 : oSetDatasetNames.insert(
1502 0 : poSourceDataset->GetDescription());
1503 : }
1504 : }
1505 1 : if (nThreads > 1)
1506 : {
1507 1 : poThreadPool = GDALGetGlobalThreadPool(nThreads);
1508 : }
1509 : }
1510 : }
1511 :
1512 : // Compute total number of pixels of sources
1513 53 : for (int i = 0; i < nSources; ++i)
1514 : {
1515 33 : auto poSimpleSource =
1516 33 : static_cast<VRTSimpleSource *>(papoSources[i]);
1517 33 : assert(poSimpleSource);
1518 33 : auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
1519 33 : assert(poSimpleSourceBand);
1520 33 : sContext.nTotalPixelsOfSources +=
1521 33 : static_cast<uint64_t>(poSimpleSourceBand->GetXSize()) *
1522 33 : poSimpleSourceBand->GetYSize();
1523 : }
1524 :
1525 20 : if (poThreadPool)
1526 : {
1527 1 : CPLDebugOnly("VRT", "ComputeStatistics(): use optimized "
1528 : "multi-threaded code path for mosaic");
1529 2 : std::vector<Job> asJobs(nSources);
1530 2 : auto poQueue = poThreadPool->CreateJobQueue();
1531 3 : for (int i = 0; i < nSources; ++i)
1532 : {
1533 2 : auto poSimpleSource =
1534 2 : static_cast<VRTSimpleSource *>(papoSources[i]);
1535 2 : assert(poSimpleSource);
1536 2 : auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
1537 2 : assert(poSimpleSourceBand);
1538 2 : asJobs[i].psContext = &sContext;
1539 2 : asJobs[i].poRasterBand = poSimpleSourceBand;
1540 2 : if (!poQueue->SubmitJob(JobRunner, &asJobs[i]))
1541 : {
1542 0 : sContext.bFailure = true;
1543 0 : break;
1544 : }
1545 : }
1546 1 : poQueue->WaitCompletion();
1547 1 : if (!(sContext.bFailure || sContext.bFallbackToBase))
1548 : {
1549 3 : for (int i = 0; i < nSources; ++i)
1550 : {
1551 2 : Job::UpdateStats(&asJobs[i]);
1552 : }
1553 : }
1554 : }
1555 : else
1556 : {
1557 19 : CPLDebugOnly(
1558 : "VRT",
1559 : "ComputeStatistics(): use optimized code path for mosaic");
1560 47 : for (int i = 0; i < nSources; ++i)
1561 : {
1562 30 : auto poSimpleSource =
1563 30 : static_cast<VRTSimpleSource *>(papoSources[i]);
1564 30 : assert(poSimpleSource);
1565 30 : auto poSimpleSourceBand = poSimpleSource->GetRasterBand();
1566 30 : assert(poSimpleSourceBand);
1567 30 : Job sJob;
1568 30 : sJob.psContext = &sContext;
1569 30 : sJob.poRasterBand = poSimpleSourceBand;
1570 30 : JobRunner(&sJob);
1571 30 : if (sContext.bFailure || sContext.bFallbackToBase)
1572 : break;
1573 28 : Job::UpdateStats(&sJob);
1574 : }
1575 : }
1576 :
1577 20 : if (sContext.bFailure)
1578 0 : return CE_Failure;
1579 20 : if (sContext.bFallbackToBase)
1580 : {
1581 : // If the VRT band nodata value is in the [min, max] range
1582 : // of the source and that the source has no nodata value set,
1583 : // then we can't use the optimization.
1584 2 : CPLDebugOnly("VRT", "ComputeStatistics(): revert back to "
1585 : "generic case because of nodata value in range "
1586 : "of source raster");
1587 2 : return GDALRasterBand::ComputeStatistics(
1588 : bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev, pfnProgress,
1589 2 : pProgressData);
1590 : }
1591 :
1592 18 : const uint64_t nTotalPixels =
1593 18 : static_cast<uint64_t>(nRasterXSize) * nRasterYSize;
1594 7 : if (m_bNoDataValueSet && m_bHideNoDataValue &&
1595 25 : !std::isnan(m_dfNoDataValue) && IsNoDataValueInDataTypeRange())
1596 : {
1597 1 : UpdateStatsWithConstantValue(sContext, m_dfNoDataValue,
1598 : nTotalPixels -
1599 1 : sContext.nGlobalValidPixels);
1600 : }
1601 17 : else if (!m_bNoDataValueSet)
1602 : {
1603 11 : sContext.nGlobalValidPixels = nTotalPixels;
1604 : }
1605 :
1606 : #ifdef naive_update_not_used
1607 : const double dfGlobalMean =
1608 : sContext.nGlobalValidPixels > 0
1609 : ? sContext.dfGlobalSum / sContext.nGlobalValidPixels
1610 : : 0;
1611 : const double dfGlobalStdDev =
1612 : sContext.nGlobalValidPixels > 0
1613 : ? sqrt(sContext.dfGlobalSumSquare /
1614 : sContext.nGlobalValidPixels -
1615 : dfGlobalMean * dfGlobalMean)
1616 : : 0;
1617 : #else
1618 18 : const double dfGlobalMean = sContext.dfGlobalMean;
1619 : const double dfGlobalStdDev =
1620 18 : sContext.nGlobalValidPixels > 0
1621 18 : ? sqrt(sContext.dfGlobalM2 / sContext.nGlobalValidPixels)
1622 18 : : 0;
1623 : #endif
1624 18 : if (sContext.nGlobalValidPixels > 0)
1625 : {
1626 16 : if (bApproxOK)
1627 : {
1628 1 : SetMetadataItem("STATISTICS_APPROXIMATE", "YES");
1629 : }
1630 15 : else if (GetMetadataItem("STATISTICS_APPROXIMATE"))
1631 : {
1632 0 : SetMetadataItem("STATISTICS_APPROXIMATE", nullptr);
1633 : }
1634 16 : SetStatistics(sContext.dfGlobalMin, sContext.dfGlobalMax,
1635 16 : dfGlobalMean, dfGlobalStdDev);
1636 : }
1637 : else
1638 : {
1639 2 : sContext.dfGlobalMin = 0.0;
1640 2 : sContext.dfGlobalMax = 0.0;
1641 : }
1642 :
1643 18 : SetValidPercent(nTotalPixels, sContext.nGlobalValidPixels);
1644 :
1645 18 : if (pdfMin)
1646 14 : *pdfMin = sContext.dfGlobalMin;
1647 18 : if (pdfMax)
1648 14 : *pdfMax = sContext.dfGlobalMax;
1649 18 : if (pdfMean)
1650 10 : *pdfMean = dfGlobalMean;
1651 18 : if (pdfStdDev)
1652 10 : *pdfStdDev = dfGlobalStdDev;
1653 :
1654 18 : if (sContext.nGlobalValidPixels == 0)
1655 : {
1656 2 : ReportError(CE_Failure, CPLE_AppDefined,
1657 : "Failed to compute statistics, no valid pixels found "
1658 : "in sampling.");
1659 : }
1660 :
1661 18 : return sContext.nGlobalValidPixels > 0 ? CE_None : CE_Failure;
1662 : }
1663 : else
1664 : {
1665 6 : return GDALRasterBand::ComputeStatistics(bApproxOK, pdfMin, pdfMax,
1666 : pdfMean, pdfStdDev,
1667 6 : pfnProgress, pProgressData);
1668 : }
1669 : }
1670 :
1671 : /************************************************************************/
1672 : /* GetHistogram() */
1673 : /************************************************************************/
1674 :
1675 7 : CPLErr VRTSourcedRasterBand::GetHistogram(double dfMin, double dfMax,
1676 : int nBuckets, GUIntBig *panHistogram,
1677 : int bIncludeOutOfRange, int bApproxOK,
1678 : GDALProgressFunc pfnProgress,
1679 : void *pProgressData)
1680 :
1681 : {
1682 : /* -------------------------------------------------------------------- */
1683 : /* If we have overviews, use them for the histogram. */
1684 : /* -------------------------------------------------------------------- */
1685 7 : if (bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews())
1686 : {
1687 : // FIXME: Should we use the most reduced overview here or use some
1688 : // minimum number of samples like GDALRasterBand::ComputeStatistics()
1689 : // does?
1690 1 : GDALRasterBand *poBand = GetRasterSampleOverview(0);
1691 :
1692 1 : if (poBand != nullptr && poBand != this)
1693 : {
1694 1 : auto l_poDS = dynamic_cast<VRTDataset *>(poDS);
1695 2 : if (l_poDS && !l_poDS->m_apoOverviews.empty() &&
1696 1 : dynamic_cast<VRTSourcedRasterBand *>(poBand) != nullptr)
1697 : {
1698 1 : auto apoTmpOverviews = std::move(l_poDS->m_apoOverviews);
1699 1 : l_poDS->m_apoOverviews.clear();
1700 1 : auto eErr = poBand->GDALRasterBand::GetHistogram(
1701 : dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange,
1702 : bApproxOK, pfnProgress, pProgressData);
1703 1 : l_poDS->m_apoOverviews = std::move(apoTmpOverviews);
1704 1 : return eErr;
1705 : }
1706 : else
1707 : {
1708 0 : return poBand->GetHistogram(
1709 : dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange,
1710 0 : bApproxOK, pfnProgress, pProgressData);
1711 : }
1712 : }
1713 : }
1714 :
1715 6 : if (nSources != 1)
1716 2 : return VRTRasterBand::GetHistogram(dfMin, dfMax, nBuckets, panHistogram,
1717 : bIncludeOutOfRange, bApproxOK,
1718 2 : pfnProgress, pProgressData);
1719 :
1720 4 : if (pfnProgress == nullptr)
1721 4 : pfnProgress = GDALDummyProgress;
1722 :
1723 8 : const std::string osFctId("VRTSourcedRasterBand::GetHistogram");
1724 8 : GDALAntiRecursionGuard oGuard(osFctId);
1725 4 : if (oGuard.GetCallDepth() >= 32)
1726 : {
1727 0 : CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1728 0 : return CE_Failure;
1729 : }
1730 :
1731 12 : GDALAntiRecursionGuard oGuard2(oGuard, poDS->GetDescription());
1732 4 : if (oGuard2.GetCallDepth() >= 2)
1733 : {
1734 0 : CPLError(CE_Failure, CPLE_AppDefined, "Recursion detected");
1735 0 : return CE_Failure;
1736 : }
1737 :
1738 : /* -------------------------------------------------------------------- */
1739 : /* Try with source bands. */
1740 : /* -------------------------------------------------------------------- */
1741 4 : const CPLErr eErr = papoSources[0]->GetHistogram(
1742 : GetXSize(), GetYSize(), dfMin, dfMax, nBuckets, panHistogram,
1743 4 : bIncludeOutOfRange, bApproxOK, pfnProgress, pProgressData);
1744 4 : if (eErr != CE_None)
1745 : {
1746 0 : const CPLErr eErr2 = GDALRasterBand::GetHistogram(
1747 : dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange, bApproxOK,
1748 : pfnProgress, pProgressData);
1749 0 : return eErr2;
1750 : }
1751 :
1752 4 : SetDefaultHistogram(dfMin, dfMax, nBuckets, panHistogram);
1753 :
1754 4 : return CE_None;
1755 : }
1756 :
1757 : /************************************************************************/
1758 : /* AddSource() */
1759 : /************************************************************************/
1760 :
1761 7044 : CPLErr VRTSourcedRasterBand::AddSource(VRTSource *poNewSource)
1762 :
1763 : {
1764 7044 : nSources++;
1765 :
1766 7044 : papoSources = static_cast<VRTSource **>(
1767 7044 : CPLRealloc(papoSources, sizeof(void *) * nSources));
1768 7044 : papoSources[nSources - 1] = poNewSource;
1769 :
1770 7044 : static_cast<VRTDataset *>(poDS)->SetNeedsFlush();
1771 :
1772 7044 : if (poNewSource->IsSimpleSource())
1773 : {
1774 7031 : VRTSimpleSource *poSS = static_cast<VRTSimpleSource *>(poNewSource);
1775 7031 : if (GetMetadataItem("NBITS", "IMAGE_STRUCTURE") != nullptr)
1776 : {
1777 47 : int nBits = atoi(GetMetadataItem("NBITS", "IMAGE_STRUCTURE"));
1778 47 : if (nBits >= 1 && nBits <= 31)
1779 : {
1780 47 : poSS->SetMaxValue(static_cast<int>((1U << nBits) - 1));
1781 : }
1782 : }
1783 : }
1784 :
1785 7044 : return CE_None;
1786 : }
1787 :
1788 : /*! @endcond */
1789 :
1790 : /************************************************************************/
1791 : /* VRTAddSource() */
1792 : /************************************************************************/
1793 :
1794 : /**
1795 : * @see VRTSourcedRasterBand::AddSource().
1796 : */
1797 :
1798 0 : CPLErr CPL_STDCALL VRTAddSource(VRTSourcedRasterBandH hVRTBand,
1799 : VRTSourceH hNewSource)
1800 : {
1801 0 : VALIDATE_POINTER1(hVRTBand, "VRTAddSource", CE_Failure);
1802 :
1803 0 : return reinterpret_cast<VRTSourcedRasterBand *>(hVRTBand)->AddSource(
1804 0 : reinterpret_cast<VRTSource *>(hNewSource));
1805 : }
1806 :
1807 : /*! @cond Doxygen_Suppress */
1808 :
1809 : /************************************************************************/
1810 : /* XMLInit() */
1811 : /************************************************************************/
1812 :
1813 1143 : CPLErr VRTSourcedRasterBand::XMLInit(
1814 : const CPLXMLNode *psTree, const char *pszVRTPath,
1815 : std::map<CPLString, GDALDataset *> &oMapSharedSources)
1816 :
1817 : {
1818 : {
1819 : const CPLErr eErr =
1820 1143 : VRTRasterBand::XMLInit(psTree, pszVRTPath, oMapSharedSources);
1821 1143 : if (eErr != CE_None)
1822 0 : return eErr;
1823 : }
1824 :
1825 : /* -------------------------------------------------------------------- */
1826 : /* Process sources. */
1827 : /* -------------------------------------------------------------------- */
1828 : VRTDriver *const poDriver =
1829 1143 : static_cast<VRTDriver *>(GDALGetDriverByName("VRT"));
1830 :
1831 1143 : for (const CPLXMLNode *psChild = psTree->psChild;
1832 6831 : psChild != nullptr && poDriver != nullptr; psChild = psChild->psNext)
1833 : {
1834 5709 : if (psChild->eType != CXT_Element)
1835 2705 : continue;
1836 :
1837 3004 : CPLErrorReset();
1838 : VRTSource *const poSource =
1839 3004 : poDriver->ParseSource(psChild, pszVRTPath, oMapSharedSources);
1840 3004 : if (poSource != nullptr)
1841 1473 : AddSource(poSource);
1842 1531 : else if (CPLGetLastErrorType() != CE_None)
1843 21 : return CE_Failure;
1844 : }
1845 :
1846 : /* -------------------------------------------------------------------- */
1847 : /* Done. */
1848 : /* -------------------------------------------------------------------- */
1849 : const char *pszSubclass =
1850 1122 : CPLGetXMLValue(psTree, "subclass", "VRTSourcedRasterBand");
1851 1122 : if (nSources == 0 && !EQUAL(pszSubclass, "VRTDerivedRasterBand"))
1852 147 : CPLDebug("VRT", "No valid sources found for band in VRT file %s",
1853 147 : GetDataset() ? GetDataset()->GetDescription() : "");
1854 :
1855 1122 : return CE_None;
1856 : }
1857 :
1858 : /************************************************************************/
1859 : /* SerializeToXML() */
1860 : /************************************************************************/
1861 :
1862 547 : CPLXMLNode *VRTSourcedRasterBand::SerializeToXML(const char *pszVRTPath,
1863 : bool &bHasWarnedAboutRAMUsage,
1864 : size_t &nAccRAMUsage)
1865 :
1866 : {
1867 547 : CPLXMLNode *psTree = VRTRasterBand::SerializeToXML(
1868 : pszVRTPath, bHasWarnedAboutRAMUsage, nAccRAMUsage);
1869 547 : CPLXMLNode *psLastChild = psTree->psChild;
1870 2053 : while (psLastChild != nullptr && psLastChild->psNext != nullptr)
1871 1506 : psLastChild = psLastChild->psNext;
1872 :
1873 : /* -------------------------------------------------------------------- */
1874 : /* Process Sources. */
1875 : /* -------------------------------------------------------------------- */
1876 :
1877 547 : GIntBig nUsableRAM = -1;
1878 :
1879 1478 : for (int iSource = 0; iSource < nSources; iSource++)
1880 : {
1881 : CPLXMLNode *const psXMLSrc =
1882 931 : papoSources[iSource]->SerializeToXML(pszVRTPath);
1883 :
1884 931 : if (psXMLSrc == nullptr)
1885 0 : break;
1886 :
1887 : // Creating the CPLXMLNode tree representation of a VRT can easily
1888 : // take several times RAM usage than its string serialization, or its
1889 : // internal representation in the driver.
1890 : // We multiply the estimate by a factor of 2, experimentally found to
1891 : // be more realistic than the conservative raw estimate.
1892 931 : nAccRAMUsage += 2 * CPLXMLNodeGetRAMUsageEstimate(psXMLSrc);
1893 931 : if (!bHasWarnedAboutRAMUsage && nAccRAMUsage > 512 * 1024 * 1024)
1894 : {
1895 0 : if (nUsableRAM < 0)
1896 0 : nUsableRAM = CPLGetUsablePhysicalRAM();
1897 0 : if (nUsableRAM > 0 &&
1898 0 : nAccRAMUsage > static_cast<uint64_t>(nUsableRAM) / 10 * 8)
1899 : {
1900 0 : bHasWarnedAboutRAMUsage = true;
1901 0 : CPLError(CE_Warning, CPLE_AppDefined,
1902 : "Serialization of this VRT file has already consumed "
1903 : "at least %.02f GB of RAM over a total of %.02f. This "
1904 : "process may abort",
1905 0 : double(nAccRAMUsage) / (1024 * 1024 * 1024),
1906 0 : double(nUsableRAM) / (1024 * 1024 * 1024));
1907 : }
1908 : }
1909 :
1910 931 : if (psLastChild == nullptr)
1911 0 : psTree->psChild = psXMLSrc;
1912 : else
1913 931 : psLastChild->psNext = psXMLSrc;
1914 931 : psLastChild = psXMLSrc;
1915 : }
1916 :
1917 547 : return psTree;
1918 : }
1919 :
1920 : /************************************************************************/
1921 : /* SkipBufferInitialization() */
1922 : /************************************************************************/
1923 :
1924 47527 : bool VRTSourcedRasterBand::SkipBufferInitialization()
1925 : {
1926 47527 : if (m_nSkipBufferInitialization >= 0)
1927 45530 : return m_nSkipBufferInitialization != 0;
1928 : /* -------------------------------------------------------------------- */
1929 : /* Check if we can avoid buffer initialization. */
1930 : /* -------------------------------------------------------------------- */
1931 :
1932 : // Note: if one day we do alpha compositing, we will need to check that.
1933 1997 : m_nSkipBufferInitialization = FALSE;
1934 1997 : if (nSources != 1 || !papoSources[0]->IsSimpleSource())
1935 : {
1936 865 : return false;
1937 : }
1938 1132 : VRTSimpleSource *poSS = static_cast<VRTSimpleSource *>(papoSources[0]);
1939 1132 : if (strcmp(poSS->GetType(), "SimpleSource") == 0)
1940 : {
1941 874 : auto l_poBand = poSS->GetRasterBand();
1942 866 : if (l_poBand != nullptr && poSS->m_dfSrcXOff >= 0.0 &&
1943 838 : poSS->m_dfSrcYOff >= 0.0 &&
1944 838 : poSS->m_dfSrcXOff + poSS->m_dfSrcXSize <= l_poBand->GetXSize() &&
1945 832 : poSS->m_dfSrcYOff + poSS->m_dfSrcYSize <= l_poBand->GetYSize() &&
1946 832 : poSS->m_dfDstXOff <= 0.0 && poSS->m_dfDstYOff <= 0.0 &&
1947 2563 : poSS->m_dfDstXOff + poSS->m_dfDstXSize >= nRasterXSize &&
1948 823 : poSS->m_dfDstYOff + poSS->m_dfDstYSize >= nRasterYSize)
1949 : {
1950 821 : m_nSkipBufferInitialization = TRUE;
1951 : }
1952 : }
1953 1132 : return m_nSkipBufferInitialization != 0;
1954 : }
1955 :
1956 : /************************************************************************/
1957 : /* ConfigureSource() */
1958 : /************************************************************************/
1959 :
1960 5105 : void VRTSourcedRasterBand::ConfigureSource(VRTSimpleSource *poSimpleSource,
1961 : GDALRasterBand *poSrcBand,
1962 : int bAddAsMaskBand, double dfSrcXOff,
1963 : double dfSrcYOff, double dfSrcXSize,
1964 : double dfSrcYSize, double dfDstXOff,
1965 : double dfDstYOff, double dfDstXSize,
1966 : double dfDstYSize)
1967 : {
1968 : /* -------------------------------------------------------------------- */
1969 : /* Default source and dest rectangles. */
1970 : /* -------------------------------------------------------------------- */
1971 5105 : if (dfSrcYSize == -1)
1972 : {
1973 286 : dfSrcXOff = 0;
1974 286 : dfSrcYOff = 0;
1975 286 : dfSrcXSize = poSrcBand->GetXSize();
1976 286 : dfSrcYSize = poSrcBand->GetYSize();
1977 : }
1978 :
1979 5105 : if (dfDstYSize == -1)
1980 : {
1981 286 : dfDstXOff = 0;
1982 286 : dfDstYOff = 0;
1983 286 : dfDstXSize = nRasterXSize;
1984 286 : dfDstYSize = nRasterYSize;
1985 : }
1986 :
1987 5105 : if (bAddAsMaskBand)
1988 28 : poSimpleSource->SetSrcMaskBand(poSrcBand);
1989 : else
1990 5077 : poSimpleSource->SetSrcBand(poSrcBand);
1991 :
1992 5105 : poSimpleSource->SetSrcWindow(dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize);
1993 5105 : poSimpleSource->SetDstWindow(dfDstXOff, dfDstYOff, dfDstXSize, dfDstYSize);
1994 :
1995 : /* -------------------------------------------------------------------- */
1996 : /* If we can get the associated GDALDataset, add a reference to it.*/
1997 : /* -------------------------------------------------------------------- */
1998 5105 : GDALDataset *poSrcBandDataset = poSrcBand->GetDataset();
1999 5105 : if (poSrcBandDataset != nullptr)
2000 : {
2001 : VRTDataset *poVRTSrcBandDataset =
2002 5105 : dynamic_cast<VRTDataset *>(poSrcBandDataset);
2003 5105 : if (poVRTSrcBandDataset && !poVRTSrcBandDataset->m_bCanTakeRef)
2004 : {
2005 : // Situation triggered by VRTDataset::AddVirtualOverview()
2006 : // We create an overview dataset that is a VRT of a reduction of
2007 : // ourselves. But we don't want to take a reference on ourselves,
2008 : // otherwise this will prevent us to be closed in number of
2009 : // circumstances
2010 60 : poSimpleSource->m_bDropRefOnSrcBand = false;
2011 : }
2012 : else
2013 : {
2014 5045 : poSrcBandDataset->Reference();
2015 : }
2016 : }
2017 5105 : }
2018 :
2019 : /************************************************************************/
2020 : /* AddSimpleSource() */
2021 : /************************************************************************/
2022 :
2023 346 : CPLErr VRTSourcedRasterBand::AddSimpleSource(
2024 : const char *pszFilename, int nBandIn, double dfSrcXOff, double dfSrcYOff,
2025 : double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
2026 : double dfDstXSize, double dfDstYSize, const char *pszResampling,
2027 : double dfNoDataValueIn)
2028 :
2029 : {
2030 : /* -------------------------------------------------------------------- */
2031 : /* Create source. */
2032 : /* -------------------------------------------------------------------- */
2033 346 : VRTSimpleSource *poSimpleSource = nullptr;
2034 :
2035 346 : if (pszResampling != nullptr && STARTS_WITH_CI(pszResampling, "aver"))
2036 : {
2037 0 : auto poAveragedSource = new VRTAveragedSource();
2038 0 : poSimpleSource = poAveragedSource;
2039 0 : if (dfNoDataValueIn != VRT_NODATA_UNSET)
2040 0 : poAveragedSource->SetNoDataValue(dfNoDataValueIn);
2041 : }
2042 : else
2043 : {
2044 346 : poSimpleSource = new VRTSimpleSource();
2045 346 : if (dfNoDataValueIn != VRT_NODATA_UNSET)
2046 0 : CPLError(
2047 : CE_Warning, CPLE_AppDefined,
2048 : "NODATA setting not currently supported for nearest "
2049 : "neighbour sampled simple sources on Virtual Datasources.");
2050 : }
2051 :
2052 346 : poSimpleSource->SetSrcBand(pszFilename, nBandIn);
2053 :
2054 346 : poSimpleSource->SetSrcWindow(dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize);
2055 346 : poSimpleSource->SetDstWindow(dfDstXOff, dfDstYOff, dfDstXSize, dfDstYSize);
2056 :
2057 : /* -------------------------------------------------------------------- */
2058 : /* add to list. */
2059 : /* -------------------------------------------------------------------- */
2060 346 : return AddSource(poSimpleSource);
2061 : }
2062 :
2063 : /************************************************************************/
2064 : /* AddSimpleSource() */
2065 : /************************************************************************/
2066 :
2067 1478 : CPLErr VRTSourcedRasterBand::AddSimpleSource(
2068 : GDALRasterBand *poSrcBand, double dfSrcXOff, double dfSrcYOff,
2069 : double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
2070 : double dfDstXSize, double dfDstYSize, const char *pszResampling,
2071 : double dfNoDataValueIn)
2072 :
2073 : {
2074 : /* -------------------------------------------------------------------- */
2075 : /* Create source. */
2076 : /* -------------------------------------------------------------------- */
2077 1478 : VRTSimpleSource *poSimpleSource = nullptr;
2078 :
2079 1478 : if (pszResampling != nullptr && STARTS_WITH_CI(pszResampling, "aver"))
2080 : {
2081 0 : auto poAveragedSource = new VRTAveragedSource();
2082 0 : poSimpleSource = poAveragedSource;
2083 0 : if (dfNoDataValueIn != VRT_NODATA_UNSET)
2084 0 : poAveragedSource->SetNoDataValue(dfNoDataValueIn);
2085 : }
2086 : else
2087 : {
2088 1478 : poSimpleSource = new VRTSimpleSource();
2089 1478 : if (dfNoDataValueIn != VRT_NODATA_UNSET)
2090 0 : CPLError(
2091 : CE_Warning, CPLE_AppDefined,
2092 : "NODATA setting not currently supported for "
2093 : "neighbour sampled simple sources on Virtual Datasources.");
2094 : }
2095 :
2096 1478 : ConfigureSource(poSimpleSource, poSrcBand, FALSE, dfSrcXOff, dfSrcYOff,
2097 : dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
2098 : dfDstYSize);
2099 :
2100 : /* -------------------------------------------------------------------- */
2101 : /* add to list. */
2102 : /* -------------------------------------------------------------------- */
2103 1478 : return AddSource(poSimpleSource);
2104 : }
2105 :
2106 : /************************************************************************/
2107 : /* AddMaskBandSource() */
2108 : /************************************************************************/
2109 :
2110 : // poSrcBand is not the mask band, but the band from which the mask band is
2111 : // taken.
2112 18 : CPLErr VRTSourcedRasterBand::AddMaskBandSource(
2113 : GDALRasterBand *poSrcBand, double dfSrcXOff, double dfSrcYOff,
2114 : double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
2115 : double dfDstXSize, double dfDstYSize)
2116 : {
2117 : /* -------------------------------------------------------------------- */
2118 : /* Create source. */
2119 : /* -------------------------------------------------------------------- */
2120 18 : VRTSimpleSource *poSimpleSource = new VRTSimpleSource();
2121 :
2122 18 : ConfigureSource(poSimpleSource, poSrcBand, TRUE, dfSrcXOff, dfSrcYOff,
2123 : dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
2124 : dfDstYSize);
2125 :
2126 : /* -------------------------------------------------------------------- */
2127 : /* add to list. */
2128 : /* -------------------------------------------------------------------- */
2129 18 : return AddSource(poSimpleSource);
2130 : }
2131 :
2132 : /*! @endcond */
2133 :
2134 : /************************************************************************/
2135 : /* VRTAddSimpleSource() */
2136 : /************************************************************************/
2137 :
2138 : /**
2139 : * @see VRTSourcedRasterBand::AddSimpleSource().
2140 : */
2141 :
2142 1125 : CPLErr CPL_STDCALL VRTAddSimpleSource(VRTSourcedRasterBandH hVRTBand,
2143 : GDALRasterBandH hSrcBand, int nSrcXOff,
2144 : int nSrcYOff, int nSrcXSize,
2145 : int nSrcYSize, int nDstXOff, int nDstYOff,
2146 : int nDstXSize, int nDstYSize,
2147 : const char *pszResampling,
2148 : double dfNoDataValue)
2149 : {
2150 1125 : VALIDATE_POINTER1(hVRTBand, "VRTAddSimpleSource", CE_Failure);
2151 :
2152 1125 : return reinterpret_cast<VRTSourcedRasterBand *>(hVRTBand)->AddSimpleSource(
2153 : reinterpret_cast<GDALRasterBand *>(hSrcBand), nSrcXOff, nSrcYOff,
2154 : nSrcXSize, nSrcYSize, nDstXOff, nDstYOff, nDstXSize, nDstYSize,
2155 1125 : pszResampling, dfNoDataValue);
2156 : }
2157 :
2158 : /*! @cond Doxygen_Suppress */
2159 :
2160 : /************************************************************************/
2161 : /* AddComplexSource() */
2162 : /************************************************************************/
2163 :
2164 19 : CPLErr VRTSourcedRasterBand::AddComplexSource(
2165 : const char *pszFilename, int nBandIn, double dfSrcXOff, double dfSrcYOff,
2166 : double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
2167 : double dfDstXSize, double dfDstYSize, double dfScaleOff,
2168 : double dfScaleRatio, double dfNoDataValueIn, int nColorTableComponent)
2169 :
2170 : {
2171 : /* -------------------------------------------------------------------- */
2172 : /* Create source. */
2173 : /* -------------------------------------------------------------------- */
2174 19 : VRTComplexSource *const poSource = new VRTComplexSource();
2175 :
2176 19 : poSource->SetSrcBand(pszFilename, nBandIn);
2177 :
2178 19 : poSource->SetSrcWindow(dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize);
2179 19 : poSource->SetDstWindow(dfDstXOff, dfDstYOff, dfDstXSize, dfDstYSize);
2180 :
2181 : /* -------------------------------------------------------------------- */
2182 : /* Set complex parameters. */
2183 : /* -------------------------------------------------------------------- */
2184 19 : if (dfNoDataValueIn != VRT_NODATA_UNSET)
2185 0 : poSource->SetNoDataValue(dfNoDataValueIn);
2186 :
2187 19 : if (dfScaleOff != 0.0 || dfScaleRatio != 1.0)
2188 4 : poSource->SetLinearScaling(dfScaleOff, dfScaleRatio);
2189 :
2190 19 : poSource->SetColorTableComponent(nColorTableComponent);
2191 :
2192 : /* -------------------------------------------------------------------- */
2193 : /* add to list. */
2194 : /* -------------------------------------------------------------------- */
2195 19 : return AddSource(poSource);
2196 : }
2197 :
2198 : /************************************************************************/
2199 : /* AddComplexSource() */
2200 : /************************************************************************/
2201 :
2202 11 : CPLErr VRTSourcedRasterBand::AddComplexSource(
2203 : GDALRasterBand *poSrcBand, double dfSrcXOff, double dfSrcYOff,
2204 : double dfSrcXSize, double dfSrcYSize, double dfDstXOff, double dfDstYOff,
2205 : double dfDstXSize, double dfDstYSize, double dfScaleOff,
2206 : double dfScaleRatio, double dfNoDataValueIn, int nColorTableComponent)
2207 :
2208 : {
2209 : /* -------------------------------------------------------------------- */
2210 : /* Create source. */
2211 : /* -------------------------------------------------------------------- */
2212 11 : VRTComplexSource *const poSource = new VRTComplexSource();
2213 :
2214 11 : ConfigureSource(poSource, poSrcBand, FALSE, dfSrcXOff, dfSrcYOff,
2215 : dfSrcXSize, dfSrcYSize, dfDstXOff, dfDstYOff, dfDstXSize,
2216 : dfDstYSize);
2217 :
2218 : /* -------------------------------------------------------------------- */
2219 : /* Set complex parameters. */
2220 : /* -------------------------------------------------------------------- */
2221 11 : if (dfNoDataValueIn != VRT_NODATA_UNSET)
2222 0 : poSource->SetNoDataValue(dfNoDataValueIn);
2223 :
2224 11 : if (dfScaleOff != 0.0 || dfScaleRatio != 1.0)
2225 11 : poSource->SetLinearScaling(dfScaleOff, dfScaleRatio);
2226 :
2227 11 : poSource->SetColorTableComponent(nColorTableComponent);
2228 :
2229 : /* -------------------------------------------------------------------- */
2230 : /* add to list. */
2231 : /* -------------------------------------------------------------------- */
2232 11 : return AddSource(poSource);
2233 : }
2234 :
2235 : /*! @endcond */
2236 :
2237 : /************************************************************************/
2238 : /* VRTAddComplexSource() */
2239 : /************************************************************************/
2240 :
2241 : /**
2242 : * @see VRTSourcedRasterBand::AddComplexSource().
2243 : */
2244 :
2245 0 : CPLErr CPL_STDCALL VRTAddComplexSource(
2246 : VRTSourcedRasterBandH hVRTBand, GDALRasterBandH hSrcBand, int nSrcXOff,
2247 : int nSrcYOff, int nSrcXSize, int nSrcYSize, int nDstXOff, int nDstYOff,
2248 : int nDstXSize, int nDstYSize, double dfScaleOff, double dfScaleRatio,
2249 : double dfNoDataValue)
2250 : {
2251 0 : VALIDATE_POINTER1(hVRTBand, "VRTAddComplexSource", CE_Failure);
2252 :
2253 0 : return reinterpret_cast<VRTSourcedRasterBand *>(hVRTBand)->AddComplexSource(
2254 : reinterpret_cast<GDALRasterBand *>(hSrcBand), nSrcXOff, nSrcYOff,
2255 : nSrcXSize, nSrcYSize, nDstXOff, nDstYOff, nDstXSize, nDstYSize,
2256 0 : dfScaleOff, dfScaleRatio, dfNoDataValue);
2257 : }
2258 :
2259 : /*! @cond Doxygen_Suppress */
2260 :
2261 : /************************************************************************/
2262 : /* AddFuncSource() */
2263 : /************************************************************************/
2264 :
2265 0 : CPLErr VRTSourcedRasterBand::AddFuncSource(VRTImageReadFunc pfnReadFunc,
2266 : void *pCBData,
2267 : double dfNoDataValueIn)
2268 :
2269 : {
2270 : /* -------------------------------------------------------------------- */
2271 : /* Create source. */
2272 : /* -------------------------------------------------------------------- */
2273 0 : VRTFuncSource *const poFuncSource = new VRTFuncSource;
2274 :
2275 0 : poFuncSource->fNoDataValue = static_cast<float>(dfNoDataValueIn);
2276 0 : poFuncSource->pfnReadFunc = pfnReadFunc;
2277 0 : poFuncSource->pCBData = pCBData;
2278 0 : poFuncSource->eType = GetRasterDataType();
2279 :
2280 : /* -------------------------------------------------------------------- */
2281 : /* add to list. */
2282 : /* -------------------------------------------------------------------- */
2283 0 : return AddSource(poFuncSource);
2284 : }
2285 :
2286 : /*! @endcond */
2287 :
2288 : /************************************************************************/
2289 : /* VRTAddFuncSource() */
2290 : /************************************************************************/
2291 :
2292 : /**
2293 : * @see VRTSourcedRasterBand::AddFuncSource().
2294 : */
2295 :
2296 0 : CPLErr CPL_STDCALL VRTAddFuncSource(VRTSourcedRasterBandH hVRTBand,
2297 : VRTImageReadFunc pfnReadFunc, void *pCBData,
2298 : double dfNoDataValue)
2299 : {
2300 0 : VALIDATE_POINTER1(hVRTBand, "VRTAddFuncSource", CE_Failure);
2301 :
2302 0 : return reinterpret_cast<VRTSourcedRasterBand *>(hVRTBand)->AddFuncSource(
2303 0 : pfnReadFunc, pCBData, dfNoDataValue);
2304 : }
2305 :
2306 : /*! @cond Doxygen_Suppress */
2307 :
2308 : /************************************************************************/
2309 : /* GetMetadataDomainList() */
2310 : /************************************************************************/
2311 :
2312 0 : char **VRTSourcedRasterBand::GetMetadataDomainList()
2313 : {
2314 0 : return CSLAddString(GDALRasterBand::GetMetadataDomainList(),
2315 0 : "LocationInfo");
2316 : }
2317 :
2318 : /************************************************************************/
2319 : /* GetMetadataItem() */
2320 : /************************************************************************/
2321 :
2322 11803 : const char *VRTSourcedRasterBand::GetMetadataItem(const char *pszName,
2323 : const char *pszDomain)
2324 :
2325 : {
2326 : /* ==================================================================== */
2327 : /* LocationInfo handling. */
2328 : /* ==================================================================== */
2329 11803 : if (pszDomain != nullptr && EQUAL(pszDomain, "LocationInfo") &&
2330 1 : (STARTS_WITH_CI(pszName, "Pixel_") ||
2331 0 : STARTS_WITH_CI(pszName, "GeoPixel_")))
2332 : {
2333 : /* --------------------------------------------------------------------
2334 : */
2335 : /* What pixel are we aiming at? */
2336 : /* --------------------------------------------------------------------
2337 : */
2338 1 : int iPixel = 0;
2339 1 : int iLine = 0;
2340 :
2341 1 : if (STARTS_WITH_CI(pszName, "Pixel_"))
2342 : {
2343 : // TODO(schwehr): Replace sscanf.
2344 1 : if (sscanf(pszName + 6, "%d_%d", &iPixel, &iLine) != 2)
2345 0 : return nullptr;
2346 : }
2347 0 : else if (STARTS_WITH_CI(pszName, "GeoPixel_"))
2348 : {
2349 0 : const double dfGeoX = CPLAtof(pszName + 9);
2350 0 : const char *const pszUnderscore = strchr(pszName + 9, '_');
2351 0 : if (!pszUnderscore)
2352 0 : return nullptr;
2353 0 : const double dfGeoY = CPLAtof(pszUnderscore + 1);
2354 :
2355 0 : if (GetDataset() == nullptr)
2356 0 : return nullptr;
2357 :
2358 0 : double adfGeoTransform[6] = {0.0};
2359 0 : if (GetDataset()->GetGeoTransform(adfGeoTransform) != CE_None)
2360 0 : return nullptr;
2361 :
2362 0 : double adfInvGeoTransform[6] = {0.0};
2363 0 : if (!GDALInvGeoTransform(adfGeoTransform, adfInvGeoTransform))
2364 0 : return nullptr;
2365 :
2366 0 : iPixel = static_cast<int>(floor(adfInvGeoTransform[0] +
2367 0 : adfInvGeoTransform[1] * dfGeoX +
2368 0 : adfInvGeoTransform[2] * dfGeoY));
2369 0 : iLine = static_cast<int>(floor(adfInvGeoTransform[3] +
2370 0 : adfInvGeoTransform[4] * dfGeoX +
2371 0 : adfInvGeoTransform[5] * dfGeoY));
2372 : }
2373 : else
2374 : {
2375 0 : return nullptr;
2376 : }
2377 :
2378 2 : if (iPixel < 0 || iLine < 0 || iPixel >= GetXSize() ||
2379 1 : iLine >= GetYSize())
2380 0 : return nullptr;
2381 :
2382 : /* --------------------------------------------------------------------
2383 : */
2384 : /* Find the file(s) at this location. */
2385 : /* --------------------------------------------------------------------
2386 : */
2387 1 : char **papszFileList = nullptr;
2388 1 : int nListSize = 0; // keep it in this scope
2389 1 : int nListMaxSize = 0; // keep it in this scope
2390 : CPLHashSet *const hSetFiles =
2391 1 : CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, nullptr);
2392 :
2393 2 : for (int iSource = 0; iSource < nSources; iSource++)
2394 : {
2395 1 : if (!papoSources[iSource]->IsSimpleSource())
2396 0 : continue;
2397 :
2398 1 : VRTSimpleSource *const poSrc =
2399 1 : static_cast<VRTSimpleSource *>(papoSources[iSource]);
2400 :
2401 1 : double dfReqXOff = 0.0;
2402 1 : double dfReqYOff = 0.0;
2403 1 : double dfReqXSize = 0.0;
2404 1 : double dfReqYSize = 0.0;
2405 1 : int nReqXOff = 0;
2406 1 : int nReqYOff = 0;
2407 1 : int nReqXSize = 0;
2408 1 : int nReqYSize = 0;
2409 1 : int nOutXOff = 0;
2410 1 : int nOutYOff = 0;
2411 1 : int nOutXSize = 0;
2412 1 : int nOutYSize = 0;
2413 :
2414 1 : bool bError = false;
2415 1 : if (!poSrc->GetSrcDstWindow(iPixel, iLine, 1, 1, 1, 1, &dfReqXOff,
2416 : &dfReqYOff, &dfReqXSize, &dfReqYSize,
2417 : &nReqXOff, &nReqYOff, &nReqXSize,
2418 : &nReqYSize, &nOutXOff, &nOutYOff,
2419 : &nOutXSize, &nOutYSize, bError))
2420 : {
2421 0 : if (bError)
2422 : {
2423 0 : CSLDestroy(papszFileList);
2424 0 : CPLHashSetDestroy(hSetFiles);
2425 0 : return nullptr;
2426 : }
2427 0 : continue;
2428 : }
2429 :
2430 1 : poSrc->GetFileList(&papszFileList, &nListSize, &nListMaxSize,
2431 1 : hSetFiles);
2432 : }
2433 :
2434 : /* --------------------------------------------------------------------
2435 : */
2436 : /* Format into XML. */
2437 : /* --------------------------------------------------------------------
2438 : */
2439 1 : m_osLastLocationInfo = "<LocationInfo>";
2440 2 : for (int i = 0; i < nListSize && papszFileList[i] != nullptr; i++)
2441 : {
2442 1 : m_osLastLocationInfo += "<File>";
2443 : char *const pszXMLEscaped =
2444 1 : CPLEscapeString(papszFileList[i], -1, CPLES_XML);
2445 1 : m_osLastLocationInfo += pszXMLEscaped;
2446 1 : CPLFree(pszXMLEscaped);
2447 1 : m_osLastLocationInfo += "</File>";
2448 : }
2449 1 : m_osLastLocationInfo += "</LocationInfo>";
2450 :
2451 1 : CSLDestroy(papszFileList);
2452 1 : CPLHashSetDestroy(hSetFiles);
2453 :
2454 1 : return m_osLastLocationInfo.c_str();
2455 : }
2456 :
2457 : /* ==================================================================== */
2458 : /* Other domains. */
2459 : /* ==================================================================== */
2460 :
2461 11802 : return GDALRasterBand::GetMetadataItem(pszName, pszDomain);
2462 : }
2463 :
2464 : /************************************************************************/
2465 : /* GetMetadata() */
2466 : /************************************************************************/
2467 :
2468 2928 : char **VRTSourcedRasterBand::GetMetadata(const char *pszDomain)
2469 :
2470 : {
2471 : /* ==================================================================== */
2472 : /* vrt_sources domain handling. */
2473 : /* ==================================================================== */
2474 2928 : if (pszDomain != nullptr && EQUAL(pszDomain, "vrt_sources"))
2475 : {
2476 1 : CSLDestroy(m_papszSourceList);
2477 1 : m_papszSourceList = nullptr;
2478 :
2479 : /* --------------------------------------------------------------------
2480 : */
2481 : /* Process SimpleSources. */
2482 : /* --------------------------------------------------------------------
2483 : */
2484 2 : for (int iSource = 0; iSource < nSources; iSource++)
2485 : {
2486 : CPLXMLNode *const psXMLSrc =
2487 1 : papoSources[iSource]->SerializeToXML(nullptr);
2488 1 : if (psXMLSrc == nullptr)
2489 0 : continue;
2490 :
2491 1 : char *const pszXML = CPLSerializeXMLTree(psXMLSrc);
2492 :
2493 1 : m_papszSourceList = CSLSetNameValue(
2494 : m_papszSourceList, CPLSPrintf("source_%d", iSource), pszXML);
2495 1 : CPLFree(pszXML);
2496 1 : CPLDestroyXMLNode(psXMLSrc);
2497 : }
2498 :
2499 1 : return m_papszSourceList;
2500 : }
2501 :
2502 : /* ==================================================================== */
2503 : /* Other domains. */
2504 : /* ==================================================================== */
2505 :
2506 2927 : return GDALRasterBand::GetMetadata(pszDomain);
2507 : }
2508 :
2509 : /************************************************************************/
2510 : /* SetMetadataItem() */
2511 : /************************************************************************/
2512 :
2513 1859 : CPLErr VRTSourcedRasterBand::SetMetadataItem(const char *pszName,
2514 : const char *pszValue,
2515 : const char *pszDomain)
2516 :
2517 : {
2518 : #if DEBUG_VERBOSE
2519 : CPLDebug("VRT", "VRTSourcedRasterBand::SetMetadataItem(%s,%s,%s)\n",
2520 : pszName, pszValue ? pszValue : "(null)",
2521 : pszDomain ? pszDomain : "(null)");
2522 : #endif
2523 :
2524 1859 : if (pszDomain != nullptr && EQUAL(pszDomain, "new_vrt_sources"))
2525 : {
2526 : VRTDriver *const poDriver =
2527 1 : static_cast<VRTDriver *>(GDALGetDriverByName("VRT"));
2528 :
2529 1 : CPLXMLNode *const psTree = CPLParseXMLString(pszValue);
2530 1 : if (psTree == nullptr)
2531 0 : return CE_Failure;
2532 :
2533 1 : auto l_poDS = dynamic_cast<VRTDataset *>(GetDataset());
2534 1 : if (l_poDS == nullptr)
2535 : {
2536 0 : CPLDestroyXMLNode(psTree);
2537 0 : return CE_Failure;
2538 : }
2539 : VRTSource *const poSource =
2540 1 : poDriver->ParseSource(psTree, nullptr, l_poDS->m_oMapSharedSources);
2541 1 : CPLDestroyXMLNode(psTree);
2542 :
2543 1 : if (poSource != nullptr)
2544 1 : return AddSource(poSource);
2545 :
2546 0 : return CE_Failure;
2547 : }
2548 1858 : else if (pszDomain != nullptr && EQUAL(pszDomain, "vrt_sources"))
2549 : {
2550 1 : int iSource = 0;
2551 : // TODO(schwehr): Replace sscanf.
2552 2 : if (sscanf(pszName, "source_%d", &iSource) != 1 || iSource < 0 ||
2553 1 : iSource >= nSources)
2554 : {
2555 0 : CPLError(CE_Failure, CPLE_AppDefined,
2556 : "%s metadata item name is not recognized. "
2557 : "Should be between source_0 and source_%d",
2558 0 : pszName, nSources - 1);
2559 0 : return CE_Failure;
2560 : }
2561 :
2562 : VRTDriver *const poDriver =
2563 1 : static_cast<VRTDriver *>(GDALGetDriverByName("VRT"));
2564 :
2565 1 : CPLXMLNode *const psTree = CPLParseXMLString(pszValue);
2566 1 : if (psTree == nullptr)
2567 0 : return CE_Failure;
2568 :
2569 1 : auto l_poDS = dynamic_cast<VRTDataset *>(GetDataset());
2570 1 : if (l_poDS == nullptr)
2571 : {
2572 0 : CPLDestroyXMLNode(psTree);
2573 0 : return CE_Failure;
2574 : }
2575 : VRTSource *const poSource =
2576 1 : poDriver->ParseSource(psTree, nullptr, l_poDS->m_oMapSharedSources);
2577 1 : CPLDestroyXMLNode(psTree);
2578 :
2579 1 : if (poSource != nullptr)
2580 : {
2581 1 : delete papoSources[iSource];
2582 1 : papoSources[iSource] = poSource;
2583 1 : static_cast<VRTDataset *>(poDS)->SetNeedsFlush();
2584 1 : return CE_None;
2585 : }
2586 :
2587 0 : return CE_Failure;
2588 : }
2589 :
2590 1857 : return VRTRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
2591 : }
2592 :
2593 : /************************************************************************/
2594 : /* SetMetadata() */
2595 : /************************************************************************/
2596 :
2597 1815 : CPLErr VRTSourcedRasterBand::SetMetadata(char **papszNewMD,
2598 : const char *pszDomain)
2599 :
2600 : {
2601 1815 : if (pszDomain != nullptr && (EQUAL(pszDomain, "new_vrt_sources") ||
2602 1815 : EQUAL(pszDomain, "vrt_sources")))
2603 : {
2604 : VRTDriver *const poDriver =
2605 7 : static_cast<VRTDriver *>(GDALGetDriverByName("VRT"));
2606 :
2607 7 : if (EQUAL(pszDomain, "vrt_sources"))
2608 : {
2609 7 : for (int i = 0; i < nSources; i++)
2610 0 : delete papoSources[i];
2611 7 : CPLFree(papoSources);
2612 7 : papoSources = nullptr;
2613 7 : nSources = 0;
2614 : }
2615 :
2616 5 : for (const char *const pszMDItem :
2617 17 : cpl::Iterate(CSLConstList(papszNewMD)))
2618 : {
2619 7 : const char *const pszXML = CPLParseNameValue(pszMDItem, nullptr);
2620 7 : CPLXMLTreeCloser psTree(CPLParseXMLString(pszXML));
2621 7 : if (!psTree)
2622 0 : return CE_Failure;
2623 :
2624 7 : auto l_poDS = dynamic_cast<VRTDataset *>(GetDataset());
2625 7 : if (l_poDS == nullptr)
2626 : {
2627 0 : return CE_Failure;
2628 : }
2629 7 : VRTSource *const poSource = poDriver->ParseSource(
2630 7 : psTree.get(), nullptr, l_poDS->m_oMapSharedSources);
2631 7 : if (poSource == nullptr)
2632 2 : return CE_Failure;
2633 :
2634 5 : const CPLErr eErr = AddSource(poSource);
2635 : // cppcheck-suppress knownConditionTrueFalse
2636 5 : if (eErr != CE_None)
2637 0 : return eErr;
2638 : }
2639 :
2640 5 : return CE_None;
2641 : }
2642 :
2643 1808 : return VRTRasterBand::SetMetadata(papszNewMD, pszDomain);
2644 : }
2645 :
2646 : /************************************************************************/
2647 : /* GetFileList() */
2648 : /************************************************************************/
2649 :
2650 62 : void VRTSourcedRasterBand::GetFileList(char ***ppapszFileList, int *pnSize,
2651 : int *pnMaxSize, CPLHashSet *hSetFiles)
2652 : {
2653 163 : for (int i = 0; i < nSources; i++)
2654 : {
2655 101 : papoSources[i]->GetFileList(ppapszFileList, pnSize, pnMaxSize,
2656 101 : hSetFiles);
2657 : }
2658 :
2659 62 : VRTRasterBand::GetFileList(ppapszFileList, pnSize, pnMaxSize, hSetFiles);
2660 62 : }
2661 :
2662 : /************************************************************************/
2663 : /* CloseDependentDatasets() */
2664 : /************************************************************************/
2665 :
2666 4945 : int VRTSourcedRasterBand::CloseDependentDatasets()
2667 : {
2668 4945 : int ret = VRTRasterBand::CloseDependentDatasets();
2669 :
2670 4945 : if (nSources == 0)
2671 381 : return ret;
2672 :
2673 11606 : for (int i = 0; i < nSources; i++)
2674 7042 : delete papoSources[i];
2675 :
2676 4564 : CPLFree(papoSources);
2677 4564 : papoSources = nullptr;
2678 4564 : nSources = 0;
2679 :
2680 4564 : return TRUE;
2681 : }
2682 :
2683 : /************************************************************************/
2684 : /* FlushCache() */
2685 : /************************************************************************/
2686 :
2687 5342 : CPLErr VRTSourcedRasterBand::FlushCache(bool bAtClosing)
2688 : {
2689 5342 : CPLErr eErr = VRTRasterBand::FlushCache(bAtClosing);
2690 12814 : for (int i = 0; i < nSources && eErr == CE_None; i++)
2691 : {
2692 7472 : eErr = papoSources[i]->FlushCache(bAtClosing);
2693 : }
2694 5342 : return eErr;
2695 : }
2696 :
2697 : /************************************************************************/
2698 : /* RemoveCoveredSources() */
2699 : /************************************************************************/
2700 :
2701 : /** Remove sources that are covered by other sources.
2702 : *
2703 : * This method removes sources that are covered entirely by (one or several)
2704 : * sources of higher priority (even if they declare a nodata setting).
2705 : * This optimizes the size of the VRT and the rendering time.
2706 : */
2707 7 : void VRTSourcedRasterBand::RemoveCoveredSources(CSLConstList papszOptions)
2708 : {
2709 : #ifndef HAVE_GEOS
2710 : if (CPLTestBool(CSLFetchNameValueDef(
2711 : papszOptions, "EMIT_ERROR_IF_GEOS_NOT_AVAILABLE", "TRUE")))
2712 : {
2713 : CPLError(CE_Failure, CPLE_NotSupported,
2714 : "RemoveCoveredSources() not implemented in builds "
2715 : "without GEOS support");
2716 : }
2717 : #else
2718 : (void)papszOptions;
2719 :
2720 : CPLRectObj globalBounds;
2721 7 : globalBounds.minx = 0;
2722 7 : globalBounds.miny = 0;
2723 7 : globalBounds.maxx = nRasterXSize;
2724 7 : globalBounds.maxy = nRasterYSize;
2725 :
2726 : // Create an index with the bbox of all sources
2727 7 : CPLQuadTree *hTree = CPLQuadTreeCreate(&globalBounds, nullptr);
2728 17 : for (int i = 0; i < nSources; i++)
2729 : {
2730 10 : if (papoSources[i]->IsSimpleSource())
2731 : {
2732 : VRTSimpleSource *poSS =
2733 10 : cpl::down_cast<VRTSimpleSource *>(papoSources[i]);
2734 10 : void *hFeature =
2735 10 : reinterpret_cast<void *>(static_cast<uintptr_t>(i));
2736 : CPLRectObj rect;
2737 10 : rect.minx = std::max(0.0, poSS->m_dfDstXOff);
2738 10 : rect.miny = std::max(0.0, poSS->m_dfDstYOff);
2739 20 : rect.maxx = std::min(double(nRasterXSize),
2740 10 : poSS->m_dfDstXOff + poSS->m_dfDstXSize);
2741 20 : rect.maxy = std::min(double(nRasterYSize),
2742 10 : poSS->m_dfDstYOff + poSS->m_dfDstYSize);
2743 10 : CPLQuadTreeInsertWithBounds(hTree, hFeature, &rect);
2744 : }
2745 : }
2746 :
2747 17 : for (int i = 0; i < nSources; i++)
2748 : {
2749 10 : if (papoSources[i]->IsSimpleSource())
2750 : {
2751 : VRTSimpleSource *poSS =
2752 10 : cpl::down_cast<VRTSimpleSource *>(papoSources[i]);
2753 : CPLRectObj rect;
2754 10 : rect.minx = std::max(0.0, poSS->m_dfDstXOff);
2755 10 : rect.miny = std::max(0.0, poSS->m_dfDstYOff);
2756 20 : rect.maxx = std::min(double(nRasterXSize),
2757 10 : poSS->m_dfDstXOff + poSS->m_dfDstXSize);
2758 20 : rect.maxy = std::min(double(nRasterYSize),
2759 10 : poSS->m_dfDstYOff + poSS->m_dfDstYSize);
2760 :
2761 : // Find sources whose extent intersect with the current one
2762 10 : int nFeatureCount = 0;
2763 : void **pahFeatures =
2764 10 : CPLQuadTreeSearch(hTree, &rect, &nFeatureCount);
2765 :
2766 : // Compute the bounding box of those sources, only if they are
2767 : // on top of the current one
2768 : CPLRectObj rectIntersecting;
2769 10 : rectIntersecting.minx = std::numeric_limits<double>::max();
2770 10 : rectIntersecting.miny = std::numeric_limits<double>::max();
2771 10 : rectIntersecting.maxx = -std::numeric_limits<double>::max();
2772 10 : rectIntersecting.maxy = -std::numeric_limits<double>::max();
2773 24 : for (int j = 0; j < nFeatureCount; j++)
2774 : {
2775 14 : const int curFeature = static_cast<int>(
2776 14 : reinterpret_cast<uintptr_t>(pahFeatures[j]));
2777 14 : if (curFeature > i)
2778 : {
2779 : VRTSimpleSource *poOtherSS =
2780 8 : cpl::down_cast<VRTSimpleSource *>(
2781 4 : papoSources[curFeature]);
2782 4 : rectIntersecting.minx =
2783 4 : std::min(rectIntersecting.minx, poOtherSS->m_dfDstXOff);
2784 4 : rectIntersecting.miny =
2785 4 : std::min(rectIntersecting.miny, poOtherSS->m_dfDstYOff);
2786 4 : rectIntersecting.maxx = std::max(
2787 : rectIntersecting.maxx,
2788 4 : poOtherSS->m_dfDstXOff + poOtherSS->m_dfDstXSize);
2789 4 : rectIntersecting.maxy = std::max(
2790 : rectIntersecting.maxy,
2791 4 : poOtherSS->m_dfDstYOff + poOtherSS->m_dfDstXSize);
2792 : }
2793 : }
2794 :
2795 : // If the boundinx box of those sources overlap the current one,
2796 : // then compute their union, and check if it contains the current
2797 : // source
2798 10 : if (rectIntersecting.minx <= rect.minx &&
2799 2 : rectIntersecting.miny <= rect.miny &&
2800 2 : rectIntersecting.maxx >= rect.maxx &&
2801 2 : rectIntersecting.maxy >= rect.maxy)
2802 : {
2803 4 : OGRPolygon oPoly;
2804 : {
2805 2 : auto poLR = new OGRLinearRing();
2806 2 : poLR->addPoint(rect.minx, rect.miny);
2807 2 : poLR->addPoint(rect.minx, rect.maxy);
2808 2 : poLR->addPoint(rect.maxx, rect.maxy);
2809 2 : poLR->addPoint(rect.maxx, rect.miny);
2810 2 : poLR->addPoint(rect.minx, rect.miny);
2811 2 : oPoly.addRingDirectly(poLR);
2812 : }
2813 :
2814 2 : std::unique_ptr<OGRGeometry> poUnion;
2815 7 : for (int j = 0; j < nFeatureCount; j++)
2816 : {
2817 5 : const int curFeature = static_cast<int>(
2818 5 : reinterpret_cast<uintptr_t>(pahFeatures[j]));
2819 5 : if (curFeature > i)
2820 : {
2821 : VRTSimpleSource *poOtherSS =
2822 6 : cpl::down_cast<VRTSimpleSource *>(
2823 3 : papoSources[curFeature]);
2824 : CPLRectObj otherRect;
2825 3 : otherRect.minx = std::max(0.0, poOtherSS->m_dfDstXOff);
2826 3 : otherRect.miny = std::max(0.0, poOtherSS->m_dfDstYOff);
2827 6 : otherRect.maxx = std::min(double(nRasterXSize),
2828 6 : poOtherSS->m_dfDstXOff +
2829 3 : poOtherSS->m_dfDstXSize);
2830 6 : otherRect.maxy = std::min(double(nRasterYSize),
2831 6 : poOtherSS->m_dfDstYOff +
2832 3 : poOtherSS->m_dfDstYSize);
2833 6 : OGRPolygon oOtherPoly;
2834 : {
2835 3 : auto poLR = new OGRLinearRing();
2836 3 : poLR->addPoint(otherRect.minx, otherRect.miny);
2837 3 : poLR->addPoint(otherRect.minx, otherRect.maxy);
2838 3 : poLR->addPoint(otherRect.maxx, otherRect.maxy);
2839 3 : poLR->addPoint(otherRect.maxx, otherRect.miny);
2840 3 : poLR->addPoint(otherRect.minx, otherRect.miny);
2841 3 : oOtherPoly.addRingDirectly(poLR);
2842 : }
2843 3 : if (poUnion == nullptr)
2844 2 : poUnion.reset(oOtherPoly.clone());
2845 : else
2846 1 : poUnion.reset(oOtherPoly.Union(poUnion.get()));
2847 : }
2848 : }
2849 :
2850 2 : if (poUnion != nullptr && poUnion->Contains(&oPoly))
2851 : {
2852 : // We can remove the current source
2853 2 : delete papoSources[i];
2854 2 : papoSources[i] = nullptr;
2855 : }
2856 : }
2857 10 : CPLFree(pahFeatures);
2858 :
2859 10 : void *hFeature =
2860 10 : reinterpret_cast<void *>(static_cast<uintptr_t>(i));
2861 10 : CPLQuadTreeRemove(hTree, hFeature, &rect);
2862 : }
2863 : }
2864 :
2865 : // Compact the papoSources array
2866 7 : int iDst = 0;
2867 17 : for (int iSrc = 0; iSrc < nSources; iSrc++)
2868 : {
2869 10 : if (papoSources[iSrc])
2870 8 : papoSources[iDst++] = papoSources[iSrc];
2871 : }
2872 7 : nSources = iDst;
2873 :
2874 7 : CPLQuadTreeDestroy(hTree);
2875 : #endif
2876 7 : }
2877 :
2878 : /*! @endcond */
|