Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
QgsGdalProvider::readBlock(): simplify call to GDALRasterIO()
Instead of doing our own nearest neighbour resampling, use directly the
one of GDAL to avoid creating a temporary buffer. Should hopefully be
slightly more performant, and at least less complicated on QGIS side.

Update a few test cases that are sensitive to the difference in
resampling
  • Loading branch information
rouault authored and nyalldawson committed Jun 19, 2020
1 parent 5cd56e3 commit 45539cf
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 133 deletions.
103 changes: 13 additions & 90 deletions src/core/providers/gdal/qgsgdalprovider.cpp
Expand Up @@ -741,12 +741,9 @@ bool QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &reqExtent, int
QgsDebugMsgLevel( "extent: " + mExtent.toString(), 5 );
QgsDebugMsgLevel( "intersectExtent: " + intersectExtent.toString(), 5 );

const double reqXRes = reqExtent.width() / bufferWidthPix;
const double reqYRes = reqExtent.height() / bufferHeightPix;

const double srcXRes = mGeoTransform[1];
const double srcYRes = mGeoTransform[5]; // may be negative?
QgsDebugMsgLevel( QStringLiteral( "reqXRes = %1 reqYRes = %2 srcXRes = %3 srcYRes = %4" ).arg( reqXRes ).arg( reqYRes ).arg( srcXRes ).arg( srcYRes ), 5 );
QgsDebugMsgLevel( QStringLiteral( "srcXRes = %1 srcYRes = %2" ).arg( srcXRes ).arg( srcYRes ), 5 );

// Find top, bottom rows and left, right column the raster extent covers
// These are limits in target grid space
Expand All @@ -769,77 +766,41 @@ bool QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &reqExtent, int
// Get necessary src extent aligned to src resolution
if ( mExtent.xMinimum() < intersectExtent.xMinimum() )
{
srcLeft = static_cast<int>( std::floor( ( intersectExtent.xMinimum() - mExtent.xMinimum() ) / srcXRes ) );
srcLeft = static_cast<int>( std::floor( ( intersectExtent.xMinimum() - mExtent.xMinimum() ) / srcXRes + 1e-8 ) );
}
if ( mExtent.xMaximum() > intersectExtent.xMaximum() )
{
// Clamp to raster width to avoid potential rounding errors (see GH #34435)
srcRight = std::min( mWidth - 1, static_cast<int>( std::floor( ( intersectExtent.xMaximum() - mExtent.xMinimum() ) / srcXRes ) ) );
srcRight = std::min( mWidth - 1, static_cast<int>( std::floor( ( intersectExtent.xMaximum() - mExtent.xMinimum() ) / srcXRes - 1e-8 ) ) );
}

// GDAL states that mGeoTransform[3] is top, may it also be bottom and mGeoTransform[5] positive?
if ( mExtent.yMaximum() > intersectExtent.yMaximum() )
{
srcTop = static_cast<int>( std::floor( -1. * ( mExtent.yMaximum() - intersectExtent.yMaximum() ) / srcYRes ) );
srcTop = static_cast<int>( std::floor( -1. * ( mExtent.yMaximum() - intersectExtent.yMaximum() ) / srcYRes + 1e-8 ) );
}
if ( mExtent.yMinimum() < intersectExtent.yMinimum() )
{
// Clamp to raster height to avoid potential rounding errors (see GH #34435)
srcBottom = std::min( mHeight - 1, static_cast<int>( std::floor( -1. * ( mExtent.yMaximum() - intersectExtent.yMinimum() ) / srcYRes ) ) );
srcBottom = std::min( mHeight - 1, static_cast<int>( std::floor( -1. * ( mExtent.yMaximum() - intersectExtent.yMinimum() ) / srcYRes - 1e-8 ) ) );
}

const int srcWidth = srcRight - srcLeft + 1;
const int srcHeight = srcBottom - srcTop + 1;
const int srcWidth = std::max( 1, srcRight - srcLeft + 1 );
const int srcHeight = std::max( 1, srcBottom - srcTop + 1 );
QgsDebugMsgLevel( QStringLiteral( "srcTop = %1 srcBottom = %2 srcWidth = %3 srcHeight = %4" ).arg( srcTop ).arg( srcBottom ).arg( srcWidth ).arg( srcHeight ), 5 );

