Allow QgsPropertyTransformers to have an associated QgsCurveTransform

This commit is contained in:
Nyall Dawson 2017-02-21 13:59:17 +10:00
parent 5c3198daea
commit dcf6104753
4 changed files with 244 additions and 19 deletions

View File

@ -60,6 +60,8 @@ class QgsPropertyTransformer
QgsPropertyTransformer( double minValue = 0.0, double maxValue = 1.0 );
QgsPropertyTransformer( const QgsPropertyTransformer& other );
virtual ~QgsPropertyTransformer();
virtual Type transformerType() const = 0;
@ -78,11 +80,18 @@ class QgsPropertyTransformer
void setMaxValue( double max );
QgsCurveTransform* curveTransform() const;
void setCurveTransform( QgsCurveTransform* transform /Transfer/ );
virtual QVariant transform( const QgsExpressionContext& context, const QVariant& value ) const = 0;
virtual QString toExpression( const QString& baseExpression ) const = 0;
static QgsPropertyTransformer* fromExpression( const QString& expression, QString& baseExpression /Out/, QString& fieldName /Out/ ) /Factory/;
protected:
double transformNumeric( double input ) const;
};
class QgsGenericNumericTransformer : QgsPropertyTransformer
{
@ -98,6 +107,8 @@ class QgsGenericNumericTransformer : QgsPropertyTransformer
double nullOutput = 0.0,
double exponent = 1.0 );
QgsGenericNumericTransformer( const QgsGenericNumericTransformer& other );
virtual Type transformerType() const;
virtual QgsGenericNumericTransformer* clone() /Factory/;
virtual bool writeXml( QDomElement& transformerElem, QDomDocument& doc ) const;
@ -140,6 +151,8 @@ class QgsSizeScaleTransformer : QgsPropertyTransformer
double nullSize = 0.0,
double exponent = 1.0 );
QgsSizeScaleTransformer( const QgsSizeScaleTransformer& other );
virtual Type transformerType() const;
virtual QgsSizeScaleTransformer* clone() /Factory/;
virtual bool writeXml( QDomElement& transformerElem, QDomDocument& doc ) const;

View File

