1 /***************************************************************************
2 testqgsproperty.cpp
3 -------------------
4 begin : April 2015
5 copyright : (C) 2015 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "qgstest.h"
19 #include "qgsproperty.h"
20 #include "qgspropertycollection.h"
21 #include "qgsvectorlayer.h"
22 #include "qgsapplication.h"
23 #include "qgscolorramp.h"
24 #include "qgssymbollayerutils.h"
25 #include "qgspropertytransformer.h"
26 #include <QObject>
27
28 enum PropertyKeys
29 {
30 Property1,
31 Property2,
32 Property3,
33 Property4,
34 };
35
36 class TestTransformer : public QgsPropertyTransformer
37 {
38 public:
39
TestTransformer(double minValue,double maxValue)40 TestTransformer( double minValue, double maxValue )
41 : QgsPropertyTransformer( minValue, maxValue )
42 {
43
44 }
45
transformerType() const46 Type transformerType() const override { return SizeScaleTransformer; }
clone() const47 TestTransformer *clone() const override
48 {
49 return new TestTransformer( mMinValue, mMaxValue );
50 }
toExpression(const QString &) const51 QString toExpression( const QString & ) const override { return QString(); }
52
53 private:
54
transform(const QgsExpressionContext & context,const QVariant & value) const55 QVariant transform( const QgsExpressionContext &context, const QVariant &value ) const override
56 {
57 Q_UNUSED( context );
58
59 if ( value.isNull() )
60 return -1.0;
61
62 return value.toDouble() * 2;
63 }
64
65 };
66
67
68 class TestQgsProperty : public QObject
69 {
70 Q_OBJECT
71
72 private slots:
73 void initTestCase();// will be called before the first testfunction is executed.
74 void cleanupTestCase();// will be called after the last testfunction was executed.
75 void init();// will be called before each testfunction is executed.
76 void cleanup();// will be called after every testfunction.
77 void conversions(); //test QgsProperty static conversion methods
78 void invalid(); //test invalid properties
79 void staticProperty(); //test for QgsStaticProperty
80 void fieldBasedProperty(); //test for QgsFieldBasedProperty
81 void expressionBasedProperty(); //test for QgsExpressionBasedProperty
82 void equality();
83 void propertyTransformer(); //test for QgsPropertyTransformer
84 void propertyTransformerFromExpression(); // text converting expression into QgsPropertyTransformer
85 void genericNumericTransformer();
86 void genericNumericTransformerFromExpression(); // text converting expression to QgsGenericNumericTransformer
87 void sizeScaleTransformer(); //test for QgsSizeScaleTransformer
88 void sizeScaleTransformerFromExpression(); // text converting expression to QgsSizeScaleTransformer
89 void colorRampTransformer(); //test for QgsColorRampTransformer
90 void propertyToTransformer(); //test converting expression based property to transformer/expression pair
91 void asExpression(); //test converting property to expression
92 void propertyCollection(); //test for QgsPropertyCollection
93 void collectionStack(); //test for QgsPropertyCollectionStack
94 void curveTransform();
95 void asVariant();
96 void isProjectColor();
97 void referencedFieldsIgnoreContext();
98
99 private:
100
101 QgsPropertiesDefinition mDefinitions;
102 void checkCurveResult( const QList< QgsPointXY > &controlPoints, const QVector<double> &x, const QVector<double> &y );
103
104 };
105
initTestCase()106 void TestQgsProperty::initTestCase()
107 {
108 QgsApplication::init();
109 QgsApplication::initQgis();
110 mDefinitions.insert( Property1, QgsPropertyDefinition( QStringLiteral( "p1" ), QgsPropertyDefinition::DataTypeString, QString(), QString() ) );
111 mDefinitions.insert( Property2, QgsPropertyDefinition( QStringLiteral( "p2" ), QgsPropertyDefinition::DataTypeString, QString(), QString() ) );
112 mDefinitions.insert( Property3, QgsPropertyDefinition( QStringLiteral( "p3" ), QgsPropertyDefinition::DataTypeString, QString(), QString() ) );
113 mDefinitions.insert( Property4, QgsPropertyDefinition( QStringLiteral( "p4" ), QgsPropertyDefinition::DataTypeString, QString(), QString() ) );
114 }
115
cleanupTestCase()116 void TestQgsProperty::cleanupTestCase()
117 {
118 QgsApplication::exitQgis();
119 }
120
init()121 void TestQgsProperty::init()
122 {
123
124 }
125
cleanup()126 void TestQgsProperty::cleanup()
127 {
128
129 }
130
conversions()131 void TestQgsProperty::conversions()
132 {
133 QgsExpressionContext context;
134
135 //all these tests are done for both a property and a collection
136 QgsPropertyCollection collection;
137
138 bool ok = false;
139
140 //test color conversions
141
142 //no color, should return defaultColor
143 QgsProperty c1 = QgsProperty::fromValue( QVariant(), true );
144 collection.setProperty( 0, c1 );
145 QCOMPARE( c1.valueAsColor( context, QColor( 200, 210, 220 ) ), QColor( 200, 210, 220 ) );
146 QCOMPARE( collection.valueAsColor( 0, context, QColor( 200, 210, 220 ) ), QColor( 200, 210, 220 ) );
147 c1.setStaticValue( QColor( 255, 200, 100, 50 ) ); //color in qvariant
148 collection.property( 0 ).setStaticValue( QColor( 255, 200, 100, 50 ) ); //color in qvariant
149 QCOMPARE( c1.valueAsColor( context, QColor( 200, 210, 220 ), &ok ), QColor( 255, 200, 100, 50 ) );
150 QVERIFY( ok );
151 QCOMPARE( collection.valueAsColor( 0, context, QColor( 200, 210, 220 ) ), QColor( 255, 200, 100, 50 ) );
152 c1.setStaticValue( QColor() ); //invalid color in qvariant, should return default color
153 collection.property( 0 ).setStaticValue( QColor() ); //invalid color in qvariant, should return default color
154 QCOMPARE( c1.valueAsColor( context, QColor( 200, 210, 220 ) ), QColor( 200, 210, 220 ) );
155 QCOMPARE( collection.valueAsColor( 0, context, QColor( 200, 210, 220 ) ), QColor( 200, 210, 220 ) );
156 c1.setStaticValue( QgsSymbolLayerUtils::encodeColor( QColor( 255, 200, 100, 50 ) ) ); //encoded color
157 collection.property( 0 ).setStaticValue( QgsSymbolLayerUtils::encodeColor( QColor( 255, 200, 100, 50 ) ) ); //encoded color
158 QCOMPARE( c1.valueAsColor( context, QColor( 200, 210, 220 ) ), QColor( 255, 200, 100, 50 ) );
159 QCOMPARE( collection.valueAsColor( 0, context, QColor( 200, 210, 220 ) ), QColor( 255, 200, 100, 50 ) );
160 c1.setStaticValue( "i am not a color" ); //badly encoded color, should return default color
161 collection.property( 0 ).setStaticValue( "i am not a color" ); //badly encoded color, should return default color
162 QCOMPARE( c1.valueAsColor( context, QColor( 200, 210, 220 ) ), QColor( 200, 210, 220 ) );
163 QCOMPARE( collection.valueAsColor( 0, context, QColor( 200, 210, 220 ) ), QColor( 200, 210, 220 ) );
164 collection.property( 0 ).setStaticValue( QVariant( QVariant::String ) ); //null value
165 QCOMPARE( c1.valueAsColor( context, QColor( 200, 210, 220 ), &ok ), QColor( 200, 210, 220 ) );
166 QVERIFY( !ok );
167 QCOMPARE( collection.valueAsColor( 0, context, QColor( 200, 210, 220 ), &ok ), QColor( 200, 210, 220 ) );
168 QVERIFY( !ok );
169
170 // test double conversions
171 QgsProperty d1 = QgsProperty::fromValue( QVariant(), true );
172 collection.setProperty( 1, d1 );
173 QCOMPARE( d1.valueAsDouble( context, -1.2, &ok ), -1.2 );
174 QVERIFY( !ok );
175 QCOMPARE( collection.valueAsDouble( 1, context, -1.2 ), -1.2 );
176 d1.setStaticValue( 12.3 ); //double in qvariant
177 collection.property( 1 ).setStaticValue( 12.3 ); //double in qvariant
178 QCOMPARE( d1.valueAsDouble( context, -1.2, &ok ), 12.3 );
179 QVERIFY( ok );
180 QCOMPARE( collection.valueAsDouble( 1, context, -1.2 ), 12.3 );
181 d1.setStaticValue( "15.6" ); //double as string
182 collection.property( 1 ).setStaticValue( "15.6" ); //double as string
183 QCOMPARE( d1.valueAsDouble( context, -1.2 ), 15.6 );
184 QCOMPARE( collection.valueAsDouble( 1, context, -1.2 ), 15.6 );
185 d1.setStaticValue( "i am not a double" ); //not a double, should return default value
186 collection.property( 1 ).setStaticValue( "i am not a double" ); //not a double, should return default value
187 QCOMPARE( d1.valueAsDouble( context, -1.2 ), -1.2 );
188 QCOMPARE( collection.valueAsDouble( 1, context, -1.2 ), -1.2 );
189 d1.setStaticValue( QVariant( QVariant::Double ) ); //null value
190 collection.property( 1 ).setStaticValue( QVariant( QVariant::Double ) ); //null value
191 QCOMPARE( d1.valueAsDouble( context, -1.2, &ok ), -1.2 );
192 QVERIFY( !ok );
193 QCOMPARE( collection.valueAsDouble( 1, context, -1.2, &ok ), -1.2 );
194 QVERIFY( !ok );
195
196 // test integer conversions
197 QgsProperty i1 = QgsProperty::fromValue( QVariant(), true );
198 collection.setProperty( 2, i1 );
199 QCOMPARE( i1.valueAsInt( context, -11, &ok ), -11 );
200 QVERIFY( !ok );
201 QCOMPARE( collection.valueAsInt( 2, context, -11 ), -11 );
202 i1.setStaticValue( 13 ); //integer in qvariant
203 collection.property( 2 ).setStaticValue( 13 ); //integer in qvariant
204 QCOMPARE( i1.valueAsInt( context, -11, &ok ), 13 );
205 QVERIFY( ok );
206 QCOMPARE( collection.valueAsInt( 2, context, -11, &ok ), 13 );
207 QVERIFY( ok );
208 i1.setStaticValue( 13.9 ); //double in qvariant, should be rounded
209 collection.property( 2 ).setStaticValue( 13.9 ); //double in qvariant, should be rounded
210 QCOMPARE( i1.valueAsInt( context, -11 ), 14 );
211 QCOMPARE( collection.valueAsInt( 2, context, -11 ), 14 );
212 i1.setStaticValue( "15" ); //integer as string
213 collection.property( 2 ).setStaticValue( "15" ); //integer as string
214 QCOMPARE( i1.valueAsInt( context, -11 ), 15 );
215 QCOMPARE( collection.valueAsInt( 2, context, -11 ), 15 );
216 i1.setStaticValue( "15.9" ); //double as string, should be rounded
217 collection.property( 2 ).setStaticValue( "15.9" ); //double as string, should be rounded
218 QCOMPARE( i1.valueAsInt( context, -11 ), 16 );
219 QCOMPARE( collection.valueAsInt( 2, context, -11 ), 16 );
220 i1.setStaticValue( "i am not a int" ); //not a int, should return default value
221 collection.property( 2 ).setStaticValue( "i am not a int" ); //not a int, should return default value
222 QCOMPARE( i1.valueAsInt( context, -11 ), -11 );
223 QCOMPARE( collection.valueAsInt( 2, context, -11 ), -11 );
224 i1.setStaticValue( QVariant( QVariant::Int ) ); // null value
225 collection.property( 2 ).setStaticValue( QVariant( QVariant::Int ) ); // null value
226 QCOMPARE( i1.valueAsInt( context, -11, &ok ), -11 );
227 QVERIFY( !ok );
228 QCOMPARE( collection.valueAsInt( 2, context, -11, &ok ), -11 );
229 QVERIFY( !ok );
230
231 // test boolean conversions
232 QgsProperty b1 = QgsProperty::fromValue( QVariant(), true );
233 collection.setProperty( 3, b1 );
234 QCOMPARE( b1.valueAsBool( context, false, &ok ), false );
235 QVERIFY( !ok );
236 QCOMPARE( b1.valueAsBool( context, true, &ok ), true );
237 QVERIFY( !ok );
238 QCOMPARE( collection.valueAsBool( 3, context, false ), false );
239 QCOMPARE( collection.valueAsBool( 3, context, true ), true );
240 b1.setStaticValue( true );
241 collection.property( 3 ).setStaticValue( true );
242 QCOMPARE( b1.valueAsBool( context, false, &ok ), true );
243 QVERIFY( ok );
244 QCOMPARE( b1.valueAsBool( context, true, &ok ), true );
245 QVERIFY( ok );
246 QCOMPARE( collection.valueAsBool( 3, context, false ), true );
247 QCOMPARE( collection.valueAsBool( 3, context, true ), true );
248 b1.setStaticValue( false );
249 collection.property( 3 ).setStaticValue( false );
250 QCOMPARE( b1.valueAsBool( context, false ), false );
251 QCOMPARE( b1.valueAsBool( context, true ), false );
252 QCOMPARE( collection.valueAsBool( 3, context, false ), false );
253 QCOMPARE( collection.valueAsBool( 3, context, true ), false );
254 b1.setStaticValue( 1 );
255 collection.property( 3 ).setStaticValue( 1 );
256 QCOMPARE( b1.valueAsBool( context, false ), true );
257 QCOMPARE( b1.valueAsBool( context, true ), true );
258 QCOMPARE( collection.valueAsBool( 3, context, false ), true );
259 QCOMPARE( collection.valueAsBool( 3, context, true ), true );
260 b1.setStaticValue( 0 );
261 collection.property( 3 ).setStaticValue( 0 );
262 QCOMPARE( b1.valueAsBool( context, false ), false );
263 QCOMPARE( b1.valueAsBool( context, true ), false );
264 QCOMPARE( collection.valueAsBool( 3, context, false ), false );
265 QCOMPARE( collection.valueAsBool( 3, context, true ), false );
266 b1.setStaticValue( "true" );
267 collection.property( 3 ).setStaticValue( "true" );
268 QCOMPARE( b1.valueAsBool( context, false ), true );
269 QCOMPARE( b1.valueAsBool( context, true ), true );
270 QCOMPARE( collection.valueAsBool( 3, context, false ), true );
271 QCOMPARE( collection.valueAsBool( 3, context, true ), true );
272 b1.setStaticValue( "" );
273 collection.property( 3 ).setStaticValue( "" );
274 QCOMPARE( b1.valueAsBool( context, false ), false );
275 QCOMPARE( b1.valueAsBool( context, true ), false );
276 QCOMPARE( collection.valueAsBool( 3, context, false ), false );
277 QCOMPARE( collection.valueAsBool( 3, context, true ), false );
278 b1.setStaticValue( QVariant( QVariant::Bool ) ); // null value
279 collection.property( 3 ).setStaticValue( QVariant( QVariant::Bool ) );
280 QCOMPARE( b1.valueAsBool( context, false ), false );
281 QCOMPARE( b1.valueAsBool( context, true ), true );
282 QCOMPARE( collection.valueAsBool( 3, context, false, &ok ), false );
283 QVERIFY( !ok );
284 QCOMPARE( collection.valueAsBool( 3, context, true, &ok ), true );
285 QVERIFY( !ok );
286
287 // test string conversions
288 QgsProperty s1 = QgsProperty::fromValue( QVariant(), true );
289 collection.setProperty( 4, s1 );
290 QCOMPARE( s1.valueAsString( context, "n", &ok ), QStringLiteral( "n" ) );
291 QVERIFY( !ok );
292 QCOMPARE( collection.valueAsString( 4, context, "y", &ok ), QStringLiteral( "y" ) );
293 QVERIFY( !ok );
294 s1.setStaticValue( "s" );
295 collection.property( 4 ).setStaticValue( "s" );
296 QCOMPARE( s1.valueAsString( context, "n", &ok ), QStringLiteral( "s" ) );
297 QVERIFY( ok );
298 QCOMPARE( collection.valueAsString( 4, context, "y", &ok ), QStringLiteral( "s" ) );
299 QVERIFY( ok );
300 s1.setStaticValue( QVariant( QVariant::String ) );
301 collection.property( 4 ).setStaticValue( QVariant( QVariant::String ) );
302 QCOMPARE( s1.valueAsString( context, "n", &ok ), QStringLiteral( "n" ) );
303 QVERIFY( !ok );
304 QCOMPARE( collection.valueAsString( 4, context, "y", &ok ), QStringLiteral( "y" ) );
305 QVERIFY( !ok );
306
307 // test datetime conversions
308 QDateTime dt = QDateTime( QDate( 2020, 1, 1 ), QTime( 0, 0, 0 ) );
309 QDateTime dt2 = QDateTime( QDate( 2010, 1, 1 ), QTime( 0, 0, 0 ) );
310 QgsProperty dt1 = QgsProperty::fromValue( QVariant(), true );
311 collection.setProperty( 5, dt1 );
312 QCOMPARE( d1.valueAsDateTime( context, dt, &ok ), dt );
313 QVERIFY( !ok );
314 QCOMPARE( collection.valueAsDateTime( 5, context, dt, &ok ), dt );
315 QVERIFY( !ok );
316 d1.setStaticValue( dt2 ); //datetime in qvariant
317 collection.property( 5 ).setStaticValue( dt2 ); //datetime in qvariant
318 QCOMPARE( d1.valueAsDateTime( context, dt, &ok ), dt2 );
319 QVERIFY( ok );
320 QCOMPARE( collection.valueAsDateTime( 5, context, dt, &ok ), dt2 );
321 QVERIFY( ok );
322 d1.setStaticValue( "2010-01-01" ); //datetime as string
323 collection.property( 5 ).setStaticValue( "2010-01-01" ); //datetime as string
324 QCOMPARE( d1.valueAsDateTime( context, dt ), dt2 );
325 QCOMPARE( collection.valueAsDateTime( 5, context, dt ), dt2 );
326 d1.setStaticValue( "i am not a datetime" ); //not a datetime, should return default value
327 collection.property( 5 ).setStaticValue( "i am not a datetime" ); //not a double, should return default value
328 QCOMPARE( d1.valueAsDateTime( context, dt ), dt );
329 QCOMPARE( collection.valueAsDateTime( 5, context, dt ), dt );
330 d1.setStaticValue( QVariant( QVariant::DateTime ) ); // null value
331 collection.property( 5 ).setStaticValue( QVariant( QVariant::DateTime ) ); // null value
332 QCOMPARE( d1.valueAsDateTime( context, dt, &ok ), dt );
333 QVERIFY( !ok );
334 QCOMPARE( collection.valueAsDateTime( 5, context, dt, &ok ), dt );
335 QVERIFY( !ok );
336 }
337
invalid()338 void TestQgsProperty::invalid()
339 {
340 QgsProperty p; //invalid property
341 QCOMPARE( p.propertyType(), QgsProperty::InvalidProperty );
342 QgsProperty p2( p );
343 QCOMPARE( p2.propertyType(), QgsProperty::InvalidProperty );
344 QgsProperty p3 = QgsProperty::fromValue( 5 );
345 p3 = p;
346 QCOMPARE( p3.propertyType(), QgsProperty::InvalidProperty );
347
348 }
349
staticProperty()350 void TestQgsProperty::staticProperty()
351 {
352 QgsExpressionContext context;
353 QgsProperty property = QgsProperty::fromValue( QStringLiteral( "test" ), true );
354 QCOMPARE( property.propertyType(), QgsProperty::StaticProperty );
355 QVERIFY( property.isActive() );
356 QVERIFY( property.referencedFields( context ).isEmpty() );
357 QCOMPARE( property.value( context, QStringLiteral( "default" ) ).toString(), QStringLiteral( "test" ) );
358 property.setActive( false );
359 QVERIFY( property.referencedFields( context ).isEmpty() );
360 QVERIFY( !property.isActive() );
361 QCOMPARE( property.value( context, QStringLiteral( "default" ) ).toString(), QStringLiteral( "default" ) );
362 property.setStaticValue( 5 );
363 property.setActive( true );
364 QCOMPARE( property.value( context ).toInt(), 5 );
365
366 //saving and restoring
367
368 //create a test dom element
369 QDomImplementation DomImplementation;
370 QDomDocumentType documentType =
371 DomImplementation.createDocumentType(
372 QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
373 QDomDocument doc( documentType );
374
375 QgsProperty p1;
376 p1.setActive( true );
377 p1.setStaticValue( "test" );
378 p1.setTransformer( new TestTransformer( 10, 20 ) );
379
380 QVariant element = p1.toVariant();
381
382 QgsProperty r1;
383 r1.loadVariant( element );
384 QVERIFY( r1.isActive() );
385 QVERIFY( r1.transformer() );
386 QCOMPARE( r1.staticValue(), QVariant( "test" ) );
387
388 p1.setActive( false );
389 element = p1.toVariant();
390 r1.loadVariant( element );
391 QVERIFY( !r1.isActive() );
392
393 //saving/restoring different types
394 p1.setStaticValue( QVariant( 5 ) ); //int
395 element = p1.toVariant();
396 r1.loadVariant( element );
397 QCOMPARE( r1.staticValue(), p1.staticValue() );
398 p1.setStaticValue( QVariant( 5.7 ) ); //double
399 element = p1.toVariant();
400 r1.loadVariant( element );
401 QCOMPARE( r1.staticValue(), p1.staticValue() );
402 p1.setStaticValue( QVariant( true ) ); //bool
403 element = p1.toVariant();
404 r1.loadVariant( element );
405 QCOMPARE( r1.staticValue(), p1.staticValue() );
406 p1.setStaticValue( QVariant( 5LL ) ); //longlong
407 element = p1.toVariant();
408 r1.loadVariant( element );
409 QCOMPARE( r1.staticValue(), p1.staticValue() );
410
411 // test copying a static property
412 QgsProperty p2;
413 p2.setActive( true );
414 p2.setStaticValue( "test" );
415 p2.setTransformer( new TestTransformer( 10, 20 ) );
416 // copy assign
417 QgsProperty p3;
418 p3 = p2;
419 QVERIFY( p3.isActive() );
420 QCOMPARE( p3.staticValue().toString(), QStringLiteral( "test" ) );
421 QVERIFY( p3.transformer() );
422 p2.setActive( false );
423 p2.setStaticValue( 5.9 );
424 p3 = p2;
425 QVERIFY( !p3.isActive() );
426 QCOMPARE( p3.staticValue().toDouble(), 5.9 );
427
428 // copy constructor
429 QgsProperty p4( p2 );
430 QVERIFY( !p4.isActive() );
431 QCOMPARE( p4.staticValue().toDouble(), 5.9 );
432 QVERIFY( p4.transformer() );
433 }
434
fieldBasedProperty()435 void TestQgsProperty::fieldBasedProperty()
436 {
437 //make a feature
438 QgsFeature ft;
439 QgsFields fields;
440 fields.append( QgsField( QStringLiteral( "field1" ), QVariant::Int ) );
441 fields.append( QgsField( QStringLiteral( "field2" ), QVariant::Int ) );
442 ft.setFields( fields );
443 QgsAttributes attr;
444 attr << QVariant( 5 ) << QVariant( 7 );
445 ft.setAttributes( attr );
446 ft.setValid( true );
447
448 // throw it in an expression context
449 QgsExpressionContext context;
450 context.setFeature( ft );
451 context.setFields( fields );
452
453 QgsProperty property = QgsProperty::fromField( QStringLiteral( "field1" ), true );
454 QCOMPARE( property.propertyType(), QgsProperty::FieldBasedProperty );
455 QVERIFY( property.isActive() );
456 QCOMPARE( property.value( context, -1 ).toInt(), 5 );
457 QCOMPARE( property.referencedFields( context ), QSet< QString >() << "field1" );
458 property.setActive( false );
459 QVERIFY( !property.isActive() );
460 QCOMPARE( property.value( context, -1 ).toInt(), -1 );
461 QVERIFY( property.referencedFields( context ).isEmpty() );
462 property.setField( QStringLiteral( "field2" ) );
463 property.setActive( true );
464 QCOMPARE( property.value( context, -1 ).toInt(), 7 );
465 QCOMPARE( property.referencedFields( context ), QSet< QString >() << "field2" );
466 //bad field reference
467 property.setField( QStringLiteral( "bad_field" ) );
468 QCOMPARE( property.value( context, -1 ).toInt(), -1 );
469 // unset field name
470 QgsProperty defaultProperty = QgsProperty::fromField( QString() );
471 QCOMPARE( defaultProperty.value( context, -1 ).toInt(), -1 );
472 QVERIFY( defaultProperty.referencedFields( context ).isEmpty() );
473 defaultProperty.setActive( true );
474 QCOMPARE( defaultProperty.value( context, -1 ).toInt(), -1 );
475 QVERIFY( defaultProperty.referencedFields( context ).isEmpty() );
476
477 //test preparation
478 QgsProperty property3 = QgsProperty::fromField( QStringLiteral( "field1" ), true );
479 QVERIFY( property3.prepare( context ) );
480 QCOMPARE( property3.value( context, -1 ).toInt(), 5 );
481
482 //saving and restoring
483
484 //create a test dom element
485 QDomImplementation DomImplementation;
486 QDomDocumentType documentType =
487 DomImplementation.createDocumentType(
488 QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
489 QDomDocument doc( documentType );
490
491 QgsProperty p1;
492 p1.setActive( true );
493 p1.setField( QStringLiteral( "test_field" ) );
494
495 QVariant element;
496 QgsProperty r1;
497 //try reading from an empty element
498 r1.loadVariant( element );
499 QVERIFY( !r1.isActive() );
500 QVERIFY( r1.field().isEmpty() );
501
502 // now populate element and re-read
503 element = p1.toVariant();
504 r1.loadVariant( element );
505 QVERIFY( r1.isActive() );
506 QCOMPARE( r1.field(), QStringLiteral( "test_field" ) );
507
508 p1.setActive( false );
509 element = p1.toVariant();
510 r1.loadVariant( element );
511 QVERIFY( !r1.isActive() );
512
513 // test copying a field based property
514 QgsProperty p2;
515 p2.setActive( true );
516 p2.setField( QStringLiteral( "test" ) );
517 p2.setTransformer( new TestTransformer( 10, 20 ) );
518
519 // copy constructor
520 QgsProperty p3( p2 );
521 QVERIFY( p3.isActive() );
522 QCOMPARE( p3.field(), QStringLiteral( "test" ) );
523 QVERIFY( p3.transformer() );
524 p2.setActive( false );
525
526 // assignment operator
527 QgsProperty p4;
528 p4 = p2;
529 QVERIFY( !p4.isActive() );
530 QCOMPARE( p4.field(), QStringLiteral( "test" ) );
531 QVERIFY( p4.transformer() );
532 }
533
expressionBasedProperty()534 void TestQgsProperty::expressionBasedProperty()
535 {
536 //make a feature
537 QgsFeature ft;
538 QgsFields fields;
539 fields.append( QgsField( QStringLiteral( "field1" ), QVariant::Int ) );
540 fields.append( QgsField( QStringLiteral( "field2" ), QVariant::Int ) );
541 ft.setFields( fields );
542 QgsAttributes attr;
543 attr << QVariant( 5 ) << QVariant( 7 );
544 ft.setAttributes( attr );
545 ft.setValid( true );
546
547 // throw it in an expression context
548 QgsExpressionContext context;
549 context.setFeature( ft );
550 context.setFields( fields );
551
552 QgsProperty property = QgsProperty::fromExpression( QStringLiteral( "\"field1\" + \"field2\"" ), true );
553 QCOMPARE( property.propertyType(), QgsProperty::ExpressionBasedProperty );
554 QVERIFY( property.isActive() );
555 QCOMPARE( property.value( context, -1 ).toInt(), 12 );
556 QCOMPARE( property.referencedFields( context ).count(), 2 );
557 QVERIFY( property.referencedFields( context ).contains( "field1" ) );
558 QVERIFY( property.referencedFields( context ).contains( "field2" ) );
559 property.setExpressionString( QStringLiteral( "\"field2\"*2" ) );
560 QCOMPARE( property.value( context, -1 ).toInt(), 14 );
561 QCOMPARE( property.referencedFields( context ), QSet< QString >() << "field2" );
562 property.setActive( false );
563 QVERIFY( !property.isActive() );
564 QCOMPARE( property.value( context, -1 ).toInt(), -1 );
565 QVERIFY( property.referencedFields( context ).isEmpty() );
566 property.setExpressionString( QStringLiteral( "'a'||'b'" ) );
567 property.setActive( true );
568 QVERIFY( property.referencedFields( context ).isEmpty() );
569 QCOMPARE( property.value( context, "bb" ).toString(), QStringLiteral( "ab" ) );
570 //bad expression
571 property.setExpressionString( QStringLiteral( "bad_ 5" ) );
572 QCOMPARE( property.value( context, -1 ).toInt(), -1 );
573 QVERIFY( property.referencedFields( context ).isEmpty() );
574 // unset expression
575 QgsProperty defaultProperty = QgsProperty::fromExpression( QString() );
576 QCOMPARE( defaultProperty.value( context, -1 ).toInt(), -1 );
577 QVERIFY( defaultProperty.referencedFields( context ).isEmpty() );
578 defaultProperty.setActive( true );
579 QCOMPARE( defaultProperty.value( context, -1 ).toInt(), -1 );
580 QVERIFY( defaultProperty.referencedFields( context ).isEmpty() );
581
582 //preparation
583 QgsProperty property3 = QgsProperty::fromExpression( QStringLiteral( "\"field1\" + \"field2\"" ), true );
584 QVERIFY( property3.prepare( context ) );
585 QCOMPARE( property3.value( context, -1 ).toInt(), 12 );
586 QgsProperty property4 = QgsProperty::fromExpression( QStringLiteral( "\"field1\" + " ), true );
587 QVERIFY( !property4.prepare( context ) );
588
589 //saving and restoring
590
591 //create a test dom element
592 QDomImplementation DomImplementation;
593 QDomDocumentType documentType =
594 DomImplementation.createDocumentType(
595 QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
596 QDomDocument doc( documentType );
597
598 QgsProperty p1;
599 p1.setActive( true );
600 p1.setExpressionString( QStringLiteral( "4+5" ) );
601
602 QVariant element;
603 QgsProperty r1;
604 //try reading from an empty element
605 r1.loadVariant( element );
606 QVERIFY( !r1.isActive() );
607 QVERIFY( r1.expressionString().isEmpty() );
608 QCOMPARE( r1.value( context, -1 ).toInt(), -1 );
609
610 // now populate element and re-read
611 element = p1.toVariant();
612 r1.loadVariant( element );
613 QVERIFY( r1.isActive() );
614 QCOMPARE( r1.expressionString(), QStringLiteral( "4+5" ) );
615 QCOMPARE( r1.value( context, -1 ).toInt(), 9 );
616
617 p1.setActive( false );
618 element = p1.toVariant();
619 r1.loadVariant( element );
620 QVERIFY( !r1.isActive() );
621 QCOMPARE( r1.value( context, -1 ).toInt(), -1 );
622
623 // test copying an expression based property
624 QgsProperty p2;
625 p2.setActive( true );
626 p2.setExpressionString( QStringLiteral( "1+6" ) );
627
628 // copy constructor
629 QgsProperty p3( p2 );
630 QVERIFY( p3.isActive() );
631 QCOMPARE( p3.expressionString(), QStringLiteral( "1+6" ) );
632 QCOMPARE( p3.value( context, -1 ).toInt(), 7 );
633
634 // assignment operator
635 QgsProperty p4;
636 p2.setActive( false );
637 p4 = p2;
638 QVERIFY( !p4.isActive() );
639 QCOMPARE( p4.value( context, -1 ).toInt(), -1 );
640 p2.setTransformer( new TestTransformer( 10, 20 ) );
641 p4 = p2;
642 QVERIFY( p4.transformer() );
643 }
644
equality()645 void TestQgsProperty::equality()
646 {
647 QgsProperty dd1;
648 dd1.setActive( true );
649 dd1.setField( QStringLiteral( "field" ) );
650 QgsProperty dd2;
651 dd2.setActive( true );
652 dd2.setField( QStringLiteral( "field" ) );
653 QVERIFY( dd1 == dd2 );
654 QVERIFY( !( dd1 != dd2 ) );
655
656 dd1.setExpressionString( QStringLiteral( "expression" ) );
657 dd2.setExpressionString( QStringLiteral( "expression" ) );
658 QVERIFY( dd1 == dd2 );
659 QVERIFY( !( dd1 != dd2 ) );
660
661 //test that all applicable components contribute to equality
662 dd2.setActive( false );
663 QVERIFY( !( dd1 == dd2 ) );
664 QVERIFY( dd1 != dd2 );
665 dd2.setActive( true );
666 dd2.setExpressionString( QStringLiteral( "a" ) );
667 QVERIFY( !( dd1 == dd2 ) );
668 QVERIFY( dd1 != dd2 );
669 dd2.setField( QStringLiteral( "field" ) );
670 QVERIFY( !( dd1 == dd2 ) );
671 QVERIFY( dd1 != dd2 );
672 dd1.setField( QStringLiteral( "fieldb" ) );
673 QVERIFY( !( dd1 == dd2 ) );
674 QVERIFY( dd1 != dd2 );
675 dd1.setField( QStringLiteral( "field" ) );
676 QVERIFY( dd1 == dd2 );
677 QVERIFY( !( dd1 != dd2 ) );
678
679 // with transformer
680 dd1.setTransformer( new QgsGenericNumericTransformer( 1, 2, 3, 4 ) );
681 QVERIFY( !( dd1 == dd2 ) );
682 QVERIFY( dd1 != dd2 );
683 dd2.setTransformer( new QgsGenericNumericTransformer( 1, 2, 3, 4 ) );
684 QVERIFY( dd1 == dd2 );
685 QVERIFY( !( dd1 != dd2 ) );
686 }
687
propertyTransformer()688 void TestQgsProperty::propertyTransformer()
689 {
690 QgsExpressionContext context;
691 TestTransformer transform( -5, 5 );
692 QCOMPARE( transform.minValue(), -5.0 );
693 transform.setMinValue( -1 );
694 QCOMPARE( transform.minValue(), -1.0 );
695 QCOMPARE( transform.maxValue(), 5.0 );
696 transform.setMaxValue( 10.0 );
697 QCOMPARE( transform.maxValue(), 10.0 );
698
699 //saving and restoring
700
701 //create a test dom element
702 QDomImplementation DomImplementation;
703 QDomDocumentType documentType =
704 DomImplementation.createDocumentType(
705 QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
706 QDomDocument doc( documentType );
707
708 TestTransformer t1( -5, 6 );
709 QVariant element;
710 TestTransformer r1( -99, -98 );
711 element = t1.toVariant();
712 QVERIFY( r1.loadVariant( element ) );
713 QCOMPARE( r1.minValue(), -5.0 );
714 QCOMPARE( r1.maxValue(), 6.0 );
715
716 //install into property and test evaluation
717 QgsProperty p1;
718 p1.setTransformer( new TestTransformer( 10, 20 ) );
719 QVERIFY( dynamic_cast< const TestTransformer * >( p1.transformer() ) );
720 QCOMPARE( static_cast< const TestTransformer * >( p1.transformer() )->minValue(), 10.0 );
721 QCOMPARE( static_cast< const TestTransformer * >( p1.transformer() )->maxValue(), 20.0 );
722 p1.setStaticValue( QVariant( QVariant::Double ) );
723 QCOMPARE( p1.value( context, -99 ).toDouble(), -1.0 );
724 p1.setStaticValue( 11.0 );
725 QCOMPARE( p1.value( context, -99 ).toDouble(), 22.0 );
726
727 //test that transform is saved/restored with property
728 QVariant propElement;
729 QgsProperty p2;
730 QVERIFY( !p2.transformer() );
731 propElement = p1.toVariant();
732 p2.loadVariant( propElement );
733 QVERIFY( p2.transformer() );
734 QCOMPARE( p2.transformer()->minValue(), 10.0 );
735 QCOMPARE( p2.transformer()->maxValue(), 20.0 );
736
737 //test that copy constructor copies transformer
738 QgsProperty p4( p1 );
739 QVERIFY( p4.transformer() );
740 QCOMPARE( p4.transformer()->minValue(), 10.0 );
741 QCOMPARE( p4.transformer()->maxValue(), 20.0 );
742
743 //test that assignment operator copies transformer
744 QgsProperty p5;
745 p5 = p1;
746 QVERIFY( p5.transformer() );
747 QCOMPARE( p5.transformer()->minValue(), 10.0 );
748 QCOMPARE( p5.transformer()->maxValue(), 20.0 );
749 }
750
propertyTransformerFromExpression()751 void TestQgsProperty::propertyTransformerFromExpression()
752 {
753 QString baseExpression;
754 QString fieldName;
755 // not convertible to a transformer
756 std::unique_ptr< QgsPropertyTransformer > exp( QgsPropertyTransformer::fromExpression( QStringLiteral( "1 * 2" ), baseExpression, fieldName ) );
757 QVERIFY( !exp.get() );
758 QVERIFY( baseExpression.isEmpty() );
759 QVERIFY( fieldName.isEmpty() );
760
761 // convertible to a size scale transformer
762 exp.reset( QgsPropertyTransformer::fromExpression( QStringLiteral( "coalesce(scale_linear(column, 1, 7, 2, 10), 0)" ), baseExpression, fieldName ) );
763 QVERIFY( exp.get() );
764 QCOMPARE( exp->transformerType(), QgsPropertyTransformer::SizeScaleTransformer );
765 QCOMPARE( fieldName, QStringLiteral( "column" ) );
766 QVERIFY( baseExpression.isEmpty() );
767
768 exp.reset( QgsPropertyTransformer::fromExpression( QStringLiteral( "coalesce(scale_linear(column * 2, 1, 7, 2, 10), 0)" ), baseExpression, fieldName ) );
769 QVERIFY( exp.get() );
770 QCOMPARE( exp->transformerType(), QgsPropertyTransformer::SizeScaleTransformer );
771 QCOMPARE( baseExpression, QStringLiteral( "column * 2" ) );
772 QVERIFY( fieldName.isEmpty() );
773 }
774
genericNumericTransformer()775 void TestQgsProperty::genericNumericTransformer()
776 {
777 QgsExpressionContext context;
778 QgsGenericNumericTransformer t1( 10,
779 20,
780 100,
781 200,
782 -10,
783 1.0 );
784 QCOMPARE( t1.transformerType(), QgsPropertyTransformer::GenericNumericTransformer );
785 QCOMPARE( t1.minValue(), 10.0 );
786 QCOMPARE( t1.maxValue(), 20.0 );
787 QCOMPARE( t1.minOutputValue(), 100.0 );
788 QCOMPARE( t1.maxOutputValue(), 200.0 );
789 QCOMPARE( t1.nullOutputValue(), -10.0 );
790 QCOMPARE( t1.exponent(), 1.0 );
791
792 //transform
793 QCOMPARE( t1.transform( context, 10 ).toInt(), 100 );
794 QCOMPARE( t1.transform( context, 20 ).toInt(), 200 );
795 //null value
796 QCOMPARE( t1.transform( context, QVariant( QVariant::Double ) ).toInt(), -10 );
797 //non numeric value
798 QCOMPARE( t1.transform( context, QVariant( "ffff" ) ), QVariant( "ffff" ) );
799
800 // add a curve
801 QVERIFY( !t1.curveTransform() );
802 t1.setCurveTransform( new QgsCurveTransform( QList< QgsPointXY >() << QgsPointXY( 0, 0.8 ) << QgsPointXY( 1, 0.2 ) ) );
803 QVERIFY( t1.curveTransform() );
804 QCOMPARE( t1.curveTransform()->controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0, 0.8 ) << QgsPointXY( 1, 0.2 ) );
805
806 QCOMPARE( t1.transform( context, 10 ).toInt(), 180 );
807 QCOMPARE( t1.transform( context, 20 ).toInt(), 120 );
808
809 // copy
810 QgsGenericNumericTransformer s1( t1 );
811 QVERIFY( s1.curveTransform() );
812 QCOMPARE( s1.curveTransform()->controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0, 0.8 ) << QgsPointXY( 1, 0.2 ) );
813
814 // assignment
815 QgsGenericNumericTransformer s2;
816 s2 = t1;
817 QVERIFY( s2.curveTransform() );
818 QCOMPARE( s2.curveTransform()->controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0, 0.8 ) << QgsPointXY( 1, 0.2 ) );
819
820 //saving and restoring
821
822 //create a test dom element
823 QDomImplementation DomImplementation;
824 QDomDocumentType documentType =
825 DomImplementation.createDocumentType(
826 QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
827 QDomDocument doc( documentType );
828
829 QgsGenericNumericTransformer t2( 15,
830 25,
831 150,
832 250,
833 -10,
834 99 );
835 t2.setCurveTransform( new QgsCurveTransform( QList< QgsPointXY >() << QgsPointXY( 0, 0.8 ) << QgsPointXY( 1, 0.2 ) ) );
836
837 QVariant element;
838 element = t2.toVariant();
839 QgsGenericNumericTransformer r1;
840 QVERIFY( r1.loadVariant( element ) );
841 QCOMPARE( r1.minValue(), 15.0 );
842 QCOMPARE( r1.maxValue(), 25.0 );
843 QCOMPARE( r1.minOutputValue(), 150.0 );
844 QCOMPARE( r1.maxOutputValue(), 250.0 );
845 QCOMPARE( r1.nullOutputValue(), -10.0 );
846 QCOMPARE( r1.exponent(), 99.0 );
847 QVERIFY( r1.curveTransform() );
848 QCOMPARE( r1.curveTransform()->controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0, 0.8 ) << QgsPointXY( 1, 0.2 ) );
849
850 // test cloning
851 std::unique_ptr< QgsGenericNumericTransformer > r2( t2.clone() );
852 QCOMPARE( r2->minValue(), 15.0 );
853 QCOMPARE( r2->maxValue(), 25.0 );
854 QCOMPARE( r2->minOutputValue(), 150.0 );
855 QCOMPARE( r2->maxOutputValue(), 250.0 );
856 QCOMPARE( r2->nullOutputValue(), -10.0 );
857 QCOMPARE( r2->exponent(), 99.0 );
858 QVERIFY( r2->curveTransform() );
859 QCOMPARE( r2->curveTransform()->controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0, 0.8 ) << QgsPointXY( 1, 0.2 ) );
860
861 //test various min/max value/size and scaling methods
862
863 //getters and setters
864 QgsGenericNumericTransformer t;
865 t.setMinValue( 100 );
866 QCOMPARE( t.minValue(), 100.0 );
867 t.setMaxValue( 200 );
868 QCOMPARE( t.maxValue(), 200.0 );
869 t.setMinOutputValue( 10.0 );
870 QCOMPARE( t.minOutputValue(), 10.0 );
871 t.setMaxOutputValue( 20.0 );
872 QCOMPARE( t.maxOutputValue(), 20.0 );
873 t.setNullOutputValue( 1 );
874 QCOMPARE( t.nullOutputValue(), 1.0 );
875 t.setExponent( 2.5 );
876 QCOMPARE( t.exponent(), 2.5 );
877
878 //test linear scaling
879 t.setExponent( 1.0 );
880 QCOMPARE( t.value( 100 ), 10.0 );
881 QCOMPARE( t.value( 150 ), 15.0 );
882 QCOMPARE( t.value( 200 ), 20.0 );
883 //test exponential scaling
884 t.setExponent( 1.5 );
885 QCOMPARE( t.value( 100 ), 10.0 );
886 QGSCOMPARENEAR( t.value( 150 ), 13.5355, 0.001 );
887 QCOMPARE( t.value( 200 ), 20.0 );
888
889 // invalid settings, where minValue = maxValue
890 QgsGenericNumericTransformer invalid( 1.0, 1.0, 0, 1.0 );
891 QCOMPARE( invalid.value( -1 ), 0.0 );
892 QCOMPARE( invalid.value( 0 ), 0.0 );
893 QCOMPARE( invalid.value( 1.0 ), 1.0 );
894 QCOMPARE( invalid.value( 2.0 ), 1.0 );
895
896 //as expression
897 QgsGenericNumericTransformer t3( 15,
898 25,
899 150,
900 250,
901 -10,
902 1.0 );
903 QCOMPARE( t3.toExpression( "5+6" ), QStringLiteral( "coalesce(scale_linear(5+6, 15, 25, 150, 250), -10)" ) );
904 t3.setExponent( 1.6 );
905 QCOMPARE( t3.toExpression( "5+6" ), QStringLiteral( "coalesce(scale_exp(5+6, 15, 25, 150, 250, 1.6), -10)" ) );
906
907 // test size scale transformer inside property
908 QgsProperty p;
909 p.setTransformer( new QgsGenericNumericTransformer( 15,
910 25,
911 150,
912 250,
913 -10,
914 99 ) );
915 p.setStaticValue( QVariant() );
916 bool ok = false;
917 QCOMPARE( p.valueAsDouble( context, 100, &ok ), -10.0 );
918 QVERIFY( ok );
919 p.setExpressionString( QStringLiteral( "NULL" ) );
920 QCOMPARE( p.valueAsDouble( context, 100, &ok ), -10.0 );
921 QVERIFY( ok );
922 p.setExpressionString( QStringLiteral( "no field" ) );
923 QCOMPARE( p.valueAsDouble( context, 100, &ok ), -10.0 );
924 QVERIFY( ok );
925 }
926
genericNumericTransformerFromExpression()927 void TestQgsProperty::genericNumericTransformerFromExpression()
928 {
929 QString baseExpression;
930 QString fieldName;
931 std::unique_ptr< QgsGenericNumericTransformer > exp( QgsGenericNumericTransformer::fromExpression( QStringLiteral( "coalesce(scale_linear(column, 1, 7, 2, 10), 0)" ), baseExpression, fieldName ) );
932 QVERIFY( exp.get() );
933 QCOMPARE( fieldName, QStringLiteral( "column" ) );
934 QVERIFY( baseExpression.isEmpty() );
935 QCOMPARE( exp->minValue(), 1. );
936 QCOMPARE( exp->maxValue(), 7. );
937 QCOMPARE( exp->minOutputValue(), 2. );
938 QCOMPARE( exp->maxOutputValue(), 10. );
939 QCOMPARE( exp->nullOutputValue(), 0.0 );
940
941 exp.reset( QgsGenericNumericTransformer::fromExpression( QStringLiteral( "scale_linear(column, 1, 7, 2, 10)" ), baseExpression, fieldName ) );
942 QVERIFY( exp.get() );
943 QCOMPARE( fieldName, QStringLiteral( "column" ) );
944 QVERIFY( baseExpression.isEmpty() );
945 QCOMPARE( exp->minValue(), 1. );
946 QCOMPARE( exp->maxValue(), 7. );
947 QCOMPARE( exp->minOutputValue(), 2. );
948 QCOMPARE( exp->maxOutputValue(), 10. );
949
950 exp.reset( QgsGenericNumericTransformer::fromExpression( QStringLiteral( "scale_linear(column * 2, 1, 7, 2, 10)" ), baseExpression, fieldName ) );
951 QVERIFY( exp.get() );
952 QCOMPARE( baseExpression, QStringLiteral( "column * 2" ) );
953 QVERIFY( fieldName.isEmpty() );
954 QCOMPARE( exp->minValue(), 1. );
955 QCOMPARE( exp->maxValue(), 7. );
956 QCOMPARE( exp->minOutputValue(), 2. );
957 QCOMPARE( exp->maxOutputValue(), 10. );
958
959 exp.reset( QgsGenericNumericTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, 2, 10, 0.51), 1)" ), baseExpression, fieldName ) );
960 QVERIFY( exp.get() );
961 QCOMPARE( exp->minValue(), 1. );
962 QCOMPARE( exp->maxValue(), 7. );
963 QCOMPARE( exp->minOutputValue(), 2. );
964 QCOMPARE( exp->maxOutputValue(), 10. );
965 QCOMPARE( exp->exponent(), 0.51 );
966 QCOMPARE( exp->nullOutputValue(), 1.0 );
967
968 QVERIFY( !QgsGenericNumericTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, a, 10, 0.5), 0)" ), baseExpression, fieldName ) );
969 QVERIFY( !QgsGenericNumericTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7), 0)" ), baseExpression, fieldName ) );
970 QVERIFY( !QgsGenericNumericTransformer::fromExpression( QStringLiteral( "1+2" ), baseExpression, fieldName ) );
971 QVERIFY( !QgsGenericNumericTransformer::fromExpression( QString(), baseExpression, fieldName ) );
972 }
973
sizeScaleTransformer()974 void TestQgsProperty::sizeScaleTransformer()
975 {
976 QgsExpressionContext context;
977 QgsSizeScaleTransformer scale( QgsSizeScaleTransformer::Linear,
978 10,
979 20,
980 100,
981 200,
982 -10,
983 1.0 );
984 QCOMPARE( scale.transformerType(), QgsPropertyTransformer::SizeScaleTransformer );
985 QCOMPARE( scale.minValue(), 10.0 );
986 QCOMPARE( scale.maxValue(), 20.0 );
987 QCOMPARE( scale.minSize(), 100.0 );
988 QCOMPARE( scale.maxSize(), 200.0 );
989 QCOMPARE( scale.nullSize(), -10.0 );
990 QCOMPARE( scale.exponent(), 1.0 );
991 QCOMPARE( scale.type(), QgsSizeScaleTransformer::Linear );
992
993 //transform
994 QCOMPARE( scale.transform( context, 10 ).toInt(), 100 );
995 QCOMPARE( scale.transform( context, 20 ).toInt(), 200 );
996 //null value
997 QCOMPARE( scale.transform( context, QVariant( QVariant::Double ) ).toInt(), -10 );
998 //non numeric value
999 QCOMPARE( scale.transform( context, QVariant( "ffff" ) ), QVariant( "ffff" ) );
1000
1001 // add a curve
1002 QVERIFY( !scale.curveTransform() );
1003 scale.setCurveTransform( new QgsCurveTransform( QList< QgsPointXY >() << QgsPointXY( 0, 0.2 ) << QgsPointXY( 1, 0.8 ) ) );
1004 QVERIFY( scale.curveTransform() );
1005 QCOMPARE( scale.curveTransform()->controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0, 0.2 ) << QgsPointXY( 1, 0.8 ) );
1006 QCOMPARE( scale.transform( context, 10 ).toInt(), 120 );
1007 QCOMPARE( scale.transform( context, 20 ).toInt(), 180 );
1008
1009 // copy
1010 QgsSizeScaleTransformer s1( scale );
1011 QVERIFY( s1.curveTransform() );
1012 QCOMPARE( s1.curveTransform()->controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0, 0.2 ) << QgsPointXY( 1, 0.8 ) );
1013
1014 // assignment
1015 QgsSizeScaleTransformer s2;
1016 s2 = scale;
1017 QVERIFY( s2.curveTransform() );
1018 QCOMPARE( s2.curveTransform()->controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0, 0.2 ) << QgsPointXY( 1, 0.8 ) );
1019
1020 //saving and restoring
1021
1022 //create a test dom element
1023 QDomImplementation DomImplementation;
1024 QDomDocumentType documentType =
1025 DomImplementation.createDocumentType(
1026 QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
1027 QDomDocument doc( documentType );
1028
1029 QgsSizeScaleTransformer t1( QgsSizeScaleTransformer::Exponential,
1030 15,
1031 25,
1032 150,
1033 250,
1034 -10,
1035 99 );
1036 t1.setCurveTransform( new QgsCurveTransform( QList< QgsPointXY >() << QgsPointXY( 0, 0.8 ) << QgsPointXY( 1, 0.2 ) ) );
1037
1038 QVariant element;
1039 element = t1.toVariant();
1040 QgsSizeScaleTransformer r1;
1041 QVERIFY( r1.loadVariant( element ) );
1042 QCOMPARE( r1.minValue(), 15.0 );
1043 QCOMPARE( r1.maxValue(), 25.0 );
1044 QCOMPARE( r1.minSize(), 150.0 );
1045 QCOMPARE( r1.maxSize(), 250.0 );
1046 QCOMPARE( r1.nullSize(), -10.0 );
1047 QCOMPARE( r1.exponent(), 99.0 );
1048 QCOMPARE( r1.type(), QgsSizeScaleTransformer::Exponential );
1049 QVERIFY( r1.curveTransform() );
1050 QCOMPARE( r1.curveTransform()->controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0, 0.8 ) << QgsPointXY( 1, 0.2 ) );
1051
1052 // test cloning
1053 std::unique_ptr< QgsSizeScaleTransformer > r2( t1.clone() );
1054 QCOMPARE( r2->minValue(), 15.0 );
1055 QCOMPARE( r2->maxValue(), 25.0 );
1056 QCOMPARE( r2->minSize(), 150.0 );
1057 QCOMPARE( r2->maxSize(), 250.0 );
1058 QCOMPARE( r2->nullSize(), -10.0 );
1059 QCOMPARE( r2->exponent(), 99.0 );
1060 QCOMPARE( r2->type(), QgsSizeScaleTransformer::Exponential );
1061 QVERIFY( r2->curveTransform() );
1062 QCOMPARE( r2->curveTransform()->controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0, 0.8 ) << QgsPointXY( 1, 0.2 ) );
1063
1064 //test various min/max value/size and scaling methods
1065
1066 //getters and setters
1067 QgsSizeScaleTransformer t;
1068 t.setMinValue( 100 );
1069 QCOMPARE( t.minValue(), 100.0 );
1070 t.setMaxValue( 200 );
1071 QCOMPARE( t.maxValue(), 200.0 );
1072 t.setMinSize( 10.0 );
1073 QCOMPARE( t.minSize(), 10.0 );
1074 t.setMaxSize( 20.0 );
1075 QCOMPARE( t.maxSize(), 20.0 );
1076 t.setNullSize( 1 );
1077 QCOMPARE( t.nullSize(), 1.0 );
1078 t.setType( QgsSizeScaleTransformer::Area );
1079 QCOMPARE( t.type(), QgsSizeScaleTransformer::Area );
1080 t.setExponent( 2.5 );
1081 QCOMPARE( t.exponent(), 2.5 );
1082
1083 //test that setting type updates exponent
1084 t.setType( QgsSizeScaleTransformer::Linear );
1085 QCOMPARE( t.exponent(), 1.0 );
1086 t.setType( QgsSizeScaleTransformer::Area );
1087 QCOMPARE( t.exponent(), 0.5 );
1088 t.setType( QgsSizeScaleTransformer::Flannery );
1089 QCOMPARE( t.exponent(), 0.57 );
1090
1091 //test linear scaling
1092 t.setType( QgsSizeScaleTransformer::Linear );
1093 QCOMPARE( t.size( 100 ), 10.0 );
1094 QCOMPARE( t.size( 150 ), 15.0 );
1095 QCOMPARE( t.size( 200 ), 20.0 );
1096 //test area scaling
1097 t.setType( QgsSizeScaleTransformer::Area );
1098 QCOMPARE( t.size( 100 ), 10.0 );
1099 QGSCOMPARENEAR( t.size( 150 ), 17.0711, 0.001 );
1100 QCOMPARE( t.size( 200 ), 20.0 );
1101 //test flannery scaling
1102 t.setType( QgsSizeScaleTransformer::Flannery );
1103 QCOMPARE( t.size( 100 ), 10.0 );
1104 QGSCOMPARENEAR( t.size( 150 ), 16.7362, 0.001 );
1105 QCOMPARE( t.size( 200 ), 20.0 );
1106 //test exponential scaling
1107 t.setType( QgsSizeScaleTransformer::Exponential );
1108 t.setExponent( 1.5 );
1109 QCOMPARE( t.size( 100 ), 10.0 );
1110 QGSCOMPARENEAR( t.size( 150 ), 13.5355, 0.001 );
1111 QCOMPARE( t.size( 200 ), 20.0 );
1112
1113 //as expression
1114 QgsSizeScaleTransformer t2( QgsSizeScaleTransformer::Linear,
1115 15,
1116 25,
1117 150,
1118 250,
1119 -10,
1120 1.6 );
1121 QCOMPARE( t2.toExpression( "5+6" ), QStringLiteral( "coalesce(scale_linear(5+6, 15, 25, 150, 250), -10)" ) );
1122 t2.setType( QgsSizeScaleTransformer::Exponential );
1123 t2.setExponent( 1.6 );
1124 QCOMPARE( t2.toExpression( "5+6" ), QStringLiteral( "coalesce(scale_exp(5+6, 15, 25, 150, 250, 1.6), -10)" ) );
1125
1126 // test size scale transformer inside property
1127 QgsProperty p;
1128 p.setTransformer( new QgsSizeScaleTransformer( QgsSizeScaleTransformer::Exponential,
1129 15,
1130 25,
1131 150,
1132 250,
1133 -10,
1134 99 ) );
1135 p.setStaticValue( QVariant() );
1136 bool ok = false;
1137 QCOMPARE( p.valueAsDouble( context, 100, &ok ), -10.0 );
1138 QVERIFY( ok );
1139 p.setExpressionString( QStringLiteral( "NULL" ) );
1140 QCOMPARE( p.valueAsDouble( context, 100, &ok ), -10.0 );
1141 QVERIFY( ok );
1142 p.setExpressionString( QStringLiteral( "no field" ) );
1143 QCOMPARE( p.valueAsDouble( context, 100, &ok ), -10.0 );
1144 QVERIFY( ok );
1145 }
1146
sizeScaleTransformerFromExpression()1147 void TestQgsProperty::sizeScaleTransformerFromExpression()
1148 {
1149 QString baseExpression;
1150 QString fieldName;
1151 std::unique_ptr< QgsSizeScaleTransformer > exp( QgsSizeScaleTransformer::fromExpression( QStringLiteral( "coalesce(scale_linear(column, 1, 7, 2, 10), 0)" ), baseExpression, fieldName ) );
1152 QVERIFY( exp.get() );
1153 QCOMPARE( exp->type(), QgsSizeScaleTransformer::Linear );
1154 QCOMPARE( fieldName, QStringLiteral( "column" ) );
1155 QVERIFY( baseExpression.isEmpty() );
1156 QCOMPARE( exp->minValue(), 1. );
1157 QCOMPARE( exp->maxValue(), 7. );
1158 QCOMPARE( exp->minSize(), 2. );
1159 QCOMPARE( exp->maxSize(), 10. );
1160 QCOMPARE( exp->nullSize(), 0.0 );
1161
1162 exp.reset( QgsSizeScaleTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, 2, 10, 0.5), 0)" ), baseExpression, fieldName ) );
1163 QVERIFY( exp.get() );
1164 QCOMPARE( exp->type(), QgsSizeScaleTransformer::Area );
1165
1166 exp.reset( QgsSizeScaleTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, 2, 10, 0.57), 0)" ), baseExpression, fieldName ) );
1167 QVERIFY( exp.get() );
1168 QCOMPARE( exp->type(), QgsSizeScaleTransformer::Flannery );
1169
1170 exp.reset( QgsSizeScaleTransformer::fromExpression( QStringLiteral( "scale_linear(column, 1, 7, 2, 10)" ), baseExpression, fieldName ) );
1171 QVERIFY( exp.get() );
1172 QCOMPARE( exp->type(), QgsSizeScaleTransformer::Linear );
1173 QCOMPARE( fieldName, QStringLiteral( "column" ) );
1174 QVERIFY( baseExpression.isEmpty() );
1175 QCOMPARE( exp->minValue(), 1. );
1176 QCOMPARE( exp->maxValue(), 7. );
1177 QCOMPARE( exp->minSize(), 2. );
1178 QCOMPARE( exp->maxSize(), 10. );
1179
1180 exp.reset( QgsSizeScaleTransformer::fromExpression( QStringLiteral( "scale_linear(column * 2, 1, 7, 2, 10)" ), baseExpression, fieldName ) );
1181 QVERIFY( exp.get() );
1182 QCOMPARE( exp->type(), QgsSizeScaleTransformer::Linear );
1183 QCOMPARE( baseExpression, QStringLiteral( "column * 2" ) );
1184 QVERIFY( fieldName.isEmpty() );
1185 QCOMPARE( exp->minValue(), 1. );
1186 QCOMPARE( exp->maxValue(), 7. );
1187 QCOMPARE( exp->minSize(), 2. );
1188 QCOMPARE( exp->maxSize(), 10. );
1189
1190 exp.reset( QgsSizeScaleTransformer::fromExpression( QStringLiteral( "scale_exp(column, 1, 7, 2, 10, 0.5)" ), baseExpression, fieldName ) );
1191 QVERIFY( exp.get() );
1192 QCOMPARE( exp->type(), QgsSizeScaleTransformer::Area );
1193
1194 exp.reset( QgsSizeScaleTransformer::fromExpression( QStringLiteral( "scale_exp(column, 1, 7, 2, 10, 0.57)" ), baseExpression, fieldName ) );
1195 QVERIFY( exp.get() );
1196 QCOMPARE( exp->type(), QgsSizeScaleTransformer::Flannery );
1197
1198 exp.reset( QgsSizeScaleTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, 2, 10, 0.51), 22)" ), baseExpression, fieldName ) );
1199 QVERIFY( exp.get() );
1200 QCOMPARE( exp->type(), QgsSizeScaleTransformer::Exponential );
1201 QCOMPARE( exp->nullSize(), 22.0 );
1202
1203 QVERIFY( !QgsSizeScaleTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7, a, 10, 0.5), 0)" ), baseExpression, fieldName ) );
1204 QVERIFY( !QgsSizeScaleTransformer::fromExpression( QStringLiteral( "coalesce(scale_exp(column, 1, 7), 0)" ), baseExpression, fieldName ) );
1205 QVERIFY( !QgsSizeScaleTransformer::fromExpression( QStringLiteral( "1+2" ), baseExpression, fieldName ) );
1206 QVERIFY( !QgsSizeScaleTransformer::fromExpression( QString(), baseExpression, fieldName ) );
1207 }
1208
colorRampTransformer()1209 void TestQgsProperty::colorRampTransformer()
1210 {
1211 QgsExpressionContext context;
1212 QgsColorRampTransformer scale( 10,
1213 20,
1214 new QgsGradientColorRamp( QColor( 0, 0, 0 ), QColor( 255, 255, 255 ) ),
1215 QColor( 100, 150, 200 ) );
1216 QCOMPARE( scale.transformerType(), QgsPropertyTransformer::ColorRampTransformer );
1217 QCOMPARE( scale.minValue(), 10.0 );
1218 QCOMPARE( scale.maxValue(), 20.0 );
1219 QVERIFY( scale.colorRamp() );
1220 QCOMPARE( scale.nullColor(), QColor( 100, 150, 200 ) );
1221
1222 //transform
1223 QCOMPARE( scale.transform( context, 10 ).value<QColor>(), QColor( 0, 0, 0 ) );
1224 QCOMPARE( scale.transform( context, 20 ).value<QColor>(), QColor( 255, 255, 255 ) );
1225 //null value
1226 QCOMPARE( scale.transform( context, QVariant( QVariant::Double ) ).value<QColor>(), QColor( 100, 150, 200 ) );
1227 //non numeric value
1228 QCOMPARE( scale.transform( context, QVariant( "ffff" ) ), QVariant( "ffff" ) );
1229
1230 // add a curve
1231 QVERIFY( !scale.curveTransform() );
1232 scale.setCurveTransform( new QgsCurveTransform( QList< QgsPointXY >() << QgsPointXY( 0, 0.2 ) << QgsPointXY( 1, 0.8 ) ) );
1233 QVERIFY( scale.curveTransform() );
1234 QCOMPARE( scale.curveTransform()->controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0, 0.2 ) << QgsPointXY( 1, 0.8 ) );
1235
1236 QCOMPARE( scale.transform( context, 10 ).value<QColor>().name(), QString( "#333333" ) );
1237 QCOMPARE( scale.transform( context, 20 ).value<QColor>().name(), QString( "#cccccc" ) );
1238
1239 // copy
1240 QgsColorRampTransformer s1( scale );
1241 QVERIFY( s1.curveTransform() );
1242 QCOMPARE( s1.curveTransform()->controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0, 0.2 ) << QgsPointXY( 1, 0.8 ) );
1243
1244 // assignment
1245 QgsColorRampTransformer s2;
1246 s2 = scale;
1247 QVERIFY( s2.curveTransform() );
1248 QCOMPARE( s2.curveTransform()->controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0, 0.2 ) << QgsPointXY( 1, 0.8 ) );
1249
1250 //saving and restoring
1251
1252 //create a test dom element
1253 QDomImplementation DomImplementation;
1254 QDomDocumentType documentType =
1255 DomImplementation.createDocumentType(
1256 QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
1257 QDomDocument doc( documentType );
1258
1259 QgsColorRampTransformer t1( 15,
1260 25,
1261 new QgsGradientColorRamp( QColor( 10, 20, 30 ), QColor( 200, 190, 180 ) ),
1262 QColor( 100, 150, 200 ) );
1263 t1.setRampName( QStringLiteral( "rampname " ) );
1264 t1.setCurveTransform( new QgsCurveTransform( QList< QgsPointXY >() << QgsPointXY( 0, 0.8 ) << QgsPointXY( 1, 0.2 ) ) );
1265
1266 QVariant element;
1267 element = t1.toVariant();
1268 QgsColorRampTransformer r1;
1269 QVERIFY( r1.loadVariant( element ) );
1270 QCOMPARE( r1.minValue(), 15.0 );
1271 QCOMPARE( r1.maxValue(), 25.0 );
1272 QCOMPARE( r1.nullColor(), QColor( 100, 150, 200 ) );
1273 QCOMPARE( r1.rampName(), QStringLiteral( "rampname " ) );
1274 QVERIFY( dynamic_cast< QgsGradientColorRamp * >( r1.colorRamp() ) );
1275 QCOMPARE( static_cast< QgsGradientColorRamp * >( r1.colorRamp() )->color1(), QColor( 10, 20, 30 ) );
1276 QCOMPARE( static_cast< QgsGradientColorRamp * >( r1.colorRamp() )->color2(), QColor( 200, 190, 180 ) );
1277 QVERIFY( r1.curveTransform() );
1278 QCOMPARE( r1.curveTransform()->controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0, 0.8 ) << QgsPointXY( 1, 0.2 ) );
1279
1280 // test cloning
1281 std::unique_ptr< QgsColorRampTransformer > r2( t1.clone() );
1282 QCOMPARE( r2->minValue(), 15.0 );
1283 QCOMPARE( r2->maxValue(), 25.0 );
1284 QCOMPARE( r2->nullColor(), QColor( 100, 150, 200 ) );
1285 QCOMPARE( r2->rampName(), QStringLiteral( "rampname " ) );
1286 QCOMPARE( static_cast< QgsGradientColorRamp * >( r2->colorRamp() )->color1(), QColor( 10, 20, 30 ) );
1287 QCOMPARE( static_cast< QgsGradientColorRamp * >( r2->colorRamp() )->color2(), QColor( 200, 190, 180 ) );
1288 QVERIFY( r2->curveTransform() );
1289 QCOMPARE( r2->curveTransform()->controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0, 0.8 ) << QgsPointXY( 1, 0.2 ) );
1290
1291 // copy constructor
1292 QgsColorRampTransformer r3( t1 );
1293 QCOMPARE( r3.minValue(), 15.0 );
1294 QCOMPARE( r3.maxValue(), 25.0 );
1295 QCOMPARE( r3.nullColor(), QColor( 100, 150, 200 ) );
1296 QCOMPARE( r3.rampName(), QStringLiteral( "rampname " ) );
1297 QCOMPARE( static_cast< QgsGradientColorRamp * >( r3.colorRamp() )->color1(), QColor( 10, 20, 30 ) );
1298 QCOMPARE( static_cast< QgsGradientColorRamp * >( r3.colorRamp() )->color2(), QColor( 200, 190, 180 ) );
1299 QVERIFY( r3.curveTransform() );
1300 QCOMPARE( r3.curveTransform()->controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0, 0.8 ) << QgsPointXY( 1, 0.2 ) );
1301
1302 // assignment operator
1303 QgsColorRampTransformer r4;
1304 r4 = t1;
1305 QCOMPARE( r4.minValue(), 15.0 );
1306 QCOMPARE( r4.maxValue(), 25.0 );
1307 QCOMPARE( r4.nullColor(), QColor( 100, 150, 200 ) );
1308 QCOMPARE( r4.rampName(), QStringLiteral( "rampname " ) );
1309 QCOMPARE( static_cast< QgsGradientColorRamp * >( r4.colorRamp() )->color1(), QColor( 10, 20, 30 ) );
1310 QCOMPARE( static_cast< QgsGradientColorRamp * >( r4.colorRamp() )->color2(), QColor( 200, 190, 180 ) );
1311 QVERIFY( r4.curveTransform() );
1312 QCOMPARE( r4.curveTransform()->controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0, 0.8 ) << QgsPointXY( 1, 0.2 ) );
1313
1314 //test various min/max value/color and scaling methods
1315
1316 //getters and setters
1317 QgsColorRampTransformer t;
1318 t.setMinValue( 100 );
1319 QCOMPARE( t.minValue(), 100.0 );
1320 t.setMaxValue( 200 );
1321 QCOMPARE( t.maxValue(), 200.0 );
1322 t.setNullColor( QColor( 1, 10, 11, 21 ) );
1323 QCOMPARE( t.nullColor(), QColor( 1, 10, 11, 21 ) );
1324 t.setColorRamp( new QgsGradientColorRamp( QColor( 10, 20, 100 ), QColor( 100, 200, 200 ) ) );
1325 QCOMPARE( static_cast< QgsGradientColorRamp * >( t.colorRamp() )->color1(), QColor( 10, 20, 100 ) );
1326 t.setRampName( QStringLiteral( "colorramp" ) );
1327 QCOMPARE( t.rampName(), QStringLiteral( "colorramp" ) );
1328
1329 //test colors
1330 QCOMPARE( t.color( 50 ), QColor( 10, 20, 100 ) ); //out of range
1331 QCOMPARE( t.color( 100 ), QColor( 10, 20, 100 ) );
1332 QCOMPARE( t.color( 150 ), QColor( 55, 110, 150 ) );
1333 QCOMPARE( t.color( 200 ), QColor( 100, 200, 200 ) );
1334 QCOMPARE( t.color( 250 ), QColor( 100, 200, 200 ) ); //out of range
1335
1336 //toExpression
1337 QgsColorRampTransformer t5( 15,
1338 25,
1339 new QgsGradientColorRamp( QColor( 10, 20, 30 ), QColor( 200, 190, 180 ) ),
1340 QColor( 100, 150, 200 ) );
1341 QCOMPARE( t5.toExpression( "5+6" ), QStringLiteral( "coalesce(ramp_color('custom ramp',scale_linear(5+6, 15, 25, 0, 1)), '#6496c8')" ) );
1342 t5.setRampName( QStringLiteral( "my ramp" ) );
1343 QCOMPARE( t5.toExpression( "5+6" ), QStringLiteral( "coalesce(ramp_color('my ramp',scale_linear(5+6, 15, 25, 0, 1)), '#6496c8')" ) );
1344 }
1345
propertyToTransformer()1346 void TestQgsProperty::propertyToTransformer()
1347 {
1348 // not convertible to a transformer:
1349
1350 // fields cannot be converted
1351 QgsProperty p = QgsProperty::fromField( QStringLiteral( "a field" ) );
1352 QVERIFY( !p.convertToTransformer() );
1353 QVERIFY( !p.transformer() );
1354 QCOMPARE( p.field(), QStringLiteral( "a field" ) );
1355
1356 // static values cannot be converted
1357 p = QgsProperty::fromValue( 5 );
1358 QVERIFY( !p.convertToTransformer() );
1359 QVERIFY( !p.transformer() );
1360 QCOMPARE( p.staticValue(), QVariant( 5 ) );
1361
1362 // bad expression which cannot be converted
1363 p = QgsProperty::fromExpression( QStringLiteral( "5*5" ) );
1364 QVERIFY( !p.convertToTransformer() );
1365 QVERIFY( !p.transformer() );
1366 QCOMPARE( p.expressionString(), QStringLiteral( "5*5" ) );
1367
1368 // expression which can be converted to size scale transformer with base expression
1369 p = QgsProperty::fromExpression( QStringLiteral( "coalesce(scale_linear(column * 2, 1, 7, 2, 10), 0)" ) );
1370 QVERIFY( p.convertToTransformer() );
1371 QVERIFY( p.transformer() );
1372 QCOMPARE( p.expressionString(), QStringLiteral( "column * 2" ) );
1373
1374 // expression which can be converted to a size scale transformer with base column ref
1375 p = QgsProperty::fromExpression( QStringLiteral( "coalesce(scale_linear(column, 1, 7, 2, 10), 0)" ) );
1376 QVERIFY( p.convertToTransformer() );
1377 QVERIFY( p.transformer() );
1378 QCOMPARE( p.field(), QStringLiteral( "column" ) );
1379 }
1380
asExpression()1381 void TestQgsProperty::asExpression()
1382 {
1383 // static property
1384 QgsProperty p = QgsProperty::fromValue( 5 );
1385 QCOMPARE( p.asExpression(), QStringLiteral( "5" ) );
1386 p = QgsProperty::fromValue( "value" );
1387 QCOMPARE( p.asExpression(), QStringLiteral( "'value'" ) );
1388
1389 // field based property
1390 p = QgsProperty::fromField( QStringLiteral( "a field" ) );
1391 QCOMPARE( p.asExpression(), QStringLiteral( "\"a field\"" ) );
1392
1393 // expression based property
1394 p = QgsProperty::fromExpression( QStringLiteral( "5 + 6" ) );
1395 QCOMPARE( p.asExpression(), QStringLiteral( "5 + 6" ) );
1396
1397 // with transformer
1398 p.setTransformer( new QgsSizeScaleTransformer( QgsSizeScaleTransformer::Linear,
1399 15,
1400 25,
1401 150,
1402 250,
1403 -10,
1404 1 ) );
1405 QCOMPARE( p.asExpression(), QStringLiteral( "coalesce(scale_linear(5 + 6, 15, 25, 150, 250), -10)" ) );
1406 }
1407
propertyCollection()1408 void TestQgsProperty::propertyCollection()
1409 {
1410 //make a feature
1411 QgsFeature ft;
1412 QgsFields fields;
1413 fields.append( QgsField( QStringLiteral( "field1" ), QVariant::Int ) );
1414 fields.append( QgsField( QStringLiteral( "field2" ), QVariant::Int ) );
1415 ft.setFields( fields );
1416 QgsAttributes attr;
1417 attr << QVariant( 5 ) << QVariant( 7 );
1418 ft.setAttributes( attr );
1419 ft.setValid( true );
1420
1421 // throw it in an expression context
1422 QgsExpressionContext context;
1423 context.setFeature( ft );
1424 context.setFields( fields );
1425
1426 QgsPropertyCollection collection( QStringLiteral( "collection" ) );
1427 QCOMPARE( collection.name(), QStringLiteral( "collection" ) );
1428 QVERIFY( !collection.hasProperty( Property1 ) );
1429 QVERIFY( collection.referencedFields( context ).isEmpty() );
1430 QCOMPARE( collection.count(), 0 );
1431 QCOMPARE( collection.propertyKeys(), QSet< int >() );
1432 QVERIFY( !collection.hasDynamicProperties() );
1433 QVERIFY( !collection.hasActiveProperties() );
1434
1435 QgsPropertyCollection collection2;
1436 QVERIFY( collection == collection2 );
1437 QVERIFY( !( collection != collection2 ) );
1438
1439 QgsProperty property = QgsProperty::fromValue( "value", true );
1440 collection.setProperty( Property1, property );
1441 QVERIFY( collection.hasProperty( Property1 ) );
1442 QCOMPARE( collection.count(), 1 );
1443 QCOMPARE( collection.propertyKeys(), QSet< int >() << Property1 );
1444 QCOMPARE( collection.property( Property1 ).value( context ), property.value( context ) );
1445 QCOMPARE( collection.value( Property1, context ), property.value( context ) );
1446 QVERIFY( collection.isActive( Property1 ) );
1447 QVERIFY( collection.hasActiveProperties() );
1448 QVERIFY( !collection.hasDynamicProperties() );
1449
1450 QVERIFY( collection != collection2 );
1451 QVERIFY( !( collection == collection2 ) );
1452 collection2.setProperty( Property1, property );
1453 QVERIFY( collection == collection2 );
1454 QVERIFY( !( collection != collection2 ) );
1455
1456 //preparation
1457 QVERIFY( collection.prepare( context ) );
1458
1459 //test bad property
1460 QVERIFY( !const_cast< const QgsPropertyCollection * >( &collection )->property( Property2 ) );
1461 QVERIFY( !collection.value( Property2, context ).isValid() );
1462 QCOMPARE( collection.value( Property2, context, QStringLiteral( "default" ) ).toString(), QStringLiteral( "default" ) );
1463 QVERIFY( !collection.isActive( Property2 ) );
1464
1465 //test replacing property
1466 QgsProperty property2 = QgsProperty::fromValue( "value2", true );
1467 collection.setProperty( Property1, property2 );
1468 QCOMPARE( collection.count(), 1 );
1469 QCOMPARE( collection.propertyKeys(), QSet< int >() << Property1 );
1470 QCOMPARE( collection.property( Property1 ).value( context ), property2.value( context ) );
1471 QVERIFY( collection.hasActiveProperties() );
1472 QVERIFY( !collection.hasDynamicProperties() );
1473 QVERIFY( collection != collection2 );
1474 QVERIFY( !( collection == collection2 ) );
1475
1476 //implicit conversion
1477 collection.setProperty( Property3, 5 );
1478 QCOMPARE( collection.property( Property3 ).value( context ).toInt(), 5 );
1479 QVERIFY( collection.property( Property3 ).isActive() );
1480 QCOMPARE( collection.count(), 2 );
1481 QCOMPARE( collection.propertyKeys(), QSet<int>() << Property1 << Property3 );
1482
1483 //test removing a property
1484 collection.setProperty( Property1, QgsProperty() );
1485 QVERIFY( !const_cast< const QgsPropertyCollection * >( &collection )->property( Property1 ) );
1486 QVERIFY( !collection.hasProperty( Property1 ) );
1487 QCOMPARE( collection.propertyKeys(), QSet<int>() << Property3 );
1488 QVERIFY( !collection.property( Property1 ) ); // should insert a default created invalid property in internal hash
1489 QVERIFY( !collection.hasProperty( Property1 ) );
1490
1491 //clear
1492 collection.clear();
1493 QCOMPARE( collection.count(), 0 );
1494 QCOMPARE( collection.propertyKeys(), QSet<int>() );
1495 QVERIFY( !collection.hasActiveProperties() );
1496 QVERIFY( !collection.hasDynamicProperties() );
1497
1498 collection.setProperty( Property1, QgsProperty::fromValue( "v1", true ) );
1499 collection.setProperty( Property2, QgsProperty::fromValue( "v2", false ) );
1500 collection.setProperty( Property3, QgsProperty::fromField( QStringLiteral( "field1" ), true ) );
1501 collection.setProperty( Property4, QgsProperty::fromExpression( QStringLiteral( "\"field1\" + \"field2\"" ), true ) );
1502 QCOMPARE( collection.count(), 4 );
1503
1504 collection2 = collection;
1505 QVERIFY( collection == collection2 );
1506 QVERIFY( !( collection != collection2 ) );
1507 collection2.setProperty( Property3, QgsProperty() );
1508 QVERIFY( collection != collection2 );
1509 QVERIFY( !( collection == collection2 ) );
1510
1511 // test referenced fields
1512 QCOMPARE( collection.referencedFields( context ).count(), 2 );
1513 QVERIFY( collection.referencedFields( context ).contains( "field1" ) );
1514 QVERIFY( collection.referencedFields( context ).contains( "field2" ) );
1515
1516 //saving and restoring
1517
1518 QDomImplementation DomImplementation;
1519 QDomDocumentType documentType =
1520 DomImplementation.createDocumentType(
1521 QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
1522 QDomDocument doc( documentType );
1523 QVariant collectionElement = collection.toVariant( mDefinitions );
1524
1525 QgsPropertyCollection restoredCollection;
1526 restoredCollection.loadVariant( collectionElement, mDefinitions );
1527 QCOMPARE( restoredCollection.name(), QStringLiteral( "collection" ) );
1528 QCOMPARE( restoredCollection.count(), 4 );
1529 QCOMPARE( restoredCollection.property( Property1 ).propertyType(), QgsProperty::StaticProperty );
1530 QVERIFY( restoredCollection.property( Property1 ).isActive() );
1531 QCOMPARE( restoredCollection.property( Property1 ).staticValue(), QVariant( "v1" ) );
1532 QCOMPARE( restoredCollection.property( Property2 ).propertyType(), QgsProperty::StaticProperty );
1533 QVERIFY( !restoredCollection.property( Property2 ).isActive() );
1534 QCOMPARE( restoredCollection.property( Property2 ).staticValue(), QVariant( "v2" ) );
1535 QCOMPARE( restoredCollection.property( Property3 ).propertyType(), QgsProperty::FieldBasedProperty );
1536 QVERIFY( restoredCollection.property( Property3 ).isActive() );
1537 QCOMPARE( restoredCollection.property( Property3 ).field(), QStringLiteral( "field1" ) );
1538 QCOMPARE( restoredCollection.property( Property4 ).propertyType(), QgsProperty::ExpressionBasedProperty );
1539 QVERIFY( restoredCollection.property( Property4 ).isActive() );
1540 QCOMPARE( restoredCollection.property( Property4 ).expressionString(), QStringLiteral( "\"field1\" + \"field2\"" ) );
1541 QVERIFY( restoredCollection.hasActiveProperties() );
1542 QVERIFY( restoredCollection.hasDynamicProperties() );
1543
1544 // copy constructor
1545 collection2 = QgsPropertyCollection( collection );
1546 QCOMPARE( collection2.name(), QStringLiteral( "collection" ) );
1547 QCOMPARE( collection2.count(), 4 );
1548 QCOMPARE( collection2.property( Property1 ).propertyType(), QgsProperty::StaticProperty );
1549 QVERIFY( collection2.property( Property1 ).isActive() );
1550 QCOMPARE( collection2.property( Property1 ).staticValue(), QVariant( "v1" ) );
1551 QCOMPARE( collection2.property( Property2 ).propertyType(), QgsProperty::StaticProperty );
1552 QVERIFY( !collection2.property( Property2 ).isActive() );
1553 QCOMPARE( collection2.property( Property2 ).staticValue(), QVariant( "v2" ) );
1554 QCOMPARE( collection2.property( Property3 ).propertyType(), QgsProperty::FieldBasedProperty );
1555 QVERIFY( collection2.property( Property3 ).isActive() );
1556 QCOMPARE( collection2.property( Property3 ).field(), QStringLiteral( "field1" ) );
1557 QCOMPARE( collection2.property( Property4 ).propertyType(), QgsProperty::ExpressionBasedProperty );
1558 QVERIFY( collection2.property( Property4 ).isActive() );
1559 QCOMPARE( collection2.property( Property4 ).expressionString(), QStringLiteral( "\"field1\" + \"field2\"" ) );
1560 QVERIFY( collection2.hasActiveProperties() );
1561 QVERIFY( collection2.hasDynamicProperties() );
1562
1563 // assignment operator
1564 QgsPropertyCollection collection3;
1565 collection3.setProperty( Property1, QgsProperty::fromValue( "aaaa", false ) );
1566 collection3 = collection;
1567 QCOMPARE( collection3.name(), QStringLiteral( "collection" ) );
1568 QCOMPARE( collection3.count(), 4 );
1569 QCOMPARE( collection3.property( Property1 ).propertyType(), QgsProperty::StaticProperty );
1570 QVERIFY( collection3.property( Property1 ).isActive() );
1571 QCOMPARE( collection3.property( Property1 ).staticValue(), QVariant( "v1" ) );
1572 QCOMPARE( collection3.property( Property2 ).propertyType(), QgsProperty::StaticProperty );
1573 QVERIFY( !collection3.property( Property2 ).isActive() );
1574 QCOMPARE( collection3.property( Property2 ).staticValue(), QVariant( "v2" ) );
1575 QCOMPARE( collection3.property( Property3 ).propertyType(), QgsProperty::FieldBasedProperty );
1576 QVERIFY( collection3.property( Property3 ).isActive() );
1577 QCOMPARE( collection3.property( Property3 ).field(), QStringLiteral( "field1" ) );
1578 QCOMPARE( collection3.property( Property4 ).propertyType(), QgsProperty::ExpressionBasedProperty );
1579 QVERIFY( collection3.property( Property4 ).isActive() );
1580 QCOMPARE( collection3.property( Property4 ).expressionString(), QStringLiteral( "\"field1\" + \"field2\"" ) );
1581 QVERIFY( collection3.hasActiveProperties() );
1582 QVERIFY( collection3.hasDynamicProperties() );
1583
1584 //test hasActiveProperties() and hasDynamicProperties()
1585 collection3.property( Property1 ).setActive( false );
1586 collection3.property( Property2 ).setActive( false );
1587 collection3.property( Property3 ).setActive( false );
1588 collection3.property( Property4 ).setActive( false );
1589 QVERIFY( !collection3.hasActiveProperties() );
1590 QVERIFY( !collection3.hasDynamicProperties() );
1591 collection3.property( Property4 ).setActive( true );
1592 QVERIFY( collection3.hasDynamicProperties() );
1593 QVERIFY( collection3.hasActiveProperties() );
1594 collection3.property( Property4 ).setActive( false );
1595 collection3.property( Property2 ).setActive( true );
1596 QVERIFY( !collection3.hasDynamicProperties() );
1597 QVERIFY( collection3.hasActiveProperties() );
1598 collection3.property( Property2 ).setActive( false );
1599 QVERIFY( !collection3.hasActiveProperties() );
1600 collection3.setProperty( Property1, "5" );
1601 QVERIFY( collection3.hasActiveProperties() );
1602 collection3.setProperty( Property1, QgsProperty::fromValue( "6", true ) );
1603 QVERIFY( collection3.hasActiveProperties() );
1604 collection3.setProperty( Property1, QgsProperty::fromValue( "7", false ) );
1605 QVERIFY( !collection3.hasActiveProperties() );
1606 collection3.setProperty( Property3, QVariant( "val" ) );
1607 QVERIFY( collection3.hasActiveProperties() );
1608 }
1609
collectionStack()1610 void TestQgsProperty::collectionStack()
1611 {
1612 //make a feature
1613 QgsFeature ft;
1614 QgsFields fields;
1615 fields.append( QgsField( QStringLiteral( "field1" ), QVariant::Int ) );
1616 fields.append( QgsField( QStringLiteral( "field2" ), QVariant::Int ) );
1617 ft.setFields( fields );
1618 QgsAttributes attr;
1619 attr << QVariant( 5 ) << QVariant( 7 );
1620 ft.setAttributes( attr );
1621 ft.setValid( true );
1622
1623 // throw it in an expression context
1624 QgsExpressionContext context;
1625 context.setFeature( ft );
1626 context.setFields( fields );
1627
1628 QgsPropertyCollectionStack stack;
1629 //test retrieving from empty stack
1630 QVERIFY( !stack.property( Property1 ) );
1631 QVERIFY( !stack.at( 0 ) );
1632 QVERIFY( !const_cast< const QgsPropertyCollectionStack * >( &stack )->at( 0 ) );
1633 QVERIFY( !stack.collection( "nothing" ) );
1634 QVERIFY( !stack.value( Property1, context ).isValid() );
1635 QCOMPARE( stack.value( Property1, context, "default" ).toString(), QStringLiteral( "default" ) );
1636 QVERIFY( !stack.isActive( Property1 ) );
1637 QVERIFY( stack.referencedFields( context ).isEmpty() );
1638 QCOMPARE( stack.count(), 0 );
1639 QVERIFY( !stack.hasDynamicProperties() );
1640 QVERIFY( !stack.hasActiveProperties() );
1641
1642 //add a collection to the stack
1643 QgsPropertyCollection *collection = new QgsPropertyCollection( QStringLiteral( "collection" ) );
1644 stack.appendCollection( collection );
1645 QCOMPARE( stack.count(), 1 );
1646 QCOMPARE( stack.at( 0 ), collection );
1647 QCOMPARE( const_cast< const QgsPropertyCollectionStack * >( &stack )->at( 0 ), collection );
1648 QVERIFY( !stack.collection( "nothing" ) );
1649 QCOMPARE( stack.collection( "collection" ), collection );
1650 QVERIFY( !stack.property( Property1 ) );
1651 QVERIFY( !stack.value( Property1, context ).isValid() );
1652 QCOMPARE( stack.value( Property1, context, "default" ).toString(), QStringLiteral( "default" ) );
1653 QVERIFY( !stack.isActive( Property1 ) );
1654 QVERIFY( !stack.hasDynamicProperties() );
1655 QVERIFY( !stack.hasActiveProperties() );
1656 QVERIFY( stack.referencedFields( context ).isEmpty() );
1657
1658 //now add a property to the collection
1659 QgsProperty property = QgsProperty::fromValue( "value", true );
1660 stack.at( 0 )->setProperty( Property1, property );
1661 QVERIFY( stack.isActive( Property1 ) );
1662 QCOMPARE( stack.property( Property1 ).value( context ), property.value( context ) );
1663 QCOMPARE( stack.value( Property1, context ), property.value( context ) );
1664 QVERIFY( !stack.hasDynamicProperties() );
1665 QVERIFY( stack.hasActiveProperties() );
1666 QVERIFY( !stack.isActive( Property2 ) );
1667 collection->setProperty( Property2, QgsProperty::fromValue( "value1", true ) );
1668 QVERIFY( stack.isActive( Property2 ) );
1669 QVERIFY( !stack.hasDynamicProperties() );
1670 QVERIFY( stack.hasActiveProperties() );
1671
1672 //add a second collection
1673 QgsPropertyCollection *collection2 = new QgsPropertyCollection( QStringLiteral( "collection2" ) );
1674 stack.appendCollection( collection2 );
1675 QCOMPARE( stack.count(), 2 );
1676 QCOMPARE( stack.at( 1 ), collection2 );
1677 QCOMPARE( const_cast< const QgsPropertyCollectionStack * >( &stack )->at( 1 ), collection2 );
1678 QCOMPARE( stack.collection( "collection2" ), collection2 );
1679 QVERIFY( !stack.hasDynamicProperties() );
1680 QVERIFY( stack.hasActiveProperties() );
1681 QgsProperty property2 = QgsProperty::fromValue( "value2", true );
1682 collection2->setProperty( Property2, property2 );
1683 QVERIFY( stack.isActive( Property2 ) );
1684 QCOMPARE( stack.property( Property2 ).value( context ), property2.value( context ) );
1685 QCOMPARE( stack.value( Property2, context ), property2.value( context ) );
1686 QVERIFY( !stack.hasDynamicProperties() );
1687 QVERIFY( stack.hasActiveProperties() );
1688
1689 //preparation
1690 QVERIFY( stack.prepare( context ) );
1691
1692 //test adding active property later in the stack
1693 QgsProperty property3 = QgsProperty::fromValue( "value3", true );
1694 collection2->setProperty( Property1, property3 );
1695 QVERIFY( stack.isActive( Property1 ) );
1696 QCOMPARE( stack.property( Property1 ).value( context, "default" ), property3.value( context ) );
1697 QCOMPARE( stack.value( Property1, context ), property3.value( context ) );
1698 collection2->property( Property1 ).setActive( false );
1699 QCOMPARE( stack.value( Property1, context ), property.value( context ) );
1700
1701 //test overriding a property
1702 QgsProperty property4 = QgsProperty::fromValue( "value4", true );
1703 collection2->setProperty( Property2, property4 );
1704 QVERIFY( stack.isActive( Property2 ) );
1705 QCOMPARE( stack.property( Property2 ).value( context ), property4.value( context ) );
1706 QCOMPARE( stack.value( Property2, context ), property4.value( context ) );
1707 collection2->property( Property2 ).setActive( false );
1708 QCOMPARE( stack.property( Property2 ).value( context ), QVariant( "value1" ) );
1709 QCOMPARE( stack.value( Property2, context ), QVariant( "value1" ) );
1710
1711 //clearing
1712 stack.clear();
1713 QCOMPARE( stack.count(), 0 );
1714 QVERIFY( !stack.hasDynamicProperties() );
1715 QVERIFY( !stack.hasActiveProperties() );
1716
1717 // test copying a stack
1718 QgsPropertyCollectionStack stack2;
1719 stack2.appendCollection( new QgsPropertyCollection( QStringLiteral( "collection1" ) ) );
1720 stack2.at( 0 )->setProperty( Property1, "val1" );
1721 stack2.at( 0 )->setProperty( Property2, "val2" );
1722 stack2.appendCollection( new QgsPropertyCollection( QStringLiteral( "collection2" ) ) );
1723 stack2.at( 1 )->setProperty( Property3, "val3" );
1724 //copy constructor
1725 QgsPropertyCollectionStack stack3( stack2 );
1726 QCOMPARE( stack3.count(), 2 );
1727 QCOMPARE( stack3.at( 0 )->name(), QStringLiteral( "collection1" ) );
1728 QCOMPARE( stack3.at( 1 )->name(), QStringLiteral( "collection2" ) );
1729 QCOMPARE( stack3.at( 0 )->property( Property1 ).staticValue(), QVariant( "val1" ) );
1730 QCOMPARE( stack3.at( 0 )->property( Property2 ).staticValue(), QVariant( "val2" ) );
1731 QCOMPARE( stack3.at( 1 )->property( Property3 ).staticValue(), QVariant( "val3" ) );
1732 QVERIFY( !stack3.hasDynamicProperties() );
1733 QVERIFY( stack3.hasActiveProperties() );
1734 //assignment operator
1735 stack3.clear();
1736 stack3.appendCollection( new QgsPropertyCollection( QStringLiteral( "temp" ) ) );
1737 stack3 = stack2;
1738 QCOMPARE( stack3.count(), 2 );
1739 QCOMPARE( stack3.at( 0 )->name(), QStringLiteral( "collection1" ) );
1740 QCOMPARE( stack3.at( 1 )->name(), QStringLiteral( "collection2" ) );
1741 QCOMPARE( stack3.at( 0 )->property( Property1 ).staticValue(), QVariant( "val1" ) );
1742 QCOMPARE( stack3.at( 0 )->property( Property2 ).staticValue(), QVariant( "val2" ) );
1743 QCOMPARE( stack3.at( 1 )->property( Property3 ).staticValue(), QVariant( "val3" ) );
1744 QVERIFY( !stack3.hasDynamicProperties() );
1745 QVERIFY( stack3.hasActiveProperties() );
1746
1747 //check hasDynamicProperties() and hasActiveProperties()
1748 QgsPropertyCollectionStack stack4;
1749 stack4.appendCollection( new QgsPropertyCollection( QStringLiteral( "collection1" ) ) );
1750 stack4.at( 0 )->setProperty( Property1, "val1" );
1751 QVERIFY( !stack4.hasDynamicProperties() );
1752 QVERIFY( stack4.hasActiveProperties() );
1753 stack4.at( 0 )->property( Property1 ).setActive( false );
1754 QVERIFY( !stack4.hasActiveProperties() );
1755 stack4.at( 0 )->setProperty( Property1, "6" );
1756 QVERIFY( stack4.hasActiveProperties() );
1757 stack4.at( 0 )->setProperty( Property2, QgsProperty::fromExpression( QStringLiteral( "\"field1\" + \"field2\"" ), true ) );
1758 QVERIFY( stack4.hasActiveProperties() );
1759 QVERIFY( stack4.hasDynamicProperties() );
1760 QCOMPARE( stack4.referencedFields( context ), QSet< QString>() << "field1" << "field2" );
1761 stack4.at( 0 )->property( Property1 ).setActive( false );
1762 QVERIFY( stack4.hasActiveProperties() );
1763 QVERIFY( stack4.hasDynamicProperties() );
1764 stack4.at( 0 )->property( Property2 ).setActive( false );
1765 QVERIFY( !stack4.hasActiveProperties() );
1766 QVERIFY( !stack4.hasDynamicProperties() );
1767 }
1768
curveTransform()1769 void TestQgsProperty::curveTransform()
1770 {
1771 QgsCurveTransform t;
1772 // linear transform
1773 QCOMPARE( t.y( -1 ), 0.0 );
1774 QCOMPARE( t.y( 0 ), 0.0 );
1775 QCOMPARE( t.y( 0.2 ), 0.2 );
1776 QCOMPARE( t.y( 0.5 ), 0.5 );
1777 QCOMPARE( t.y( 0.8 ), 0.8 );
1778 QCOMPARE( t.y( 1 ), 1.0 );
1779 QCOMPARE( t.y( 2 ), 1.0 );
1780
1781 QVector< double > x;
1782 x << -1 << 0 << 0.2 << 0.5 << 0.8 << 1 << 2;
1783 QVector< double > y = t.y( x );
1784 QCOMPARE( y[0], 0.0 );
1785 QCOMPARE( y[1], 0.0 );
1786 QCOMPARE( y[2], 0.2 );
1787 QCOMPARE( y[3], 0.5 );
1788 QCOMPARE( y[4], 0.8 );
1789 QCOMPARE( y[5], 1.0 );
1790 QCOMPARE( y[6], 1.0 );
1791
1792 // linear transform with y =/= x
1793 checkCurveResult( QList< QgsPointXY >() << QgsPointXY( 0, 0.2 ) << QgsPointXY( 1.0, 0.8 ),
1794 QVector< double >() << -1 << 0 << 0.2 << 0.5 << 0.8 << 1 << 2,
1795 QVector< double >() << 0.2 << 0.2 << 0.32 << 0.5 << 0.68 << 0.8 << 0.8 );
1796
1797 // reverse linear transform with y = -x
1798 checkCurveResult( QList< QgsPointXY >() << QgsPointXY( 0.0, 1.0 ) << QgsPointXY( 1.0, 0 ),
1799 QVector< double >() << -1 << 0 << 0.2 << 0.5 << 0.8 << 1 << 2,
1800 QVector< double >() << 1.0 << 1.0 << 0.8 << 0.5 << 0.2 << 0.0 << 0.0 );
1801
1802 // OK, time for some more complex tests...
1803
1804 // 3 control points, but linear
1805 checkCurveResult( QList< QgsPointXY >() << QgsPointXY( 0, 0.0 ) << QgsPointXY( 0.2, 0.2 ) << QgsPointXY( 1.0, 1.0 ),
1806 QVector< double >() << -1 << 0 << 0.2 << 0.5 << 0.8 << 1 << 2,
1807 QVector< double >() << 0.0 << 0.0 << 0.2 << 0.5 << 0.8 << 1.0 << 1.0 );
1808
1809 // test for "flat" response for x outside of control point range
1810 checkCurveResult( QList< QgsPointXY >() << QgsPointXY( 0.2, 0.2 ) << QgsPointXY( 0.5, 0.5 ) << QgsPointXY( 0.8, 0.8 ),
1811 QVector< double >() << -1 << 0 << 0.1 << 0.2 << 0.5 << 0.8 << 0.9 << 1 << 2,
1812 QVector< double >() << 0.2 << 0.2 << 0.2 << 0.2 << 0.5 << 0.8 << 0.8 << 0.8 << 0.8 );
1813
1814 //curves!
1815 checkCurveResult( QList< QgsPointXY >() << QgsPointXY( 0.0, 0.0 ) << QgsPointXY( 0.4, 0.6 ) << QgsPointXY( 0.6, 0.8 ) << QgsPointXY( 1.0, 1.0 ),
1816 QVector< double >() << -1 << 0 << 0.2 << 0.4 << 0.5 << 0.6 << 0.8 << 0.9 << 1.0 << 2.0,
1817 QVector< double >() << 0.0 << 0.0 << 0.321429 << 0.6 << 0.710714 << 0.8 << 0.921429 << 0.963393 << 1.0 << 1.0 );
1818
1819 //curves with more control points
1820 checkCurveResult( QList< QgsPointXY >() << QgsPointXY( 0.0, 0.0 ) << QgsPointXY( 0.2, 0.6 ) << QgsPointXY( 0.4, 0.6 ) << QgsPointXY( 0.6, 0.8 ) << QgsPointXY( 0.8, 0.3 ) << QgsPointXY( 1.0, 1.0 ),
1821 QVector< double >() << -1 << 0 << 0.2 << 0.4 << 0.5 << 0.6 << 0.8 << 0.9 << 1.0 << 2.0,
1822 QVector< double >() << 0.0 << 0.0 << 0.6 << 0.6 << 0.751316 << 0.8 << 0.3 << 0.508074 << 1.0 << 1.0 );
1823
1824 // general tests
1825 QList< QgsPointXY > points = QList< QgsPointXY >() << QgsPointXY( 0.0, 0.0 ) << QgsPointXY( 0.4, 0.6 ) << QgsPointXY( 0.6, 0.8 ) << QgsPointXY( 1.0, 1.0 );
1826 QgsCurveTransform src( points );
1827 QCOMPARE( src.controlPoints(), points );
1828 points = QList< QgsPointXY >() << QgsPointXY( 0.0, 0.0 ) << QgsPointXY( 0.5, 0.6 ) << QgsPointXY( 0.6, 0.8 ) << QgsPointXY( 1.0, 1.0 );
1829 src.setControlPoints( points );
1830 QCOMPARE( src.controlPoints(), points );
1831
1832 src.setControlPoints( QList< QgsPointXY >() << QgsPointXY( 0.0, 0.0 ) << QgsPointXY( 1.0, 1.0 ) );
1833 src.addControlPoint( 0.2, 0.3 );
1834 src.addControlPoint( 0.1, 0.4 );
1835 QCOMPARE( src.controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0.0, 0.0 ) << QgsPointXY( 0.1, 0.4 ) << QgsPointXY( 0.2, 0.3 ) << QgsPointXY( 1.0, 1.0 ) );
1836
1837 // remove non-existent point
1838 src.removeControlPoint( 0.6, 0.7 );
1839 QCOMPARE( src.controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0.0, 0.0 ) << QgsPointXY( 0.1, 0.4 ) << QgsPointXY( 0.2, 0.3 ) << QgsPointXY( 1.0, 1.0 ) );
1840
1841 // remove valid point
1842 src.removeControlPoint( 0.1, 0.4 );
1843 QCOMPARE( src.controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0.0, 0.0 ) << QgsPointXY( 0.2, 0.3 ) << QgsPointXY( 1.0, 1.0 ) );
1844
1845 // copy constructor
1846 QgsCurveTransform dest( src );
1847 QCOMPARE( dest.controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0.0, 0.0 ) << QgsPointXY( 0.2, 0.3 ) << QgsPointXY( 1.0, 1.0 ) );
1848 // check a value to ensure that derivative matrix was copied OK
1849 QGSCOMPARENEAR( dest.y( 0.5 ), 0.1, 0.638672 );
1850
1851 // assignment operator
1852 QgsCurveTransform dest2;
1853 dest2 = src;
1854 QCOMPARE( dest2.controlPoints(), QList< QgsPointXY >() << QgsPointXY( 0.0, 0.0 ) << QgsPointXY( 0.2, 0.3 ) << QgsPointXY( 1.0, 1.0 ) );
1855 QGSCOMPARENEAR( dest2.y( 0.5 ), 0.1, 0.638672 );
1856
1857 // writing and reading from xml
1858 QDomImplementation DomImplementation;
1859 QDomDocumentType documentType =
1860 DomImplementation.createDocumentType(
1861 QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
1862 QDomDocument doc( documentType );
1863
1864 QDomElement element = doc.createElement( QStringLiteral( "xform" ) );
1865 QVERIFY( src.writeXml( element, doc ) );
1866
1867 QgsCurveTransform r1;
1868 QVERIFY( r1.readXml( element, doc ) );
1869 QCOMPARE( r1.controlPoints(), src.controlPoints() );
1870 QGSCOMPARENEAR( dest2.y( 0.5 ), 0.1, 0.638672 );
1871 }
1872
asVariant()1873 void TestQgsProperty::asVariant()
1874 {
1875 QgsProperty original = QgsProperty::fromField( QStringLiteral( "field1" ), true );
1876
1877 //convert to and from a QVariant
1878 QVariant var = QVariant::fromValue( original );
1879 QVERIFY( var.isValid() );
1880
1881 QgsProperty fromVar = qvariant_cast<QgsProperty>( var );
1882 QCOMPARE( fromVar.propertyType(), QgsProperty::FieldBasedProperty );
1883 QVERIFY( fromVar.isActive() );
1884 QCOMPARE( fromVar.field(), QStringLiteral( "field1" ) );
1885 }
1886
isProjectColor()1887 void TestQgsProperty::isProjectColor()
1888 {
1889 QgsProperty p = QgsProperty::fromValue( 3, true );
1890 QVERIFY( !p.isProjectColor() );
1891 p = QgsProperty::fromField( QStringLiteral( "blah" ), true );
1892 QVERIFY( !p.isProjectColor() );
1893 p = QgsProperty::fromExpression( QStringLiteral( "1+2" ), true );
1894 QVERIFY( !p.isProjectColor() );
1895 p = QgsProperty::fromExpression( QStringLiteral( "project_color('mine')" ), true );
1896 QVERIFY( p.isProjectColor() );
1897 p = QgsProperty::fromExpression( QStringLiteral( "project_color('burnt pineapple Skin 76')" ), true );
1898 QVERIFY( p.isProjectColor() );
1899 p.setActive( false );
1900 QVERIFY( p.isProjectColor() );
1901 }
1902
referencedFieldsIgnoreContext()1903 void TestQgsProperty::referencedFieldsIgnoreContext()
1904 {
1905 // Currently QgsProperty::referencedFields() for an expression will return field names
1906 // only if those field names are present in the context's fields. The ignoreContext
1907 // argument is a workaround for the case when we don't have fields yet.
1908
1909 QgsProperty p = QgsProperty::fromExpression( QStringLiteral( "foo + bar" ) );
1910 QCOMPARE( p.referencedFields( QgsExpressionContext() ), QSet<QString>() );
1911 QCOMPARE( p.referencedFields( QgsExpressionContext(), true ), QSet<QString>() << QStringLiteral( "foo" ) << QStringLiteral( "bar" ) );
1912
1913 // if the property is from a field, the ignoreContext does not make a difference
1914 QgsProperty p2 = QgsProperty::fromField( QStringLiteral( "boo" ) );
1915 QCOMPARE( p2.referencedFields( QgsExpressionContext() ), QSet<QString>() << QStringLiteral( "boo" ) );
1916 QCOMPARE( p2.referencedFields( QgsExpressionContext(), true ), QSet<QString>() << QStringLiteral( "boo" ) );
1917
1918 QgsPropertyCollection collection;
1919 collection.setProperty( 0, p );
1920 collection.setProperty( 1, p2 );
1921
1922 QCOMPARE( collection.referencedFields( QgsExpressionContext() ), QSet<QString>() << QStringLiteral( "boo" ) );
1923 QCOMPARE( collection.referencedFields( QgsExpressionContext(), true ), QSet<QString>() << QStringLiteral( "boo" ) << QStringLiteral( "foo" ) << QStringLiteral( "bar" ) );
1924 }
1925
checkCurveResult(const QList<QgsPointXY> & controlPoints,const QVector<double> & x,const QVector<double> & y)1926 void TestQgsProperty::checkCurveResult( const QList<QgsPointXY> &controlPoints, const QVector<double> &x, const QVector<double> &y )
1927 {
1928 // build transform
1929 QgsCurveTransform t( controlPoints );
1930
1931 // we check two approaches
1932 for ( int i = 0; i < x.count(); ++i )
1933 {
1934 QGSCOMPARENEAR( t.y( x.at( i ) ), y.at( i ), 0.0001 );
1935 }
1936
1937 QVector< double > results = t.y( x );
1938 for ( int i = 0; i < y.count(); ++i )
1939 {
1940 QGSCOMPARENEAR( results.at( i ), y.at( i ), 0.0001 );
1941 }
1942 }
1943
1944 QGSTEST_MAIN( TestQgsProperty )
1945 #include "testqgsproperty.moc"
1946