// Determine the dimensions of the buffer into which we will ask GDAL to write
// pixels.
// In downsampling scenarios, we will use the request resolution to compute the dimension
// In upsampling (or exactly at raster resolution) scenarios, we will use the raster resolution to compute the dimension
int tmpWidth = srcWidth;
int tmpHeight = srcHeight;

if ( reqXRes > srcXRes )
{
// downsampling
tmpWidth = static_cast<int>( std::round( srcWidth * srcXRes / reqXRes ) );
}
if ( reqYRes > std::fabs( srcYRes ) )
{
// downsampling
tmpHeight = static_cast<int>( std::round( -1.*srcHeight * srcYRes / reqYRes ) );
}

const double tmpXMin = mExtent.xMinimum() + srcLeft * srcXRes;
const double tmpYMax = mExtent.yMaximum() + srcTop * srcYRes;
QgsDebugMsgLevel( QStringLiteral( "tmpXMin = %1 tmpYMax = %2 tmpWidth = %3 tmpHeight = %4" ).arg( tmpXMin ).arg( tmpYMax ).arg( tmpWidth ).arg( tmpHeight ), 5 );

// Allocate temporary block
size_t bufferSize = dataSize * static_cast<size_t>( tmpWidth ) * static_cast<size_t>( tmpHeight );
#ifdef Q_PROCESSOR_X86_32
// Safety check for 32 bit systems
qint64 _buffer_size = dataSize * static_cast<qint64>( tmpWidth ) * static_cast<qint64>( tmpHeight );
if ( _buffer_size != static_cast<qint64>( bufferSize ) )
{
QgsDebugMsg( QStringLiteral( "Integer overflow calculating buffer size on a 32 bit system." ) );
return false;
}
#endif
char *tmpBlock = static_cast<char *>( qgsMalloc( bufferSize ) );
if ( ! tmpBlock )
{
QgsDebugMsgLevel( QStringLiteral( "Couldn't allocate temporary buffer of %1 bytes" ).arg( dataSize * tmpWidth * tmpHeight ), 5 );
return false;
}
GDALRasterBandH gdalBand = getBand( bandNo );
GDALDataType type = static_cast<GDALDataType>( mGdalDataType.at( bandNo - 1 ) );
CPLErrorReset();

CPLErr err = gdalRasterIO( gdalBand, GF_Read,
srcLeft, srcTop, srcWidth, srcHeight,
static_cast<void *>( tmpBlock ),
tmpWidth, tmpHeight, type,
0, 0, feedback );
static_cast<char *>( data ) +
( tgtTop * bufferWidthPix + tgtLeft ) * dataSize,
tgtWidth, tgtHeight,
type,
dataSize, bufferWidthPix * dataSize,
feedback );

if ( err != CPLE_None )
{
Expand All @@ -848,47 +809,9 @@ bool QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &reqExtent, int
feedback->appendError( lastError );

QgsLogger::warning( "RasterIO error: " + lastError );
qgsFree( tmpBlock );
return false;
}

const double tmpXRes = srcWidth * srcXRes / tmpWidth;
const double tmpYRes = srcHeight * srcYRes / tmpHeight; // negative

double y = intersectExtent.yMaximum() - 0.5 * reqYRes;
for ( int row = 0; row < tgtHeight; row++ )
{
int tmpRow = static_cast<int>( std::floor( -1. * ( tmpYMax - y ) / tmpYRes ) );
tmpRow = std::min( tmpRow, tmpHeight - 1 );

char *srcRowBlock = tmpBlock + dataSize * tmpRow * tmpWidth;
char *dstRowBlock = ( char * )data + dataSize * ( tgtTop + row ) * bufferWidthPix;

double x = ( intersectExtent.xMinimum() + 0.5 * reqXRes - tmpXMin ) / tmpXRes; // cell center
double increment = reqXRes / tmpXRes;

char *dst = dstRowBlock + dataSize * tgtLeft;
char *src = srcRowBlock;
int tmpCol = 0;
int lastCol = 0;
for ( int col = 0; col < tgtWidth; ++col )
{
// std::floor() is quite slow! Use just cast to int.
tmpCol = static_cast<int>( x );
tmpCol = std::min( tmpCol, tmpWidth - 1 );
if ( tmpCol > lastCol )
{
src += ( tmpCol - lastCol ) * dataSize;
lastCol = tmpCol;
}
memcpy( dst, src, dataSize );
dst += dataSize;
x += increment;
}
y -= reqYRes;
}