@ -50,11 +50,32 @@ QgsPropertyTransformer::QgsPropertyTransformer( double minValue, double maxValue
, mMaxValue( maxValue )
{}
QgsPropertyTransformer::QgsPropertyTransformer( const QgsPropertyTransformer& other )
: mMinValue( other.mMinValue )
, mMaxValue( other.mMaxValue )
, mCurveTransform( other.mCurveTransform ? new QgsCurveTransform( *other.mCurveTransform ) : nullptr )
{}
QgsPropertyTransformer& QgsPropertyTransformer::operator=( const QgsPropertyTransformer & other )
{
mMinValue = other.mMinValue;
mMaxValue = other.mMaxValue;
mCurveTransform.reset( other.mCurveTransform ? new QgsCurveTransform( *other.mCurveTransform ) : nullptr );
return *this;
}
bool QgsPropertyTransformer::writeXml( QDomElement& transformerElem, QDomDocument& doc ) const
{
Q_UNUSED( doc );
transformerElem.setAttribute( "minValue", QString::number( mMinValue ) );
transformerElem.setAttribute( "maxValue", QString::number( mMaxValue ) );
if ( mCurveTransform )
{
QDomElement curveElement = doc.createElement( "curve" );
mCurveTransform->writeXml( curveElement, doc );
transformerElem.appendChild( curveElement );
}
return true;
}
@ -69,11 +90,35 @@ QgsPropertyTransformer* QgsPropertyTransformer::fromExpression( const QString& e
return nullptr;
}
double QgsPropertyTransformer::transformNumeric( double input ) const
{
if ( !mCurveTransform )
return input;
if ( qgsDoubleNear( mMaxValue, mMinValue ) )
return input;
// convert input into target range
double scaledInput = ( input - mMinValue ) / ( mMaxValue - mMinValue );
return mMinValue + ( mMaxValue - mMinValue ) * mCurveTransform->y( scaledInput );
}
bool QgsPropertyTransformer::readXml( const QDomElement &transformerElem, const QDomDocument &doc )
{
Q_UNUSED( doc );
mMinValue = transformerElem.attribute( "minValue", "0.0" ).toDouble();
mMaxValue = transformerElem.attribute( "maxValue", "1.0" ).toDouble();
mCurveTransform.reset( nullptr );
QDomNodeList curveNodeList = transformerElem.elementsByTagName( "curve" );
if ( !curveNodeList.isEmpty() )
{
QDomElement curveElem = curveNodeList.at( 0 ).toElement();
mCurveTransform.reset( new QgsCurveTransform() );
mCurveTransform->readXml( curveElem, doc );
}
return true;
}
@ -89,14 +134,35 @@ QgsGenericNumericTransformer::QgsGenericNumericTransformer( double minValue, dou
, mExponent( exponent )
{}
QgsGenericNumericTransformer::QgsGenericNumericTransformer( const QgsGenericNumericTransformer& other )
: QgsPropertyTransformer( other )
, mMinOutput( other.mMinOutput )
, mMaxOutput( other.mMaxOutput )
, mNullOutput( other.mNullOutput )
, mExponent( other.mExponent )
{}
QgsGenericNumericTransformer& QgsGenericNumericTransformer::operator=( const QgsGenericNumericTransformer & other )
{
QgsPropertyTransformer::operator=( other );
mMinOutput = other.mMinOutput;
mMaxOutput = other.mMaxOutput;
mNullOutput = other.mNullOutput;
mExponent = other.mExponent;
return *this;
}
QgsGenericNumericTransformer *QgsGenericNumericTransformer::clone()
{
return new QgsGenericNumericTransformer( mMinValue,
mMaxValue,
mMinOutput,
mMaxOutput,
mNullOutput,
mExponent );
std::unique_ptr< QgsGenericNumericTransformer > t( new QgsGenericNumericTransformer( mMinValue,
mMaxValue,
mMinOutput,
mMaxOutput,
mNullOutput,
mExponent ) );
if ( mCurveTransform )
t->setCurveTransform( new QgsCurveTransform( *mCurveTransform ) );
return t.release();
}
bool QgsGenericNumericTransformer::writeXml( QDomElement &transformerElem, QDomDocument &doc ) const
@ -145,7 +211,7 @@ QVariant QgsGenericNumericTransformer::transform( const QgsExpressionContext& co
if ( ok )
{
//apply scaling to value
return value( dblValue );
return value( transformNumeric( dblValue ) );
}
else
{
@ -257,15 +323,38 @@ QgsSizeScaleTransformer::QgsSizeScaleTransformer( ScaleType type, double minValu
setType( type );
}
QgsSizeScaleTransformer::QgsSizeScaleTransformer( const QgsSizeScaleTransformer& other )
: QgsPropertyTransformer( other )
, mType( other.mType )
, mMinSize( other.mMinSize )
, mMaxSize( other.mMaxSize )
, mNullSize( other.mNullSize )
, mExponent( other.mExponent )
{}
QgsSizeScaleTransformer& QgsSizeScaleTransformer::operator=( const QgsSizeScaleTransformer & other )
{
QgsPropertyTransformer::operator=( other );
mType = other.mType;
mMinSize = other.mMinSize;
mMaxSize = other.mMaxSize;
mNullSize = other.mNullSize;
mExponent = other.mExponent;
return *this;
}
QgsSizeScaleTransformer *QgsSizeScaleTransformer::clone()
{
return new QgsSizeScaleTransformer( mType,
mMinValue,
mMaxValue,
mMinSize,
mMaxSize,
mNullSize,
mExponent );
std::unique_ptr< QgsSizeScaleTransformer > t( new QgsSizeScaleTransformer( mType,
mMinValue,
mMaxValue,
mMinSize,
mMaxSize,
mNullSize,
mExponent ) );
if ( mCurveTransform )
t->setCurveTransform( new QgsCurveTransform( *mCurveTransform ) );
return t.release();
}
bool QgsSizeScaleTransformer::writeXml( QDomElement &transformerElem, QDomDocument &doc ) const
@ -344,7 +433,7 @@ QVariant QgsSizeScaleTransformer::transform( const QgsExpressionContext& context
if ( ok )
{
//apply scaling to value
return size( dblValue );
return size( transformNumeric( dblValue ) );
}
else
{
@ -483,6 +572,7 @@ QgsColorRampTransformer::QgsColorRampTransformer( const QgsColorRampTransformer
QgsColorRampTransformer &QgsColorRampTransformer::operator=( const QgsColorRampTransformer & other )
{
QgsPropertyTransformer::operator=( other );
mMinValue = other.mMinValue;
mMaxValue = other.mMaxValue;
mGradientRamp.reset( other.mGradientRamp ? other.mGradientRamp->clone() : nullptr );
@ -493,11 +583,13 @@ QgsColorRampTransformer &QgsColorRampTransformer::operator=( const QgsColorRampT
QgsColorRampTransformer* QgsColorRampTransformer::clone()
{
QgsColorRampTransformer* c = new QgsColorRampTransformer( mMinValue, mMaxValue,
std::unique_ptr< QgsColorRampTransformer > c( new QgsColorRampTransformer( mMinValue, mMaxValue,
mGradientRamp ? mGradientRamp->clone() : nullptr,
mNullColor );
mNullColor ) );
c->setRampName( mRampName );
return c;
if ( mCurveTransform )
c->setCurveTransform( new QgsCurveTransform( *mCurveTransform ) );
return c.release();
}
bool QgsColorRampTransformer::writeXml( QDomElement &transformerElem, QDomDocument &doc ) const
@ -546,7 +638,7 @@ QVariant QgsColorRampTransformer::transform( const QgsExpressionContext &context
if ( ok )
{
//apply scaling to value
return color( dblValue );
return color( transformNumeric( dblValue ) );
}
else
{

View File

@ -177,6 +177,12 @@ class CORE_EXPORT QgsPropertyTransformer
*/
QgsPropertyTransformer( double minValue = 0.0, double maxValue = 1.0 );
/**
* Copy constructor.
*/
QgsPropertyTransformer( const QgsPropertyTransformer& other );
QgsPropertyTransformer& operator=( const QgsPropertyTransformer& other );
virtual ~QgsPropertyTransformer() = default;
/**
@ -235,6 +241,21 @@ class CORE_EXPORT QgsPropertyTransformer
*/
void setMaxValue( double max ) { mMaxValue = max; }
/**
* Returns the curve transform applied to input values before they are transformed
* by the individual transform subclasses.
* @see setCurveTransform()
*/
QgsCurveTransform* curveTransform() const { return mCurveTransform.get(); }
/**
* Sets a curve transform to apply to input values before they are transformed
* by the individual transform subclasses. Ownership of \a transform is transferred
* to the property transformer.
* @see curveTransform()
*/
void setCurveTransform( QgsCurveTransform* transform ) { mCurveTransform.reset( transform ); }
/**
* Calculates the transform of a value. Derived classes must implement this to perform their transformations
* on input values
@ -271,6 +292,15 @@ class CORE_EXPORT QgsPropertyTransformer
//! Maximum value expected by the transformer
double mMaxValue;
//! Optional curve transform
std::unique_ptr< QgsCurveTransform > mCurveTransform;
/**
* Applies base class numeric transformations. Derived classes should call this
* to transform an \a input numeric value before they apply any transform to the result.
* This applies any curve transforms which may exist on the transformer.
*/
double transformNumeric( double input ) const;
};
/**
@ -300,6 +330,12 @@ class CORE_EXPORT QgsGenericNumericTransformer : public QgsPropertyTransformer
double nullOutput = 0.0,
double exponent = 1.0 );
/**
* Copy constructor.
*/
QgsGenericNumericTransformer( const QgsGenericNumericTransformer& other );
QgsGenericNumericTransformer& operator=( const QgsGenericNumericTransformer& other );
virtual Type transformerType() const override { return GenericNumericTransformer; }
virtual QgsGenericNumericTransformer* clone() override;
virtual bool writeXml( QDomElement& transformerElem, QDomDocument& doc ) const override;
@ -430,6 +466,12 @@ class CORE_EXPORT QgsSizeScaleTransformer : public QgsPropertyTransformer
double nullSize = 0.0,
double exponent = 1.0 );
/**
* Copy constructor.
*/
QgsSizeScaleTransformer( const QgsSizeScaleTransformer& other );
QgsSizeScaleTransformer& operator=( const QgsSizeScaleTransformer& other );
virtual Type transformerType() const override { return SizeScaleTransformer; }
virtual QgsSizeScaleTransformer* clone() override;
virtual bool writeXml( QDomElement& transformerElem, QDomDocument& doc ) const override;

View File

@ -718,6 +718,26 @@ void TestQgsProperty::genericNumericTransformer()
//non numeric value
QCOMPARE( t1.transform( context, QVariant( "ffff" ) ), QVariant( "ffff" ) );
// add a curve
QVERIFY( !t1.curveTransform() );
t1.setCurveTransform( new QgsCurveTransform( QList< QgsPoint >() << QgsPoint( 0, 0.8 ) << QgsPoint( 1, 0.2 ) ) );
QVERIFY( t1.curveTransform() );
QCOMPARE( t1.curveTransform()->controlPoints(), QList< QgsPoint >() << QgsPoint( 0, 0.8 ) << QgsPoint( 1, 0.2 ) );
QCOMPARE( t1.transform( context, 10 ).toInt(), 180 );
QCOMPARE( t1.transform( context, 20 ).toInt(), 120 );
// copy
QgsGenericNumericTransformer s1( t1 );
QVERIFY( s1.curveTransform() );
QCOMPARE( s1.curveTransform()->controlPoints(), QList< QgsPoint >() << QgsPoint( 0, 0.8 ) << QgsPoint( 1, 0.2 ) );
// assignment
QgsGenericNumericTransformer s2;
s2 = t1;
QVERIFY( s2.curveTransform() );
QCOMPARE( s2.curveTransform()->controlPoints(), QList< QgsPoint >() << QgsPoint( 0, 0.8 ) << QgsPoint( 1, 0.2 ) );
//saving and restoring
//create a test dom element
@ -733,6 +753,7 @@ void TestQgsProperty::genericNumericTransformer()
250,
-10,
99 );
t2.setCurveTransform( new QgsCurveTransform( QList< QgsPoint >() << QgsPoint( 0, 0.8 ) << QgsPoint( 1, 0.2 ) ) );
QDomElement element = doc.createElement( "xform" );
QVERIFY( t2.writeXml( element, doc ) );
@ -744,6 +765,8 @@ void TestQgsProperty::genericNumericTransformer()
QCOMPARE( r1.maxOutputValue(), 250.0 );
QCOMPARE( r1.nullOutputValue(), -10.0 );
QCOMPARE( r1.exponent(), 99.0 );
QVERIFY( r1.curveTransform() );
QCOMPARE( r1.curveTransform()->controlPoints(), QList< QgsPoint >() << QgsPoint( 0, 0.8 ) << QgsPoint( 1, 0.2 ) );
// test cloning
std::unique_ptr< QgsGenericNumericTransformer > r2( t2.clone() );
@ -753,6 +776,8 @@ void TestQgsProperty::genericNumericTransformer()
QCOMPARE( r2->maxOutputValue(), 250.0 );
QCOMPARE( r2->nullOutputValue(), -10.0 );
QCOMPARE( r2->exponent(), 99.0 );
QVERIFY( r2->curveTransform() );
QCOMPARE( r2->curveTransform()->controlPoints(), QList< QgsPoint >() << QgsPoint( 0, 0.8 ) << QgsPoint( 1, 0.2 ) );
//test various min/max value/size and scaling methods
@ -887,6 +912,25 @@ void TestQgsProperty::sizeScaleTransformer()
//non numeric value
QCOMPARE( scale.transform( context, QVariant( "ffff" ) ), QVariant( "ffff" ) );
// add a curve
QVERIFY( !scale.curveTransform() );
scale.setCurveTransform( new QgsCurveTransform( QList< QgsPoint >() << QgsPoint( 0, 0.2 ) << QgsPoint( 1, 0.8 ) ) );
QVERIFY( scale.curveTransform() );
QCOMPARE( scale.curveTransform()->controlPoints(), QList< QgsPoint >() << QgsPoint( 0, 0.2 ) << QgsPoint( 1, 0.8 ) );
QCOMPARE( scale.transform( context, 10 ).toInt(), 120 );
QCOMPARE( scale.transform( context, 20 ).toInt(), 180 );
// copy
QgsSizeScaleTransformer s1( scale );
QVERIFY( s1.curveTransform() );
QCOMPARE( s1.curveTransform()->controlPoints(), QList< QgsPoint >() << QgsPoint( 0, 0.2 ) << QgsPoint( 1, 0.8 ) );
// assignment
QgsSizeScaleTransformer s2;
s2 = scale;
QVERIFY( s2.curveTransform() );
QCOMPARE( s2.curveTransform()->controlPoints(), QList< QgsPoint >() << QgsPoint( 0, 0.2 ) << QgsPoint( 1, 0.8 ) );
//saving and restoring
//create a test dom element
@ -903,6 +947,7 @@ void TestQgsProperty::sizeScaleTransformer()
250,
-10,
99 );
t1.setCurveTransform( new QgsCurveTransform( QList< QgsPoint >() << QgsPoint( 0, 0.8 ) << QgsPoint( 1, 0.2 ) ) );
QDomElement element = doc.createElement( "xform" );
QVERIFY( t1.writeXml( element, doc ) );
@ -915,6 +960,8 @@ void TestQgsProperty::sizeScaleTransformer()
QCOMPARE( r1.nullSize(), -10.0 );
QCOMPARE( r1.exponent(), 99.0 );
QCOMPARE( r1.type(), QgsSizeScaleTransformer::Exponential );
QVERIFY( r1.curveTransform() );
QCOMPARE( r1.curveTransform()->controlPoints(), QList< QgsPoint >() << QgsPoint( 0, 0.8 ) << QgsPoint( 1, 0.2 ) );
// test cloning
std::unique_ptr< QgsSizeScaleTransformer > r2( t1.clone() );
@ -925,6 +972,8 @@ void TestQgsProperty::sizeScaleTransformer()
QCOMPARE( r2->nullSize(), -10.0 );
QCOMPARE( r2->exponent(), 99.0 );
QCOMPARE( r2->type(), QgsSizeScaleTransformer::Exponential );
QVERIFY( r2->curveTransform() );
QCOMPARE( r2->curveTransform()->controlPoints(), QList< QgsPoint >() << QgsPoint( 0, 0.8 ) << QgsPoint( 1, 0.2 ) );
//test various min/max value/size and scaling methods
@ -1092,6 +1141,26 @@ void TestQgsProperty::colorRampTransformer()
//non numeric value
QCOMPARE( scale.transform( context, QVariant( "ffff" ) ), QVariant( "ffff" ) );
// add a curve
QVERIFY( !scale.curveTransform() );
scale.setCurveTransform( new QgsCurveTransform( QList< QgsPoint >() << QgsPoint( 0, 0.2 ) << QgsPoint( 1, 0.8 ) ) );
QVERIFY( scale.curveTransform() );
QCOMPARE( scale.curveTransform()->controlPoints(), QList< QgsPoint >() << QgsPoint( 0, 0.2 ) << QgsPoint( 1, 0.8 ) );
QCOMPARE( scale.transform( context, 10 ).value<QColor>().name(), QString( "#333333" ) );
QCOMPARE( scale.transform( context, 20 ).value<QColor>().name(), QString( "#cccccc" ) );
// copy
QgsColorRampTransformer s1( scale );
QVERIFY( s1.curveTransform() );
QCOMPARE( s1.curveTransform()->controlPoints(), QList< QgsPoint >() << QgsPoint( 0, 0.2 ) << QgsPoint( 1, 0.8 ) );
// assignment
QgsColorRampTransformer s2;
s2 = scale;
QVERIFY( s2.curveTransform() );
QCOMPARE( s2.curveTransform()->controlPoints(), QList< QgsPoint >() << QgsPoint( 0, 0.2 ) << QgsPoint( 1, 0.8 ) );
//saving and restoring
//create a test dom element
@ -1106,6 +1175,7 @@ void TestQgsProperty::colorRampTransformer()
new QgsGradientColorRamp( QColor( 10, 20, 30 ), QColor( 200, 190, 180 ) ),
QColor( 100, 150, 200 ) );
t1.setRampName( "rampname " );
t1.setCurveTransform( new QgsCurveTransform( QList< QgsPoint >() << QgsPoint( 0, 0.8 ) << QgsPoint( 1, 0.2 ) ) );
QDomElement element = doc.createElement( "xform" );
QVERIFY( t1.writeXml( element, doc ) );
@ -1118,6 +1188,8 @@ void TestQgsProperty::colorRampTransformer()
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 ) );
QVERIFY( r1.curveTransform() );
QCOMPARE( r1.curveTransform()->controlPoints(), QList< QgsPoint >() << QgsPoint( 0, 0.8 ) << QgsPoint( 1, 0.2 ) );
// test cloning
std::unique_ptr< QgsColorRampTransformer > r2( t1.clone() );
@ -1127,6 +1199,8 @@ void TestQgsProperty::colorRampTransformer()
QCOMPARE( r2->rampName(), QStringLiteral( "rampname " ) );
QCOMPARE( static_cast< QgsGradientColorRamp* >( r2->colorRamp() )->color1(), QColor( 10, 20, 30 ) );
QCOMPARE( static_cast< QgsGradientColorRamp* >( r2->colorRamp() )->color2(), QColor( 200, 190, 180 ) );
QVERIFY( r2->curveTransform() );
QCOMPARE( r2->curveTransform()->controlPoints(), QList< QgsPoint >() << QgsPoint( 0, 0.8 ) << QgsPoint( 1, 0.2 ) );
// copy constructor
QgsColorRampTransformer r3( t1 );
@ -1136,6 +1210,8 @@ void TestQgsProperty::colorRampTransformer()
QCOMPARE( r3.rampName(), QStringLiteral( "rampname " ) );
QCOMPARE( static_cast< QgsGradientColorRamp* >( r3.colorRamp() )->color1(), QColor( 10, 20, 30 ) );
QCOMPARE( static_cast< QgsGradientColorRamp* >( r3.colorRamp() )->color2(), QColor( 200, 190, 180 ) );
QVERIFY( r3.curveTransform() );
QCOMPARE( r3.curveTransform()->controlPoints(), QList< QgsPoint >() << QgsPoint( 0, 0.8 ) << QgsPoint( 1, 0.2 ) );
// assignment operator
QgsColorRampTransformer r4;
@ -1146,6 +1222,8 @@ void TestQgsProperty::colorRampTransformer()
QCOMPARE( r4.rampName(), QStringLiteral( "rampname " ) );
QCOMPARE( static_cast< QgsGradientColorRamp* >( r4.colorRamp() )->color1(), QColor( 10, 20, 30 ) );
QCOMPARE( static_cast< QgsGradientColorRamp* >( r4.colorRamp() )->color2(), QColor( 200, 190, 180 ) );
QVERIFY( r4.curveTransform() );
QCOMPARE( r4.curveTransform()->controlPoints(), QList< QgsPoint >() << QgsPoint( 0, 0.8 ) << QgsPoint( 1, 0.2 ) );
//test various min/max value/color and scaling methods