Skip to content

Commit

Permalink
Allow conversion of QgsPropertyTransfomers to expressions (when possi…
Browse files Browse the repository at this point in the history
…ble)
  • Loading branch information
nyalldawson committed Feb 5, 2017
1 parent ac55f39 commit c5546b0
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 8 deletions.
7 changes: 7 additions & 0 deletions python/core/qgspropertytransformer.sip
Expand Up @@ -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;

};

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand All @@ -136,5 +139,9 @@ class QgsColorRampTransformer : QgsPropertyTransformer

void setNullColor( const QColor& color );

QString rampName() const;

void setRampName( const QString& name );

};

15 changes: 10 additions & 5 deletions src/core/qgsproperty.cpp
Expand Up @@ -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
Expand Down
48 changes: 45 additions & 3 deletions src/core/qgspropertytransformer.cpp
Expand Up @@ -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
Expand All @@ -191,6 +214,7 @@ QgsColorRampTransformer::QgsColorRampTransformer( const QgsColorRampTransformer
: QgsPropertyTransformer( other )
, mGradientRamp( other.mGradientRamp ? other.mGradientRamp->clone() : nullptr )
, mNullColor( other.mNullColor )
, mRampName( other.mRampName )
{

}
Expand All @@ -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
Expand All @@ -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;
}
Expand All @@ -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;
}

Expand All @@ -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 );
Expand Down
23 changes: 23 additions & 0 deletions src/core/qgspropertytransformer.h
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;

};

Expand Down
58 changes: 58 additions & 0 deletions tests/src/core/testqgsproperty.cpp
Expand Up @@ -47,6 +47,7 @@ class TestTransformer : public QgsPropertyTransformer
{
return new TestTransformer( mMinValue, mMaxValue );
}
virtual QString toExpression( const QString& ) const override { return QString(); }

private:

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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 ) );
Expand All @@ -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 ) );
Expand All @@ -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 ) );

Expand All @@ -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 ) );

Expand All @@ -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 ) );

Expand All @@ -814,13 +834,51 @@ 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
QCOMPARE( t.color( 100 ), QColor( 10, 20, 100 ) );
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()
Expand Down

0 comments on commit c5546b0

Please sign in to comment.