mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
[FEATURE] raster_statistic expression function for retrieving
raster band stats from a loaded layer Allows raster band stats (eg min, max, avg) to be used in expressions
This commit is contained in:
parent
86ab3022b5
commit
20dc7fb266
14
resources/function_help/json/raster_statistic
Normal file
14
resources/function_help/json/raster_statistic
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "raster_statistic",
|
||||
"type": "function",
|
||||
"description": "Returns statistics from a raster layer.",
|
||||
"arguments": [
|
||||
{"arg":"layer", "description":"a string, representing either a raster layer name or layer ID"},
|
||||
{"arg":"band", "description":"integer representing the band number from the raster layer, starting at 1"},
|
||||
{"arg":"property", "description":"a string corresponding to the property to return. Valid options are:<br /><ul><li>min: minimum value</li><li>max: maximum value</li><li>avg: average (mean) value</li><li>stdev: standard deviation of values</li><li>range: range of values (max - min)</li><li>sum: sum of all values from raster</li></ul>"}
|
||||
],
|
||||
"examples": [
|
||||
{ "expression":"raster_statistic('lc',1,'avg')", "returns":"Average value from band 1 from 'lc' raster layer"},
|
||||
{ "expression":"raster_statistic('ac2010',3,'min')", "returns":"Minimum value from band 3 from 'ac2010' raster layer"}
|
||||
]
|
||||
}
|
@ -54,6 +54,8 @@
|
||||
#include "qgsmaptopixelgeometrysimplifier.h"
|
||||
#include "qgsmessagelog.h"
|
||||
#include "qgscsexception.h"
|
||||
#include "qgsrasterlayer.h"
|
||||
#include "qgsrasterdataprovider.h"
|
||||
|
||||
// from parser
|
||||
extern QgsExpression::Node* parseExpression( const QString& str, QString& parserErrorMsg );
|
||||
@ -3423,6 +3425,75 @@ static QVariant fcnGetLayerProperty( const QVariantList& values, const QgsExpres
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
static QVariant fcnGetRasterBandStat( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
|
||||
{
|
||||
QString layerIdOrName = getStringValue( values.at( 0 ), parent );
|
||||
|
||||
//try to find a matching layer by name
|
||||
QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerIdOrName ); //search by id first
|
||||
if ( !layer )
|
||||
{
|
||||
QList<QgsMapLayer *> layersByName = QgsMapLayerRegistry::instance()->mapLayersByName( layerIdOrName );
|
||||
if ( !layersByName.isEmpty() )
|
||||
{
|
||||
layer = layersByName.at( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !layer )
|
||||
return QVariant();
|
||||
|
||||
QgsRasterLayer* rl = qobject_cast< QgsRasterLayer* >( layer );
|
||||
if ( !rl )
|
||||
return QVariant();
|
||||
|
||||
int band = getIntValue( values.at( 1 ), parent );
|
||||
if ( band < 1 || band > rl->bandCount() )
|
||||
{
|
||||
parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer %2" ).arg( band ).arg( layerIdOrName ) );
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QString layerProperty = getStringValue( values.at( 2 ), parent );
|
||||
int stat = 0;
|
||||
|
||||
if ( QString::compare( layerProperty, QStringLiteral( "avg" ), Qt::CaseInsensitive ) == 0 )
|
||||
stat = QgsRasterBandStats::Mean;
|
||||
else if ( QString::compare( layerProperty, QStringLiteral( "stdev" ), Qt::CaseInsensitive ) == 0 )
|
||||
stat = QgsRasterBandStats::StdDev;
|
||||
else if ( QString::compare( layerProperty, QStringLiteral( "min" ), Qt::CaseInsensitive ) == 0 )
|
||||
stat = QgsRasterBandStats::Min;
|
||||
else if ( QString::compare( layerProperty, QStringLiteral( "max" ), Qt::CaseInsensitive ) == 0 )
|
||||
stat = QgsRasterBandStats::Max;
|
||||
else if ( QString::compare( layerProperty, QStringLiteral( "range" ), Qt::CaseInsensitive ) == 0 )
|
||||
stat = QgsRasterBandStats::Range;
|
||||
else if ( QString::compare( layerProperty, QStringLiteral( "sum" ), Qt::CaseInsensitive ) == 0 )
|
||||
stat = QgsRasterBandStats::Sum;
|
||||
else
|
||||
{
|
||||
parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
|
||||
switch ( stat )
|
||||
{
|
||||
case QgsRasterBandStats::Mean:
|
||||
return stats.mean;
|
||||
case QgsRasterBandStats::StdDev:
|
||||
return stats.stdDev;
|
||||
case QgsRasterBandStats::Min:
|
||||
return stats.minimumValue;
|
||||
case QgsRasterBandStats::Max:
|
||||
return stats.maximumValue;
|
||||
case QgsRasterBandStats::Range:
|
||||
return stats.range;
|
||||
case QgsRasterBandStats::Sum:
|
||||
return stats.sum;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
static QVariant fcnArray( const QVariantList& values, const QgsExpressionContext*, QgsExpression* )
|
||||
{
|
||||
return values;
|
||||
@ -4007,6 +4078,9 @@ const QList<QgsExpression::Function*>& QgsExpression::Functions()
|
||||
// **General** functions
|
||||
|
||||
<< new StaticFunction( QStringLiteral( "layer_property" ), 2, fcnGetLayerProperty, QStringLiteral( "General" ) )
|
||||
<< new StaticFunction( QStringLiteral( "raster_statistic" ), ParameterList() << Parameter( QStringLiteral( "layer" ) )
|
||||
<< Parameter( QStringLiteral( "band" ) )
|
||||
<< Parameter( QStringLiteral( "statistic" ) ), fcnGetRasterBandStat, QStringLiteral( "General" ) )
|
||||
<< new StaticFunction( QStringLiteral( "var" ), 1, fcnGetVariable, QStringLiteral( "General" ) )
|
||||
|
||||
//return all attributes string for referencedColumns - this is caught by
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "qgsmaplayerregistry.h"
|
||||
#include "qgsvectordataprovider.h"
|
||||
#include "qgsdistancearea.h"
|
||||
#include "qgsrasterlayer.h"
|
||||
#include "qgsproject.h"
|
||||
|
||||
static void _parseAndEvalExpr( int arg )
|
||||
@ -55,6 +56,7 @@ class TestQgsExpression: public QObject
|
||||
, mMemoryLayer( nullptr )
|
||||
, mAggregatesLayer( nullptr )
|
||||
, mChildLayer( nullptr )
|
||||
, mRasterLayer( nullptr )
|
||||
{}
|
||||
|
||||
private:
|
||||
@ -63,6 +65,7 @@ class TestQgsExpression: public QObject
|
||||
QgsVectorLayer* mMemoryLayer;
|
||||
QgsVectorLayer* mAggregatesLayer;
|
||||
QgsVectorLayer* mChildLayer;
|
||||
QgsRasterLayer* mRasterLayer;
|
||||
|
||||
private slots:
|
||||
|
||||
@ -94,6 +97,12 @@ class TestQgsExpression: public QObject
|
||||
mPointsLayer->setMaximumScale( 500 );
|
||||
mPointsLayer->setMinimumScale( 1000 );
|
||||
|
||||
QString rasterFileName = testDataDir + "tenbytenraster.asc";
|
||||
QFileInfo rasterFileInfo( rasterFileName );
|
||||
mRasterLayer = new QgsRasterLayer( rasterFileInfo.filePath(),
|
||||
rasterFileInfo.completeBaseName() );
|
||||
QgsMapLayerRegistry::instance()->addMapLayer( mRasterLayer );
|
||||
|
||||
// test memory layer for get_feature tests
|
||||
mMemoryLayer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:integer&field=col2:string" ), QStringLiteral( "test" ), QStringLiteral( "memory" ) );
|
||||
QVERIFY( mMemoryLayer->isValid() );
|
||||
@ -1039,6 +1048,21 @@ class TestQgsExpression: public QObject
|
||||
QTest::newRow( "layer_property storage_type" ) << QStringLiteral( "layer_property('%1','storage_type')" ).arg( mPointsLayer->name() ) << false << QVariant( "ESRI Shapefile" );
|
||||
QTest::newRow( "layer_property geometry_type" ) << QStringLiteral( "layer_property('%1','geometry_type')" ).arg( mPointsLayer->name() ) << false << QVariant( "Point" );
|
||||
|
||||
// raster_statistic tests
|
||||
QTest::newRow( "raster_statistic no layer" ) << "raster_statistic('',1,'min')" << false << QVariant();
|
||||
QTest::newRow( "raster_statistic bad layer" ) << "raster_statistic('bad',1,'min')" << false << QVariant();
|
||||
QTest::newRow( "raster_statistic bad band" ) << QStringLiteral( "raster_statistic('%1',0,'min')" ).arg( mRasterLayer->name() ) << true << QVariant();
|
||||
QTest::newRow( "raster_statistic bad band 2" ) << QStringLiteral( "raster_statistic('%1',100,'min')" ).arg( mRasterLayer->name() ) << true << QVariant();
|
||||
QTest::newRow( "raster_statistic no property" ) << QStringLiteral( "raster_statistic('%1',1,'')" ).arg( mRasterLayer->name() ) << true << QVariant();
|
||||
QTest::newRow( "raster_statistic bad property" ) << QStringLiteral( "raster_statistic('%1',1,'bad')" ).arg( mRasterLayer->name() ) << true << QVariant();
|
||||
QTest::newRow( "raster_statistic min by id" ) << QStringLiteral( "raster_statistic('%1',1,'min')" ).arg( mRasterLayer->id() ) << false << QVariant( 0.0 );
|
||||
QTest::newRow( "raster_statistic min name" ) << QStringLiteral( "raster_statistic('%1',1,'min')" ).arg( mRasterLayer->name() ) << false << QVariant( 0.0 );
|
||||
QTest::newRow( "raster_statistic max" ) << QStringLiteral( "raster_statistic('%1',1,'max')" ).arg( mRasterLayer->id() ) << false << QVariant( 9.0 );
|
||||
QTest::newRow( "raster_statistic avg" ) << QStringLiteral( "round(10*raster_statistic('%1',1,'avg'))" ).arg( mRasterLayer->id() ) << false << QVariant( 45 );
|
||||
QTest::newRow( "raster_statistic stdev" ) << QStringLiteral( "round(100*raster_statistic('%1',1,'stdev'))" ).arg( mRasterLayer->id() ) << false << QVariant( 287 );
|
||||
QTest::newRow( "raster_statistic range" ) << QStringLiteral( "raster_statistic('%1',1,'range')" ).arg( mRasterLayer->id() ) << false << QVariant( 9.0 );
|
||||
QTest::newRow( "raster_statistic sum" ) << QStringLiteral( "round(raster_statistic('%1',1,'sum'))" ).arg( mRasterLayer->id() ) << false << QVariant( 450 );
|
||||
|
||||
//test conversions to bool
|
||||
QTest::newRow( "feature to bool false" ) << QStringLiteral( "case when get_feature('none','none',499) then true else false end" ) << false << QVariant( false );
|
||||
QTest::newRow( "feature to bool true" ) << QStringLiteral( "case when get_feature('test','col1',10) then true else false end" ) << false << QVariant( true );
|
||||
|
Loading…
x
Reference in New Issue
Block a user