Skip to content

Commit

Permalink
[FEATURE][expression] add raster_value() function (#7487)
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvn committed Jul 27, 2018
1 parent 2692de6 commit 2d96491
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 1 deletion.
10 changes: 10 additions & 0 deletions resources/function_help/json/raster_value
@@ -0,0 +1,10 @@
{
"name": "raster_value",
"type": "function",
"description": "Returns the raster value found at the provided point.",
"arguments": [ {"arg":"layer","description":"the name or id of a raster layer"},
{"arg":"band","description":"the band number to sample the value from."},
{"arg":"point","description":"point geometry (for multipart geometries having more than one part, a null value will be returned)"}],
"examples": [ { "expression":"raster_value('dem', 1, make_point(1,1))", "returns":"25"}]
}

45 changes: 44 additions & 1 deletion src/core/expression/qgsexpressionfunction.cpp
Expand Up @@ -1283,6 +1283,48 @@ static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *
return QVariant( static_cast< int >( f.id() ) );
}

static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QgsRasterLayer *layer = QgsExpressionUtils::getRasterLayer( values.at( 0 ), parent );
if ( !layer || !layer->dataProvider() )
{
parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
return QVariant();
}

int bandNb = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
if ( bandNb < 1 || bandNb > layer->bandCount() )
{
parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
return QVariant();
}

QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
if ( geom.isNull() || geom.type() != QgsWkbTypes::PointGeometry )
{
parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
return QVariant();
}

QgsPointXY point = geom.asPoint();
if ( geom.isMultipart() )
{
QgsMultiPointXY multiPoint = geom.asMultiPoint();
if ( multiPoint.count() == 1 )
{
point = multiPoint[0];
}
else
{
// if the geometry contains more than one part, return an undefined value
return QVariant();
}
}

double value = layer->dataProvider()->sample( point, bandNb );
return std::isnan( value ) ? QVariant() : value;
}

static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
{
if ( !context )
Expand Down Expand Up @@ -4559,7 +4601,7 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< new QgsStaticExpressionFunction( QStringLiteral( "layer_property" ), 2, fcnGetLayerProperty, QStringLiteral( "General" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "raster_statistic" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "band" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "statistic" ) ), fcnGetRasterBandStat, QStringLiteral( "General" ) );
<< QgsExpressionFunction::Parameter( QStringLiteral( "statistic" ) ), fcnGetRasterBandStat, QStringLiteral( "Rasters" ) );

// **var** function
QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( QStringLiteral( "var" ), 1, fcnGetVariable, QStringLiteral( "General" ) );
Expand Down Expand Up @@ -4617,6 +4659,7 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< new QgsStaticExpressionFunction( QStringLiteral( "env" ), 1, fcnEnvVar, QStringLiteral( "General" ), QString() )
<< new QgsWithVariableExpressionFunction()
<< new QgsStaticExpressionFunction( QStringLiteral( "attribute" ), 2, fcnAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES )
<< new QgsStaticExpressionFunction( QStringLiteral( "raster_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterValue, QStringLiteral( "Rasters" ) )

// functions for arrays
<< new QgsStaticExpressionFunction( QStringLiteral( "array" ), -1, fcnArray, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
Expand Down
5 changes: 5 additions & 0 deletions src/core/expression/qgsexpressionutils.h
Expand Up @@ -23,6 +23,7 @@
#include "qgsexpression.h"
#include "qgscolorramp.h"
#include "qgsvectorlayer.h"
#include "qgsrasterlayer.h"
#include "qgsproject.h"
#include "qgsrelationmanager.h"

Expand Down Expand Up @@ -355,6 +356,10 @@ class QgsExpressionUtils
return qobject_cast<QgsVectorLayer *>( getMapLayer( value, e ) );
}

static QgsRasterLayer *getRasterLayer( const QVariant &value, QgsExpression *e )
{
return qobject_cast<QgsRasterLayer *>( getMapLayer( value, e ) );
}

static QVariantList getListValue( const QVariant &value, QgsExpression *parent )
{
Expand Down
9 changes: 9 additions & 0 deletions tests/src/core/testqgsexpression.cpp
Expand Up @@ -1246,6 +1246,15 @@ class TestQgsExpression: public QObject
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 );

// raster_value tests
QTest::newRow( "raster_value no layer" ) << "raster_value('',1,make_point(1,1))" << true << QVariant();
QTest::newRow( "raster_value bad layer" ) << "raster_value('bad',1,make_point(1,1))" << true << QVariant();
QTest::newRow( "raster_value bad band" ) << QStringLiteral( "raster_value('%1',0,make_point(1,1))" ).arg( mRasterLayer->name() ) << true << QVariant();
QTest::newRow( "raster_value bad band 2" ) << QStringLiteral( "raster_value('%1',100,make_point(1,1))" ).arg( mRasterLayer->name() ) << true << QVariant();
QTest::newRow( "raster_value invalid geometry" ) << QStringLiteral( "raster_value('%1',1,'invalid geom')" ).arg( mRasterLayer->name() ) << true << QVariant();
QTest::newRow( "raster_value valid" ) << QStringLiteral( "raster_value('%1',1,make_point(1535390,5083270))" ).arg( mRasterLayer->name() ) << false << QVariant( 1.0 );
QTest::newRow( "raster_value outside extent" ) << QStringLiteral( "raster_value('%1',1,make_point(1535370,5083250))" ).arg( mRasterLayer->name() ) << false << QVariant();

//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 );
Expand Down

0 comments on commit 2d96491

Please sign in to comment.