qgsFree( tmpBlock );
return true;
}

Expand Down
77 changes: 40 additions & 37 deletions tests/src/analysis/testqgsrastercalculator.cpp
Expand Up @@ -448,7 +448,7 @@ void TestQgsRasterCalculator::calcWithLayers()
entries << entry1 << entry2;

QgsCoordinateReferenceSystem crs( QStringLiteral( "EPSG:32633" ) );
QgsRectangle extent( 783235, 3348110, 783350, 3347960 );
QgsRectangle extent( 783201.375, 3348130.125, 783315.375, 3347959.125 );

QTemporaryFile tmpFile;
tmpFile.open(); // fileName is not available until open
Expand All @@ -467,12 +467,13 @@ void TestQgsRasterCalculator::calcWithLayers()
QCOMPARE( result->width(), 2 );
QCOMPARE( result->height(), 3 );
QgsRasterBlock *block = result->dataProvider()->block( 1, extent, 2, 3 );
QCOMPARE( block->value( 0, 0 ), 127.0 );
QCOMPARE( block->value( 0, 1 ), 127.0 );
QCOMPARE( block->value( 1, 0 ), 126.0 );
QCOMPARE( block->value( 1, 1 ), 127.0 );
QCOMPARE( block->value( 2, 0 ), 127.0 );
QCOMPARE( block->value( 2, 1 ), 126.0 );
// gdal_translate ../tests/testdata/landsat.tif -projwin 783201.375 3348130.125 783315.375 3348073.125 /vsistdout/ -of aaigrid -b 1
QCOMPARE( block->value( 0, 0 ), 125.0 + 2 );
QCOMPARE( block->value( 0, 1 ), 125.0 + 2 );
QCOMPARE( block->value( 1, 0 ), 125.0 + 2 );
QCOMPARE( block->value( 1, 1 ), 124.0 + 2 );
QCOMPARE( block->value( 2, 0 ), 126.0 + 2 );
QCOMPARE( block->value( 2, 1 ), 125.0 + 2 );
delete result;
delete block;

Expand All @@ -489,12 +490,14 @@ void TestQgsRasterCalculator::calcWithLayers()
QCOMPARE( result->width(), 2 );
QCOMPARE( result->height(), 3 );
block = result->dataProvider()->block( 1, extent, 2, 3 );
QCOMPARE( block->value( 0, 0 ), 265.0 );
QCOMPARE( block->value( 0, 1 ), 263.0 );
QCOMPARE( block->value( 1, 0 ), 263.0 );
QCOMPARE( block->value( 1, 1 ), 264.0 );
QCOMPARE( block->value( 2, 0 ), 266.0 );
QCOMPARE( block->value( 2, 1 ), 261.0 );
// gdal_translate ../tests/testdata/landsat.tif -projwin 783201.375 3348130.125 783315.375 3348073.125 /vsistdout/ -of aaigrid -b 1
// gdal_translate ../tests/testdata/landsat.tif -projwin 783201.375 3348130.125 783315.375 3348073.125 /vsistdout/ -of aaigrid -b 2
QCOMPARE( block->value( 0, 0 ), 125.0 + 139.0 );
QCOMPARE( block->value( 0, 1 ), 125.0 + 140.0 );
QCOMPARE( block->value( 1, 0 ), 125.0 + 140.0 );
QCOMPARE( block->value( 1, 1 ), 124.0 + 139.0 );
QCOMPARE( block->value( 2, 0 ), 126.0 + 141.0 );
QCOMPARE( block->value( 2, 1 ), 125.0 + 141.0 );
delete result;
delete block;
}
Expand All @@ -515,7 +518,7 @@ void TestQgsRasterCalculator::calcWithReprojectedLayers()
entries << entry1 << entry2;

QgsCoordinateReferenceSystem crs( QStringLiteral( "EPSG:32633" ) );
QgsRectangle extent( 783235, 3348110, 783350, 3347960 );
QgsRectangle extent( 783201.375, 3348130.125, 783315.375, 3347959.125 );

