Skip to content

Commit b376ae1

Browse files
committed
Adds methods which attempt to parse expression based properties
as property transformers
1 parent f70a031 commit b376ae1

7 files changed

+328
-48
lines changed

python/core/qgsproperty.sip

+2
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ class QgsProperty
131131

132132
const QgsPropertyTransformer* transformer() const;
133133

134+
bool convertToTransformer();
135+
134136
};
135137

136138

python/core/qgspropertytransformer.sip

+4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ class QgsPropertyTransformer
4747
virtual QVariant transform( const QgsExpressionContext& context, const QVariant& value ) const = 0;
4848
virtual QString toExpression( const QString& baseExpression ) const = 0;
4949

50+
static QgsPropertyTransformer* fromExpression( const QString& expression, QString& baseExpression /Out/, QString& fieldName /Out/ ) /Factory/;
51+
5052
};
5153

5254
class QgsSizeScaleTransformer : QgsPropertyTransformer
@@ -80,6 +82,8 @@ class QgsSizeScaleTransformer : QgsPropertyTransformer
8082
virtual QVariant transform( const QgsExpressionContext& context, const QVariant& value ) const;
8183
virtual QString toExpression( const QString& baseExpression ) const;
8284

85+
static QgsSizeScaleTransformer* fromExpression( const QString& expression, QString& baseExpression /Out/, QString& fieldName /Out/ ) /Factory/;
86+
8387
double size( double value ) const;
8488

8589
double minSize() const;

src/core/qgsproperty.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -704,5 +704,28 @@ const QgsPropertyTransformer* QgsProperty::transformer() const
704704
return d->transformer;
705705
}
706706

707+
bool QgsProperty::convertToTransformer()
708+
{
709+
if ( d->type != ExpressionBasedProperty )
710+
return false;
711+
712+
if ( d->transformer )
713+
return false; // already a transformer
714+
715+
QString baseExpression;
716+
QString fieldName;
717+
std::unique_ptr< QgsPropertyTransformer > transformer( QgsPropertyTransformer::fromExpression( d->expressionString, baseExpression, fieldName ) );
718+
if ( !transformer )
719+
return false;
720+
721+
d.detach();
722+
d->transformer = transformer.release();
723+
if ( !fieldName.isEmpty() )
724+
setField( fieldName );
725+
else
726+
setExpressionString( baseExpression );
727+
return true;
728+
}
729+
707730

708731

src/core/qgsproperty.h

+8
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,14 @@ class CORE_EXPORT QgsProperty
414414
*/
415415
const QgsPropertyTransformer* transformer() const;
416416

417+
/**
418+
* Attempts to convert an existing expression based property to a base expression with
419+
* corresponding transformer. Returns true if conversion was successful. Note that
420+
* calling this method requires multiple parsing of expressions, so it should only
421+
* be called in non-performance critical code.
422+
*/
423+
bool convertToTransformer();
424+
417425
private:
418426

419427
mutable QExplicitlySharedDataPointer<QgsPropertyPrivate> d;

src/core/qgspropertytransformer.cpp

+93
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ bool QgsPropertyTransformer::writeXml( QDomElement& transformerElem, QDomDocumen
5555
return true;
5656
}
5757

58+
QgsPropertyTransformer* QgsPropertyTransformer::fromExpression( const QString& expression, QString& baseExpression, QString& fieldName )
59+
{
60+
baseExpression.clear();
61+
fieldName.clear();
62+
63+
if ( QgsPropertyTransformer* sizeScale = QgsSizeScaleTransformer::fromExpression( expression, baseExpression, fieldName ) )
64+
return sizeScale;
65+
else
66+
return nullptr;
67+
}
68+
5869
bool QgsPropertyTransformer::readXml( const QDomElement &transformerElem, const QDomDocument &doc )
5970
{
6071
Q_UNUSED( doc );
@@ -195,6 +206,88 @@ QString QgsSizeScaleTransformer::toExpression( const QString& baseExpression ) c
195206
return QString();
196207
}
197208

