Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #4069 from alexbruy/zonalstatistics-tests
Zonal statistics tests
  • Loading branch information
alexbruy committed Jan 27, 2017
2 parents aef2b00 + 9378d20 commit eca795f
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 70 deletions.
35 changes: 18 additions & 17 deletions src/analysis/vector/qgszonalstatistics.cpp
Expand Up @@ -110,77 +110,77 @@ int QgsZonalStatistics::calculateStatistics( QProgressDialog* p )
QString countFieldName;
if ( mStatistics & QgsZonalStatistics::Count )
{
countFieldName = getUniqueFieldName( mAttributePrefix + "count" );
countFieldName = getUniqueFieldName( mAttributePrefix + "count", newFieldList );
QgsField countField( countFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
newFieldList.push_back( countField );
}
QString sumFieldName;
if ( mStatistics & QgsZonalStatistics::Sum )
{
sumFieldName = getUniqueFieldName( mAttributePrefix + "sum" );
sumFieldName = getUniqueFieldName( mAttributePrefix + "sum", newFieldList );
QgsField sumField( sumFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
newFieldList.push_back( sumField );
}
QString meanFieldName;
if ( mStatistics & QgsZonalStatistics::Mean )
{
meanFieldName = getUniqueFieldName( mAttributePrefix + "mean" );
meanFieldName = getUniqueFieldName( mAttributePrefix + "mean", newFieldList );
QgsField meanField( meanFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
newFieldList.push_back( meanField );
}
QString medianFieldName;
if ( mStatistics & QgsZonalStatistics::Median )
{
medianFieldName = getUniqueFieldName( mAttributePrefix + "median" );
medianFieldName = getUniqueFieldName( mAttributePrefix + "median", newFieldList );
QgsField medianField( medianFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
newFieldList.push_back( medianField );
}
QString stdevFieldName;
if ( mStatistics & QgsZonalStatistics::StDev )
{
stdevFieldName = getUniqueFieldName( mAttributePrefix + "stdev" );
stdevFieldName = getUniqueFieldName( mAttributePrefix + "stdev", newFieldList );
QgsField stdField( stdevFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
newFieldList.push_back( stdField );
}
QString minFieldName;
if ( mStatistics & QgsZonalStatistics::Min )
{
minFieldName = getUniqueFieldName( mAttributePrefix + "min" );
minFieldName = getUniqueFieldName( mAttributePrefix + "min", newFieldList );
QgsField minField( minFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
newFieldList.push_back( minField );
}
QString maxFieldName;
if ( mStatistics & QgsZonalStatistics::Max )
{
maxFieldName = getUniqueFieldName( mAttributePrefix + "max" );
maxFieldName = getUniqueFieldName( mAttributePrefix + "max", newFieldList );
QgsField maxField( maxFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
newFieldList.push_back( maxField );
}
QString rangeFieldName;
if ( mStatistics & QgsZonalStatistics::Range )
{
rangeFieldName = getUniqueFieldName( mAttributePrefix + "range" );
rangeFieldName = getUniqueFieldName( mAttributePrefix + "range", newFieldList );
QgsField rangeField( rangeFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
newFieldList.push_back( rangeField );
}
QString minorityFieldName;
if ( mStatistics & QgsZonalStatistics::Minority )
{
minorityFieldName = getUniqueFieldName( mAttributePrefix + "minority" );
minorityFieldName = getUniqueFieldName( mAttributePrefix + "minority", newFieldList );
QgsField minorityField( minorityFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
newFieldList.push_back( minorityField );
}
QString majorityFieldName;
if ( mStatistics & QgsZonalStatistics::Majority )
{
majorityFieldName = getUniqueFieldName( mAttributePrefix + "majority" );
majorityFieldName = getUniqueFieldName( mAttributePrefix + "majority", newFieldList );
QgsField majField( majorityFieldName, QVariant::Double, QStringLiteral( "double precision" ) );
newFieldList.push_back( majField );
}
QString varietyFieldName;
if ( mStatistics & QgsZonalStatistics::Variety )
{
varietyFieldName = getUniqueFieldName( mAttributePrefix + "variety" );
varietyFieldName = getUniqueFieldName( mAttributePrefix + "variety", newFieldList );
QgsField varietyField( varietyFieldName, QVariant::Int, QStringLiteral( "int" ) );
newFieldList.push_back( varietyField );
}
Expand Down Expand Up @@ -521,7 +521,7 @@ bool QgsZonalStatistics::validPixel( float value ) const
return true;
}

QString QgsZonalStatistics::getUniqueFieldName( const QString& fieldName )
QString QgsZonalStatistics::getUniqueFieldName( const QString& fieldName, const QList<QgsField>& newFields )
{
QgsVectorDataProvider* dp = mPolygonLayer->dataProvider();

Expand All @@ -530,13 +530,14 @@ QString QgsZonalStatistics::getUniqueFieldName( const QString& fieldName )
return fieldName;
}

QgsFields providerFields = dp->fields();
QList<QgsField> allFields = dp->fields().toList();
allFields.append( newFields );
QString shortName = fieldName.mid( 0, 10 );

bool found = false;
for ( int idx = 0; idx < providerFields.count(); ++idx )
for ( int idx = 0; idx < allFields.count(); ++idx )
{
if ( shortName == providerFields.at( idx ).name() )
if ( shortName == allFields.at( idx ).name() )
{
found = true;
break;
Expand All @@ -554,9 +555,9 @@ QString QgsZonalStatistics::getUniqueFieldName( const QString& fieldName )
while ( found )
{
found = false;
for ( int idx = 0; idx < providerFields.count(); ++idx )
for ( int idx = 0; idx < allFields.count(); ++idx )
{
if ( shortName == providerFields.at( idx ).name() )
if ( shortName == allFields.at( idx ).name() )
{
n += 1;
if ( n < 9 )
Expand Down
3 changes: 2 additions & 1 deletion src/analysis/vector/qgszonalstatistics.h
Expand Up @@ -28,6 +28,7 @@ class QgsGeometry;
class QgsVectorLayer;
class QProgressDialog;
class QgsRectangle;
class QgsField;

/** \ingroup analysis
* A class that calculates raster statistics (count, sum, mean) for a polygon or multipolygon layer and appends the results as attributes*/
Expand Down Expand Up @@ -123,7 +124,7 @@ class ANALYSIS_EXPORT QgsZonalStatistics
//! Tests whether a pixel's value should be included in the result
bool validPixel( float value ) const;

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

QString mRasterFilePath;
//! Raster band to calculate statistics from (defaults to 1)
Expand Down
52 changes: 50 additions & 2 deletions tests/src/analysis/testqgszonalstatistics.cpp
Expand Up @@ -84,7 +84,7 @@ void TestQgsZonalStatistics::cleanupTestCase()

void TestQgsZonalStatistics::testStatistics()
{
QgsZonalStatistics zs( mVectorLayer, mRasterPath, QLatin1String( "" ), 1 );
QgsZonalStatistics zs( mVectorLayer, mRasterPath, QLatin1String( "" ), 1, QgsZonalStatistics::All );
zs.calculateStatistics( nullptr );

QgsFeature f;
Expand All @@ -95,23 +95,47 @@ void TestQgsZonalStatistics::testStatistics()
QCOMPARE( f.attribute( "count" ).toDouble(), 12.0 );
QCOMPARE( f.attribute( "sum" ).toDouble(), 8.0 );
QCOMPARE( f.attribute( "mean" ).toDouble(), 0.666666666666667 );
QCOMPARE( f.attribute( "median" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "stdev" ).toDouble(), 0.47140452079103201 );
QCOMPARE( f.attribute( "min" ).toDouble(), 0.0 );
QCOMPARE( f.attribute( "max" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "range" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "minority" ).toDouble(), 0.0 );
QCOMPARE( f.attribute( "majority" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "variety" ).toDouble(), 2.0 );

request.setFilterFid( 1 );
fetched = mVectorLayer->getFeatures( request ).nextFeature( f );
QVERIFY( fetched );
QCOMPARE( f.attribute( "count" ).toDouble(), 9.0 );
QCOMPARE( f.attribute( "sum" ).toDouble(), 5.0 );
QCOMPARE( f.attribute( "mean" ).toDouble(), 0.555555555555556 );
QCOMPARE( f.attribute( "median" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "stdev" ).toDouble(), 0.49690399499995302 );
QCOMPARE( f.attribute( "min" ).toDouble(), 0.0 );
QCOMPARE( f.attribute( "max" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "range" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "minority" ).toDouble(), 0.0 );
QCOMPARE( f.attribute( "majority" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "variety" ).toDouble(), 2.0 );

request.setFilterFid( 2 );
fetched = mVectorLayer->getFeatures( request ).nextFeature( f );
QVERIFY( fetched );
QCOMPARE( f.attribute( "count" ).toDouble(), 6.0 );
QCOMPARE( f.attribute( "sum" ).toDouble(), 5.0 );
QCOMPARE( f.attribute( "mean" ).toDouble(), 0.833333333333333 );
QCOMPARE( f.attribute( "median" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "stdev" ).toDouble(), 0.372677996249965 );
QCOMPARE( f.attribute( "min" ).toDouble(), 0.0 );
QCOMPARE( f.attribute( "max" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "range" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "minority" ).toDouble(), 0.0 );
QCOMPARE( f.attribute( "majority" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "variety" ).toDouble(), 2.0 );

// same with long prefix to ensure that field name truncation handled correctly
QgsZonalStatistics zsl( mVectorLayer, mRasterPath, QStringLiteral( "myqgis2_" ), 1 );
QgsZonalStatistics zsl( mVectorLayer, mRasterPath, QStringLiteral( "myqgis2_" ), 1, QgsZonalStatistics::All );
zsl.calculateStatistics( nullptr );

request.setFilterFid( 0 );
Expand All @@ -120,20 +144,44 @@ void TestQgsZonalStatistics::testStatistics()
QCOMPARE( f.attribute( "myqgis2_co" ).toDouble(), 12.0 );
QCOMPARE( f.attribute( "myqgis2_su" ).toDouble(), 8.0 );
QCOMPARE( f.attribute( "myqgis2_me" ).toDouble(), 0.666666666666667 );
QCOMPARE( f.attribute( "myqgis2__1" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "myqgis2_st" ).toDouble(), 0.47140452079103201 );
QCOMPARE( f.attribute( "myqgis2_mi" ).toDouble(), 0.0 );
QCOMPARE( f.attribute( "myqgis2_ma" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "myqgis2_ra" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "myqgis2__2" ).toDouble(), 0.0 );
QCOMPARE( f.attribute( "myqgis2__3" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "myqgis2_va" ).toDouble(), 2.0 );

request.setFilterFid( 1 );
fetched = mVectorLayer->getFeatures( request ).nextFeature( f );
QVERIFY( fetched );
QCOMPARE( f.attribute( "myqgis2_co" ).toDouble(), 9.0 );
QCOMPARE( f.attribute( "myqgis2_su" ).toDouble(), 5.0 );
QCOMPARE( f.attribute( "myqgis2_me" ).toDouble(), 0.555555555555556 );
QCOMPARE( f.attribute( "myqgis2__1" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "myqgis2_st" ).toDouble(), 0.49690399499995302 );
QCOMPARE( f.attribute( "myqgis2_mi" ).toDouble(), 0.0 );
QCOMPARE( f.attribute( "myqgis2_ma" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "myqgis2_ra" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "myqgis2__2" ).toDouble(), 0.0 );
QCOMPARE( f.attribute( "myqgis2__3" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "myqgis2_va" ).toDouble(), 2.0 );

request.setFilterFid( 2 );
fetched = mVectorLayer->getFeatures( request ).nextFeature( f );
QVERIFY( fetched );
QCOMPARE( f.attribute( "myqgis2_co" ).toDouble(), 6.0 );
QCOMPARE( f.attribute( "myqgis2_su" ).toDouble(), 5.0 );
QCOMPARE( f.attribute( "myqgis2_me" ).toDouble(), 0.833333333333333 );
QCOMPARE( f.attribute( "myqgis2__1" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "myqgis2_st" ).toDouble(), 0.372677996249965 );
QCOMPARE( f.attribute( "myqgis2_mi" ).toDouble(), 0.0 );
QCOMPARE( f.attribute( "myqgis2_ma" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "myqgis2_ra" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "myqgis2__2" ).toDouble(), 0.0 );
QCOMPARE( f.attribute( "myqgis2__3" ).toDouble(), 1.0 );
QCOMPARE( f.attribute( "myqgis2_va" ).toDouble(), 2.0 );
}

QGSTEST_MAIN( TestQgsZonalStatistics )
Expand Down
1 change: 0 additions & 1 deletion tests/src/python/CMakeLists.txt
Expand Up @@ -10,7 +10,6 @@ ENDIF (WITH_SERVER)

ADD_PYTHON_TEST(PyQgsActionManager test_qgsactionmanager.py)
ADD_PYTHON_TEST(PyQgsAggregateCalculator test_qgsaggregatecalculator.py)
ADD_PYTHON_TEST(PyQgsAnalysis test_qgsanalysis.py)
ADD_PYTHON_TEST(PyQgsApplication test_qgsapplication.py)
ADD_PYTHON_TEST(PyQgsAtlasComposition test_qgsatlascomposition.py)
ADD_PYTHON_TEST(PyQgsAttributeFormEditorWidget test_qgsattributeformeditorwidget.py)
Expand Down
48 changes: 0 additions & 48 deletions tests/src/python/test_qgsanalysis.py

This file was deleted.

50 changes: 49 additions & 1 deletion tests/src/python/test_qgszonalstatistics.py
Expand Up @@ -39,7 +39,7 @@ def testStatistics(self):

myVector = QgsVectorLayer(myTempPath + "polys.shp", "poly", "ogr")
myRasterPath = myTempPath + "edge_problem.asc"
zs = QgsZonalStatistics(myVector, myRasterPath, "", 1)
zs = QgsZonalStatistics(myVector, myRasterPath, "", 1, QgsZonalStatistics.All)
zs.calculateStatistics(None)

feat = QgsFeature()
Expand All @@ -52,6 +52,22 @@ def testStatistics(self):
assert feat[2] == 8.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (0.666666666666667, feat[3]))
assert abs(feat[3] - 0.666666666666667) < 0.00001, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[4]))
assert feat[4] == 1.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (0.47140452079103201, feat[5]))
assert abs(feat[5] - 0.47140452079103201) < 0.00001, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (0.0, feat[6]))
assert feat[6] == 0.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[7]))
assert feat[7] == 1.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[8]))
assert feat[8] == 1.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (0.0, feat[9]))
assert feat[9] == 0.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[10]))
assert feat[10] == 1.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (2.0, feat[11]))
assert feat[11] == 2.0, myMessage

