Skip to content

Commit

Permalink
Adds methods which attempt to parse expression based properties
Browse files Browse the repository at this point in the history
as property transformers
  • Loading branch information
nyalldawson committed Feb 14, 2017
1 parent f70a031 commit b376ae1
Show file tree
Hide file tree
Showing 7 changed files with 328 additions and 48 deletions.
2 changes: 2 additions & 0 deletions python/core/qgsproperty.sip
Expand Up @@ -131,6 +131,8 @@ class QgsProperty

const QgsPropertyTransformer* transformer() const;

bool convertToTransformer();

};


4 changes: 4 additions & 0 deletions python/core/qgspropertytransformer.sip
Expand Up @@ -47,6 +47,8 @@ class QgsPropertyTransformer
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/;

};

class QgsSizeScaleTransformer : QgsPropertyTransformer
Expand Down Expand Up @@ -80,6 +82,8 @@ class QgsSizeScaleTransformer : QgsPropertyTransformer
virtual QVariant transform( const QgsExpressionContext& context, const QVariant& value ) const;
virtual QString toExpression( const QString& baseExpression ) const;

static QgsSizeScaleTransformer* fromExpression( const QString& expression, QString& baseExpression /Out/, QString& fieldName /Out/ ) /Factory/;

double size( double value ) const;

double minSize() const;
Expand Down
23 changes: 23 additions & 0 deletions src/core/qgsproperty.cpp
Expand Up @@ -704,5 +704,28 @@ const QgsPropertyTransformer* QgsProperty::transformer() const
return d->transformer;
}

bool QgsProperty::convertToTransformer()
{
if ( d->type != ExpressionBasedProperty )
return false;

if ( d->transformer )
return false; // already a transformer

QString baseExpression;
QString fieldName;
std::unique_ptr< QgsPropertyTransformer > transformer( QgsPropertyTransformer::fromExpression( d->expressionString, baseExpression, fieldName ) );
if ( !transformer )
return false;

d.detach();
d->transformer = transformer.release();
if ( !fieldName.isEmpty() )
setField( fieldName );
else
setExpressionString( baseExpression );
return true;
}



8 changes: 8 additions & 0 deletions src/core/qgsproperty.h
Expand Up @@ -414,6 +414,14 @@ class CORE_EXPORT QgsProperty
*/
const QgsPropertyTransformer* transformer() const;

/**
* Attempts to convert an existing expression based property to a base expression with
* corresponding transformer. Returns true if conversion was successful. Note that
* calling this method requires multiple parsing of expressions, so it should only
* be called in non-performance critical code.
*/
bool convertToTransformer();

private:

mutable QExplicitlySharedDataPointer<QgsPropertyPrivate> d;
Expand Down
93 changes: 93 additions & 0 deletions src/core/qgspropertytransformer.cpp
Expand Up @@ -55,6 +55,17 @@ bool QgsPropertyTransformer::writeXml( QDomElement& transformerElem, QDomDocumen
return true;
}

QgsPropertyTransformer* QgsPropertyTransformer::fromExpression( const QString& expression, QString& baseExpression, QString& fieldName )
{
baseExpression.clear();
fieldName.clear();

if ( QgsPropertyTransformer* sizeScale = QgsSizeScaleTransformer::fromExpression( expression, baseExpression, fieldName ) )
return sizeScale;
else
return nullptr;
}

bool QgsPropertyTransformer::readXml( const QDomElement &transformerElem, const QDomDocument &doc )
{
Q_UNUSED( doc );
Expand Down Expand Up @@ -195,6 +206,88 @@ QString QgsSizeScaleTransformer::toExpression( const QString& baseExpression ) c
return QString();
}

QgsSizeScaleTransformer* QgsSizeScaleTransformer::fromExpression( const QString& expression, QString& baseExpression, QString& fieldName )
{
bool ok = false;

ScaleType type = Linear;
double nullSize = 0.0;
double exponent = 1.0;

baseExpression.clear();
fieldName.clear();

QgsExpression e( expression );

if ( !e.rootNode() )
return nullptr;

const QgsExpression::NodeFunction * f = dynamic_cast<const QgsExpression::NodeFunction*>( e.rootNode() );
if ( !f )
return nullptr;

QList<QgsExpression::Node*> 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<const QgsExpression::NodeFunction*>( args[0] );
if ( !f )
return nullptr;
nullSize = QgsExpression( args[1]->dump() ).evaluate().toDouble( &ok );
if ( ! ok )
return nullptr;
args = f->args()->list();
}

if ( "scale_linear" == QgsExpression::Functions()[f->fnIndex()]->name() )
{
type = Linear;
}
else if ( "scale_exp" == QgsExpression::Functions()[f->fnIndex()]->name() )
{
exponent = QgsExpression( args[5]->dump() ).evaluate().toDouble( &ok );
if ( ! ok )
return nullptr;
if ( qgsDoubleNear( exponent, 0.57, 0.001 ) )
type = Flannery;
else if ( qgsDoubleNear( exponent, 0.5, 0.001 ) )
type = Area;
else
type = Exponential;
}
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 minSize = QgsExpression( args[3]->dump() ).evaluate().toDouble( &ok );
expOk &= ok;
double maxSize = 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 QgsSizeScaleTransformer( type, minValue, maxValue, minSize, maxSize, nullSize, exponent );
}


//
// QgsColorRampTransformer
Expand Down
28 changes: 28 additions & 0 deletions src/core/qgspropertytransformer.h
Expand Up @@ -133,6 +133,20 @@ class CORE_EXPORT QgsPropertyTransformer
*/
virtual QString toExpression( const QString& baseExpression ) const = 0;

/**
* Attempts to parse an expression into a corresponding property transformer.
* @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 property transformer, or nullptr if expression could not
* be parsed to a transformer.
*/
static QgsPropertyTransformer* fromExpression( const QString& expression, QString& baseExpression, QString& fieldName );

protected:

//! Minimum value expected by the transformer
Expand Down Expand Up @@ -190,6 +204,20 @@ class CORE_EXPORT QgsSizeScaleTransformer : public QgsPropertyTransformer
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 QgsSizeScaleTransformer* fromExpression( const QString& expression, QString& baseExpression, QString& fieldName );

/**
* Calculates the size corresponding to a specific value.
* @param value value to calculate size for
Expand Down

0 comments on commit b376ae1

Please sign in to comment.