Skip to content

Commit 8ddab44

Browse files
committed
Ensure zonal stats respsects all user set no data values
1 parent 8391396 commit 8ddab44

12 files changed

+87
-13
lines changed

src/analysis/vector/qgszonalstatistics.cpp

+4-5
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ int QgsZonalStatistics::calculateStatistics( QgsFeedback *feedback )
6262
}
6363

6464
mRasterProvider = mRasterLayer->dataProvider();
65-
mInputNodataValue = mRasterProvider->sourceNoDataValue( mRasterBand );
6665

6766
//get geometry info about raster layer
6867
int nCellsXProvider = mRasterProvider->xSize();
@@ -402,7 +401,7 @@ void QgsZonalStatistics::statisticsFromMiddlePointTest( const QgsGeometry &poly,
402401
for ( int j = 0; j < nCellsX; ++j )
403402
{
404403
double pixelValue = block->value( i, j );
405-
if ( validPixel( pixelValue ) )
404+
if ( validPixel( pixelValue ) && !block->isNoData( i, j ) )
406405
{
407406
QgsPoint cellCenter( cellCenterX, cellCenterY );
408407
if ( polyEngine->contains( &cellCenter ) )
@@ -446,7 +445,7 @@ void QgsZonalStatistics::statisticsFromPreciseIntersection( const QgsGeometry &p
446445
for ( int j = 0; j < nCellsX; ++j )
447446
{
448447
double pixelValue = block->value( i, j );
449-
if ( !validPixel( pixelValue ) )
448+
if ( !validPixel( pixelValue ) || block->isNoData( i, j ) )
450449
{
451450
continue;
452451
}
@@ -474,9 +473,9 @@ void QgsZonalStatistics::statisticsFromPreciseIntersection( const QgsGeometry &p
474473
}
475474
}
476475

477-
bool QgsZonalStatistics::validPixel( float value ) const
476+
bool QgsZonalStatistics::validPixel( double value ) const
478477
{
479-
return !( value == mInputNodataValue || std::isnan( value ) );
478+
return !std::isnan( value );
480479
}
481480

482481
QString QgsZonalStatistics::getUniqueFieldName( const QString &fieldName, const QList<QgsField> &newFields )

src/analysis/vector/qgszonalstatistics.h

+1-3
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ class ANALYSIS_EXPORT QgsZonalStatistics
143143
double cellSizeX, double cellSizeY, const QgsRectangle &rasterBBox, FeatureStats &stats );
144144

145145
//! Tests whether a pixel's value should be included in the result
146-
bool validPixel( float value ) const;
146+
bool validPixel( double value ) const;
147147

148148
QString getUniqueFieldName( const QString &fieldName, const QList<QgsField> &newFields );
149149

@@ -153,8 +153,6 @@ class ANALYSIS_EXPORT QgsZonalStatistics
153153
int mRasterBand = 0;
154154
QgsVectorLayer *mPolygonLayer = nullptr;
155155
QString mAttributePrefix;
156-
//! The nodata value of the input layer
157-
float mInputNodataValue = -1;
158156
Statistics mStatistics = QgsZonalStatistics::All;
159157
};
160158

tests/src/analysis/testqgszonalstatistics.cpp

+61-5
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ class TestQgsZonalStatistics : public QObject
4040

4141
void testStatistics();
4242
void testReprojection();
43+
void testNoData();
4344

4445
private:
4546
QgsVectorLayer *mVectorLayer = nullptr;
4647
QgsRasterLayer *mRasterLayer = nullptr;
48+
QString mTempPath;
4749
};
4850

4951
void TestQgsZonalStatistics::initTestCase()
@@ -54,19 +56,19 @@ void TestQgsZonalStatistics::initTestCase()
5456

5557
QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
5658
QString myTestDataPath = myDataPath + "/zonalstatistics/";
57-
QString myTempPath = QDir::tempPath() + '/';
59+
mTempPath = QDir::tempPath() + '/';
5860

5961
// copy test data to temp directory
6062
QDir testDir( myTestDataPath );
6163
QStringList files = testDir.entryList( QDir::Files | QDir::NoDotAndDotDot );
6264
for ( int i = 0; i < files.size(); ++i )
6365
{
64-
QFile::remove( myTempPath + files.at( i ) );
65-
QVERIFY( QFile::copy( myTestDataPath + files.at( i ), myTempPath + files.at( i ) ) );
66+
QFile::remove( mTempPath + files.at( i ) );
67+
QVERIFY( QFile::copy( myTestDataPath + files.at( i ), mTempPath + files.at( i ) ) );
6668
}
6769

68-
mVectorLayer = new QgsVectorLayer( myTempPath + "polys.shp", QStringLiteral( "poly" ), QStringLiteral( "ogr" ) );
69-
mRasterLayer = new QgsRasterLayer( myTempPath + "edge_problem.asc", QStringLiteral( "raster" ), QStringLiteral( "gdal" ) );
70+
mVectorLayer = new QgsVectorLayer( mTempPath + "polys.shp", QStringLiteral( "poly" ), QStringLiteral( "ogr" ) );
71+
mRasterLayer = new QgsRasterLayer( mTempPath + "edge_problem.asc", QStringLiteral( "raster" ), QStringLiteral( "gdal" ) );
7072
QgsProject::instance()->addMapLayers(
7173
QList<QgsMapLayer *>() << mVectorLayer << mRasterLayer );
7274
}
@@ -246,5 +248,59 @@ void TestQgsZonalStatistics::testReprojection()
246248
QCOMPARE( f.attribute( "variance" ).toDouble(), 0.13888888888889 );
247249
}
248250

