Skip to content

Commit

Permalink
Merge pull request #8613 from rouault/vrt_minmax
Browse files Browse the repository at this point in the history
VRTSourcedRasterBand::GetMinimum/GetMaximum(): limit to 1 second max when iterating over sources
  • Loading branch information
rouault authored Oct 27, 2023
2 parents 880214b + c4c861a commit 0f9d6db
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 7 deletions.
20 changes: 20 additions & 0 deletions autotest/gcore/vrt_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,26 @@ def test_vrt_read_6():
gdal.GetDriverByName("VRT").Delete("/vsimem/vrt_read_6.vrt")


###############################################################################
# Test GetMinimum() and GetMaximum()


def test_vrt_read_min_max_several_sources(tmp_path):

src1 = str(tmp_path / "left.tif")
src2 = str(tmp_path / "right.tif")
vrt = str(tmp_path / "tmp.vrt")
ds = gdal.Translate(src1, "data/byte.tif", srcWin=[0, 0, 10, 20])
ds.GetRasterBand(1).ComputeStatistics(False)
ds = None
ds = gdal.Translate(src2, "data/byte.tif", srcWin=[10, 0, 10, 20])
ds.GetRasterBand(1).ComputeStatistics(False)
ds = None
ds = gdal.BuildVRT(vrt, [src1, src2])
assert ds.GetRasterBand(1).GetMinimum() == 74
assert ds.GetRasterBand(1).GetMaximum() == 255


###############################################################################
# Test GDALOpen() anti-recursion mechanism

Expand Down
96 changes: 89 additions & 7 deletions frmts/vrt/vrtsourcedrasterband.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,37 @@ CPLErr VRTSourcedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
nPixelSize * nBlockXSize, &sExtraArg);
}

/************************************************************************/
/* CPLGettimeofday() */
/************************************************************************/

#if defined(_WIN32) && !defined(__CYGWIN__)
#include <sys/timeb.h>

namespace
{
struct CPLTimeVal
{
time_t tv_sec; /* seconds */
long tv_usec; /* and microseconds */
};
} // namespace

static int CPLGettimeofday(struct CPLTimeVal *tp, void * /* timezonep*/)
{
struct _timeb theTime;

_ftime(&theTime);
tp->tv_sec = static_cast<time_t>(theTime.time);
tp->tv_usec = theTime.millitm * 1000;
return 0;
}
#else
#include <sys/time.h> /* for gettimeofday() */
#define CPLTimeVal timeval
#define CPLGettimeofday(t, u) gettimeofday(t, u)
#endif

/************************************************************************/
/* CanUseSourcesMinMaxImplementations() */
/************************************************************************/
Expand All @@ -552,6 +583,10 @@ bool VRTSourcedRasterBand::CanUseSourcesMinMaxImplementations()
// on the filesystem, whose open time and GetMinimum()/GetMaximum()
// implementations we hope to be fast enough.
// In case of doubt return FALSE.
struct CPLTimeVal tvStart;
memset(&tvStart, 0, sizeof(CPLTimeVal));
if (nSources > 1)
CPLGettimeofday(&tvStart, nullptr);
for (int iSource = 0; iSource < nSources; iSource++)
{
if (!(papoSources[iSource]->IsSimpleSource()))
Expand All @@ -572,7 +607,7 @@ bool VRTSourcedRasterBand::CanUseSourcesMinMaxImplementations()
{
if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') || ch == ':' || ch == '/' ||
ch == '\\' || ch == ' ' || ch == '.'))
ch == '\\' || ch == ' ' || ch == '.' || ch == '_'))
break;
}
if (ch != '\0')
Expand All @@ -581,6 +616,15 @@ bool VRTSourcedRasterBand::CanUseSourcesMinMaxImplementations()
VSIStatBuf sStat;
if (VSIStat(pszFilename, &sStat) != 0)
return false;
if (nSources > 1)
{
struct CPLTimeVal tvCur;
CPLGettimeofday(&tvCur, nullptr);
if (tvCur.tv_sec - tvStart.tv_sec +
(tvCur.tv_usec - tvStart.tv_usec) * 1e-6 >
1)
return false;
}
}
}
return true;
Expand All @@ -592,9 +636,6 @@ bool VRTSourcedRasterBand::CanUseSourcesMinMaxImplementations()