QTemporaryFile tmpFile;
tmpFile.open(); // fileName is not available until open
Expand All @@ -536,10 +539,10 @@ void TestQgsRasterCalculator::calcWithReprojectedLayers()
QgsRasterBlock *block = result->dataProvider()->block( 1, extent, 2, 3 );
QCOMPARE( block->value( 0, 0 ), 264.0 );
QCOMPARE( block->value( 0, 1 ), 263.0 );
QCOMPARE( block->value( 1, 0 ), 264.0 );
QCOMPARE( block->value( 1, 0 ), 265.0 );
QCOMPARE( block->value( 1, 1 ), 264.0 );
QCOMPARE( block->value( 2, 0 ), 266.0 );
QCOMPARE( block->value( 2, 1 ), 261.0 );
QCOMPARE( block->value( 2, 0 ), 267.0 );
QCOMPARE( block->value( 2, 1 ), 266.0 );
delete result;
delete block;
}
Expand Down Expand Up @@ -766,7 +769,7 @@ void TestQgsRasterCalculator::calcFormulasWithReprojectedLayers()
entries << entry1 << entry2;

QgsCoordinateReferenceSystem crs( QStringLiteral( "EPSG:32633" ) );
QgsRectangle extent( 783235, 3348110, 783350, 3347960 );
QgsRectangle extent( 783201.375, 3348130.125, 783315.375, 3347959.125 );


auto _chk = [ = ]( const QString & formula, const std::vector<float> &values, bool useOpenCL )
Expand Down Expand Up @@ -797,9 +800,9 @@ void TestQgsRasterCalculator::calcFormulasWithReprojectedLayers()
QCOMPARE( result->width(), 2 );
QCOMPARE( result->height(), 3 );
QgsRasterBlock *block = result->dataProvider()->block( 1, extent, 2, 3 );
qDebug() << "Actual:" << block->value( 0, 0 ) << block->value( 0, 1 ) << block->value( 1, 0 ) << block->value( 1, 1 ) << block->value( 2, 0 ) << block->value( 2, 1 );
qDebug() << "Actual: " << block->value( 0, 0 ) << block->value( 0, 1 ) << block->value( 1, 0 ) << block->value( 1, 1 ) << block->value( 2, 0 ) << block->value( 2, 1 );
qDebug() << "Expected:" << values[0] << values[1] << values[2] << values[3] << values[4] << values[5];
const double epsilon { 0.0001 };
const double epsilon { 0.001 };
QVERIFY( qgsDoubleNear( block->value( 0, 0 ), static_cast<double>( values[0] ), epsilon ) );
QVERIFY( qgsDoubleNear( block->value( 0, 1 ), static_cast<double>( values[1] ), epsilon ) );
QVERIFY( qgsDoubleNear( block->value( 1, 0 ), static_cast<double>( values[2] ), epsilon ) );
Expand All @@ -810,35 +813,35 @@ void TestQgsRasterCalculator::calcFormulasWithReprojectedLayers()
delete block;
};

