diff --git a/python/core/expression/qgsexpressionfunction.sip b/python/core/expression/qgsexpressionfunction.sip index 01fc3dddfdb..f7c429e7a7a 100644 --- a/python/core/expression/qgsexpressionfunction.sip +++ b/python/core/expression/qgsexpressionfunction.sip @@ -9,6 +9,7 @@ + class QgsExpressionFunction { %Docstring diff --git a/python/core/expression/qgsexpressionnode.sip b/python/core/expression/qgsexpressionnode.sip index bacfe2f9d59..bee452b0286 100644 --- a/python/core/expression/qgsexpressionnode.sip +++ b/python/core/expression/qgsexpressionnode.sip @@ -9,6 +9,8 @@ + + class QgsExpressionNode /Abstract/ { %Docstring diff --git a/python/core/expression/qgsexpressionnodeimpl.sip b/python/core/expression/qgsexpressionnodeimpl.sip index 3bbf03d53df..bd4e774c1b1 100644 --- a/python/core/expression/qgsexpressionnodeimpl.sip +++ b/python/core/expression/qgsexpressionnodeimpl.sip @@ -7,6 +7,8 @@ ************************************************************************/ + + class QgsExpressionNodeUnaryOperator : QgsExpressionNode { %Docstring diff --git a/src/core/expression/qgsexpression.cpp b/src/core/expression/qgsexpression.cpp index 92f04acc1bf..890e2e51f12 100644 --- a/src/core/expression/qgsexpression.cpp +++ b/src/core/expression/qgsexpression.cpp @@ -14,3879 +14,30 @@ ***************************************************************************/ #include "qgsexpression.h" -#include "qgsrelationmanager.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "qgsdistancearea.h" -#include "qgsfeature.h" -#include "qgsfeatureiterator.h" -#include "qgsgeometry.h" -#include "qgsgeometryengine.h" -#include "qgsgeometryutils.h" -#include "qgslogger.h" -#include "qgsogcutils.h" -#include "qgsvectorlayer.h" -#include "qgssymbollayerutils.h" -#include "qgscolorramp.h" -#include "qgsstyle.h" -#include "qgsexpressioncontext.h" -#include "qgsproject.h" -#include "qgsstringutils.h" -#include "qgsgeometrycollection.h" -#include "qgspointv2.h" -#include "qgspolygon.h" -#include "qgstriangle.h" -#include "qgscircle.h" -#include "qgsellipse.h" -#include "qgsregularpolygon.h" -#include "qgsmultipoint.h" -#include "qgsmultilinestring.h" -#include "qgscurvepolygon.h" -#include "qgsexpressionprivate.h" -#include "qgsexpressionsorter.h" -#include "qgsmaptopixelgeometrysimplifier.h" -#include "qgsmessagelog.h" -#include "qgscsexception.h" -#include "qgsrasterlayer.h" -#include "qgsrasterdataprovider.h" -#include "qgsexpressionnode.h" -#include "qgsexpressionnodeimpl.h" #include "qgsexpressionfunction.h" +#include "qgsexpressionprivate.h" +#include "qgsexpressionnodeimpl.h" +#include "qgsfeaturerequest.h" +#include "qgslogger.h" +#include "qgsexpressioncontext.h" +#include "qgsgeometry.h" +#include "qgsproject.h" // from parser extern QgsExpressionNode *parseExpression( const QString &str, QString &parserErrorMsg ); -/////////////////////////////////////////////// -// three-value logic - -enum TVL -{ - False, - True, - Unknown -}; - -static TVL AND[3][3] = -{ - // false true unknown - { False, False, False }, // false - { False, True, Unknown }, // true - { False, Unknown, Unknown } // unknown -}; - -static TVL OR[3][3] = -{ - { False, True, Unknown }, // false - { True, True, True }, // true - { Unknown, True, Unknown } // unknown -}; - -static TVL NOT[3] = { True, False, Unknown }; - -static QVariant tvl2variant( TVL v ) -{ - switch ( v ) - { - case False: - return 0; - case True: - return 1; - case Unknown: - default: - return QVariant(); - } -} - -#define TVL_True QVariant(1) -#define TVL_False QVariant(0) -#define TVL_Unknown QVariant() - /////////////////////////////////////////////// // QVariant checks and conversions -inline bool isIntSafe( const QVariant &v ) -{ - if ( v.type() == QVariant::Int ) - return true; - if ( v.type() == QVariant::UInt ) - return true; - if ( v.type() == QVariant::LongLong ) - return true; - if ( v.type() == QVariant::ULongLong ) - return true; - if ( v.type() == QVariant::Double ) - return false; - if ( v.type() == QVariant::String ) - { - bool ok; - v.toString().toInt( &ok ); - return ok; - } - return false; -} -inline bool isDoubleSafe( const QVariant &v ) -{ - if ( v.type() == QVariant::Double ) - return true; - if ( v.type() == QVariant::Int ) - return true; - if ( v.type() == QVariant::UInt ) - return true; - if ( v.type() == QVariant::LongLong ) - return true; - if ( v.type() == QVariant::ULongLong ) - return true; - if ( v.type() == QVariant::String ) - { - bool ok; - double val = v.toString().toDouble( &ok ); - ok = ok && qIsFinite( val ) && !qIsNaN( val ); - return ok; - } - return false; -} - -inline bool isDateTimeSafe( const QVariant &v ) -{ - return v.type() == QVariant::DateTime - || v.type() == QVariant::Date - || v.type() == QVariant::Time; -} - -inline bool isIntervalSafe( const QVariant &v ) -{ - if ( v.canConvert() ) - { - return true; - } - - if ( v.type() == QVariant::String ) - { - return QgsInterval::fromString( v.toString() ).isValid(); - } - return false; -} - -inline bool isNull( const QVariant &v ) -{ - return v.isNull(); -} - -inline bool isList( const QVariant &v ) -{ - return v.type() == QVariant::List; -} - /////////////////////////////////////////////// // evaluation error macros -#define ENSURE_NO_EVAL_ERROR { if (parent->hasEvalError()) return QVariant(); } -#define SET_EVAL_ERROR(x) { parent->setEvalErrorString(x); return QVariant(); } - -/////////////////////////////////////////////// -// operators - -const char *QgsExpressionNodeBinaryOperator::BINARY_OPERATOR_TEXT[] = -{ - // this must correspond (number and order of element) to the declaration of the enum BinaryOperator - "OR", "AND", - "=", "<>", "<=", ">=", "<", ">", "~", "LIKE", "NOT LIKE", "ILIKE", "NOT ILIKE", "IS", "IS NOT", - "+", "-", "*", "/", "//", "%", "^", - "||" -}; - -const char *QgsExpressionNodeUnaryOperator::UNARY_OPERATOR_TEXT[] = -{ - // this must correspond (number and order of element) to the declaration of the enum UnaryOperator - "NOT", "-" -}; - /////////////////////////////////////////////// // functions -// implicit conversion to string -static QString getStringValue( const QVariant &value, QgsExpression * ) -{ - return value.toString(); -} - -static double getDoubleValue( const QVariant &value, QgsExpression *parent ) -{ - bool ok; - double x = value.toDouble( &ok ); - if ( !ok || qIsNaN( x ) || !qIsFinite( x ) ) - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to double" ).arg( value.toString() ) ); - return 0; - } - return x; -} - -static qlonglong getIntValue( const QVariant &value, QgsExpression *parent ) -{ - bool ok; - qlonglong x = value.toLongLong( &ok ); - if ( ok ) - { - return x; - } - else - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to int" ).arg( value.toString() ) ); - return 0; - } -} - -static int getNativeIntValue( const QVariant &value, QgsExpression *parent ) -{ - bool ok; - qlonglong x = value.toLongLong( &ok ); - if ( ok && x >= std::numeric_limits::min() && x <= std::numeric_limits::max() ) - { - return x; - } - else - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to native int" ).arg( value.toString() ) ); - return 0; - } -} - -static QDateTime getDateTimeValue( const QVariant &value, QgsExpression *parent ) -{ - QDateTime d = value.toDateTime(); - if ( d.isValid() ) - { - return d; - } - else - { - QTime t = value.toTime(); - if ( t.isValid() ) - { - return QDateTime( QDate( 1, 1, 1 ), t ); - } - - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( value.toString() ) ); - return QDateTime(); - } -} - -static QDate getDateValue( const QVariant &value, QgsExpression *parent ) -{ - QDate d = value.toDate(); - if ( d.isValid() ) - { - return d; - } - else - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( value.toString() ) ); - return QDate(); - } -} - -static QTime getTimeValue( const QVariant &value, QgsExpression *parent ) -{ - QTime t = value.toTime(); - if ( t.isValid() ) - { - return t; - } - else - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( value.toString() ) ); - return QTime(); - } -} - -static QgsInterval getInterval( const QVariant &value, QgsExpression *parent, bool report_error = false ) -{ - if ( value.canConvert() ) - return value.value(); - - QgsInterval inter = QgsInterval::fromString( value.toString() ); - if ( inter.isValid() ) - { - return inter; - } - // If we get here then we can't convert so we just error and return invalid. - if ( report_error ) - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Interval" ).arg( value.toString() ) ); - - return QgsInterval(); -} - -static QgsGeometry getGeometry( const QVariant &value, QgsExpression *parent ) -{ - if ( value.canConvert() ) - return value.value(); - - parent->setEvalErrorString( QStringLiteral( "Cannot convert to QgsGeometry" ) ); - return QgsGeometry(); -} - -static QgsFeature getFeature( const QVariant &value, QgsExpression *parent ) -{ - if ( value.canConvert() ) - return value.value(); - - parent->setEvalErrorString( QStringLiteral( "Cannot convert to QgsFeature" ) ); - return 0; -} - -#define FEAT_FROM_CONTEXT(c, f) if (!(c) || !(c)->hasFeature() ) return QVariant(); \ - QgsFeature f = ( c )->feature(); - -static QgsExpressionNode *getNode( const QVariant &value, QgsExpression *parent ) -{ - if ( value.canConvert() ) - return value.value(); - - parent->setEvalErrorString( QStringLiteral( "Cannot convert to Node" ) ); - return nullptr; -} - -QgsVectorLayer *getVectorLayer( const QVariant &value, QgsExpression * ) -{ - QgsMapLayer *ml = value.value< QgsWeakMapLayerPointer >().data(); - QgsVectorLayer *vl = qobject_cast( ml ); - if ( !vl ) - { - QString layerString = value.toString(); - vl = qobject_cast( QgsProject::instance()->mapLayer( layerString ) ); //search by id first - - if ( !vl ) - { - QList layersByName = QgsProject::instance()->mapLayersByName( layerString ); - if ( !layersByName.isEmpty() ) - { - vl = qobject_cast( layersByName.at( 0 ) ); - } - } - } - - return vl; -} - - -// this handles also NULL values -static TVL getTVLValue( const QVariant &value, QgsExpression *parent ) -{ - // we need to convert to TVL - if ( value.isNull() ) - return Unknown; - - //handle some special cases - if ( value.canConvert() ) - { - //geom is false if empty - QgsGeometry geom = value.value(); - return geom.isNull() ? False : True; - } - else if ( value.canConvert() ) - { - //feat is false if non-valid - QgsFeature feat = value.value(); - return feat.isValid() ? True : False; - } - - if ( value.type() == QVariant::Int ) - return value.toInt() != 0 ? True : False; - - bool ok; - double x = value.toDouble( &ok ); - if ( !ok ) - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to boolean" ).arg( value.toString() ) ); - return Unknown; - } - return !qgsDoubleNear( x, 0.0 ) ? True : False; -} - -static QVariantList getListValue( const QVariant &value, QgsExpression *parent ) -{ - if ( value.type() == QVariant::List || value.type() == QVariant::StringList ) - { - return value.toList(); - } - else - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to array" ).arg( value.toString() ) ); - return QVariantList(); - } -} - -static QVariantMap getMapValue( const QVariant &value, QgsExpression *parent ) -{ - if ( value.type() == QVariant::Map ) - { - return value.toMap(); - } - else - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to map" ).arg( value.toString() ) ); - return QVariantMap(); - } -} - ////// -static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - if ( !context ) - return QVariant(); - - QString name = getStringValue( values.at( 0 ), parent ); - return context->variable( name ); -} - -static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - if ( !context ) - return QVariant(); - - QString expString = getStringValue( values.at( 0 ), parent ); - QgsExpression expression( expString ); - return expression.evaluate( context ); -} - -static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double x = getDoubleValue( values.at( 0 ), parent ); - return QVariant( sqrt( x ) ); -} - -static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double val = getDoubleValue( values.at( 0 ), parent ); - return QVariant( fabs( val ) ); -} - -static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double deg = getDoubleValue( values.at( 0 ), parent ); - return ( deg * M_PI ) / 180; -} -static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double rad = getDoubleValue( values.at( 0 ), parent ); - return ( 180 * rad ) / M_PI; -} -static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double x = getDoubleValue( values.at( 0 ), parent ); - return QVariant( sin( x ) ); -} -static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double x = getDoubleValue( values.at( 0 ), parent ); - return QVariant( cos( x ) ); -} -static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double x = getDoubleValue( values.at( 0 ), parent ); - return QVariant( tan( x ) ); -} -static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double x = getDoubleValue( values.at( 0 ), parent ); - return QVariant( asin( x ) ); -} -static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double x = getDoubleValue( values.at( 0 ), parent ); - return QVariant( acos( x ) ); -} -static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double x = getDoubleValue( values.at( 0 ), parent ); - return QVariant( atan( x ) ); -} -static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double y = getDoubleValue( values.at( 0 ), parent ); - double x = getDoubleValue( values.at( 1 ), parent ); - return QVariant( atan2( y, x ) ); -} -static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double x = getDoubleValue( values.at( 0 ), parent ); - return QVariant( exp( x ) ); -} -static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double x = getDoubleValue( values.at( 0 ), parent ); - if ( x <= 0 ) - return QVariant(); - return QVariant( log( x ) ); -} -static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double x = getDoubleValue( values.at( 0 ), parent ); - if ( x <= 0 ) - return QVariant(); - return QVariant( log10( x ) ); -} -static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double b = getDoubleValue( values.at( 0 ), parent ); - double x = getDoubleValue( values.at( 1 ), parent ); - if ( x <= 0 || b <= 0 ) - return QVariant(); - return QVariant( log( x ) / log( b ) ); -} -static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double min = getDoubleValue( values.at( 0 ), parent ); - double max = getDoubleValue( values.at( 1 ), parent ); - if ( max < min ) - return QVariant(); - - // Return a random double in the range [min, max] (inclusive) - double f = static_cast< double >( qrand() ) / RAND_MAX; - return QVariant( min + f * ( max - min ) ); -} -static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - qlonglong min = getIntValue( values.at( 0 ), parent ); - qlonglong max = getIntValue( values.at( 1 ), parent ); - if ( max < min ) - return QVariant(); - - // Return a random integer in the range [min, max] (inclusive) - return QVariant( min + ( qrand() % static_cast< qlonglong >( max - min + 1 ) ) ); -} - -static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double val = getDoubleValue( values.at( 0 ), parent ); - double domainMin = getDoubleValue( values.at( 1 ), parent ); - double domainMax = getDoubleValue( values.at( 2 ), parent ); - double rangeMin = getDoubleValue( values.at( 3 ), parent ); - double rangeMax = getDoubleValue( values.at( 4 ), parent ); - - if ( domainMin >= domainMax ) - { - parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) ); - return QVariant(); - } - - // outside of domain? - if ( val >= domainMax ) - { - return rangeMax; - } - else if ( val <= domainMin ) - { - return rangeMin; - } - - // calculate linear scale - double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin ); - double c = rangeMin - ( domainMin * m ); - - // Return linearly scaled value - return QVariant( m * val + c ); -} - -static QVariant fcnExpScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double val = getDoubleValue( values.at( 0 ), parent ); - double domainMin = getDoubleValue( values.at( 1 ), parent ); - double domainMax = getDoubleValue( values.at( 2 ), parent ); - double rangeMin = getDoubleValue( values.at( 3 ), parent ); - double rangeMax = getDoubleValue( values.at( 4 ), parent ); - double exponent = getDoubleValue( values.at( 5 ), parent ); - - if ( domainMin >= domainMax ) - { - parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) ); - return QVariant(); - } - if ( exponent <= 0 ) - { - parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) ); - return QVariant(); - } - - // outside of domain? - if ( val >= domainMax ) - { - return rangeMax; - } - else if ( val <= domainMin ) - { - return rangeMin; - } - - // Return exponentially scaled value - return QVariant( ( ( rangeMax - rangeMin ) / pow( domainMax - domainMin, exponent ) ) * pow( val - domainMin, exponent ) + rangeMin ); -} - -static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - //initially set max as first value - double maxVal = getDoubleValue( values.at( 0 ), parent ); - - //check against all other values - for ( int i = 1; i < values.length(); ++i ) - { - double testVal = getDoubleValue( values[i], parent ); - if ( testVal > maxVal ) - { - maxVal = testVal; - } - } - - return QVariant( maxVal ); -} - -static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - //initially set min as first value - double minVal = getDoubleValue( values.at( 0 ), parent ); - - //check against all other values - for ( int i = 1; i < values.length(); ++i ) - { - double testVal = getDoubleValue( values[i], parent ); - if ( testVal < minVal ) - { - minVal = testVal; - } - } - - return QVariant( minVal ); -} - -static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - //lazy eval, so we need to evaluate nodes now - - //first node is layer id or name - QgsExpressionNode *node = getNode( values.at( 0 ), parent ); - ENSURE_NO_EVAL_ERROR; - QVariant value = node->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - QgsVectorLayer *vl = getVectorLayer( value, parent ); - if ( !vl ) - { - parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) ); - return QVariant(); - } - - // second node is aggregate type - node = getNode( values.at( 1 ), parent ); - ENSURE_NO_EVAL_ERROR; - value = node->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - bool ok = false; - QgsAggregateCalculator::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( getStringValue( value, parent ), &ok ); - if ( !ok ) - { - parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) ); - return QVariant(); - } - - // third node is subexpression (or field name) - node = getNode( values.at( 2 ), parent ); - ENSURE_NO_EVAL_ERROR; - QString subExpression = node->dump(); - - QgsAggregateCalculator::AggregateParameters parameters; - //optional forth node is filter - if ( values.count() > 3 ) - { - node = getNode( values.at( 3 ), parent ); - ENSURE_NO_EVAL_ERROR; - QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node ); - if ( !nl || nl->value().isValid() ) - parameters.filter = node->dump(); - } - - //optional fifth node is concatenator - if ( values.count() > 4 ) - { - node = getNode( values.at( 4 ), parent ); - ENSURE_NO_EVAL_ERROR; - value = node->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - parameters.delimiter = value.toString(); - } - - QVariant result; - if ( context ) - { - QString cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter ); - - QgsExpression subExp( subExpression ); - QgsExpression filterExp( parameters.filter ); - if ( filterExp.referencedVariables().contains( "parent" ) - || filterExp.referencedVariables().contains( QString() ) - || subExp.referencedVariables().contains( "parent" ) - || subExp.referencedVariables().contains( QString() ) ) - { - cacheKey += ':' + qHash( context->feature() ); - } - - if ( context && context->hasCachedValue( cacheKey ) ) - return context->cachedValue( cacheKey ); - - QgsExpressionContext subContext( *context ); - QgsExpressionContextScope *subScope = new QgsExpressionContextScope(); - subScope->setVariable( "parent", context->feature() ); - subContext.appendScope( subScope ); - result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok ); - - context->setCachedValue( cacheKey, result ); - } - else - { - result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok ); - } - if ( !ok ) - { - parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) ); - return QVariant(); - } - - return result; -} - -static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - if ( !context ) - { - parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) ); - return QVariant(); - } - - // first step - find current layer - QgsVectorLayer *vl = getVectorLayer( context->variable( "layer" ), parent ); - if ( !vl ) - { - parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) ); - return QVariant(); - } - - //lazy eval, so we need to evaluate nodes now - - //first node is relation name - QgsExpressionNode *node = getNode( values.at( 0 ), parent ); - ENSURE_NO_EVAL_ERROR; - QVariant value = node->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - QString relationId = value.toString(); - // check relation exists - QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId ); - if ( !relation.isValid() || relation.referencedLayer() != vl ) - { - // check for relations by name - QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId ); - if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl ) - { - parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) ); - return QVariant(); - } - else - { - relation = relations.at( 0 ); - } - } - - QgsVectorLayer *childLayer = relation.referencingLayer(); - - // second node is aggregate type - node = getNode( values.at( 1 ), parent ); - ENSURE_NO_EVAL_ERROR; - value = node->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - bool ok = false; - QgsAggregateCalculator::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( getStringValue( value, parent ), &ok ); - if ( !ok ) - { - parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) ); - return QVariant(); - } - - //third node is subexpression (or field name) - node = getNode( values.at( 2 ), parent ); - ENSURE_NO_EVAL_ERROR; - QString subExpression = node->dump(); - - //optional fourth node is concatenator - QgsAggregateCalculator::AggregateParameters parameters; - if ( values.count() > 3 ) - { - node = getNode( values.at( 3 ), parent ); - ENSURE_NO_EVAL_ERROR; - value = node->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - parameters.delimiter = value.toString(); - } - - FEAT_FROM_CONTEXT( context, f ); - parameters.filter = relation.getRelatedFeaturesFilter( f ); - - QString cacheKey = QStringLiteral( "relagg:%1:%2:%3:%4" ).arg( vl->id(), - QString::number( static_cast< int >( aggregate ) ), - subExpression, - parameters.filter ); - if ( context && context->hasCachedValue( cacheKey ) ) - return context->cachedValue( cacheKey ); - - QVariant result; - ok = false; - - - QgsExpressionContext subContext( *context ); - result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok ); - - if ( !ok ) - { - parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) ); - return QVariant(); - } - - // cache value - if ( context ) - context->setCachedValue( cacheKey, result ); - return result; -} - - -static QVariant fcnAggregateGeneric( QgsAggregateCalculator::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent ) -{ - if ( !context ) - { - parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) ); - return QVariant(); - } - - // first step - find current layer - QgsVectorLayer *vl = getVectorLayer( context->variable( "layer" ), parent ); - if ( !vl ) - { - parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) ); - return QVariant(); - } - - //lazy eval, so we need to evaluate nodes now - - //first node is subexpression (or field name) - QgsExpressionNode *node = getNode( values.at( 0 ), parent ); - ENSURE_NO_EVAL_ERROR; - QString subExpression = node->dump(); - - //optional second node is group by - QString groupBy; - if ( values.count() > 1 ) - { - node = getNode( values.at( 1 ), parent ); - ENSURE_NO_EVAL_ERROR; - QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node ); - if ( !nl || nl->value().isValid() ) - groupBy = node->dump(); - } - - //optional third node is filter - if ( values.count() > 2 ) - { - node = getNode( values.at( 2 ), parent ); - ENSURE_NO_EVAL_ERROR; - QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node ); - if ( !nl || nl->value().isValid() ) - parameters.filter = node->dump(); - } - - // build up filter with group by - - // find current group by value - if ( !groupBy.isEmpty() ) - { - QgsExpression groupByExp( groupBy ); - QVariant groupByValue = groupByExp.evaluate( context ); - QString groupByClause = QStringLiteral( "%1 %2 %3" ).arg( groupBy, - groupByValue.isNull() ? "is" : "=", - QgsExpression::quotedValue( groupByValue ) ); - if ( !parameters.filter.isEmpty() ) - parameters.filter = QStringLiteral( "(%1) AND (%2)" ).arg( parameters.filter, groupByClause ); - else - parameters.filter = groupByClause; - } - - QString cacheKey = QStringLiteral( "agg:%1:%2:%3:%4" ).arg( vl->id(), - QString::number( static_cast< int >( aggregate ) ), - subExpression, - parameters.filter ); - if ( context && context->hasCachedValue( cacheKey ) ) - return context->cachedValue( cacheKey ); - - QVariant result; - bool ok = false; - - QgsExpressionContext subContext( *context ); - result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok ); - - if ( !ok ) - { - parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) ); - return QVariant(); - } - - // cache value - if ( context ) - context->setCachedValue( cacheKey, result ); - return result; -} - - -static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - return fcnAggregateGeneric( QgsAggregateCalculator::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); -} - -static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - QgsAggregateCalculator::AggregateParameters parameters; - - //fourth node is concatenator - if ( values.count() > 3 ) - { - QgsExpressionNode *node = getNode( values.at( 3 ), parent ); - ENSURE_NO_EVAL_ERROR; - QVariant value = node->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - parameters.delimiter = value.toString(); - } - - return fcnAggregateGeneric( QgsAggregateCalculator::StringConcatenate, values, parameters, context, parent ); -} - -static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double minValue = getDoubleValue( values.at( 0 ), parent ); - double testValue = getDoubleValue( values.at( 1 ), parent ); - double maxValue = getDoubleValue( values.at( 2 ), parent ); - - // force testValue to sit inside the range specified by the min and max value - if ( testValue <= minValue ) - { - return QVariant( minValue ); - } - else if ( testValue >= maxValue ) - { - return QVariant( maxValue ); - } - else - { - return QVariant( testValue ); - } -} - -static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double x = getDoubleValue( values.at( 0 ), parent ); - return QVariant( floor( x ) ); -} - -static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double x = getDoubleValue( values.at( 0 ), parent ); - return QVariant( ceil( x ) ); -} - -static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - return QVariant( getIntValue( values.at( 0 ), parent ) ); -} -static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - return QVariant( getDoubleValue( values.at( 0 ), parent ) ); -} -static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - return QVariant( getStringValue( values.at( 0 ), parent ) ); -} - -static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - return QVariant( getDateTimeValue( values.at( 0 ), parent ) ); -} - -static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression * ) -{ - Q_FOREACH ( const QVariant &value, values ) - { - if ( value.isNull() ) - continue; - return value; - } - return QVariant(); -} -static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString str = getStringValue( values.at( 0 ), parent ); - return QVariant( str.toLower() ); -} -static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString str = getStringValue( values.at( 0 ), parent ); - return QVariant( str.toUpper() ); -} -static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString str = getStringValue( values.at( 0 ), parent ); - QStringList elems = str.split( ' ' ); - for ( int i = 0; i < elems.size(); i++ ) - { - if ( elems[i].size() > 1 ) - elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower(); - } - return QVariant( elems.join( QStringLiteral( " " ) ) ); -} - -static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString str = getStringValue( values.at( 0 ), parent ); - return QVariant( str.trimmed() ); -} - -static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString string1 = getStringValue( values.at( 0 ), parent ); - QString string2 = getStringValue( values.at( 1 ), parent ); - return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) ); -} - -static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString string1 = getStringValue( values.at( 0 ), parent ); - QString string2 = getStringValue( values.at( 1 ), parent ); - return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) ); -} - -static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString string1 = getStringValue( values.at( 0 ), parent ); - QString string2 = getStringValue( values.at( 1 ), parent ); - int dist = QgsStringUtils::hammingDistance( string1, string2 ); - return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) ); -} - -static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString string = getStringValue( values.at( 0 ), parent ); - return QVariant( QgsStringUtils::soundex( string ) ); -} - -static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QChar character = QChar( getNativeIntValue( values.at( 0 ), parent ) ); - return QVariant( QString( character ) ); -} - -static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - if ( values.length() == 2 || values.length() == 3 ) - { - QString str = getStringValue( values.at( 0 ), parent ); - qlonglong wrap = getIntValue( values.at( 1 ), parent ); - - if ( !str.isEmpty() && wrap != 0 ) - { - QString newstr; - QRegExp rx; - QString customdelimiter = getStringValue( values.at( 2 ), parent ); - int delimiterlength; - - if ( customdelimiter.length() > 0 ) - { - rx.setPatternSyntax( QRegExp::FixedString ); - rx.setPattern( customdelimiter ); - delimiterlength = customdelimiter.length(); - } - else - { - // \x200B is a ZERO-WIDTH SPACE, needed for worwrap to support a number of complex scripts (Indic, Arabic, etc.) - rx.setPattern( QStringLiteral( "[\\s\\x200B]" ) ); - delimiterlength = 1; - } - - - QStringList lines = str.split( '\n' ); - int strlength, strcurrent, strhit, lasthit; - - for ( int i = 0; i < lines.size(); i++ ) - { - strlength = lines[i].length(); - strcurrent = 0; - strhit = 0; - lasthit = 0; - - while ( strcurrent < strlength ) - { - // positive wrap value = desired maximum line width to wrap - // negative wrap value = desired minimum line width before wrap - if ( wrap > 0 ) - { - //first try to locate delimiter backwards - strhit = lines[i].lastIndexOf( rx, strcurrent + wrap ); - if ( strhit == lasthit || strhit == -1 ) - { - //if no new backward delimiter found, try to locate forward - strhit = lines[i].indexOf( rx, strcurrent + qAbs( wrap ) ); - } - lasthit = strhit; - } - else - { - strhit = lines[i].indexOf( rx, strcurrent + qAbs( wrap ) ); - } - if ( strhit > -1 ) - { - newstr.append( lines[i].midRef( strcurrent, strhit - strcurrent ) ); - newstr.append( '\n' ); - strcurrent = strhit + delimiterlength; - } - else - { - newstr.append( lines[i].midRef( strcurrent ) ); - strcurrent = strlength; - } - } - if ( i < lines.size() - 1 ) newstr.append( '\n' ); - } - - return QVariant( newstr ); - } - } - - return QVariant(); -} - -static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - // two variants, one for geometry, one for string - if ( values.at( 0 ).canConvert() ) - { - //geometry variant - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - if ( geom.type() != QgsWkbTypes::LineGeometry ) - return QVariant(); - - return QVariant( geom.length() ); - } - - //otherwise fall back to string variant - QString str = getStringValue( values.at( 0 ), parent ); - return QVariant( str.length() ); -} - -static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - if ( values.count() == 2 && values.at( 1 ).type() == QVariant::Map ) - { - QString str = getStringValue( values.at( 0 ), parent ); - QVariantMap map = getMapValue( values.at( 1 ), parent ); - - for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it ) - { - str = str.replace( it.key(), it.value().toString() ); - } - - return QVariant( str ); - } - else if ( values.count() == 3 ) - { - QString str = getStringValue( values.at( 0 ), parent ); - QVariantList before; - QVariantList after; - bool isSingleReplacement = false; - - if ( values.at( 1 ).type() != QVariant::List && values.at( 2 ).type() != QVariant::StringList ) - { - before = QVariantList() << getStringValue( values.at( 1 ), parent ); - } - else - { - before = getListValue( values.at( 1 ), parent ); - } - - if ( values.at( 2 ).type() != QVariant::List && values.at( 2 ).type() != QVariant::StringList ) - { - after = QVariantList() << getStringValue( values.at( 2 ), parent ); - isSingleReplacement = true; - } - else - { - after = getListValue( values.at( 2 ), parent ); - } - - if ( !isSingleReplacement && before.length() != after.length() ) - { - parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) ); - return QVariant(); - } - - for ( int i = 0; i < before.length(); i++ ) - { - str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() ); - } - - return QVariant( str ); - } - else - { - parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) ); - return QVariant(); - } -} -static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString str = getStringValue( values.at( 0 ), parent ); - QString regexp = getStringValue( values.at( 1 ), parent ); - QString after = getStringValue( values.at( 2 ), parent ); - - QRegularExpression re( regexp ); - if ( !re.isValid() ) - { - parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) ); - return QVariant(); - } - return QVariant( str.replace( re, after ) ); -} - -static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString str = getStringValue( values.at( 0 ), parent ); - QString regexp = getStringValue( values.at( 1 ), parent ); - - QRegularExpression re( regexp ); - if ( !re.isValid() ) - { - parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) ); - return QVariant(); - } - return QVariant( ( str.indexOf( re ) + 1 ) ); -} - -static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString str = getStringValue( values.at( 0 ), parent ); - QString regexp = getStringValue( values.at( 1 ), parent ); - QString empty = getStringValue( values.at( 2 ), parent ); - - QRegularExpression re( regexp ); - if ( !re.isValid() ) - { - parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) ); - return QVariant(); - } - - QRegularExpressionMatch matches = re.match( str ); - if ( matches.hasMatch() ) - { - QVariantList array; - QStringList list = matches.capturedTexts(); - - // Skip the first string to only return captured groups - for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it ) - { - array += ( !( *it ).isEmpty() ) ? *it : empty; - } - - return QVariant( array ); - } - else - { - return QVariant(); - } -} - -static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString str = getStringValue( values.at( 0 ), parent ); - QString regexp = getStringValue( values.at( 1 ), parent ); - - QRegularExpression re( regexp ); - if ( !re.isValid() ) - { - parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) ); - return QVariant(); - } - - // extract substring - QRegularExpressionMatch match = re.match( str ); - if ( match.hasMatch() ) - { - // return first capture - return QVariant( match.captured( 0 ) ); - } - else - { - return QVariant( "" ); - } -} - -static QVariant fcnUuid( const QVariantList &, const QgsExpressionContext *, QgsExpression * ) -{ - return QUuid::createUuid().toString(); -} - -static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() ) - return QVariant(); - - QString str = getStringValue( values.at( 0 ), parent ); - qlonglong from = getIntValue( values.at( 1 ), parent ); - - qlonglong len = 0; - if ( values.at( 2 ).isValid() ) - len = getIntValue( values.at( 2 ), parent ); - else - len = str.size(); - - if ( from < 0 ) - { - from = str.size() + from; - if ( from < 0 ) - { - from = 0; - } - } - else if ( from > 0 ) - { - //account for the fact that substr() starts at 1 - from -= 1; - } - - if ( len < 0 ) - { - len = str.size() + len - from; - if ( len < 0 ) - { - len = 0; - } - } - - return QVariant( str.mid( from, len ) ); -} -static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression * ) -{ - FEAT_FROM_CONTEXT( context, f ); - // TODO: handling of 64-bit feature ids? - return QVariant( static_cast< int >( f.id() ) ); -} - -static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression * ) -{ - if ( !context ) - return QVariant(); - - return context->feature(); -} -static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsFeature feat = getFeature( values.at( 0 ), parent ); - QString attr = getStringValue( values.at( 1 ), parent ); - - return feat.attribute( attr ); -} - -static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - QgsVectorLayer *layer = nullptr; - QgsFeature feature; - - if ( values.isEmpty() ) - { - feature = context->feature(); - layer = getVectorLayer( context->variable( "layer" ), parent ); - } - else if ( values.size() == 1 ) - { - layer = getVectorLayer( context->variable( "layer" ), parent ); - feature = getFeature( values.at( 0 ), parent ); - } - else if ( values.size() == 2 ) - { - layer = getVectorLayer( values.at( 0 ), parent ); - feature = getFeature( values.at( 1 ), parent ); - } - else - { - parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %1 given." ).arg( values.length() ) ); - return QVariant(); - } - - if ( !layer || !feature.isValid() ) - { - return QVariant( QVariant::Bool ); - } - - return layer->selectedFeatureIds().contains( feature.id() ); -} - -static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - QgsVectorLayer *layer = nullptr; - - if ( values.isEmpty() ) - layer = getVectorLayer( context->variable( "layer" ), parent ); - else if ( values.count() == 1 ) - layer = getVectorLayer( values.at( 0 ), parent ); - else - { - parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one QgsExpressionFunction::Parameter. %1 given." ).arg( values.length() ) ); - return QVariant(); - } - - if ( !layer ) - { - return QVariant( QVariant::LongLong ); - } - - return layer->selectedFeatureCount(); -} - -static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString concat; - Q_FOREACH ( const QVariant &value, values ) - { - concat += getStringValue( value, parent ); - } - return concat; -} - -static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString string = getStringValue( values.at( 0 ), parent ); - return string.indexOf( getStringValue( values.at( 1 ), parent ) ) + 1; -} - -static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString string = getStringValue( values.at( 0 ), parent ); - qlonglong pos = getIntValue( values.at( 1 ), parent ); - return string.right( pos ); -} - -static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString string = getStringValue( values.at( 0 ), parent ); - qlonglong pos = getIntValue( values.at( 1 ), parent ); - return string.left( pos ); -} - -static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString string = getStringValue( values.at( 0 ), parent ); - qlonglong length = getIntValue( values.at( 1 ), parent ); - QString fill = getStringValue( values.at( 2 ), parent ); - return string.leftJustified( length, fill.at( 0 ), true ); -} - -static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString string = getStringValue( values.at( 0 ), parent ); - qlonglong length = getIntValue( values.at( 1 ), parent ); - QString fill = getStringValue( values.at( 2 ), parent ); - return string.rightJustified( length, fill.at( 0 ), true ); -} - -static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString string = getStringValue( values.at( 0 ), parent ); - for ( int n = 1; n < values.length(); n++ ) - { - string = string.arg( getStringValue( values.at( n ), parent ) ); - } - return string; -} - - -static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression * ) -{ - return QVariant( QDateTime::currentDateTime() ); -} - -static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - return QVariant( getDateValue( values.at( 0 ), parent ) ); -} - -static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - return QVariant( getTimeValue( values.at( 0 ), parent ) ); -} - -static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - return QVariant::fromValue( getInterval( values.at( 0 ), parent ) ); -} - -static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QDateTime d1 = getDateTimeValue( values.at( 0 ), parent ); - QDateTime d2 = getDateTimeValue( values.at( 1 ), parent ); - int seconds = d2.secsTo( d1 ); - return QVariant::fromValue( QgsInterval( seconds ) ); -} - -static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - if ( !values.at( 0 ).canConvert() ) - return QVariant(); - - QDate date = getDateValue( values.at( 0 ), parent ); - if ( !date.isValid() ) - return QVariant(); - - // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat) - // (to match PostgreSQL behavior) - return date.dayOfWeek() % 7; -} - -static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariant value = values.at( 0 ); - QgsInterval inter = getInterval( value, parent, false ); - if ( inter.isValid() ) - { - return QVariant( inter.days() ); - } - else - { - QDateTime d1 = getDateTimeValue( value, parent ); - return QVariant( d1.date().day() ); - } -} - -static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariant value = values.at( 0 ); - QgsInterval inter = getInterval( value, parent, false ); - if ( inter.isValid() ) - { - return QVariant( inter.years() ); - } - else - { - QDateTime d1 = getDateTimeValue( value, parent ); - return QVariant( d1.date().year() ); - } -} - -static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariant value = values.at( 0 ); - QgsInterval inter = getInterval( value, parent, false ); - if ( inter.isValid() ) - { - return QVariant( inter.months() ); - } - else - { - QDateTime d1 = getDateTimeValue( value, parent ); - return QVariant( d1.date().month() ); - } -} - -static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariant value = values.at( 0 ); - QgsInterval inter = getInterval( value, parent, false ); - if ( inter.isValid() ) - { - return QVariant( inter.weeks() ); - } - else - { - QDateTime d1 = getDateTimeValue( value, parent ); - return QVariant( d1.date().weekNumber() ); - } -} - -static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariant value = values.at( 0 ); - QgsInterval inter = getInterval( value, parent, false ); - if ( inter.isValid() ) - { - return QVariant( inter.hours() ); - } - else - { - QTime t1 = getTimeValue( value, parent ); - return QVariant( t1.hour() ); - } -} - -static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariant value = values.at( 0 ); - QgsInterval inter = getInterval( value, parent, false ); - if ( inter.isValid() ) - { - return QVariant( inter.minutes() ); - } - else - { - QTime t1 = getTimeValue( value, parent ); - return QVariant( t1.minute() ); - } -} - -static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariant value = values.at( 0 ); - QgsInterval inter = getInterval( value, parent, false ); - if ( inter.isValid() ) - { - return QVariant( inter.seconds() ); - } - else - { - QTime t1 = getTimeValue( value, parent ); - return QVariant( t1.second() ); - } -} - -static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QDateTime dt = getDateTimeValue( values.at( 0 ), parent ); - if ( dt.isValid() ) - { - return QVariant( dt.toMSecsSinceEpoch() ); - } - else - { - return QVariant(); - } -} - -#define ENSURE_GEOM_TYPE(f, g, geomtype) \ - if ( !(f).hasGeometry() ) \ - return QVariant(); \ - QgsGeometry g = (f).geometry(); \ - if ( (g).type() != (geomtype) ) \ - return QVariant(); - -static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression * ) -{ - FEAT_FROM_CONTEXT( context, f ); - ENSURE_GEOM_TYPE( f, g, QgsWkbTypes::PointGeometry ); - if ( g.isMultipart() ) - { - return g.asMultiPoint().at( 0 ).x(); - } - else - { - return g.asPoint().x(); - } -} - -static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression * ) -{ - FEAT_FROM_CONTEXT( context, f ); - ENSURE_GEOM_TYPE( f, g, QgsWkbTypes::PointGeometry ); - if ( g.isMultipart() ) - { - return g.asMultiPoint().at( 0 ).y(); - } - else - { - return g.asPoint().y(); - } -} - -static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - if ( geom.isNull() ) - return QVariant(); - - //if single point, return the point's x coordinate - if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() ) - { - return geom.asPoint().x(); - } - - //otherwise return centroid x - QgsGeometry centroid = geom.centroid(); - QVariant result( centroid.asPoint().x() ); - return result; -} - -static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - if ( geom.isNull() ) - return QVariant(); - - //if single point, return the point's y coordinate - if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() ) - { - return geom.asPoint().y(); - } - - //otherwise return centroid y - QgsGeometry centroid = geom.centroid(); - QVariant result( centroid.asPoint().y() ); - return result; -} - -static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - if ( geom.isNull() ) - return QVariant(); //or 0? - - //if single point, return the point's z coordinate - if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() ) - { - QgsPointV2 *point = dynamic_cast< QgsPointV2 * >( geom.geometry() ); - if ( point ) - return point->z(); - } - - return QVariant(); -} - -static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - if ( geom.isNull() ) - return QVariant(); //or 0? - - //if single point, return the point's m value - if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() ) - { - QgsPointV2 *point = dynamic_cast< QgsPointV2 * >( geom.geometry() ); - if ( point ) - return point->m(); - } - - return QVariant(); -} - -static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - - if ( geom.isNull() ) - return QVariant(); - - //idx is 1 based - qlonglong idx = getIntValue( values.at( 1 ), parent ) - 1; - - QgsVertexId vId; - if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) ) - { - parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) ); - return QVariant(); - } - - QgsPointV2 point = geom.geometry()->vertexAt( vId ); - return QVariant::fromValue( QgsGeometry( new QgsPointV2( point ) ) ); -} - -static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - - if ( geom.isNull() ) - return QVariant(); - - QgsVertexId vId; - if ( !geom.vertexIdFromVertexNr( 0, vId ) ) - { - return QVariant(); - } - - QgsPointV2 point = geom.geometry()->vertexAt( vId ); - return QVariant::fromValue( QgsGeometry( new QgsPointV2( point ) ) ); -} - -static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - - if ( geom.isNull() ) - return QVariant(); - - QgsVertexId vId; - if ( !geom.vertexIdFromVertexNr( geom.geometry()->nCoordinates() - 1, vId ) ) - { - return QVariant(); - } - - QgsPointV2 point = geom.geometry()->vertexAt( vId ); - return QVariant::fromValue( QgsGeometry( new QgsPointV2( point ) ) ); -} - -static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - - if ( geom.isNull() ) - return QVariant(); - - bool ignoreClosing = false; - if ( values.length() > 1 ) - { - ignoreClosing = getIntValue( values.at( 1 ), parent ); - } - - QgsMultiPointV2 *mp = new QgsMultiPointV2(); - - Q_FOREACH ( const QgsRingSequence &part, geom.geometry()->coordinateSequence() ) - { - Q_FOREACH ( const QgsPointSequence &ring, part ) - { - bool skipLast = false; - if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() ) - { - skipLast = true; - } - - for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i ) - { - mp->addGeometry( ring.at( i ).clone() ); - } - } - } - - return QVariant::fromValue( QgsGeometry( mp ) ); -} - -static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - - if ( geom.isNull() ) - return QVariant(); - - QList< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.geometry() ); - - //ok, now we have a complete list of segmentized lines from the geometry - QgsMultiLineString *ml = new QgsMultiLineString(); - Q_FOREACH ( QgsLineString *line, linesToProcess ) - { - for ( int i = 0; i < line->numPoints() - 1; ++i ) - { - QgsLineString *segment = new QgsLineString(); - segment->setPoints( QgsPointSequence() - << line->pointN( i ) - << line->pointN( i + 1 ) ); - ml->addGeometry( segment ); - } - delete line; - } - - return QVariant::fromValue( QgsGeometry( ml ) ); -} - -static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - - if ( geom.isNull() ) - return QVariant(); - - QgsCurvePolygon *curvePolygon = dynamic_cast< QgsCurvePolygon * >( geom.geometry() ); - if ( !curvePolygon ) - return QVariant(); - - //idx is 1 based - qlonglong idx = getIntValue( values.at( 1 ), parent ) - 1; - - if ( idx >= curvePolygon->numInteriorRings() || idx < 0 ) - return QVariant(); - - QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( idx )->clone() ); - QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant(); - return result; -} - -static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - - if ( geom.isNull() ) - return QVariant(); - - QgsGeometryCollection *collection = dynamic_cast< QgsGeometryCollection * >( geom.geometry() ); - if ( !collection ) - return QVariant(); - - //idx is 1 based - qlonglong idx = getIntValue( values.at( 1 ), parent ) - 1; - - if ( idx < 0 || idx >= collection->numGeometries() ) - return QVariant(); - - QgsAbstractGeometry *part = collection->geometryN( idx )->clone(); - QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant(); - return result; -} - -static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - - if ( geom.isNull() ) - return QVariant(); - - QgsAbstractGeometry *boundary = geom.geometry()->boundary(); - if ( !boundary ) - return QVariant(); - - return QVariant::fromValue( QgsGeometry( boundary ) ); -} - -static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - - if ( geom.isNull() ) - return QVariant(); - - QgsGeometry merged = geom.mergeLines(); - if ( merged.isNull() ) - return QVariant(); - - return QVariant::fromValue( merged ); -} - -static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - - if ( geom.isNull() ) - return QVariant(); - - double tolerance = getDoubleValue( values.at( 1 ), parent ); - - QgsGeometry simplified = geom.simplify( tolerance ); - if ( simplified.isNull() ) - return QVariant(); - - return simplified; -} - -static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - - if ( geom.isNull() ) - return QVariant(); - - double tolerance = getDoubleValue( values.at( 1 ), parent ); - - QgsMapToPixelSimplifier simplifier( QgsMapToPixelSimplifier::SimplifyGeometry, tolerance, QgsMapToPixelSimplifier::Visvalingam ); - - QgsGeometry simplified = simplifier.simplify( geom ); - if ( simplified.isNull() ) - return QVariant(); - - return simplified; -} - -static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - - if ( geom.isNull() ) - return QVariant(); - - int iterations = qMin( getNativeIntValue( values.at( 1 ), parent ), 10 ); - double offset = qBound( 0.0, getDoubleValue( values.at( 2 ), parent ), 0.5 ); - double minLength = getDoubleValue( values.at( 3 ), parent ); - double maxAngle = qBound( 0.0, getDoubleValue( values.at( 4 ), parent ), 180.0 ); - - QgsGeometry smoothed = geom.smooth( iterations, offset, minLength, maxAngle ); - if ( smoothed.isNull() ) - return QVariant(); - - return smoothed; -} - -static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - if ( values.count() < 2 || values.count() > 4 ) - { - parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) ); - return QVariant(); - } - - double x = getDoubleValue( values.at( 0 ), parent ); - double y = getDoubleValue( values.at( 1 ), parent ); - double z = values.count() >= 3 ? getDoubleValue( values.at( 2 ), parent ) : 0.0; - double m = values.count() >= 4 ? getDoubleValue( values.at( 3 ), parent ) : 0.0; - switch ( values.count() ) - { - case 2: - return QVariant::fromValue( QgsGeometry( new QgsPointV2( x, y ) ) ); - case 3: - return QVariant::fromValue( QgsGeometry( new QgsPointV2( QgsWkbTypes::PointZ, x, y, z ) ) ); - case 4: - return QVariant::fromValue( QgsGeometry( new QgsPointV2( QgsWkbTypes::PointZM, x, y, z, m ) ) ); - } - return QVariant(); //avoid warning -} - -static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double x = getDoubleValue( values.at( 0 ), parent ); - double y = getDoubleValue( values.at( 1 ), parent ); - double m = getDoubleValue( values.at( 2 ), parent ); - return QVariant::fromValue( QgsGeometry( new QgsPointV2( QgsWkbTypes::PointM, x, y, 0.0, m ) ) ); -} - -static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - if ( values.count() < 2 ) - { - return QVariant(); - } - - QgsLineString *lineString = new QgsLineString(); - lineString->clear(); - - Q_FOREACH ( const QVariant &value, values ) - { - QgsGeometry geom = getGeometry( value, parent ); - if ( geom.isNull() ) - continue; - - if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() ) - continue; - - QgsPointV2 *point = dynamic_cast< QgsPointV2 * >( geom.geometry() ); - if ( !point ) - continue; - - lineString->addVertex( *point ); - } - - return QVariant::fromValue( QgsGeometry( lineString ) ); -} - -static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - if ( values.count() < 1 ) - { - parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) ); - return QVariant(); - } - - QgsGeometry outerRing = getGeometry( values.at( 0 ), parent ); - if ( outerRing.type() != QgsWkbTypes::LineGeometry || outerRing.isMultipart() || outerRing.isNull() ) - return QVariant(); - - QgsPolygonV2 *polygon = new QgsPolygonV2(); - polygon->setExteriorRing( dynamic_cast< QgsCurve * >( outerRing.geometry()->clone() ) ); - - for ( int i = 1; i < values.count(); ++i ) - { - QgsGeometry ringGeom = getGeometry( values.at( i ), parent ); - if ( ringGeom.isNull() ) - continue; - - if ( ringGeom.type() != QgsWkbTypes::LineGeometry || ringGeom.isMultipart() || ringGeom.isNull() ) - continue; - - polygon->addInteriorRing( dynamic_cast< QgsCurve * >( ringGeom.geometry()->clone() ) ); - } - - return QVariant::fromValue( QgsGeometry( polygon ) ); -} - -static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - std::unique_ptr tr( new QgsTriangle() ); - std::unique_ptr lineString( new QgsLineString() ); - lineString->clear(); - - Q_FOREACH ( const QVariant &value, values ) - { - QgsGeometry geom = getGeometry( value, parent ); - if ( geom.isNull() ) - return QVariant(); - - if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() ) - return QVariant(); - - QgsPointV2 *point = dynamic_cast< QgsPointV2 * >( geom.geometry() ); - if ( !point ) - return QVariant(); - - lineString->addVertex( *point ); - } - - tr->setExteriorRing( lineString.release() ); - - return QVariant::fromValue( QgsGeometry( tr.release() ) ); -} - -static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - if ( geom.isNull() ) - return QVariant(); - - if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() ) - return QVariant(); - - double radius = getDoubleValue( values.at( 1 ), parent ); - double segment = getIntValue( values.at( 2 ), parent ); - - if ( segment < 3 ) - { - parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) ); - return QVariant(); - } - QgsPointV2 *point = static_cast< QgsPointV2 * >( geom.geometry() ); - QgsCircle circ( *point, radius ); - return QVariant::fromValue( QgsGeometry( circ.toPolygon( segment ) ) ); -} - -static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - if ( geom.isNull() ) - return QVariant(); - - if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() ) - return QVariant(); - - double majorAxis = getDoubleValue( values.at( 1 ), parent ); - double minorAxis = getDoubleValue( values.at( 2 ), parent ); - double azimuth = getDoubleValue( values.at( 3 ), parent ); - double segment = getIntValue( values.at( 4 ), parent ); - if ( segment < 3 ) - { - parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) ); - return QVariant(); - } - QgsPointV2 *point = static_cast< QgsPointV2 * >( geom.geometry() ); - QgsEllipse elp( *point, majorAxis, minorAxis, azimuth ); - return QVariant::fromValue( QgsGeometry( elp.toPolygon( segment ) ) ); -} - -static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - - QgsGeometry pt1 = getGeometry( values.at( 0 ), parent ); - if ( pt1.isNull() ) - return QVariant(); - - if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() ) - return QVariant(); - - QgsGeometry pt2 = getGeometry( values.at( 1 ), parent ); - if ( pt2.isNull() ) - return QVariant(); - - if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() ) - return QVariant(); - - unsigned int nbEdges = static_cast( getIntValue( values.at( 2 ), parent ) ); - if ( nbEdges < 3 ) - { - parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) ); - return QVariant(); - } - - QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( getIntValue( values.at( 3 ), parent ) ); - if ( ( option < QgsRegularPolygon::InscribedCircle ) || ( option > QgsRegularPolygon::CircumscribedCircle ) ) - { - parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) ); - return QVariant(); - } - QgsPointV2 *center = static_cast< QgsPointV2 * >( pt1.geometry() ); - QgsPointV2 *corner = static_cast< QgsPointV2 * >( pt2.geometry() ); - - QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option ); - - return QVariant::fromValue( QgsGeometry( rp.toPolygon( ) ) ); - -} - -static QVariant pointAt( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) // helper function -{ - FEAT_FROM_CONTEXT( context, f ); - qlonglong idx = getIntValue( values.at( 0 ), parent ); - QgsGeometry g = f.geometry(); - if ( g.isNull() ) - return QVariant(); - - if ( idx < 0 ) - { - idx += g.geometry()->nCoordinates(); - } - if ( idx < 0 || idx >= g.geometry()->nCoordinates() ) - { - parent->setEvalErrorString( QObject::tr( "Index is out of range" ) ); - return QVariant(); - } - - QgsPoint p = g.vertexAt( idx ); - return QVariant( QPointF( p.x(), p.y() ) ); -} - -static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent ) -{ - QVariant v = pointAt( values, f, parent ); - if ( v.type() == QVariant::PointF ) - return QVariant( v.toPointF().x() ); - else - return QVariant(); -} -static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent ) -{ - QVariant v = pointAt( values, f, parent ); - if ( v.type() == QVariant::PointF ) - return QVariant( v.toPointF().y() ); - else - return QVariant(); -} -static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression * ) -{ - FEAT_FROM_CONTEXT( context, f ); - QgsGeometry geom = f.geometry(); - if ( !geom.isNull() ) - return QVariant::fromValue( geom ); - else - return QVariant( QVariant::UserType ); -} -static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString wkt = getStringValue( values.at( 0 ), parent ); - QgsGeometry geom = QgsGeometry::fromWkt( wkt ); - QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} -static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString gml = getStringValue( values.at( 0 ), parent ); - QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml ); - QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} - -static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent ) -{ - FEAT_FROM_CONTEXT( context, f ); - ENSURE_GEOM_TYPE( f, g, QgsWkbTypes::PolygonGeometry ); - QgsDistanceArea *calc = parent->geomCalculator(); - if ( calc ) - { - double area = calc->measureArea( f.geometry() ); - area = calc->convertAreaMeasurement( area, parent->areaUnits() ); - return QVariant( area ); - } - else - { - return QVariant( f.geometry().area() ); - } -} - -static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - - if ( geom.type() != QgsWkbTypes::PolygonGeometry ) - return QVariant(); - - return QVariant( geom.area() ); -} - -static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent ) -{ - FEAT_FROM_CONTEXT( context, f ); - ENSURE_GEOM_TYPE( f, g, QgsWkbTypes::LineGeometry ); - QgsDistanceArea *calc = parent->geomCalculator(); - if ( calc ) - { - double len = calc->measureLength( f.geometry() ); - len = calc->convertLengthMeasurement( len, parent->distanceUnits() ); - return QVariant( len ); - } - else - { - return QVariant( f.geometry().length() ); - } -} - -static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent ) -{ - FEAT_FROM_CONTEXT( context, f ); - ENSURE_GEOM_TYPE( f, g, QgsWkbTypes::PolygonGeometry ); - QgsDistanceArea *calc = parent->geomCalculator(); - if ( calc ) - { - double len = calc->measurePerimeter( f.geometry() ); - len = calc->convertLengthMeasurement( len, parent->distanceUnits() ); - return QVariant( len ); - } - else - { - return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().geometry()->perimeter() ); - } -} - -static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - - if ( geom.type() != QgsWkbTypes::PolygonGeometry ) - return QVariant(); - - //length for polygons = perimeter - return QVariant( geom.length() ); -} - -static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - return QVariant( geom.isNull() ? 0 : geom.geometry()->nCoordinates() ); -} - -static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - if ( geom.isNull() ) - return QVariant(); - - return QVariant( geom.geometry()->partCount() ); -} - -static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - - if ( geom.isNull() ) - return QVariant(); - - QgsCurvePolygon *curvePolygon = dynamic_cast< QgsCurvePolygon * >( geom.geometry() ); - if ( curvePolygon ) - return QVariant( curvePolygon->numInteriorRings() ); - - QgsGeometryCollection *collection = dynamic_cast< QgsGeometryCollection * >( geom.geometry() ); - if ( collection ) - { - //find first CurvePolygon in collection - for ( int i = 0; i < collection->numGeometries(); ++i ) - { - curvePolygon = dynamic_cast< QgsCurvePolygon *>( collection->geometryN( i ) ); - if ( !curvePolygon ) - continue; - - return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() ); - } - } - - return QVariant(); -} - -static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - - if ( geom.isNull() ) - return QVariant(); - - QgsCurvePolygon *curvePolygon = dynamic_cast< QgsCurvePolygon * >( geom.geometry() ); - if ( curvePolygon ) - return QVariant( curvePolygon->ringCount() ); - - bool foundPoly = false; - int ringCount = 0; - QgsGeometryCollection *collection = dynamic_cast< QgsGeometryCollection * >( geom.geometry() ); - if ( collection ) - { - //find CurvePolygons in collection - for ( int i = 0; i < collection->numGeometries(); ++i ) - { - curvePolygon = dynamic_cast< QgsCurvePolygon *>( collection->geometryN( i ) ); - if ( !curvePolygon ) - continue; - - foundPoly = true; - ringCount += curvePolygon->ringCount(); - } - } - - if ( !foundPoly ) - return QVariant(); - - return QVariant( ringCount ); -} - -static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() ); - QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant(); - return result; -} - -static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - return QVariant::fromValue( geom.boundingBox().width() ); -} - -static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - return QVariant::fromValue( geom.boundingBox().height() ); -} - -static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - return QVariant::fromValue( geom.boundingBox().xMinimum() ); -} - -static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - return QVariant::fromValue( geom.boundingBox().xMaximum() ); -} - -static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - return QVariant::fromValue( geom.boundingBox().yMinimum() ); -} - -static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - return QVariant::fromValue( geom.boundingBox().yMaximum() ); -} - -static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - if ( fGeom.isNull() ) - return QVariant(); - - QgsCurve *curve = dynamic_cast< QgsCurve * >( fGeom.geometry() ); - if ( !curve ) - return QVariant(); - - return QVariant::fromValue( curve->isClosed() ); -} - -static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - if ( values.length() < 2 || values.length() > 3 ) - return QVariant(); - - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); - - if ( fGeom.isNull() || sGeom.isNull() ) - return QVariant(); - - std::unique_ptr engine( QgsGeometry::createGeometryEngine( fGeom.geometry() ) ); - - if ( values.length() == 2 ) - { - //two geometry arguments, return relation - QString result = engine->relate( *sGeom.geometry() ); - return QVariant::fromValue( result ); - } - else - { - //three arguments, test pattern - QString pattern = getStringValue( values.at( 2 ), parent ); - bool result = engine->relatePattern( *sGeom.geometry(), pattern ); - return QVariant::fromValue( result ); - } -} - -static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); - return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False; -} -static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); - return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False; -} -static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); - return fGeom.intersects( sGeom ) ? TVL_True : TVL_False; -} -static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); - return fGeom.touches( sGeom ) ? TVL_True : TVL_False; -} -static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); - return fGeom.crosses( sGeom ) ? TVL_True : TVL_False; -} -static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); - return fGeom.contains( sGeom ) ? TVL_True : TVL_False; -} -static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); - return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False; -} -static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); - return fGeom.within( sGeom ) ? TVL_True : TVL_False; -} -static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - if ( values.length() < 2 || values.length() > 3 ) - return QVariant(); - - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - double dist = getDoubleValue( values.at( 1 ), parent ); - qlonglong seg = 8; - if ( values.length() == 3 ) - seg = getIntValue( values.at( 2 ), parent ); - - QgsGeometry geom = fGeom.buffer( dist, seg ); - QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} - -static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - double dist = getDoubleValue( values.at( 1 ), parent ); - qlonglong segments = getIntValue( values.at( 2 ), parent ); - QgsGeometry::JoinStyle join = static_cast< QgsGeometry::JoinStyle >( getIntValue( values.at( 3 ), parent ) ); - if ( join < QgsGeometry::JoinStyleRound || join > QgsGeometry::JoinStyleBevel ) - return QVariant(); - double mitreLimit = getDoubleValue( values.at( 3 ), parent ); - - QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, mitreLimit ); - QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} - -static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - double dist = getDoubleValue( values.at( 1 ), parent ); - qlonglong segments = getIntValue( values.at( 2 ), parent ); - QgsGeometry::JoinStyle join = static_cast< QgsGeometry::JoinStyle >( getIntValue( values.at( 3 ), parent ) ); - if ( join < QgsGeometry::JoinStyleRound || join > QgsGeometry::JoinStyleBevel ) - return QVariant(); - double mitreLimit = getDoubleValue( values.at( 3 ), parent ); - - QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, QgsGeometry::SideLeft, join, mitreLimit ); - QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} - -static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - double distStart = getDoubleValue( values.at( 1 ), parent ); - double distEnd = getDoubleValue( values.at( 2 ), parent ); - - QgsGeometry geom = fGeom.extendLine( distStart, distEnd ); - QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} - -static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - double dx = getDoubleValue( values.at( 1 ), parent ); - double dy = getDoubleValue( values.at( 2 ), parent ); - fGeom.translate( dx, dy ); - return QVariant::fromValue( fGeom ); -} -static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry geom = fGeom.centroid(); - QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} -static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry geom = fGeom.pointOnSurface(); - QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} - -static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - double tolerance = getDoubleValue( values.at( 1 ), parent ); - QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance ); - QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} - -static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry geom = fGeom.convexHull(); - QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} -static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); - QgsGeometry geom = fGeom.difference( sGeom ); - QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} - -static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - if ( fGeom.isNull() ) - return QVariant(); - - QgsCurve *curve = dynamic_cast< QgsCurve * >( fGeom.geometry() ); - if ( !curve ) - return QVariant(); - - QgsCurve *reversed = curve->reversed(); - QVariant result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant(); - return result; -} - -static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - if ( fGeom.isNull() ) - return QVariant(); - - QgsCurvePolygon *curvePolygon = dynamic_cast< QgsCurvePolygon * >( fGeom.geometry() ); - if ( !curvePolygon || !curvePolygon->exteriorRing() ) - return QVariant(); - - QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() ); - QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant(); - return result; -} - -static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); - return QVariant( fGeom.distance( sGeom ) ); -} -static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); - QgsGeometry geom = fGeom.intersection( sGeom ); - QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} -static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); - QgsGeometry geom = fGeom.symDifference( sGeom ); - QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} -static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); - QgsGeometry geom = fGeom.combine( sGeom ); - QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} -static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - if ( values.length() < 1 || values.length() > 2 ) - return QVariant(); - - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - qlonglong prec = 8; - if ( values.length() == 2 ) - prec = getIntValue( values.at( 1 ), parent ); - QString wkt = fGeom.exportToWkt( prec ); - return QVariant( wkt ); -} - -static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - if ( values.length() != 2 ) - { - parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %1 given." ).arg( values.length() ) ); - return QVariant(); - } - - QgsGeometry fGeom1 = getGeometry( values.at( 0 ), parent ); - QgsGeometry fGeom2 = getGeometry( values.at( 1 ), parent ); - - const QgsPointV2 *pt1 = dynamic_cast( fGeom1.geometry() ); - const QgsPointV2 *pt2 = dynamic_cast( fGeom2.geometry() ); - - if ( !pt1 || !pt2 ) - { - parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) ); - return QVariant(); - } - - // Code from postgis - if ( pt1->x() == pt2->x() ) - { - if ( pt1->y() < pt2->y() ) - return 0.0; - else if ( pt1->y() > pt2->y() ) - return M_PI; - else - return 0; - } - - if ( pt1->y() == pt2->y() ) - { - if ( pt1->x() < pt2->x() ) - return M_PI / 2; - else if ( pt1->x() > pt2->x() ) - return M_PI + ( M_PI / 2 ); - else - return 0; - } - - if ( pt1->x() < pt2->x() ) - { - if ( pt1->y() < pt2->y() ) - { - return atan( fabs( pt1->x() - pt2->x() ) / fabs( pt1->y() - pt2->y() ) ); - } - else /* ( pt1->y() > pt2->y() ) - equality case handled above */ - { - return atan( fabs( pt1->y() - pt2->y() ) / fabs( pt1->x() - pt2->x() ) ) - + ( M_PI / 2 ); - } - } - - else /* ( pt1->x() > pt2->x() ) - equality case handled above */ - { - if ( pt1->y() > pt2->y() ) - { - return atan( fabs( pt1->x() - pt2->x() ) / fabs( pt1->y() - pt2->y() ) ) - + M_PI; - } - else /* ( pt1->y() < pt2->y() ) - equality case handled above */ - { - return atan( fabs( pt1->y() - pt2->y() ) / fabs( pt1->x() - pt2->x() ) ) - + ( M_PI + ( M_PI / 2 ) ); - } - } -} - -static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - - if ( geom.type() != QgsWkbTypes::PointGeometry ) - { - parent->setEvalErrorString( QStringLiteral( "'project' requires a point geometry" ) ); - return QVariant(); - } - - double distance = getDoubleValue( values.at( 1 ), parent ); - double azimuth = getDoubleValue( values.at( 2 ), parent ); - double inclination = getDoubleValue( values.at( 3 ), parent ); - - const QgsPointV2 *p = static_cast( geom.geometry() ); - QgsPointV2 newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI ); - - return QVariant::fromValue( QgsGeometry( new QgsPointV2( newPoint ) ) ); -} - -static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom1 = getGeometry( values.at( 0 ), parent ); - QgsGeometry fGeom2 = getGeometry( values.at( 1 ), parent ); - - const QgsPointV2 *pt1 = dynamic_cast( fGeom1.geometry() ); - const QgsPointV2 *pt2 = dynamic_cast( fGeom2.geometry() ); - - if ( ( fGeom1.type() != QgsWkbTypes::PointGeometry ) || ( fGeom2.type() != QgsWkbTypes::PointGeometry ) || - !pt1 || !pt2 ) - { - parent->setEvalErrorString( QStringLiteral( "Function 'inclination' requires two points as arguments." ) ); - return QVariant(); - } - - return pt1->inclination( *pt2 ); - -} - -static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - if ( values.length() != 3 ) - return QVariant(); - - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - double x = getDoubleValue( values.at( 1 ), parent ); - double y = getDoubleValue( values.at( 2 ), parent ); - - QgsGeometry geom = fGeom.extrude( x, y ); - - QVariant result = geom.geometry() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} - -static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent ) -{ - if ( values.length() < 2 ) - return QVariant(); - - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - - if ( !fGeom.isMultipart() ) - return values.at( 0 ); - - QString expString = getStringValue( values.at( 1 ), parent ); - QVariant cachedExpression; - if ( ctx ) - cachedExpression = ctx->cachedValue( expString ); - QgsExpression expression; - - if ( cachedExpression.isValid() ) - { - expression = cachedExpression.value(); - } - else - expression = QgsExpression( expString ); - - bool asc = values.value( 2 ).toBool(); - - QgsExpressionContext *unconstedContext = nullptr; - QgsFeature f; - if ( ctx ) - { - // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after - // so no reason to worry - unconstedContext = const_cast( ctx ); - f = ctx->feature(); - } - else - { - // If there's no context provided, create a fake one - unconstedContext = new QgsExpressionContext(); - } - - QgsGeometryCollection *collection = dynamic_cast( fGeom.geometry() ); - Q_ASSERT( collection ); // Should have failed the multipart check above - - QgsFeatureRequest::OrderBy orderBy; - orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) ); - QgsExpressionSorter sorter( orderBy ); - - QList partFeatures; - partFeatures.reserve( collection->partCount() ); - for ( int i = 0; i < collection->partCount(); ++i ) - { - f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) ); - partFeatures << f; - } - - sorter.sortFeatures( partFeatures, unconstedContext ); - - QgsGeometryCollection *orderedGeom = dynamic_cast( fGeom.geometry()->clone() ); - - Q_ASSERT( orderedGeom ); - - while ( orderedGeom->partCount() ) - orderedGeom->removeGeometry( 0 ); - - Q_FOREACH ( const QgsFeature &feature, partFeatures ) - { - orderedGeom->addGeometry( feature.geometry().geometry()->clone() ); - } - - QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) ); - - if ( !ctx ) - delete unconstedContext; - - return result; -} - -static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fromGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry toGeom = getGeometry( values.at( 1 ), parent ); - - QgsGeometry geom = fromGeom.nearestPoint( toGeom ); - - QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} - -static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fromGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry toGeom = getGeometry( values.at( 1 ), parent ); - - QgsGeometry geom = fromGeom.shortestLine( toGeom ); - - QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} - -static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry lineGeom = getGeometry( values.at( 0 ), parent ); - double distance = getDoubleValue( values.at( 1 ), parent ); - - QgsGeometry geom = lineGeom.interpolate( distance ); - - QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); - return result; -} - -static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry lineGeom = getGeometry( values.at( 0 ), parent ); - double distance = getDoubleValue( values.at( 1 ), parent ); - - return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI; -} - -static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - qlonglong vertex = getIntValue( values.at( 1 ), parent ); - - return geom.angleAtVertex( vertex ) * 180.0 / M_PI; -} - -static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry geom = getGeometry( values.at( 0 ), parent ); - qlonglong vertex = getIntValue( values.at( 1 ), parent ); - - return geom.distanceToVertex( vertex ); -} - -static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry lineGeom = getGeometry( values.at( 0 ), parent ); - QgsGeometry pointGeom = getGeometry( values.at( 1 ), parent ); - - double distance = lineGeom.lineLocatePoint( pointGeom ); - - return distance >= 0 ? distance : QVariant(); -} - -static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - if ( values.length() == 2 && values.at( 1 ).toInt() != 0 ) - { - double number = getDoubleValue( values.at( 0 ), parent ); - double scaler = pow( 10.0, getIntValue( values.at( 1 ), parent ) ); - return QVariant( qRound( number * scaler ) / scaler ); - } - - if ( values.length() >= 1 ) - { - double number = getIntValue( values.at( 0 ), parent ); - return QVariant( qRound( number ) ); - } - - return QVariant(); -} - -static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - Q_UNUSED( values ); - Q_UNUSED( parent ); - return M_PI; -} - -static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - double value = getDoubleValue( values.at( 0 ), parent ); - int places = getIntValue( values.at( 1 ), parent ); - if ( places < 0 ) - { - parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) ); - return QVariant(); - } - return QStringLiteral( "%L1" ).arg( value, 0, 'f', places ); -} - -static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QDateTime dt = getDateTimeValue( values.at( 0 ), parent ); - QString format = getStringValue( values.at( 1 ), parent ); - return dt.toString( format ); -} - -static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - int red = getIntValue( values.at( 0 ), parent ); - int green = getIntValue( values.at( 1 ), parent ); - int blue = getIntValue( values.at( 2 ), parent ); - QColor color = QColor( red, green, blue ); - if ( ! color.isValid() ) - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) ); - color = QColor( 0, 0, 0 ); - } - - return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() ); -} - -static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) -{ - QgsExpressionNode *node = getNode( values.at( 0 ), parent ); - ENSURE_NO_EVAL_ERROR; - QVariant value = node->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - if ( value.toBool() ) - { - node = getNode( values.at( 1 ), parent ); - ENSURE_NO_EVAL_ERROR; - value = node->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - } - else - { - node = getNode( values.at( 2 ), parent ); - ENSURE_NO_EVAL_ERROR; - value = node->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - } - return value; -} - -static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - int red = getIntValue( values.at( 0 ), parent ); - int green = getIntValue( values.at( 1 ), parent ); - int blue = getIntValue( values.at( 2 ), parent ); - int alpha = getIntValue( values.at( 3 ), parent ); - QColor color = QColor( red, green, blue, alpha ); - if ( ! color.isValid() ) - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) ); - color = QColor( 0, 0, 0 ); - } - return QgsSymbolLayerUtils::encodeColor( color ); -} - -QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString rampName = getStringValue( values.at( 0 ), parent ); - const QgsColorRamp *mRamp = QgsStyle::defaultStyle()->colorRampRef( rampName ); - if ( ! mRamp ) - { - parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) ); - return QColor( 0, 0, 0 ).name(); - } - double value = getDoubleValue( values.at( 1 ), parent ); - QColor color = mRamp->color( value ); - return QgsSymbolLayerUtils::encodeColor( color ); -} - -static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - // Hue ranges from 0 - 360 - double hue = getIntValue( values.at( 0 ), parent ) / 360.0; - // Saturation ranges from 0 - 100 - double saturation = getIntValue( values.at( 1 ), parent ) / 100.0; - // Lightness ranges from 0 - 100 - double lightness = getIntValue( values.at( 2 ), parent ) / 100.0; - - QColor color = QColor::fromHslF( hue, saturation, lightness ); - - if ( ! color.isValid() ) - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) ); - color = QColor( 0, 0, 0 ); - } - - return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() ); -} - -static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - // Hue ranges from 0 - 360 - double hue = getIntValue( values.at( 0 ), parent ) / 360.0; - // Saturation ranges from 0 - 100 - double saturation = getIntValue( values.at( 1 ), parent ) / 100.0; - // Lightness ranges from 0 - 100 - double lightness = getIntValue( values.at( 2 ), parent ) / 100.0; - // Alpha ranges from 0 - 255 - double alpha = getIntValue( values.at( 3 ), parent ) / 255.0; - - QColor color = QColor::fromHslF( hue, saturation, lightness, alpha ); - if ( ! color.isValid() ) - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) ); - color = QColor( 0, 0, 0 ); - } - return QgsSymbolLayerUtils::encodeColor( color ); -} - -static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - // Hue ranges from 0 - 360 - double hue = getIntValue( values.at( 0 ), parent ) / 360.0; - // Saturation ranges from 0 - 100 - double saturation = getIntValue( values.at( 1 ), parent ) / 100.0; - // Value ranges from 0 - 100 - double value = getIntValue( values.at( 2 ), parent ) / 100.0; - - QColor color = QColor::fromHsvF( hue, saturation, value ); - - if ( ! color.isValid() ) - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) ); - color = QColor( 0, 0, 0 ); - } - - return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() ); -} - -static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - // Hue ranges from 0 - 360 - double hue = getIntValue( values.at( 0 ), parent ) / 360.0; - // Saturation ranges from 0 - 100 - double saturation = getIntValue( values.at( 1 ), parent ) / 100.0; - // Value ranges from 0 - 100 - double value = getIntValue( values.at( 2 ), parent ) / 100.0; - // Alpha ranges from 0 - 255 - double alpha = getIntValue( values.at( 3 ), parent ) / 255.0; - - QColor color = QColor::fromHsvF( hue, saturation, value, alpha ); - if ( ! color.isValid() ) - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) ); - color = QColor( 0, 0, 0 ); - } - return QgsSymbolLayerUtils::encodeColor( color ); -} - -static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - // Cyan ranges from 0 - 100 - double cyan = getIntValue( values.at( 0 ), parent ) / 100.0; - // Magenta ranges from 0 - 100 - double magenta = getIntValue( values.at( 1 ), parent ) / 100.0; - // Yellow ranges from 0 - 100 - double yellow = getIntValue( values.at( 2 ), parent ) / 100.0; - // Black ranges from 0 - 100 - double black = getIntValue( values.at( 3 ), parent ) / 100.0; - - QColor color = QColor::fromCmykF( cyan, magenta, yellow, black ); - - if ( ! color.isValid() ) - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) ); - color = QColor( 0, 0, 0 ); - } - - return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() ); -} - -static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - // Cyan ranges from 0 - 100 - double cyan = getIntValue( values.at( 0 ), parent ) / 100.0; - // Magenta ranges from 0 - 100 - double magenta = getIntValue( values.at( 1 ), parent ) / 100.0; - // Yellow ranges from 0 - 100 - double yellow = getIntValue( values.at( 2 ), parent ) / 100.0; - // Black ranges from 0 - 100 - double black = getIntValue( values.at( 3 ), parent ) / 100.0; - // Alpha ranges from 0 - 255 - double alpha = getIntValue( values.at( 4 ), parent ) / 255.0; - - QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha ); - if ( ! color.isValid() ) - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) ); - color = QColor( 0, 0, 0 ); - } - return QgsSymbolLayerUtils::encodeColor( color ); -} - -static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() ); - if ( ! color.isValid() ) - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) ); - return QVariant(); - } - - QString part = getStringValue( values.at( 1 ), parent ); - if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 ) - return color.red(); - else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 ) - return color.green(); - else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 ) - return color.blue(); - else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 ) - return color.alpha(); - else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 ) - return color.hsvHueF() * 360; - else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 ) - return color.hsvSaturationF() * 100; - else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 ) - return color.valueF() * 100; - else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 ) - return color.hslHueF() * 360; - else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 ) - return color.hslSaturationF() * 100; - else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 ) - return color.lightnessF() * 100; - else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 ) - return color.cyanF() * 100; - else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 ) - return color.magentaF() * 100; - else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 ) - return color.yellowF() * 100; - else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 ) - return color.blackF() * 100; - - parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) ); - return QVariant(); -} - -static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() ); - if ( ! color.isValid() ) - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) ); - return QVariant(); - } - - QString part = getStringValue( values.at( 1 ), parent ); - int value = getIntValue( values.at( 2 ), parent ); - if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 ) - color.setRed( value ); - else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 ) - color.setGreen( value ); - else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 ) - color.setBlue( value ); - else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 ) - color.setAlpha( value ); - else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 ) - color.setHsv( value, color.hsvSaturation(), color.value(), color.alpha() ); - else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 ) - color.setHsvF( color.hsvHueF(), value / 100.0, color.valueF(), color.alphaF() ); - else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 ) - color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), value / 100.0, color.alphaF() ); - else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 ) - color.setHsl( value, color.hslSaturation(), color.lightness(), color.alpha() ); - else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 ) - color.setHslF( color.hslHueF(), value / 100.0, color.lightnessF(), color.alphaF() ); - else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 ) - color.setHslF( color.hslHueF(), color.hslSaturationF(), value / 100.0, color.alphaF() ); - else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 ) - color.setCmykF( value / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() ); - else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 ) - color.setCmykF( color.cyanF(), value / 100.0, color.yellowF(), color.blackF(), color.alphaF() ); - else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 ) - color.setCmykF( color.cyanF(), color.magentaF(), value / 100.0, color.blackF(), color.alphaF() ); - else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 ) - color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), value / 100.0, color.alphaF() ); - else - { - parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) ); - return QVariant(); - } - return QgsSymbolLayerUtils::encodeColor( color ); -} - -static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() ); - if ( ! color.isValid() ) - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) ); - return QVariant(); - } - - color = color.darker( getIntValue( values.at( 1 ), parent ) ); - - return QgsSymbolLayerUtils::encodeColor( color ); -} - -static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() ); - if ( ! color.isValid() ) - { - parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) ); - return QVariant(); - } - - color = color.lighter( getIntValue( values.at( 1 ), parent ) ); - - return QgsSymbolLayerUtils::encodeColor( color ); -} - -static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsFeature feat = getFeature( values.at( 0 ), parent ); - QgsGeometry geom = feat.geometry(); - if ( !geom.isNull() ) - return QVariant::fromValue( geom ); - return QVariant(); -} - -static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); - QString sAuthId = getStringValue( values.at( 1 ), parent ); - QString dAuthId = getStringValue( values.at( 2 ), parent ); - - QgsCoordinateReferenceSystem s = QgsCoordinateReferenceSystem::fromOgcWmsCrs( sAuthId ); - if ( ! s.isValid() ) - return QVariant::fromValue( fGeom ); - QgsCoordinateReferenceSystem d = QgsCoordinateReferenceSystem::fromOgcWmsCrs( dAuthId ); - if ( ! d.isValid() ) - return QVariant::fromValue( fGeom ); - - QgsCoordinateTransform t( s, d ); - try - { - if ( fGeom.transform( t ) == 0 ) - return QVariant::fromValue( fGeom ); - } - catch ( QgsCsException &cse ) - { - QgsMessageLog::logMessage( QStringLiteral( "Transform error caught in transform() function: %1" ).arg( cse.what() ) ); - return QVariant(); - } - return QVariant(); -} - - -static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - //arguments: 1. layer id / name, 2. key attribute, 3. eq value - QgsVectorLayer *vl = getVectorLayer( values.at( 0 ), parent ); - - //no layer found - if ( !vl ) - { - return QVariant(); - } - - QString attribute = getStringValue( values.at( 1 ), parent ); - int attributeId = vl->fields().lookupField( attribute ); - if ( attributeId == -1 ) - { - return QVariant(); - } - - const QVariant &attVal = values.at( 2 ); - QgsFeatureRequest req; - req.setFilterExpression( QStringLiteral( "%1=%2" ).arg( QgsExpression::quotedColumnRef( attribute ), - QgsExpression::quotedString( attVal.toString() ) ) ); - req.setLimit( 1 ); - if ( !parent->needsGeometry() ) - { - req.setFlags( QgsFeatureRequest::NoGeometry ); - } - QgsFeatureIterator fIt = vl->getFeatures( req ); - - QgsFeature fet; - if ( fIt.nextFeature( fet ) ) - return QVariant::fromValue( fet ); - - return QVariant(); -} - -static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString layerIdOrName = getStringValue( values.at( 0 ), parent ); - - //try to find a matching layer by name - QgsMapLayer *layer = QgsProject::instance()->mapLayer( layerIdOrName ); //search by id first - if ( !layer ) - { - QList layersByName = QgsProject::instance()->mapLayersByName( layerIdOrName ); - if ( !layersByName.isEmpty() ) - { - layer = layersByName.at( 0 ); - } - } - - if ( !layer ) - return QVariant(); - - QString layerProperty = getStringValue( values.at( 1 ), parent ); - if ( QString::compare( layerProperty, QStringLiteral( "name" ), Qt::CaseInsensitive ) == 0 ) - return layer->name(); - else if ( QString::compare( layerProperty, QStringLiteral( "id" ), Qt::CaseInsensitive ) == 0 ) - return layer->id(); - else if ( QString::compare( layerProperty, QStringLiteral( "title" ), Qt::CaseInsensitive ) == 0 ) - return layer->title(); - else if ( QString::compare( layerProperty, QStringLiteral( "abstract" ), Qt::CaseInsensitive ) == 0 ) - return layer->abstract(); - else if ( QString::compare( layerProperty, QStringLiteral( "keywords" ), Qt::CaseInsensitive ) == 0 ) - return layer->keywordList(); - else if ( QString::compare( layerProperty, QStringLiteral( "data_url" ), Qt::CaseInsensitive ) == 0 ) - return layer->dataUrl(); - else if ( QString::compare( layerProperty, QStringLiteral( "attribution" ), Qt::CaseInsensitive ) == 0 ) - return layer->attribution(); - else if ( QString::compare( layerProperty, QStringLiteral( "attribution_url" ), Qt::CaseInsensitive ) == 0 ) - return layer->attributionUrl(); - else if ( QString::compare( layerProperty, QStringLiteral( "source" ), Qt::CaseInsensitive ) == 0 ) - return layer->publicSource(); - else if ( QString::compare( layerProperty, QStringLiteral( "min_scale" ), Qt::CaseInsensitive ) == 0 ) - return layer->minimumScale(); - else if ( QString::compare( layerProperty, QStringLiteral( "max_scale" ), Qt::CaseInsensitive ) == 0 ) - return layer->maximumScale(); - else if ( QString::compare( layerProperty, QStringLiteral( "crs" ), Qt::CaseInsensitive ) == 0 ) - return layer->crs().authid(); - else if ( QString::compare( layerProperty, QStringLiteral( "crs_definition" ), Qt::CaseInsensitive ) == 0 ) - return layer->crs().toProj4(); - else if ( QString::compare( layerProperty, QStringLiteral( "extent" ), Qt::CaseInsensitive ) == 0 ) - { - QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() ); - QVariant result = QVariant::fromValue( extentGeom ); - return result; - } - else if ( QString::compare( layerProperty, QStringLiteral( "type" ), Qt::CaseInsensitive ) == 0 ) - { - switch ( layer->type() ) - { - case QgsMapLayer::VectorLayer: - return QCoreApplication::translate( "expressions", "Vector" ); - case QgsMapLayer::RasterLayer: - return QCoreApplication::translate( "expressions", "Raster" ); - case QgsMapLayer::PluginLayer: - return QCoreApplication::translate( "expressions", "Plugin" ); - } - } - else - { - //vector layer methods - QgsVectorLayer *vLayer = dynamic_cast< QgsVectorLayer * >( layer ); - if ( vLayer ) - { - if ( QString::compare( layerProperty, QStringLiteral( "storage_type" ), Qt::CaseInsensitive ) == 0 ) - return vLayer->storageType(); - else if ( QString::compare( layerProperty, QStringLiteral( "geometry_type" ), Qt::CaseInsensitive ) == 0 ) - return QgsWkbTypes::geometryDisplayString( vLayer->geometryType() ); - else if ( QString::compare( layerProperty, QStringLiteral( "feature_count" ), Qt::CaseInsensitive ) == 0 ) - return QVariant::fromValue( vLayer->featureCount() ); - } - } - - 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 = QgsProject::instance()->mapLayer( layerIdOrName ); //search by id first - if ( !layer ) - { - QList layersByName = QgsProject::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; -} - -static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - return getListValue( values.at( 0 ), parent ).length(); -} - -static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - return QVariant( getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) ); -} - -static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - return getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) ); -} - -static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - const QVariantList list = getListValue( values.at( 0 ), parent ); - const qlonglong pos = getIntValue( values.at( 1 ), parent ); - if ( pos < 0 || pos >= list.length() ) return QVariant(); - return list.at( pos ); -} - -static QVariant convertToSameType( const QVariant &value, QVariant::Type type ) -{ - QVariant result = value; - result.convert( type ); - return result; -} - -static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariantList list = getListValue( values.at( 0 ), parent ); - list.append( values.at( 1 ) ); - return convertToSameType( list, values.at( 0 ).type() ); -} - -static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariantList list = getListValue( values.at( 0 ), parent ); - list.prepend( values.at( 1 ) ); - return convertToSameType( list, values.at( 0 ).type() ); -} - -static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariantList list = getListValue( values.at( 0 ), parent ); - list.insert( getIntValue( values.at( 1 ), parent ), values.at( 2 ) ); - return convertToSameType( list, values.at( 0 ).type() ); -} - -static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariantList list = getListValue( values.at( 0 ), parent ); - list.removeAt( getIntValue( values.at( 1 ), parent ) ); - return convertToSameType( list, values.at( 0 ).type() ); -} - -static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariantList list = getListValue( values.at( 0 ), parent ); - list.removeAll( values.at( 1 ) ); - return convertToSameType( list, values.at( 0 ).type() ); -} - -static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariantList list; - Q_FOREACH ( const QVariant &cur, values ) - { - list += getListValue( cur, parent ); - } - return convertToSameType( list, values.at( 0 ).type() ); -} - -static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - const QVariantList array1 = getListValue( values.at( 0 ), parent ); - Q_FOREACH ( const QVariant &cur, getListValue( values.at( 1 ), parent ) ) - { - if ( array1.contains( cur ) ) - return QVariant( true ); - } - return QVariant( false ); -} - - -static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariantList array = getListValue( values.at( 0 ), parent ); - - QVariantList distinct; - - for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it ) - { - if ( !distinct.contains( *it ) ) - { - distinct += ( *it ); - } - } - - return distinct; -} - -static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariantList array = getListValue( values.at( 0 ), parent ); - QString delimiter = getStringValue( values.at( 1 ), parent ); - QString empty = getStringValue( values.at( 2 ), parent ); - - QString str; - - for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it ) - { - str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty; - if ( it != ( array.constEnd() - 1 ) ) - { - str += delimiter; - } - } - - return QVariant( str ); -} - -static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QString str = getStringValue( values.at( 0 ), parent ); - QString delimiter = getStringValue( values.at( 1 ), parent ); - QString empty = getStringValue( values.at( 2 ), parent ); - - QStringList list = str.split( delimiter ); - QVariantList array; - - for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it ) - { - array += ( !( *it ).isEmpty() ) ? *it : empty; - } - - return array; -} - -static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariantMap result; - for ( int i = 0; i + 1 < values.length(); i += 2 ) - { - result.insert( getStringValue( values.at( i ), parent ), values.at( i + 1 ) ); - } - return result; -} - -static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - return getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() ); -} - -static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - return getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() ); -} - -static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariantMap map = getMapValue( values.at( 0 ), parent ); - map.remove( values.at( 1 ).toString() ); - return map; -} - -static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariantMap map = getMapValue( values.at( 0 ), parent ); - map.insert( values.at( 1 ).toString(), values.at( 2 ) ); - return map; -} - -static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - QVariantMap result; - Q_FOREACH ( const QVariant &cur, values ) - { - const QVariantMap curMap = getMapValue( cur, parent ); - for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it ) - result.insert( it.key(), it.value() ); - } - return result; -} - -static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - return QStringList( getMapValue( values.at( 0 ), parent ).keys() ); -} - -static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) -{ - return getMapValue( values.at( 0 ), parent ).values(); -} - -static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression * ) -{ - QString envVarName = values.at( 0 ).toString(); - return QProcessEnvironment::systemEnvironment().value( envVarName ); -} - bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership ) { int fnIdx = functionIndex( function->name() ); @@ -3936,557 +87,6 @@ const QStringList &QgsExpression::BuiltinFunctions() QList QgsExpression::sFunctions; QList QgsExpression::sOwnedFunctions; -const QList &QgsExpression::Functions() -{ - // The construction of the list isn't thread-safe, and without the mutex, - // crashes in the WFS provider may occur, since it can parse expressions - // in parallel. - // The mutex needs to be recursive. - static QMutex sFunctionsMutex( QMutex::Recursive ); - QMutexLocker locker( &sFunctionsMutex ); - - if ( sFunctions.isEmpty() ) - { - QgsExpressionFunction::ParameterList aggParams = QgsExpressionFunction::ParameterList() - << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "group_by" ), true ) - << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true ); - - sFunctions - << new QgsStaticExpressionFunction( QStringLiteral( "sqrt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnSqrt, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "radians" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "degrees" ) ), fcnRadians, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "degrees" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "radians" ) ), fcnDegrees, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "azimuth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnAzimuth, QStringList() << QStringLiteral( "Math" ) << QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "inclination" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnInclination, QStringList() << QStringLiteral( "Math" ) << QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "project" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "elevation" ), true, M_PI / 2 ), fcnProject, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "abs" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAbs, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "cos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnCos, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "sin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnSin, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "tan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnTan, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "asin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAsin, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "acos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAcos, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "atan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAtan, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "atan2" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ), fcnAtan2, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnExp, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "ln" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLn, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "log10" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog10, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "log" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "base" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "round" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ), fcnRound, QStringLiteral( "Math" ) ); - - QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( QStringLiteral( "rand" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnRnd, QStringLiteral( "Math" ) ); - randFunc->setIsStatic( false ); - sFunctions << randFunc; - - QgsStaticExpressionFunction *randfFunc = new QgsStaticExpressionFunction( QStringLiteral( "randf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ), true, 0.0 ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ), true, 1.0 ), fcnRndF, QStringLiteral( "Math" ) ); - randfFunc->setIsStatic( false ); - sFunctions << randfFunc; - - sFunctions - << new QgsStaticExpressionFunction( QStringLiteral( "max" ), -1, fcnMax, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "min" ), -1, fcnMin, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "clamp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnClamp, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "scale_linear" ), 5, fcnLinearScale, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "scale_exp" ), 6, fcnExpScale, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "floor" ), 1, fcnFloor, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "ceil" ), 1, fcnCeil, QStringLiteral( "Math" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "pi" ), 0, fcnPi, QStringLiteral( "Math" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "$pi" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "to_int" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInt, QStringLiteral( "Conversions" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "toint" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "to_real" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToReal, QStringLiteral( "Conversions" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "toreal" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "to_string" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToString, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "String" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "tostring" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "to_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToDateTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "todatetime" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "to_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToDate, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "todate" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "to_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "totime" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "to_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInterval, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "tointerval" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "coalesce" ), -1, fcnCoalesce, QStringLiteral( "Conditionals" ), QString(), false, QSet(), false, QStringList(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "if" ), 3, fcnIf, QStringLiteral( "Conditionals" ), QString(), False, QSet(), true ) - - << new QgsStaticExpressionFunction( QStringLiteral( "aggregate" ), - QgsExpressionFunction::ParameterList() - << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true ) - << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true ), - fcnAggregate, - QStringLiteral( "Aggregates" ), - QString(), - []( const QgsExpressionNodeFunction * node ) - { - // usesGeometry callback: return true if @parent variable is referenced - - if ( !node ) - return true; - - if ( !node->args() ) - return false; - - QSet referencedVars; - if ( node->args()->count() > 2 ) - { - QgsExpressionNode *subExpressionNode = node->args()->at( 2 ); - referencedVars = subExpressionNode->referencedVariables(); - } - - if ( node->args()->count() > 3 ) - { - QgsExpressionNode *filterNode = node->args()->at( 3 ); - referencedVars.unite( filterNode->referencedVariables() ); - } - return referencedVars.contains( "parent" ) || referencedVars.contains( QString() ); - }, - []( const QgsExpressionNodeFunction * node ) - { - // referencedColumns callback: return AllAttributes if @parent variable is referenced - - if ( !node ) - return QSet() << QgsFeatureRequest::ALL_ATTRIBUTES; - - if ( !node->args() ) - return QSet(); - - QSet referencedCols; - QSet referencedVars; - - if ( node->args()->count() > 2 ) - { - QgsExpressionNode *subExpressionNode = node->args()->at( 2 ); - referencedVars = subExpressionNode->referencedVariables(); - referencedCols = subExpressionNode->referencedColumns(); - } - if ( node->args()->count() > 3 ) - { - QgsExpressionNode *filterNode = node->args()->at( 3 ); - referencedVars = filterNode->referencedVariables(); - referencedCols.unite( filterNode->referencedColumns() ); - } - - if ( referencedVars.contains( "parent" ) || referencedVars.contains( QString() ) ) - return QSet() << QgsFeatureRequest::ALL_ATTRIBUTES; - else - return referencedCols; - }, - true - ) - - << new QgsStaticExpressionFunction( QStringLiteral( "relation_aggregate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "relation" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true ), - fcnAggregateRelation, QStringLiteral( "Aggregates" ), QString(), False, QSet() << QgsFeatureRequest::ALL_ATTRIBUTES, true ) - - << new QgsStaticExpressionFunction( QStringLiteral( "count" ), aggParams, fcnAggregateCount, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "count_distinct" ), aggParams, fcnAggregateCountDistinct, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "count_missing" ), aggParams, fcnAggregateCountMissing, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "minimum" ), aggParams, fcnAggregateMin, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "maximum" ), aggParams, fcnAggregateMax, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "sum" ), aggParams, fcnAggregateSum, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "mean" ), aggParams, fcnAggregateMean, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "median" ), aggParams, fcnAggregateMedian, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "stdev" ), aggParams, fcnAggregateStdev, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "range" ), aggParams, fcnAggregateRange, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "minority" ), aggParams, fcnAggregateMinority, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "majority" ), aggParams, fcnAggregateMajority, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "q1" ), aggParams, fcnAggregateQ1, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "q3" ), aggParams, fcnAggregateQ3, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "iqr" ), aggParams, fcnAggregateIQR, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "min_length" ), aggParams, fcnAggregateMinLength, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "max_length" ), aggParams, fcnAggregateMaxLength, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "collect" ), aggParams, fcnAggregateCollectGeometry, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "concatenate" ), aggParams << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true ), fcnAggregateStringConcat, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) - - << new QgsStaticExpressionFunction( QStringLiteral( "regexp_match" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpMatch, QStringList() << QStringLiteral( "Conditionals" ) << QStringLiteral( "String" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "regexp_matches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnRegexpMatches, QStringLiteral( "Arrays" ) ) - - << new QgsStaticExpressionFunction( QStringLiteral( "now" ), 0, fcnNow, QStringLiteral( "Date and Time" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "$now" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "age" ), 2, fcnAge, QStringLiteral( "Date and Time" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "year" ), 1, fcnYear, QStringLiteral( "Date and Time" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "month" ), 1, fcnMonth, QStringLiteral( "Date and Time" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "week" ), 1, fcnWeek, QStringLiteral( "Date and Time" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "day" ), 1, fcnDay, QStringLiteral( "Date and Time" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "hour" ), 1, fcnHour, QStringLiteral( "Date and Time" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "minute" ), 1, fcnMinute, QStringLiteral( "Date and Time" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "second" ), 1, fcnSeconds, QStringLiteral( "Date and Time" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnEpoch, QStringLiteral( "Date and Time" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "day_of_week" ), 1, fcnDayOfWeek, QStringLiteral( "Date and Time" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "lower" ), 1, fcnLower, QStringLiteral( "String" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "upper" ), 1, fcnUpper, QStringLiteral( "String" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "title" ), 1, fcnTitle, QStringLiteral( "String" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "trim" ), 1, fcnTrim, QStringLiteral( "String" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "levenshtein" ), 2, fcnLevenshtein, QStringLiteral( "Fuzzy Matching" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "longest_common_substring" ), 2, fcnLCS, QStringLiteral( "Fuzzy Matching" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "hamming_distance" ), 2, fcnHamming, QStringLiteral( "Fuzzy Matching" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "soundex" ), 1, fcnSoundex, QStringLiteral( "Fuzzy Matching" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "char" ), 1, fcnChar, QStringLiteral( "String" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "wordwrap" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "" ), fcnWordwrap, QStringLiteral( "String" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ), true, "" ), fcnLength, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "regexp_replace" ), 3, fcnRegexpReplace, QStringLiteral( "String" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "regexp_substr" ), 2, fcnRegexpSubstr, QStringLiteral( "String" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start " ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ), true ), fcnSubstr, QStringLiteral( "String" ), QString(), - false, QSet< QString >(), false, QStringList(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "concat" ), -1, fcnConcat, QStringLiteral( "String" ), QString(), false, QSet(), false, QStringList(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "strpos" ), 2, fcnStrpos, QStringLiteral( "String" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "left" ), 2, fcnLeft, QStringLiteral( "String" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "right" ), 2, fcnRight, QStringLiteral( "String" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "rpad" ), 3, fcnRPad, QStringLiteral( "String" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "lpad" ), 3, fcnLPad, QStringLiteral( "String" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "format" ), -1, fcnFormatString, QStringLiteral( "String" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "format_number" ), 2, fcnFormatNumber, QStringLiteral( "String" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "format_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ) ), fcnFormatDate, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "Date and Time" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "color_rgb" ), 3, fcnColorRgb, QStringLiteral( "Color" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "color_rgba" ), 4, fncColorRgba, QStringLiteral( "Color" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color" ), 2, fcnRampColor, QStringLiteral( "Color" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "color_hsl" ), 3, fcnColorHsl, QStringLiteral( "Color" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "color_hsla" ), 4, fncColorHsla, QStringLiteral( "Color" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "color_hsv" ), 3, fcnColorHsv, QStringLiteral( "Color" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "color_hsva" ), 4, fncColorHsva, QStringLiteral( "Color" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyk" ), 4, fcnColorCmyk, QStringLiteral( "Color" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyka" ), 5, fncColorCmyka, QStringLiteral( "Color" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "color_part" ), 2, fncColorPart, QStringLiteral( "Color" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "darker" ), 2, fncDarker, QStringLiteral( "Color" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "lighter" ), 2, fncLighter, QStringLiteral( "Color" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "set_color_part" ), 3, fncSetColorPart, QStringLiteral( "Color" ) ); - - QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( QStringLiteral( "$geometry" ), 0, fcnGeometry, QStringLiteral( "GeometryGroup" ), QString(), true ); - geomFunc->setIsStatic( false ); - sFunctions << geomFunc; - - QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( QStringLiteral( "$area" ), 0, fcnGeomArea, QStringLiteral( "GeometryGroup" ), QString(), true ); - areaFunc->setIsStatic( false ); - sFunctions << areaFunc; - - sFunctions << new QgsStaticExpressionFunction( QStringLiteral( "area" ), 1, fcnArea, QStringLiteral( "GeometryGroup" ) ); - - QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( QStringLiteral( "$length" ), 0, fcnGeomLength, QStringLiteral( "GeometryGroup" ), QString(), true ); - lengthFunc->setIsStatic( false ); - sFunctions << lengthFunc; - - QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( QStringLiteral( "$perimeter" ), 0, fcnGeomPerimeter, QStringLiteral( "GeometryGroup" ), QString(), true ); - perimeterFunc->setIsStatic( false ); - sFunctions << perimeterFunc; - - sFunctions << new QgsStaticExpressionFunction( QStringLiteral( "perimeter" ), 1, fcnPerimeter, QStringLiteral( "GeometryGroup" ) ); - - QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x" ), 0, fcnX, QStringLiteral( "GeometryGroup" ), QString(), true ); - xFunc->setIsStatic( false ); - sFunctions << xFunc; - - QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y" ), 0, fcnY, QStringLiteral( "GeometryGroup" ), QString(), true ); - yFunc->setIsStatic( false ); - sFunctions << yFunc; - - sFunctions - << new QgsStaticExpressionFunction( QStringLiteral( "x" ), 1, fcnGeomX, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "y" ), 1, fcnGeomY, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "z" ), 1, fcnGeomZ, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "m" ), 1, fcnGeomM, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "point_n" ), 2, fcnPointN, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "start_point" ), 1, fcnStartPoint, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "end_point" ), 1, fcnEndPoint, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "nodes_to_points" ), -1, fcnNodesToPoints, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "segments_to_lines" ), 1, fcnSegmentsToLines, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "make_point" ), -1, fcnMakePoint, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "make_point_m" ), 3, fcnMakePointM, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "make_line" ), -1, fcnMakeLine, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "make_polygon" ), -1, fcnMakePolygon, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "make_triangle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), - fcnMakeTriangle, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "make_circle" ), QgsExpressionFunction::ParameterList() - << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ), - fcnMakeCircle, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "make_ellipse" ), QgsExpressionFunction::ParameterList() - << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "semi_major_axis" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "semi_minor_axis" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ), - fcnMakeEllipse, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "make_regular_polygon" ), QgsExpressionFunction::ParameterList() - << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "number_sides" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "circle" ), true, 0 ), - fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) ); - - QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x_at" ), 1, fcnXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet(), false, QStringList() << QStringLiteral( "xat" ) << QStringLiteral( "x_at" ) ); - xAtFunc->setIsStatic( false ); - sFunctions << xAtFunc; - - QgsStaticExpressionFunction *yAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y_at" ), 1, fcnYat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet(), false, QStringList() << QStringLiteral( "yat" ) << QStringLiteral( "y_at" ) ); - yAtFunc->setIsStatic( false ); - sFunctions << yAtFunc; - - sFunctions - << new QgsStaticExpressionFunction( QStringLiteral( "x_min" ), 1, fcnXMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "xmin" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "x_max" ), 1, fcnXMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "xmax" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "y_min" ), 1, fcnYMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "ymin" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "y_max" ), 1, fcnYMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "ymax" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkt" ), 1, fcnGeomFromWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "geomFromWKT" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_gml" ), 1, fcnGeomFromGML, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "geomFromGML" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "relate" ), -1, fcnRelate, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "intersects_bbox" ), 2, fcnBbox, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "bbox" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "disjoint" ), 2, fcnDisjoint, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "intersects" ), 2, fcnIntersects, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "touches" ), 2, fcnTouches, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "crosses" ), 2, fcnCrosses, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "contains" ), 2, fcnContains, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "overlaps" ), 2, fcnOverlaps, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "within" ), 2, fcnWithin, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "translate" ), 3, fcnTranslate, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "buffer" ), -1, fcnBuffer, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "offset_curve" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 ) - << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QgsGeometry::JoinStyleRound ) - << QgsExpressionFunction::Parameter( QStringLiteral( "mitre_limit" ), true, 2.0 ), - fcnOffsetCurve, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "single_sided_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 ) - << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QgsGeometry::JoinStyleRound ) - << QgsExpressionFunction::Parameter( QStringLiteral( "mitre_limit" ), true, 2.0 ), - fcnSingleSidedBuffer, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "extend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ), - fcnExtend, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "centroid" ), 1, fcnCentroid, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "point_on_surface" ), 1, fcnPointOnSurface, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "pole_of_inaccessibility" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnPoleOfInaccessibility, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "reverse" ), 1, fcnReverse, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "exterior_ring" ), 1, fcnExteriorRing, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "interior_ring_n" ), 2, fcnInteriorRingN, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "geometry_n" ), 2, fcnGeometryN, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "boundary" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundary, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "line_merge" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLineMerge, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "bounds" ), 1, fcnBounds, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "simplify" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplify, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "simplify_vw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplifyVW, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "smooth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "iterations" ), true, 1 ) - << QgsExpressionFunction::Parameter( QStringLiteral( "offset" ), true, 0.25 ) - << QgsExpressionFunction::Parameter( QStringLiteral( "min_length" ), true, -1 ) - << QgsExpressionFunction::Parameter( QStringLiteral( "max_angle" ), true, 180 ), fcnSmooth, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "num_points" ), 1, fcnGeomNumPoints, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "num_interior_rings" ), 1, fcnGeomNumInteriorRings, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "num_rings" ), 1, fcnGeomNumRings, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "num_geometries" ), 1, fcnGeomNumGeometries, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "bounds_width" ), 1, fcnBoundsWidth, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "bounds_height" ), 1, fcnBoundsHeight, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "is_closed" ), 1, fcnIsClosed, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "convex_hull" ), 1, fcnConvexHull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "convexHull" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "difference" ), 2, fcnDifference, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "distance" ), 2, fcnDistance, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "intersection" ), 2, fcnIntersection, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "sym_difference" ), 2, fcnSymDifference, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "symDifference" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "combine" ), 2, fcnCombine, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "union" ), 2, fcnCombine, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkt" ), -1, fcnGeomToWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "geomToWKT" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "geometry" ), 1, fcnGetGeometry, QStringLiteral( "GeometryGroup" ), QString(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "transform" ), 3, fcnTransformGeometry, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "extrude" ), 3, fcnExtrude, QStringLiteral( "GeometryGroup" ), QString() );; - - QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction( QStringLiteral( "order_parts" ), 3, fcnOrderParts, QStringLiteral( "GeometryGroup" ), QString() ); - - orderPartsFunc->setIsStaticFunction( - []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context ) - { - Q_FOREACH ( QgsExpressionNode *argNode, node->args()->list() ) - { - if ( !argNode->isStatic( parent, context ) ) - return false; - } - - if ( node->args()->count() > 1 ) - { - QgsExpressionNode *argNode = node->args()->at( 1 ); - - QString expString = argNode->eval( parent, context ).toString(); - - QgsExpression e( expString ); - - if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) ) - return true; - } - - return true; - } ); - - orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context ) - { - if ( node->args()->count() > 1 ) - { - QgsExpressionNode *argNode = node->args()->at( 1 ); - QString expression = argNode->eval( parent, context ).toString(); - QgsExpression e( expression ); - e.prepare( context ); - context->setCachedValue( expression, QVariant::fromValue( e ) ); - } - return true; - } - ); - sFunctions << orderPartsFunc; - - sFunctions - << new QgsStaticExpressionFunction( QStringLiteral( "closest_point" ), 2, fcnClosestPoint, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "shortest_line" ), 2, fcnShortestLine, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolatePoint, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_angle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolateAngle, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnLineLocatePoint, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "angle_at_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnAngleAtVertex, QStringLiteral( "GeometryGroup" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "distance_to_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) - << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnDistanceToVertex, QStringLiteral( "GeometryGroup" ) ); - - - // **Record** functions - - QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( QStringLiteral( "$id" ), 0, fcnFeatureId, QStringLiteral( "Record" ) ); - idFunc->setIsStatic( false ); - sFunctions << idFunc; - - QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( QStringLiteral( "$currentfeature" ), 0, fcnFeature, QStringLiteral( "Record" ) ); - currentFeatureFunc->setIsStatic( false ); - sFunctions << currentFeatureFunc; - - QgsStaticExpressionFunction *uuidFunc = new QgsStaticExpressionFunction( QStringLiteral( "uuid" ), 0, fcnUuid, QStringLiteral( "Record" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "$uuid" ) ); - uuidFunc->setIsStatic( true ); - sFunctions << uuidFunc; - - sFunctions - << new QgsStaticExpressionFunction( QStringLiteral( "get_feature" ), 3, fcnGetFeature, QStringLiteral( "Record" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "getFeature" ) ); - - QgsStaticExpressionFunction *isSelectedFunc = new QgsStaticExpressionFunction( - QStringLiteral( "is_selected" ), - -1, - fcnIsSelected, - QStringLiteral( "Record" ), - QString(), - false, - QSet() - ); - isSelectedFunc->setIsStatic( false ); - sFunctions << isSelectedFunc; - - sFunctions - << new QgsStaticExpressionFunction( - QStringLiteral( "num_selected" ), - -1, - fcnNumSelected, - QStringLiteral( "Record" ), - QString(), - false, - QSet() - ) - - // **General** 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" ) ); - - // **var** function - QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( QStringLiteral( "var" ), 1, fcnGetVariable, QStringLiteral( "General" ) ); - varFunction->setIsStaticFunction( - []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context ) - { - /* A variable node is static if it has a static name and the name can be found at prepare - * time and is tagged with isStatic. - * It is not static if a variable is set during iteration or not tagged isStatic. - * (e.g. geom_part variable) - */ - if ( node->args()->count() > 0 ) - { - QgsExpressionNode *argNode = node->args()->at( 0 ); - - if ( !argNode->isStatic( parent, context ) ) - return false; - - QString varName = argNode->eval( parent, context ).toString(); - - const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName ); - return scope ? scope->isStatic( varName ) : false; - } - return false; - } - ); - - sFunctions - << varFunction; - QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( QStringLiteral( "eval" ), 1, fcnEval, QStringLiteral( "General" ), QString(), true, QSet() << QgsFeatureRequest::ALL_ATTRIBUTES ); - evalFunc->setIsStaticFunction( - []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context ) - { - if ( node->args()->count() > 0 ) - { - QgsExpressionNode *argNode = node->args()->at( 0 ); - - if ( argNode->isStatic( parent, context ) ) - { - QString expString = argNode->eval( parent, context ).toString(); - - QgsExpression e( expString ); - - if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) ) - return true; - } - } - - return false; - } ); - - sFunctions << evalFunc; - - sFunctions - << new QgsStaticExpressionFunction( QStringLiteral( "env" ), 1, fcnEnvVar, QStringLiteral( "General" ), QString() ) - << new QgsStaticExpressionFunction( QStringLiteral( "attribute" ), 2, fcnAttribute, QStringLiteral( "Record" ), QString(), false, QSet() << QgsFeatureRequest::ALL_ATTRIBUTES ) - - // functions for arrays - << new QgsStaticExpressionFunction( QStringLiteral( "array" ), -1, fcnArray, QStringLiteral( "Arrays" ), QString(), false, QSet(), false, QStringList(), true ) - << new QgsStaticExpressionFunction( QStringLiteral( "array_length" ), 1, fcnArrayLength, QStringLiteral( "Arrays" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "array_contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayContains, QStringLiteral( "Arrays" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "array_find" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayFind, QStringLiteral( "Arrays" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "array_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayGet, QStringLiteral( "Arrays" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "array_append" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayAppend, QStringLiteral( "Arrays" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "array_prepend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayPrepend, QStringLiteral( "Arrays" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "array_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayInsert, QStringLiteral( "Arrays" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayRemoveAt, QStringLiteral( "Arrays" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayRemoveAll, QStringLiteral( "Arrays" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "array_cat" ), -1, fcnArrayCat, QStringLiteral( "Arrays" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "array_intersect" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array2" ) ), fcnArrayIntersect, QStringLiteral( "Arrays" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "array_distinct" ), 1, fcnArrayDistinct, QStringLiteral( "Arrays" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "array_to_string" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "," ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnArrayToString, QStringLiteral( "Arrays" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "string_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "," ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnStringToArray, QStringLiteral( "Arrays" ) ) - - //functions for maps - << new QgsStaticExpressionFunction( QStringLiteral( "map" ), -1, fcnMap, QStringLiteral( "Maps" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "map_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapGet, QStringLiteral( "Maps" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "map_exist" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapExist, QStringLiteral( "Maps" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "map_delete" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapDelete, QStringLiteral( "Maps" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "map_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnMapInsert, QStringLiteral( "Maps" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "map_concat" ), -1, fcnMapConcat, QStringLiteral( "Maps" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "map_akeys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAKeys, QStringLiteral( "Maps" ) ) - << new QgsStaticExpressionFunction( QStringLiteral( "map_avals" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAVals, QStringLiteral( "Maps" ) ) - ; - - QgsExpressionContextUtils::registerContextFunctions(); - - //QgsExpression has ownership of all built-in functions - Q_FOREACH ( QgsExpressionFunction *func, sFunctions ) - { - sOwnedFunctions << func; - sBuiltinFunctions << func->name(); - sBuiltinFunctions.append( func->aliases() ); - } - } - return sFunctions; -} - bool QgsExpression::checkExpression( const QString &text, const QgsExpressionContext *context, QString &errorMessage ) { QgsExpression exp( text ); @@ -4657,14 +257,6 @@ QSet QgsExpression::referencedVariables() const return d->mRootNode->referencedVariables(); } -bool QgsExpressionNodeInOperator::needsGeometry() const -{ - bool needs = false; - Q_FOREACH ( QgsExpressionNode *n, mList->list() ) - needs |= n->needsGeometry(); - return needs; -} - QSet QgsExpression::referencedAttributeIndexes( const QgsFields &fields ) const { if ( !d->mRootNode ) @@ -4893,1359 +485,6 @@ double QgsExpression::evaluateToDouble( const QString &text, const double fallba } -/////////////////////////////////////////////// -// nodes - -QgsExpressionNode::NodeList::~NodeList() -{ - qDeleteAll( mList ); -} - -void QgsExpressionNode::NodeList::append( QgsExpressionNode::NamedNode *node ) -{ - mList.append( node->node ); - mNameList.append( node->name.toLower() ); - mHasNamedNodes = true; - delete node; -} - -QgsExpressionNode::NodeList *QgsExpressionNode::NodeList::clone() const -{ - NodeList *nl = new NodeList; - Q_FOREACH ( QgsExpressionNode *node, mList ) - { - nl->mList.append( node->clone() ); - } - nl->mNameList = mNameList; - - return nl; -} - -QString QgsExpressionNode::NodeList::dump() const -{ - QString msg; - bool first = true; - Q_FOREACH ( QgsExpressionNode *n, mList ) - { - if ( !first ) msg += QLatin1String( ", " ); - else first = false; - msg += n->dump(); - } - return msg; -} - - -// - -QVariant QgsExpressionNodeUnaryOperator::evalNode( QgsExpression *parent, const QgsExpressionContext *context ) -{ - QVariant val = mOperand->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - - switch ( mOp ) - { - case uoNot: - { - TVL tvl = getTVLValue( val, parent ); - ENSURE_NO_EVAL_ERROR; - return tvl2variant( NOT[tvl] ); - } - - case uoMinus: - if ( isIntSafe( val ) ) - return QVariant( - getIntValue( val, parent ) ); - else if ( isDoubleSafe( val ) ) - return QVariant( - getDoubleValue( val, parent ) ); - else - SET_EVAL_ERROR( tr( "Unary minus only for numeric values." ) ); - default: - Q_ASSERT( false && "unknown unary operation" ); - } - return QVariant(); -} - -QgsExpressionNode::NodeType QgsExpressionNodeUnaryOperator::nodeType() const -{ - return ntUnaryOperator; -} - -bool QgsExpressionNodeUnaryOperator::prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) -{ - return mOperand->prepare( parent, context ); -} - -QString QgsExpressionNodeUnaryOperator::dump() const -{ - return QStringLiteral( "%1 %2" ).arg( UNARY_OPERATOR_TEXT[mOp], mOperand->dump() ); -} - -QSet QgsExpressionNodeUnaryOperator::referencedColumns() const -{ - return mOperand->referencedColumns(); -} - -QSet QgsExpressionNodeUnaryOperator::referencedVariables() const -{ - return mOperand->referencedVariables(); -} - -bool QgsExpressionNodeUnaryOperator::needsGeometry() const -{ - return mOperand->needsGeometry(); -} - -QgsExpressionNode *QgsExpressionNodeUnaryOperator::clone() const -{ - QgsExpressionNodeUnaryOperator *copy = new QgsExpressionNodeUnaryOperator( mOp, mOperand->clone() ); - cloneTo( copy ); - return copy; -} - -bool QgsExpressionNodeUnaryOperator::isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const -{ - return mOperand->isStatic( parent, context ); -} - -QString QgsExpressionNodeUnaryOperator::text() const -{ - return UNARY_OPERATOR_TEXT[mOp]; -} - -// - -QVariant QgsExpressionNodeBinaryOperator::evalNode( QgsExpression *parent, const QgsExpressionContext *context ) -{ - QVariant vL = mOpLeft->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - QVariant vR = mOpRight->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - - switch ( mOp ) - { - case boPlus: - if ( vL.type() == QVariant::String && vR.type() == QVariant::String ) - { - QString sL = isNull( vL ) ? QString() : getStringValue( vL, parent ); - ENSURE_NO_EVAL_ERROR; - QString sR = isNull( vR ) ? QString() : getStringValue( vR, parent ); - ENSURE_NO_EVAL_ERROR; - return QVariant( sL + sR ); - } - //intentional fall-through - FALLTHROUGH; - case boMinus: - case boMul: - case boDiv: - case boMod: - { - if ( isNull( vL ) || isNull( vR ) ) - return QVariant(); - else if ( mOp != boDiv && isIntSafe( vL ) && isIntSafe( vR ) ) - { - // both are integers - let's use integer arithmetics - qlonglong iL = getIntValue( vL, parent ); - ENSURE_NO_EVAL_ERROR; - qlonglong iR = getIntValue( vR, parent ); - ENSURE_NO_EVAL_ERROR; - - if ( mOp == boMod && iR == 0 ) - return QVariant(); - - return QVariant( computeInt( iL, iR ) ); - } - else if ( isDateTimeSafe( vL ) && isIntervalSafe( vR ) ) - { - QDateTime dL = getDateTimeValue( vL, parent ); - ENSURE_NO_EVAL_ERROR; - QgsInterval iL = getInterval( vR, parent ); - ENSURE_NO_EVAL_ERROR; - if ( mOp == boDiv || mOp == boMul || mOp == boMod ) - { - parent->setEvalErrorString( tr( "Can't perform /, *, or % on DateTime and Interval" ) ); - return QVariant(); - } - return QVariant( computeDateTimeFromInterval( dL, &iL ) ); - } - else if ( mOp == boPlus && ( ( vL.type() == QVariant::Date && vR.type() == QVariant::Time ) || - ( vR.type() == QVariant::Date && vL.type() == QVariant::Time ) ) ) - { - QDate date = getDateValue( vL.type() == QVariant::Date ? vL : vR, parent ); - ENSURE_NO_EVAL_ERROR; - QTime time = getTimeValue( vR.type() == QVariant::Time ? vR : vL, parent ); - ENSURE_NO_EVAL_ERROR; - QDateTime dt = QDateTime( date, time ); - return QVariant( dt ); - } - else if ( mOp == boMinus && vL.type() == QVariant::Date && vR.type() == QVariant::Date ) - { - QDate date1 = getDateValue( vL, parent ); - ENSURE_NO_EVAL_ERROR; - QDate date2 = getDateValue( vR, parent ); - ENSURE_NO_EVAL_ERROR; - return date1 - date2; - } - else if ( mOp == boMinus && vL.type() == QVariant::Time && vR.type() == QVariant::Time ) - { - QTime time1 = getTimeValue( vL, parent ); - ENSURE_NO_EVAL_ERROR; - QTime time2 = getTimeValue( vR, parent ); - ENSURE_NO_EVAL_ERROR; - return time1 - time2; - } - else if ( mOp == boMinus && vL.type() == QVariant::DateTime && vR.type() == QVariant::DateTime ) - { - QDateTime datetime1 = getDateTimeValue( vL, parent ); - ENSURE_NO_EVAL_ERROR; - QDateTime datetime2 = getDateTimeValue( vR, parent ); - ENSURE_NO_EVAL_ERROR; - return datetime1 - datetime2; - } - else - { - // general floating point arithmetic - double fL = getDoubleValue( vL, parent ); - ENSURE_NO_EVAL_ERROR; - double fR = getDoubleValue( vR, parent ); - ENSURE_NO_EVAL_ERROR; - if ( ( mOp == boDiv || mOp == boMod ) && fR == 0. ) - return QVariant(); // silently handle division by zero and return NULL - return QVariant( computeDouble( fL, fR ) ); - } - } - case boIntDiv: - { - //integer division - double fL = getDoubleValue( vL, parent ); - ENSURE_NO_EVAL_ERROR; - double fR = getDoubleValue( vR, parent ); - ENSURE_NO_EVAL_ERROR; - if ( fR == 0. ) - return QVariant(); // silently handle division by zero and return NULL - return QVariant( qFloor( fL / fR ) ); - } - case boPow: - if ( isNull( vL ) || isNull( vR ) ) - return QVariant(); - else - { - double fL = getDoubleValue( vL, parent ); - ENSURE_NO_EVAL_ERROR; - double fR = getDoubleValue( vR, parent ); - ENSURE_NO_EVAL_ERROR; - return QVariant( pow( fL, fR ) ); - } - - case boAnd: - { - TVL tvlL = getTVLValue( vL, parent ), tvlR = getTVLValue( vR, parent ); - ENSURE_NO_EVAL_ERROR; - return tvl2variant( AND[tvlL][tvlR] ); - } - - case boOr: - { - TVL tvlL = getTVLValue( vL, parent ), tvlR = getTVLValue( vR, parent ); - ENSURE_NO_EVAL_ERROR; - return tvl2variant( OR[tvlL][tvlR] ); - } - - case boEQ: - case boNE: - case boLT: - case boGT: - case boLE: - case boGE: - if ( isNull( vL ) || isNull( vR ) ) - { - return TVL_Unknown; - } - else if ( isList( vL ) || isList( vR ) ) - { - // verify that we have two lists - if ( !isList( vL ) || !isList( vR ) ) - return TVL_Unknown; - - // and search for not equal respective items - QVariantList lL = vL.toList(); - QVariantList lR = vR.toList(); - for ( int i = 0; i < lL.length() && i < lR.length(); i++ ) - { - if ( isNull( lL.at( i ) ) && isNull( lR.at( i ) ) ) - continue; // same behavior as PostgreSQL - - if ( isNull( lL.at( i ) ) || isNull( lR.at( i ) ) ) - { - switch ( mOp ) - { - case boEQ: - return false; - case boNE: - return true; - case boLT: - case boLE: - return isNull( lR.at( i ) ); - case boGT: - case boGE: - return isNull( lL.at( i ) ); - default: - Q_ASSERT( false ); - return TVL_Unknown; - } - } - - QgsExpressionNodeLiteral nL( lL.at( i ) ); - QgsExpressionNodeLiteral nR( lR.at( i ) ); - QgsExpressionNodeBinaryOperator eqNode( boEQ, nL.clone(), nR.clone() ); - QVariant eq = eqNode.eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - if ( eq == TVL_False ) - { - // return the two items comparison - QgsExpressionNodeBinaryOperator node( mOp, nL.clone(), nR.clone() ); - QVariant v = node.eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - return v; - } - } - - // default to length comparison - switch ( mOp ) - { - case boEQ: - return lL.length() == lR.length(); - case boNE: - return lL.length() != lR.length(); - case boLT: - return lL.length() < lR.length(); - case boGT: - return lL.length() > lR.length(); - case boLE: - return lL.length() <= lR.length(); - case boGE: - return lL.length() >= lR.length(); - default: - Q_ASSERT( false ); - return TVL_Unknown; - } - } - else if ( isDoubleSafe( vL ) && isDoubleSafe( vR ) && - ( vL.type() != QVariant::String || vR.type() != QVariant::String ) ) - { - // do numeric comparison if both operators can be converted to numbers, - // and they aren't both string - double fL = getDoubleValue( vL, parent ); - ENSURE_NO_EVAL_ERROR; - double fR = getDoubleValue( vR, parent ); - ENSURE_NO_EVAL_ERROR; - return compare( fL - fR ) ? TVL_True : TVL_False; - } - else - { - // do string comparison otherwise - QString sL = getStringValue( vL, parent ); - ENSURE_NO_EVAL_ERROR; - QString sR = getStringValue( vR, parent ); - ENSURE_NO_EVAL_ERROR; - int diff = QString::compare( sL, sR ); - return compare( diff ) ? TVL_True : TVL_False; - } - - case boIs: - case boIsNot: - if ( isNull( vL ) && isNull( vR ) ) // both operators null - return ( mOp == boIs ? TVL_True : TVL_False ); - else if ( isNull( vL ) || isNull( vR ) ) // one operator null - return ( mOp == boIs ? TVL_False : TVL_True ); - else // both operators non-null - { - bool equal = false; - if ( isDoubleSafe( vL ) && isDoubleSafe( vR ) && - ( vL.type() != QVariant::String || vR.type() != QVariant::String ) ) - { - double fL = getDoubleValue( vL, parent ); - ENSURE_NO_EVAL_ERROR; - double fR = getDoubleValue( vR, parent ); - ENSURE_NO_EVAL_ERROR; - equal = qgsDoubleNear( fL, fR ); - } - else - { - QString sL = getStringValue( vL, parent ); - ENSURE_NO_EVAL_ERROR; - QString sR = getStringValue( vR, parent ); - ENSURE_NO_EVAL_ERROR; - equal = QString::compare( sL, sR ) == 0; - } - if ( equal ) - return mOp == boIs ? TVL_True : TVL_False; - else - return mOp == boIs ? TVL_False : TVL_True; - } - - case boRegexp: - case boLike: - case boNotLike: - case boILike: - case boNotILike: - if ( isNull( vL ) || isNull( vR ) ) - return TVL_Unknown; - else - { - QString str = getStringValue( vL, parent ); - ENSURE_NO_EVAL_ERROR; - QString regexp = getStringValue( vR, parent ); - ENSURE_NO_EVAL_ERROR; - // TODO: cache QRegExp in case that regexp is a literal string (i.e. it will stay constant) - bool matches; - if ( mOp == boLike || mOp == boILike || mOp == boNotLike || mOp == boNotILike ) // change from LIKE syntax to regexp - { - QString esc_regexp = QRegExp::escape( regexp ); - // manage escape % and _ - if ( esc_regexp.startsWith( '%' ) ) - { - esc_regexp.replace( 0, 1, QStringLiteral( ".*" ) ); - } - QRegExp rx( "[^\\\\](%)" ); - int pos = 0; - while ( ( pos = rx.indexIn( esc_regexp, pos ) ) != -1 ) - { - esc_regexp.replace( pos + 1, 1, QStringLiteral( ".*" ) ); - pos += 1; - } - rx.setPattern( QStringLiteral( "\\\\%" ) ); - esc_regexp.replace( rx, QStringLiteral( "%" ) ); - if ( esc_regexp.startsWith( '_' ) ) - { - esc_regexp.replace( 0, 1, QStringLiteral( "." ) ); - } - rx.setPattern( QStringLiteral( "[^\\\\](_)" ) ); - pos = 0; - while ( ( pos = rx.indexIn( esc_regexp, pos ) ) != -1 ) - { - esc_regexp.replace( pos + 1, 1, '.' ); - pos += 1; - } - rx.setPattern( QStringLiteral( "\\\\_" ) ); - esc_regexp.replace( rx, QStringLiteral( "_" ) ); - matches = QRegExp( esc_regexp, mOp == boLike || mOp == boNotLike ? Qt::CaseSensitive : Qt::CaseInsensitive ).exactMatch( str ); - } - else - { - matches = QRegExp( regexp ).indexIn( str ) != -1; - } - - if ( mOp == boNotLike || mOp == boNotILike ) - { - matches = !matches; - } - - return matches ? TVL_True : TVL_False; - } - - case boConcat: - if ( isNull( vL ) || isNull( vR ) ) - return QVariant(); - else - { - QString sL = getStringValue( vL, parent ); - ENSURE_NO_EVAL_ERROR; - QString sR = getStringValue( vR, parent ); - ENSURE_NO_EVAL_ERROR; - return QVariant( sL + sR ); - } - - default: - break; - } - Q_ASSERT( false ); - return QVariant(); -} - -bool QgsExpressionNodeBinaryOperator::compare( double diff ) -{ - switch ( mOp ) - { - case boEQ: - return qgsDoubleNear( diff, 0.0 ); - case boNE: - return !qgsDoubleNear( diff, 0.0 ); - case boLT: - return diff < 0; - case boGT: - return diff > 0; - case boLE: - return diff <= 0; - case boGE: - return diff >= 0; - default: - Q_ASSERT( false ); - return false; - } -} - -qlonglong QgsExpressionNodeBinaryOperator::computeInt( qlonglong x, qlonglong y ) -{ - switch ( mOp ) - { - case boPlus: - return x + y; - case boMinus: - return x - y; - case boMul: - return x * y; - case boDiv: - return x / y; - case boMod: - return x % y; - default: - Q_ASSERT( false ); - return 0; - } -} - -QDateTime QgsExpressionNodeBinaryOperator::computeDateTimeFromInterval( const QDateTime &d, QgsInterval *i ) -{ - switch ( mOp ) - { - case boPlus: - return d.addSecs( i->seconds() ); - case boMinus: - return d.addSecs( -i->seconds() ); - default: - Q_ASSERT( false ); - return QDateTime(); - } -} - -double QgsExpressionNodeBinaryOperator::computeDouble( double x, double y ) -{ - switch ( mOp ) - { - case boPlus: - return x + y; - case boMinus: - return x - y; - case boMul: - return x * y; - case boDiv: - return x / y; - case boMod: - return fmod( x, y ); - default: - Q_ASSERT( false ); - return 0; - } -} - -QgsExpressionNode::NodeType QgsExpressionNodeBinaryOperator::nodeType() const -{ - return ntBinaryOperator; -} - -bool QgsExpressionNodeBinaryOperator::prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) -{ - bool resL = mOpLeft->prepare( parent, context ); - bool resR = mOpRight->prepare( parent, context ); - return resL && resR; -} - -int QgsExpressionNodeBinaryOperator::precedence() const -{ - // see left/right in qgsexpressionparser.yy - switch ( mOp ) - { - case boOr: - return 1; - - case boAnd: - return 2; - - case boEQ: - case boNE: - case boLE: - case boGE: - case boLT: - case boGT: - case boRegexp: - case boLike: - case boILike: - case boNotLike: - case boNotILike: - case boIs: - case boIsNot: - return 3; - - case boPlus: - case boMinus: - return 4; - - case boMul: - case boDiv: - case boIntDiv: - case boMod: - return 5; - - case boPow: - return 6; - - case boConcat: - return 7; - } - Q_ASSERT( false && "unexpected binary operator" ); - return -1; -} - -bool QgsExpressionNodeBinaryOperator::leftAssociative() const -{ - // see left/right in qgsexpressionparser.yy - switch ( mOp ) - { - case boOr: - case boAnd: - case boEQ: - case boNE: - case boLE: - case boGE: - case boLT: - case boGT: - case boRegexp: - case boLike: - case boILike: - case boNotLike: - case boNotILike: - case boIs: - case boIsNot: - case boPlus: - case boMinus: - case boMul: - case boDiv: - case boIntDiv: - case boMod: - case boConcat: - return true; - - case boPow: - return false; - } - Q_ASSERT( false && "unexpected binary operator" ); - return false; -} - -QString QgsExpressionNodeBinaryOperator::dump() const -{ - QgsExpressionNodeBinaryOperator *lOp = dynamic_cast( mOpLeft ); - QgsExpressionNodeBinaryOperator *rOp = dynamic_cast( mOpRight ); - QgsExpressionNodeUnaryOperator *ruOp = dynamic_cast( mOpRight ); - - QString rdump( mOpRight->dump() ); - - // avoid dumping "IS (NOT ...)" as "IS NOT ..." - if ( mOp == boIs && ruOp && ruOp->op() == QgsExpressionNodeUnaryOperator::uoNot ) - { - rdump.prepend( '(' ).append( ')' ); - } - - QString fmt; - if ( leftAssociative() ) - { - fmt += lOp && ( lOp->precedence() < precedence() ) ? "(%1)" : "%1"; - fmt += QLatin1String( " %2 " ); - fmt += rOp && ( rOp->precedence() <= precedence() ) ? "(%3)" : "%3"; - } - else - { - fmt += lOp && ( lOp->precedence() <= precedence() ) ? "(%1)" : "%1"; - fmt += QLatin1String( " %2 " ); - fmt += rOp && ( rOp->precedence() < precedence() ) ? "(%3)" : "%3"; - } - - return fmt.arg( mOpLeft->dump(), BINARY_OPERATOR_TEXT[mOp], rdump ); -} - -QSet QgsExpressionNodeBinaryOperator::referencedColumns() const -{ - return mOpLeft->referencedColumns() + mOpRight->referencedColumns(); -} - -QSet QgsExpressionNodeBinaryOperator::referencedVariables() const -{ - return mOpLeft->referencedVariables() + mOpRight->referencedVariables(); -} - -bool QgsExpressionNodeBinaryOperator::needsGeometry() const -{ - return mOpLeft->needsGeometry() || mOpRight->needsGeometry(); -} - -QgsExpressionNode *QgsExpressionNodeBinaryOperator::clone() const -{ - QgsExpressionNodeBinaryOperator *copy = new QgsExpressionNodeBinaryOperator( mOp, mOpLeft->clone(), mOpRight->clone() ); - cloneTo( copy ); - return copy; -} - -bool QgsExpressionNodeBinaryOperator::isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const -{ - return mOpLeft->isStatic( parent, context ) && mOpRight->isStatic( parent, context ); -} - -// - -QVariant QgsExpressionNodeInOperator::evalNode( QgsExpression *parent, const QgsExpressionContext *context ) -{ - if ( mList->count() == 0 ) - return mNotIn ? TVL_True : TVL_False; - QVariant v1 = mNode->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - if ( isNull( v1 ) ) - return TVL_Unknown; - - bool listHasNull = false; - - Q_FOREACH ( QgsExpressionNode *n, mList->list() ) - { - QVariant v2 = n->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - if ( isNull( v2 ) ) - listHasNull = true; - else - { - bool equal = false; - // check whether they are equal - if ( isDoubleSafe( v1 ) && isDoubleSafe( v2 ) ) - { - double f1 = getDoubleValue( v1, parent ); - ENSURE_NO_EVAL_ERROR; - double f2 = getDoubleValue( v2, parent ); - ENSURE_NO_EVAL_ERROR; - equal = qgsDoubleNear( f1, f2 ); - } - else - { - QString s1 = getStringValue( v1, parent ); - ENSURE_NO_EVAL_ERROR; - QString s2 = getStringValue( v2, parent ); - ENSURE_NO_EVAL_ERROR; - equal = QString::compare( s1, s2 ) == 0; - } - - if ( equal ) // we know the result - return mNotIn ? TVL_False : TVL_True; - } - } - - // item not found - if ( listHasNull ) - return TVL_Unknown; - else - return mNotIn ? TVL_True : TVL_False; -} - -QgsExpressionNodeInOperator::~QgsExpressionNodeInOperator() -{ - delete mNode; - delete mList; -} - -QgsExpressionNode::NodeType QgsExpressionNodeInOperator::nodeType() const -{ - return ntInOperator; -} - -bool QgsExpressionNodeInOperator::prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) -{ - bool res = mNode->prepare( parent, context ); - Q_FOREACH ( QgsExpressionNode *n, mList->list() ) - { - res = res && n->prepare( parent, context ); - } - return res; -} - -QString QgsExpressionNodeInOperator::dump() const -{ - return QStringLiteral( "%1 %2 IN (%3)" ).arg( mNode->dump(), mNotIn ? "NOT" : "", mList->dump() ); -} - -QgsExpressionNode *QgsExpressionNodeInOperator::clone() const -{ - QgsExpressionNodeInOperator *copy = new QgsExpressionNodeInOperator( mNode->clone(), mList->clone(), mNotIn ); - cloneTo( copy ); - return copy; -} - -bool QgsExpressionNodeInOperator::isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const -{ - if ( !mNode->isStatic( parent, context ) ) - return false; - - Q_FOREACH ( QgsExpressionNode *n, mList->list() ) - { - if ( !n->isStatic( parent, context ) ) - return false; - } - - return true; -} - -// - -QVariant QgsExpressionNodeFunction::evalNode( QgsExpression *parent, const QgsExpressionContext *context ) -{ - QString name = QgsExpression::QgsExpression::Functions()[mFnIndex]->name(); - QgsExpressionFunction *fd = context && context->hasFunction( name ) ? context->function( name ) : QgsExpression::QgsExpression::Functions()[mFnIndex]; - - // evaluate arguments - QVariantList argValues; - if ( mArgs ) - { - Q_FOREACH ( QgsExpressionNode *n, mArgs->list() ) - { - QVariant v; - if ( fd->lazyEval() ) - { - // Pass in the node for the function to eval as it needs. - v = QVariant::fromValue( n ); - } - else - { - v = n->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - if ( isNull( v ) && !fd->handlesNull() ) - return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal) - } - argValues.append( v ); - } - } - - // run the function - QVariant res = fd->func( argValues, context, parent ); - ENSURE_NO_EVAL_ERROR; - - // everything went fine - return res; -} - -QgsExpressionNodeFunction::QgsExpressionNodeFunction( int fnIndex, QgsExpressionNode::NodeList *args ) - : mFnIndex( fnIndex ) -{ - const QgsExpressionFunction::ParameterList &functionParams = QgsExpression::QgsExpression::Functions()[mFnIndex]->parameters(); - if ( !args || functionParams.isEmpty() ) - { - // no QgsExpressionFunction::Parameters, or function does not support them - mArgs = args; - } - else - { - mArgs = new NodeList(); - - int idx = 0; - //first loop through unnamed arguments - while ( idx < args->names().size() && args->names().at( idx ).isEmpty() ) - { - mArgs->append( args->list().at( idx )->clone() ); - idx++; - } - - //next copy named QgsExpressionFunction::Parameters in order expected by function - for ( ; idx < functionParams.count(); ++idx ) - { - int nodeIdx = args->names().indexOf( functionParams.at( idx ).name().toLower() ); - if ( nodeIdx < 0 ) - { - //QgsExpressionFunction::Parameter not found - insert default value for QgsExpressionFunction::Parameter - mArgs->append( new QgsExpressionNodeLiteral( functionParams.at( idx ).defaultValue() ) ); - } - else - { - mArgs->append( args->list().at( nodeIdx )->clone() ); - } - } - - delete args; - } -} - -QgsExpressionNodeFunction::~QgsExpressionNodeFunction() -{ - delete mArgs; -} - -QgsExpressionNode::NodeType QgsExpressionNodeFunction::nodeType() const -{ - return ntFunction; -} - -bool QgsExpressionNodeFunction::prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) -{ - QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[mFnIndex]; - - bool res = fd->prepare( this, parent, context ); - if ( mArgs && !fd->lazyEval() ) - { - Q_FOREACH ( QgsExpressionNode *n, mArgs->list() ) - { - res = res && n->prepare( parent, context ); - } - } - return res; -} - -QString QgsExpressionNodeFunction::dump() const -{ - QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[mFnIndex]; - if ( fd->params() == 0 ) - return QStringLiteral( "%1%2" ).arg( fd->name(), fd->name().startsWith( '$' ) ? "" : "()" ); // special column - else - return QStringLiteral( "%1(%2)" ).arg( fd->name(), mArgs ? mArgs->dump() : QString() ); // function -} - -QSet QgsExpressionNodeFunction::referencedColumns() const -{ - QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[mFnIndex]; - QSet functionColumns = fd->referencedColumns( this ); - - if ( !mArgs ) - { - //no referenced columns in arguments, just return function's referenced columns - return functionColumns; - } - - Q_FOREACH ( QgsExpressionNode *n, mArgs->list() ) - { - functionColumns.unite( n->referencedColumns() ); - } - - return functionColumns; -} - -QSet QgsExpressionNodeFunction::referencedVariables() const -{ - QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[mFnIndex]; - if ( fd->name() == "var" ) - { - if ( !mArgs->list().isEmpty() ) - { - QgsExpressionNodeLiteral *var = dynamic_cast( mArgs->list().first() ); - if ( var ) - return QSet() << var->value().toString(); - } - return QSet() << QString(); - } - else - { - QSet functionVariables = QSet(); - - if ( !mArgs ) - return functionVariables; - - Q_FOREACH ( QgsExpressionNode *n, mArgs->list() ) - { - functionVariables.unite( n->referencedVariables() ); - } - - return functionVariables; - } -} - -bool QgsExpressionNodeFunction::needsGeometry() const -{ - bool needs = QgsExpression::QgsExpression::Functions()[mFnIndex]->usesGeometry( this ); - if ( mArgs ) - { - Q_FOREACH ( QgsExpressionNode *n, mArgs->list() ) - needs |= n->needsGeometry(); - } - return needs; -} - -QgsExpressionNode *QgsExpressionNodeFunction::clone() const -{ - QgsExpressionNodeFunction *copy = new QgsExpressionNodeFunction( mFnIndex, mArgs ? mArgs->clone() : nullptr ); - cloneTo( copy ); - return copy; -} - -bool QgsExpressionNodeFunction::isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const -{ - return QgsExpression::Functions()[mFnIndex]->isStatic( this, parent, context ); -} - -bool QgsExpressionNodeFunction::validateParams( int fnIndex, QgsExpressionNode::NodeList *args, QString &error ) -{ - if ( !args || !args->hasNamedNodes() ) - return true; - - const QgsExpressionFunction::ParameterList &functionParams = QgsExpression::Functions()[fnIndex]->parameters(); - if ( functionParams.isEmpty() ) - { - error = QStringLiteral( "%1 does not support named QgsExpressionFunction::Parameters" ).arg( QgsExpression::Functions()[fnIndex]->name() ); - return false; - } - else - { - QSet< int > providedArgs; - QSet< int > handledArgs; - int idx = 0; - //first loop through unnamed arguments - while ( args->names().at( idx ).isEmpty() ) - { - providedArgs << idx; - handledArgs << idx; - idx++; - } - - //next check named QgsExpressionFunction::Parameters - for ( ; idx < functionParams.count(); ++idx ) - { - int nodeIdx = args->names().indexOf( functionParams.at( idx ).name().toLower() ); - if ( nodeIdx < 0 ) - { - if ( !functionParams.at( idx ).optional() ) - { - error = QStringLiteral( "No value specified for QgsExpressionFunction::Parameter '%1' for %2" ).arg( functionParams.at( idx ).name(), QgsExpression::Functions()[fnIndex]->name() ); - return false; - } - } - else - { - if ( providedArgs.contains( idx ) ) - { - error = QStringLiteral( "Duplicate QgsExpressionFunction::Parameter specified for '%1' for %2" ).arg( functionParams.at( idx ).name(), QgsExpression::Functions()[fnIndex]->name() ); - return false; - } - } - providedArgs << idx; - handledArgs << nodeIdx; - } - - //last check for bad names - idx = 0; - Q_FOREACH ( const QString &name, args->names() ) - { - if ( !name.isEmpty() && !functionParams.contains( name ) ) - { - error = QStringLiteral( "Invalid QgsExpressionFunction::Parameter name '%1' for %2" ).arg( name, QgsExpression::Functions()[fnIndex]->name() ); - return false; - } - if ( !name.isEmpty() && !handledArgs.contains( idx ) ) - { - int functionIdx = functionParams.indexOf( name ); - if ( providedArgs.contains( functionIdx ) ) - { - error = QStringLiteral( "Duplicate QgsExpressionFunction::Parameter specified for '%1' for %2" ).arg( functionParams.at( functionIdx ).name(), QgsExpression::Functions()[fnIndex]->name() ); - return false; - } - } - idx++; - } - - } - return true; -} - -// - -QVariant QgsExpressionNodeLiteral::evalNode( QgsExpression *parent, const QgsExpressionContext *context ) -{ - Q_UNUSED( context ); - Q_UNUSED( parent ); - return mValue; -} - -QgsExpressionNode::NodeType QgsExpressionNodeLiteral::nodeType() const -{ - return ntLiteral; -} - -bool QgsExpressionNodeLiteral::prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) -{ - Q_UNUSED( parent ); - Q_UNUSED( context ); - return true; -} - - -QString QgsExpressionNodeLiteral::dump() const -{ - if ( mValue.isNull() ) - return QStringLiteral( "NULL" ); - - switch ( mValue.type() ) - { - case QVariant::Int: - return QString::number( mValue.toInt() ); - case QVariant::Double: - return QString::number( mValue.toDouble() ); - case QVariant::String: - return QgsExpression::quotedString( mValue.toString() ); - case QVariant::Bool: - return mValue.toBool() ? QStringLiteral( "TRUE" ) : QStringLiteral( "FALSE" ); - default: - return tr( "[unsupported type;%1; value:%2]" ).arg( mValue.typeName(), mValue.toString() ); - } -} - -QSet QgsExpressionNodeLiteral::referencedColumns() const -{ - return QSet(); -} - -QSet QgsExpressionNodeLiteral::referencedVariables() const -{ - return QSet(); -} - -bool QgsExpressionNodeLiteral::needsGeometry() const -{ - return false; -} - -QgsExpressionNode *QgsExpressionNodeLiteral::clone() const -{ - QgsExpressionNodeLiteral *copy = new QgsExpressionNodeLiteral( mValue ); - cloneTo( copy ); - return copy; -} - -bool QgsExpressionNodeLiteral::isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const -{ - Q_UNUSED( context ) - Q_UNUSED( parent ) - return true; -} - -// - -QVariant QgsExpressionNodeColumnRef::evalNode( QgsExpression *parent, const QgsExpressionContext *context ) -{ - Q_UNUSED( parent ); - int index = mIndex; - - if ( index < 0 ) - { - // have not yet found field index - first check explicitly set fields collection - if ( context && context->hasVariable( QgsExpressionContext::EXPR_FIELDS ) ) - { - QgsFields fields = qvariant_cast( context->variable( QgsExpressionContext::EXPR_FIELDS ) ); - index = fields.lookupField( mName ); - } - } - - if ( context && context->hasFeature() ) - { - QgsFeature feature = context->feature(); - if ( index >= 0 ) - return feature.attribute( index ); - else - return feature.attribute( mName ); - } - return QVariant( '[' + mName + ']' ); -} - -QgsExpressionNode::NodeType QgsExpressionNodeColumnRef::nodeType() const -{ - return ntColumnRef; -} - -bool QgsExpressionNodeColumnRef::prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) -{ - if ( !context || !context->hasVariable( QgsExpressionContext::EXPR_FIELDS ) ) - return false; - - QgsFields fields = qvariant_cast( context->variable( QgsExpressionContext::EXPR_FIELDS ) ); - - mIndex = fields.lookupField( mName ); - if ( mIndex >= 0 ) - { - return true; - } - else - { - parent->setEvalErrorString( tr( "Column '%1' not found" ).arg( mName ) ); - mIndex = -1; - return false; - } -} - -QString QgsExpressionNodeColumnRef::dump() const -{ - return QRegExp( "^[A-Za-z_\x80-\xff][A-Za-z0-9_\x80-\xff]*$" ).exactMatch( mName ) ? mName : QgsExpression::quotedColumnRef( mName ); -} - -QSet QgsExpressionNodeColumnRef::referencedColumns() const -{ - return QSet() << mName; -} - -QSet QgsExpressionNodeColumnRef::referencedVariables() const -{ - return QSet(); -} - -bool QgsExpressionNodeColumnRef::needsGeometry() const -{ - return false; -} - -QgsExpressionNode *QgsExpressionNodeColumnRef::clone() const -{ - QgsExpressionNodeColumnRef *copy = new QgsExpressionNodeColumnRef( mName ); - cloneTo( copy ); - return copy; -} - -bool QgsExpressionNodeColumnRef::isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const -{ - Q_UNUSED( context ) - Q_UNUSED( parent ) - return false; -} - -// - -QgsExpressionNodeCondition::QgsExpressionNodeCondition( QgsExpressionNodeCondition::WhenThenList *conditions, QgsExpressionNode *elseExp ) - : mConditions( *conditions ) - , mElseExp( elseExp ) -{ - delete conditions; -} - -QgsExpressionNodeCondition::~QgsExpressionNodeCondition() -{ - delete mElseExp; - qDeleteAll( mConditions ); -} - -QgsExpressionNode::NodeType QgsExpressionNodeCondition::nodeType() const -{ - return ntCondition; -} - -QVariant QgsExpressionNodeCondition::evalNode( QgsExpression *parent, const QgsExpressionContext *context ) -{ - Q_FOREACH ( WhenThen *cond, mConditions ) - { - QVariant vWhen = cond->mWhenExp->eval( parent, context ); - TVL tvl = getTVLValue( vWhen, parent ); - ENSURE_NO_EVAL_ERROR; - if ( tvl == True ) - { - QVariant vRes = cond->mThenExp->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - return vRes; - } - } - - if ( mElseExp ) - { - QVariant vElse = mElseExp->eval( parent, context ); - ENSURE_NO_EVAL_ERROR; - return vElse; - } - - // return NULL if no condition is matching - return QVariant(); -} - -bool QgsExpressionNodeCondition::prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) -{ - bool res; - Q_FOREACH ( WhenThen *cond, mConditions ) - { - res = cond->mWhenExp->prepare( parent, context ) - & cond->mThenExp->prepare( parent, context ); - if ( !res ) - return false; - } - - if ( mElseExp ) - return mElseExp->prepare( parent, context ); - - return true; -} - -QString QgsExpressionNodeCondition::dump() const -{ - QString msg( QStringLiteral( "CASE" ) ); - Q_FOREACH ( WhenThen *cond, mConditions ) - { - msg += QStringLiteral( " WHEN %1 THEN %2" ).arg( cond->mWhenExp->dump(), cond->mThenExp->dump() ); - } - if ( mElseExp ) - msg += QStringLiteral( " ELSE %1" ).arg( mElseExp->dump() ); - msg += QStringLiteral( " END" ); - return msg; -} - -QSet QgsExpressionNodeCondition::referencedColumns() const -{ - QSet lst; - Q_FOREACH ( WhenThen *cond, mConditions ) - { - lst += cond->mWhenExp->referencedColumns() + cond->mThenExp->referencedColumns(); - } - - if ( mElseExp ) - lst += mElseExp->referencedColumns(); - - return lst; -} - -QSet QgsExpressionNodeCondition::referencedVariables() const -{ - QSet lst; - Q_FOREACH ( WhenThen *cond, mConditions ) - { - lst += cond->mWhenExp->referencedVariables() + cond->mThenExp->referencedVariables(); - } - - if ( mElseExp ) - lst += mElseExp->referencedVariables(); - - return lst; -} - -bool QgsExpressionNodeCondition::needsGeometry() const -{ - Q_FOREACH ( WhenThen *cond, mConditions ) - { - if ( cond->mWhenExp->needsGeometry() || - cond->mThenExp->needsGeometry() ) - return true; - } - - if ( mElseExp && mElseExp->needsGeometry() ) - return true; - - return false; -} - -QgsExpressionNode *QgsExpressionNodeCondition::clone() const -{ - WhenThenList conditions; - Q_FOREACH ( WhenThen *wt, mConditions ) - conditions.append( wt->clone() ); - - QgsExpressionNodeCondition *copy = new QgsExpressionNodeCondition( conditions, mElseExp ? mElseExp->clone() : nullptr ); - cloneTo( copy ); - return copy; -} - -bool QgsExpressionNodeCondition::isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const -{ - Q_FOREACH ( WhenThen *wt, mConditions ) - { - if ( !wt->mWhenExp->isStatic( parent, context ) || !wt->mThenExp->isStatic( parent, context ) ) - return false; - } - - if ( mElseExp ) - return mElseExp->isStatic( parent, context ); - - return true; -} - QString QgsExpression::helpText( QString name ) { @@ -6592,206 +831,6 @@ const QgsExpressionNode *QgsExpression::rootNode() const return d->mRootNode; } -QSet QgsExpressionNodeInOperator::referencedColumns() const -{ - QSet lst( mNode->referencedColumns() ); - Q_FOREACH ( const QgsExpressionNode *n, mList->list() ) - lst.unite( n->referencedColumns() ); - return lst; -} - -QSet QgsExpressionNodeInOperator::referencedVariables() const -{ - QSet lst( mNode->referencedVariables() ); - Q_FOREACH ( const QgsExpressionNode *n, mList->list() ) - lst.unite( n->referencedVariables() ); - return lst; -} - -bool QgsExpressionFunction::usesGeometry( const QgsExpressionNodeFunction *node ) const -{ - Q_UNUSED( node ) - return true; -} - -QStringList QgsExpressionFunction::aliases() const -{ - return QStringList(); -} - -bool QgsExpressionFunction::isStatic( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) const -{ - Q_UNUSED( parent ) - Q_UNUSED( context ) - Q_UNUSED( node ) - return false; -} - -bool QgsExpressionFunction::prepare( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) const -{ - Q_UNUSED( parent ) - Q_UNUSED( context ) - Q_UNUSED( node ) - return true; -} - -QSet QgsExpressionFunction::referencedColumns( const QgsExpressionNodeFunction *node ) const -{ - Q_UNUSED( node ) - return QSet() << QgsFeatureRequest::ALL_ATTRIBUTES; -} - -bool QgsExpressionFunction::isDeprecated() const -{ - return mGroups.isEmpty() ? false : mGroups.contains( QStringLiteral( "deprecated" ) ); -} - -bool QgsExpressionFunction::operator==( const QgsExpressionFunction &other ) const -{ - return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 ); -} - -bool QgsExpressionFunction::handlesNull() const -{ - return mHandlesNull; -} - -QgsStaticExpressionFunction::QgsStaticExpressionFunction( const QString &fnname, const QgsExpressionFunction::ParameterList ¶ms, - FcnEval fcn, - const QString &group, - const QString &helpText, - std::function < bool ( const QgsExpressionNodeFunction *node ) > usesGeometry, - std::function < QSet( const QgsExpressionNodeFunction *node ) > referencedColumns, - bool lazyEval, - const QStringList &aliases, - bool handlesNull ) - : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull ) - , mFnc( fcn ) - , mAliases( aliases ) - , mUsesGeometry( false ) - , mUsesGeometryFunc( usesGeometry ) - , mReferencedColumnsFunc( referencedColumns ) -{ -} - -QStringList QgsStaticExpressionFunction::aliases() const -{ - return mAliases; -} - -bool QgsStaticExpressionFunction::usesGeometry( const QgsExpressionNodeFunction *node ) const -{ - if ( mUsesGeometryFunc ) - return mUsesGeometryFunc( node ); - else - return mUsesGeometry; -} - -QSet QgsStaticExpressionFunction::referencedColumns( const QgsExpressionNodeFunction *node ) const -{ - if ( mReferencedColumnsFunc ) - return mReferencedColumnsFunc( node ); - else - return mReferencedColumns; -} - -bool QgsStaticExpressionFunction::isStatic( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) const -{ - if ( mIsStaticFunc ) - return mIsStaticFunc( node, parent, context ); - else - return mIsStatic; -} - -bool QgsStaticExpressionFunction::prepare( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) const -{ - if ( mPrepareFunc ) - return mPrepareFunc( node, parent, context ); - - return true; -} - -void QgsStaticExpressionFunction::setIsStaticFunction( std::function isStatic ) -{ - mIsStaticFunc = isStatic; -} - -void QgsStaticExpressionFunction::setIsStatic( bool isStatic ) -{ - mIsStaticFunc = nullptr; - mIsStatic = isStatic; -} - -void QgsStaticExpressionFunction::setPrepareFunction( std::function prepareFunc ) -{ - mPrepareFunc = prepareFunc; -} - -bool QgsExpressionFunction::allParamsStatic( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) -{ - if ( node && node->args() ) - { - Q_FOREACH ( QgsExpressionNode *argNode, node->args()->list() ) - { - if ( !argNode->isStatic( parent, context ) ) - return false; - } - } - - return true; -} - -QVariant QgsExpressionNode::eval( QgsExpression *parent, const QgsExpressionContext *context ) -{ - if ( mHasCachedValue ) - { - return mCachedStaticValue; - } - else - { - QVariant res = evalNode( parent, context ); - return res; - } -} - -bool QgsExpressionNode::prepare( QgsExpression *parent, const QgsExpressionContext *context ) -{ - if ( isStatic( parent, context ) ) - { - mCachedStaticValue = evalNode( parent, context ); - mHasCachedValue = true; - return true; - } - else - { - mHasCachedValue = false; - return prepareNode( parent, context ); - } -} - -void QgsExpressionNode::cloneTo( QgsExpressionNode *target ) const -{ - target->mHasCachedValue = mHasCachedValue; - target->mCachedStaticValue = mCachedStaticValue; -} - -QgsExpressionNodeCondition::WhenThen::WhenThen( QgsExpressionNode *whenExp, QgsExpressionNode *thenExp ) - : mWhenExp( whenExp ) - , mThenExp( thenExp ) -{ -} - -QgsExpressionNodeCondition::WhenThen::~WhenThen() -{ - delete mWhenExp; - delete mThenExp; -} - -QgsExpressionNodeCondition::WhenThen *QgsExpressionNodeCondition::WhenThen::clone() const -{ - return new WhenThen( mWhenExp->clone(), mThenExp->clone() ); -} - bool QgsExpression::isField() const { return d->mRootNode && d->mRootNode->nodeType() == QgsExpressionNode::ntColumnRef; diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index 46d878bb1e8..15d1bbd18a7 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -1,7 +1,4181 @@ +/*************************************************************************** + qgsexpressionfunction.cpp + ------------------- + begin : May 2017 + copyright : (C) 2017 Matthias Kuhn + email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + #include "qgsexpressionfunction.h" -#include "qgsexpression.h" +#include "qgsexpressionutils.h" +#include "qgsexpressionnodeimpl.h" +#include "qgsfeaturerequest.h" +#include "qgsstringutils.h" +#include "qgsmultipoint.h" +#include "qgsgeometryutils.h" +#include "qgsmultilinestring.h" +#include "qgslinestring.h" +#include "qgscurvepolygon.h" +#include "qgsmaptopixelgeometrysimplifier.h" +#include "qgspolygon.h" +#include "qgstriangle.h" +#include "qgscurve.h" +#include "qgsregularpolygon.h" +#include "qgsmultipolygon.h" +#include "qgsogcutils.h" +#include "qgsdistancearea.h" +#include "qgsgeometryengine.h" +#include "qgsexpressionsorter.h" +#include "qgssymbollayerutils.h" +#include "qgsstyle.h" +#include "qgscsexception.h" +#include "qgsmessagelog.h" +#include "qgsrasterlayer.h" +#include "qgsvectorlayer.h" +#include "qgsrasterbandstats.h" const QString QgsExpressionFunction::helpText() const { return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText; } + +bool QgsExpressionFunction::usesGeometry( const QgsExpressionNodeFunction *node ) const +{ + Q_UNUSED( node ) + return true; +} + +QStringList QgsExpressionFunction::aliases() const +{ + return QStringList(); +} + +bool QgsExpressionFunction::isStatic( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) const +{ + Q_UNUSED( parent ) + Q_UNUSED( context ) + Q_UNUSED( node ) + return false; +} + +bool QgsExpressionFunction::prepare( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) const +{ + Q_UNUSED( parent ) + Q_UNUSED( context ) + Q_UNUSED( node ) + return true; +} + +QSet QgsExpressionFunction::referencedColumns( const QgsExpressionNodeFunction *node ) const +{ + Q_UNUSED( node ) + return QSet() << QgsFeatureRequest::ALL_ATTRIBUTES; +} + +bool QgsExpressionFunction::isDeprecated() const +{ + return mGroups.isEmpty() ? false : mGroups.contains( QStringLiteral( "deprecated" ) ); +} + +bool QgsExpressionFunction::operator==( const QgsExpressionFunction &other ) const +{ + return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 ); +} + +bool QgsExpressionFunction::handlesNull() const +{ + return mHandlesNull; +} + +QgsStaticExpressionFunction::QgsStaticExpressionFunction( const QString &fnname, const QgsExpressionFunction::ParameterList ¶ms, + FcnEval fcn, + const QString &group, + const QString &helpText, + std::function < bool ( const QgsExpressionNodeFunction *node ) > usesGeometry, + std::function < QSet( const QgsExpressionNodeFunction *node ) > referencedColumns, + bool lazyEval, + const QStringList &aliases, + bool handlesNull ) + : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull ) + , mFnc( fcn ) + , mAliases( aliases ) + , mUsesGeometry( false ) + , mUsesGeometryFunc( usesGeometry ) + , mReferencedColumnsFunc( referencedColumns ) +{ +} + +QStringList QgsStaticExpressionFunction::aliases() const +{ + return mAliases; +} + +bool QgsStaticExpressionFunction::usesGeometry( const QgsExpressionNodeFunction *node ) const +{ + if ( mUsesGeometryFunc ) + return mUsesGeometryFunc( node ); + else + return mUsesGeometry; +} + +QSet QgsStaticExpressionFunction::referencedColumns( const QgsExpressionNodeFunction *node ) const +{ + if ( mReferencedColumnsFunc ) + return mReferencedColumnsFunc( node ); + else + return mReferencedColumns; +} + +bool QgsStaticExpressionFunction::isStatic( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) const +{ + if ( mIsStaticFunc ) + return mIsStaticFunc( node, parent, context ); + else + return mIsStatic; +} + +bool QgsStaticExpressionFunction::prepare( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) const +{ + if ( mPrepareFunc ) + return mPrepareFunc( node, parent, context ); + + return true; +} + +void QgsStaticExpressionFunction::setIsStaticFunction( std::function isStatic ) +{ + mIsStaticFunc = isStatic; +} + +void QgsStaticExpressionFunction::setIsStatic( bool isStatic ) +{ + mIsStaticFunc = nullptr; + mIsStatic = isStatic; +} + +void QgsStaticExpressionFunction::setPrepareFunction( std::function prepareFunc ) +{ + mPrepareFunc = prepareFunc; +} + +bool QgsExpressionFunction::allParamsStatic( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) +{ + if ( node && node->args() ) + { + Q_FOREACH ( QgsExpressionNode *argNode, node->args()->list() ) + { + if ( !argNode->isStatic( parent, context ) ) + return false; + } + } + + return true; +} + + + + + + +static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + if ( !context ) + return QVariant(); + + QString name = getStringValue( values.at( 0 ), parent ); + return context->variable( name ); +} + +static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + if ( !context ) + return QVariant(); + + QString expString = getStringValue( values.at( 0 ), parent ); + QgsExpression expression( expString ); + return expression.evaluate( context ); +} + +static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double x = getDoubleValue( values.at( 0 ), parent ); + return QVariant( sqrt( x ) ); +} + +static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double val = getDoubleValue( values.at( 0 ), parent ); + return QVariant( fabs( val ) ); +} + +static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double deg = getDoubleValue( values.at( 0 ), parent ); + return ( deg * M_PI ) / 180; +} +static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double rad = getDoubleValue( values.at( 0 ), parent ); + return ( 180 * rad ) / M_PI; +} +static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double x = getDoubleValue( values.at( 0 ), parent ); + return QVariant( sin( x ) ); +} +static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double x = getDoubleValue( values.at( 0 ), parent ); + return QVariant( cos( x ) ); +} +static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double x = getDoubleValue( values.at( 0 ), parent ); + return QVariant( tan( x ) ); +} +static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double x = getDoubleValue( values.at( 0 ), parent ); + return QVariant( asin( x ) ); +} +static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double x = getDoubleValue( values.at( 0 ), parent ); + return QVariant( acos( x ) ); +} +static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double x = getDoubleValue( values.at( 0 ), parent ); + return QVariant( atan( x ) ); +} +static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double y = getDoubleValue( values.at( 0 ), parent ); + double x = getDoubleValue( values.at( 1 ), parent ); + return QVariant( atan2( y, x ) ); +} +static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double x = getDoubleValue( values.at( 0 ), parent ); + return QVariant( exp( x ) ); +} +static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double x = getDoubleValue( values.at( 0 ), parent ); + if ( x <= 0 ) + return QVariant(); + return QVariant( log( x ) ); +} +static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double x = getDoubleValue( values.at( 0 ), parent ); + if ( x <= 0 ) + return QVariant(); + return QVariant( log10( x ) ); +} +static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double b = getDoubleValue( values.at( 0 ), parent ); + double x = getDoubleValue( values.at( 1 ), parent ); + if ( x <= 0 || b <= 0 ) + return QVariant(); + return QVariant( log( x ) / log( b ) ); +} +static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double min = getDoubleValue( values.at( 0 ), parent ); + double max = getDoubleValue( values.at( 1 ), parent ); + if ( max < min ) + return QVariant(); + + // Return a random double in the range [min, max] (inclusive) + double f = static_cast< double >( qrand() ) / RAND_MAX; + return QVariant( min + f * ( max - min ) ); +} +static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + qlonglong min = getIntValue( values.at( 0 ), parent ); + qlonglong max = getIntValue( values.at( 1 ), parent ); + if ( max < min ) + return QVariant(); + + // Return a random integer in the range [min, max] (inclusive) + return QVariant( min + ( qrand() % static_cast< qlonglong >( max - min + 1 ) ) ); +} + +static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double val = getDoubleValue( values.at( 0 ), parent ); + double domainMin = getDoubleValue( values.at( 1 ), parent ); + double domainMax = getDoubleValue( values.at( 2 ), parent ); + double rangeMin = getDoubleValue( values.at( 3 ), parent ); + double rangeMax = getDoubleValue( values.at( 4 ), parent ); + + if ( domainMin >= domainMax ) + { + parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) ); + return QVariant(); + } + + // outside of domain? + if ( val >= domainMax ) + { + return rangeMax; + } + else if ( val <= domainMin ) + { + return rangeMin; + } + + // calculate linear scale + double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin ); + double c = rangeMin - ( domainMin * m ); + + // Return linearly scaled value + return QVariant( m * val + c ); +} + +static QVariant fcnExpScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double val = getDoubleValue( values.at( 0 ), parent ); + double domainMin = getDoubleValue( values.at( 1 ), parent ); + double domainMax = getDoubleValue( values.at( 2 ), parent ); + double rangeMin = getDoubleValue( values.at( 3 ), parent ); + double rangeMax = getDoubleValue( values.at( 4 ), parent ); + double exponent = getDoubleValue( values.at( 5 ), parent ); + + if ( domainMin >= domainMax ) + { + parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) ); + return QVariant(); + } + if ( exponent <= 0 ) + { + parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) ); + return QVariant(); + } + + // outside of domain? + if ( val >= domainMax ) + { + return rangeMax; + } + else if ( val <= domainMin ) + { + return rangeMin; + } + + // Return exponentially scaled value + return QVariant( ( ( rangeMax - rangeMin ) / pow( domainMax - domainMin, exponent ) ) * pow( val - domainMin, exponent ) + rangeMin ); +} + +static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + //initially set max as first value + double maxVal = getDoubleValue( values.at( 0 ), parent ); + + //check against all other values + for ( int i = 1; i < values.length(); ++i ) + { + double testVal = getDoubleValue( values[i], parent ); + if ( testVal > maxVal ) + { + maxVal = testVal; + } + } + + return QVariant( maxVal ); +} + +static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + //initially set min as first value + double minVal = getDoubleValue( values.at( 0 ), parent ); + + //check against all other values + for ( int i = 1; i < values.length(); ++i ) + { + double testVal = getDoubleValue( values[i], parent ); + if ( testVal < minVal ) + { + minVal = testVal; + } + } + + return QVariant( minVal ); +} + +static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + //lazy eval, so we need to evaluate nodes now + + //first node is layer id or name + QgsExpressionNode *node = getNode( values.at( 0 ), parent ); + ENSURE_NO_EVAL_ERROR; + QVariant value = node->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + QgsVectorLayer *vl = getVectorLayer( value, parent ); + if ( !vl ) + { + parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) ); + return QVariant(); + } + + // second node is aggregate type + node = getNode( values.at( 1 ), parent ); + ENSURE_NO_EVAL_ERROR; + value = node->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + bool ok = false; + QgsAggregateCalculator::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( getStringValue( value, parent ), &ok ); + if ( !ok ) + { + parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) ); + return QVariant(); + } + + // third node is subexpression (or field name) + node = getNode( values.at( 2 ), parent ); + ENSURE_NO_EVAL_ERROR; + QString subExpression = node->dump(); + + QgsAggregateCalculator::AggregateParameters parameters; + //optional forth node is filter + if ( values.count() > 3 ) + { + node = getNode( values.at( 3 ), parent ); + ENSURE_NO_EVAL_ERROR; + QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node ); + if ( !nl || nl->value().isValid() ) + parameters.filter = node->dump(); + } + + //optional fifth node is concatenator + if ( values.count() > 4 ) + { + node = getNode( values.at( 4 ), parent ); + ENSURE_NO_EVAL_ERROR; + value = node->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + parameters.delimiter = value.toString(); + } + + QVariant result; + if ( context ) + { + QString cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter ); + + QgsExpression subExp( subExpression ); + QgsExpression filterExp( parameters.filter ); + if ( filterExp.referencedVariables().contains( "parent" ) + || filterExp.referencedVariables().contains( QString() ) + || subExp.referencedVariables().contains( "parent" ) + || subExp.referencedVariables().contains( QString() ) ) + { + cacheKey += ':' + qHash( context->feature() ); + } + + if ( context && context->hasCachedValue( cacheKey ) ) + return context->cachedValue( cacheKey ); + + QgsExpressionContext subContext( *context ); + QgsExpressionContextScope *subScope = new QgsExpressionContextScope(); + subScope->setVariable( "parent", context->feature() ); + subContext.appendScope( subScope ); + result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok ); + + context->setCachedValue( cacheKey, result ); + } + else + { + result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok ); + } + if ( !ok ) + { + parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) ); + return QVariant(); + } + + return result; +} + +static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + if ( !context ) + { + parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) ); + return QVariant(); + } + + // first step - find current layer + QgsVectorLayer *vl = getVectorLayer( context->variable( "layer" ), parent ); + if ( !vl ) + { + parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) ); + return QVariant(); + } + + //lazy eval, so we need to evaluate nodes now + + //first node is relation name + QgsExpressionNode *node = getNode( values.at( 0 ), parent ); + ENSURE_NO_EVAL_ERROR; + QVariant value = node->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + QString relationId = value.toString(); + // check relation exists + QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId ); + if ( !relation.isValid() || relation.referencedLayer() != vl ) + { + // check for relations by name + QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId ); + if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl ) + { + parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) ); + return QVariant(); + } + else + { + relation = relations.at( 0 ); + } + } + + QgsVectorLayer *childLayer = relation.referencingLayer(); + + // second node is aggregate type + node = getNode( values.at( 1 ), parent ); + ENSURE_NO_EVAL_ERROR; + value = node->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + bool ok = false; + QgsAggregateCalculator::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( getStringValue( value, parent ), &ok ); + if ( !ok ) + { + parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) ); + return QVariant(); + } + + //third node is subexpression (or field name) + node = getNode( values.at( 2 ), parent ); + ENSURE_NO_EVAL_ERROR; + QString subExpression = node->dump(); + + //optional fourth node is concatenator + QgsAggregateCalculator::AggregateParameters parameters; + if ( values.count() > 3 ) + { + node = getNode( values.at( 3 ), parent ); + ENSURE_NO_EVAL_ERROR; + value = node->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + parameters.delimiter = value.toString(); + } + + FEAT_FROM_CONTEXT( context, f ); + parameters.filter = relation.getRelatedFeaturesFilter( f ); + + QString cacheKey = QStringLiteral( "relagg:%1:%2:%3:%4" ).arg( vl->id(), + QString::number( static_cast< int >( aggregate ) ), + subExpression, + parameters.filter ); + if ( context && context->hasCachedValue( cacheKey ) ) + return context->cachedValue( cacheKey ); + + QVariant result; + ok = false; + + + QgsExpressionContext subContext( *context ); + result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok ); + + if ( !ok ) + { + parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) ); + return QVariant(); + } + + // cache value + if ( context ) + context->setCachedValue( cacheKey, result ); + return result; +} + + +static QVariant fcnAggregateGeneric( QgsAggregateCalculator::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent ) +{ + if ( !context ) + { + parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) ); + return QVariant(); + } + + // first step - find current layer + QgsVectorLayer *vl = getVectorLayer( context->variable( "layer" ), parent ); + if ( !vl ) + { + parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) ); + return QVariant(); + } + + //lazy eval, so we need to evaluate nodes now + + //first node is subexpression (or field name) + QgsExpressionNode *node = getNode( values.at( 0 ), parent ); + ENSURE_NO_EVAL_ERROR; + QString subExpression = node->dump(); + + //optional second node is group by + QString groupBy; + if ( values.count() > 1 ) + { + node = getNode( values.at( 1 ), parent ); + ENSURE_NO_EVAL_ERROR; + QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node ); + if ( !nl || nl->value().isValid() ) + groupBy = node->dump(); + } + + //optional third node is filter + if ( values.count() > 2 ) + { + node = getNode( values.at( 2 ), parent ); + ENSURE_NO_EVAL_ERROR; + QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node ); + if ( !nl || nl->value().isValid() ) + parameters.filter = node->dump(); + } + + // build up filter with group by + + // find current group by value + if ( !groupBy.isEmpty() ) + { + QgsExpression groupByExp( groupBy ); + QVariant groupByValue = groupByExp.evaluate( context ); + QString groupByClause = QStringLiteral( "%1 %2 %3" ).arg( groupBy, + groupByValue.isNull() ? "is" : "=", + QgsExpression::quotedValue( groupByValue ) ); + if ( !parameters.filter.isEmpty() ) + parameters.filter = QStringLiteral( "(%1) AND (%2)" ).arg( parameters.filter, groupByClause ); + else + parameters.filter = groupByClause; + } + + QString cacheKey = QStringLiteral( "agg:%1:%2:%3:%4" ).arg( vl->id(), + QString::number( static_cast< int >( aggregate ) ), + subExpression, + parameters.filter ); + if ( context && context->hasCachedValue( cacheKey ) ) + return context->cachedValue( cacheKey ); + + QVariant result; + bool ok = false; + + QgsExpressionContext subContext( *context ); + result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok ); + + if ( !ok ) + { + parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) ); + return QVariant(); + } + + // cache value + if ( context ) + context->setCachedValue( cacheKey, result ); + return result; +} + + +static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + return fcnAggregateGeneric( QgsAggregateCalculator::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent ); +} + +static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + QgsAggregateCalculator::AggregateParameters parameters; + + //fourth node is concatenator + if ( values.count() > 3 ) + { + QgsExpressionNode *node = getNode( values.at( 3 ), parent ); + ENSURE_NO_EVAL_ERROR; + QVariant value = node->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + parameters.delimiter = value.toString(); + } + + return fcnAggregateGeneric( QgsAggregateCalculator::StringConcatenate, values, parameters, context, parent ); +} + +static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double minValue = getDoubleValue( values.at( 0 ), parent ); + double testValue = getDoubleValue( values.at( 1 ), parent ); + double maxValue = getDoubleValue( values.at( 2 ), parent ); + + // force testValue to sit inside the range specified by the min and max value + if ( testValue <= minValue ) + { + return QVariant( minValue ); + } + else if ( testValue >= maxValue ) + { + return QVariant( maxValue ); + } + else + { + return QVariant( testValue ); + } +} + +static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double x = getDoubleValue( values.at( 0 ), parent ); + return QVariant( floor( x ) ); +} + +static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double x = getDoubleValue( values.at( 0 ), parent ); + return QVariant( ceil( x ) ); +} + +static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + return QVariant( getIntValue( values.at( 0 ), parent ) ); +} +static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + return QVariant( getDoubleValue( values.at( 0 ), parent ) ); +} +static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + return QVariant( getStringValue( values.at( 0 ), parent ) ); +} + +static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + return QVariant( getDateTimeValue( values.at( 0 ), parent ) ); +} + +static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression * ) +{ + Q_FOREACH ( const QVariant &value, values ) + { + if ( value.isNull() ) + continue; + return value; + } + return QVariant(); +} +static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString str = getStringValue( values.at( 0 ), parent ); + return QVariant( str.toLower() ); +} +static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString str = getStringValue( values.at( 0 ), parent ); + return QVariant( str.toUpper() ); +} +static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString str = getStringValue( values.at( 0 ), parent ); + QStringList elems = str.split( ' ' ); + for ( int i = 0; i < elems.size(); i++ ) + { + if ( elems[i].size() > 1 ) + elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower(); + } + return QVariant( elems.join( QStringLiteral( " " ) ) ); +} + +static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString str = getStringValue( values.at( 0 ), parent ); + return QVariant( str.trimmed() ); +} + +static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString string1 = getStringValue( values.at( 0 ), parent ); + QString string2 = getStringValue( values.at( 1 ), parent ); + return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) ); +} + +static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString string1 = getStringValue( values.at( 0 ), parent ); + QString string2 = getStringValue( values.at( 1 ), parent ); + return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) ); +} + +static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString string1 = getStringValue( values.at( 0 ), parent ); + QString string2 = getStringValue( values.at( 1 ), parent ); + int dist = QgsStringUtils::hammingDistance( string1, string2 ); + return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) ); +} + +static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString string = getStringValue( values.at( 0 ), parent ); + return QVariant( QgsStringUtils::soundex( string ) ); +} + +static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QChar character = QChar( getNativeIntValue( values.at( 0 ), parent ) ); + return QVariant( QString( character ) ); +} + +static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + if ( values.length() == 2 || values.length() == 3 ) + { + QString str = getStringValue( values.at( 0 ), parent ); + qlonglong wrap = getIntValue( values.at( 1 ), parent ); + + if ( !str.isEmpty() && wrap != 0 ) + { + QString newstr; + QRegExp rx; + QString customdelimiter = getStringValue( values.at( 2 ), parent ); + int delimiterlength; + + if ( customdelimiter.length() > 0 ) + { + rx.setPatternSyntax( QRegExp::FixedString ); + rx.setPattern( customdelimiter ); + delimiterlength = customdelimiter.length(); + } + else + { + // \x200B is a ZERO-WIDTH SPACE, needed for worwrap to support a number of complex scripts (Indic, Arabic, etc.) + rx.setPattern( QStringLiteral( "[\\s\\x200B]" ) ); + delimiterlength = 1; + } + + + QStringList lines = str.split( '\n' ); + int strlength, strcurrent, strhit, lasthit; + + for ( int i = 0; i < lines.size(); i++ ) + { + strlength = lines[i].length(); + strcurrent = 0; + strhit = 0; + lasthit = 0; + + while ( strcurrent < strlength ) + { + // positive wrap value = desired maximum line width to wrap + // negative wrap value = desired minimum line width before wrap + if ( wrap > 0 ) + { + //first try to locate delimiter backwards + strhit = lines[i].lastIndexOf( rx, strcurrent + wrap ); + if ( strhit == lasthit || strhit == -1 ) + { + //if no new backward delimiter found, try to locate forward + strhit = lines[i].indexOf( rx, strcurrent + qAbs( wrap ) ); + } + lasthit = strhit; + } + else + { + strhit = lines[i].indexOf( rx, strcurrent + qAbs( wrap ) ); + } + if ( strhit > -1 ) + { + newstr.append( lines[i].midRef( strcurrent, strhit - strcurrent ) ); + newstr.append( '\n' ); + strcurrent = strhit + delimiterlength; + } + else + { + newstr.append( lines[i].midRef( strcurrent ) ); + strcurrent = strlength; + } + } + if ( i < lines.size() - 1 ) newstr.append( '\n' ); + } + + return QVariant( newstr ); + } + } + + return QVariant(); +} + +static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + // two variants, one for geometry, one for string + if ( values.at( 0 ).canConvert() ) + { + //geometry variant + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + if ( geom.type() != QgsWkbTypes::LineGeometry ) + return QVariant(); + + return QVariant( geom.length() ); + } + + //otherwise fall back to string variant + QString str = getStringValue( values.at( 0 ), parent ); + return QVariant( str.length() ); +} + +static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + if ( values.count() == 2 && values.at( 1 ).type() == QVariant::Map ) + { + QString str = getStringValue( values.at( 0 ), parent ); + QVariantMap map = getMapValue( values.at( 1 ), parent ); + + for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it ) + { + str = str.replace( it.key(), it.value().toString() ); + } + + return QVariant( str ); + } + else if ( values.count() == 3 ) + { + QString str = getStringValue( values.at( 0 ), parent ); + QVariantList before; + QVariantList after; + bool isSingleReplacement = false; + + if ( values.at( 1 ).type() != QVariant::List && values.at( 2 ).type() != QVariant::StringList ) + { + before = QVariantList() << getStringValue( values.at( 1 ), parent ); + } + else + { + before = getListValue( values.at( 1 ), parent ); + } + + if ( values.at( 2 ).type() != QVariant::List && values.at( 2 ).type() != QVariant::StringList ) + { + after = QVariantList() << getStringValue( values.at( 2 ), parent ); + isSingleReplacement = true; + } + else + { + after = getListValue( values.at( 2 ), parent ); + } + + if ( !isSingleReplacement && before.length() != after.length() ) + { + parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) ); + return QVariant(); + } + + for ( int i = 0; i < before.length(); i++ ) + { + str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() ); + } + + return QVariant( str ); + } + else + { + parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) ); + return QVariant(); + } +} +static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString str = getStringValue( values.at( 0 ), parent ); + QString regexp = getStringValue( values.at( 1 ), parent ); + QString after = getStringValue( values.at( 2 ), parent ); + + QRegularExpression re( regexp ); + if ( !re.isValid() ) + { + parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) ); + return QVariant(); + } + return QVariant( str.replace( re, after ) ); +} + +static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString str = getStringValue( values.at( 0 ), parent ); + QString regexp = getStringValue( values.at( 1 ), parent ); + + QRegularExpression re( regexp ); + if ( !re.isValid() ) + { + parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) ); + return QVariant(); + } + return QVariant( ( str.indexOf( re ) + 1 ) ); +} + +static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString str = getStringValue( values.at( 0 ), parent ); + QString regexp = getStringValue( values.at( 1 ), parent ); + QString empty = getStringValue( values.at( 2 ), parent ); + + QRegularExpression re( regexp ); + if ( !re.isValid() ) + { + parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) ); + return QVariant(); + } + + QRegularExpressionMatch matches = re.match( str ); + if ( matches.hasMatch() ) + { + QVariantList array; + QStringList list = matches.capturedTexts(); + + // Skip the first string to only return captured groups + for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it ) + { + array += ( !( *it ).isEmpty() ) ? *it : empty; + } + + return QVariant( array ); + } + else + { + return QVariant(); + } +} + +static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString str = getStringValue( values.at( 0 ), parent ); + QString regexp = getStringValue( values.at( 1 ), parent ); + + QRegularExpression re( regexp ); + if ( !re.isValid() ) + { + parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) ); + return QVariant(); + } + + // extract substring + QRegularExpressionMatch match = re.match( str ); + if ( match.hasMatch() ) + { + // return first capture + return QVariant( match.captured( 0 ) ); + } + else + { + return QVariant( "" ); + } +} + +static QVariant fcnUuid( const QVariantList &, const QgsExpressionContext *, QgsExpression * ) +{ + return QUuid::createUuid().toString(); +} + +static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() ) + return QVariant(); + + QString str = getStringValue( values.at( 0 ), parent ); + qlonglong from = getIntValue( values.at( 1 ), parent ); + + qlonglong len = 0; + if ( values.at( 2 ).isValid() ) + len = getIntValue( values.at( 2 ), parent ); + else + len = str.size(); + + if ( from < 0 ) + { + from = str.size() + from; + if ( from < 0 ) + { + from = 0; + } + } + else if ( from > 0 ) + { + //account for the fact that substr() starts at 1 + from -= 1; + } + + if ( len < 0 ) + { + len = str.size() + len - from; + if ( len < 0 ) + { + len = 0; + } + } + + return QVariant( str.mid( from, len ) ); +} +static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression * ) +{ + FEAT_FROM_CONTEXT( context, f ); + // TODO: handling of 64-bit feature ids? + return QVariant( static_cast< int >( f.id() ) ); +} + +static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression * ) +{ + if ( !context ) + return QVariant(); + + return context->feature(); +} +static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsFeature feat = getFeature( values.at( 0 ), parent ); + QString attr = getStringValue( values.at( 1 ), parent ); + + return feat.attribute( attr ); +} + +static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + QgsVectorLayer *layer = nullptr; + QgsFeature feature; + + if ( values.isEmpty() ) + { + feature = context->feature(); + layer = getVectorLayer( context->variable( "layer" ), parent ); + } + else if ( values.size() == 1 ) + { + layer = getVectorLayer( context->variable( "layer" ), parent ); + feature = getFeature( values.at( 0 ), parent ); + } + else if ( values.size() == 2 ) + { + layer = getVectorLayer( values.at( 0 ), parent ); + feature = getFeature( values.at( 1 ), parent ); + } + else + { + parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %1 given." ).arg( values.length() ) ); + return QVariant(); + } + + if ( !layer || !feature.isValid() ) + { + return QVariant( QVariant::Bool ); + } + + return layer->selectedFeatureIds().contains( feature.id() ); +} + +static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + QgsVectorLayer *layer = nullptr; + + if ( values.isEmpty() ) + layer = getVectorLayer( context->variable( "layer" ), parent ); + else if ( values.count() == 1 ) + layer = getVectorLayer( values.at( 0 ), parent ); + else + { + parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one QgsExpressionFunction::Parameter. %1 given." ).arg( values.length() ) ); + return QVariant(); + } + + if ( !layer ) + { + return QVariant( QVariant::LongLong ); + } + + return layer->selectedFeatureCount(); +} + +static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString concat; + Q_FOREACH ( const QVariant &value, values ) + { + concat += getStringValue( value, parent ); + } + return concat; +} + +static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString string = getStringValue( values.at( 0 ), parent ); + return string.indexOf( getStringValue( values.at( 1 ), parent ) ) + 1; +} + +static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString string = getStringValue( values.at( 0 ), parent ); + qlonglong pos = getIntValue( values.at( 1 ), parent ); + return string.right( pos ); +} + +static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString string = getStringValue( values.at( 0 ), parent ); + qlonglong pos = getIntValue( values.at( 1 ), parent ); + return string.left( pos ); +} + +static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString string = getStringValue( values.at( 0 ), parent ); + qlonglong length = getIntValue( values.at( 1 ), parent ); + QString fill = getStringValue( values.at( 2 ), parent ); + return string.leftJustified( length, fill.at( 0 ), true ); +} + +static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString string = getStringValue( values.at( 0 ), parent ); + qlonglong length = getIntValue( values.at( 1 ), parent ); + QString fill = getStringValue( values.at( 2 ), parent ); + return string.rightJustified( length, fill.at( 0 ), true ); +} + +static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString string = getStringValue( values.at( 0 ), parent ); + for ( int n = 1; n < values.length(); n++ ) + { + string = string.arg( getStringValue( values.at( n ), parent ) ); + } + return string; +} + + +static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression * ) +{ + return QVariant( QDateTime::currentDateTime() ); +} + +static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + return QVariant( getDateValue( values.at( 0 ), parent ) ); +} + +static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + return QVariant( getTimeValue( values.at( 0 ), parent ) ); +} + +static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + return QVariant::fromValue( getInterval( values.at( 0 ), parent ) ); +} + +static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QDateTime d1 = getDateTimeValue( values.at( 0 ), parent ); + QDateTime d2 = getDateTimeValue( values.at( 1 ), parent ); + int seconds = d2.secsTo( d1 ); + return QVariant::fromValue( QgsInterval( seconds ) ); +} + +static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + if ( !values.at( 0 ).canConvert() ) + return QVariant(); + + QDate date = getDateValue( values.at( 0 ), parent ); + if ( !date.isValid() ) + return QVariant(); + + // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat) + // (to match PostgreSQL behavior) + return date.dayOfWeek() % 7; +} + +static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariant value = values.at( 0 ); + QgsInterval inter = getInterval( value, parent, false ); + if ( inter.isValid() ) + { + return QVariant( inter.days() ); + } + else + { + QDateTime d1 = getDateTimeValue( value, parent ); + return QVariant( d1.date().day() ); + } +} + +static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariant value = values.at( 0 ); + QgsInterval inter = getInterval( value, parent, false ); + if ( inter.isValid() ) + { + return QVariant( inter.years() ); + } + else + { + QDateTime d1 = getDateTimeValue( value, parent ); + return QVariant( d1.date().year() ); + } +} + +static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariant value = values.at( 0 ); + QgsInterval inter = getInterval( value, parent, false ); + if ( inter.isValid() ) + { + return QVariant( inter.months() ); + } + else + { + QDateTime d1 = getDateTimeValue( value, parent ); + return QVariant( d1.date().month() ); + } +} + +static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariant value = values.at( 0 ); + QgsInterval inter = getInterval( value, parent, false ); + if ( inter.isValid() ) + { + return QVariant( inter.weeks() ); + } + else + { + QDateTime d1 = getDateTimeValue( value, parent ); + return QVariant( d1.date().weekNumber() ); + } +} + +static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariant value = values.at( 0 ); + QgsInterval inter = getInterval( value, parent, false ); + if ( inter.isValid() ) + { + return QVariant( inter.hours() ); + } + else + { + QTime t1 = getTimeValue( value, parent ); + return QVariant( t1.hour() ); + } +} + +static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariant value = values.at( 0 ); + QgsInterval inter = getInterval( value, parent, false ); + if ( inter.isValid() ) + { + return QVariant( inter.minutes() ); + } + else + { + QTime t1 = getTimeValue( value, parent ); + return QVariant( t1.minute() ); + } +} + +static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariant value = values.at( 0 ); + QgsInterval inter = getInterval( value, parent, false ); + if ( inter.isValid() ) + { + return QVariant( inter.seconds() ); + } + else + { + QTime t1 = getTimeValue( value, parent ); + return QVariant( t1.second() ); + } +} + +static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QDateTime dt = getDateTimeValue( values.at( 0 ), parent ); + if ( dt.isValid() ) + { + return QVariant( dt.toMSecsSinceEpoch() ); + } + else + { + return QVariant(); + } +} + +#define ENSURE_GEOM_TYPE(f, g, geomtype) \ + if ( !(f).hasGeometry() ) \ + return QVariant(); \ + QgsGeometry g = (f).geometry(); \ + if ( (g).type() != (geomtype) ) \ + return QVariant(); + +static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression * ) +{ + FEAT_FROM_CONTEXT( context, f ); + ENSURE_GEOM_TYPE( f, g, QgsWkbTypes::PointGeometry ); + if ( g.isMultipart() ) + { + return g.asMultiPoint().at( 0 ).x(); + } + else + { + return g.asPoint().x(); + } +} + +static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression * ) +{ + FEAT_FROM_CONTEXT( context, f ); + ENSURE_GEOM_TYPE( f, g, QgsWkbTypes::PointGeometry ); + if ( g.isMultipart() ) + { + return g.asMultiPoint().at( 0 ).y(); + } + else + { + return g.asPoint().y(); + } +} + +static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + if ( geom.isNull() ) + return QVariant(); + + //if single point, return the point's x coordinate + if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() ) + { + return geom.asPoint().x(); + } + + //otherwise return centroid x + QgsGeometry centroid = geom.centroid(); + QVariant result( centroid.asPoint().x() ); + return result; +} + +static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + if ( geom.isNull() ) + return QVariant(); + + //if single point, return the point's y coordinate + if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() ) + { + return geom.asPoint().y(); + } + + //otherwise return centroid y + QgsGeometry centroid = geom.centroid(); + QVariant result( centroid.asPoint().y() ); + return result; +} + +static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + if ( geom.isNull() ) + return QVariant(); //or 0? + + //if single point, return the point's z coordinate + if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() ) + { + QgsPointV2 *point = dynamic_cast< QgsPointV2 * >( geom.geometry() ); + if ( point ) + return point->z(); + } + + return QVariant(); +} + +static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + if ( geom.isNull() ) + return QVariant(); //or 0? + + //if single point, return the point's m value + if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() ) + { + QgsPointV2 *point = dynamic_cast< QgsPointV2 * >( geom.geometry() ); + if ( point ) + return point->m(); + } + + return QVariant(); +} + +static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + + if ( geom.isNull() ) + return QVariant(); + + //idx is 1 based + qlonglong idx = getIntValue( values.at( 1 ), parent ) - 1; + + QgsVertexId vId; + if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) ) + { + parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) ); + return QVariant(); + } + + QgsPointV2 point = geom.geometry()->vertexAt( vId ); + return QVariant::fromValue( QgsGeometry( new QgsPointV2( point ) ) ); +} + +static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + + if ( geom.isNull() ) + return QVariant(); + + QgsVertexId vId; + if ( !geom.vertexIdFromVertexNr( 0, vId ) ) + { + return QVariant(); + } + + QgsPointV2 point = geom.geometry()->vertexAt( vId ); + return QVariant::fromValue( QgsGeometry( new QgsPointV2( point ) ) ); +} + +static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + + if ( geom.isNull() ) + return QVariant(); + + QgsVertexId vId; + if ( !geom.vertexIdFromVertexNr( geom.geometry()->nCoordinates() - 1, vId ) ) + { + return QVariant(); + } + + QgsPointV2 point = geom.geometry()->vertexAt( vId ); + return QVariant::fromValue( QgsGeometry( new QgsPointV2( point ) ) ); +} + +static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + + if ( geom.isNull() ) + return QVariant(); + + bool ignoreClosing = false; + if ( values.length() > 1 ) + { + ignoreClosing = getIntValue( values.at( 1 ), parent ); + } + + QgsMultiPointV2 *mp = new QgsMultiPointV2(); + + Q_FOREACH ( const QgsRingSequence &part, geom.geometry()->coordinateSequence() ) + { + Q_FOREACH ( const QgsPointSequence &ring, part ) + { + bool skipLast = false; + if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() ) + { + skipLast = true; + } + + for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i ) + { + mp->addGeometry( ring.at( i ).clone() ); + } + } + } + + return QVariant::fromValue( QgsGeometry( mp ) ); +} + +static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + + if ( geom.isNull() ) + return QVariant(); + + QList< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.geometry() ); + + //ok, now we have a complete list of segmentized lines from the geometry + QgsMultiLineString *ml = new QgsMultiLineString(); + Q_FOREACH ( QgsLineString *line, linesToProcess ) + { + for ( int i = 0; i < line->numPoints() - 1; ++i ) + { + QgsLineString *segment = new QgsLineString(); + segment->setPoints( QgsPointSequence() + << line->pointN( i ) + << line->pointN( i + 1 ) ); + ml->addGeometry( segment ); + } + delete line; + } + + return QVariant::fromValue( QgsGeometry( ml ) ); +} + +static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + + if ( geom.isNull() ) + return QVariant(); + + QgsCurvePolygon *curvePolygon = dynamic_cast< QgsCurvePolygon * >( geom.geometry() ); + if ( !curvePolygon ) + return QVariant(); + + //idx is 1 based + qlonglong idx = getIntValue( values.at( 1 ), parent ) - 1; + + if ( idx >= curvePolygon->numInteriorRings() || idx < 0 ) + return QVariant(); + + QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( idx )->clone() ); + QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant(); + return result; +} + +static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + + if ( geom.isNull() ) + return QVariant(); + + QgsGeometryCollection *collection = dynamic_cast< QgsGeometryCollection * >( geom.geometry() ); + if ( !collection ) + return QVariant(); + + //idx is 1 based + qlonglong idx = getIntValue( values.at( 1 ), parent ) - 1; + + if ( idx < 0 || idx >= collection->numGeometries() ) + return QVariant(); + + QgsAbstractGeometry *part = collection->geometryN( idx )->clone(); + QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant(); + return result; +} + +static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + + if ( geom.isNull() ) + return QVariant(); + + QgsAbstractGeometry *boundary = geom.geometry()->boundary(); + if ( !boundary ) + return QVariant(); + + return QVariant::fromValue( QgsGeometry( boundary ) ); +} + +static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + + if ( geom.isNull() ) + return QVariant(); + + QgsGeometry merged = geom.mergeLines(); + if ( merged.isNull() ) + return QVariant(); + + return QVariant::fromValue( merged ); +} + +static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + + if ( geom.isNull() ) + return QVariant(); + + double tolerance = getDoubleValue( values.at( 1 ), parent ); + + QgsGeometry simplified = geom.simplify( tolerance ); + if ( simplified.isNull() ) + return QVariant(); + + return simplified; +} + +static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + + if ( geom.isNull() ) + return QVariant(); + + double tolerance = getDoubleValue( values.at( 1 ), parent ); + + QgsMapToPixelSimplifier simplifier( QgsMapToPixelSimplifier::SimplifyGeometry, tolerance, QgsMapToPixelSimplifier::Visvalingam ); + + QgsGeometry simplified = simplifier.simplify( geom ); + if ( simplified.isNull() ) + return QVariant(); + + return simplified; +} + +static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + + if ( geom.isNull() ) + return QVariant(); + + int iterations = qMin( getNativeIntValue( values.at( 1 ), parent ), 10 ); + double offset = qBound( 0.0, getDoubleValue( values.at( 2 ), parent ), 0.5 ); + double minLength = getDoubleValue( values.at( 3 ), parent ); + double maxAngle = qBound( 0.0, getDoubleValue( values.at( 4 ), parent ), 180.0 ); + + QgsGeometry smoothed = geom.smooth( iterations, offset, minLength, maxAngle ); + if ( smoothed.isNull() ) + return QVariant(); + + return smoothed; +} + +static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + if ( values.count() < 2 || values.count() > 4 ) + { + parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) ); + return QVariant(); + } + + double x = getDoubleValue( values.at( 0 ), parent ); + double y = getDoubleValue( values.at( 1 ), parent ); + double z = values.count() >= 3 ? getDoubleValue( values.at( 2 ), parent ) : 0.0; + double m = values.count() >= 4 ? getDoubleValue( values.at( 3 ), parent ) : 0.0; + switch ( values.count() ) + { + case 2: + return QVariant::fromValue( QgsGeometry( new QgsPointV2( x, y ) ) ); + case 3: + return QVariant::fromValue( QgsGeometry( new QgsPointV2( QgsWkbTypes::PointZ, x, y, z ) ) ); + case 4: + return QVariant::fromValue( QgsGeometry( new QgsPointV2( QgsWkbTypes::PointZM, x, y, z, m ) ) ); + } + return QVariant(); //avoid warning +} + +static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double x = getDoubleValue( values.at( 0 ), parent ); + double y = getDoubleValue( values.at( 1 ), parent ); + double m = getDoubleValue( values.at( 2 ), parent ); + return QVariant::fromValue( QgsGeometry( new QgsPointV2( QgsWkbTypes::PointM, x, y, 0.0, m ) ) ); +} + +static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + if ( values.count() < 2 ) + { + return QVariant(); + } + + QgsLineString *lineString = new QgsLineString(); + lineString->clear(); + + Q_FOREACH ( const QVariant &value, values ) + { + QgsGeometry geom = getGeometry( value, parent ); + if ( geom.isNull() ) + continue; + + if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() ) + continue; + + QgsPointV2 *point = dynamic_cast< QgsPointV2 * >( geom.geometry() ); + if ( !point ) + continue; + + lineString->addVertex( *point ); + } + + return QVariant::fromValue( QgsGeometry( lineString ) ); +} + +static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + if ( values.count() < 1 ) + { + parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) ); + return QVariant(); + } + + QgsGeometry outerRing = getGeometry( values.at( 0 ), parent ); + if ( outerRing.type() != QgsWkbTypes::LineGeometry || outerRing.isMultipart() || outerRing.isNull() ) + return QVariant(); + + QgsPolygonV2 *polygon = new QgsPolygonV2(); + polygon->setExteriorRing( dynamic_cast< QgsCurve * >( outerRing.geometry()->clone() ) ); + + for ( int i = 1; i < values.count(); ++i ) + { + QgsGeometry ringGeom = getGeometry( values.at( i ), parent ); + if ( ringGeom.isNull() ) + continue; + + if ( ringGeom.type() != QgsWkbTypes::LineGeometry || ringGeom.isMultipart() || ringGeom.isNull() ) + continue; + + polygon->addInteriorRing( dynamic_cast< QgsCurve * >( ringGeom.geometry()->clone() ) ); + } + + return QVariant::fromValue( QgsGeometry( polygon ) ); +} + +static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + std::unique_ptr tr( new QgsTriangle() ); + std::unique_ptr lineString( new QgsLineString() ); + lineString->clear(); + + Q_FOREACH ( const QVariant &value, values ) + { + QgsGeometry geom = getGeometry( value, parent ); + if ( geom.isNull() ) + return QVariant(); + + if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() ) + return QVariant(); + + QgsPointV2 *point = dynamic_cast< QgsPointV2 * >( geom.geometry() ); + if ( !point ) + return QVariant(); + + lineString->addVertex( *point ); + } + + tr->setExteriorRing( lineString.release() ); + + return QVariant::fromValue( QgsGeometry( tr.release() ) ); +} + +static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + if ( geom.isNull() ) + return QVariant(); + + if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() ) + return QVariant(); + + double radius = getDoubleValue( values.at( 1 ), parent ); + double segment = getIntValue( values.at( 2 ), parent ); + + if ( segment < 3 ) + { + parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) ); + return QVariant(); + } + QgsPointV2 *point = static_cast< QgsPointV2 * >( geom.geometry() ); + QgsCircle circ( *point, radius ); + return QVariant::fromValue( QgsGeometry( circ.toPolygon( segment ) ) ); +} + +static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + if ( geom.isNull() ) + return QVariant(); + + if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() ) + return QVariant(); + + double majorAxis = getDoubleValue( values.at( 1 ), parent ); + double minorAxis = getDoubleValue( values.at( 2 ), parent ); + double azimuth = getDoubleValue( values.at( 3 ), parent ); + double segment = getIntValue( values.at( 4 ), parent ); + if ( segment < 3 ) + { + parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) ); + return QVariant(); + } + QgsPointV2 *point = static_cast< QgsPointV2 * >( geom.geometry() ); + QgsEllipse elp( *point, majorAxis, minorAxis, azimuth ); + return QVariant::fromValue( QgsGeometry( elp.toPolygon( segment ) ) ); +} + +static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + + QgsGeometry pt1 = getGeometry( values.at( 0 ), parent ); + if ( pt1.isNull() ) + return QVariant(); + + if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() ) + return QVariant(); + + QgsGeometry pt2 = getGeometry( values.at( 1 ), parent ); + if ( pt2.isNull() ) + return QVariant(); + + if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() ) + return QVariant(); + + unsigned int nbEdges = static_cast( getIntValue( values.at( 2 ), parent ) ); + if ( nbEdges < 3 ) + { + parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) ); + return QVariant(); + } + + QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( getIntValue( values.at( 3 ), parent ) ); + if ( ( option < QgsRegularPolygon::InscribedCircle ) || ( option > QgsRegularPolygon::CircumscribedCircle ) ) + { + parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) ); + return QVariant(); + } + QgsPointV2 *center = static_cast< QgsPointV2 * >( pt1.geometry() ); + QgsPointV2 *corner = static_cast< QgsPointV2 * >( pt2.geometry() ); + + QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option ); + + return QVariant::fromValue( QgsGeometry( rp.toPolygon( ) ) ); + +} + +static QVariant pointAt( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) // helper function +{ + FEAT_FROM_CONTEXT( context, f ); + qlonglong idx = getIntValue( values.at( 0 ), parent ); + QgsGeometry g = f.geometry(); + if ( g.isNull() ) + return QVariant(); + + if ( idx < 0 ) + { + idx += g.geometry()->nCoordinates(); + } + if ( idx < 0 || idx >= g.geometry()->nCoordinates() ) + { + parent->setEvalErrorString( QObject::tr( "Index is out of range" ) ); + return QVariant(); + } + + QgsPoint p = g.vertexAt( idx ); + return QVariant( QPointF( p.x(), p.y() ) ); +} + +static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent ) +{ + QVariant v = pointAt( values, f, parent ); + if ( v.type() == QVariant::PointF ) + return QVariant( v.toPointF().x() ); + else + return QVariant(); +} +static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent ) +{ + QVariant v = pointAt( values, f, parent ); + if ( v.type() == QVariant::PointF ) + return QVariant( v.toPointF().y() ); + else + return QVariant(); +} +static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression * ) +{ + FEAT_FROM_CONTEXT( context, f ); + QgsGeometry geom = f.geometry(); + if ( !geom.isNull() ) + return QVariant::fromValue( geom ); + else + return QVariant( QVariant::UserType ); +} +static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString wkt = getStringValue( values.at( 0 ), parent ); + QgsGeometry geom = QgsGeometry::fromWkt( wkt ); + QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} +static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString gml = getStringValue( values.at( 0 ), parent ); + QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml ); + QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} + +static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent ) +{ + FEAT_FROM_CONTEXT( context, f ); + ENSURE_GEOM_TYPE( f, g, QgsWkbTypes::PolygonGeometry ); + QgsDistanceArea *calc = parent->geomCalculator(); + if ( calc ) + { + double area = calc->measureArea( f.geometry() ); + area = calc->convertAreaMeasurement( area, parent->areaUnits() ); + return QVariant( area ); + } + else + { + return QVariant( f.geometry().area() ); + } +} + +static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + + if ( geom.type() != QgsWkbTypes::PolygonGeometry ) + return QVariant(); + + return QVariant( geom.area() ); +} + +static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent ) +{ + FEAT_FROM_CONTEXT( context, f ); + ENSURE_GEOM_TYPE( f, g, QgsWkbTypes::LineGeometry ); + QgsDistanceArea *calc = parent->geomCalculator(); + if ( calc ) + { + double len = calc->measureLength( f.geometry() ); + len = calc->convertLengthMeasurement( len, parent->distanceUnits() ); + return QVariant( len ); + } + else + { + return QVariant( f.geometry().length() ); + } +} + +static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent ) +{ + FEAT_FROM_CONTEXT( context, f ); + ENSURE_GEOM_TYPE( f, g, QgsWkbTypes::PolygonGeometry ); + QgsDistanceArea *calc = parent->geomCalculator(); + if ( calc ) + { + double len = calc->measurePerimeter( f.geometry() ); + len = calc->convertLengthMeasurement( len, parent->distanceUnits() ); + return QVariant( len ); + } + else + { + return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().geometry()->perimeter() ); + } +} + +static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + + if ( geom.type() != QgsWkbTypes::PolygonGeometry ) + return QVariant(); + + //length for polygons = perimeter + return QVariant( geom.length() ); +} + +static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + return QVariant( geom.isNull() ? 0 : geom.geometry()->nCoordinates() ); +} + +static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + if ( geom.isNull() ) + return QVariant(); + + return QVariant( geom.geometry()->partCount() ); +} + +static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + + if ( geom.isNull() ) + return QVariant(); + + QgsCurvePolygon *curvePolygon = dynamic_cast< QgsCurvePolygon * >( geom.geometry() ); + if ( curvePolygon ) + return QVariant( curvePolygon->numInteriorRings() ); + + QgsGeometryCollection *collection = dynamic_cast< QgsGeometryCollection * >( geom.geometry() ); + if ( collection ) + { + //find first CurvePolygon in collection + for ( int i = 0; i < collection->numGeometries(); ++i ) + { + curvePolygon = dynamic_cast< QgsCurvePolygon *>( collection->geometryN( i ) ); + if ( !curvePolygon ) + continue; + + return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() ); + } + } + + return QVariant(); +} + +static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + + if ( geom.isNull() ) + return QVariant(); + + QgsCurvePolygon *curvePolygon = dynamic_cast< QgsCurvePolygon * >( geom.geometry() ); + if ( curvePolygon ) + return QVariant( curvePolygon->ringCount() ); + + bool foundPoly = false; + int ringCount = 0; + QgsGeometryCollection *collection = dynamic_cast< QgsGeometryCollection * >( geom.geometry() ); + if ( collection ) + { + //find CurvePolygons in collection + for ( int i = 0; i < collection->numGeometries(); ++i ) + { + curvePolygon = dynamic_cast< QgsCurvePolygon *>( collection->geometryN( i ) ); + if ( !curvePolygon ) + continue; + + foundPoly = true; + ringCount += curvePolygon->ringCount(); + } + } + + if ( !foundPoly ) + return QVariant(); + + return QVariant( ringCount ); +} + +static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() ); + QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant(); + return result; +} + +static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + return QVariant::fromValue( geom.boundingBox().width() ); +} + +static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + return QVariant::fromValue( geom.boundingBox().height() ); +} + +static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + return QVariant::fromValue( geom.boundingBox().xMinimum() ); +} + +static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + return QVariant::fromValue( geom.boundingBox().xMaximum() ); +} + +static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + return QVariant::fromValue( geom.boundingBox().yMinimum() ); +} + +static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + return QVariant::fromValue( geom.boundingBox().yMaximum() ); +} + +static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + if ( fGeom.isNull() ) + return QVariant(); + + QgsCurve *curve = dynamic_cast< QgsCurve * >( fGeom.geometry() ); + if ( !curve ) + return QVariant(); + + return QVariant::fromValue( curve->isClosed() ); +} + +static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + if ( values.length() < 2 || values.length() > 3 ) + return QVariant(); + + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); + + if ( fGeom.isNull() || sGeom.isNull() ) + return QVariant(); + + std::unique_ptr engine( QgsGeometry::createGeometryEngine( fGeom.geometry() ) ); + + if ( values.length() == 2 ) + { + //two geometry arguments, return relation + QString result = engine->relate( *sGeom.geometry() ); + return QVariant::fromValue( result ); + } + else + { + //three arguments, test pattern + QString pattern = getStringValue( values.at( 2 ), parent ); + bool result = engine->relatePattern( *sGeom.geometry(), pattern ); + return QVariant::fromValue( result ); + } +} + +static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); + return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False; +} +static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); + return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False; +} +static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); + return fGeom.intersects( sGeom ) ? TVL_True : TVL_False; +} +static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); + return fGeom.touches( sGeom ) ? TVL_True : TVL_False; +} +static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); + return fGeom.crosses( sGeom ) ? TVL_True : TVL_False; +} +static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); + return fGeom.contains( sGeom ) ? TVL_True : TVL_False; +} +static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); + return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False; +} +static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); + return fGeom.within( sGeom ) ? TVL_True : TVL_False; +} +static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + if ( values.length() < 2 || values.length() > 3 ) + return QVariant(); + + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + double dist = getDoubleValue( values.at( 1 ), parent ); + qlonglong seg = 8; + if ( values.length() == 3 ) + seg = getIntValue( values.at( 2 ), parent ); + + QgsGeometry geom = fGeom.buffer( dist, seg ); + QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} + +static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + double dist = getDoubleValue( values.at( 1 ), parent ); + qlonglong segments = getIntValue( values.at( 2 ), parent ); + QgsGeometry::JoinStyle join = static_cast< QgsGeometry::JoinStyle >( getIntValue( values.at( 3 ), parent ) ); + if ( join < QgsGeometry::JoinStyleRound || join > QgsGeometry::JoinStyleBevel ) + return QVariant(); + double mitreLimit = getDoubleValue( values.at( 3 ), parent ); + + QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, mitreLimit ); + QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} + +static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + double dist = getDoubleValue( values.at( 1 ), parent ); + qlonglong segments = getIntValue( values.at( 2 ), parent ); + QgsGeometry::JoinStyle join = static_cast< QgsGeometry::JoinStyle >( getIntValue( values.at( 3 ), parent ) ); + if ( join < QgsGeometry::JoinStyleRound || join > QgsGeometry::JoinStyleBevel ) + return QVariant(); + double mitreLimit = getDoubleValue( values.at( 3 ), parent ); + + QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, QgsGeometry::SideLeft, join, mitreLimit ); + QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} + +static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + double distStart = getDoubleValue( values.at( 1 ), parent ); + double distEnd = getDoubleValue( values.at( 2 ), parent ); + + QgsGeometry geom = fGeom.extendLine( distStart, distEnd ); + QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} + +static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + double dx = getDoubleValue( values.at( 1 ), parent ); + double dy = getDoubleValue( values.at( 2 ), parent ); + fGeom.translate( dx, dy ); + return QVariant::fromValue( fGeom ); +} +static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry geom = fGeom.centroid(); + QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} +static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry geom = fGeom.pointOnSurface(); + QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} + +static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + double tolerance = getDoubleValue( values.at( 1 ), parent ); + QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance ); + QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} + +static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry geom = fGeom.convexHull(); + QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} +static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); + QgsGeometry geom = fGeom.difference( sGeom ); + QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} + +static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + if ( fGeom.isNull() ) + return QVariant(); + + QgsCurve *curve = dynamic_cast< QgsCurve * >( fGeom.geometry() ); + if ( !curve ) + return QVariant(); + + QgsCurve *reversed = curve->reversed(); + QVariant result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant(); + return result; +} + +static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + if ( fGeom.isNull() ) + return QVariant(); + + QgsCurvePolygon *curvePolygon = dynamic_cast< QgsCurvePolygon * >( fGeom.geometry() ); + if ( !curvePolygon || !curvePolygon->exteriorRing() ) + return QVariant(); + + QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() ); + QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant(); + return result; +} + +static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); + return QVariant( fGeom.distance( sGeom ) ); +} +static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); + QgsGeometry geom = fGeom.intersection( sGeom ); + QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} +static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); + QgsGeometry geom = fGeom.symDifference( sGeom ); + QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} +static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry sGeom = getGeometry( values.at( 1 ), parent ); + QgsGeometry geom = fGeom.combine( sGeom ); + QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} +static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + if ( values.length() < 1 || values.length() > 2 ) + return QVariant(); + + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + qlonglong prec = 8; + if ( values.length() == 2 ) + prec = getIntValue( values.at( 1 ), parent ); + QString wkt = fGeom.exportToWkt( prec ); + return QVariant( wkt ); +} + +static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + if ( values.length() != 2 ) + { + parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %1 given." ).arg( values.length() ) ); + return QVariant(); + } + + QgsGeometry fGeom1 = getGeometry( values.at( 0 ), parent ); + QgsGeometry fGeom2 = getGeometry( values.at( 1 ), parent ); + + const QgsPointV2 *pt1 = dynamic_cast( fGeom1.geometry() ); + const QgsPointV2 *pt2 = dynamic_cast( fGeom2.geometry() ); + + if ( !pt1 || !pt2 ) + { + parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) ); + return QVariant(); + } + + // Code from postgis + if ( pt1->x() == pt2->x() ) + { + if ( pt1->y() < pt2->y() ) + return 0.0; + else if ( pt1->y() > pt2->y() ) + return M_PI; + else + return 0; + } + + if ( pt1->y() == pt2->y() ) + { + if ( pt1->x() < pt2->x() ) + return M_PI / 2; + else if ( pt1->x() > pt2->x() ) + return M_PI + ( M_PI / 2 ); + else + return 0; + } + + if ( pt1->x() < pt2->x() ) + { + if ( pt1->y() < pt2->y() ) + { + return atan( fabs( pt1->x() - pt2->x() ) / fabs( pt1->y() - pt2->y() ) ); + } + else /* ( pt1->y() > pt2->y() ) - equality case handled above */ + { + return atan( fabs( pt1->y() - pt2->y() ) / fabs( pt1->x() - pt2->x() ) ) + + ( M_PI / 2 ); + } + } + + else /* ( pt1->x() > pt2->x() ) - equality case handled above */ + { + if ( pt1->y() > pt2->y() ) + { + return atan( fabs( pt1->x() - pt2->x() ) / fabs( pt1->y() - pt2->y() ) ) + + M_PI; + } + else /* ( pt1->y() < pt2->y() ) - equality case handled above */ + { + return atan( fabs( pt1->y() - pt2->y() ) / fabs( pt1->x() - pt2->x() ) ) + + ( M_PI + ( M_PI / 2 ) ); + } + } +} + +static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + + if ( geom.type() != QgsWkbTypes::PointGeometry ) + { + parent->setEvalErrorString( QStringLiteral( "'project' requires a point geometry" ) ); + return QVariant(); + } + + double distance = getDoubleValue( values.at( 1 ), parent ); + double azimuth = getDoubleValue( values.at( 2 ), parent ); + double inclination = getDoubleValue( values.at( 3 ), parent ); + + const QgsPointV2 *p = static_cast( geom.geometry() ); + QgsPointV2 newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI ); + + return QVariant::fromValue( QgsGeometry( new QgsPointV2( newPoint ) ) ); +} + +static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom1 = getGeometry( values.at( 0 ), parent ); + QgsGeometry fGeom2 = getGeometry( values.at( 1 ), parent ); + + const QgsPointV2 *pt1 = dynamic_cast( fGeom1.geometry() ); + const QgsPointV2 *pt2 = dynamic_cast( fGeom2.geometry() ); + + if ( ( fGeom1.type() != QgsWkbTypes::PointGeometry ) || ( fGeom2.type() != QgsWkbTypes::PointGeometry ) || + !pt1 || !pt2 ) + { + parent->setEvalErrorString( QStringLiteral( "Function 'inclination' requires two points as arguments." ) ); + return QVariant(); + } + + return pt1->inclination( *pt2 ); + +} + +static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + if ( values.length() != 3 ) + return QVariant(); + + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + double x = getDoubleValue( values.at( 1 ), parent ); + double y = getDoubleValue( values.at( 2 ), parent ); + + QgsGeometry geom = fGeom.extrude( x, y ); + + QVariant result = geom.geometry() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} + +static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent ) +{ + if ( values.length() < 2 ) + return QVariant(); + + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + + if ( !fGeom.isMultipart() ) + return values.at( 0 ); + + QString expString = getStringValue( values.at( 1 ), parent ); + QVariant cachedExpression; + if ( ctx ) + cachedExpression = ctx->cachedValue( expString ); + QgsExpression expression; + + if ( cachedExpression.isValid() ) + { + expression = cachedExpression.value(); + } + else + expression = QgsExpression( expString ); + + bool asc = values.value( 2 ).toBool(); + + QgsExpressionContext *unconstedContext = nullptr; + QgsFeature f; + if ( ctx ) + { + // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after + // so no reason to worry + unconstedContext = const_cast( ctx ); + f = ctx->feature(); + } + else + { + // If there's no context provided, create a fake one + unconstedContext = new QgsExpressionContext(); + } + + QgsGeometryCollection *collection = dynamic_cast( fGeom.geometry() ); + Q_ASSERT( collection ); // Should have failed the multipart check above + + QgsFeatureRequest::OrderBy orderBy; + orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) ); + QgsExpressionSorter sorter( orderBy ); + + QList partFeatures; + partFeatures.reserve( collection->partCount() ); + for ( int i = 0; i < collection->partCount(); ++i ) + { + f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) ); + partFeatures << f; + } + + sorter.sortFeatures( partFeatures, unconstedContext ); + + QgsGeometryCollection *orderedGeom = dynamic_cast( fGeom.geometry()->clone() ); + + Q_ASSERT( orderedGeom ); + + while ( orderedGeom->partCount() ) + orderedGeom->removeGeometry( 0 ); + + Q_FOREACH ( const QgsFeature &feature, partFeatures ) + { + orderedGeom->addGeometry( feature.geometry().geometry()->clone() ); + } + + QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) ); + + if ( !ctx ) + delete unconstedContext; + + return result; +} + +static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fromGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry toGeom = getGeometry( values.at( 1 ), parent ); + + QgsGeometry geom = fromGeom.nearestPoint( toGeom ); + + QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} + +static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fromGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry toGeom = getGeometry( values.at( 1 ), parent ); + + QgsGeometry geom = fromGeom.shortestLine( toGeom ); + + QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} + +static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry lineGeom = getGeometry( values.at( 0 ), parent ); + double distance = getDoubleValue( values.at( 1 ), parent ); + + QgsGeometry geom = lineGeom.interpolate( distance ); + + QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant(); + return result; +} + +static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry lineGeom = getGeometry( values.at( 0 ), parent ); + double distance = getDoubleValue( values.at( 1 ), parent ); + + return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI; +} + +static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + qlonglong vertex = getIntValue( values.at( 1 ), parent ); + + return geom.angleAtVertex( vertex ) * 180.0 / M_PI; +} + +static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + qlonglong vertex = getIntValue( values.at( 1 ), parent ); + + return geom.distanceToVertex( vertex ); +} + +static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry lineGeom = getGeometry( values.at( 0 ), parent ); + QgsGeometry pointGeom = getGeometry( values.at( 1 ), parent ); + + double distance = lineGeom.lineLocatePoint( pointGeom ); + + return distance >= 0 ? distance : QVariant(); +} + +static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + if ( values.length() == 2 && values.at( 1 ).toInt() != 0 ) + { + double number = getDoubleValue( values.at( 0 ), parent ); + double scaler = pow( 10.0, getIntValue( values.at( 1 ), parent ) ); + return QVariant( qRound( number * scaler ) / scaler ); + } + + if ( values.length() >= 1 ) + { + double number = getIntValue( values.at( 0 ), parent ); + return QVariant( qRound( number ) ); + } + + return QVariant(); +} + +static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + Q_UNUSED( values ); + Q_UNUSED( parent ); + return M_PI; +} + +static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + double value = getDoubleValue( values.at( 0 ), parent ); + int places = getIntValue( values.at( 1 ), parent ); + if ( places < 0 ) + { + parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) ); + return QVariant(); + } + return QStringLiteral( "%L1" ).arg( value, 0, 'f', places ); +} + +static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QDateTime dt = getDateTimeValue( values.at( 0 ), parent ); + QString format = getStringValue( values.at( 1 ), parent ); + return dt.toString( format ); +} + +static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + int red = getIntValue( values.at( 0 ), parent ); + int green = getIntValue( values.at( 1 ), parent ); + int blue = getIntValue( values.at( 2 ), parent ); + QColor color = QColor( red, green, blue ); + if ( ! color.isValid() ) + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) ); + color = QColor( 0, 0, 0 ); + } + + return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() ); +} + +static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) +{ + QgsExpressionNode *node = getNode( values.at( 0 ), parent ); + ENSURE_NO_EVAL_ERROR; + QVariant value = node->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + if ( value.toBool() ) + { + node = getNode( values.at( 1 ), parent ); + ENSURE_NO_EVAL_ERROR; + value = node->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + } + else + { + node = getNode( values.at( 2 ), parent ); + ENSURE_NO_EVAL_ERROR; + value = node->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + } + return value; +} + +static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + int red = getIntValue( values.at( 0 ), parent ); + int green = getIntValue( values.at( 1 ), parent ); + int blue = getIntValue( values.at( 2 ), parent ); + int alpha = getIntValue( values.at( 3 ), parent ); + QColor color = QColor( red, green, blue, alpha ); + if ( ! color.isValid() ) + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) ); + color = QColor( 0, 0, 0 ); + } + return QgsSymbolLayerUtils::encodeColor( color ); +} + +QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString rampName = getStringValue( values.at( 0 ), parent ); + const QgsColorRamp *mRamp = QgsStyle::defaultStyle()->colorRampRef( rampName ); + if ( ! mRamp ) + { + parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) ); + return QColor( 0, 0, 0 ).name(); + } + double value = getDoubleValue( values.at( 1 ), parent ); + QColor color = mRamp->color( value ); + return QgsSymbolLayerUtils::encodeColor( color ); +} + +static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + // Hue ranges from 0 - 360 + double hue = getIntValue( values.at( 0 ), parent ) / 360.0; + // Saturation ranges from 0 - 100 + double saturation = getIntValue( values.at( 1 ), parent ) / 100.0; + // Lightness ranges from 0 - 100 + double lightness = getIntValue( values.at( 2 ), parent ) / 100.0; + + QColor color = QColor::fromHslF( hue, saturation, lightness ); + + if ( ! color.isValid() ) + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) ); + color = QColor( 0, 0, 0 ); + } + + return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() ); +} + +static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + // Hue ranges from 0 - 360 + double hue = getIntValue( values.at( 0 ), parent ) / 360.0; + // Saturation ranges from 0 - 100 + double saturation = getIntValue( values.at( 1 ), parent ) / 100.0; + // Lightness ranges from 0 - 100 + double lightness = getIntValue( values.at( 2 ), parent ) / 100.0; + // Alpha ranges from 0 - 255 + double alpha = getIntValue( values.at( 3 ), parent ) / 255.0; + + QColor color = QColor::fromHslF( hue, saturation, lightness, alpha ); + if ( ! color.isValid() ) + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) ); + color = QColor( 0, 0, 0 ); + } + return QgsSymbolLayerUtils::encodeColor( color ); +} + +static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + // Hue ranges from 0 - 360 + double hue = getIntValue( values.at( 0 ), parent ) / 360.0; + // Saturation ranges from 0 - 100 + double saturation = getIntValue( values.at( 1 ), parent ) / 100.0; + // Value ranges from 0 - 100 + double value = getIntValue( values.at( 2 ), parent ) / 100.0; + + QColor color = QColor::fromHsvF( hue, saturation, value ); + + if ( ! color.isValid() ) + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) ); + color = QColor( 0, 0, 0 ); + } + + return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() ); +} + +static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + // Hue ranges from 0 - 360 + double hue = getIntValue( values.at( 0 ), parent ) / 360.0; + // Saturation ranges from 0 - 100 + double saturation = getIntValue( values.at( 1 ), parent ) / 100.0; + // Value ranges from 0 - 100 + double value = getIntValue( values.at( 2 ), parent ) / 100.0; + // Alpha ranges from 0 - 255 + double alpha = getIntValue( values.at( 3 ), parent ) / 255.0; + + QColor color = QColor::fromHsvF( hue, saturation, value, alpha ); + if ( ! color.isValid() ) + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) ); + color = QColor( 0, 0, 0 ); + } + return QgsSymbolLayerUtils::encodeColor( color ); +} + +static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + // Cyan ranges from 0 - 100 + double cyan = getIntValue( values.at( 0 ), parent ) / 100.0; + // Magenta ranges from 0 - 100 + double magenta = getIntValue( values.at( 1 ), parent ) / 100.0; + // Yellow ranges from 0 - 100 + double yellow = getIntValue( values.at( 2 ), parent ) / 100.0; + // Black ranges from 0 - 100 + double black = getIntValue( values.at( 3 ), parent ) / 100.0; + + QColor color = QColor::fromCmykF( cyan, magenta, yellow, black ); + + if ( ! color.isValid() ) + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) ); + color = QColor( 0, 0, 0 ); + } + + return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() ); +} + +static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + // Cyan ranges from 0 - 100 + double cyan = getIntValue( values.at( 0 ), parent ) / 100.0; + // Magenta ranges from 0 - 100 + double magenta = getIntValue( values.at( 1 ), parent ) / 100.0; + // Yellow ranges from 0 - 100 + double yellow = getIntValue( values.at( 2 ), parent ) / 100.0; + // Black ranges from 0 - 100 + double black = getIntValue( values.at( 3 ), parent ) / 100.0; + // Alpha ranges from 0 - 255 + double alpha = getIntValue( values.at( 4 ), parent ) / 255.0; + + QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha ); + if ( ! color.isValid() ) + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) ); + color = QColor( 0, 0, 0 ); + } + return QgsSymbolLayerUtils::encodeColor( color ); +} + +static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() ); + if ( ! color.isValid() ) + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) ); + return QVariant(); + } + + QString part = getStringValue( values.at( 1 ), parent ); + if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 ) + return color.red(); + else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 ) + return color.green(); + else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 ) + return color.blue(); + else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 ) + return color.alpha(); + else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 ) + return color.hsvHueF() * 360; + else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 ) + return color.hsvSaturationF() * 100; + else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 ) + return color.valueF() * 100; + else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 ) + return color.hslHueF() * 360; + else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 ) + return color.hslSaturationF() * 100; + else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 ) + return color.lightnessF() * 100; + else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 ) + return color.cyanF() * 100; + else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 ) + return color.magentaF() * 100; + else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 ) + return color.yellowF() * 100; + else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 ) + return color.blackF() * 100; + + parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) ); + return QVariant(); +} + +static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() ); + if ( ! color.isValid() ) + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) ); + return QVariant(); + } + + QString part = getStringValue( values.at( 1 ), parent ); + int value = getIntValue( values.at( 2 ), parent ); + if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 ) + color.setRed( value ); + else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 ) + color.setGreen( value ); + else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 ) + color.setBlue( value ); + else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 ) + color.setAlpha( value ); + else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 ) + color.setHsv( value, color.hsvSaturation(), color.value(), color.alpha() ); + else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 ) + color.setHsvF( color.hsvHueF(), value / 100.0, color.valueF(), color.alphaF() ); + else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 ) + color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), value / 100.0, color.alphaF() ); + else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 ) + color.setHsl( value, color.hslSaturation(), color.lightness(), color.alpha() ); + else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 ) + color.setHslF( color.hslHueF(), value / 100.0, color.lightnessF(), color.alphaF() ); + else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 ) + color.setHslF( color.hslHueF(), color.hslSaturationF(), value / 100.0, color.alphaF() ); + else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 ) + color.setCmykF( value / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() ); + else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 ) + color.setCmykF( color.cyanF(), value / 100.0, color.yellowF(), color.blackF(), color.alphaF() ); + else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 ) + color.setCmykF( color.cyanF(), color.magentaF(), value / 100.0, color.blackF(), color.alphaF() ); + else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 ) + color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), value / 100.0, color.alphaF() ); + else + { + parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) ); + return QVariant(); + } + return QgsSymbolLayerUtils::encodeColor( color ); +} + +static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() ); + if ( ! color.isValid() ) + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) ); + return QVariant(); + } + + color = color.darker( getIntValue( values.at( 1 ), parent ) ); + + return QgsSymbolLayerUtils::encodeColor( color ); +} + +static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() ); + if ( ! color.isValid() ) + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) ); + return QVariant(); + } + + color = color.lighter( getIntValue( values.at( 1 ), parent ) ); + + return QgsSymbolLayerUtils::encodeColor( color ); +} + +static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsFeature feat = getFeature( values.at( 0 ), parent ); + QgsGeometry geom = feat.geometry(); + if ( !geom.isNull() ) + return QVariant::fromValue( geom ); + return QVariant(); +} + +static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QgsGeometry fGeom = getGeometry( values.at( 0 ), parent ); + QString sAuthId = getStringValue( values.at( 1 ), parent ); + QString dAuthId = getStringValue( values.at( 2 ), parent ); + + QgsCoordinateReferenceSystem s = QgsCoordinateReferenceSystem::fromOgcWmsCrs( sAuthId ); + if ( ! s.isValid() ) + return QVariant::fromValue( fGeom ); + QgsCoordinateReferenceSystem d = QgsCoordinateReferenceSystem::fromOgcWmsCrs( dAuthId ); + if ( ! d.isValid() ) + return QVariant::fromValue( fGeom ); + + QgsCoordinateTransform t( s, d ); + try + { + if ( fGeom.transform( t ) == 0 ) + return QVariant::fromValue( fGeom ); + } + catch ( QgsCsException &cse ) + { + QgsMessageLog::logMessage( QStringLiteral( "Transform error caught in transform() function: %1" ).arg( cse.what() ) ); + return QVariant(); + } + return QVariant(); +} + + +static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + //arguments: 1. layer id / name, 2. key attribute, 3. eq value + QgsVectorLayer *vl = getVectorLayer( values.at( 0 ), parent ); + + //no layer found + if ( !vl ) + { + return QVariant(); + } + + QString attribute = getStringValue( values.at( 1 ), parent ); + int attributeId = vl->fields().lookupField( attribute ); + if ( attributeId == -1 ) + { + return QVariant(); + } + + const QVariant &attVal = values.at( 2 ); + QgsFeatureRequest req; + req.setFilterExpression( QStringLiteral( "%1=%2" ).arg( QgsExpression::quotedColumnRef( attribute ), + QgsExpression::quotedString( attVal.toString() ) ) ); + req.setLimit( 1 ); + if ( !parent->needsGeometry() ) + { + req.setFlags( QgsFeatureRequest::NoGeometry ); + } + QgsFeatureIterator fIt = vl->getFeatures( req ); + + QgsFeature fet; + if ( fIt.nextFeature( fet ) ) + return QVariant::fromValue( fet ); + + return QVariant(); +} + +static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString layerIdOrName = getStringValue( values.at( 0 ), parent ); + + //try to find a matching layer by name + QgsMapLayer *layer = QgsProject::instance()->mapLayer( layerIdOrName ); //search by id first + if ( !layer ) + { + QList layersByName = QgsProject::instance()->mapLayersByName( layerIdOrName ); + if ( !layersByName.isEmpty() ) + { + layer = layersByName.at( 0 ); + } + } + + if ( !layer ) + return QVariant(); + + QString layerProperty = getStringValue( values.at( 1 ), parent ); + if ( QString::compare( layerProperty, QStringLiteral( "name" ), Qt::CaseInsensitive ) == 0 ) + return layer->name(); + else if ( QString::compare( layerProperty, QStringLiteral( "id" ), Qt::CaseInsensitive ) == 0 ) + return layer->id(); + else if ( QString::compare( layerProperty, QStringLiteral( "title" ), Qt::CaseInsensitive ) == 0 ) + return layer->title(); + else if ( QString::compare( layerProperty, QStringLiteral( "abstract" ), Qt::CaseInsensitive ) == 0 ) + return layer->abstract(); + else if ( QString::compare( layerProperty, QStringLiteral( "keywords" ), Qt::CaseInsensitive ) == 0 ) + return layer->keywordList(); + else if ( QString::compare( layerProperty, QStringLiteral( "data_url" ), Qt::CaseInsensitive ) == 0 ) + return layer->dataUrl(); + else if ( QString::compare( layerProperty, QStringLiteral( "attribution" ), Qt::CaseInsensitive ) == 0 ) + return layer->attribution(); + else if ( QString::compare( layerProperty, QStringLiteral( "attribution_url" ), Qt::CaseInsensitive ) == 0 ) + return layer->attributionUrl(); + else if ( QString::compare( layerProperty, QStringLiteral( "source" ), Qt::CaseInsensitive ) == 0 ) + return layer->publicSource(); + else if ( QString::compare( layerProperty, QStringLiteral( "min_scale" ), Qt::CaseInsensitive ) == 0 ) + return layer->minimumScale(); + else if ( QString::compare( layerProperty, QStringLiteral( "max_scale" ), Qt::CaseInsensitive ) == 0 ) + return layer->maximumScale(); + else if ( QString::compare( layerProperty, QStringLiteral( "crs" ), Qt::CaseInsensitive ) == 0 ) + return layer->crs().authid(); + else if ( QString::compare( layerProperty, QStringLiteral( "crs_definition" ), Qt::CaseInsensitive ) == 0 ) + return layer->crs().toProj4(); + else if ( QString::compare( layerProperty, QStringLiteral( "extent" ), Qt::CaseInsensitive ) == 0 ) + { + QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() ); + QVariant result = QVariant::fromValue( extentGeom ); + return result; + } + else if ( QString::compare( layerProperty, QStringLiteral( "type" ), Qt::CaseInsensitive ) == 0 ) + { + switch ( layer->type() ) + { + case QgsMapLayer::VectorLayer: + return QCoreApplication::translate( "expressions", "Vector" ); + case QgsMapLayer::RasterLayer: + return QCoreApplication::translate( "expressions", "Raster" ); + case QgsMapLayer::PluginLayer: + return QCoreApplication::translate( "expressions", "Plugin" ); + } + } + else + { + //vector layer methods + QgsVectorLayer *vLayer = dynamic_cast< QgsVectorLayer * >( layer ); + if ( vLayer ) + { + if ( QString::compare( layerProperty, QStringLiteral( "storage_type" ), Qt::CaseInsensitive ) == 0 ) + return vLayer->storageType(); + else if ( QString::compare( layerProperty, QStringLiteral( "geometry_type" ), Qt::CaseInsensitive ) == 0 ) + return QgsWkbTypes::geometryDisplayString( vLayer->geometryType() ); + else if ( QString::compare( layerProperty, QStringLiteral( "feature_count" ), Qt::CaseInsensitive ) == 0 ) + return QVariant::fromValue( vLayer->featureCount() ); + } + } + + 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 = QgsProject::instance()->mapLayer( layerIdOrName ); //search by id first + if ( !layer ) + { + QList layersByName = QgsProject::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; +} + +static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + return getListValue( values.at( 0 ), parent ).length(); +} + +static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + return QVariant( getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) ); +} + +static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + return getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) ); +} + +static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + const QVariantList list = getListValue( values.at( 0 ), parent ); + const qlonglong pos = getIntValue( values.at( 1 ), parent ); + if ( pos < 0 || pos >= list.length() ) return QVariant(); + return list.at( pos ); +} + +static QVariant convertToSameType( const QVariant &value, QVariant::Type type ) +{ + QVariant result = value; + result.convert( type ); + return result; +} + +static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariantList list = getListValue( values.at( 0 ), parent ); + list.append( values.at( 1 ) ); + return convertToSameType( list, values.at( 0 ).type() ); +} + +static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariantList list = getListValue( values.at( 0 ), parent ); + list.prepend( values.at( 1 ) ); + return convertToSameType( list, values.at( 0 ).type() ); +} + +static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariantList list = getListValue( values.at( 0 ), parent ); + list.insert( getIntValue( values.at( 1 ), parent ), values.at( 2 ) ); + return convertToSameType( list, values.at( 0 ).type() ); +} + +static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariantList list = getListValue( values.at( 0 ), parent ); + list.removeAt( getIntValue( values.at( 1 ), parent ) ); + return convertToSameType( list, values.at( 0 ).type() ); +} + +static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariantList list = getListValue( values.at( 0 ), parent ); + list.removeAll( values.at( 1 ) ); + return convertToSameType( list, values.at( 0 ).type() ); +} + +static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariantList list; + Q_FOREACH ( const QVariant &cur, values ) + { + list += getListValue( cur, parent ); + } + return convertToSameType( list, values.at( 0 ).type() ); +} + +static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + const QVariantList array1 = getListValue( values.at( 0 ), parent ); + Q_FOREACH ( const QVariant &cur, getListValue( values.at( 1 ), parent ) ) + { + if ( array1.contains( cur ) ) + return QVariant( true ); + } + return QVariant( false ); +} + + +static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariantList array = getListValue( values.at( 0 ), parent ); + + QVariantList distinct; + + for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it ) + { + if ( !distinct.contains( *it ) ) + { + distinct += ( *it ); + } + } + + return distinct; +} + +static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariantList array = getListValue( values.at( 0 ), parent ); + QString delimiter = getStringValue( values.at( 1 ), parent ); + QString empty = getStringValue( values.at( 2 ), parent ); + + QString str; + + for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it ) + { + str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty; + if ( it != ( array.constEnd() - 1 ) ) + { + str += delimiter; + } + } + + return QVariant( str ); +} + +static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QString str = getStringValue( values.at( 0 ), parent ); + QString delimiter = getStringValue( values.at( 1 ), parent ); + QString empty = getStringValue( values.at( 2 ), parent ); + + QStringList list = str.split( delimiter ); + QVariantList array; + + for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it ) + { + array += ( !( *it ).isEmpty() ) ? *it : empty; + } + + return array; +} + +static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariantMap result; + for ( int i = 0; i + 1 < values.length(); i += 2 ) + { + result.insert( getStringValue( values.at( i ), parent ), values.at( i + 1 ) ); + } + return result; +} + +static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + return getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() ); +} + +static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + return getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() ); +} + +static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariantMap map = getMapValue( values.at( 0 ), parent ); + map.remove( values.at( 1 ).toString() ); + return map; +} + +static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariantMap map = getMapValue( values.at( 0 ), parent ); + map.insert( values.at( 1 ).toString(), values.at( 2 ) ); + return map; +} + +static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + QVariantMap result; + Q_FOREACH ( const QVariant &cur, values ) + { + const QVariantMap curMap = getMapValue( cur, parent ); + for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it ) + result.insert( it.key(), it.value() ); + } + return result; +} + +static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + return QStringList( getMapValue( values.at( 0 ), parent ).keys() ); +} + +static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent ) +{ + return getMapValue( values.at( 0 ), parent ).values(); +} + +static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression * ) +{ + QString envVarName = values.at( 0 ).toString(); + return QProcessEnvironment::systemEnvironment().value( envVarName ); +} + +const QList &QgsExpression::Functions() +{ + // The construction of the list isn't thread-safe, and without the mutex, + // crashes in the WFS provider may occur, since it can parse expressions + // in parallel. + // The mutex needs to be recursive. + static QMutex sFunctionsMutex( QMutex::Recursive ); + QMutexLocker locker( &sFunctionsMutex ); + + static QList sFunctions; + if ( sFunctions.isEmpty() ) + { + QgsExpressionFunction::ParameterList aggParams = QgsExpressionFunction::ParameterList() + << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "group_by" ), true ) + << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true ); + + sFunctions + << new QgsStaticExpressionFunction( QStringLiteral( "sqrt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnSqrt, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "radians" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "degrees" ) ), fcnRadians, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "degrees" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "radians" ) ), fcnDegrees, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "azimuth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnAzimuth, QStringList() << QStringLiteral( "Math" ) << QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "inclination" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnInclination, QStringList() << QStringLiteral( "Math" ) << QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "project" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "elevation" ), true, M_PI / 2 ), fcnProject, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "abs" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAbs, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "cos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnCos, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "sin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnSin, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "tan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnTan, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "asin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAsin, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "acos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAcos, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "atan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAtan, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "atan2" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ), fcnAtan2, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnExp, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "ln" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLn, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "log10" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog10, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "log" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "base" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "round" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ), fcnRound, QStringLiteral( "Math" ) ); + + QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( QStringLiteral( "rand" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnRnd, QStringLiteral( "Math" ) ); + randFunc->setIsStatic( false ); + sFunctions << randFunc; + + QgsStaticExpressionFunction *randfFunc = new QgsStaticExpressionFunction( QStringLiteral( "randf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ), true, 0.0 ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ), true, 1.0 ), fcnRndF, QStringLiteral( "Math" ) ); + randfFunc->setIsStatic( false ); + sFunctions << randfFunc; + + sFunctions + << new QgsStaticExpressionFunction( QStringLiteral( "max" ), -1, fcnMax, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "min" ), -1, fcnMin, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "clamp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnClamp, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "scale_linear" ), 5, fcnLinearScale, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "scale_exp" ), 6, fcnExpScale, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "floor" ), 1, fcnFloor, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "ceil" ), 1, fcnCeil, QStringLiteral( "Math" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "pi" ), 0, fcnPi, QStringLiteral( "Math" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "$pi" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "to_int" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInt, QStringLiteral( "Conversions" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "toint" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "to_real" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToReal, QStringLiteral( "Conversions" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "toreal" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "to_string" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToString, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "String" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "tostring" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "to_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToDateTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "todatetime" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "to_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToDate, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "todate" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "to_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "totime" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "to_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInterval, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "tointerval" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "coalesce" ), -1, fcnCoalesce, QStringLiteral( "Conditionals" ), QString(), false, QSet(), false, QStringList(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "if" ), 3, fcnIf, QStringLiteral( "Conditionals" ), QString(), False, QSet(), true ) + + << new QgsStaticExpressionFunction( QStringLiteral( "aggregate" ), + QgsExpressionFunction::ParameterList() + << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true ) + << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true ), + fcnAggregate, + QStringLiteral( "Aggregates" ), + QString(), + []( const QgsExpressionNodeFunction * node ) + { + // usesGeometry callback: return true if @parent variable is referenced + + if ( !node ) + return true; + + if ( !node->args() ) + return false; + + QSet referencedVars; + if ( node->args()->count() > 2 ) + { + QgsExpressionNode *subExpressionNode = node->args()->at( 2 ); + referencedVars = subExpressionNode->referencedVariables(); + } + + if ( node->args()->count() > 3 ) + { + QgsExpressionNode *filterNode = node->args()->at( 3 ); + referencedVars.unite( filterNode->referencedVariables() ); + } + return referencedVars.contains( "parent" ) || referencedVars.contains( QString() ); + }, + []( const QgsExpressionNodeFunction * node ) + { + // referencedColumns callback: return AllAttributes if @parent variable is referenced + + if ( !node ) + return QSet() << QgsFeatureRequest::ALL_ATTRIBUTES; + + if ( !node->args() ) + return QSet(); + + QSet referencedCols; + QSet referencedVars; + + if ( node->args()->count() > 2 ) + { + QgsExpressionNode *subExpressionNode = node->args()->at( 2 ); + referencedVars = subExpressionNode->referencedVariables(); + referencedCols = subExpressionNode->referencedColumns(); + } + if ( node->args()->count() > 3 ) + { + QgsExpressionNode *filterNode = node->args()->at( 3 ); + referencedVars = filterNode->referencedVariables(); + referencedCols.unite( filterNode->referencedColumns() ); + } + + if ( referencedVars.contains( "parent" ) || referencedVars.contains( QString() ) ) + return QSet() << QgsFeatureRequest::ALL_ATTRIBUTES; + else + return referencedCols; + }, + true + ) + + << new QgsStaticExpressionFunction( QStringLiteral( "relation_aggregate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "relation" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true ), + fcnAggregateRelation, QStringLiteral( "Aggregates" ), QString(), False, QSet() << QgsFeatureRequest::ALL_ATTRIBUTES, true ) + + << new QgsStaticExpressionFunction( QStringLiteral( "count" ), aggParams, fcnAggregateCount, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "count_distinct" ), aggParams, fcnAggregateCountDistinct, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "count_missing" ), aggParams, fcnAggregateCountMissing, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "minimum" ), aggParams, fcnAggregateMin, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "maximum" ), aggParams, fcnAggregateMax, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "sum" ), aggParams, fcnAggregateSum, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "mean" ), aggParams, fcnAggregateMean, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "median" ), aggParams, fcnAggregateMedian, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "stdev" ), aggParams, fcnAggregateStdev, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "range" ), aggParams, fcnAggregateRange, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "minority" ), aggParams, fcnAggregateMinority, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "majority" ), aggParams, fcnAggregateMajority, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "q1" ), aggParams, fcnAggregateQ1, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "q3" ), aggParams, fcnAggregateQ3, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "iqr" ), aggParams, fcnAggregateIQR, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "min_length" ), aggParams, fcnAggregateMinLength, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "max_length" ), aggParams, fcnAggregateMaxLength, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "collect" ), aggParams, fcnAggregateCollectGeometry, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "concatenate" ), aggParams << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true ), fcnAggregateStringConcat, QStringLiteral( "Aggregates" ), QString(), False, QSet(), true ) + + << new QgsStaticExpressionFunction( QStringLiteral( "regexp_match" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpMatch, QStringList() << QStringLiteral( "Conditionals" ) << QStringLiteral( "String" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "regexp_matches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnRegexpMatches, QStringLiteral( "Arrays" ) ) + + << new QgsStaticExpressionFunction( QStringLiteral( "now" ), 0, fcnNow, QStringLiteral( "Date and Time" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "$now" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "age" ), 2, fcnAge, QStringLiteral( "Date and Time" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "year" ), 1, fcnYear, QStringLiteral( "Date and Time" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "month" ), 1, fcnMonth, QStringLiteral( "Date and Time" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "week" ), 1, fcnWeek, QStringLiteral( "Date and Time" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "day" ), 1, fcnDay, QStringLiteral( "Date and Time" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "hour" ), 1, fcnHour, QStringLiteral( "Date and Time" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "minute" ), 1, fcnMinute, QStringLiteral( "Date and Time" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "second" ), 1, fcnSeconds, QStringLiteral( "Date and Time" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnEpoch, QStringLiteral( "Date and Time" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "day_of_week" ), 1, fcnDayOfWeek, QStringLiteral( "Date and Time" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "lower" ), 1, fcnLower, QStringLiteral( "String" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "upper" ), 1, fcnUpper, QStringLiteral( "String" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "title" ), 1, fcnTitle, QStringLiteral( "String" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "trim" ), 1, fcnTrim, QStringLiteral( "String" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "levenshtein" ), 2, fcnLevenshtein, QStringLiteral( "Fuzzy Matching" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "longest_common_substring" ), 2, fcnLCS, QStringLiteral( "Fuzzy Matching" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "hamming_distance" ), 2, fcnHamming, QStringLiteral( "Fuzzy Matching" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "soundex" ), 1, fcnSoundex, QStringLiteral( "Fuzzy Matching" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "char" ), 1, fcnChar, QStringLiteral( "String" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "wordwrap" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "" ), fcnWordwrap, QStringLiteral( "String" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ), true, "" ), fcnLength, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "regexp_replace" ), 3, fcnRegexpReplace, QStringLiteral( "String" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "regexp_substr" ), 2, fcnRegexpSubstr, QStringLiteral( "String" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start " ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ), true ), fcnSubstr, QStringLiteral( "String" ), QString(), + false, QSet< QString >(), false, QStringList(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "concat" ), -1, fcnConcat, QStringLiteral( "String" ), QString(), false, QSet(), false, QStringList(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "strpos" ), 2, fcnStrpos, QStringLiteral( "String" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "left" ), 2, fcnLeft, QStringLiteral( "String" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "right" ), 2, fcnRight, QStringLiteral( "String" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "rpad" ), 3, fcnRPad, QStringLiteral( "String" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "lpad" ), 3, fcnLPad, QStringLiteral( "String" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "format" ), -1, fcnFormatString, QStringLiteral( "String" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "format_number" ), 2, fcnFormatNumber, QStringLiteral( "String" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "format_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ) ), fcnFormatDate, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "Date and Time" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "color_rgb" ), 3, fcnColorRgb, QStringLiteral( "Color" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "color_rgba" ), 4, fncColorRgba, QStringLiteral( "Color" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color" ), 2, fcnRampColor, QStringLiteral( "Color" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "color_hsl" ), 3, fcnColorHsl, QStringLiteral( "Color" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "color_hsla" ), 4, fncColorHsla, QStringLiteral( "Color" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "color_hsv" ), 3, fcnColorHsv, QStringLiteral( "Color" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "color_hsva" ), 4, fncColorHsva, QStringLiteral( "Color" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyk" ), 4, fcnColorCmyk, QStringLiteral( "Color" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyka" ), 5, fncColorCmyka, QStringLiteral( "Color" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "color_part" ), 2, fncColorPart, QStringLiteral( "Color" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "darker" ), 2, fncDarker, QStringLiteral( "Color" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "lighter" ), 2, fncLighter, QStringLiteral( "Color" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "set_color_part" ), 3, fncSetColorPart, QStringLiteral( "Color" ) ); + + QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( QStringLiteral( "$geometry" ), 0, fcnGeometry, QStringLiteral( "GeometryGroup" ), QString(), true ); + geomFunc->setIsStatic( false ); + sFunctions << geomFunc; + + QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( QStringLiteral( "$area" ), 0, fcnGeomArea, QStringLiteral( "GeometryGroup" ), QString(), true ); + areaFunc->setIsStatic( false ); + sFunctions << areaFunc; + + sFunctions << new QgsStaticExpressionFunction( QStringLiteral( "area" ), 1, fcnArea, QStringLiteral( "GeometryGroup" ) ); + + QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( QStringLiteral( "$length" ), 0, fcnGeomLength, QStringLiteral( "GeometryGroup" ), QString(), true ); + lengthFunc->setIsStatic( false ); + sFunctions << lengthFunc; + + QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( QStringLiteral( "$perimeter" ), 0, fcnGeomPerimeter, QStringLiteral( "GeometryGroup" ), QString(), true ); + perimeterFunc->setIsStatic( false ); + sFunctions << perimeterFunc; + + sFunctions << new QgsStaticExpressionFunction( QStringLiteral( "perimeter" ), 1, fcnPerimeter, QStringLiteral( "GeometryGroup" ) ); + + QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x" ), 0, fcnX, QStringLiteral( "GeometryGroup" ), QString(), true ); + xFunc->setIsStatic( false ); + sFunctions << xFunc; + + QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y" ), 0, fcnY, QStringLiteral( "GeometryGroup" ), QString(), true ); + yFunc->setIsStatic( false ); + sFunctions << yFunc; + + sFunctions + << new QgsStaticExpressionFunction( QStringLiteral( "x" ), 1, fcnGeomX, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "y" ), 1, fcnGeomY, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "z" ), 1, fcnGeomZ, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "m" ), 1, fcnGeomM, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "point_n" ), 2, fcnPointN, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "start_point" ), 1, fcnStartPoint, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "end_point" ), 1, fcnEndPoint, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "nodes_to_points" ), -1, fcnNodesToPoints, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "segments_to_lines" ), 1, fcnSegmentsToLines, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "make_point" ), -1, fcnMakePoint, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "make_point_m" ), 3, fcnMakePointM, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "make_line" ), -1, fcnMakeLine, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "make_polygon" ), -1, fcnMakePolygon, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "make_triangle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), + fcnMakeTriangle, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "make_circle" ), QgsExpressionFunction::ParameterList() + << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ), + fcnMakeCircle, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "make_ellipse" ), QgsExpressionFunction::ParameterList() + << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "semi_major_axis" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "semi_minor_axis" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ), + fcnMakeEllipse, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "make_regular_polygon" ), QgsExpressionFunction::ParameterList() + << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "number_sides" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "circle" ), true, 0 ), + fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) ); + + QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x_at" ), 1, fcnXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet(), false, QStringList() << QStringLiteral( "xat" ) << QStringLiteral( "x_at" ) ); + xAtFunc->setIsStatic( false ); + sFunctions << xAtFunc; + + QgsStaticExpressionFunction *yAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y_at" ), 1, fcnYat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet(), false, QStringList() << QStringLiteral( "yat" ) << QStringLiteral( "y_at" ) ); + yAtFunc->setIsStatic( false ); + sFunctions << yAtFunc; + + sFunctions + << new QgsStaticExpressionFunction( QStringLiteral( "x_min" ), 1, fcnXMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "xmin" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "x_max" ), 1, fcnXMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "xmax" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "y_min" ), 1, fcnYMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "ymin" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "y_max" ), 1, fcnYMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "ymax" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkt" ), 1, fcnGeomFromWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "geomFromWKT" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_gml" ), 1, fcnGeomFromGML, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "geomFromGML" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "relate" ), -1, fcnRelate, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "intersects_bbox" ), 2, fcnBbox, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "bbox" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "disjoint" ), 2, fcnDisjoint, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "intersects" ), 2, fcnIntersects, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "touches" ), 2, fcnTouches, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "crosses" ), 2, fcnCrosses, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "contains" ), 2, fcnContains, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "overlaps" ), 2, fcnOverlaps, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "within" ), 2, fcnWithin, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "translate" ), 3, fcnTranslate, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "buffer" ), -1, fcnBuffer, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "offset_curve" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 ) + << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QgsGeometry::JoinStyleRound ) + << QgsExpressionFunction::Parameter( QStringLiteral( "mitre_limit" ), true, 2.0 ), + fcnOffsetCurve, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "single_sided_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 ) + << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QgsGeometry::JoinStyleRound ) + << QgsExpressionFunction::Parameter( QStringLiteral( "mitre_limit" ), true, 2.0 ), + fcnSingleSidedBuffer, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "extend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ), + fcnExtend, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "centroid" ), 1, fcnCentroid, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "point_on_surface" ), 1, fcnPointOnSurface, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "pole_of_inaccessibility" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnPoleOfInaccessibility, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "reverse" ), 1, fcnReverse, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "exterior_ring" ), 1, fcnExteriorRing, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "interior_ring_n" ), 2, fcnInteriorRingN, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "geometry_n" ), 2, fcnGeometryN, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "boundary" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundary, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "line_merge" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLineMerge, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "bounds" ), 1, fcnBounds, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "simplify" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplify, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "simplify_vw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplifyVW, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "smooth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "iterations" ), true, 1 ) + << QgsExpressionFunction::Parameter( QStringLiteral( "offset" ), true, 0.25 ) + << QgsExpressionFunction::Parameter( QStringLiteral( "min_length" ), true, -1 ) + << QgsExpressionFunction::Parameter( QStringLiteral( "max_angle" ), true, 180 ), fcnSmooth, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "num_points" ), 1, fcnGeomNumPoints, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "num_interior_rings" ), 1, fcnGeomNumInteriorRings, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "num_rings" ), 1, fcnGeomNumRings, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "num_geometries" ), 1, fcnGeomNumGeometries, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "bounds_width" ), 1, fcnBoundsWidth, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "bounds_height" ), 1, fcnBoundsHeight, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "is_closed" ), 1, fcnIsClosed, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "convex_hull" ), 1, fcnConvexHull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "convexHull" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "difference" ), 2, fcnDifference, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "distance" ), 2, fcnDistance, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "intersection" ), 2, fcnIntersection, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "sym_difference" ), 2, fcnSymDifference, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "symDifference" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "combine" ), 2, fcnCombine, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "union" ), 2, fcnCombine, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkt" ), -1, fcnGeomToWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "geomToWKT" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "geometry" ), 1, fcnGetGeometry, QStringLiteral( "GeometryGroup" ), QString(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "transform" ), 3, fcnTransformGeometry, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "extrude" ), 3, fcnExtrude, QStringLiteral( "GeometryGroup" ), QString() );; + + QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction( QStringLiteral( "order_parts" ), 3, fcnOrderParts, QStringLiteral( "GeometryGroup" ), QString() ); + + orderPartsFunc->setIsStaticFunction( + []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context ) + { + Q_FOREACH ( QgsExpressionNode *argNode, node->args()->list() ) + { + if ( !argNode->isStatic( parent, context ) ) + return false; + } + + if ( node->args()->count() > 1 ) + { + QgsExpressionNode *argNode = node->args()->at( 1 ); + + QString expString = argNode->eval( parent, context ).toString(); + + QgsExpression e( expString ); + + if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) ) + return true; + } + + return true; + } ); + + orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context ) + { + if ( node->args()->count() > 1 ) + { + QgsExpressionNode *argNode = node->args()->at( 1 ); + QString expression = argNode->eval( parent, context ).toString(); + QgsExpression e( expression ); + e.prepare( context ); + context->setCachedValue( expression, QVariant::fromValue( e ) ); + } + return true; + } + ); + sFunctions << orderPartsFunc; + + sFunctions + << new QgsStaticExpressionFunction( QStringLiteral( "closest_point" ), 2, fcnClosestPoint, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "shortest_line" ), 2, fcnShortestLine, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolatePoint, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_angle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolateAngle, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnLineLocatePoint, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "angle_at_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnAngleAtVertex, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "distance_to_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) + << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnDistanceToVertex, QStringLiteral( "GeometryGroup" ) ); + + + // **Record** functions + + QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( QStringLiteral( "$id" ), 0, fcnFeatureId, QStringLiteral( "Record" ) ); + idFunc->setIsStatic( false ); + sFunctions << idFunc; + + QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( QStringLiteral( "$currentfeature" ), 0, fcnFeature, QStringLiteral( "Record" ) ); + currentFeatureFunc->setIsStatic( false ); + sFunctions << currentFeatureFunc; + + QgsStaticExpressionFunction *uuidFunc = new QgsStaticExpressionFunction( QStringLiteral( "uuid" ), 0, fcnUuid, QStringLiteral( "Record" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "$uuid" ) ); + uuidFunc->setIsStatic( true ); + sFunctions << uuidFunc; + + sFunctions + << new QgsStaticExpressionFunction( QStringLiteral( "get_feature" ), 3, fcnGetFeature, QStringLiteral( "Record" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "getFeature" ) ); + + QgsStaticExpressionFunction *isSelectedFunc = new QgsStaticExpressionFunction( + QStringLiteral( "is_selected" ), + -1, + fcnIsSelected, + QStringLiteral( "Record" ), + QString(), + false, + QSet() + ); + isSelectedFunc->setIsStatic( false ); + sFunctions << isSelectedFunc; + + sFunctions + << new QgsStaticExpressionFunction( + QStringLiteral( "num_selected" ), + -1, + fcnNumSelected, + QStringLiteral( "Record" ), + QString(), + false, + QSet() + ) + + // **General** 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" ) ); + + // **var** function + QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( QStringLiteral( "var" ), 1, fcnGetVariable, QStringLiteral( "General" ) ); + varFunction->setIsStaticFunction( + []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context ) + { + /* A variable node is static if it has a static name and the name can be found at prepare + * time and is tagged with isStatic. + * It is not static if a variable is set during iteration or not tagged isStatic. + * (e.g. geom_part variable) + */ + if ( node->args()->count() > 0 ) + { + QgsExpressionNode *argNode = node->args()->at( 0 ); + + if ( !argNode->isStatic( parent, context ) ) + return false; + + QString varName = argNode->eval( parent, context ).toString(); + + const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName ); + return scope ? scope->isStatic( varName ) : false; + } + return false; + } + ); + + sFunctions + << varFunction; + QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( QStringLiteral( "eval" ), 1, fcnEval, QStringLiteral( "General" ), QString(), true, QSet() << QgsFeatureRequest::ALL_ATTRIBUTES ); + evalFunc->setIsStaticFunction( + []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context ) + { + if ( node->args()->count() > 0 ) + { + QgsExpressionNode *argNode = node->args()->at( 0 ); + + if ( argNode->isStatic( parent, context ) ) + { + QString expString = argNode->eval( parent, context ).toString(); + + QgsExpression e( expString ); + + if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) ) + return true; + } + } + + return false; + } ); + + sFunctions << evalFunc; + + sFunctions + << new QgsStaticExpressionFunction( QStringLiteral( "env" ), 1, fcnEnvVar, QStringLiteral( "General" ), QString() ) + << new QgsStaticExpressionFunction( QStringLiteral( "attribute" ), 2, fcnAttribute, QStringLiteral( "Record" ), QString(), false, QSet() << QgsFeatureRequest::ALL_ATTRIBUTES ) + + // functions for arrays + << new QgsStaticExpressionFunction( QStringLiteral( "array" ), -1, fcnArray, QStringLiteral( "Arrays" ), QString(), false, QSet(), false, QStringList(), true ) + << new QgsStaticExpressionFunction( QStringLiteral( "array_length" ), 1, fcnArrayLength, QStringLiteral( "Arrays" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "array_contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayContains, QStringLiteral( "Arrays" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "array_find" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayFind, QStringLiteral( "Arrays" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "array_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayGet, QStringLiteral( "Arrays" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "array_append" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayAppend, QStringLiteral( "Arrays" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "array_prepend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayPrepend, QStringLiteral( "Arrays" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "array_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayInsert, QStringLiteral( "Arrays" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayRemoveAt, QStringLiteral( "Arrays" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayRemoveAll, QStringLiteral( "Arrays" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "array_cat" ), -1, fcnArrayCat, QStringLiteral( "Arrays" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "array_intersect" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array2" ) ), fcnArrayIntersect, QStringLiteral( "Arrays" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "array_distinct" ), 1, fcnArrayDistinct, QStringLiteral( "Arrays" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "array_to_string" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "," ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnArrayToString, QStringLiteral( "Arrays" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "string_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "," ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnStringToArray, QStringLiteral( "Arrays" ) ) + + //functions for maps + << new QgsStaticExpressionFunction( QStringLiteral( "map" ), -1, fcnMap, QStringLiteral( "Maps" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "map_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapGet, QStringLiteral( "Maps" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "map_exist" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapExist, QStringLiteral( "Maps" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "map_delete" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapDelete, QStringLiteral( "Maps" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "map_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnMapInsert, QStringLiteral( "Maps" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "map_concat" ), -1, fcnMapConcat, QStringLiteral( "Maps" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "map_akeys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAKeys, QStringLiteral( "Maps" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "map_avals" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAVals, QStringLiteral( "Maps" ) ) + ; + + QgsExpressionContextUtils::registerContextFunctions(); + + //QgsExpression has ownership of all built-in functions + Q_FOREACH ( QgsExpressionFunction *func, sFunctions ) + { + sOwnedFunctions << func; + sBuiltinFunctions << func->name(); + sBuiltinFunctions.append( func->aliases() ); + } + } + return sFunctions; +} diff --git a/src/core/expression/qgsexpressionfunction.h b/src/core/expression/qgsexpressionfunction.h index acb7b2e145b..992cdbf9a6b 100644 --- a/src/core/expression/qgsexpressionfunction.h +++ b/src/core/expression/qgsexpressionfunction.h @@ -1,3 +1,18 @@ +/*************************************************************************** + qgsexpressionfunction.h + ------------------- + begin : May 2017 + copyright : (C) 2017 Matthias Kuhn + email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + #ifndef QGSEXPRESSIONFUNCTION_H #define QGSEXPRESSIONFUNCTION_H @@ -421,6 +436,7 @@ class QgsStaticExpressionFunction : public QgsExpressionFunction */ void setPrepareFunction( std::function < bool( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) > prepareFunc ); + static const QList &functions(); private: FcnEval mFnc; diff --git a/src/core/expression/qgsexpressionnode.cpp b/src/core/expression/qgsexpressionnode.cpp index 01b1e5fb148..c7f8d726229 100644 --- a/src/core/expression/qgsexpressionnode.cpp +++ b/src/core/expression/qgsexpressionnode.cpp @@ -1 +1,51 @@ +/*************************************************************************** + qgsexpressionnode.cpp + ------------------- + begin : May 2017 + copyright : (C) 2017 Matthias Kuhn + email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + #include "qgsexpressionnode.h" + + +QVariant QgsExpressionNode::eval( QgsExpression *parent, const QgsExpressionContext *context ) +{ + if ( mHasCachedValue ) + { + return mCachedStaticValue; + } + else + { + QVariant res = evalNode( parent, context ); + return res; + } +} + +bool QgsExpressionNode::prepare( QgsExpression *parent, const QgsExpressionContext *context ) +{ + if ( isStatic( parent, context ) ) + { + mCachedStaticValue = evalNode( parent, context ); + mHasCachedValue = true; + return true; + } + else + { + mHasCachedValue = false; + return prepareNode( parent, context ); + } +} + +void QgsExpressionNode::cloneTo( QgsExpressionNode *target ) const +{ + target->mHasCachedValue = mHasCachedValue; + target->mCachedStaticValue = mCachedStaticValue; +} diff --git a/src/core/expression/qgsexpressionnode.h b/src/core/expression/qgsexpressionnode.h index be81e74ff90..83404092d03 100644 --- a/src/core/expression/qgsexpressionnode.h +++ b/src/core/expression/qgsexpressionnode.h @@ -1,3 +1,19 @@ +/*************************************************************************** + qgsexpressionnode.h + ------------------- + begin : May 2017 + copyright : (C) 2017 Matthias Kuhn + email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + + #ifndef QGSEXPRESSIONNODE_H #define QGSEXPRESSIONNODE_H diff --git a/src/core/expression/qgsexpressionnodeimpl.cpp b/src/core/expression/qgsexpressionnodeimpl.cpp index eeb26605afd..d5abfd6c0b9 100644 --- a/src/core/expression/qgsexpressionnodeimpl.cpp +++ b/src/core/expression/qgsexpressionnodeimpl.cpp @@ -1,4 +1,1430 @@ +/*************************************************************************** + qgsexpressionnodeimpl.cpp + ------------------- + begin : May 2017 + copyright : (C) 2017 Matthias Kuhn + email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + #include "qgsexpressionnodeimpl.h" +#include "qgsexpressionutils.h" +#include "qgsexpression.h" + +#include "qgsgeometry.h" +#include "qgsfeaturerequest.h" + +const char *QgsExpressionNodeBinaryOperator::BINARY_OPERATOR_TEXT[] = +{ + // this must correspond (number and order of element) to the declaration of the enum BinaryOperator + "OR", "AND", + "=", "<>", "<=", ">=", "<", ">", "~", "LIKE", "NOT LIKE", "ILIKE", "NOT ILIKE", "IS", "IS NOT", + "+", "-", "*", "/", "//", "%", "^", + "||" +}; + +const char *QgsExpressionNodeUnaryOperator::UNARY_OPERATOR_TEXT[] = +{ + // this must correspond (number and order of element) to the declaration of the enum UnaryOperator + "NOT", "-" +}; + +bool QgsExpressionNodeInOperator::needsGeometry() const +{ + bool needs = false; + Q_FOREACH ( QgsExpressionNode *n, mList->list() ) + needs |= n->needsGeometry(); + return needs; +} + +QgsExpressionNode::NodeList::~NodeList() +{ + qDeleteAll( mList ); +} + +void QgsExpressionNode::NodeList::append( QgsExpressionNode::NamedNode *node ) +{ + mList.append( node->node ); + mNameList.append( node->name.toLower() ); + mHasNamedNodes = true; + delete node; +} + +QgsExpressionNode::NodeList *QgsExpressionNode::NodeList::clone() const +{ + NodeList *nl = new NodeList; + Q_FOREACH ( QgsExpressionNode *node, mList ) + { + nl->mList.append( node->clone() ); + } + nl->mNameList = mNameList; + + return nl; +} + +QString QgsExpressionNode::NodeList::dump() const +{ + QString msg; + bool first = true; + Q_FOREACH ( QgsExpressionNode *n, mList ) + { + if ( !first ) msg += QLatin1String( ", " ); + else first = false; + msg += n->dump(); + } + return msg; +} + + +// + +QVariant QgsExpressionNodeUnaryOperator::evalNode( QgsExpression *parent, const QgsExpressionContext *context ) +{ + QVariant val = mOperand->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + + switch ( mOp ) + { + case uoNot: + { + TVL tvl = getTVLValue( val, parent ); + ENSURE_NO_EVAL_ERROR; + return tvl2variant( NOT[tvl] ); + } + + case uoMinus: + if ( isIntSafe( val ) ) + return QVariant( - getIntValue( val, parent ) ); + else if ( isDoubleSafe( val ) ) + return QVariant( - getDoubleValue( val, parent ) ); + else + SET_EVAL_ERROR( tr( "Unary minus only for numeric values." ) ); + default: + Q_ASSERT( false && "unknown unary operation" ); + } + return QVariant(); +} + +QgsExpressionNode::NodeType QgsExpressionNodeUnaryOperator::nodeType() const +{ + return ntUnaryOperator; +} + +bool QgsExpressionNodeUnaryOperator::prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) +{ + return mOperand->prepare( parent, context ); +} + +QString QgsExpressionNodeUnaryOperator::dump() const +{ + return QStringLiteral( "%1 %2" ).arg( UNARY_OPERATOR_TEXT[mOp], mOperand->dump() ); +} + +QSet QgsExpressionNodeUnaryOperator::referencedColumns() const +{ + return mOperand->referencedColumns(); +} + +QSet QgsExpressionNodeUnaryOperator::referencedVariables() const +{ + return mOperand->referencedVariables(); +} + +bool QgsExpressionNodeUnaryOperator::needsGeometry() const +{ + return mOperand->needsGeometry(); +} + +QgsExpressionNode *QgsExpressionNodeUnaryOperator::clone() const +{ + QgsExpressionNodeUnaryOperator *copy = new QgsExpressionNodeUnaryOperator( mOp, mOperand->clone() ); + cloneTo( copy ); + return copy; +} + +bool QgsExpressionNodeUnaryOperator::isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const +{ + return mOperand->isStatic( parent, context ); +} + +QString QgsExpressionNodeUnaryOperator::text() const +{ + return UNARY_OPERATOR_TEXT[mOp]; +} + +// + +QVariant QgsExpressionNodeBinaryOperator::evalNode( QgsExpression *parent, const QgsExpressionContext *context ) +{ + QVariant vL = mOpLeft->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + QVariant vR = mOpRight->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + + switch ( mOp ) + { + case boPlus: + if ( vL.type() == QVariant::String && vR.type() == QVariant::String ) + { + QString sL = isNull( vL ) ? QString() : getStringValue( vL, parent ); + ENSURE_NO_EVAL_ERROR; + QString sR = isNull( vR ) ? QString() : getStringValue( vR, parent ); + ENSURE_NO_EVAL_ERROR; + return QVariant( sL + sR ); + } + //intentional fall-through + FALLTHROUGH; + case boMinus: + case boMul: + case boDiv: + case boMod: + { + if ( isNull( vL ) || isNull( vR ) ) + return QVariant(); + else if ( mOp != boDiv && isIntSafe( vL ) && isIntSafe( vR ) ) + { + // both are integers - let's use integer arithmetics + qlonglong iL = getIntValue( vL, parent ); + ENSURE_NO_EVAL_ERROR; + qlonglong iR = getIntValue( vR, parent ); + ENSURE_NO_EVAL_ERROR; + + if ( mOp == boMod && iR == 0 ) + return QVariant(); + + return QVariant( computeInt( iL, iR ) ); + } + else if ( isDateTimeSafe( vL ) && isIntervalSafe( vR ) ) + { + QDateTime dL = getDateTimeValue( vL, parent ); + ENSURE_NO_EVAL_ERROR; + QgsInterval iL = getInterval( vR, parent ); + ENSURE_NO_EVAL_ERROR; + if ( mOp == boDiv || mOp == boMul || mOp == boMod ) + { + parent->setEvalErrorString( tr( "Can't perform /, *, or % on DateTime and Interval" ) ); + return QVariant(); + } + return QVariant( computeDateTimeFromInterval( dL, &iL ) ); + } + else if ( mOp == boPlus && ( ( vL.type() == QVariant::Date && vR.type() == QVariant::Time ) || + ( vR.type() == QVariant::Date && vL.type() == QVariant::Time ) ) ) + { + QDate date = getDateValue( vL.type() == QVariant::Date ? vL : vR, parent ); + ENSURE_NO_EVAL_ERROR; + QTime time = getTimeValue( vR.type() == QVariant::Time ? vR : vL, parent ); + ENSURE_NO_EVAL_ERROR; + QDateTime dt = QDateTime( date, time ); + return QVariant( dt ); + } + else if ( mOp == boMinus && vL.type() == QVariant::Date && vR.type() == QVariant::Date ) + { + QDate date1 = getDateValue( vL, parent ); + ENSURE_NO_EVAL_ERROR; + QDate date2 = getDateValue( vR, parent ); + ENSURE_NO_EVAL_ERROR; + return date1 - date2; + } + else if ( mOp == boMinus && vL.type() == QVariant::Time && vR.type() == QVariant::Time ) + { + QTime time1 = getTimeValue( vL, parent ); + ENSURE_NO_EVAL_ERROR; + QTime time2 = getTimeValue( vR, parent ); + ENSURE_NO_EVAL_ERROR; + return time1 - time2; + } + else if ( mOp == boMinus && vL.type() == QVariant::DateTime && vR.type() == QVariant::DateTime ) + { + QDateTime datetime1 = getDateTimeValue( vL, parent ); + ENSURE_NO_EVAL_ERROR; + QDateTime datetime2 = getDateTimeValue( vR, parent ); + ENSURE_NO_EVAL_ERROR; + return datetime1 - datetime2; + } + else + { + // general floating point arithmetic + double fL = getDoubleValue( vL, parent ); + ENSURE_NO_EVAL_ERROR; + double fR = getDoubleValue( vR, parent ); + ENSURE_NO_EVAL_ERROR; + if ( ( mOp == boDiv || mOp == boMod ) && fR == 0. ) + return QVariant(); // silently handle division by zero and return NULL + return QVariant( computeDouble( fL, fR ) ); + } + } + case boIntDiv: + { + //integer division + double fL = getDoubleValue( vL, parent ); + ENSURE_NO_EVAL_ERROR; + double fR = getDoubleValue( vR, parent ); + ENSURE_NO_EVAL_ERROR; + if ( fR == 0. ) + return QVariant(); // silently handle division by zero and return NULL + return QVariant( qFloor( fL / fR ) ); + } + case boPow: + if ( isNull( vL ) || isNull( vR ) ) + return QVariant(); + else + { + double fL = getDoubleValue( vL, parent ); + ENSURE_NO_EVAL_ERROR; + double fR = getDoubleValue( vR, parent ); + ENSURE_NO_EVAL_ERROR; + return QVariant( pow( fL, fR ) ); + } + + case boAnd: + { + TVL tvlL = getTVLValue( vL, parent ), tvlR = getTVLValue( vR, parent ); + ENSURE_NO_EVAL_ERROR; + return tvl2variant( AND[tvlL][tvlR] ); + } + + case boOr: + { + TVL tvlL = getTVLValue( vL, parent ), tvlR = getTVLValue( vR, parent ); + ENSURE_NO_EVAL_ERROR; + return tvl2variant( OR[tvlL][tvlR] ); + } + + case boEQ: + case boNE: + case boLT: + case boGT: + case boLE: + case boGE: + if ( isNull( vL ) || isNull( vR ) ) + { + return TVL_Unknown; + } + else if ( isList( vL ) || isList( vR ) ) + { + // verify that we have two lists + if ( !isList( vL ) || !isList( vR ) ) + return TVL_Unknown; + + // and search for not equal respective items + QVariantList lL = vL.toList(); + QVariantList lR = vR.toList(); + for ( int i = 0; i < lL.length() && i < lR.length(); i++ ) + { + if ( isNull( lL.at( i ) ) && isNull( lR.at( i ) ) ) + continue; // same behavior as PostgreSQL + + if ( isNull( lL.at( i ) ) || isNull( lR.at( i ) ) ) + { + switch ( mOp ) + { + case boEQ: + return false; + case boNE: + return true; + case boLT: + case boLE: + return isNull( lR.at( i ) ); + case boGT: + case boGE: + return isNull( lL.at( i ) ); + default: + Q_ASSERT( false ); + return TVL_Unknown; + } + } + + QgsExpressionNodeLiteral nL( lL.at( i ) ); + QgsExpressionNodeLiteral nR( lR.at( i ) ); + QgsExpressionNodeBinaryOperator eqNode( boEQ, nL.clone(), nR.clone() ); + QVariant eq = eqNode.eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + if ( eq == TVL_False ) + { + // return the two items comparison + QgsExpressionNodeBinaryOperator node( mOp, nL.clone(), nR.clone() ); + QVariant v = node.eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + return v; + } + } + + // default to length comparison + switch ( mOp ) + { + case boEQ: + return lL.length() == lR.length(); + case boNE: + return lL.length() != lR.length(); + case boLT: + return lL.length() < lR.length(); + case boGT: + return lL.length() > lR.length(); + case boLE: + return lL.length() <= lR.length(); + case boGE: + return lL.length() >= lR.length(); + default: + Q_ASSERT( false ); + return TVL_Unknown; + } + } + else if ( isDoubleSafe( vL ) && isDoubleSafe( vR ) && + ( vL.type() != QVariant::String || vR.type() != QVariant::String ) ) + { + // do numeric comparison if both operators can be converted to numbers, + // and they aren't both string + double fL = getDoubleValue( vL, parent ); + ENSURE_NO_EVAL_ERROR; + double fR = getDoubleValue( vR, parent ); + ENSURE_NO_EVAL_ERROR; + return compare( fL - fR ) ? TVL_True : TVL_False; + } + else + { + // do string comparison otherwise + QString sL = getStringValue( vL, parent ); + ENSURE_NO_EVAL_ERROR; + QString sR = getStringValue( vR, parent ); + ENSURE_NO_EVAL_ERROR; + int diff = QString::compare( sL, sR ); + return compare( diff ) ? TVL_True : TVL_False; + } + + case boIs: + case boIsNot: + if ( isNull( vL ) && isNull( vR ) ) // both operators null + return ( mOp == boIs ? TVL_True : TVL_False ); + else if ( isNull( vL ) || isNull( vR ) ) // one operator null + return ( mOp == boIs ? TVL_False : TVL_True ); + else // both operators non-null + { + bool equal = false; + if ( isDoubleSafe( vL ) && isDoubleSafe( vR ) && + ( vL.type() != QVariant::String || vR.type() != QVariant::String ) ) + { + double fL = getDoubleValue( vL, parent ); + ENSURE_NO_EVAL_ERROR; + double fR = getDoubleValue( vR, parent ); + ENSURE_NO_EVAL_ERROR; + equal = qgsDoubleNear( fL, fR ); + } + else + { + QString sL = getStringValue( vL, parent ); + ENSURE_NO_EVAL_ERROR; + QString sR = getStringValue( vR, parent ); + ENSURE_NO_EVAL_ERROR; + equal = QString::compare( sL, sR ) == 0; + } + if ( equal ) + return mOp == boIs ? TVL_True : TVL_False; + else + return mOp == boIs ? TVL_False : TVL_True; + } + + case boRegexp: + case boLike: + case boNotLike: + case boILike: + case boNotILike: + if ( isNull( vL ) || isNull( vR ) ) + return TVL_Unknown; + else + { + QString str = getStringValue( vL, parent ); + ENSURE_NO_EVAL_ERROR; + QString regexp = getStringValue( vR, parent ); + ENSURE_NO_EVAL_ERROR; + // TODO: cache QRegExp in case that regexp is a literal string (i.e. it will stay constant) + bool matches; + if ( mOp == boLike || mOp == boILike || mOp == boNotLike || mOp == boNotILike ) // change from LIKE syntax to regexp + { + QString esc_regexp = QRegExp::escape( regexp ); + // manage escape % and _ + if ( esc_regexp.startsWith( '%' ) ) + { + esc_regexp.replace( 0, 1, QStringLiteral( ".*" ) ); + } + QRegExp rx( "[^\\\\](%)" ); + int pos = 0; + while ( ( pos = rx.indexIn( esc_regexp, pos ) ) != -1 ) + { + esc_regexp.replace( pos + 1, 1, QStringLiteral( ".*" ) ); + pos += 1; + } + rx.setPattern( QStringLiteral( "\\\\%" ) ); + esc_regexp.replace( rx, QStringLiteral( "%" ) ); + if ( esc_regexp.startsWith( '_' ) ) + { + esc_regexp.replace( 0, 1, QStringLiteral( "." ) ); + } + rx.setPattern( QStringLiteral( "[^\\\\](_)" ) ); + pos = 0; + while ( ( pos = rx.indexIn( esc_regexp, pos ) ) != -1 ) + { + esc_regexp.replace( pos + 1, 1, '.' ); + pos += 1; + } + rx.setPattern( QStringLiteral( "\\\\_" ) ); + esc_regexp.replace( rx, QStringLiteral( "_" ) ); + matches = QRegExp( esc_regexp, mOp == boLike || mOp == boNotLike ? Qt::CaseSensitive : Qt::CaseInsensitive ).exactMatch( str ); + } + else + { + matches = QRegExp( regexp ).indexIn( str ) != -1; + } + + if ( mOp == boNotLike || mOp == boNotILike ) + { + matches = !matches; + } + + return matches ? TVL_True : TVL_False; + } + + case boConcat: + if ( isNull( vL ) || isNull( vR ) ) + return QVariant(); + else + { + QString sL = getStringValue( vL, parent ); + ENSURE_NO_EVAL_ERROR; + QString sR = getStringValue( vR, parent ); + ENSURE_NO_EVAL_ERROR; + return QVariant( sL + sR ); + } + + default: + break; + } + Q_ASSERT( false ); + return QVariant(); +} + +bool QgsExpressionNodeBinaryOperator::compare( double diff ) +{ + switch ( mOp ) + { + case boEQ: + return qgsDoubleNear( diff, 0.0 ); + case boNE: + return !qgsDoubleNear( diff, 0.0 ); + case boLT: + return diff < 0; + case boGT: + return diff > 0; + case boLE: + return diff <= 0; + case boGE: + return diff >= 0; + default: + Q_ASSERT( false ); + return false; + } +} + +qlonglong QgsExpressionNodeBinaryOperator::computeInt( qlonglong x, qlonglong y ) +{ + switch ( mOp ) + { + case boPlus: + return x + y; + case boMinus: + return x - y; + case boMul: + return x * y; + case boDiv: + return x / y; + case boMod: + return x % y; + default: + Q_ASSERT( false ); + return 0; + } +} + +QDateTime QgsExpressionNodeBinaryOperator::computeDateTimeFromInterval( const QDateTime &d, QgsInterval *i ) +{ + switch ( mOp ) + { + case boPlus: + return d.addSecs( i->seconds() ); + case boMinus: + return d.addSecs( -i->seconds() ); + default: + Q_ASSERT( false ); + return QDateTime(); + } +} + +double QgsExpressionNodeBinaryOperator::computeDouble( double x, double y ) +{ + switch ( mOp ) + { + case boPlus: + return x + y; + case boMinus: + return x - y; + case boMul: + return x * y; + case boDiv: + return x / y; + case boMod: + return fmod( x, y ); + default: + Q_ASSERT( false ); + return 0; + } +} + +QgsExpressionNode::NodeType QgsExpressionNodeBinaryOperator::nodeType() const +{ + return ntBinaryOperator; +} + +bool QgsExpressionNodeBinaryOperator::prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) +{ + bool resL = mOpLeft->prepare( parent, context ); + bool resR = mOpRight->prepare( parent, context ); + return resL && resR; +} + +int QgsExpressionNodeBinaryOperator::precedence() const +{ + // see left/right in qgsexpressionparser.yy + switch ( mOp ) + { + case boOr: + return 1; + + case boAnd: + return 2; + + case boEQ: + case boNE: + case boLE: + case boGE: + case boLT: + case boGT: + case boRegexp: + case boLike: + case boILike: + case boNotLike: + case boNotILike: + case boIs: + case boIsNot: + return 3; + + case boPlus: + case boMinus: + return 4; + + case boMul: + case boDiv: + case boIntDiv: + case boMod: + return 5; + + case boPow: + return 6; + + case boConcat: + return 7; + } + Q_ASSERT( false && "unexpected binary operator" ); + return -1; +} + +bool QgsExpressionNodeBinaryOperator::leftAssociative() const +{ + // see left/right in qgsexpressionparser.yy + switch ( mOp ) + { + case boOr: + case boAnd: + case boEQ: + case boNE: + case boLE: + case boGE: + case boLT: + case boGT: + case boRegexp: + case boLike: + case boILike: + case boNotLike: + case boNotILike: + case boIs: + case boIsNot: + case boPlus: + case boMinus: + case boMul: + case boDiv: + case boIntDiv: + case boMod: + case boConcat: + return true; + + case boPow: + return false; + } + Q_ASSERT( false && "unexpected binary operator" ); + return false; +} + +QString QgsExpressionNodeBinaryOperator::dump() const +{ + QgsExpressionNodeBinaryOperator *lOp = dynamic_cast( mOpLeft ); + QgsExpressionNodeBinaryOperator *rOp = dynamic_cast( mOpRight ); + QgsExpressionNodeUnaryOperator *ruOp = dynamic_cast( mOpRight ); + + QString rdump( mOpRight->dump() ); + + // avoid dumping "IS (NOT ...)" as "IS NOT ..." + if ( mOp == boIs && ruOp && ruOp->op() == QgsExpressionNodeUnaryOperator::uoNot ) + { + rdump.prepend( '(' ).append( ')' ); + } + + QString fmt; + if ( leftAssociative() ) + { + fmt += lOp && ( lOp->precedence() < precedence() ) ? "(%1)" : "%1"; + fmt += QLatin1String( " %2 " ); + fmt += rOp && ( rOp->precedence() <= precedence() ) ? "(%3)" : "%3"; + } + else + { + fmt += lOp && ( lOp->precedence() <= precedence() ) ? "(%1)" : "%1"; + fmt += QLatin1String( " %2 " ); + fmt += rOp && ( rOp->precedence() < precedence() ) ? "(%3)" : "%3"; + } + + return fmt.arg( mOpLeft->dump(), BINARY_OPERATOR_TEXT[mOp], rdump ); +} + +QSet QgsExpressionNodeBinaryOperator::referencedColumns() const +{ + return mOpLeft->referencedColumns() + mOpRight->referencedColumns(); +} + +QSet QgsExpressionNodeBinaryOperator::referencedVariables() const +{ + return mOpLeft->referencedVariables() + mOpRight->referencedVariables(); +} + +bool QgsExpressionNodeBinaryOperator::needsGeometry() const +{ + return mOpLeft->needsGeometry() || mOpRight->needsGeometry(); +} + +QgsExpressionNode *QgsExpressionNodeBinaryOperator::clone() const +{ + QgsExpressionNodeBinaryOperator *copy = new QgsExpressionNodeBinaryOperator( mOp, mOpLeft->clone(), mOpRight->clone() ); + cloneTo( copy ); + return copy; +} + +bool QgsExpressionNodeBinaryOperator::isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const +{ + return mOpLeft->isStatic( parent, context ) && mOpRight->isStatic( parent, context ); +} + +// + +QVariant QgsExpressionNodeInOperator::evalNode( QgsExpression *parent, const QgsExpressionContext *context ) +{ + if ( mList->count() == 0 ) + return mNotIn ? TVL_True : TVL_False; + QVariant v1 = mNode->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + if ( isNull( v1 ) ) + return TVL_Unknown; + + bool listHasNull = false; + + Q_FOREACH ( QgsExpressionNode *n, mList->list() ) + { + QVariant v2 = n->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + if ( isNull( v2 ) ) + listHasNull = true; + else + { + bool equal = false; + // check whether they are equal + if ( isDoubleSafe( v1 ) && isDoubleSafe( v2 ) ) + { + double f1 = getDoubleValue( v1, parent ); + ENSURE_NO_EVAL_ERROR; + double f2 = getDoubleValue( v2, parent ); + ENSURE_NO_EVAL_ERROR; + equal = qgsDoubleNear( f1, f2 ); + } + else + { + QString s1 = getStringValue( v1, parent ); + ENSURE_NO_EVAL_ERROR; + QString s2 = getStringValue( v2, parent ); + ENSURE_NO_EVAL_ERROR; + equal = QString::compare( s1, s2 ) == 0; + } + + if ( equal ) // we know the result + return mNotIn ? TVL_False : TVL_True; + } + } + + // item not found + if ( listHasNull ) + return TVL_Unknown; + else + return mNotIn ? TVL_True : TVL_False; +} + +QgsExpressionNodeInOperator::~QgsExpressionNodeInOperator() +{ + delete mNode; + delete mList; +} + +QgsExpressionNode::NodeType QgsExpressionNodeInOperator::nodeType() const +{ + return ntInOperator; +} + +bool QgsExpressionNodeInOperator::prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) +{ + bool res = mNode->prepare( parent, context ); + Q_FOREACH ( QgsExpressionNode *n, mList->list() ) + { + res = res && n->prepare( parent, context ); + } + return res; +} + +QString QgsExpressionNodeInOperator::dump() const +{ + return QStringLiteral( "%1 %2 IN (%3)" ).arg( mNode->dump(), mNotIn ? "NOT" : "", mList->dump() ); +} + +QgsExpressionNode *QgsExpressionNodeInOperator::clone() const +{ + QgsExpressionNodeInOperator *copy = new QgsExpressionNodeInOperator( mNode->clone(), mList->clone(), mNotIn ); + cloneTo( copy ); + return copy; +} + +bool QgsExpressionNodeInOperator::isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const +{ + if ( !mNode->isStatic( parent, context ) ) + return false; + + Q_FOREACH ( QgsExpressionNode *n, mList->list() ) + { + if ( !n->isStatic( parent, context ) ) + return false; + } + + return true; +} + +// + +QVariant QgsExpressionNodeFunction::evalNode( QgsExpression *parent, const QgsExpressionContext *context ) +{ + QString name = QgsExpression::QgsExpression::Functions()[mFnIndex]->name(); + QgsExpressionFunction *fd = context && context->hasFunction( name ) ? context->function( name ) : QgsExpression::QgsExpression::Functions()[mFnIndex]; + + // evaluate arguments + QVariantList argValues; + if ( mArgs ) + { + Q_FOREACH ( QgsExpressionNode *n, mArgs->list() ) + { + QVariant v; + if ( fd->lazyEval() ) + { + // Pass in the node for the function to eval as it needs. + v = QVariant::fromValue( n ); + } + else + { + v = n->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + if ( isNull( v ) && !fd->handlesNull() ) + return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal) + } + argValues.append( v ); + } + } + + // run the function + QVariant res = fd->func( argValues, context, parent ); + ENSURE_NO_EVAL_ERROR; + + // everything went fine + return res; +} + +QgsExpressionNodeFunction::QgsExpressionNodeFunction( int fnIndex, QgsExpressionNode::NodeList *args ) + : mFnIndex( fnIndex ) +{ + const QgsExpressionFunction::ParameterList &functionParams = QgsExpression::QgsExpression::Functions()[mFnIndex]->parameters(); + if ( !args || functionParams.isEmpty() ) + { + // no QgsExpressionFunction::Parameters, or function does not support them + mArgs = args; + } + else + { + mArgs = new NodeList(); + + int idx = 0; + //first loop through unnamed arguments + while ( idx < args->names().size() && args->names().at( idx ).isEmpty() ) + { + mArgs->append( args->list().at( idx )->clone() ); + idx++; + } + + //next copy named QgsExpressionFunction::Parameters in order expected by function + for ( ; idx < functionParams.count(); ++idx ) + { + int nodeIdx = args->names().indexOf( functionParams.at( idx ).name().toLower() ); + if ( nodeIdx < 0 ) + { + //QgsExpressionFunction::Parameter not found - insert default value for QgsExpressionFunction::Parameter + mArgs->append( new QgsExpressionNodeLiteral( functionParams.at( idx ).defaultValue() ) ); + } + else + { + mArgs->append( args->list().at( nodeIdx )->clone() ); + } + } + + delete args; + } +} + +QgsExpressionNodeFunction::~QgsExpressionNodeFunction() +{ + delete mArgs; +} + +QgsExpressionNode::NodeType QgsExpressionNodeFunction::nodeType() const +{ + return ntFunction; +} + +bool QgsExpressionNodeFunction::prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) +{ + QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[mFnIndex]; + + bool res = fd->prepare( this, parent, context ); + if ( mArgs && !fd->lazyEval() ) + { + Q_FOREACH ( QgsExpressionNode *n, mArgs->list() ) + { + res = res && n->prepare( parent, context ); + } + } + return res; +} + +QString QgsExpressionNodeFunction::dump() const +{ + QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[mFnIndex]; + if ( fd->params() == 0 ) + return QStringLiteral( "%1%2" ).arg( fd->name(), fd->name().startsWith( '$' ) ? "" : "()" ); // special column + else + return QStringLiteral( "%1(%2)" ).arg( fd->name(), mArgs ? mArgs->dump() : QString() ); // function +} + +QSet QgsExpressionNodeFunction::referencedColumns() const +{ + QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[mFnIndex]; + QSet functionColumns = fd->referencedColumns( this ); + + if ( !mArgs ) + { + //no referenced columns in arguments, just return function's referenced columns + return functionColumns; + } + + Q_FOREACH ( QgsExpressionNode *n, mArgs->list() ) + { + functionColumns.unite( n->referencedColumns() ); + } + + return functionColumns; +} + +QSet QgsExpressionNodeFunction::referencedVariables() const +{ + QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[mFnIndex]; + if ( fd->name() == "var" ) + { + if ( !mArgs->list().isEmpty() ) + { + QgsExpressionNodeLiteral *var = dynamic_cast( mArgs->list().first() ); + if ( var ) + return QSet() << var->value().toString(); + } + return QSet() << QString(); + } + else + { + QSet functionVariables = QSet(); + + if ( !mArgs ) + return functionVariables; + + Q_FOREACH ( QgsExpressionNode *n, mArgs->list() ) + { + functionVariables.unite( n->referencedVariables() ); + } + + return functionVariables; + } +} + +bool QgsExpressionNodeFunction::needsGeometry() const +{ + bool needs = QgsExpression::QgsExpression::Functions()[mFnIndex]->usesGeometry( this ); + if ( mArgs ) + { + Q_FOREACH ( QgsExpressionNode *n, mArgs->list() ) + needs |= n->needsGeometry(); + } + return needs; +} + +QgsExpressionNode *QgsExpressionNodeFunction::clone() const +{ + QgsExpressionNodeFunction *copy = new QgsExpressionNodeFunction( mFnIndex, mArgs ? mArgs->clone() : nullptr ); + cloneTo( copy ); + return copy; +} + +bool QgsExpressionNodeFunction::isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const +{ + return QgsExpression::Functions()[mFnIndex]->isStatic( this, parent, context ); +} + +bool QgsExpressionNodeFunction::validateParams( int fnIndex, QgsExpressionNode::NodeList *args, QString &error ) +{ + if ( !args || !args->hasNamedNodes() ) + return true; + + const QgsExpressionFunction::ParameterList &functionParams = QgsExpression::Functions()[fnIndex]->parameters(); + if ( functionParams.isEmpty() ) + { + error = QStringLiteral( "%1 does not support named QgsExpressionFunction::Parameters" ).arg( QgsExpression::Functions()[fnIndex]->name() ); + return false; + } + else + { + QSet< int > providedArgs; + QSet< int > handledArgs; + int idx = 0; + //first loop through unnamed arguments + while ( args->names().at( idx ).isEmpty() ) + { + providedArgs << idx; + handledArgs << idx; + idx++; + } + + //next check named QgsExpressionFunction::Parameters + for ( ; idx < functionParams.count(); ++idx ) + { + int nodeIdx = args->names().indexOf( functionParams.at( idx ).name().toLower() ); + if ( nodeIdx < 0 ) + { + if ( !functionParams.at( idx ).optional() ) + { + error = QStringLiteral( "No value specified for QgsExpressionFunction::Parameter '%1' for %2" ).arg( functionParams.at( idx ).name(), QgsExpression::Functions()[fnIndex]->name() ); + return false; + } + } + else + { + if ( providedArgs.contains( idx ) ) + { + error = QStringLiteral( "Duplicate QgsExpressionFunction::Parameter specified for '%1' for %2" ).arg( functionParams.at( idx ).name(), QgsExpression::Functions()[fnIndex]->name() ); + return false; + } + } + providedArgs << idx; + handledArgs << nodeIdx; + } + + //last check for bad names + idx = 0; + Q_FOREACH ( const QString &name, args->names() ) + { + if ( !name.isEmpty() && !functionParams.contains( name ) ) + { + error = QStringLiteral( "Invalid QgsExpressionFunction::Parameter name '%1' for %2" ).arg( name, QgsExpression::Functions()[fnIndex]->name() ); + return false; + } + if ( !name.isEmpty() && !handledArgs.contains( idx ) ) + { + int functionIdx = functionParams.indexOf( name ); + if ( providedArgs.contains( functionIdx ) ) + { + error = QStringLiteral( "Duplicate QgsExpressionFunction::Parameter specified for '%1' for %2" ).arg( functionParams.at( functionIdx ).name(), QgsExpression::Functions()[fnIndex]->name() ); + return false; + } + } + idx++; + } + + } + return true; +} + +// + +QVariant QgsExpressionNodeLiteral::evalNode( QgsExpression *parent, const QgsExpressionContext *context ) +{ + Q_UNUSED( context ); + Q_UNUSED( parent ); + return mValue; +} + +QgsExpressionNode::NodeType QgsExpressionNodeLiteral::nodeType() const +{ + return ntLiteral; +} + +bool QgsExpressionNodeLiteral::prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) +{ + Q_UNUSED( parent ); + Q_UNUSED( context ); + return true; +} + + +QString QgsExpressionNodeLiteral::dump() const +{ + if ( mValue.isNull() ) + return QStringLiteral( "NULL" ); + + switch ( mValue.type() ) + { + case QVariant::Int: + return QString::number( mValue.toInt() ); + case QVariant::Double: + return QString::number( mValue.toDouble() ); + case QVariant::String: + return QgsExpression::quotedString( mValue.toString() ); + case QVariant::Bool: + return mValue.toBool() ? QStringLiteral( "TRUE" ) : QStringLiteral( "FALSE" ); + default: + return tr( "[unsupported type;%1; value:%2]" ).arg( mValue.typeName(), mValue.toString() ); + } +} + +QSet QgsExpressionNodeLiteral::referencedColumns() const +{ + return QSet(); +} + +QSet QgsExpressionNodeLiteral::referencedVariables() const +{ + return QSet(); +} + +bool QgsExpressionNodeLiteral::needsGeometry() const +{ + return false; +} + +QgsExpressionNode *QgsExpressionNodeLiteral::clone() const +{ + QgsExpressionNodeLiteral *copy = new QgsExpressionNodeLiteral( mValue ); + cloneTo( copy ); + return copy; +} + +bool QgsExpressionNodeLiteral::isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const +{ + Q_UNUSED( context ) + Q_UNUSED( parent ) + return true; +} + +// + +QVariant QgsExpressionNodeColumnRef::evalNode( QgsExpression *parent, const QgsExpressionContext *context ) +{ + Q_UNUSED( parent ); + int index = mIndex; + + if ( index < 0 ) + { + // have not yet found field index - first check explicitly set fields collection + if ( context && context->hasVariable( QgsExpressionContext::EXPR_FIELDS ) ) + { + QgsFields fields = qvariant_cast( context->variable( QgsExpressionContext::EXPR_FIELDS ) ); + index = fields.lookupField( mName ); + } + } + + if ( context && context->hasFeature() ) + { + QgsFeature feature = context->feature(); + if ( index >= 0 ) + return feature.attribute( index ); + else + return feature.attribute( mName ); + } + return QVariant( '[' + mName + ']' ); +} + +QgsExpressionNode::NodeType QgsExpressionNodeColumnRef::nodeType() const +{ + return ntColumnRef; +} + +bool QgsExpressionNodeColumnRef::prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) +{ + if ( !context || !context->hasVariable( QgsExpressionContext::EXPR_FIELDS ) ) + return false; + + QgsFields fields = qvariant_cast( context->variable( QgsExpressionContext::EXPR_FIELDS ) ); + + mIndex = fields.lookupField( mName ); + if ( mIndex >= 0 ) + { + return true; + } + else + { + parent->setEvalErrorString( tr( "Column '%1' not found" ).arg( mName ) ); + mIndex = -1; + return false; + } +} + +QString QgsExpressionNodeColumnRef::dump() const +{ + return QRegExp( "^[A-Za-z_\x80-\xff][A-Za-z0-9_\x80-\xff]*$" ).exactMatch( mName ) ? mName : QgsExpression::quotedColumnRef( mName ); +} + +QSet QgsExpressionNodeColumnRef::referencedColumns() const +{ + return QSet() << mName; +} + +QSet QgsExpressionNodeColumnRef::referencedVariables() const +{ + return QSet(); +} + +bool QgsExpressionNodeColumnRef::needsGeometry() const +{ + return false; +} + +QgsExpressionNode *QgsExpressionNodeColumnRef::clone() const +{ + QgsExpressionNodeColumnRef *copy = new QgsExpressionNodeColumnRef( mName ); + cloneTo( copy ); + return copy; +} + +bool QgsExpressionNodeColumnRef::isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const +{ + Q_UNUSED( context ) + Q_UNUSED( parent ) + return false; +} + +// + +QgsExpressionNodeCondition::QgsExpressionNodeCondition( QgsExpressionNodeCondition::WhenThenList *conditions, QgsExpressionNode *elseExp ) + : mConditions( *conditions ) + , mElseExp( elseExp ) +{ + delete conditions; +} + +QgsExpressionNodeCondition::~QgsExpressionNodeCondition() +{ + delete mElseExp; + qDeleteAll( mConditions ); +} + +QgsExpressionNode::NodeType QgsExpressionNodeCondition::nodeType() const +{ + return ntCondition; +} + +QVariant QgsExpressionNodeCondition::evalNode( QgsExpression *parent, const QgsExpressionContext *context ) +{ + Q_FOREACH ( WhenThen *cond, mConditions ) + { + QVariant vWhen = cond->mWhenExp->eval( parent, context ); + TVL tvl = getTVLValue( vWhen, parent ); + ENSURE_NO_EVAL_ERROR; + if ( tvl == True ) + { + QVariant vRes = cond->mThenExp->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + return vRes; + } + } + + if ( mElseExp ) + { + QVariant vElse = mElseExp->eval( parent, context ); + ENSURE_NO_EVAL_ERROR; + return vElse; + } + + // return NULL if no condition is matching + return QVariant(); +} + +bool QgsExpressionNodeCondition::prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) +{ + bool res; + Q_FOREACH ( WhenThen *cond, mConditions ) + { + res = cond->mWhenExp->prepare( parent, context ) + & cond->mThenExp->prepare( parent, context ); + if ( !res ) + return false; + } + + if ( mElseExp ) + return mElseExp->prepare( parent, context ); + + return true; +} + +QString QgsExpressionNodeCondition::dump() const +{ + QString msg( QStringLiteral( "CASE" ) ); + Q_FOREACH ( WhenThen *cond, mConditions ) + { + msg += QStringLiteral( " WHEN %1 THEN %2" ).arg( cond->mWhenExp->dump(), cond->mThenExp->dump() ); + } + if ( mElseExp ) + msg += QStringLiteral( " ELSE %1" ).arg( mElseExp->dump() ); + msg += QStringLiteral( " END" ); + return msg; +} + +QSet QgsExpressionNodeCondition::referencedColumns() const +{ + QSet lst; + Q_FOREACH ( WhenThen *cond, mConditions ) + { + lst += cond->mWhenExp->referencedColumns() + cond->mThenExp->referencedColumns(); + } + + if ( mElseExp ) + lst += mElseExp->referencedColumns(); + + return lst; +} + +QSet QgsExpressionNodeCondition::referencedVariables() const +{ + QSet lst; + Q_FOREACH ( WhenThen *cond, mConditions ) + { + lst += cond->mWhenExp->referencedVariables() + cond->mThenExp->referencedVariables(); + } + + if ( mElseExp ) + lst += mElseExp->referencedVariables(); + + return lst; +} + +bool QgsExpressionNodeCondition::needsGeometry() const +{ + Q_FOREACH ( WhenThen *cond, mConditions ) + { + if ( cond->mWhenExp->needsGeometry() || + cond->mThenExp->needsGeometry() ) + return true; + } + + if ( mElseExp && mElseExp->needsGeometry() ) + return true; + + return false; +} + +QgsExpressionNode *QgsExpressionNodeCondition::clone() const +{ + WhenThenList conditions; + Q_FOREACH ( WhenThen *wt, mConditions ) + conditions.append( wt->clone() ); + + QgsExpressionNodeCondition *copy = new QgsExpressionNodeCondition( conditions, mElseExp ? mElseExp->clone() : nullptr ); + cloneTo( copy ); + return copy; +} + +bool QgsExpressionNodeCondition::isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const +{ + Q_FOREACH ( WhenThen *wt, mConditions ) + { + if ( !wt->mWhenExp->isStatic( parent, context ) || !wt->mThenExp->isStatic( parent, context ) ) + return false; + } + + if ( mElseExp ) + return mElseExp->isStatic( parent, context ); + + return true; +} + +QSet QgsExpressionNodeInOperator::referencedColumns() const +{ + QSet lst( mNode->referencedColumns() ); + Q_FOREACH ( const QgsExpressionNode *n, mList->list() ) + lst.unite( n->referencedColumns() ); + return lst; +} + +QSet QgsExpressionNodeInOperator::referencedVariables() const +{ + QSet lst( mNode->referencedVariables() ); + Q_FOREACH ( const QgsExpressionNode *n, mList->list() ) + lst.unite( n->referencedVariables() ); + return lst; +} + +QgsExpressionNodeCondition::WhenThen::WhenThen( QgsExpressionNode *whenExp, QgsExpressionNode *thenExp ) + : mWhenExp( whenExp ) + , mThenExp( thenExp ) +{ +} + +QgsExpressionNodeCondition::WhenThen::~WhenThen() +{ + delete mWhenExp; + delete mThenExp; +} + +QgsExpressionNodeCondition::WhenThen *QgsExpressionNodeCondition::WhenThen::clone() const +{ + return new WhenThen( mWhenExp->clone(), mThenExp->clone() ); +} QString QgsExpressionNodeBinaryOperator::text() const { diff --git a/src/core/expression/qgsexpressionnodeimpl.h b/src/core/expression/qgsexpressionnodeimpl.h index 9395b2184c5..2083576360c 100644 --- a/src/core/expression/qgsexpressionnodeimpl.h +++ b/src/core/expression/qgsexpressionnodeimpl.h @@ -1,3 +1,19 @@ +/*************************************************************************** + qgsexpressionnodeimpl.h + ------------------- + begin : May 2017 + copyright : (C) 2017 Matthias Kuhn + email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + + #ifndef QGSEXPRESSIONNODEIMPL_H #define QGSEXPRESSIONNODEIMPL_H diff --git a/src/core/expression/qgsexpressionutils.h b/src/core/expression/qgsexpressionutils.h new file mode 100644 index 00000000000..756f4ef3b2d --- /dev/null +++ b/src/core/expression/qgsexpressionutils.h @@ -0,0 +1,377 @@ +/*************************************************************************** + qgsexpressionutils.cpp + ------------------- + begin : May 2017 + copyright : (C) 2017 Matthias Kuhn + email : matthias@opengis.ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + + +#ifndef QGSEXPRESSIONUTILS_H +#define QGSEXPRESSIONUTILS_H + +#include "qgsfeature.h" +#include "qgsexpression.h" +#include "qgsvectorlayer.h" +#include "qgsproject.h" +#include "qgsrelationmanager.h" + + +#define ENSURE_NO_EVAL_ERROR { if ( parent->hasEvalError() ) return QVariant(); } +#define SET_EVAL_ERROR(x) { parent->setEvalErrorString( x ); return QVariant(); } + + +/////////////////////////////////////////////// +// three-value logic + +enum TVL +{ + False, + True, + Unknown +}; + + +static TVL AND[3][3] = +{ + // false true unknown + { False, False, False }, // false + { False, True, Unknown }, // true + { False, Unknown, Unknown } // unknown +}; + +static TVL OR[3][3] = +{ + { False, True, Unknown }, // false + { True, True, True }, // true + { Unknown, True, Unknown } // unknown +}; + +static TVL NOT[3] = { True, False, Unknown }; + +static QVariant tvl2variant( TVL v ) +{ + switch ( v ) + { + case False: + return 0; + case True: + return 1; + case Unknown: + default: + return QVariant( QVariant::Int ); + } +} + +#define TVL_True QVariant( true ) +#define TVL_False QVariant( false ) +#define TVL_Unknown QVariant( QVariant::Int ) + +// this handles also NULL values +static TVL getTVLValue( const QVariant &value, QgsExpression *parent ) +{ + // we need to convert to TVL + if ( value.isNull() ) + return Unknown; + + //handle some special cases + if ( value.canConvert() ) + { + //geom is false if empty + QgsGeometry geom = value.value(); + return geom.isNull() ? False : True; + } + else if ( value.canConvert() ) + { + //feat is false if non-valid + QgsFeature feat = value.value(); + return feat.isValid() ? True : False; + } + + if ( value.type() == QVariant::Int ) + return value.toInt() != 0 ? True : False; + + bool ok; + double x = value.toDouble( &ok ); + if ( !ok ) + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to boolean" ).arg( value.toString() ) ); + return Unknown; + } + return !qgsDoubleNear( x, 0.0 ) ? True : False; +} + + +inline bool isIntSafe( const QVariant &v ) +{ + if ( v.type() == QVariant::Int ) + return true; + if ( v.type() == QVariant::UInt ) + return true; + if ( v.type() == QVariant::LongLong ) + return true; + if ( v.type() == QVariant::ULongLong ) + return true; + if ( v.type() == QVariant::Double ) + return false; + if ( v.type() == QVariant::String ) + { + bool ok; + v.toString().toInt( &ok ); + return ok; + } + return false; +} +inline bool isDoubleSafe( const QVariant &v ) +{ + if ( v.type() == QVariant::Double ) + return true; + if ( v.type() == QVariant::Int ) + return true; + if ( v.type() == QVariant::UInt ) + return true; + if ( v.type() == QVariant::LongLong ) + return true; + if ( v.type() == QVariant::ULongLong ) + return true; + if ( v.type() == QVariant::String ) + { + bool ok; + double val = v.toString().toDouble( &ok ); + ok = ok && qIsFinite( val ) && !qIsNaN( val ); + return ok; + } + return false; +} + +inline bool isDateTimeSafe( const QVariant &v ) +{ + return v.type() == QVariant::DateTime + || v.type() == QVariant::Date + || v.type() == QVariant::Time; +} + +inline bool isIntervalSafe( const QVariant &v ) +{ + if ( v.canConvert() ) + { + return true; + } + + if ( v.type() == QVariant::String ) + { + return QgsInterval::fromString( v.toString() ).isValid(); + } + return false; +} + +inline bool isNull( const QVariant &v ) +{ + return v.isNull(); +} + +inline bool isList( const QVariant &v ) +{ + return v.type() == QVariant::List; +} + +// implicit conversion to string +static QString getStringValue( const QVariant &value, QgsExpression * ) +{ + return value.toString(); +} + +static double getDoubleValue( const QVariant &value, QgsExpression *parent ) +{ + bool ok; + double x = value.toDouble( &ok ); + if ( !ok || qIsNaN( x ) || !qIsFinite( x ) ) + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to double" ).arg( value.toString() ) ); + return 0; + } + return x; +} + +static qlonglong getIntValue( const QVariant &value, QgsExpression *parent ) +{ + bool ok; + qlonglong x = value.toLongLong( &ok ); + if ( ok ) + { + return x; + } + else + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to int" ).arg( value.toString() ) ); + return 0; + } +} + +static int getNativeIntValue( const QVariant &value, QgsExpression *parent ) +{ + bool ok; + qlonglong x = value.toLongLong( &ok ); + if ( ok && x >= std::numeric_limits::min() && x <= std::numeric_limits::max() ) + { + return x; + } + else + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to native int" ).arg( value.toString() ) ); + return 0; + } +} + +static QDateTime getDateTimeValue( const QVariant &value, QgsExpression *parent ) +{ + QDateTime d = value.toDateTime(); + if ( d.isValid() ) + { + return d; + } + else + { + QTime t = value.toTime(); + if ( t.isValid() ) + { + return QDateTime( QDate( 1, 1, 1 ), t ); + } + + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( value.toString() ) ); + return QDateTime(); + } +} + +static QDate getDateValue( const QVariant &value, QgsExpression *parent ) +{ + QDate d = value.toDate(); + if ( d.isValid() ) + { + return d; + } + else + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( value.toString() ) ); + return QDate(); + } +} + +static QTime getTimeValue( const QVariant &value, QgsExpression *parent ) +{ + QTime t = value.toTime(); + if ( t.isValid() ) + { + return t; + } + else + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( value.toString() ) ); + return QTime(); + } +} + +static QgsInterval getInterval( const QVariant &value, QgsExpression *parent, bool report_error = false ) +{ + if ( value.canConvert() ) + return value.value(); + + QgsInterval inter = QgsInterval::fromString( value.toString() ); + if ( inter.isValid() ) + { + return inter; + } + // If we get here then we can't convert so we just error and return invalid. + if ( report_error ) + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Interval" ).arg( value.toString() ) ); + + return QgsInterval(); +} + +static QgsGeometry getGeometry( const QVariant &value, QgsExpression *parent ) +{ + if ( value.canConvert() ) + return value.value(); + + parent->setEvalErrorString( QStringLiteral( "Cannot convert to QgsGeometry" ) ); + return QgsGeometry(); +} + +static QgsFeature getFeature( const QVariant &value, QgsExpression *parent ) +{ + if ( value.canConvert() ) + return value.value(); + + parent->setEvalErrorString( QStringLiteral( "Cannot convert to QgsFeature" ) ); + return 0; +} + +static QgsExpressionNode *getNode( const QVariant &value, QgsExpression *parent ) +{ + if ( value.canConvert() ) + return value.value(); + + parent->setEvalErrorString( QStringLiteral( "Cannot convert to Node" ) ); + return nullptr; +} + +QgsVectorLayer *getVectorLayer( const QVariant &value, QgsExpression * ) +{ + QgsMapLayer *ml = value.value< QgsWeakMapLayerPointer >().data(); + QgsVectorLayer *vl = qobject_cast( ml ); + if ( !vl ) + { + QString layerString = value.toString(); + vl = qobject_cast( QgsProject::instance()->mapLayer( layerString ) ); //search by id first + + if ( !vl ) + { + QList layersByName = QgsProject::instance()->mapLayersByName( layerString ); + if ( !layersByName.isEmpty() ) + { + vl = qobject_cast( layersByName.at( 0 ) ); + } + } + } + + return vl; +} + + +static QVariantList getListValue( const QVariant &value, QgsExpression *parent ) +{ + if ( value.type() == QVariant::List || value.type() == QVariant::StringList ) + { + return value.toList(); + } + else + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to array" ).arg( value.toString() ) ); + return QVariantList(); + } +} + +static QVariantMap getMapValue( const QVariant &value, QgsExpression *parent ) +{ + if ( value.type() == QVariant::Map ) + { + return value.toMap(); + } + else + { + parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to map" ).arg( value.toString() ) ); + return QVariantMap(); + } +} + +#define FEAT_FROM_CONTEXT(c, f) if (!(c) || !(c)->hasFeature() ) return QVariant(); \ + QgsFeature f = ( c )->feature(); + +#endif // QGSEXPRESSIONUTILS_H diff --git a/src/core/qgsexpressionprivate.h b/src/core/qgsexpressionprivate.h index 3977087384f..0d7d5d8c51a 100644 --- a/src/core/qgsexpressionprivate.h +++ b/src/core/qgsexpressionprivate.h @@ -23,6 +23,7 @@ #include "qgsexpression.h" #include "qgsdistancearea.h" #include "qgsunittypes.h" +#include "qgsexpressionnode.h" ///@cond