Skip to content

Commit a30fe35

Browse files
committedFeb 14, 2017
[FEATURE] Add a generic numeric transform assistant for easy scaling of values
1 parent 2df37a2 commit a30fe35

File tree

6 files changed

+647
-9
lines changed

6 files changed

+647
-9
lines changed
 

‎src/core/qgspropertytransformer.cpp

Lines changed: 169 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ QgsPropertyTransformer* QgsPropertyTransformer::create( QgsPropertyTransformer::
3232
QgsPropertyTransformer* transformer = nullptr;
3333
switch ( type )
3434
{
35+
case GenericNumericTransformer:
36+
transformer = new QgsGenericNumericTransformer();
37+
break;
3538
case SizeScaleTransformer:
3639
transformer = new QgsSizeScaleTransformer();
3740
break;
@@ -74,6 +77,172 @@ bool QgsPropertyTransformer::readXml( const QDomElement &transformerElem, const
7477
return true;
7578
}
7679

80+
//
81+
// QgsGenericNumericTransformer
82+
//
83+
84+
QgsGenericNumericTransformer::QgsGenericNumericTransformer( double minValue, double maxValue, double minOutput, double maxOutput, double nullOutput, double exponent )
85+
: QgsPropertyTransformer( minValue, maxValue )
86+
, mMinOutput( minOutput )
87+
, mMaxOutput( maxOutput )
88+
, mNullOutput( nullOutput )
89+
, mExponent( exponent )
90+
{}
91+
92+
QgsGenericNumericTransformer *QgsGenericNumericTransformer::clone()
93+
{
94+
return new QgsGenericNumericTransformer( mMinValue,
95+
mMaxValue,
96+
mMinOutput,
97+
mMaxOutput,
98+
mNullOutput,
99+
mExponent );
100+
}
101+
102+
bool QgsGenericNumericTransformer::writeXml( QDomElement &transformerElem, QDomDocument &doc ) const
103+
{
104+
if ( !QgsPropertyTransformer::writeXml( transformerElem, doc ) )
105+
return false;
106+
107+
transformerElem.setAttribute( "minOutput", QString::number( mMinOutput ) );
108+
transformerElem.setAttribute( "maxOutput", QString::number( mMaxOutput ) );
109+
transformerElem.setAttribute( "nullOutput", QString::number( mNullOutput ) );
110+
transformerElem.setAttribute( "exponent", QString::number( mExponent ) );
111+
112+
return true;
113+
}
114+
115+
bool QgsGenericNumericTransformer::readXml( const QDomElement &transformerElem, const QDomDocument &doc )
116+
{
117+
if ( !QgsPropertyTransformer::readXml( transformerElem, doc ) )
118+
return false;
119+
120+
mMinOutput = transformerElem.attribute( "minOutput", "0.0" ).toDouble();
121+
mMaxOutput = transformerElem.attribute( "maxOutput", "1.0" ).toDouble();
122+
mNullOutput = transformerElem.attribute( "nullOutput", "0.0" ).toDouble();
123+
mExponent = transformerElem.attribute( "exponent", "1.0" ).toDouble();
124+
return true;
125+
}
126+
127+
double QgsGenericNumericTransformer::value( double input ) const
128+
{
129+
if ( qgsDoubleNear( mExponent, 1.0 ) )
130+
return mMinOutput + ( qBound( mMinValue, input, mMaxValue ) - mMinValue ) * ( mMaxOutput - mMinOutput ) / ( mMaxValue - mMinValue );
131+
else
132+
return mMinOutput + qPow( qBound( mMinValue, input, mMaxValue ) - mMinValue, mExponent ) * ( mMaxOutput - mMinOutput ) / qPow( mMaxValue - mMinValue, mExponent );
133+
}
134+
135+
QVariant QgsGenericNumericTransformer::transform( const QgsExpressionContext& context, const QVariant& v ) const
136+
{
137+
Q_UNUSED( context );
138+
139+
if ( v.isNull() )
140+
return mNullOutput;
141+
142+
bool ok;
143+
double dblValue = v.toDouble( &ok );
144+
145+
if ( ok )
146+
{
147+
//apply scaling to value
148+
return value( dblValue );
149+
}
150+
else
151+
{
152+
return v;
153+
}
154+
}
155+
156+
QString QgsGenericNumericTransformer::toExpression( const QString& baseExpression ) const
157+
{
158+
QString minValueString = QString::number( mMinValue );
159+
QString maxValueString = QString::number( mMaxValue );
160+
QString minOutputString = QString::number( mMinOutput );
161+
QString maxOutputString = QString::number( mMaxOutput );
162+
QString nullOutputString = QString::number( mNullOutput );
163+
QString exponentString = QString::number( mExponent );
164+
165+
if ( qgsDoubleNear( mExponent, 1.0 ) )
166+
return QStringLiteral( "coalesce(scale_linear(%1, %2, %3, %4, %5), %6)" ).arg( baseExpression, minValueString, maxValueString, minOutputString, maxOutputString, nullOutputString );
167+
else
168+
return QStringLiteral( "coalesce(scale_exp(%1, %2, %3, %4, %5, %6), %7)" ).arg( baseExpression, minValueString, maxValueString, minOutputString, maxOutputString, exponentString, nullOutputString );
169+
}
170+
171+
QgsGenericNumericTransformer* QgsGenericNumericTransformer::fromExpression( const QString& expression, QString& baseExpression, QString& fieldName )
172+
{
173+
bool ok = false;
174+
175+
double nullValue = 0.0;
176+
double exponent = 1.0;
177+
178+
baseExpression.clear();
179+
fieldName.clear();
180+
181+
QgsExpression e( expression );
182+
183+
if ( !e.rootNode() )
184+
return nullptr;
185+
186+
const QgsExpression::NodeFunction * f = dynamic_cast<const QgsExpression::NodeFunction*>( e.rootNode() );
187+
if ( !f )
188+
return nullptr;
189+
190+
QList<QgsExpression::Node*> args = f->args()->list();
191+
192+
// the scale function may be enclosed in a coalesce(expr, 0) to avoid NULL value
193+
// to be drawn with the default size
194+
if ( "coalesce" == QgsExpression::Functions()[f->fnIndex()]->name() )
195+
{
196+
f = dynamic_cast<const QgsExpression::NodeFunction*>( args[0] );
197+
if ( !f )
198+
return nullptr;
199+
nullValue = QgsExpression( args[1]->dump() ).evaluate().toDouble( &ok );
200+
if ( ! ok )
201+
return nullptr;
202+
args = f->args()->list();
203+
}
204+
205+
if ( "scale_linear" == QgsExpression::Functions()[f->fnIndex()]->name() )
206+
{
207+
exponent = 1.0;
208+
}
209+
else if ( "scale_exp" == QgsExpression::Functions()[f->fnIndex()]->name() )
210+
{
211+
exponent = QgsExpression( args[5]->dump() ).evaluate().toDouble( &ok );
212+
}
213+
else
214+
{
215+
return nullptr;
216+
}
217+
218+
bool expOk = true;
219+
double minValue = QgsExpression( args[1]->dump() ).evaluate().toDouble( &ok );
220+
expOk &= ok;
221+
double maxValue = QgsExpression( args[2]->dump() ).evaluate().toDouble( &ok );
222+
expOk &= ok;
223+
double minOutput = QgsExpression( args[3]->dump() ).evaluate().toDouble( &ok );
224+
expOk &= ok;
225+
double maxOutput = QgsExpression( args[4]->dump() ).evaluate().toDouble( &ok );
226+
expOk &= ok;
227+
228+
if ( !expOk )
229+
{
230+
return nullptr;
231+
}
232+
233+
if ( args[0]->nodeType() == QgsExpression::ntColumnRef )
234+
{
235+
fieldName = static_cast< QgsExpression::NodeColumnRef* >( args[0] )->name();
236+
}
237+
else
238+
{
239+
baseExpression = args[0]->dump();
240+
}
241+
return new QgsGenericNumericTransformer( minValue, maxValue, minOutput, maxOutput, nullValue, exponent );
242+
}
243+
244+
245+
77246
//
78247
// QgsSizeScaleProperty
79248
//
@@ -417,4 +586,3 @@ void QgsColorRampTransformer::setColorRamp( QgsColorRamp* ramp )
417586
{
418587
mGradientRamp.reset( ramp );
419588
}
420-

‎src/core/qgspropertytransformer.h

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class CORE_EXPORT QgsPropertyTransformer
4444
//! Transformer types
4545
enum Type
4646
{
47+
GenericNumericTransformer, //!< Generic transformer for numeric values (QgsGenericNumericTransformer)
4748
SizeScaleTransformer, //!< Size scaling transformer (QgsSizeScaleTransformer)
4849
ColorRampTransformer, //!< Color ramp transformer (QgsColorRampTransformer)
4950
};
@@ -157,6 +158,124 @@ class CORE_EXPORT QgsPropertyTransformer
157158

158159
};
159160

161+
/**
162+
* \ingroup core
163+
* \class QgsGenericNumericTransformer
164+
* \brief QgsPropertyTransformer subclass for scaling an input numeric value into an output numeric value.
165+
* \note Added in version 3.0
166+
*/
167+
168+
class CORE_EXPORT QgsGenericNumericTransformer : public QgsPropertyTransformer
169+
{
170+
public:
171+
172+
/**
173+
* Constructor for QgsGenericNumericTransformer.
174+
* @param minValue minimum expected input value
175+
* @param maxValue maximum expected input value
176+
* @param minOutput minimum value to return
177+
* @param maxOutput maximum value to return
178+
* @param nullOutput value to return for null inputs
179+
* @param exponent optional exponential for non-linear scaling
180+
*/
181+
QgsGenericNumericTransformer( double minValue = 0.0,
182+
double maxValue = 1.0,
183+
double minOutput = 0.0,
184+
double maxOutput = 1.0,
185+
double nullOutput = 0.0,
186+
double exponent = 1.0 );
187+
188+
virtual Type transformerType() const override { return GenericNumericTransformer; }
189+
virtual QgsGenericNumericTransformer* clone() override;
190+
virtual bool writeXml( QDomElement& transformerElem, QDomDocument& doc ) const override;
191+
virtual bool readXml( const QDomElement& transformerElem, const QDomDocument& doc ) override;
192+
virtual QVariant transform( const QgsExpressionContext& context, const QVariant& value ) const override;
193+
virtual QString toExpression( const QString& baseExpression ) const override;
194+
195+
/**
196+
* Attempts to parse an expression into a corresponding QgsSizeScaleTransformer.
197+
* @param expression expression to parse
198+
* @param baseExpression will be set to the component of the source expression which
199+
* is used to calculate the input to the property transformer. This will be set to an
200+
* empty string if a field reference is the transformer input.
201+
* @param fieldName will be set to a field name which is used to calculate the input
202+
* to the property transformer. This will be set to an
203+
* empty string if an expression is the transformer input.
204+
* @returns corresponding QgsSizeScaleTransformer, or nullptr if expression could not
205+
* be parsed to a size scale transformer.
206+
*/
207+
static QgsGenericNumericTransformer* fromExpression( const QString& expression, QString& baseExpression, QString& fieldName );
208+
209+
/**
210+
* Calculates the size corresponding to a specific value.
211+
* @param value value to calculate size for
212+
* @returns calculated size using size scale transformer's parameters and type
213+
*/
214+
double value( double input ) const;
215+
216+
/**
217+
* Returns the minimum calculated size.
218+
* @see setMinSize()
219+
* @see maxSize()
220+
*/
221+
double minOutputValue() const { return mMinOutput; }
222+
223+
/**
224+
* Sets the minimum calculated size.
225+
* @param size minimum size
226+
* @see minSize()
227+
* @see setMaxSize()
228+
*/
229+
void setMinOutputValue( double size ) { mMinOutput = size; }
230+
231+
/**
232+
* Returns the maximum calculated size.
233+
* @see minSize()
234+
*/
235+
double maxOutputValue() const { return mMaxOutput; }
236+
237+
/**
238+
* Sets the maximum calculated size.
239+
* @param size maximum size
240+
* @see maxSize()
241+
* @see setMinSize()
242+
*/
243+
void setMaxOutputValue( double size ) { mMaxOutput = size; }
244+
245+
/**
246+
* Returns the size value when an expression evaluates to NULL.
247+
* @see setNullSize()
248+
*/
249+
double nullOutputValue() const { return mNullOutput; }
250+
251+
/**
252+
* Sets the size value for when an expression evaluates to NULL.
253+
* @param size null size
254+
* @see nullSize()
255+
*/
256+
void setNullOutputValue( double size ) { mNullOutput = size; }
257+
258+
/**
259+
* Returns the exponent for an exponential expression.
260+
* @see setExponent()
261+
* @see type()
262+
*/
263+
double exponent() const { return mExponent; }
264+
265+
/**
266+
* Sets the exponent for an exponential expression.
267+
* @param exponent exponent
268+
* @see exponent()
269+
*/
270+
void setExponent( double exponent ) { mExponent = exponent; }
271+
272+
private:
273+
double mMinOutput;
274+
double mMaxOutput;
275+
double mNullOutput;
276+
double mExponent;
277+
278+
};
160279

161280
/**
162281
* \ingroup core

‎src/gui/qgspropertyassistantwidget.cpp

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,18 +75,26 @@ QgsPropertyAssistantWidget::QgsPropertyAssistantWidget( QWidget* parent ,
7575
case QgsPropertyDefinition::StrokeWidth:
7676
{
7777
mTransformerWidget = new QgsPropertySizeAssistantWidget( this, mDefinition, initialState );
78+
mLegendPreview->show();
7879
break;
7980
}
8081

8182
case QgsPropertyDefinition::ColorNoAlpha:
8283
case QgsPropertyDefinition::ColorWithAlpha:
8384
{
8485
mTransformerWidget = new QgsPropertyColorAssistantWidget( this, mDefinition, initialState );
86+
mLegendPreview->show();
8587
break;
8688
}
8789

8890
default:
91+
{
92+
if ( mDefinition.dataType() == QgsPropertyDefinition::DataTypeNumeric )
93+
{
94+
mTransformerWidget = new QgsPropertyGenericNumericAssistantWidget( this, mDefinition, initialState );
95+
}
8996
break;
97+
}
9098
}
9199

92100
if ( mTransformerWidget )
@@ -157,7 +165,7 @@ void QgsPropertyAssistantWidget::computeValuesFromLayer()
157165

158166
void QgsPropertyAssistantWidget::updatePreview()
159167
{
160-
if ( !mTransformerWidget || !mLayer ) // TODO - make this work OK without a layer
168+
if ( mLegendPreview->isHidden() || !mTransformerWidget || !mLayer ) // TODO - make this work OK without a layer
161169
return;
162170

163171
mLegendPreview->setIconSize( QSize( 512, 512 ) );
@@ -168,11 +176,6 @@ void QgsPropertyAssistantWidget::updatePreview()
168176

169177
QList< QgsSymbolLegendNode* > nodes = mTransformerWidget->generatePreviews( breaks, mLayerTreeLayer, mSymbol.get(), minValueSpinBox->value(),
170178
maxValueSpinBox->value() );
171-
if ( nodes.isEmpty() )
172-
{
173-
mLegendPreview->show();
174-
return;
175-
}
176179

177180
int widthMax = 0;
178181
int i = 0;
@@ -200,7 +203,6 @@ void QgsPropertyAssistantWidget::updatePreview()
200203
p.end();
201204
mPreviewList.item( i )->setIcon( enlarged );
202205
}
203-
mLegendPreview->show();
204206
}
205207

206208
bool QgsPropertyAssistantWidget::computeValuesFromExpression( const QString& expression, double& minValue, double& maxValue ) const
@@ -456,3 +458,41 @@ QList<QgsSymbolLegendNode*> QgsPropertyColorAssistantWidget::generatePreviews( c
456458
}
457459
return nodes;
458460
}
461+
462+
QgsPropertyGenericNumericAssistantWidget::QgsPropertyGenericNumericAssistantWidget( QWidget* parent, const QgsPropertyDefinition& definition, const QgsProperty& initialState )
463+
: QgsPropertyAbstractTransformerWidget( parent, definition )
464+
{
465+
setupUi( this );
466+
467+
layout()->setContentsMargins( 0, 0, 0, 0 );
468+
layout()->setMargin( 0 );
469+
470+
minOutputSpinBox->setShowClearButton( false );
471+
maxOutputSpinBox->setShowClearButton( false );
472+
nullOutputSpinBox->setShowClearButton( false );
473+
474+
if ( const QgsGenericNumericTransformer* transform = dynamic_cast< const QgsGenericNumericTransformer* >( initialState.transformer() ) )
475+
{
476+
minOutputSpinBox->setValue( transform->minOutputValue() );
477+
maxOutputSpinBox->setValue( transform->maxOutputValue() );
478+
nullOutputSpinBox->setValue( transform->nullOutputValue() );
479+
exponentSpinBox->setValue( transform->exponent() );
480+
}
481+
482+
connect( minOutputSpinBox, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgsPropertySizeAssistantWidget::widgetChanged );
483+
connect( maxOutputSpinBox, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgsPropertySizeAssistantWidget::widgetChanged );
484+
connect( nullOutputSpinBox, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgsPropertySizeAssistantWidget::widgetChanged );
485+
connect( exponentSpinBox, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgsPropertySizeAssistantWidget::widgetChanged );
486+
}
487+
488+
QgsGenericNumericTransformer*QgsPropertyGenericNumericAssistantWidget::createTransformer( double minValue, double maxValue ) const
489+
{
490+
QgsGenericNumericTransformer* transformer = new QgsGenericNumericTransformer(
491+
minValue,
492+
maxValue,
493+
minOutputSpinBox->value(),
494+
maxOutputSpinBox->value(),
495+
nullOutputSpinBox->value(),
496+
exponentSpinBox->value() );
497+
return transformer;
498+
}

‎src/gui/qgspropertyassistantwidget.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "ui_qgspropertyassistantwidgetbase.h"
2323
#include "ui_qgspropertysizeassistantwidget.h"
2424
#include "ui_qgspropertycolorassistantwidget.h"
25+
#include "ui_qgspropertygenericnumericassistantwidget.h"
2526
#include "qgsproperty.h"
2627
#include "qgslayertreegroup.h"
2728
#include "qgssymbol.h"
@@ -59,6 +60,18 @@ class GUI_EXPORT QgsPropertyAbstractTransformerWidget : public QWidget
5960

6061
};
6162

63+
class GUI_EXPORT QgsPropertyGenericNumericAssistantWidget : public QgsPropertyAbstractTransformerWidget, private Ui::PropertyGenericNumericAssistant
64+
{
65+
Q_OBJECT
66+
67+
public:
68+
69+
QgsPropertyGenericNumericAssistantWidget( QWidget* parent = nullptr, const QgsPropertyDefinition& definition = QgsPropertyDefinition(), const QgsProperty& initialState = QgsProperty() );
70+
71+
virtual QgsGenericNumericTransformer* createTransformer( double minValue, double maxValue ) const override;
72+
73+
};
74+
6275
class GUI_EXPORT QgsPropertySizeAssistantWidget : public QgsPropertyAbstractTransformerWidget, private Ui::PropertySizeAssistant
6376
{
6477
Q_OBJECT
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>PropertyGenericNumericAssistant</class>
4+
<widget class="QWidget" name="PropertyGenericNumericAssistant">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>288</width>
10+
<height>192</height>
11+
</rect>
12+
</property>
13+
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,0">
14+
<item row="1" column="1">
15+
<widget class="QgsDoubleSpinBox" name="maxOutputSpinBox">
16+
<property name="decimals">
17+
<number>6</number>
18+
</property>
19+
<property name="maximum">
20+
<double>99999999.000000000000000</double>
21+
</property>
22+
<property name="value">
23+
<double>10.000000000000000</double>
24+
</property>
25+
</widget>
26+
</item>
27+
<item row="0" column="0">
28+
<widget class="QLabel" name="label_5">
29+
<property name="text">
30+
<string>Output from</string>
31+
</property>
32+
</widget>
33+
</item>
34+
<item row="3" column="0">
35+
<widget class="QLabel" name="label_8">
36+
<property name="text">
37+
<string>Output when NULL</string>
38+
</property>
39+
</widget>
40+
</item>
41+
<item row="2" column="0">
42+
<widget class="QLabel" name="label_2">
43+
<property name="text">
44+
<string>Exponent</string>
45+
</property>
46+
</widget>
47+
</item>
48+
<item row="3" column="1">
49+
<widget class="QgsDoubleSpinBox" name="nullOutputSpinBox">
50+
<property name="decimals">
51+
<number>6</number>
52+
</property>
53+
<property name="maximum">
54+
<double>99999999.000000000000000</double>
55+
</property>
56+
<property name="singleStep">
57+
<double>0.100000000000000</double>
58+
</property>
59+
</widget>
60+
</item>
61+
<item row="2" column="1">
62+
<widget class="QgsDoubleSpinBox" name="exponentSpinBox">
63+
<property name="minimum">
64+
<double>0.000000000000000</double>
65+
</property>
66+
<property name="singleStep">
67+
<double>0.050000000000000</double>
68+
</property>
69+
<property name="value">
70+
<double>1.000000000000000</double>
71+
</property>
72+
</widget>
73+
</item>
74+
<item row="1" column="0">
75+
<widget class="QLabel" name="label_6">
76+
<property name="text">
77+
<string>to</string>
78+
</property>
79+
</widget>
80+
</item>
81+
<item row="0" column="1">
82+
<widget class="QgsDoubleSpinBox" name="minOutputSpinBox">
83+
<property name="decimals">
84+
<number>6</number>
85+
</property>
86+
<property name="minimum">
87+
<double>0.000000000000000</double>
88+
</property>
89+
<property name="maximum">
90+
<double>99999999.000000000000000</double>
91+
</property>
92+
<property name="value">
93+
<double>1.000000000000000</double>
94+
</property>
95+
</widget>
96+
</item>
97+
<item row="4" column="0">
98+
<spacer name="verticalSpacer">
99+
<property name="orientation">
100+
<enum>Qt::Vertical</enum>
101+
</property>
102+
<property name="sizeHint" stdset="0">
103+
<size>
104+
<width>20</width>
105+
<height>40</height>
106+
</size>
107+
</property>
108+
</spacer>
109+
</item>
110+
</layout>
111+
</widget>
112+
<customwidgets>
113+
<customwidget>
114+
<class>QgsDoubleSpinBox</class>
115+
<extends>QDoubleSpinBox</extends>
116+
<header>qgsdoublespinbox.h</header>
117+
</customwidget>
118+
</customwidgets>
119+
<tabstops>
120+
<tabstop>minOutputSpinBox</tabstop>
121+
<tabstop>maxOutputSpinBox</tabstop>
122+
<tabstop>exponentSpinBox</tabstop>
123+
<tabstop>nullOutputSpinBox</tabstop>
124+
</tabstops>
125+
<resources/>
126+
<connections/>
127+
</ui>

‎tests/src/core/testqgsproperty.cpp

Lines changed: 172 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ class TestQgsProperty : public QObject
8181
void equality();
8282
void propertyTransformer(); //test for QgsPropertyTransformer
8383
void propertyTransformerFromExpression(); // text converting expression into QgsPropertyTransformer
84+
void genericNumericTransformer();
85+
void genericNumericTransformerFromExpression(); // text converting expression to QgsGenericNumericTransformer
8486
void sizeScaleTransformer(); //test for QgsSizeScaleTransformer
8587
void sizeScaleTransformerFromExpression(); // text converting expression to QgsSizeScaleTransformer
8688
void colorRampTransformer(); //test for QgsColorRampTransformer
@@ -680,6 +682,173 @@ void TestQgsProperty::propertyTransformerFromExpression()
680682
QVERIFY( fieldName.isEmpty() );
681683
}
682684

685+
void TestQgsProperty::genericNumericTransformer()
686+
{
687+
QgsExpressionContext context;
688+
QgsGenericNumericTransformer t1( 10,
689+
20,
690+
100,
691+
200,
692+
-10,
693+
1.0 );
694+
QCOMPARE( t1.transformerType(), QgsPropertyTransformer::GenericNumericTransformer );
695+
QCOMPARE( t1.minValue(), 10.0 );
696+
QCOMPARE( t1.maxValue(), 20.0 );
697+
QCOMPARE( t1.minOutputValue(), 100.0 );
698+
QCOMPARE( t1.maxOutputValue(), 200.0 );
699+
QCOMPARE( t1.nullOutputValue(), -10.0 );
700+
QCOMPARE( t1.exponent(), 1.0 );
701+
702+
//transform
703+
QCOMPARE( t1.transform( context, 10 ).toInt(), 100 );
704+
QCOMPARE( t1.transform( context, 20 ).toInt(), 200 );
705+
//null value
706+
QCOMPARE( t1.transform( context, QVariant( QVariant::Double ) ).toInt(), -10 );
707+
//non numeric value
708+
QCOMPARE( t1.transform( context, QVariant( "ffff" ) ), QVariant( "ffff" ) );
709+
710+
//saving and restoring
711+
712+
//create a test dom element
713+
QDomImplementation DomImplementation;
714+
QDomDocumentType documentType =
715+
DomImplementation.createDocumentType(
716+
"qgis", "http://mrcc.com/qgis.dtd", "SYSTEM" );
717+
QDomDocument doc( documentType );
718+
719+
QgsGenericNumericTransformer t2( 15,
720+
25,
721+
150,
722+
250,
723+
-10,
724+
99 );
725+
726+
QDomElement element = doc.createElement( "xform" );
727+
QVERIFY( t2.writeXml( element, doc ) );
728+
QgsGenericNumericTransformer r1;
729+
QVERIFY( r1.readXml( element, doc ) );
730+
QCOMPARE( r1.minValue(), 15.0 );
731+
QCOMPARE( r1.maxValue(), 25.0 );
732+
QCOMPARE( r1.minOutputValue(), 150.0 );
733+
QCOMPARE( r1.maxOutputValue(), 250.0 );
734+
QCOMPARE( r1.nullOutputValue(), -10.0 );
735+
QCOMPARE( r1.exponent(), 99.0 );
736+
737+
// test cloning
738+
std::unique_ptr< QgsGenericNumericTransformer > r2( t2.clone() );
739+
QCOMPARE( r2->minValue(), 15.0 );
740+
QCOMPARE( r2->maxValue(), 25.0 );
741+
QCOMPARE( r2->minOutputValue(), 150.0 );
742+
QCOMPARE( r2->maxOutputValue(), 250.0 );
743+
QCOMPARE( r2->nullOutputValue(), -10.0 );
744+
QCOMPARE( r2->exponent(), 99.0 );
745+
746+
//test various min/max value/size and scaling methods
747+
748+
//getters and setters
749+
QgsGenericNumericTransformer t;
750+
t.setMinValue( 100 );
751+
QCOMPARE( t.minValue(), 100.0 );
752+
t.setMaxValue( 200 );
753+
QCOMPARE( t.maxValue(), 200.0 );
754+
t.setMinOutputValue( 10.0 );
755+
QCOMPARE( t.minOutputValue(), 10.0 );
756+
t.setMaxOutputValue( 20.0 );
757+
QCOMPARE( t.maxOutputValue(), 20.0 );
758+
t.setNullOutputValue( 1 );
759+
QCOMPARE( t.nullOutputValue(), 1.0 );
760+
t.setExponent( 2.5 );
761+
QCOMPARE( t.exponent(), 2.5 );
762+
763+
//test linear scaling
764+
t.setExponent( 1.0 );
765+
QCOMPARE( t.value( 100 ), 10.0 );
766+
QCOMPARE( t.value( 150 ), 15.0 );
767+
QCOMPARE( t.value( 200 ), 20.0 );
768+
//test exponential scaling
769+
t.setExponent( 1.5 );
770+
QCOMPARE( t.value( 100 ), 10.0 );
771+
QVERIFY( qgsDoubleNear( t.value( 150 ), 13.5355, 0.001 ) );
772+
QCOMPARE( t.value( 200 ), 20.0 );
773+
774+
//as expression
775+
QgsGenericNumericTransformer t3( 15,
776+
25,
777+
150,
778+
250,
779+
-10,
780+
1.0 );
781+
QCOMPARE( t3.toExpression( "5+6" ), QStringLiteral( "coalesce(scale_linear(5+6, 15, 25, 150, 250), -10)" ) );
782+
t3.setExponent( 1.6 );
783+
QCOMPARE( t3.toExpression( "5+6" ), QStringLiteral( "coalesce(scale_exp(5+6, 15, 25, 150, 250, 1.6), -10)" ) );
784+
785+
// test size scale transformer inside property
786+
QgsProperty p;
787+
p.setTransformer( new QgsGenericNumericTransformer( 15,
788+
25,
789+
150,
790+
250,
791+
-10,
792+
99 ) );
793+
p.setStaticValue( QVariant() );
794+
bool ok = false;
795+
QCOMPARE( p.valueAsDouble( context, 100, &ok ), -10.0 );
796+
QVERIFY( ok );
797+
p.setExpressionString( QStringLiteral( "NULL" ) );
798+
QCOMPARE( p.valueAsDouble( context, 100, &ok ), -10.0 );
799+
QVERIFY( ok );
800+
p.setExpressionString( QStringLiteral( "no field" ) );
801+
QCOMPARE( p.valueAsDouble( context, 100, &ok ), -10.0 );
802+
QVERIFY( ok );
803+
}
804+
805+
void TestQgsProperty::genericNumericTransformerFromExpression()
806+
{
807+
QString baseExpression;
808+
QString fieldName;
809+
std::unique_ptr< QgsGenericNumericTransformer > exp( QgsGenericNumericTransformer::fromExpression( QStringLiteral( "coalesce(scale_linear(column, 1, 7, 2, 10), 0)" ), baseExpression, fieldName ) );
810+
QVERIFY( exp.get() );
811+
QCOMPARE( fieldName, QStringLiteral( "column" ) );
812+
QVERIFY( baseExpression.isEmpty() );
813+
QCOMPARE( exp->minValue(), 1. );
814+
QCOMPARE( exp->maxValue(), 7. );
815+
QCOMPARE( exp->minOutputValue(), 2. );
816+
QCOMPARE( exp->maxOutputValue(), 10. );
817+
QCOMPARE( exp->nullOutputValue(), 0.0 );
818+
819+
exp.reset( QgsGenericNumericTransformer::fromExpression( QStringLiteral( "scale_linear(column, 1, 7, 2, 10)" ), baseExpression, fieldName ) );
820+
QVERIFY( exp.get() );
821+
QCOMPARE( fieldName, QStringLiteral( "column" ) );
822+
QVERIFY( baseExpression.isEmpty() );
823+
QCOMPARE( exp->minValue(), 1. );
824+
QCOMPARE( exp->maxValue(), 7. );
825+
QCOMPARE( exp->minOutputValue(), 2. );
826+
QCOMPARE( exp->maxOutputValue(), 10. );
827+
828+
exp.reset( QgsGenericNumericTransformer::fromExpression( QStringLiteral( "scale_linear(column * 2, 1, 7, 2, 10)" ), baseExpression, fieldName ) );
829+
QVERIFY( exp.get() );
830+
QCOMPARE( baseExpression, QStringLiteral( "column * 2" ) );
831+
QVERIFY( fieldName.isEmpty() );
832+
QCOMPARE( exp->minValue(), 1. );
833+
QCOMPARE( exp->maxValue(), 7. );
834+
QCOMPARE( exp->minOutputValue(), 2. );
835+
QCOMPARE( exp->maxOutputValue(), 10. );
836+
837+
exp.reset( QgsGenericNumericTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, 2, 10, 0.51), 1)" ), baseExpression, fieldName ) );
838+
QVERIFY( exp.get() );
839+
QCOMPARE( exp->minValue(), 1. );
840+
QCOMPARE( exp->maxValue(), 7. );
841+
QCOMPARE( exp->minOutputValue(), 2. );
842+
QCOMPARE( exp->maxOutputValue(), 10. );
843+
QCOMPARE( exp->exponent(), 0.51 );
844+
QCOMPARE( exp->nullOutputValue(), 1.0 );
845+
846+
QVERIFY( !QgsGenericNumericTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, a, 10, 0.5), 0)" ), baseExpression, fieldName ) );
847+
QVERIFY( !QgsGenericNumericTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7), 0)" ), baseExpression, fieldName ) );
848+
QVERIFY( !QgsGenericNumericTransformer::fromExpression( QStringLiteral( "1+2" ), baseExpression, fieldName ) );
849+
QVERIFY( !QgsGenericNumericTransformer::fromExpression( QStringLiteral( "" ), baseExpression, fieldName ) );
850+
}
851+
683852
void TestQgsProperty::sizeScaleTransformer()
684853
{
685854
QgsExpressionContext context;
@@ -842,6 +1011,7 @@ void TestQgsProperty::sizeScaleTransformerFromExpression()
8421011
QCOMPARE( exp->maxValue(), 7. );
8431012
QCOMPARE( exp->minSize(), 2. );
8441013
QCOMPARE( exp->maxSize(), 10. );
1014+
QCOMPARE( exp->nullSize(), 0.0 );
8451015

8461016
exp.reset( QgsSizeScaleTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, 2, 10, 0.5), 0)" ), baseExpression, fieldName ) );
8471017
QVERIFY( exp.get() );
@@ -879,9 +1049,10 @@ void TestQgsProperty::sizeScaleTransformerFromExpression()
8791049
QVERIFY( exp.get() );
8801050
QCOMPARE( exp->type(), QgsSizeScaleTransformer::Flannery );
8811051

882-
exp.reset( QgsSizeScaleTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, 2, 10, 0.51), 0)" ), baseExpression, fieldName ) );
1052+
exp.reset( QgsSizeScaleTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, 2, 10, 0.51), 22)" ), baseExpression, fieldName ) );
8831053
QVERIFY( exp.get() );
8841054
QCOMPARE( exp->type(), QgsSizeScaleTransformer::Exponential );
1055+
QCOMPARE( exp->nullSize(), 22.0 );
8851056

8861057
QVERIFY( !QgsSizeScaleTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, a, 10, 0.5), 0)" ), baseExpression, fieldName ) );
8871058
QVERIFY( !QgsSizeScaleTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7), 0)" ), baseExpression, fieldName ) );

0 commit comments

Comments
 (0)
Please sign in to comment.