251+
void TestQgsZonalStatistics::testNoData()
252+
{
253+
QString myDataPath( TEST_DATA_DIR ); //defined in CmakeLists.txt
254+
QString myTestDataPath = myDataPath + "/zonalstatistics/";
255+
256+
// test that zonal stats respects no data and user set no data values
257+
std::unique_ptr< QgsRasterLayer > rasterLayer = qgis::make_unique< QgsRasterLayer >( myTestDataPath + "raster.tif", QStringLiteral( "raster" ), QStringLiteral( "gdal" ) );
258+
std::unique_ptr< QgsVectorLayer > vectorLayer = qgis::make_unique< QgsVectorLayer >( mTempPath + "polys2.shp", QStringLiteral( "poly" ), QStringLiteral( "ogr" ) );
259+
260+
QgsZonalStatistics zs( vectorLayer.get(), rasterLayer.get(), QStringLiteral( "n" ), 1, QgsZonalStatistics::All );
261+
zs.calculateStatistics( nullptr );
262+
263+
QgsFeature f;
264+
QgsFeatureRequest request;
265+
QgsFeatureIterator it = vectorLayer->getFeatures( request );
266+
bool fetched = it.nextFeature( f );
267+
QVERIFY( fetched );
268+
QCOMPARE( f.attribute( "ncount" ).toDouble(), 16.0 );
269+
QCOMPARE( f.attribute( "nsum" ).toDouble(), 13428.0 );
270+
271+
fetched = it.nextFeature( f );
272+
QVERIFY( fetched );
273+
QCOMPARE( f.attribute( "ncount" ).toDouble(), 103.0 );
274+
QCOMPARE( f.attribute( "nsum" ).toDouble(), 90536.0 );
275+
276+
fetched = it.nextFeature( f );
277+
QVERIFY( fetched );
278+
QCOMPARE( f.attribute( "ncount" ).toDouble(), 0.0 );
279+
QCOMPARE( f.attribute( "nsum" ).toDouble(), 0.0 );
280+
281+
// with user no data
282+
rasterLayer->dataProvider()->setUserNoDataValue( 1, QgsRasterRangeList() << QgsRasterRange( 842, 852 )
283+
<< QgsRasterRange( 877, 891 ) );
284+
285+
zs = QgsZonalStatistics( vectorLayer.get(), rasterLayer.get(), QStringLiteral( "un" ), 1, QgsZonalStatistics::All );
286+
zs.calculateStatistics( nullptr );
287+
288+
it = vectorLayer->getFeatures( request );
289+
fetched = it.nextFeature( f );
290+
QVERIFY( fetched );
291+
QCOMPARE( f.attribute( "uncount" ).toDouble(), 8.0 );
292+
QCOMPARE( f.attribute( "unsum" ).toDouble(), 6652.0 );
293+
294+
fetched = it.nextFeature( f );
295+
QVERIFY( fetched );
296+
QCOMPARE( f.attribute( "uncount" ).toDouble(), 52.0 );
297+
QCOMPARE( f.attribute( "unsum" ).toDouble(), 45374.0 );
298+
299+
fetched = it.nextFeature( f );
300+
QVERIFY( fetched );
301+
QCOMPARE( f.attribute( "uncount" ).toDouble(), 0.0 );
302+
QCOMPARE( f.attribute( "unsum" ).toDouble(), 0.0 );
303+
}
304+
249305
QGSTEST_MAIN( TestQgsZonalStatistics )
250306
#include "testqgszonalstatistics.moc"

tests/testdata/zonalstatistics/edge_problem.asc.aux.xml

+8
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@
1717
<Approximate>0</Approximate>
1818
<HistCounts>4|0|0|0|0|0|0|0|0|0|0|8</HistCounts>
1919
</HistItem>
20+
<HistItem>
21+
<HistMin>-0.25</HistMin>
22+
<HistMax>1.25</HistMax>
23+
<BucketCount>2</BucketCount>
24+
<IncludeOutOfRange>0</IncludeOutOfRange>
25+
<Approximate>0</Approximate>
26+
<HistCounts>4|8</HistCounts>
27+
</HistItem>
2028
</Histograms>
2129
<Metadata>
2230
<MDI key="STATISTICS_MAXIMUM">1</MDI>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
UTF-8
99 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
PROJCS["ED50_UTM_zone_30N",GEOGCS["GCS_European_1950",DATUM["D_European_1950",SPHEROID["International_1924",6378388,297]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-3],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["Meter",1]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
PROJCS["ED50 / UTM zone 30N",GEOGCS["ED50",DATUM["European_Datum_1950",SPHEROID["International 1924",6378388,297,AUTHORITY["EPSG","7022"]],TOWGS84[-87,-98,-121,0,0,0,0],AUTHORITY["EPSG","6230"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4230"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-3],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","23030"]]
508 Bytes
Binary file not shown.
124 Bytes
Binary file not shown.
1.28 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<PAMDataset>
2+
<PAMRasterBand band="1">
3+
<Metadata>
4+
<MDI key="STATISTICS_MAXIMUM">899</MDI>
5+
<MDI key="STATISTICS_MEAN">865.86666666667</MDI>
6+
<MDI key="STATISTICS_MINIMUM">826</MDI>
7+
<MDI key="STATISTICS_STDDEV">17.808206597584</MDI>
8+
</Metadata>
9+
</PAMRasterBand>
10+
</PAMDataset>

0 commit comments

Comments
 (0)