request.setFilterFid(1)
feat = next(myVector.getFeatures(request))
Expand All @@ -61,6 +77,22 @@ def testStatistics(self):
assert feat[2] == 5.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (0.555555555555556, feat[3]))
assert abs(feat[3] - 0.555555555555556) < 0.00001, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[4]))
assert feat[4] == 1.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (0.49690399499995302, feat[5]))
assert abs(feat[5] - 0.49690399499995302) < 0.00001, myMessage

This comment has been minimized.

Copy link
@nyalldawson

nyalldawson Jan 29, 2017

Collaborator

@alexbruy FYI - self.assertAlmostEqual is a much nicer way to handle this

myMessage = ('Expected: %f\nGot: %f\n' % (0.0, feat[6]))
assert feat[6] == 0.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[7]))
assert feat[7] == 1.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[8]))
assert feat[8] == 1.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (0.0, feat[9]))
assert feat[9] == 0.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[10]))
assert feat[10] == 1.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (2.0, feat[11]))
assert feat[11] == 2.0, myMessage

request.setFilterFid(2)
feat = next(myVector.getFeatures(request))
Expand All @@ -70,6 +102,22 @@ def testStatistics(self):
assert feat[2] == 5.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (0.833333333333333, feat[3]))
assert abs(feat[3] - 0.833333333333333) < 0.00001, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[4]))
assert feat[4] == 1.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (0.372677996249965, feat[5]))
assert abs(feat[5] - 0.372677996249965) < 0.00001, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (0.0, feat[6]))
assert feat[6] == 0.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[7]))
assert feat[7] == 1.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[8]))
assert feat[8] == 1.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (0.0, feat[9]))
assert feat[9] == 0.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[10]))
assert feat[10] == 1.0, myMessage
myMessage = ('Expected: %f\nGot: %f\n' % (2.0, feat[11]))
assert feat[11] == 2.0, myMessage

if __name__ == '__main__':
unittest.main()

0 comments on commit eca795f

Please sign in to comment.