209+
QgsSizeScaleTransformer* QgsSizeScaleTransformer::fromExpression( const QString& expression, QString& baseExpression, QString& fieldName )
210+
{
211+
bool ok = false;
212+
213+
ScaleType type = Linear;
214+
double nullSize = 0.0;
215+
double exponent = 1.0;
216+
217+
baseExpression.clear();
218+
fieldName.clear();
219+
220+
QgsExpression e( expression );
221+
222+
if ( !e.rootNode() )
223+
return nullptr;
224+
225+
const QgsExpression::NodeFunction * f = dynamic_cast<const QgsExpression::NodeFunction*>( e.rootNode() );
226+
if ( !f )
227+
return nullptr;
228+
229+
QList<QgsExpression::Node*> args = f->args()->list();
230+
231+
// the scale function may be enclosed in a coalesce(expr, 0) to avoid NULL value
232+
// to be drawn with the default size
233+
if ( "coalesce" == QgsExpression::Functions()[f->fnIndex()]->name() )
234+
{
235+
f = dynamic_cast<const QgsExpression::NodeFunction*>( args[0] );
236+
if ( !f )
237+
return nullptr;
238+
nullSize = QgsExpression( args[1]->dump() ).evaluate().toDouble( &ok );
239+
if ( ! ok )
240+
return nullptr;
241+
args = f->args()->list();
242+
}
243+
244+
if ( "scale_linear" == QgsExpression::Functions()[f->fnIndex()]->name() )
245+
{
246+
type = Linear;
247+
}
248+
else if ( "scale_exp" == QgsExpression::Functions()[f->fnIndex()]->name() )
249+
{
250+
exponent = QgsExpression( args[5]->dump() ).evaluate().toDouble( &ok );
251+
if ( ! ok )
252+
return nullptr;
253+
if ( qgsDoubleNear( exponent, 0.57, 0.001 ) )
254+
type = Flannery;
255+
else if ( qgsDoubleNear( exponent, 0.5, 0.001 ) )
256+
type = Area;
257+
else
258+
type = Exponential;
259+
}
260+
else
261+
{
262+
return nullptr;
263+
}
264+
265+
bool expOk = true;
266+
double minValue = QgsExpression( args[1]->dump() ).evaluate().toDouble( &ok );
267+
expOk &= ok;
268+
double maxValue = QgsExpression( args[2]->dump() ).evaluate().toDouble( &ok );
269+
expOk &= ok;
270+
double minSize = QgsExpression( args[3]->dump() ).evaluate().toDouble( &ok );
271+
expOk &= ok;
272+
double maxSize = QgsExpression( args[4]->dump() ).evaluate().toDouble( &ok );
273+
expOk &= ok;
274+
275+
if ( !expOk )
276+
{
277+
return nullptr;
278+
}
279+
280+
if ( args[0]->nodeType() == QgsExpression::ntColumnRef )
281+
{
282+
fieldName = static_cast< QgsExpression::NodeColumnRef* >( args[0] )->name();
283+
}
284+
else
285+
{
286+
baseExpression = args[0]->dump();
287+
}
288+
return new QgsSizeScaleTransformer( type, minValue, maxValue, minSize, maxSize, nullSize, exponent );
289+
}
290+
198291

199292
//
200293
// QgsColorRampTransformer

src/core/qgspropertytransformer.h

+28
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,20 @@ class CORE_EXPORT QgsPropertyTransformer
133133
*/
134134
virtual QString toExpression( const QString& baseExpression ) const = 0;
135135

136+
/**
137+
* Attempts to parse an expression into a corresponding property transformer.
138+
* @param expression expression to parse
139+
* @param baseExpression will be set to the component of the source expression which
140+
* is used to calculate the input to the property transformer. This will be set to an
141+
* empty string if a field reference is the transformer input.
142+
* @param fieldName will be set to a field name which is used to calculate the input
143+
* to the property transformer. This will be set to an
144+
* empty string if an expression is the transformer input.
145+
* @returns corresponding property transformer, or nullptr if expression could not
146+
* be parsed to a transformer.
147+
*/
148+
static QgsPropertyTransformer* fromExpression( const QString& expression, QString& baseExpression, QString& fieldName );
149+
136150
protected:
137151

138152
//! Minimum value expected by the transformer
@@ -190,6 +204,20 @@ class CORE_EXPORT QgsSizeScaleTransformer : public QgsPropertyTransformer
190204
virtual QVariant transform( const QgsExpressionContext& context, const QVariant& value ) const override;
191205
virtual QString toExpression( const QString& baseExpression ) const override;
192206

207+
/**
208+
* Attempts to parse an expression into a corresponding QgsSizeScaleTransformer.
209+
* @param expression expression to parse
210+
* @param baseExpression will be set to the component of the source expression which
211+
* is used to calculate the input to the property transformer. This will be set to an
212+
* empty string if a field reference is the transformer input.
213+
* @param fieldName will be set to a field name which is used to calculate the input
214+
* to the property transformer. This will be set to an
215+
* empty string if an expression is the transformer input.
216+
* @returns corresponding QgsSizeScaleTransformer, or nullptr if expression could not
217+
* be parsed to a size scale transformer.
218+
*/
219+
static QgsSizeScaleTransformer* fromExpression( const QString& expression, QString& baseExpression, QString& fieldName );
220+
193221
/**
194222
* Calculates the size corresponding to a specific value.
195223
* @param value value to calculate size for

0 commit comments

Comments
 (0)