double VRTSourcedRasterBand::GetMinimum(int *pbSuccess)
{
if (!CanUseSourcesMinMaxImplementations())
return GDALRasterBand::GetMinimum(pbSuccess);

const char *const pszValue = GetMetadataItem("STATISTICS_MINIMUM");
if (pszValue != nullptr)
{
Expand All @@ -604,6 +645,9 @@ double VRTSourcedRasterBand::GetMinimum(int *pbSuccess)
return CPLAtofM(pszValue);
}

if (!CanUseSourcesMinMaxImplementations())
return GDALRasterBand::GetMinimum(pbSuccess);

const std::string osFctId("VRTSourcedRasterBand::GetMinimum");
GDALAntiRecursionGuard oGuard(osFctId);
if (oGuard.GetCallDepth() >= 32)
Expand All @@ -623,6 +667,10 @@ double VRTSourcedRasterBand::GetMinimum(int *pbSuccess)
return 0;
}

struct CPLTimeVal tvStart;
memset(&tvStart, 0, sizeof(CPLTimeVal));
if (nSources > 1)
CPLGettimeofday(&tvStart, nullptr);
double dfMin = 0;
for (int iSource = 0; iSource < nSources; iSource++)
{
Expand All @@ -636,7 +684,22 @@ double VRTSourcedRasterBand::GetMinimum(int *pbSuccess)
}

if (iSource == 0 || dfSourceMin < dfMin)
{
dfMin = dfSourceMin;
if (dfMin == 0 && eDataType == GDT_Byte)
break;
}
if (nSources > 1)
{
struct CPLTimeVal tvCur;
CPLGettimeofday(&tvCur, nullptr);
if (tvCur.tv_sec - tvStart.tv_sec +
(tvCur.tv_usec - tvStart.tv_usec) * 1e-6 >
1)
{
return GDALRasterBand::GetMinimum(pbSuccess);
}
}
}

if (pbSuccess != nullptr)
Expand All @@ -651,9 +714,6 @@ double VRTSourcedRasterBand::GetMinimum(int *pbSuccess)

double VRTSourcedRasterBand::GetMaximum(int *pbSuccess)
{
if (!CanUseSourcesMinMaxImplementations())
return GDALRasterBand::GetMaximum(pbSuccess);

const char *const pszValue = GetMetadataItem("STATISTICS_MAXIMUM");
if (pszValue != nullptr)
{
Expand All @@ -663,6 +723,9 @@ double VRTSourcedRasterBand::GetMaximum(int *pbSuccess)
return CPLAtofM(pszValue);
}

if (!CanUseSourcesMinMaxImplementations())
return GDALRasterBand::GetMaximum(pbSuccess);

const std::string osFctId("VRTSourcedRasterBand::GetMaximum");
GDALAntiRecursionGuard oGuard(osFctId);
if (oGuard.GetCallDepth() >= 32)
Expand All @@ -682,6 +745,10 @@ double VRTSourcedRasterBand::GetMaximum(int *pbSuccess)
return 0;
}

struct CPLTimeVal tvStart;
memset(&tvStart, 0, sizeof(CPLTimeVal));
if (nSources > 1)
CPLGettimeofday(&tvStart, nullptr);
double dfMax = 0;
for (int iSource = 0; iSource < nSources; iSource++)
{
Expand All @@ -695,7 +762,22 @@ double VRTSourcedRasterBand::GetMaximum(int *pbSuccess)
}

if (iSource == 0 || dfSourceMax > dfMax)
{
dfMax = dfSourceMax;
if (dfMax == 255.0 && eDataType == GDT_Byte)
break;
}
if (nSources > 1)
{
struct CPLTimeVal tvCur;
CPLGettimeofday(&tvCur, nullptr);
if (tvCur.tv_sec - tvStart.tv_sec +
(tvCur.tv_usec - tvStart.tv_usec) * 1e-6 >
1)
{
return GDALRasterBand::GetMaximum(pbSuccess);
}
}
}

if (pbSuccess != nullptr)
Expand Down

0 comments on commit 0f9d6db

Please sign in to comment.