_chk( QStringLiteral( "\"landsat@1\" + \"landsat_4326@2\"" ), {264.0, 263.0, 264.0, 264.0, 266.0, 261.0}, false );
_chk( QStringLiteral( "\"landsat@1\" + \"landsat_4326@2\"" ), {264.0, 263.0, 264.0, 264.0, 266.0, 261.0}, true );
_chk( QStringLiteral( "\"landsat@1\"^2 + 3 + \"landsat_4326@2\"" ), {15767, 15766, 15519, 15767, 15769, 15516}, false );
_chk( QStringLiteral( "\"landsat@1\"^2 + 3 + \"landsat_4326@2\"" ), {15767, 15766, 15519, 15767, 15769, 15516}, true );
_chk( QStringLiteral( "0.5*((2*\"landsat@1\"+1)-sqrt((2*\"landsat@1\"+1)^2-8*(\"landsat@1\"-\"landsat_4326@2\")))" ), {-0.111504f, -0.103543f, -0.128448f, -0.111504f, -0.127425f, -0.104374f}, false );
_chk( QStringLiteral( "0.5*((2*\"landsat@1\"+1)-sqrt((2*\"landsat@1\"+1)^2-8*(\"landsat@1\"-\"landsat_4326@2\")))" ), {-0.111504f, -0.103543f, -0.128448f, -0.111504f, -0.127425f, -0.104374f}, true );
_chk( QStringLiteral( "\"landsat@1\" * ( \"landsat@1\" > 124 )" ), {125.0, 125.0, 0.0, 125.0, 125.0, 0.0}, false );
_chk( QStringLiteral( "\"landsat@1\" * ( \"landsat@1\" > 124 )" ), {125.0, 125.0, 0.0, 125.0, 125.0, 0.0}, true );
_chk( QStringLiteral( "\"landsat@1\" + \"landsat_4326@2\"" ), {264.0, 263.0, 265.0, 264.0, 267.0, 266.0}, false );
_chk( QStringLiteral( "\"landsat@1\" + \"landsat_4326@2\"" ), {264.0, 263.0, 265.0, 264.0, 267.0, 266.0}, true );
_chk( QStringLiteral( "\"landsat@1\"^2 + 3 + \"landsat_4326@2\"" ), {15767, 15766, 15768, 15519, 16020, 15769}, false );
_chk( QStringLiteral( "\"landsat@1\"^2 + 3 + \"landsat_4326@2\"" ), {15767, 15766, 15768, 15519, 16020, 15769}, true );
_chk( QStringLiteral( "0.5*((2*\"landsat@1\"+1)-sqrt((2*\"landsat@1\"+1)^2-8*(\"landsat@1\"-\"landsat_4326@2\")))" ), {-0.111504f, -0.103543f, -0.119465f, -0.128448f, -0.118522f, -0.127425f}, false );
_chk( QStringLiteral( "0.5*((2*\"landsat@1\"+1)-sqrt((2*\"landsat@1\"+1)^2-8*(\"landsat@1\"-\"landsat_4326@2\")))" ), {-0.111504f, -0.103543f, -0.119465f, -0.128456f, -0.118515f, -0.127419f}, true );
_chk( QStringLiteral( "\"landsat@1\" * ( \"landsat@1\" > 124 )" ), {125.0, 125.0, 125.0, 0.0, 126.0, 125.0}, false );
_chk( QStringLiteral( "\"landsat@1\" * ( \"landsat@1\" > 124 )" ), {125.0, 125.0, 125.0, 0.0, 126.0, 125.0}, true );

// Test negative numbers
_chk( QStringLiteral( "-2.5" ), { -2.5, -2.5, -2.5, -2.5, -2.5, -2.5 }, false );
_chk( QStringLiteral( "- 2.5" ), { -2.5, -2.5, -2.5, -2.5, -2.5, -2.5 }, false );
_chk( QStringLiteral( "-2.5" ), { -2.5, -2.5, -2.5, -2.5, -2.5, -2.5 }, true );
_chk( QStringLiteral( "- 2.5" ), { -2.5, -2.5, -2.5, -2.5, -2.5, -2.5 }, true );
_chk( QStringLiteral( "-\"landsat@1\"" ), {-125, -125, -124, -125, -125, -124}, false );
_chk( QStringLiteral( "-\"landsat@1\"" ), {-125, -125, -124, -125, -125, -124}, true );
_chk( QStringLiteral( "-\"landsat@1\"" ), {-125, -125, -125, -124, -126, -125}, false );
_chk( QStringLiteral( "-\"landsat@1\"" ), {-125, -125, -125, -124, -126, -125}, true );

// Test abs, min and max
// landsat values: 125 125 124 125 125 124
// landsat_4326 values: 139 138 140 139 141 137
_chk( QStringLiteral( "abs(-123)" ), {123, 123, 123, 123, 123, 123}, false );
_chk( QStringLiteral( "abs(-\"landsat@1\")" ), {125, 125, 124, 125, 125, 124}, true );
_chk( QStringLiteral( "abs(-\"landsat@1\")" ), {125, 125, 125, 124, 126, 125}, true );
_chk( QStringLiteral( "abs(-123)" ), {123, 123, 123, 123, 123, 123}, false );
_chk( QStringLiteral( "abs(-\"landsat@1\")" ), {125, 125, 124, 125, 125, 124}, true );
_chk( QStringLiteral( "-\"landsat_4326@2\" + 15" ), {-124, -123, -125, -124, -126, -122}, false );
_chk( QStringLiteral( "min(-\"landsat@1\", -\"landsat_4326@2\" + 15 )" ), {-125, -125, -125, -125, -126, -124}, false );
_chk( QStringLiteral( "min(-\"landsat@1\", -\"landsat_4326@2\" + 15 )" ), {-125, -125, -125, -125, -126, -124}, true );
_chk( QStringLiteral( "max(-\"landsat@1\", -\"landsat_4326@2\" + 15 )" ), {-124, -123, -124, -124, -125, -122}, false );
_chk( QStringLiteral( "max(-\"landsat@1\", -\"landsat_4326@2\" + 15 )" ), {-124, -123, -124, -124, -125, -122}, true );
_chk( QStringLiteral( "abs(-\"landsat@1\")" ), {125, 125, 125, 124, 126, 125}, true );
_chk( QStringLiteral( "-\"landsat_4326@2\" + 15" ), {-124, -123, -125, -125, -126, -126}, false );
_chk( QStringLiteral( "min(-\"landsat@1\", -\"landsat_4326@2\" + 15 )" ), {-125, -125, -125, -125, -126, -126}, false );
_chk( QStringLiteral( "min(-\"landsat@1\", -\"landsat_4326@2\" + 15 )" ), {-125, -125, -125, -125, -126, -126}, true );
_chk( QStringLiteral( "max(-\"landsat@1\", -\"landsat_4326@2\" + 15 )" ), {-124, -123, -125, -124, -126, -125}, false );
_chk( QStringLiteral( "max(-\"landsat@1\", -\"landsat_4326@2\" + 15 )" ), {-124, -123, -125, -124, -126, -125}, true );

}

