Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsexpressionutils.h 3 : : ------------------- 4 : : begin : May 2017 5 : : copyright : (C) 2017 Matthias Kuhn 6 : : email : matthias@opengis.ch 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 : : 17 : : #ifndef QGSEXPRESSIONUTILS_H 18 : : #define QGSEXPRESSIONUTILS_H 19 : : 20 : : #define SIP_NO_FILE 21 : : 22 : : #include "qgsfeature.h" 23 : : #include "qgsexpression.h" 24 : : #include "qgscolorramp.h" 25 : : #include "qgsvectorlayerfeatureiterator.h" 26 : : #include "qgsrasterlayer.h" 27 : : #include "qgsproject.h" 28 : : #include "qgsrelationmanager.h" 29 : : #include "qgsvectorlayer.h" 30 : : 31 : : #include <QThread> 32 : : 33 : : #define ENSURE_NO_EVAL_ERROR { if ( parent->hasEvalError() ) return QVariant(); } 34 : : #define SET_EVAL_ERROR(x) { parent->setEvalErrorString( x ); return QVariant(); } 35 : : 36 : : #define FEAT_FROM_CONTEXT(c, f) if ( !(c) || !( c )->hasFeature() ) return QVariant(); \ 37 : : QgsFeature f = ( c )->feature(); 38 : : 39 : : /////////////////////////////////////////////// 40 : : // three-value logic 41 : : 42 : : /// @cond PRIVATE 43 : : class QgsExpressionUtils 44 : : { 45 : : public: 46 : : enum TVL 47 : : { 48 : : False, 49 : : True, 50 : : Unknown 51 : : }; 52 : : 53 : : 54 : : static TVL AND[3][3]; 55 : : 56 : : static TVL OR[3][3]; 57 : : 58 : : static TVL NOT[3]; 59 : : 60 : : #define TVL_True QVariant( 1 ) 61 : : #define TVL_False QVariant( 0 ) 62 : : #define TVL_Unknown QVariant() 63 : : 64 : 0 : static QVariant tvl2variant( TVL v ) 65 : : { 66 : 0 : switch ( v ) 67 : : { 68 : : case False: 69 : 0 : return TVL_False; 70 : : case True: 71 : 0 : return TVL_True; 72 : : case Unknown: 73 : : default: 74 : 0 : return TVL_Unknown; 75 : : } 76 : 0 : } 77 : : 78 : : // this handles also NULL values 79 : 0 : static TVL getTVLValue( const QVariant &value, QgsExpression *parent ) 80 : : { 81 : : // we need to convert to TVL 82 : 0 : if ( value.isNull() ) 83 : 0 : return Unknown; 84 : : 85 : : //handle some special cases 86 : 0 : if ( value.canConvert<QgsGeometry>() ) 87 : : { 88 : : //geom is false if empty 89 : 0 : QgsGeometry geom = value.value<QgsGeometry>(); 90 : 0 : return geom.isNull() ? False : True; 91 : 0 : } 92 : 0 : else if ( value.canConvert<QgsFeature>() ) 93 : : { 94 : : //feat is false if non-valid 95 : 0 : QgsFeature feat = value.value<QgsFeature>(); 96 : 0 : return feat.isValid() ? True : False; 97 : 0 : } 98 : : 99 : 0 : if ( value.type() == QVariant::Int ) 100 : 0 : return value.toInt() != 0 ? True : False; 101 : : 102 : : bool ok; 103 : 0 : double x = value.toDouble( &ok ); 104 : 0 : if ( !ok ) 105 : : { 106 : 0 : parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to boolean" ).arg( value.toString() ) ); 107 : 0 : return Unknown; 108 : : } 109 : 0 : return !qgsDoubleNear( x, 0.0 ) ? True : False; 110 : 0 : } 111 : : 112 : : 113 : 0 : static inline bool isIntSafe( const QVariant &v ) 114 : : { 115 : 0 : if ( v.type() == QVariant::Int ) 116 : 0 : return true; 117 : 0 : if ( v.type() == QVariant::UInt ) 118 : 0 : return true; 119 : 0 : if ( v.type() == QVariant::LongLong ) 120 : 0 : return true; 121 : 0 : if ( v.type() == QVariant::ULongLong ) 122 : 0 : return true; 123 : 0 : if ( v.type() == QVariant::Double ) 124 : 0 : return false; 125 : 0 : if ( v.type() == QVariant::String ) 126 : : { 127 : : bool ok; 128 : 0 : v.toString().toInt( &ok ); 129 : 0 : return ok; 130 : : } 131 : 0 : return false; 132 : 0 : } 133 : : 134 : 0 : static inline bool isDoubleSafe( const QVariant &v ) 135 : : { 136 : 0 : if ( v.type() == QVariant::Double ) 137 : 0 : return true; 138 : 0 : if ( v.type() == QVariant::Int ) 139 : 0 : return true; 140 : 0 : if ( v.type() == QVariant::UInt ) 141 : 0 : return true; 142 : 0 : if ( v.type() == QVariant::LongLong ) 143 : 0 : return true; 144 : 0 : if ( v.type() == QVariant::ULongLong ) 145 : 0 : return true; 146 : 0 : if ( v.type() == QVariant::String ) 147 : : { 148 : : bool ok; 149 : 0 : double val = v.toString().toDouble( &ok ); 150 : 0 : ok = ok && std::isfinite( val ) && !std::isnan( val ); 151 : 0 : return ok; 152 : : } 153 : 0 : return false; 154 : 0 : } 155 : : 156 : 0 : static inline bool isDateTimeSafe( const QVariant &v ) 157 : : { 158 : 0 : return v.type() == QVariant::DateTime 159 : 0 : || v.type() == QVariant::Date 160 : 0 : || v.type() == QVariant::Time; 161 : : } 162 : : 163 : 0 : static inline bool isIntervalSafe( const QVariant &v ) 164 : : { 165 : 0 : if ( v.canConvert<QgsInterval>() ) 166 : : { 167 : 0 : return true; 168 : : } 169 : : 170 : 0 : if ( v.type() == QVariant::String ) 171 : : { 172 : 0 : return QgsInterval::fromString( v.toString() ).isValid(); 173 : : } 174 : 0 : return false; 175 : 0 : } 176 : : 177 : 0 : static inline bool isNull( const QVariant &v ) 178 : : { 179 : 0 : return v.isNull(); 180 : : } 181 : : 182 : 0 : static inline bool isList( const QVariant &v ) 183 : : { 184 : 0 : return v.type() == QVariant::List; 185 : : } 186 : : 187 : : // implicit conversion to string 188 : 0 : static QString getStringValue( const QVariant &value, QgsExpression * ) 189 : : { 190 : 0 : return value.toString(); 191 : : } 192 : : 193 : : /** 194 : : * Returns an expression value converted to binary (byte array) value. 195 : : * 196 : : * An empty byte array will be returned if the value is NULL. 197 : : * 198 : : * \since QGIS 3.12 199 : : */ 200 : 0 : static QByteArray getBinaryValue( const QVariant &value, QgsExpression *parent ) 201 : : { 202 : 0 : if ( value.type() != QVariant::ByteArray ) 203 : : { 204 : 0 : parent->setEvalErrorString( QObject::tr( "Value is not a binary value" ) ); 205 : 0 : return QByteArray(); 206 : : } 207 : 0 : return value.toByteArray(); 208 : 0 : } 209 : : 210 : 0 : static double getDoubleValue( const QVariant &value, QgsExpression *parent ) 211 : : { 212 : : bool ok; 213 : 0 : double x = value.toDouble( &ok ); 214 : 0 : if ( !ok || std::isnan( x ) || !std::isfinite( x ) ) 215 : : { 216 : 0 : parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to double" ).arg( value.toString() ) ); 217 : 0 : return 0; 218 : : } 219 : 0 : return x; 220 : 0 : } 221 : : 222 : 0 : static qlonglong getIntValue( const QVariant &value, QgsExpression *parent ) 223 : : { 224 : : bool ok; 225 : 0 : qlonglong x = value.toLongLong( &ok ); 226 : 0 : if ( ok ) 227 : : { 228 : 0 : return x; 229 : : } 230 : : else 231 : : { 232 : 0 : parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to int" ).arg( value.toString() ) ); 233 : 0 : return 0; 234 : : } 235 : 0 : } 236 : : 237 : 0 : static int getNativeIntValue( const QVariant &value, QgsExpression *parent ) 238 : : { 239 : : bool ok; 240 : 0 : qlonglong x = value.toLongLong( &ok ); 241 : 0 : if ( ok && x >= std::numeric_limits<int>::min() && x <= std::numeric_limits<int>::max() ) 242 : : { 243 : 0 : return static_cast<int>( x ); 244 : : } 245 : : else 246 : : { 247 : 0 : parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to native int" ).arg( value.toString() ) ); 248 : 0 : return 0; 249 : : } 250 : 0 : } 251 : : 252 : 0 : static QDateTime getDateTimeValue( const QVariant &value, QgsExpression *parent ) 253 : : { 254 : 0 : QDateTime d = value.toDateTime(); 255 : 0 : if ( d.isValid() ) 256 : : { 257 : 0 : return d; 258 : : } 259 : : else 260 : : { 261 : 0 : QTime t = value.toTime(); 262 : 0 : if ( t.isValid() ) 263 : : { 264 : 0 : return QDateTime( QDate( 1, 1, 1 ), t ); 265 : : } 266 : : 267 : 0 : parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( value.toString() ) ); 268 : 0 : return QDateTime(); 269 : : } 270 : 0 : } 271 : : 272 : 0 : static QDate getDateValue( const QVariant &value, QgsExpression *parent ) 273 : : { 274 : 0 : QDate d = value.toDate(); 275 : 0 : if ( d.isValid() ) 276 : : { 277 : 0 : return d; 278 : : } 279 : : else 280 : : { 281 : 0 : parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( value.toString() ) ); 282 : 0 : return QDate(); 283 : : } 284 : 0 : } 285 : : 286 : 0 : static QTime getTimeValue( const QVariant &value, QgsExpression *parent ) 287 : : { 288 : 0 : QTime t = value.toTime(); 289 : 0 : if ( t.isValid() ) 290 : : { 291 : 0 : return t; 292 : : } 293 : : else 294 : : { 295 : 0 : parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( value.toString() ) ); 296 : 0 : return QTime(); 297 : : } 298 : 0 : } 299 : : 300 : 0 : static QgsInterval getInterval( const QVariant &value, QgsExpression *parent, bool report_error = false ) 301 : : { 302 : 0 : if ( value.canConvert<QgsInterval>() ) 303 : 0 : return value.value<QgsInterval>(); 304 : : 305 : 0 : QgsInterval inter = QgsInterval::fromString( value.toString() ); 306 : 0 : if ( inter.isValid() ) 307 : : { 308 : 0 : return inter; 309 : : } 310 : : // If we get here then we can't convert so we just error and return invalid. 311 : 0 : if ( report_error ) 312 : 0 : parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to interval" ).arg( value.toString() ) ); 313 : : 314 : 0 : return QgsInterval(); 315 : 0 : } 316 : : 317 : 0 : static QgsGradientColorRamp getRamp( const QVariant &value, QgsExpression *parent, bool report_error = false ) 318 : : { 319 : 0 : if ( value.canConvert<QgsGradientColorRamp>() ) 320 : 0 : return value.value<QgsGradientColorRamp>(); 321 : : 322 : : // If we get here then we can't convert so we just error and return invalid. 323 : 0 : if ( report_error ) 324 : 0 : parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to gradient ramp" ).arg( value.toString() ) ); 325 : : 326 : 0 : return QgsGradientColorRamp(); 327 : 0 : } 328 : : 329 : 0 : static QgsGeometry getGeometry( const QVariant &value, QgsExpression *parent ) 330 : : { 331 : 0 : if ( value.canConvert<QgsGeometry>() ) 332 : 0 : return value.value<QgsGeometry>(); 333 : : 334 : 0 : parent->setEvalErrorString( QStringLiteral( "Cannot convert to geometry" ) ); 335 : 0 : return QgsGeometry(); 336 : 0 : } 337 : : 338 : 0 : static QgsFeature getFeature( const QVariant &value, QgsExpression *parent ) 339 : : { 340 : 0 : if ( value.canConvert<QgsFeature>() ) 341 : 0 : return value.value<QgsFeature>(); 342 : : 343 : 0 : parent->setEvalErrorString( QStringLiteral( "Cannot convert to feature" ) ); 344 : 0 : return 0; 345 : 0 : } 346 : : 347 : 0 : static QgsExpressionNode *getNode( const QVariant &value, QgsExpression *parent ) 348 : : { 349 : 0 : if ( value.canConvert<QgsExpressionNode *>() ) 350 : 0 : return value.value<QgsExpressionNode *>(); 351 : : 352 : 0 : parent->setEvalErrorString( QStringLiteral( "Cannot convert to node" ) ); 353 : 0 : return nullptr; 354 : 0 : } 355 : : 356 : 0 : static QgsMapLayer *getMapLayer( const QVariant &value, QgsExpression * ) 357 : : { 358 : : // First check if we already received a layer pointer 359 : 0 : QgsMapLayer *ml = value.value< QgsWeakMapLayerPointer >().data(); 360 : 0 : QgsProject *project = QgsProject::instance(); 361 : : 362 : : // No pointer yet, maybe it's a layer id? 363 : 0 : if ( !ml ) 364 : 0 : ml = project->mapLayer( value.toString() ); 365 : : 366 : : // Still nothing? Check for layer name 367 : 0 : if ( !ml ) 368 : 0 : ml = project->mapLayersByName( value.toString() ).value( 0 ); 369 : : 370 : 0 : return ml; 371 : 0 : } 372 : : 373 : 0 : static std::unique_ptr<QgsVectorLayerFeatureSource> getFeatureSource( const QVariant &value, QgsExpression *e ) 374 : : { 375 : 0 : std::unique_ptr<QgsVectorLayerFeatureSource> featureSource; 376 : : 377 : 0 : auto getFeatureSource = [ &value, e, &featureSource ] 378 : : { 379 : 0 : QgsVectorLayer *layer = getVectorLayer( value, e ); 380 : : 381 : 0 : if ( layer ) 382 : : { 383 : 0 : featureSource.reset( new QgsVectorLayerFeatureSource( layer ) ); 384 : 0 : } 385 : 0 : }; 386 : : 387 : : // Make sure we only deal with the vector layer on the main thread where it lives. 388 : : // Anything else risks a crash. 389 : 0 : if ( QThread::currentThread() == qApp->thread() ) 390 : 0 : getFeatureSource(); 391 : : else 392 : 0 : QMetaObject::invokeMethod( qApp, getFeatureSource, Qt::BlockingQueuedConnection ); 393 : : 394 : 0 : return featureSource; 395 : 0 : } 396 : : 397 : 0 : static QgsVectorLayer *getVectorLayer( const QVariant &value, QgsExpression *e ) 398 : : { 399 : 0 : return qobject_cast<QgsVectorLayer *>( getMapLayer( value, e ) ); 400 : : } 401 : : 402 : 0 : static QgsRasterLayer *getRasterLayer( const QVariant &value, QgsExpression *e ) 403 : : { 404 : 0 : return qobject_cast<QgsRasterLayer *>( getMapLayer( value, e ) ); 405 : : } 406 : : 407 : 0 : static QVariantList getListValue( const QVariant &value, QgsExpression *parent ) 408 : : { 409 : 0 : if ( value.type() == QVariant::List || value.type() == QVariant::StringList ) 410 : : { 411 : 0 : return value.toList(); 412 : : } 413 : : else 414 : : { 415 : 0 : parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to array" ).arg( value.toString() ) ); 416 : 0 : return QVariantList(); 417 : : } 418 : 0 : } 419 : : 420 : 0 : static QVariantMap getMapValue( const QVariant &value, QgsExpression *parent ) 421 : : { 422 : 0 : if ( value.type() == QVariant::Map ) 423 : : { 424 : 0 : return value.toMap(); 425 : : } 426 : : else 427 : : { 428 : 0 : parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to map" ).arg( value.toString() ) ); 429 : 0 : return QVariantMap(); 430 : : } 431 : 0 : } 432 : : }; 433 : : 434 : : /// @endcond 435 : : 436 : : #endif // QGSEXPRESSIONUTILS_H