Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsproperty.cpp
3 : : ---------------
4 : : Date : January 2017
5 : : Copyright : (C) 2017 by Nyall Dawson
6 : : Email : nyall dot dawson at gmail dot com
7 : : ***************************************************************************
8 : : * *
9 : : * This program is free software; you can redistribute it and/or modify *
10 : : * it under the terms of the GNU General Public License as published by *
11 : : * the Free Software Foundation; either version 2 of the License, or *
12 : : * (at your option) any later version. *
13 : : * *
14 : : ***************************************************************************/
15 : :
16 : : #include "qgsproperty.h"
17 : : #include "qgsproperty_p.h"
18 : :
19 : : #include "qgslogger.h"
20 : : #include "qgsexpression.h"
21 : : #include "qgsfeature.h"
22 : : #include "qgssymbollayerutils.h"
23 : : #include "qgscolorramp.h"
24 : :
25 : : #include <QRegularExpression>
26 : :
27 : 269 : QgsPropertyDefinition::QgsPropertyDefinition( const QString &name, const QString &description, QgsPropertyDefinition::StandardPropertyTemplate type, const QString &origin, const QString &comment )
28 : 269 : : mName( name )
29 : 269 : , mDescription( description )
30 : 269 : , mStandardType( type )
31 : 269 : , mOrigin( origin )
32 : 269 : , mComment( comment )
33 : : {
34 : 269 : switch ( mStandardType )
35 : : {
36 : : case Boolean:
37 : 35 : mTypes = DataTypeBoolean;
38 : 35 : mHelpText = QObject::tr( "bool [<b>1</b>=True|<b>0</b>=False]" );
39 : 35 : break;
40 : :
41 : : case Integer:
42 : 0 : mTypes = DataTypeNumeric;
43 : 0 : mHelpText = QObject::tr( "int [≤ 0 ≥]" );
44 : 0 : break;
45 : :
46 : : case IntegerPositive:
47 : 5 : mTypes = DataTypeNumeric;
48 : 5 : mHelpText = QObject::tr( "int [≥ 0]" );
49 : 5 : break;
50 : :
51 : : case IntegerPositiveGreaterZero:
52 : 0 : mTypes = DataTypeNumeric;
53 : 0 : mHelpText = QObject::tr( "int [≥ 1]" );
54 : 0 : break;
55 : :
56 : : case Double:
57 : 10 : mTypes = DataTypeNumeric;
58 : 10 : mHelpText = QObject::tr( "double [≤ 0.0 ≥]" );
59 : 10 : break;
60 : :
61 : : case DoublePositive:
62 : 85 : mTypes = DataTypeNumeric;
63 : 85 : mHelpText = QObject::tr( "double [≥ 0.0]" );
64 : 85 : break;
65 : :
66 : : case Double0To1:
67 : 20 : mTypes = DataTypeNumeric;
68 : 20 : mHelpText = QObject::tr( "double [0.0-1.0]" );
69 : 20 : break;
70 : :
71 : : case Rotation:
72 : 10 : mTypes = DataTypeNumeric;
73 : 10 : mHelpText = QObject::tr( "double [0.0-360.0]" );
74 : 10 : break;
75 : :
76 : : case String:
77 : 25 : mTypes = DataTypeString;
78 : 25 : mHelpText = QObject::tr( "string of variable length" );
79 : 25 : break;
80 : :
81 : : case Opacity:
82 : 9 : mTypes = DataTypeNumeric;
83 : 9 : mHelpText = QObject::tr( "int [0-100]" );
84 : 9 : break;
85 : :
86 : : case RenderUnits:
87 : 0 : mTypes = DataTypeString;
88 : 0 : mHelpText = trString() + QStringLiteral( "[<b>MM</b>|<b>MapUnit</b>|<b>Pixel</b>|<b>Point</b>]" );
89 : 0 : break;
90 : :
91 : : case ColorWithAlpha:
92 : 15 : mTypes = DataTypeString;
93 : 15 : mHelpText = QObject::tr( "string [<b>r,g,b,a</b>] as int 0-255 or #<b>AARRGGBB</b> as hex or <b>color</b> as color's name" );
94 : 15 : break;
95 : :
96 : : case ColorNoAlpha:
97 : 0 : mTypes = DataTypeString;
98 : 0 : mHelpText = QObject::tr( "string [<b>r,g,b</b>] as int 0-255 or #<b>RRGGBB</b> as hex or <b>color</b> as color's name" );
99 : 0 : break;
100 : :
101 : : case PenJoinStyle:
102 : 5 : mTypes = DataTypeString;
103 : 10 : mHelpText = trString() + QStringLiteral( "[<b>bevel</b>|<b>miter</b>|<b>round</b>]" );
104 : 5 : break;
105 : :
106 : : case BlendMode:
107 : 0 : mTypes = DataTypeString;
108 : 0 : mHelpText = trString() + QStringLiteral( "[<b>Normal</b>|<b>Lighten</b>|<b>Screen</b>|<b>Dodge</b>|<br>"
109 : : "<b>Addition</b>|<b>Darken</b>|<b>Multiply</b>|<b>Burn</b>|<b>Overlay</b>|<br>"
110 : : "<b>SoftLight</b>|<b>HardLight</b>|<b>Difference</b>|<b>Subtract</b>]" );
111 : 0 : break;
112 : :
113 : : case Point:
114 : 0 : mTypes = DataTypeString;
115 : 0 : mHelpText = QObject::tr( "double coord [<b>X,Y</b>]" );
116 : 0 : break;
117 : :
118 : : case Size:
119 : 5 : mTypes = DataTypeNumeric;
120 : 5 : mHelpText = QObject::tr( "double [≥ 0.0]" );
121 : 5 : break;
122 : :
123 : : case Size2D:
124 : 0 : mTypes = DataTypeString;
125 : 0 : mHelpText = QObject::tr( "string of doubles '<b>width,height</b>' or array of doubles <b>[width, height]</b>" );
126 : 0 : break;
127 : :
128 : : case LineStyle:
129 : 5 : mTypes = DataTypeString;
130 : 10 : mHelpText = trString() + QStringLiteral( "[<b>no</b>|<b>solid</b>|<b>dash</b>|<b>dot</b>|<b>dash dot</b>|<b>dash dot dot</b>]" );
131 : 5 : break;
132 : :
133 : : case StrokeWidth:
134 : 15 : mTypes = DataTypeNumeric;
135 : 15 : mHelpText = QObject::tr( "double [≥ 0.0]" );
136 : 15 : break;
137 : :
138 : : case FillStyle:
139 : 5 : mTypes = DataTypeString;
140 : 10 : mHelpText = trString() + QStringLiteral( "[<b>solid</b>|<b>horizontal</b>|<b>vertical</b>|<b>cross</b>|<b>b_diagonal</b>|<b>f_diagonal"
141 : : "</b>|<b>diagonal_x</b>|<b>dense1</b>|<b>dense2</b>|<b>dense3</b>|<b>dense4</b>|<b>dense5"
142 : : "</b>|<b>dense6</b>|<b>dense7</b>|<b>no]" );
143 : 5 : break;
144 : :
145 : : case CapStyle:
146 : 5 : mTypes = DataTypeString;
147 : 10 : mHelpText = trString() + QStringLiteral( "[<b>square</b>|<b>flat</b>|<b>round</b>]" );
148 : 5 : break;
149 : :
150 : : case HorizontalAnchor:
151 : 5 : mTypes = DataTypeString;
152 : 10 : mHelpText = trString() + QStringLiteral( "[<b>left</b>|<b>center</b>|<b>right</b>]" );
153 : 5 : break;
154 : :
155 : : case VerticalAnchor:
156 : 5 : mTypes = DataTypeString;
157 : 10 : mHelpText = trString() + QStringLiteral( "[<b>top</b>|<b>center</b>|<b>bottom</b>]" );
158 : 5 : break;
159 : :
160 : : case SvgPath:
161 : 0 : mTypes = DataTypeString;
162 : 0 : mHelpText = trString() + QStringLiteral( "[<b>filepath</b>] as<br>"
163 : : "<b>''</b>=empty|absolute|search-paths-relative|<br>"
164 : : "project-relative|URL" );
165 : 0 : break;
166 : :
167 : : case Offset:
168 : 5 : mTypes = DataTypeString;
169 : 5 : mHelpText = QObject::tr( "string of doubles '<b>x,y</b>' or array of doubles <b>[x, y]</b>" );
170 : 5 : break;
171 : :
172 : : case DateTime:
173 : 0 : mTypes = DataTypeString;
174 : 0 : mHelpText = QObject::tr( "DateTime or string representation of a DateTime" );
175 : 0 : break;
176 : :
177 : : case Custom:
178 : 0 : mTypes = DataTypeString;
179 : 0 : }
180 : 269 : }
181 : :
182 : 45 : QgsPropertyDefinition::QgsPropertyDefinition( const QString &name, DataType dataType, const QString &description, const QString &helpText, const QString &origin, const QString &comment )
183 : 45 : : mName( name )
184 : 45 : , mDescription( description )
185 : 45 : , mTypes( dataType )
186 : 45 : , mHelpText( helpText )
187 : 45 : , mOrigin( origin )
188 : 45 : , mComment( comment )
189 : 45 : {}
190 : :
191 : 0 : bool QgsPropertyDefinition::supportsAssistant() const
192 : : {
193 : 0 : return mTypes == DataTypeNumeric || mStandardType == Size || mStandardType == StrokeWidth || mStandardType == ColorNoAlpha || mStandardType == ColorWithAlpha
194 : 0 : || mStandardType == Rotation;
195 : : }
196 : :
197 : 30 : QString QgsPropertyDefinition::trString()
198 : : {
199 : : // just something to reduce translation redundancy
200 : 30 : return QObject::tr( "string " );
201 : : }
202 : :
203 : : //
204 : : // QgsProperty
205 : : //
206 : :
207 : 269 : QgsProperty::QgsProperty()
208 : : {
209 : 45 : d = new QgsPropertyPrivate();
210 : 0 : }
211 : :
212 : 0 : QgsProperty::~QgsProperty() = default;
213 : :
214 : 0 : QgsProperty QgsProperty::fromExpression( const QString &expression, bool isActive )
215 : : {
216 : 0 : QgsProperty p;
217 : 0 : p.setExpressionString( expression );
218 : 0 : p.setActive( isActive );
219 : 0 : return p;
220 : 0 : }
221 : :
222 : 0 : QgsProperty QgsProperty::fromField( const QString &fieldName, bool isActive )
223 : : {
224 : 0 : QgsProperty p;
225 : 0 : p.setField( fieldName );
226 : 0 : p.setActive( isActive );
227 : 0 : return p;
228 : 0 : }
229 : :
230 : 0 : QgsProperty QgsProperty::fromValue( const QVariant &value, bool isActive )
231 : : {
232 : 0 : QgsProperty p;
233 : 0 : p.setStaticValue( value );
234 : 0 : p.setActive( isActive );
235 : 0 : return p;
236 : 0 : }
237 : :
238 : 0 : QgsProperty::QgsProperty( const QgsProperty &other ) //NOLINT
239 : 0 : : d( other.d )
240 : 0 : {}
241 : :
242 : 0 : QgsProperty &QgsProperty::operator=( const QgsProperty &other ) //NOLINT
243 : : {
244 : 0 : d = other.d;
245 : 0 : return *this;
246 : : }
247 : :
248 : 0 : bool QgsProperty::operator==( const QgsProperty &other ) const
249 : : {
250 : 0 : return d->active == other.d->active
251 : 0 : && d->type == other.d->type
252 : 0 : && ( d->type != StaticProperty || d->staticValue == other.d->staticValue )
253 : 0 : && ( d->type != FieldBasedProperty || d->fieldName == other.d->fieldName )
254 : 0 : && ( d->type != ExpressionBasedProperty || d->expressionString == other.d->expressionString )
255 : 0 : && ( ( !d->transformer && !other.d->transformer ) || ( d->transformer && other.d->transformer && d->transformer->toExpression( QString() ) == other.d->transformer->toExpression( QString() ) ) );
256 : 0 : }
257 : :
258 : 0 : bool QgsProperty::operator!=( const QgsProperty &other ) const
259 : : {
260 : 0 : return ( !( ( *this ) == other ) );
261 : : }
262 : :
263 : 0 : QgsProperty::Type QgsProperty::propertyType() const
264 : : {
265 : 0 : return static_cast< Type >( d->type );
266 : : }
267 : :
268 : 0 : bool QgsProperty::isActive() const
269 : : {
270 : 0 : return d->type != InvalidProperty && d->active;
271 : : }
272 : :
273 : 0 : void QgsProperty::setActive( bool active )
274 : : {
275 : 0 : d.detach();
276 : 0 : d->active = active;
277 : 0 : }
278 : :
279 : 0 : void QgsProperty::setStaticValue( const QVariant &value )
280 : : {
281 : 0 : d.detach();
282 : 0 : d->type = StaticProperty;
283 : 0 : d->staticValue = value;
284 : 0 : }
285 : :
286 : 0 : QVariant QgsProperty::staticValue() const
287 : : {
288 : 0 : if ( d->type != StaticProperty )
289 : 0 : return QVariant();
290 : :
291 : 0 : return d->staticValue;
292 : 0 : }
293 : :
294 : 0 : void QgsProperty::setField( const QString &field )
295 : : {
296 : 0 : d.detach();
297 : 0 : d->type = FieldBasedProperty;
298 : 0 : d->fieldName = field;
299 : 0 : d->cachedFieldIdx = -1;
300 : 0 : }
301 : :
302 : 0 : QString QgsProperty::field() const
303 : : {
304 : 0 : if ( d->type != FieldBasedProperty )
305 : 0 : return QString();
306 : :
307 : 0 : return d->fieldName;
308 : 0 : }
309 : :
310 : 0 : QgsProperty::operator bool() const
311 : : {
312 : 0 : return d->type != InvalidProperty;
313 : : }
314 : :
315 : 0 : void QgsProperty::setExpressionString( const QString &expression )
316 : : {
317 : 0 : d.detach();
318 : 0 : d->type = ExpressionBasedProperty;
319 : 0 : d->expressionString = expression;
320 : 0 : d->expression = QgsExpression( expression );
321 : 0 : d->expressionPrepared = false;
322 : 0 : d->expressionIsInvalid = false;
323 : 0 : }
324 : :
325 : 0 : QString QgsProperty::expressionString() const
326 : : {
327 : 0 : if ( d->type != ExpressionBasedProperty )
328 : 0 : return QString();
329 : :
330 : 0 : return d->expressionString;
331 : 0 : }
332 : :
333 : :
334 : 0 : QString QgsProperty::asExpression() const
335 : : {
336 : 0 : QString exp;
337 : 0 : switch ( d->type )
338 : : {
339 : : case StaticProperty:
340 : 0 : exp = QgsExpression::quotedValue( d->staticValue );
341 : 0 : break;
342 : :
343 : : case FieldBasedProperty:
344 : 0 : exp = QgsExpression::quotedColumnRef( d->fieldName );
345 : 0 : break;
346 : :
347 : : case ExpressionBasedProperty:
348 : 0 : exp = d->expressionString;
349 : 0 : break;
350 : :
351 : : case InvalidProperty:
352 : 0 : exp = QString();
353 : 0 : break;
354 : : }
355 : 0 : return d->transformer ? d->transformer->toExpression( exp ) : exp;
356 : 0 : }
357 : :
358 : 0 : bool QgsProperty::prepare( const QgsExpressionContext &context ) const
359 : : {
360 : 0 : if ( !d->active )
361 : 0 : return true;
362 : :
363 : 0 : switch ( d->type )
364 : : {
365 : : case StaticProperty:
366 : 0 : return true;
367 : :
368 : : case FieldBasedProperty:
369 : : {
370 : 0 : d.detach();
371 : : // cache field index to avoid subsequent lookups
372 : 0 : QgsFields f = context.fields();
373 : 0 : d->cachedFieldIdx = f.lookupField( d->fieldName );
374 : 0 : return true;
375 : 0 : }
376 : :
377 : : case ExpressionBasedProperty:
378 : : {
379 : 0 : d.detach();
380 : 0 : if ( !d->expression.prepare( &context ) )
381 : : {
382 : 0 : d->expressionReferencedCols.clear();
383 : 0 : d->expressionPrepared = false;
384 : 0 : d->expressionIsInvalid = true;
385 : 0 : return false;
386 : : }
387 : :
388 : 0 : d->expressionPrepared = true;
389 : 0 : d->expressionIsInvalid = false;
390 : 0 : d->expressionReferencedCols = d->expression.referencedColumns();
391 : 0 : return true;
392 : : }
393 : :
394 : : case InvalidProperty:
395 : 0 : return true;
396 : :
397 : : }
398 : :
399 : 0 : return false;
400 : 0 : }
401 : :
402 : 0 : QSet<QString> QgsProperty::referencedFields( const QgsExpressionContext &context, bool ignoreContext ) const
403 : : {
404 : 0 : if ( !d->active )
405 : 0 : return QSet<QString>();
406 : :
407 : 0 : switch ( d->type )
408 : : {
409 : : case StaticProperty:
410 : : case InvalidProperty:
411 : 0 : return QSet<QString>();
412 : :
413 : : case FieldBasedProperty:
414 : : {
415 : 0 : QSet< QString > fields;
416 : 0 : if ( !d->fieldName.isEmpty() )
417 : 0 : fields.insert( d->fieldName );
418 : 0 : return fields;
419 : 0 : }
420 : :
421 : : case ExpressionBasedProperty:
422 : : {
423 : 0 : if ( ignoreContext )
424 : : {
425 : 0 : return d->expression.referencedColumns();
426 : : }
427 : :
428 : 0 : if ( d->expressionIsInvalid )
429 : 0 : return QSet< QString >();
430 : :
431 : 0 : d.detach();
432 : 0 : if ( !d->expressionPrepared && !prepare( context ) )
433 : : {
434 : 0 : d->expressionIsInvalid = true;
435 : 0 : return QSet< QString >();
436 : : }
437 : :
438 : 0 : return d->expressionReferencedCols;
439 : : }
440 : :
441 : : }
442 : 0 : return QSet<QString>();
443 : 0 : }
444 : :
445 : 0 : bool QgsProperty::isProjectColor() const
446 : : {
447 : 0 : QRegularExpression rx( QStringLiteral( "^project_color\\('.*'\\)$" ) );
448 : 0 : return d->type == QgsProperty::ExpressionBasedProperty && !d->expressionString.isEmpty()
449 : 0 : && rx.match( d->expressionString ).hasMatch();
450 : 0 : }
451 : :
452 : 0 : QVariant QgsProperty::propertyValue( const QgsExpressionContext &context, const QVariant &defaultValue, bool *ok ) const
453 : : {
454 : 0 : if ( ok )
455 : 0 : *ok = false;
456 : :
457 : 0 : if ( !d->active )
458 : 0 : return defaultValue;
459 : :
460 : 0 : switch ( d->type )
461 : : {
462 : : case StaticProperty:
463 : : {
464 : 0 : if ( ok )
465 : 0 : *ok = true;
466 : 0 : return d->staticValue;
467 : : }
468 : :
469 : : case FieldBasedProperty:
470 : : {
471 : 0 : QgsFeature f = context.feature();
472 : 0 : if ( !f.isValid() )
473 : 0 : return defaultValue;
474 : :
475 : : //shortcut the field lookup
476 : 0 : if ( d->cachedFieldIdx >= 0 )
477 : : {
478 : 0 : if ( ok )
479 : 0 : *ok = true;
480 : 0 : return f.attribute( d->cachedFieldIdx );
481 : : }
482 : 0 : prepare( context );
483 : 0 : if ( d->cachedFieldIdx < 0 )
484 : 0 : return defaultValue;
485 : :
486 : 0 : if ( ok )
487 : 0 : *ok = true;
488 : 0 : return f.attribute( d->cachedFieldIdx );
489 : 0 : }
490 : :
491 : : case ExpressionBasedProperty:
492 : : {
493 : 0 : if ( d->expressionIsInvalid )
494 : 0 : return defaultValue;
495 : :
496 : 0 : if ( !d->expressionPrepared && !prepare( context ) )
497 : 0 : return defaultValue;
498 : :
499 : 0 : QVariant result = d->expression.evaluate( &context );
500 : 0 : if ( !result.isNull() )
501 : : {
502 : 0 : if ( ok )
503 : 0 : *ok = true;
504 : 0 : return result;
505 : : }
506 : : else
507 : : {
508 : 0 : return defaultValue;
509 : : }
510 : 0 : }
511 : :
512 : : case InvalidProperty:
513 : 0 : return defaultValue;
514 : :
515 : : }
516 : :
517 : 0 : return QVariant();
518 : 0 : }
519 : :
520 : :
521 : 0 : QVariant QgsProperty::value( const QgsExpressionContext &context, const QVariant &defaultValue, bool *ok ) const
522 : : {
523 : 0 : if ( ok )
524 : 0 : *ok = false;
525 : :
526 : 0 : bool valOk = false;
527 : 0 : QVariant val = propertyValue( context, defaultValue, &valOk );
528 : 0 : if ( !d->transformer && !valOk ) // if transformer present, let it handle null values
529 : 0 : return defaultValue;
530 : :
531 : 0 : if ( d->transformer )
532 : : {
533 : 0 : if ( !valOk )
534 : 0 : val = QVariant();
535 : 0 : val = d->transformer->transform( context, val );
536 : 0 : }
537 : :
538 : 0 : if ( ok )
539 : 0 : *ok = true;
540 : :
541 : 0 : return val;
542 : 0 : }
543 : :
544 : 0 : QDateTime QgsProperty::valueAsDateTime( const QgsExpressionContext &context, const QDateTime &defaultDateTime, bool *ok ) const
545 : : {
546 : 0 : bool valOk = false;
547 : 0 : QVariant val = value( context, defaultDateTime, &valOk );
548 : :
549 : 0 : if ( !valOk || val.isNull() )
550 : : {
551 : 0 : if ( ok )
552 : 0 : *ok = false;
553 : 0 : return defaultDateTime;
554 : : }
555 : :
556 : 0 : QDateTime dateTime;
557 : 0 : if ( val.type() == QVariant::DateTime )
558 : : {
559 : 0 : dateTime = val.value<QDateTime>();
560 : 0 : }
561 : : else
562 : : {
563 : 0 : dateTime = val.toDateTime();
564 : : }
565 : :
566 : 0 : if ( !dateTime.isValid() )
567 : 0 : return defaultDateTime;
568 : : else
569 : : {
570 : 0 : if ( ok )
571 : 0 : *ok = true;
572 : 0 : return dateTime;
573 : : }
574 : 0 : }
575 : :
576 : 0 : QString QgsProperty::valueAsString( const QgsExpressionContext &context, const QString &defaultString, bool *ok ) const
577 : : {
578 : 0 : bool valOk = false;
579 : 0 : QVariant val = value( context, defaultString, &valOk );
580 : :
581 : 0 : if ( !valOk || val.isNull() )
582 : : {
583 : 0 : if ( ok )
584 : 0 : *ok = false;
585 : 0 : return defaultString;
586 : : }
587 : : else
588 : : {
589 : 0 : if ( ok )
590 : 0 : *ok = true;
591 : 0 : return val.toString();
592 : : }
593 : 0 : }
594 : :
595 : 0 : QColor QgsProperty::valueAsColor( const QgsExpressionContext &context, const QColor &defaultColor, bool *ok ) const
596 : : {
597 : 0 : if ( ok )
598 : 0 : *ok = false;
599 : :
600 : 0 : bool valOk = false;
601 : 0 : QVariant val = value( context, defaultColor, &valOk );
602 : :
603 : 0 : if ( !valOk || val.isNull() )
604 : 0 : return defaultColor;
605 : :
606 : 0 : QColor color;
607 : 0 : if ( val.type() == QVariant::Color )
608 : : {
609 : 0 : color = val.value<QColor>();
610 : 0 : }
611 : : else
612 : : {
613 : 0 : color = QgsSymbolLayerUtils::decodeColor( val.toString() );
614 : : }
615 : :
616 : 0 : if ( !color.isValid() )
617 : 0 : return defaultColor;
618 : : else
619 : : {
620 : 0 : if ( ok )
621 : 0 : *ok = true;
622 : 0 : return color;
623 : : }
624 : 0 : }
625 : :
626 : 0 : double QgsProperty::valueAsDouble( const QgsExpressionContext &context, double defaultValue, bool *ok ) const
627 : : {
628 : 0 : if ( ok )
629 : 0 : *ok = false;
630 : :
631 : 0 : bool valOk = false;
632 : 0 : QVariant val = value( context, defaultValue, &valOk );
633 : :
634 : 0 : if ( !valOk || val.isNull() )
635 : 0 : return defaultValue;
636 : :
637 : 0 : bool convertOk = false;
638 : 0 : double dbl = val.toDouble( &convertOk );
639 : 0 : if ( !convertOk )
640 : 0 : return defaultValue;
641 : : else
642 : : {
643 : 0 : if ( ok )
644 : 0 : *ok = true;
645 : 0 : return dbl;
646 : : }
647 : 0 : }
648 : :
649 : 0 : int QgsProperty::valueAsInt( const QgsExpressionContext &context, int defaultValue, bool *ok ) const
650 : : {
651 : 0 : if ( ok )
652 : 0 : *ok = false;
653 : :
654 : 0 : bool valOk = false;
655 : 0 : QVariant val = value( context, defaultValue, &valOk );
656 : :
657 : 0 : if ( !valOk || val.isNull() )
658 : 0 : return defaultValue;
659 : :
660 : 0 : bool convertOk = false;
661 : 0 : int integer = val.toInt( &convertOk );
662 : 0 : if ( !convertOk )
663 : : {
664 : : //one more option to try
665 : 0 : double dbl = val.toDouble( &convertOk );
666 : 0 : if ( convertOk )
667 : : {
668 : 0 : if ( ok )
669 : 0 : *ok = true;
670 : 0 : return std::round( dbl );
671 : : }
672 : : else
673 : : {
674 : 0 : return defaultValue;
675 : : }
676 : : }
677 : : else
678 : : {
679 : 0 : if ( ok )
680 : 0 : *ok = true;
681 : 0 : return integer;
682 : : }
683 : 0 : }
684 : :
685 : 0 : bool QgsProperty::valueAsBool( const QgsExpressionContext &context, bool defaultValue, bool *ok ) const
686 : : {
687 : 0 : if ( ok )
688 : 0 : *ok = false;
689 : :
690 : 0 : bool valOk = false;
691 : 0 : QVariant val = value( context, defaultValue, &valOk );
692 : :
693 : 0 : if ( !valOk || val.isNull() )
694 : 0 : return defaultValue;
695 : :
696 : 0 : if ( ok )
697 : 0 : *ok = true;
698 : 0 : return val.toBool();
699 : 0 : }
700 : :
701 : 0 : QVariant QgsProperty::toVariant() const
702 : : {
703 : 0 : QVariantMap propertyMap;
704 : :
705 : 0 : propertyMap.insert( QStringLiteral( "active" ), d->active );
706 : 0 : propertyMap.insert( QStringLiteral( "type" ), d->type );
707 : :
708 : 0 : switch ( d->type )
709 : : {
710 : : case StaticProperty:
711 : : // propertyMap.insert( QStringLiteral( "valType" ), d->staticValue.typeName() );
712 : 0 : propertyMap.insert( QStringLiteral( "val" ), d->staticValue.toString() );
713 : 0 : break;
714 : :
715 : : case FieldBasedProperty:
716 : 0 : propertyMap.insert( QStringLiteral( "field" ), d->fieldName );
717 : 0 : break;
718 : :
719 : : case ExpressionBasedProperty:
720 : 0 : propertyMap.insert( QStringLiteral( "expression" ), d->expressionString );
721 : 0 : break;
722 : :
723 : : case InvalidProperty:
724 : 0 : break;
725 : : }
726 : :
727 : 0 : if ( d->transformer )
728 : : {
729 : 0 : QVariantMap transformer;
730 : 0 : transformer.insert( QStringLiteral( "t" ), d->transformer->transformerType() );
731 : 0 : transformer.insert( QStringLiteral( "d" ), d->transformer->toVariant() );
732 : :
733 : 0 : propertyMap.insert( QStringLiteral( "transformer" ), transformer );
734 : 0 : }
735 : :
736 : 0 : return propertyMap;
737 : 0 : }
738 : :
739 : 0 : bool QgsProperty::loadVariant( const QVariant &property )
740 : : {
741 : 0 : QVariantMap propertyMap = property.toMap();
742 : :
743 : 0 : d.detach();
744 : 0 : d->active = propertyMap.value( QStringLiteral( "active" ) ).toBool();
745 : 0 : d->type = static_cast< Type >( propertyMap.value( QStringLiteral( "type" ), InvalidProperty ).toInt() );
746 : :
747 : 0 : switch ( d->type )
748 : : {
749 : : case StaticProperty:
750 : 0 : d->staticValue = propertyMap.value( QStringLiteral( "val" ) );
751 : : // d->staticValue.convert( QVariant::nameToType( propertyElem.attribute( "valType", "QString" ).toLocal8Bit().constData() ) );
752 : 0 : break;
753 : :
754 : : case FieldBasedProperty:
755 : 0 : d->fieldName = propertyMap.value( QStringLiteral( "field" ) ).toString();
756 : 0 : if ( d->fieldName.isEmpty() )
757 : 0 : d->active = false;
758 : 0 : break;
759 : :
760 : : case ExpressionBasedProperty:
761 : 0 : d->expressionString = propertyMap.value( QStringLiteral( "expression" ) ).toString();
762 : 0 : if ( d->expressionString.isEmpty() )
763 : 0 : d->active = false;
764 : :
765 : 0 : d->expression = QgsExpression( d->expressionString );
766 : 0 : d->expressionPrepared = false;
767 : 0 : d->expressionIsInvalid = false;
768 : 0 : d->expressionReferencedCols.clear();
769 : 0 : break;
770 : :
771 : : case InvalidProperty:
772 : 0 : break;
773 : :
774 : : }
775 : :
776 : : //restore transformer if present
777 : 0 : delete d->transformer;
778 : 0 : d->transformer = nullptr;
779 : :
780 : :
781 : 0 : QVariant transform = propertyMap.value( QStringLiteral( "transformer" ) );
782 : :
783 : 0 : if ( transform.isValid() )
784 : : {
785 : 0 : QVariantMap transformerMap = transform.toMap();
786 : :
787 : 0 : QgsPropertyTransformer::Type type = static_cast< QgsPropertyTransformer::Type >( transformerMap.value( QStringLiteral( "t" ), QgsPropertyTransformer::GenericNumericTransformer ).toInt() );
788 : 0 : std::unique_ptr< QgsPropertyTransformer > transformer( QgsPropertyTransformer::create( type ) );
789 : :
790 : 0 : if ( transformer )
791 : : {
792 : 0 : if ( transformer->loadVariant( transformerMap.value( QStringLiteral( "d" ) ) ) )
793 : 0 : d->transformer = transformer.release();
794 : 0 : }
795 : 0 : }
796 : :
797 : : return true;
798 : 0 : }
799 : :
800 : :
801 : 0 : void QgsProperty::setTransformer( QgsPropertyTransformer *transformer )
802 : : {
803 : 0 : d.detach();
804 : 0 : d->transformer = transformer;
805 : 0 : }
806 : :
807 : 0 : const QgsPropertyTransformer *QgsProperty::transformer() const
808 : : {
809 : 0 : return d->transformer;
810 : : }
811 : :
812 : 0 : bool QgsProperty::convertToTransformer()
813 : : {
814 : 0 : if ( d->type != ExpressionBasedProperty )
815 : 0 : return false;
816 : :
817 : 0 : if ( d->transformer )
818 : 0 : return false; // already a transformer
819 : :
820 : 0 : QString baseExpression;
821 : 0 : QString fieldName;
822 : 0 : std::unique_ptr< QgsPropertyTransformer > transformer( QgsPropertyTransformer::fromExpression( d->expressionString, baseExpression, fieldName ) );
823 : 0 : if ( !transformer )
824 : 0 : return false;
825 : :
826 : 0 : d.detach();
827 : 0 : d->transformer = transformer.release();
828 : 0 : if ( !fieldName.isEmpty() )
829 : 0 : setField( fieldName );
830 : : else
831 : 0 : setExpressionString( baseExpression );
832 : 0 : return true;
833 : 0 : }
834 : :
835 : :
836 : :
|