Expand Down
12 changes: 6 additions & 6 deletions tests/src/core/testqgsrasterlayer.cpp
Expand Up @@ -417,31 +417,31 @@ void TestQgsRasterLayer::checkStats()
// limited extent
myStatistics = mpRasterLayer->dataProvider()->bandStatistics( 1,
QgsRasterBandStats::Min | QgsRasterBandStats::Max |
QgsRasterBandStats::Mean | QgsRasterBandStats::StdDev, QgsRectangle( 1535400, 5083280, 1535450, 5083320 ) );
QgsRasterBandStats::Mean | QgsRasterBandStats::StdDev, QgsRectangle( 1535395, 5083275, 1535455, 5083325 ) );

QCOMPARE( myStatistics.minimumValue, 2.0 );
QCOMPARE( myStatistics.maximumValue, 7.0 );
QGSCOMPARENEAR( myStatistics.mean, 4.5, 4 * std::numeric_limits<double>::epsilon() );
QGSCOMPARENEAR( myStatistics.stdDev, 1.507557, 0.00001 );
QGSCOMPARENEAR( myStatistics.stdDev, 1.755223, 0.00001 );

// with sample size
myStatistics = mpRasterLayer->dataProvider()->bandStatistics( 1,
QgsRasterBandStats::Min | QgsRasterBandStats::Max |
QgsRasterBandStats::Mean | QgsRasterBandStats::StdDev, QgsRectangle( 1535400, 5083280, 1535450, 5083320 ), 10 );
QgsRasterBandStats::Mean | QgsRasterBandStats::StdDev, QgsRectangle( 1535395, 5083275, 1535455, 5083325 ), 10 );
QCOMPARE( myStatistics.minimumValue, 2.0 );
QCOMPARE( myStatistics.maximumValue, 7.0 );
QCOMPARE( myStatistics.elementCount, 12ULL );
QGSCOMPARENEAR( myStatistics.mean, 4.5, 4 * std::numeric_limits<double>::epsilon() );
QGSCOMPARENEAR( myStatistics.stdDev, 2.153222, 0.00001 );
QGSCOMPARENEAR( myStatistics.stdDev, 1.882938, 0.00001 );

// extremely limited extent - ~1 px size
myStatistics = mpRasterLayer->dataProvider()->bandStatistics( 1,
QgsRasterBandStats::Min | QgsRasterBandStats::Max |
QgsRasterBandStats::Mean | QgsRasterBandStats::StdDev, QgsRectangle( 1535400, 5083280, 1535412, 5083288 ) );
QCOMPARE( myStatistics.minimumValue, 2.0 );
QCOMPARE( myStatistics.maximumValue, 3.0 );
QGSCOMPARENEAR( myStatistics.mean, 2.600000, 4 * std::numeric_limits<double>::epsilon() );
QGSCOMPARENEAR( myStatistics.stdDev, 0.492366, 0.00001 );
QGSCOMPARENEAR( myStatistics.mean, 2.500000, 4 * std::numeric_limits<double>::epsilon() );
QGSCOMPARENEAR( myStatistics.stdDev, 0.502519, 0.00001 );

// extremely limited extent - ~1 px size - with sample size
myStatistics = mpRasterLayer->dataProvider()->bandStatistics( 1,
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 45539cf

Please sign in to comment.