diff --git a/src/core/qgspropertytransformer.cpp b/src/core/qgspropertytransformer.cpp index b1968f51546..3302531848e 100644 --- a/src/core/qgspropertytransformer.cpp +++ b/src/core/qgspropertytransformer.cpp @@ -32,6 +32,9 @@ QgsPropertyTransformer* QgsPropertyTransformer::create( QgsPropertyTransformer:: QgsPropertyTransformer* transformer = nullptr; switch ( type ) { + case GenericNumericTransformer: + transformer = new QgsGenericNumericTransformer(); + break; case SizeScaleTransformer: transformer = new QgsSizeScaleTransformer(); break; @@ -74,6 +77,172 @@ bool QgsPropertyTransformer::readXml( const QDomElement &transformerElem, const return true; } +// +// QgsGenericNumericTransformer +// + +QgsGenericNumericTransformer::QgsGenericNumericTransformer( double minValue, double maxValue, double minOutput, double maxOutput, double nullOutput, double exponent ) + : QgsPropertyTransformer( minValue, maxValue ) + , mMinOutput( minOutput ) + , mMaxOutput( maxOutput ) + , mNullOutput( nullOutput ) + , mExponent( exponent ) +{} + +QgsGenericNumericTransformer *QgsGenericNumericTransformer::clone() +{ + return new QgsGenericNumericTransformer( mMinValue, + mMaxValue, + mMinOutput, + mMaxOutput, + mNullOutput, + mExponent ); +} + +bool QgsGenericNumericTransformer::writeXml( QDomElement &transformerElem, QDomDocument &doc ) const +{ + if ( !QgsPropertyTransformer::writeXml( transformerElem, doc ) ) + return false; + + transformerElem.setAttribute( "minOutput", QString::number( mMinOutput ) ); + transformerElem.setAttribute( "maxOutput", QString::number( mMaxOutput ) ); + transformerElem.setAttribute( "nullOutput", QString::number( mNullOutput ) ); + transformerElem.setAttribute( "exponent", QString::number( mExponent ) ); + + return true; +} + +bool QgsGenericNumericTransformer::readXml( const QDomElement &transformerElem, const QDomDocument &doc ) +{ + if ( !QgsPropertyTransformer::readXml( transformerElem, doc ) ) + return false; + + mMinOutput = transformerElem.attribute( "minOutput", "0.0" ).toDouble(); + mMaxOutput = transformerElem.attribute( "maxOutput", "1.0" ).toDouble(); + mNullOutput = transformerElem.attribute( "nullOutput", "0.0" ).toDouble(); + mExponent = transformerElem.attribute( "exponent", "1.0" ).toDouble(); + return true; +} + +double QgsGenericNumericTransformer::value( double input ) const +{ + if ( qgsDoubleNear( mExponent, 1.0 ) ) + return mMinOutput + ( qBound( mMinValue, input, mMaxValue ) - mMinValue ) * ( mMaxOutput - mMinOutput ) / ( mMaxValue - mMinValue ); + else + return mMinOutput + qPow( qBound( mMinValue, input, mMaxValue ) - mMinValue, mExponent ) * ( mMaxOutput - mMinOutput ) / qPow( mMaxValue - mMinValue, mExponent ); +} + +QVariant QgsGenericNumericTransformer::transform( const QgsExpressionContext& context, const QVariant& v ) const +{ + Q_UNUSED( context ); + + if ( v.isNull() ) + return mNullOutput; + + bool ok; + double dblValue = v.toDouble( &ok ); + + if ( ok ) + { + //apply scaling to value + return value( dblValue ); + } + else + { + return v; + } +} + +QString QgsGenericNumericTransformer::toExpression( const QString& baseExpression ) const +{ + QString minValueString = QString::number( mMinValue ); + QString maxValueString = QString::number( mMaxValue ); + QString minOutputString = QString::number( mMinOutput ); + QString maxOutputString = QString::number( mMaxOutput ); + QString nullOutputString = QString::number( mNullOutput ); + QString exponentString = QString::number( mExponent ); + + if ( qgsDoubleNear( mExponent, 1.0 ) ) + return QStringLiteral( "coalesce(scale_linear(%1, %2, %3, %4, %5), %6)" ).arg( baseExpression, minValueString, maxValueString, minOutputString, maxOutputString, nullOutputString ); + else + return QStringLiteral( "coalesce(scale_exp(%1, %2, %3, %4, %5, %6), %7)" ).arg( baseExpression, minValueString, maxValueString, minOutputString, maxOutputString, exponentString, nullOutputString ); +} + +QgsGenericNumericTransformer* QgsGenericNumericTransformer::fromExpression( const QString& expression, QString& baseExpression, QString& fieldName ) +{ + bool ok = false; + + double nullValue = 0.0; + double exponent = 1.0; + + baseExpression.clear(); + fieldName.clear(); + + QgsExpression e( expression ); + + if ( !e.rootNode() ) + return nullptr; + + const QgsExpression::NodeFunction * f = dynamic_cast( e.rootNode() ); + if ( !f ) + return nullptr; + + QList args = f->args()->list(); + + // the scale function may be enclosed in a coalesce(expr, 0) to avoid NULL value + // to be drawn with the default size + if ( "coalesce" == QgsExpression::Functions()[f->fnIndex()]->name() ) + { + f = dynamic_cast( args[0] ); + if ( !f ) + return nullptr; + nullValue = QgsExpression( args[1]->dump() ).evaluate().toDouble( &ok ); + if ( ! ok ) + return nullptr; + args = f->args()->list(); + } + + if ( "scale_linear" == QgsExpression::Functions()[f->fnIndex()]->name() ) + { + exponent = 1.0; + } + else if ( "scale_exp" == QgsExpression::Functions()[f->fnIndex()]->name() ) + { + exponent = QgsExpression( args[5]->dump() ).evaluate().toDouble( &ok ); + } + else + { + return nullptr; + } + + bool expOk = true; + double minValue = QgsExpression( args[1]->dump() ).evaluate().toDouble( &ok ); + expOk &= ok; + double maxValue = QgsExpression( args[2]->dump() ).evaluate().toDouble( &ok ); + expOk &= ok; + double minOutput = QgsExpression( args[3]->dump() ).evaluate().toDouble( &ok ); + expOk &= ok; + double maxOutput = QgsExpression( args[4]->dump() ).evaluate().toDouble( &ok ); + expOk &= ok; + + if ( !expOk ) + { + return nullptr; + } + + if ( args[0]->nodeType() == QgsExpression::ntColumnRef ) + { + fieldName = static_cast< QgsExpression::NodeColumnRef* >( args[0] )->name(); + } + else + { + baseExpression = args[0]->dump(); + } + return new QgsGenericNumericTransformer( minValue, maxValue, minOutput, maxOutput, nullValue, exponent ); +} + + + // // QgsSizeScaleProperty // @@ -417,4 +586,3 @@ void QgsColorRampTransformer::setColorRamp( QgsColorRamp* ramp ) { mGradientRamp.reset( ramp ); } - diff --git a/src/core/qgspropertytransformer.h b/src/core/qgspropertytransformer.h index fdd8212622b..cdb8a5bd827 100644 --- a/src/core/qgspropertytransformer.h +++ b/src/core/qgspropertytransformer.h @@ -44,6 +44,7 @@ class CORE_EXPORT QgsPropertyTransformer //! Transformer types enum Type { + GenericNumericTransformer, //!< Generic transformer for numeric values (QgsGenericNumericTransformer) SizeScaleTransformer, //!< Size scaling transformer (QgsSizeScaleTransformer) ColorRampTransformer, //!< Color ramp transformer (QgsColorRampTransformer) }; @@ -157,6 +158,124 @@ class CORE_EXPORT QgsPropertyTransformer }; +/** + * \ingroup core + * \class QgsGenericNumericTransformer + * \brief QgsPropertyTransformer subclass for scaling an input numeric value into an output numeric value. + * \note Added in version 3.0 + */ + +class CORE_EXPORT QgsGenericNumericTransformer : public QgsPropertyTransformer +{ + public: + + /** + * Constructor for QgsGenericNumericTransformer. + * @param minValue minimum expected input value + * @param maxValue maximum expected input value + * @param minOutput minimum value to return + * @param maxOutput maximum value to return + * @param nullOutput value to return for null inputs + * @param exponent optional exponential for non-linear scaling + */ + QgsGenericNumericTransformer( double minValue = 0.0, + double maxValue = 1.0, + double minOutput = 0.0, + double maxOutput = 1.0, + double nullOutput = 0.0, + double exponent = 1.0 ); + + virtual Type transformerType() const override { return GenericNumericTransformer; } + virtual QgsGenericNumericTransformer* clone() override; + 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; + + /** + * Attempts to parse an expression into a corresponding QgsSizeScaleTransformer. + * @param expression expression to parse + * @param baseExpression will be set to the component of the source expression which + * is used to calculate the input to the property transformer. This will be set to an + * empty string if a field reference is the transformer input. + * @param fieldName will be set to a field name which is used to calculate the input + * to the property transformer. This will be set to an + * empty string if an expression is the transformer input. + * @returns corresponding QgsSizeScaleTransformer, or nullptr if expression could not + * be parsed to a size scale transformer. + */ + static QgsGenericNumericTransformer* fromExpression( const QString& expression, QString& baseExpression, QString& fieldName ); + + /** + * Calculates the size corresponding to a specific value. + * @param value value to calculate size for + * @returns calculated size using size scale transformer's parameters and type + */ + double value( double input ) const; + + /** + * Returns the minimum calculated size. + * @see setMinSize() + * @see maxSize() + */ + double minOutputValue() const { return mMinOutput; } + + /** + * Sets the minimum calculated size. + * @param size minimum size + * @see minSize() + * @see setMaxSize() + */ + void setMinOutputValue( double size ) { mMinOutput = size; } + + /** + * Returns the maximum calculated size. + * @see minSize() + */ + double maxOutputValue() const { return mMaxOutput; } + + /** + * Sets the maximum calculated size. + * @param size maximum size + * @see maxSize() + * @see setMinSize() + */ + void setMaxOutputValue( double size ) { mMaxOutput = size; } + + /** + * Returns the size value when an expression evaluates to NULL. + * @see setNullSize() + */ + double nullOutputValue() const { return mNullOutput; } + + /** + * Sets the size value for when an expression evaluates to NULL. + * @param size null size + * @see nullSize() + */ + void setNullOutputValue( double size ) { mNullOutput = size; } + + /** + * Returns the exponent for an exponential expression. + * @see setExponent() + * @see type() + */ + double exponent() const { return mExponent; } + + /** + * Sets the exponent for an exponential expression. + * @param exponent exponent + * @see exponent() + */ + void setExponent( double exponent ) { mExponent = exponent; } + + private: + double mMinOutput; + double mMaxOutput; + double mNullOutput; + double mExponent; + +}; /** * \ingroup core diff --git a/src/gui/qgspropertyassistantwidget.cpp b/src/gui/qgspropertyassistantwidget.cpp index bbe87502150..4cc2d4a1a38 100644 --- a/src/gui/qgspropertyassistantwidget.cpp +++ b/src/gui/qgspropertyassistantwidget.cpp @@ -75,6 +75,7 @@ QgsPropertyAssistantWidget::QgsPropertyAssistantWidget( QWidget* parent , case QgsPropertyDefinition::StrokeWidth: { mTransformerWidget = new QgsPropertySizeAssistantWidget( this, mDefinition, initialState ); + mLegendPreview->show(); break; } @@ -82,11 +83,18 @@ QgsPropertyAssistantWidget::QgsPropertyAssistantWidget( QWidget* parent , case QgsPropertyDefinition::ColorWithAlpha: { mTransformerWidget = new QgsPropertyColorAssistantWidget( this, mDefinition, initialState ); + mLegendPreview->show(); break; } default: + { + if ( mDefinition.dataType() == QgsPropertyDefinition::DataTypeNumeric ) + { + mTransformerWidget = new QgsPropertyGenericNumericAssistantWidget( this, mDefinition, initialState ); + } break; + } } if ( mTransformerWidget ) @@ -157,7 +165,7 @@ void QgsPropertyAssistantWidget::computeValuesFromLayer() void QgsPropertyAssistantWidget::updatePreview() { - if ( !mTransformerWidget || !mLayer ) // TODO - make this work OK without a layer + if ( mLegendPreview->isHidden() || !mTransformerWidget || !mLayer ) // TODO - make this work OK without a layer return; mLegendPreview->setIconSize( QSize( 512, 512 ) ); @@ -168,11 +176,6 @@ void QgsPropertyAssistantWidget::updatePreview() QList< QgsSymbolLegendNode* > nodes = mTransformerWidget->generatePreviews( breaks, mLayerTreeLayer, mSymbol.get(), minValueSpinBox->value(), maxValueSpinBox->value() ); - if ( nodes.isEmpty() ) - { - mLegendPreview->show(); - return; - } int widthMax = 0; int i = 0; @@ -200,7 +203,6 @@ void QgsPropertyAssistantWidget::updatePreview() p.end(); mPreviewList.item( i )->setIcon( enlarged ); } - mLegendPreview->show(); } bool QgsPropertyAssistantWidget::computeValuesFromExpression( const QString& expression, double& minValue, double& maxValue ) const @@ -456,3 +458,41 @@ QList QgsPropertyColorAssistantWidget::generatePreviews( c } return nodes; } + +QgsPropertyGenericNumericAssistantWidget::QgsPropertyGenericNumericAssistantWidget( QWidget* parent, const QgsPropertyDefinition& definition, const QgsProperty& initialState ) + : QgsPropertyAbstractTransformerWidget( parent, definition ) +{ + setupUi( this ); + + layout()->setContentsMargins( 0, 0, 0, 0 ); + layout()->setMargin( 0 ); + + minOutputSpinBox->setShowClearButton( false ); + maxOutputSpinBox->setShowClearButton( false ); + nullOutputSpinBox->setShowClearButton( false ); + + if ( const QgsGenericNumericTransformer* transform = dynamic_cast< const QgsGenericNumericTransformer* >( initialState.transformer() ) ) + { + minOutputSpinBox->setValue( transform->minOutputValue() ); + maxOutputSpinBox->setValue( transform->maxOutputValue() ); + nullOutputSpinBox->setValue( transform->nullOutputValue() ); + exponentSpinBox->setValue( transform->exponent() ); + } + + connect( minOutputSpinBox, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgsPropertySizeAssistantWidget::widgetChanged ); + connect( maxOutputSpinBox, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgsPropertySizeAssistantWidget::widgetChanged ); + connect( nullOutputSpinBox, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgsPropertySizeAssistantWidget::widgetChanged ); + connect( exponentSpinBox, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgsPropertySizeAssistantWidget::widgetChanged ); +} + +QgsGenericNumericTransformer*QgsPropertyGenericNumericAssistantWidget::createTransformer( double minValue, double maxValue ) const +{ + QgsGenericNumericTransformer* transformer = new QgsGenericNumericTransformer( + minValue, + maxValue, + minOutputSpinBox->value(), + maxOutputSpinBox->value(), + nullOutputSpinBox->value(), + exponentSpinBox->value() ); + return transformer; +} diff --git a/src/gui/qgspropertyassistantwidget.h b/src/gui/qgspropertyassistantwidget.h index 26aa7f78865..c5a07d17783 100644 --- a/src/gui/qgspropertyassistantwidget.h +++ b/src/gui/qgspropertyassistantwidget.h @@ -22,6 +22,7 @@ #include "ui_qgspropertyassistantwidgetbase.h" #include "ui_qgspropertysizeassistantwidget.h" #include "ui_qgspropertycolorassistantwidget.h" +#include "ui_qgspropertygenericnumericassistantwidget.h" #include "qgsproperty.h" #include "qgslayertreegroup.h" #include "qgssymbol.h" @@ -59,6 +60,18 @@ class GUI_EXPORT QgsPropertyAbstractTransformerWidget : public QWidget }; +class GUI_EXPORT QgsPropertyGenericNumericAssistantWidget : public QgsPropertyAbstractTransformerWidget, private Ui::PropertyGenericNumericAssistant +{ + Q_OBJECT + + public: + + QgsPropertyGenericNumericAssistantWidget( QWidget* parent = nullptr, const QgsPropertyDefinition& definition = QgsPropertyDefinition(), const QgsProperty& initialState = QgsProperty() ); + + virtual QgsGenericNumericTransformer* createTransformer( double minValue, double maxValue ) const override; + +}; + class GUI_EXPORT QgsPropertySizeAssistantWidget : public QgsPropertyAbstractTransformerWidget, private Ui::PropertySizeAssistant { Q_OBJECT diff --git a/src/ui/qgspropertygenericnumericassistantwidget.ui b/src/ui/qgspropertygenericnumericassistantwidget.ui new file mode 100644 index 00000000000..54c3997f4ee --- /dev/null +++ b/src/ui/qgspropertygenericnumericassistantwidget.ui @@ -0,0 +1,127 @@ + + + PropertyGenericNumericAssistant + + + + 0 + 0 + 288 + 192 + + + + + + + 6 + + + 99999999.000000000000000 + + + 10.000000000000000 + + + + + + + Output from + + + + + + + Output when NULL + + + + + + + Exponent + + + + + + + 6 + + + 99999999.000000000000000 + + + 0.100000000000000 + + + + + + + 0.000000000000000 + + + 0.050000000000000 + + + 1.000000000000000 + + + + + + + to + + + + + + + 6 + + + 0.000000000000000 + + + 99999999.000000000000000 + + + 1.000000000000000 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + QgsDoubleSpinBox + QDoubleSpinBox +
qgsdoublespinbox.h
+
+
+ + minOutputSpinBox + maxOutputSpinBox + exponentSpinBox + nullOutputSpinBox + + + +
diff --git a/tests/src/core/testqgsproperty.cpp b/tests/src/core/testqgsproperty.cpp index a3a58dbbc85..f447e3e68e5 100644 --- a/tests/src/core/testqgsproperty.cpp +++ b/tests/src/core/testqgsproperty.cpp @@ -81,6 +81,8 @@ class TestQgsProperty : public QObject void equality(); void propertyTransformer(); //test for QgsPropertyTransformer void propertyTransformerFromExpression(); // text converting expression into QgsPropertyTransformer + void genericNumericTransformer(); + void genericNumericTransformerFromExpression(); // text converting expression to QgsGenericNumericTransformer void sizeScaleTransformer(); //test for QgsSizeScaleTransformer void sizeScaleTransformerFromExpression(); // text converting expression to QgsSizeScaleTransformer void colorRampTransformer(); //test for QgsColorRampTransformer @@ -680,6 +682,173 @@ void TestQgsProperty::propertyTransformerFromExpression() QVERIFY( fieldName.isEmpty() ); } +void TestQgsProperty::genericNumericTransformer() +{ + QgsExpressionContext context; + QgsGenericNumericTransformer t1( 10, + 20, + 100, + 200, + -10, + 1.0 ); + QCOMPARE( t1.transformerType(), QgsPropertyTransformer::GenericNumericTransformer ); + QCOMPARE( t1.minValue(), 10.0 ); + QCOMPARE( t1.maxValue(), 20.0 ); + QCOMPARE( t1.minOutputValue(), 100.0 ); + QCOMPARE( t1.maxOutputValue(), 200.0 ); + QCOMPARE( t1.nullOutputValue(), -10.0 ); + QCOMPARE( t1.exponent(), 1.0 ); + + //transform + QCOMPARE( t1.transform( context, 10 ).toInt(), 100 ); + QCOMPARE( t1.transform( context, 20 ).toInt(), 200 ); + //null value + QCOMPARE( t1.transform( context, QVariant( QVariant::Double ) ).toInt(), -10 ); + //non numeric value + QCOMPARE( t1.transform( context, QVariant( "ffff" ) ), QVariant( "ffff" ) ); + + //saving and restoring + + //create a test dom element + QDomImplementation DomImplementation; + QDomDocumentType documentType = + DomImplementation.createDocumentType( + "qgis", "http://mrcc.com/qgis.dtd", "SYSTEM" ); + QDomDocument doc( documentType ); + + QgsGenericNumericTransformer t2( 15, + 25, + 150, + 250, + -10, + 99 ); + + QDomElement element = doc.createElement( "xform" ); + QVERIFY( t2.writeXml( element, doc ) ); + QgsGenericNumericTransformer r1; + QVERIFY( r1.readXml( element, doc ) ); + QCOMPARE( r1.minValue(), 15.0 ); + QCOMPARE( r1.maxValue(), 25.0 ); + QCOMPARE( r1.minOutputValue(), 150.0 ); + QCOMPARE( r1.maxOutputValue(), 250.0 ); + QCOMPARE( r1.nullOutputValue(), -10.0 ); + QCOMPARE( r1.exponent(), 99.0 ); + + // test cloning + std::unique_ptr< QgsGenericNumericTransformer > r2( t2.clone() ); + QCOMPARE( r2->minValue(), 15.0 ); + QCOMPARE( r2->maxValue(), 25.0 ); + QCOMPARE( r2->minOutputValue(), 150.0 ); + QCOMPARE( r2->maxOutputValue(), 250.0 ); + QCOMPARE( r2->nullOutputValue(), -10.0 ); + QCOMPARE( r2->exponent(), 99.0 ); + + //test various min/max value/size and scaling methods + + //getters and setters + QgsGenericNumericTransformer t; + t.setMinValue( 100 ); + QCOMPARE( t.minValue(), 100.0 ); + t.setMaxValue( 200 ); + QCOMPARE( t.maxValue(), 200.0 ); + t.setMinOutputValue( 10.0 ); + QCOMPARE( t.minOutputValue(), 10.0 ); + t.setMaxOutputValue( 20.0 ); + QCOMPARE( t.maxOutputValue(), 20.0 ); + t.setNullOutputValue( 1 ); + QCOMPARE( t.nullOutputValue(), 1.0 ); + t.setExponent( 2.5 ); + QCOMPARE( t.exponent(), 2.5 ); + + //test linear scaling + t.setExponent( 1.0 ); + QCOMPARE( t.value( 100 ), 10.0 ); + QCOMPARE( t.value( 150 ), 15.0 ); + QCOMPARE( t.value( 200 ), 20.0 ); + //test exponential scaling + t.setExponent( 1.5 ); + QCOMPARE( t.value( 100 ), 10.0 ); + QVERIFY( qgsDoubleNear( t.value( 150 ), 13.5355, 0.001 ) ); + QCOMPARE( t.value( 200 ), 20.0 ); + + //as expression + QgsGenericNumericTransformer t3( 15, + 25, + 150, + 250, + -10, + 1.0 ); + QCOMPARE( t3.toExpression( "5+6" ), QStringLiteral( "coalesce(scale_linear(5+6, 15, 25, 150, 250), -10)" ) ); + t3.setExponent( 1.6 ); + QCOMPARE( t3.toExpression( "5+6" ), QStringLiteral( "coalesce(scale_exp(5+6, 15, 25, 150, 250, 1.6), -10)" ) ); + + // test size scale transformer inside property + QgsProperty p; + p.setTransformer( new QgsGenericNumericTransformer( 15, + 25, + 150, + 250, + -10, + 99 ) ); + p.setStaticValue( QVariant() ); + bool ok = false; + QCOMPARE( p.valueAsDouble( context, 100, &ok ), -10.0 ); + QVERIFY( ok ); + p.setExpressionString( QStringLiteral( "NULL" ) ); + QCOMPARE( p.valueAsDouble( context, 100, &ok ), -10.0 ); + QVERIFY( ok ); + p.setExpressionString( QStringLiteral( "no field" ) ); + QCOMPARE( p.valueAsDouble( context, 100, &ok ), -10.0 ); + QVERIFY( ok ); +} + +void TestQgsProperty::genericNumericTransformerFromExpression() +{ + QString baseExpression; + QString fieldName; + std::unique_ptr< QgsGenericNumericTransformer > exp( QgsGenericNumericTransformer::fromExpression( QStringLiteral( "coalesce(scale_linear(column, 1, 7, 2, 10), 0)" ), baseExpression, fieldName ) ); + QVERIFY( exp.get() ); + QCOMPARE( fieldName, QStringLiteral( "column" ) ); + QVERIFY( baseExpression.isEmpty() ); + QCOMPARE( exp->minValue(), 1. ); + QCOMPARE( exp->maxValue(), 7. ); + QCOMPARE( exp->minOutputValue(), 2. ); + QCOMPARE( exp->maxOutputValue(), 10. ); + QCOMPARE( exp->nullOutputValue(), 0.0 ); + + exp.reset( QgsGenericNumericTransformer::fromExpression( QStringLiteral( "scale_linear(column, 1, 7, 2, 10)" ), baseExpression, fieldName ) ); + QVERIFY( exp.get() ); + QCOMPARE( fieldName, QStringLiteral( "column" ) ); + QVERIFY( baseExpression.isEmpty() ); + QCOMPARE( exp->minValue(), 1. ); + QCOMPARE( exp->maxValue(), 7. ); + QCOMPARE( exp->minOutputValue(), 2. ); + QCOMPARE( exp->maxOutputValue(), 10. ); + + exp.reset( QgsGenericNumericTransformer::fromExpression( QStringLiteral( "scale_linear(column * 2, 1, 7, 2, 10)" ), baseExpression, fieldName ) ); + QVERIFY( exp.get() ); + QCOMPARE( baseExpression, QStringLiteral( "column * 2" ) ); + QVERIFY( fieldName.isEmpty() ); + QCOMPARE( exp->minValue(), 1. ); + QCOMPARE( exp->maxValue(), 7. ); + QCOMPARE( exp->minOutputValue(), 2. ); + QCOMPARE( exp->maxOutputValue(), 10. ); + + exp.reset( QgsGenericNumericTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, 2, 10, 0.51), 1)" ), baseExpression, fieldName ) ); + QVERIFY( exp.get() ); + QCOMPARE( exp->minValue(), 1. ); + QCOMPARE( exp->maxValue(), 7. ); + QCOMPARE( exp->minOutputValue(), 2. ); + QCOMPARE( exp->maxOutputValue(), 10. ); + QCOMPARE( exp->exponent(), 0.51 ); + QCOMPARE( exp->nullOutputValue(), 1.0 ); + + QVERIFY( !QgsGenericNumericTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, a, 10, 0.5), 0)" ), baseExpression, fieldName ) ); + QVERIFY( !QgsGenericNumericTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7), 0)" ), baseExpression, fieldName ) ); + QVERIFY( !QgsGenericNumericTransformer::fromExpression( QStringLiteral( "1+2" ), baseExpression, fieldName ) ); + QVERIFY( !QgsGenericNumericTransformer::fromExpression( QStringLiteral( "" ), baseExpression, fieldName ) ); +} + void TestQgsProperty::sizeScaleTransformer() { QgsExpressionContext context; @@ -842,6 +1011,7 @@ void TestQgsProperty::sizeScaleTransformerFromExpression() QCOMPARE( exp->maxValue(), 7. ); QCOMPARE( exp->minSize(), 2. ); QCOMPARE( exp->maxSize(), 10. ); + QCOMPARE( exp->nullSize(), 0.0 ); exp.reset( QgsSizeScaleTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, 2, 10, 0.5), 0)" ), baseExpression, fieldName ) ); QVERIFY( exp.get() ); @@ -879,9 +1049,10 @@ void TestQgsProperty::sizeScaleTransformerFromExpression() QVERIFY( exp.get() ); QCOMPARE( exp->type(), QgsSizeScaleTransformer::Flannery ); - exp.reset( QgsSizeScaleTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, 2, 10, 0.51), 0)" ), baseExpression, fieldName ) ); + exp.reset( QgsSizeScaleTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, 2, 10, 0.51), 22)" ), baseExpression, fieldName ) ); QVERIFY( exp.get() ); QCOMPARE( exp->type(), QgsSizeScaleTransformer::Exponential ); + QCOMPARE( exp->nullSize(), 22.0 ); QVERIFY( !QgsSizeScaleTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, a, 10, 0.5), 0)" ), baseExpression, fieldName ) ); QVERIFY( !QgsSizeScaleTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7), 0)" ), baseExpression, fieldName ) );