From c5546b03577dc2f04f306dc1e5c1904bafe01015 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 5 Feb 2017 20:19:12 +1000 Subject: [PATCH] Allow conversion of QgsPropertyTransfomers to expressions (when possible) --- python/core/qgspropertytransformer.sip | 7 ++++ src/core/qgsproperty.cpp | 15 ++++--- src/core/qgspropertytransformer.cpp | 48 +++++++++++++++++++-- src/core/qgspropertytransformer.h | 23 ++++++++++ tests/src/core/testqgsproperty.cpp | 58 ++++++++++++++++++++++++++ 5 files changed, 143 insertions(+), 8 deletions(-) diff --git a/python/core/qgspropertytransformer.sip b/python/core/qgspropertytransformer.sip index bab5cc630723..98de1734381f 100644 --- a/python/core/qgspropertytransformer.sip +++ b/python/core/qgspropertytransformer.sip @@ -45,6 +45,7 @@ class QgsPropertyTransformer void setMaxValue( double max ); virtual QVariant transform( const QgsExpressionContext& context, const QVariant& value ) const = 0; + virtual QString toExpression( const QString& baseExpression ) const = 0; }; @@ -77,6 +78,7 @@ class QgsSizeScaleTransformer : QgsPropertyTransformer virtual bool writeXml( QDomElement& transformerElem, QDomDocument& doc ) const; virtual bool readXml( const QDomElement& transformerElem, const QDomDocument& doc ); virtual QVariant transform( const QgsExpressionContext& context, const QVariant& value ) const; + virtual QString toExpression( const QString& baseExpression ) const; double size( double value ) const; @@ -125,6 +127,7 @@ class QgsColorRampTransformer : QgsPropertyTransformer virtual bool writeXml( QDomElement& transformerElem, QDomDocument& doc ) const; virtual bool readXml( const QDomElement& transformerElem, const QDomDocument& doc ); virtual QVariant transform( const QgsExpressionContext& context, const QVariant& value ) const; + virtual QString toExpression( const QString& baseExpression ) const; QColor color( double value ) const; @@ -136,5 +139,9 @@ class QgsColorRampTransformer : QgsPropertyTransformer void setNullColor( const QColor& color ); + QString rampName() const; + + void setRampName( const QString& name ); + }; diff --git a/src/core/qgsproperty.cpp b/src/core/qgsproperty.cpp index bb1e6d0d49b7..1ecd2d073bc4 100644 --- a/src/core/qgsproperty.cpp +++ b/src/core/qgsproperty.cpp @@ -293,21 +293,26 @@ QString QgsProperty::expressionString() const QString QgsProperty::asExpression() const { + QString exp; switch ( d->type ) { case StaticProperty: - return QgsExpression::quotedValue( d->staticValue ); + exp = QgsExpression::quotedValue( d->staticValue ); + break; case FieldBasedProperty: - return QgsExpression::quotedColumnRef( d->fieldName ); + exp = QgsExpression::quotedColumnRef( d->fieldName ); + break; case ExpressionBasedProperty: - return d->expressionString; + exp = d->expressionString; + break; case InvalidProperty: - return QString(); + exp = QString(); + break; } - return QString(); + return d->transformer ? d->transformer->toExpression( exp ) : exp; } bool QgsProperty::prepare( const QgsExpressionContext& context ) const diff --git a/src/core/qgspropertytransformer.cpp b/src/core/qgspropertytransformer.cpp index 761601ce9fcb..4502b4484aef 100644 --- a/src/core/qgspropertytransformer.cpp +++ b/src/core/qgspropertytransformer.cpp @@ -172,6 +172,29 @@ QVariant QgsSizeScaleTransformer::transform( const QgsExpressionContext& context } } +QString QgsSizeScaleTransformer::toExpression( const QString& baseExpression ) const +{ + QString minValueString = QString::number( mMinValue ); + QString maxValueString = QString::number( mMaxValue ); + QString minSizeString = QString::number( mMinSize ); + QString maxSizeString = QString::number( mMaxSize ); + QString nullSizeString = QString::number( mNullSize ); + QString exponentString = QString::number( mExponent ); + + switch ( mType ) + { + case Linear: + return QStringLiteral( "coalesce(scale_linear(%1, %2, %3, %4, %5), %6)" ).arg( baseExpression, minValueString, maxValueString, minSizeString, maxSizeString, nullSizeString ); + + case Area: + case Flannery: + case Exponential: + return QStringLiteral( "coalesce(scale_exp(%1, %2, %3, %4, %5, %6), %7)" ).arg( baseExpression, minValueString, maxValueString, minSizeString, maxSizeString, exponentString, nullSizeString ); + + } + return QString(); +} + // // QgsColorRampTransformer @@ -191,6 +214,7 @@ QgsColorRampTransformer::QgsColorRampTransformer( const QgsColorRampTransformer : QgsPropertyTransformer( other ) , mGradientRamp( other.mGradientRamp ? other.mGradientRamp->clone() : nullptr ) , mNullColor( other.mNullColor ) + , mRampName( other.mRampName ) { } @@ -201,14 +225,17 @@ QgsColorRampTransformer &QgsColorRampTransformer::operator=( const QgsColorRampT mMaxValue = other.mMaxValue; mGradientRamp.reset( other.mGradientRamp ? other.mGradientRamp->clone() : nullptr ); mNullColor = other.mNullColor; + mRampName = other.mRampName; return *this; } QgsColorRampTransformer* QgsColorRampTransformer::clone() { - return new QgsColorRampTransformer( mMinValue, mMaxValue, - mGradientRamp ? mGradientRamp->clone() : nullptr, - mNullColor ); + QgsColorRampTransformer* c = new QgsColorRampTransformer( mMinValue, mMaxValue, + mGradientRamp ? mGradientRamp->clone() : nullptr, + mNullColor ); + c->setRampName( mRampName ); + return c; } bool QgsColorRampTransformer::writeXml( QDomElement &transformerElem, QDomDocument &doc ) const @@ -222,6 +249,7 @@ bool QgsColorRampTransformer::writeXml( QDomElement &transformerElem, QDomDocume transformerElem.appendChild( colorRampElem ); } transformerElem.setAttribute( "nullColor", QgsSymbolLayerUtils::encodeColor( mNullColor ) ); + transformerElem.setAttribute( "rampName", mRampName ); return true; } @@ -239,6 +267,7 @@ bool QgsColorRampTransformer::readXml( const QDomElement &transformerElem, const } mNullColor = QgsSymbolLayerUtils::decodeColor( transformerElem.attribute( "nullColor", "0,0,0,0" ) ); + mRampName = transformerElem.attribute( "rampName", QString() ); return true; } @@ -263,6 +292,19 @@ QVariant QgsColorRampTransformer::transform( const QgsExpressionContext &context } } +QString QgsColorRampTransformer::toExpression( const QString& baseExpression ) const +{ + if ( !mGradientRamp ) + return QgsExpression::quotedValue( mNullColor.name() ); + + QString minValueString = QString::number( mMinValue ); + QString maxValueString = QString::number( mMaxValue ); + QString nullColorString = mNullColor.name(); + + return QStringLiteral( "coalesce(ramp_color('%1',scale_linear(%2, %3, %4, 0, 1), '%5')" ).arg( !mRampName.isEmpty() ? mRampName : QStringLiteral( "custom ramp" ), + baseExpression, minValueString, maxValueString, nullColorString ); +} + QColor QgsColorRampTransformer::color( double value ) const { double scaledVal = qBound( 0.0, ( value - mMinValue ) / ( mMaxValue - mMinValue ), 1.0 ); diff --git a/src/core/qgspropertytransformer.h b/src/core/qgspropertytransformer.h index fa6142308238..10483cbcc2d0 100644 --- a/src/core/qgspropertytransformer.h +++ b/src/core/qgspropertytransformer.h @@ -126,6 +126,12 @@ class CORE_EXPORT QgsPropertyTransformer */ virtual QVariant transform( const QgsExpressionContext& context, const QVariant& value ) const = 0; + /** + * Converts the transformer to a QGIS expression string. The \a baseExpression string consists + * of a sub-expression reflecting the parent property's state. + */ + virtual QString toExpression( const QString& baseExpression ) const = 0; + protected: //! Minimum value expected by the transformer @@ -181,6 +187,7 @@ class CORE_EXPORT QgsSizeScaleTransformer : public QgsPropertyTransformer virtual bool writeXml( QDomElement& transformerElem, QDomDocument& doc ) const override; virtual bool readXml( const QDomElement& transformerElem, const QDomDocument& doc ) override; virtual QVariant transform( const QgsExpressionContext& context, const QVariant& value ) const override; + virtual QString toExpression( const QString& baseExpression ) const override; /** * Calculates the size corresponding to a specific value. @@ -303,6 +310,7 @@ class CORE_EXPORT QgsColorRampTransformer : public QgsPropertyTransformer virtual bool writeXml( QDomElement& transformerElem, QDomDocument& doc ) const override; virtual bool readXml( const QDomElement& transformerElem, const QDomDocument& doc ) override; virtual QVariant transform( const QgsExpressionContext& context, const QVariant& value ) const override; + virtual QString toExpression( const QString& baseExpression ) const override; /** * Calculates the color corresponding to a specific value. @@ -338,10 +346,25 @@ class CORE_EXPORT QgsColorRampTransformer : public QgsPropertyTransformer */ void setNullColor( const QColor& color ) { mNullColor = color; } + /** + * Returns the color ramp's name. + * @see setRampName() + */ + QString rampName() const { return mRampName; } + + /** + * Sets the color ramp's \a name. The ramp name must be set to match + * a color ramp available in the style database for conversion to expression + * to work correctly. + * @see rampName() + */ + void setRampName( const QString& name ) { mRampName = name; } + private: QScopedPointer< QgsColorRamp > mGradientRamp; QColor mNullColor; + QString mRampName; }; diff --git a/tests/src/core/testqgsproperty.cpp b/tests/src/core/testqgsproperty.cpp index 81b26c6adf0b..ad6da2d12588 100644 --- a/tests/src/core/testqgsproperty.cpp +++ b/tests/src/core/testqgsproperty.cpp @@ -47,6 +47,7 @@ class TestTransformer : public QgsPropertyTransformer { return new TestTransformer( mMinValue, mMaxValue ); } + virtual QString toExpression( const QString& ) const override { return QString(); } private: @@ -80,6 +81,7 @@ class TestQgsProperty : public QObject void propertyTransformer(); //test for QgsPropertyTransformer void sizeScaleTransformer(); //test for QgsSizeScaleTransformer void colorRampTransformer(); //test for QgsColorRampTransformer + void asExpression(); //test converting property to expression void propertyCollection(); //test for QgsPropertyCollection void collectionStack(); //test for QgsPropertyCollectionStack @@ -729,6 +731,19 @@ void TestQgsProperty::sizeScaleTransformer() QCOMPARE( t.size( 100 ), 10.0 ); QVERIFY( qgsDoubleNear( t.size( 150 ), 13.5355, 0.001 ) ); QCOMPARE( t.size( 200 ), 20.0 ); + + //as expression + QgsSizeScaleTransformer t2( QgsSizeScaleTransformer::Linear, + 15, + 25, + 150, + 250, + -10, + 1.6 ); + QCOMPARE( t2.toExpression( "5+6" ), QStringLiteral( "coalesce(scale_linear(5+6, 15, 25, 150, 250), -10)" ) ); + t2.setType( QgsSizeScaleTransformer::Exponential ); + t2.setExponent( 1.6 ); + QCOMPARE( t2.toExpression( "5+6" ), QStringLiteral( "coalesce(scale_exp(5+6, 15, 25, 150, 250, 1.6), -10)" ) ); } void TestQgsProperty::colorRampTransformer() @@ -765,6 +780,7 @@ void TestQgsProperty::colorRampTransformer() 25, new QgsGradientColorRamp( QColor( 10, 20, 30 ), QColor( 200, 190, 180 ) ), QColor( 100, 150, 200 ) ); + t1.setRampName( "rampname " ); QDomElement element = doc.createElement( "xform" ); QVERIFY( t1.writeXml( element, doc ) ); @@ -773,6 +789,7 @@ void TestQgsProperty::colorRampTransformer() QCOMPARE( r1.minValue(), 15.0 ); QCOMPARE( r1.maxValue(), 25.0 ); QCOMPARE( r1.nullColor(), QColor( 100, 150, 200 ) ); + QCOMPARE( r1.rampName(), QString( "rampname " ) ); QVERIFY( dynamic_cast< QgsGradientColorRamp* >( r1.colorRamp() ) ); QCOMPARE( static_cast< QgsGradientColorRamp* >( r1.colorRamp() )->color1(), QColor( 10, 20, 30 ) ); QCOMPARE( static_cast< QgsGradientColorRamp* >( r1.colorRamp() )->color2(), QColor( 200, 190, 180 ) ); @@ -782,6 +799,7 @@ void TestQgsProperty::colorRampTransformer() QCOMPARE( r2->minValue(), 15.0 ); QCOMPARE( r2->maxValue(), 25.0 ); QCOMPARE( r2->nullColor(), QColor( 100, 150, 200 ) ); + QCOMPARE( r2->rampName(), QString( "rampname " ) ); QCOMPARE( static_cast< QgsGradientColorRamp* >( r2->colorRamp() )->color1(), QColor( 10, 20, 30 ) ); QCOMPARE( static_cast< QgsGradientColorRamp* >( r2->colorRamp() )->color2(), QColor( 200, 190, 180 ) ); @@ -790,6 +808,7 @@ void TestQgsProperty::colorRampTransformer() QCOMPARE( r3.minValue(), 15.0 ); QCOMPARE( r3.maxValue(), 25.0 ); QCOMPARE( r3.nullColor(), QColor( 100, 150, 200 ) ); + QCOMPARE( r3.rampName(), QString( "rampname " ) ); QCOMPARE( static_cast< QgsGradientColorRamp* >( r3.colorRamp() )->color1(), QColor( 10, 20, 30 ) ); QCOMPARE( static_cast< QgsGradientColorRamp* >( r3.colorRamp() )->color2(), QColor( 200, 190, 180 ) ); @@ -799,6 +818,7 @@ void TestQgsProperty::colorRampTransformer() QCOMPARE( r4.minValue(), 15.0 ); QCOMPARE( r4.maxValue(), 25.0 ); QCOMPARE( r4.nullColor(), QColor( 100, 150, 200 ) ); + QCOMPARE( r4.rampName(), QString( "rampname " ) ); QCOMPARE( static_cast< QgsGradientColorRamp* >( r4.colorRamp() )->color1(), QColor( 10, 20, 30 ) ); QCOMPARE( static_cast< QgsGradientColorRamp* >( r4.colorRamp() )->color2(), QColor( 200, 190, 180 ) ); @@ -814,6 +834,8 @@ void TestQgsProperty::colorRampTransformer() QCOMPARE( t.nullColor(), QColor( 1, 10, 11, 21 ) ); t.setColorRamp( new QgsGradientColorRamp( QColor( 10, 20, 100 ), QColor( 100, 200, 200 ) ) ); QCOMPARE( static_cast< QgsGradientColorRamp* >( t.colorRamp() )->color1(), QColor( 10, 20, 100 ) ); + t.setRampName( "colorramp" ); + QCOMPARE( t.rampName(), QString( "colorramp" ) ); //test colors QCOMPARE( t.color( 50 ), QColor( 10, 20, 100 ) ); //out of range @@ -821,6 +843,42 @@ void TestQgsProperty::colorRampTransformer() QCOMPARE( t.color( 150 ), QColor( 55, 110, 150 ) ); QCOMPARE( t.color( 200 ), QColor( 100, 200, 200 ) ); QCOMPARE( t.color( 250 ), QColor( 100, 200, 200 ) ); //out of range + + //toExpression + QgsColorRampTransformer t5( 15, + 25, + new QgsGradientColorRamp( QColor( 10, 20, 30 ), QColor( 200, 190, 180 ) ), + QColor( 100, 150, 200 ) ); + QCOMPARE( t5.toExpression( "5+6" ), QStringLiteral( "coalesce(ramp_color('custom ramp',scale_linear(5+6, 15, 25, 0, 1), '#6496c8')" ) ); + t5.setRampName( "my ramp" ); + QCOMPARE( t5.toExpression( "5+6" ), QStringLiteral( "coalesce(ramp_color('my ramp',scale_linear(5+6, 15, 25, 0, 1), '#6496c8')" ) ); +} + +void TestQgsProperty::asExpression() +{ + // static property + QgsProperty p = QgsProperty::fromValue( 5 ); + QCOMPARE( p.asExpression(), QStringLiteral( "5" ) ); + p = QgsProperty::fromValue( "value" ); + QCOMPARE( p.asExpression(), QStringLiteral( "'value'" ) ); + + // field based property + p = QgsProperty::fromField( "a field" ); + QCOMPARE( p.asExpression(), QStringLiteral( "\"a field\"" ) ); + + // expression based property + p = QgsProperty::fromExpression( "5 + 6" ); + QCOMPARE( p.asExpression(), QStringLiteral( "5 + 6" ) ); + + // with transformer + p.setTransformer( new QgsSizeScaleTransformer( QgsSizeScaleTransformer::Linear, + 15, + 25, + 150, + 250, + -10, + 1 ) ); + QCOMPARE( p.asExpression(), QStringLiteral( "coalesce(scale_linear(5 + 6, 15, 25, 150, 250), -10)" ) ); } void TestQgsProperty::propertyCollection()