QGIS API Documentation  3.3.0-Master (77eafc0)
qgsexpression.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsexpression.cpp
3  -------------------
4  begin : August 2011
5  copyright : (C) 2011 Martin Dobias
6  email : wonder.sk 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 "qgsexpression.h"
17 #include "qgsexpressionfunction.h"
18 #include "qgsexpressionprivate.h"
19 #include "qgsexpressionnodeimpl.h"
20 #include "qgsfeaturerequest.h"
21 #include "qgscolorramp.h"
22 #include "qgslogger.h"
23 #include "qgsexpressioncontext.h"
24 #include "qgsgeometry.h"
25 #include "qgsproject.h"
26 
27 
28 // from parser
29 extern QgsExpressionNode *parseExpression( const QString &str, QString &parserErrorMsg, QList<QgsExpression::ParserError> &parserErrors );
30 
32 // QVariant checks and conversions
33 
35 // evaluation error macros
36 
38 // functions
39 
41 
42 bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
43 {
44  int fnIdx = functionIndex( function->name() );
45  if ( fnIdx != -1 )
46  {
47  return false;
48  }
49  QgsExpression::sFunctions.append( function );
50  if ( transferOwnership )
51  QgsExpression::sOwnedFunctions.append( function );
52  return true;
53 }
54 
55 bool QgsExpression::unregisterFunction( const QString &name )
56 {
57  // You can never override the built in functions.
58  if ( QgsExpression::BuiltinFunctions().contains( name ) )
59  {
60  return false;
61  }
62  int fnIdx = functionIndex( name );
63  if ( fnIdx != -1 )
64  {
65  QgsExpression::sFunctions.removeAt( fnIdx );
66  return true;
67  }
68  return false;
69 }
70 
72 {
73  qDeleteAll( QgsExpression::sOwnedFunctions );
75 }
76 
78 
79 const QStringList &QgsExpression::BuiltinFunctions()
80 {
81  if ( sBuiltinFunctions.isEmpty() )
82  {
83  Functions(); // this method builds the gmBuiltinFunctions as well
84  }
85  return sBuiltinFunctions;
86 }
87 
88 QList<QgsExpressionFunction *> QgsExpression::sFunctions;
89 QList<QgsExpressionFunction *> QgsExpression::sOwnedFunctions;
90 
91 bool QgsExpression::checkExpression( const QString &text, const QgsExpressionContext *context, QString &errorMessage )
92 {
93  QgsExpression exp( text );
94  exp.prepare( context );
95  errorMessage = exp.parserErrorString();
96  return !exp.hasParserError();
97 }
98 
100 {
101  detach();
102  d->mRootNode = ::parseExpression( expression, d->mParserErrorString, d->mParserErrors );
103  d->mEvalErrorString = QString();
104  d->mExp = expression;
105 }
106 
108 {
109  if ( !d->mExp.isNull() )
110  return d->mExp;
111  else
112  return dump();
113 }
114 
115 QString QgsExpression::quotedColumnRef( QString name )
116 {
117  return QStringLiteral( "\"%1\"" ).arg( name.replace( '\"', QLatin1String( "\"\"" ) ) );
118 }
119 
120 QString QgsExpression::quotedString( QString text )
121 {
122  text.replace( '\'', QLatin1String( "''" ) );
123  text.replace( '\\', QLatin1String( "\\\\" ) );
124  text.replace( '\n', QLatin1String( "\\n" ) );
125  text.replace( '\t', QLatin1String( "\\t" ) );
126  return QStringLiteral( "'%1'" ).arg( text );
127 }
128 
129 QString QgsExpression::quotedValue( const QVariant &value )
130 {
131  return quotedValue( value, value.type() );
132 }
133 
134 QString QgsExpression::quotedValue( const QVariant &value, QVariant::Type type )
135 {
136  if ( value.isNull() )
137  return QStringLiteral( "NULL" );
138 
139  switch ( type )
140  {
141  case QVariant::Int:
142  case QVariant::LongLong:
143  case QVariant::Double:
144  return value.toString();
145 
146  case QVariant::Bool:
147  return value.toBool() ? QStringLiteral( "TRUE" ) : QStringLiteral( "FALSE" );
148 
149  case QVariant::List:
150  {
151  QStringList quotedValues;
152  const QVariantList values = value.toList();
153  for ( const QVariant &v : values )
154  {
155  quotedValues += quotedValue( v );
156  }
157  return QStringLiteral( "array( %1 )" ).arg( quotedValues.join( QStringLiteral( ", " ) ) );
158  }
159 
160  default:
161  case QVariant::String:
162  return quotedString( value.toString() );
163  }
164 
165 }
166 
167 bool QgsExpression::isFunctionName( const QString &name )
168 {
169  return functionIndex( name ) != -1;
170 }
171 
172 int QgsExpression::functionIndex( const QString &name )
173 {
174  int count = functionCount();
175  for ( int i = 0; i < count; i++ )
176  {
177  if ( QString::compare( name, QgsExpression::Functions()[i]->name(), Qt::CaseInsensitive ) == 0 )
178  return i;
179  const QStringList aliases = QgsExpression::Functions()[i]->aliases();
180  for ( const QString &alias : aliases )
181  {
182  if ( QString::compare( name, alias, Qt::CaseInsensitive ) == 0 )
183  return i;
184  }
185  }
186  return -1;
187 }
188 
190 {
191  return Functions().size();
192 }
193 
194 
195 QgsExpression::QgsExpression( const QString &expr )
196  : d( new QgsExpressionPrivate )
197 {
198  d->mRootNode = ::parseExpression( expr, d->mParserErrorString, d->mParserErrors );
199  d->mExp = expr;
200  Q_ASSERT( !d->mParserErrorString.isNull() || d->mRootNode );
201 }
202 
204  : d( other.d )
205 {
206  d->ref.ref();
207 }
208 
210 {
211  if ( !d->ref.deref() )
212  {
213  delete d;
214  }
215 
216  d = other.d;
217  d->ref.ref();
218  return *this;
219 }
220 
221 QgsExpression::operator QString() const
222 {
223  return d->mExp;
224 }
225 
227  : d( new QgsExpressionPrivate )
228 {
229 }
230 
232 {
233  Q_ASSERT( d );
234  if ( !d->ref.deref() )
235  delete d;
236 }
237 
238 bool QgsExpression::operator==( const QgsExpression &other ) const
239 {
240  return ( d == other.d || d->mExp == other.d->mExp );
241 }
242 
244 {
245  return d->mRootNode;
246 }
247 
249 {
250  return d->mParserErrors.count() > 0;
251 }
252 
254 {
255  return d->mParserErrorString;
256 }
257 
258 QList<QgsExpression::ParserError> QgsExpression::parserErrors() const
259 {
260  return d->mParserErrors;
261 }
262 
263 QSet<QString> QgsExpression::referencedColumns() const
264 {
265  if ( !d->mRootNode )
266  return QSet<QString>();
267 
268  return d->mRootNode->referencedColumns();
269 }
270 
272 {
273  if ( !d->mRootNode )
274  return QSet<QString>();
275 
276  return d->mRootNode->referencedVariables();
277 }
278 
280 {
281  if ( !d->mRootNode )
282  return QSet<QString>();
283 
284  return d->mRootNode->referencedFunctions();
285 }
286 
287 QSet<int> QgsExpression::referencedAttributeIndexes( const QgsFields &fields ) const
288 {
289  if ( !d->mRootNode )
290  return QSet<int>();
291 
292  const QSet<QString> referencedFields = d->mRootNode->referencedColumns();
293  QSet<int> referencedIndexes;
294 
295  for ( const QString &fieldName : referencedFields )
296  {
297  if ( fieldName == QgsFeatureRequest::ALL_ATTRIBUTES )
298  {
299  referencedIndexes = fields.allAttributesList().toSet();
300  break;
301  }
302  referencedIndexes << fields.lookupField( fieldName );
303  }
304 
305  return referencedIndexes;
306 }
307 
309 {
310  if ( !d->mRootNode )
311  return false;
312  return d->mRootNode->needsGeometry();
313 }
314 
315 void QgsExpression::initGeomCalculator()
316 {
317  if ( d->mCalc )
318  return;
319 
320  // Use planimetric as default
321  d->mCalc = std::shared_ptr<QgsDistanceArea>( new QgsDistanceArea() );
322 }
323 
324 void QgsExpression::detach()
325 {
326  Q_ASSERT( d );
327 
328  if ( d->ref > 1 )
329  {
330  ( void )d->ref.deref();
331 
332  d = new QgsExpressionPrivate( *d );
333  }
334 }
335 
337 {
338  detach();
339  if ( calc )
340  d->mCalc = std::shared_ptr<QgsDistanceArea>( new QgsDistanceArea( *calc ) );
341  else
342  d->mCalc.reset();
343 }
344 
346 {
347  detach();
348  d->mEvalErrorString = QString();
349  if ( !d->mRootNode )
350  {
351  //re-parse expression. Creation of QgsExpressionContexts may have added extra
352  //known functions since this expression was created, so we have another try
353  //at re-parsing it now that the context must have been created
354  d->mRootNode = ::parseExpression( d->mExp, d->mParserErrorString, d->mParserErrors );
355  }
356 
357  if ( !d->mRootNode )
358  {
359  d->mEvalErrorString = tr( "No root node! Parsing failed?" );
360  return false;
361  }
362 
363  return d->mRootNode->prepare( this, context );
364 }
365 
367 {
368  d->mEvalErrorString = QString();
369  if ( !d->mRootNode )
370  {
371  d->mEvalErrorString = tr( "No root node! Parsing failed?" );
372  return QVariant();
373  }
374 
375  return d->mRootNode->eval( this, static_cast<const QgsExpressionContext *>( nullptr ) );
376 }
377 
379 {
380  d->mEvalErrorString = QString();
381  if ( !d->mRootNode )
382  {
383  d->mEvalErrorString = tr( "No root node! Parsing failed?" );
384  return QVariant();
385  }
386 
387  return d->mRootNode->eval( this, context );
388 }
389 
391 {
392  return !d->mEvalErrorString.isNull();
393 }
394 
396 {
397  return d->mEvalErrorString;
398 }
399 
400 void QgsExpression::setEvalErrorString( const QString &str )
401 {
402  d->mEvalErrorString = str;
403 }
404 
405 QString QgsExpression::dump() const
406 {
407  if ( !d->mRootNode )
408  return QString();
409 
410  return d->mRootNode->dump();
411 }
412 
414 {
415  return d->mCalc.get();
416 }
417 
419 {
420  return d->mDistanceUnit;
421 }
422 
424 {
425  d->mDistanceUnit = unit;
426 }
427 
429 {
430  return d->mAreaUnit;
431 }
432 
434 {
435  d->mAreaUnit = unit;
436 }
437 
438 QString QgsExpression::replaceExpressionText( const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea )
439 {
440  QString expr_action;
441 
442  int index = 0;
443  while ( index < action.size() )
444  {
445  QRegExp rx = QRegExp( "\\[%([^\\]]+)%\\]" );
446 
447  int pos = rx.indexIn( action, index );
448  if ( pos < 0 )
449  break;
450 
451  int start = index;
452  index = pos + rx.matchedLength();
453  QString to_replace = rx.cap( 1 ).trimmed();
454  QgsDebugMsg( "Found expression: " + to_replace );
455 
456  QgsExpression exp( to_replace );
457  if ( exp.hasParserError() )
458  {
459  QgsDebugMsg( "Expression parser error: " + exp.parserErrorString() );
460  expr_action += action.midRef( start, index - start );
461  continue;
462  }
463 
464  if ( distanceArea )
465  {
466  //if QgsDistanceArea specified for area/distance conversion, use it
467  exp.setGeomCalculator( distanceArea );
468  }
469 
470  QVariant result = exp.evaluate( context );
471 
472  if ( exp.hasEvalError() )
473  {
474  QgsDebugMsg( "Expression parser eval error: " + exp.evalErrorString() );
475  expr_action += action.midRef( start, index - start );
476  continue;
477  }
478 
479  QgsDebugMsg( "Expression result is: " + result.toString() );
480  expr_action += action.mid( start, pos - start ) + result.toString();
481  }
482 
483  expr_action += action.midRef( index );
484 
485  return expr_action;
486 }
487 
488 QSet<QString> QgsExpression::referencedVariables( const QString &text )
489 {
490  QSet<QString> variables;
491  int index = 0;
492  while ( index < text.size() )
493  {
494  QRegExp rx = QRegExp( "\\[%([^\\]]+)%\\]" );
495 
496  int pos = rx.indexIn( text, index );
497  if ( pos < 0 )
498  break;
499 
500  index = pos + rx.matchedLength();
501  QString to_replace = rx.cap( 1 ).trimmed();
502 
503  QgsExpression exp( to_replace );
504  variables.unite( exp.referencedVariables() );
505  }
506 
507  return variables;
508 }
509 
510 double QgsExpression::evaluateToDouble( const QString &text, const double fallbackValue )
511 {
512  bool ok;
513  //first test if text is directly convertible to double
514  // use system locale: e.g. in German locale, user is presented with numbers "1,23" instead of "1.23" in C locale
515  // so we also want to allow user to rewrite it to "5,23" and it is still accepted
516  double convertedValue = QLocale().toDouble( text, &ok );
517  if ( ok )
518  {
519  return convertedValue;
520  }
521 
522  //otherwise try to evaluate as expression
523  QgsExpression expr( text );
524 
525  QgsExpressionContext context;
528 
529  QVariant result = expr.evaluate( &context );
530  convertedValue = result.toDouble( &ok );
531  if ( expr.hasEvalError() || !ok )
532  {
533  return fallbackValue;
534  }
535  return convertedValue;
536 }
537 
538 
539 
540 QString QgsExpression::helpText( QString name )
541 {
542  QgsExpression::initFunctionHelp();
543 
544  if ( !sFunctionHelpTexts.contains( name ) )
545  return tr( "function help for %1 missing" ).arg( name );
546 
547  const Help &f = sFunctionHelpTexts[ name ];
548 
549  name = f.mName;
550  if ( f.mType == tr( "group" ) )
551  name = group( name );
552 
553  name = name.toHtmlEscaped();
554 
555  QString helpContents( QStringLiteral( "<h3>%1</h3>\n<div class=\"description\"><p>%2</p></div>" )
556  .arg( tr( "%1 %2" ).arg( f.mType, name ),
557  f.mDescription ) );
558 
559  for ( const HelpVariant &v : qgis::as_const( f.mVariants ) )
560  {
561  if ( f.mVariants.size() > 1 )
562  {
563  helpContents += QStringLiteral( "<h3>%1</h3>\n<div class=\"description\">%2</p></div>" ).arg( v.mName, v.mDescription );
564  }
565 
566  if ( f.mType != tr( "group" ) && f.mType != tr( "expression" ) )
567  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"syntax\">\n" ).arg( tr( "Syntax" ) );
568 
569  if ( f.mType == tr( "operator" ) )
570  {
571  if ( v.mArguments.size() == 1 )
572  {
573  helpContents += QStringLiteral( "<code><span class=\"functionname\">%1</span> <span class=\"argument\">%2</span></code>" )
574  .arg( name, v.mArguments[0].mArg );
575  }
576  else if ( v.mArguments.size() == 2 )
577  {
578  helpContents += QStringLiteral( "<code><span class=\"argument\">%1</span> <span class=\"functionname\">%2</span> <span class=\"argument\">%3</span></code>" )
579  .arg( v.mArguments[0].mArg, name, v.mArguments[1].mArg );
580  }
581  }
582  else if ( f.mType != tr( "group" ) && f.mType != tr( "expression" ) )
583  {
584  helpContents += QStringLiteral( "<code><span class=\"functionname\">%1</span>" ).arg( name );
585 
586  bool hasOptionalArgs = false;
587 
588  if ( f.mType == tr( "function" ) && ( f.mName[0] != '$' || !v.mArguments.isEmpty() || v.mVariableLenArguments ) )
589  {
590  helpContents += '(';
591 
592  QString delim;
593  for ( const HelpArg &a : qgis::as_const( v.mArguments ) )
594  {
595  if ( !a.mDescOnly )
596  {
597  if ( a.mOptional )
598  {
599  hasOptionalArgs = true;
600  helpContents += QStringLiteral( "[" );
601  }
602 
603  helpContents += delim;
604  helpContents += QStringLiteral( "<span class=\"argument\">%2%3</span>" ).arg(
605  a.mArg,
606  a.mDefaultVal.isEmpty() ? QLatin1String( "" ) : '=' + a.mDefaultVal
607  );
608 
609  if ( a.mOptional )
610  helpContents += QStringLiteral( "]" );
611  }
612  delim = QStringLiteral( "," );
613  }
614 
615  if ( v.mVariableLenArguments )
616  {
617  helpContents += QChar( 0x2026 );
618  }
619 
620  helpContents += ')';
621  }
622 
623  helpContents += QLatin1String( "</code>" );
624 
625  if ( hasOptionalArgs )
626  {
627  helpContents += QLatin1String( "<br/><br/>" ) + tr( "[ ] marks optional components" );
628  }
629  }
630 
631  if ( !v.mArguments.isEmpty() )
632  {
633  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"arguments\">\n<table>" ).arg( tr( "Arguments" ) );
634 
635  for ( const HelpArg &a : qgis::as_const( v.mArguments ) )
636  {
637  if ( a.mSyntaxOnly )
638  continue;
639 
640  helpContents += QStringLiteral( "<tr><td class=\"argument\">%1</td><td>%2</td></tr>" ).arg( a.mArg, a.mDescription );
641  }
642 
643  helpContents += QLatin1String( "</table>\n</div>\n" );
644  }
645 
646  if ( !v.mExamples.isEmpty() )
647  {
648  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"examples\">\n<ul>\n" ).arg( tr( "Examples" ) );
649 
650  for ( const HelpExample &e : qgis::as_const( v.mExamples ) )
651  {
652  helpContents += "<li><code>" + e.mExpression + "</code> &rarr; <code>" + e.mReturns + "</code>";
653 
654  if ( !e.mNote.isEmpty() )
655  helpContents += QStringLiteral( " (%1)" ).arg( e.mNote );
656 
657  helpContents += QLatin1String( "</li>\n" );
658  }
659 
660  helpContents += QLatin1String( "</ul>\n</div>\n" );
661  }
662 
663  if ( !v.mNotes.isEmpty() )
664  {
665  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"notes\"><p>%2</p></div>\n" ).arg( tr( "Notes" ), v.mNotes );
666  }
667  }
668 
669  return helpContents;
670 }
671 
672 QHash<QString, QString> QgsExpression::sVariableHelpTexts;
673 
674 void QgsExpression::initVariableHelp()
675 {
676  if ( !sVariableHelpTexts.isEmpty() )
677  return;
678 
679  //global variables
680  sVariableHelpTexts.insert( QStringLiteral( "qgis_version" ), QCoreApplication::translate( "variable_help", "Current QGIS version string." ) );
681  sVariableHelpTexts.insert( QStringLiteral( "qgis_version_no" ), QCoreApplication::translate( "variable_help", "Current QGIS version number." ) );
682  sVariableHelpTexts.insert( QStringLiteral( "qgis_release_name" ), QCoreApplication::translate( "variable_help", "Current QGIS release name." ) );
683  sVariableHelpTexts.insert( QStringLiteral( "qgis_os_name" ), QCoreApplication::translate( "variable_help", "Operating system name, e.g., 'windows', 'linux' or 'osx'." ) );
684  sVariableHelpTexts.insert( QStringLiteral( "qgis_platform" ), QCoreApplication::translate( "variable_help", "QGIS platform, e.g., 'desktop' or 'server'." ) );
685  sVariableHelpTexts.insert( QStringLiteral( "user_account_name" ), QCoreApplication::translate( "variable_help", "Current user's operating system account name." ) );
686  sVariableHelpTexts.insert( QStringLiteral( "user_full_name" ), QCoreApplication::translate( "variable_help", "Current user's operating system user name (if available)." ) );
687 
688  //project variables
689  sVariableHelpTexts.insert( QStringLiteral( "project_title" ), QCoreApplication::translate( "variable_help", "Title of current project." ) );
690  sVariableHelpTexts.insert( QStringLiteral( "project_path" ), QCoreApplication::translate( "variable_help", "Full path (including file name) of current project." ) );
691  sVariableHelpTexts.insert( QStringLiteral( "project_folder" ), QCoreApplication::translate( "variable_help", "Folder for current project." ) );
692  sVariableHelpTexts.insert( QStringLiteral( "project_filename" ), QCoreApplication::translate( "variable_help", "Filename of current project." ) );
693  sVariableHelpTexts.insert( QStringLiteral( "project_basename" ), QCoreApplication::translate( "variable_help", "Base name of current project's filename (without path and extension)." ) );
694  sVariableHelpTexts.insert( QStringLiteral( "project_home" ), QCoreApplication::translate( "variable_help", "Home path of current project." ) );
695  sVariableHelpTexts.insert( QStringLiteral( "project_crs" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of project (e.g., 'EPSG:4326')." ) );
696  sVariableHelpTexts.insert( QStringLiteral( "project_crs_definition" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of project (full definition)." ) );
697  sVariableHelpTexts.insert( QStringLiteral( "project_author" ), QCoreApplication::translate( "variable_help", "Project author, taken from project metadata." ) );
698  sVariableHelpTexts.insert( QStringLiteral( "project_abstract" ), QCoreApplication::translate( "variable_help", "Project abstract, taken from project metadata." ) );
699  sVariableHelpTexts.insert( QStringLiteral( "project_creation_date" ), QCoreApplication::translate( "variable_help", "Project creation date, taken from project metadata." ) );
700  sVariableHelpTexts.insert( QStringLiteral( "project_identifier" ), QCoreApplication::translate( "variable_help", "Project identifier, taken from project metadata." ) );
701  sVariableHelpTexts.insert( QStringLiteral( "project_keywords" ), QCoreApplication::translate( "variable_help", "Project keywords, taken from project metadata." ) );
702 
703  //layer variables
704  sVariableHelpTexts.insert( QStringLiteral( "layer_name" ), QCoreApplication::translate( "variable_help", "Name of current layer." ) );
705  sVariableHelpTexts.insert( QStringLiteral( "layer_id" ), QCoreApplication::translate( "variable_help", "ID of current layer." ) );
706  sVariableHelpTexts.insert( QStringLiteral( "layer" ), QCoreApplication::translate( "variable_help", "The current layer." ) );
707 
708  //composition variables
709  sVariableHelpTexts.insert( QStringLiteral( "layout_name" ), QCoreApplication::translate( "variable_help", "Name of composition." ) );
710  sVariableHelpTexts.insert( QStringLiteral( "layout_numpages" ), QCoreApplication::translate( "variable_help", "Number of pages in composition." ) );
711  sVariableHelpTexts.insert( QStringLiteral( "layout_page" ), QCoreApplication::translate( "variable_help", "Current page number in composition." ) );
712  sVariableHelpTexts.insert( QStringLiteral( "layout_pageheight" ), QCoreApplication::translate( "variable_help", "Composition page height in mm." ) );
713  sVariableHelpTexts.insert( QStringLiteral( "layout_pagewidth" ), QCoreApplication::translate( "variable_help", "Composition page width in mm." ) );
714  sVariableHelpTexts.insert( QStringLiteral( "layout_dpi" ), QCoreApplication::translate( "variable_help", "Composition resolution (DPI)." ) );
715 
716  //atlas variables
717  sVariableHelpTexts.insert( QStringLiteral( "atlas_layerid" ), QCoreApplication::translate( "variable_help", "Current atlas coverage layer ID." ) );
718  sVariableHelpTexts.insert( QStringLiteral( "atlas_layername" ), QCoreApplication::translate( "variable_help", "Current atlas coverage layer name." ) );
719  sVariableHelpTexts.insert( QStringLiteral( "atlas_totalfeatures" ), QCoreApplication::translate( "variable_help", "Total number of features in atlas." ) );
720  sVariableHelpTexts.insert( QStringLiteral( "atlas_featurenumber" ), QCoreApplication::translate( "variable_help", "Current atlas feature number." ) );
721  sVariableHelpTexts.insert( QStringLiteral( "atlas_filename" ), QCoreApplication::translate( "variable_help", "Current atlas file name." ) );
722  sVariableHelpTexts.insert( QStringLiteral( "atlas_pagename" ), QCoreApplication::translate( "variable_help", "Current atlas page name." ) );
723  sVariableHelpTexts.insert( QStringLiteral( "atlas_feature" ), QCoreApplication::translate( "variable_help", "Current atlas feature (as feature object)." ) );
724  sVariableHelpTexts.insert( QStringLiteral( "atlas_featureid" ), QCoreApplication::translate( "variable_help", "Current atlas feature ID." ) );
725  sVariableHelpTexts.insert( QStringLiteral( "atlas_geometry" ), QCoreApplication::translate( "variable_help", "Current atlas feature geometry." ) );
726 
727  //layout item variables
728  sVariableHelpTexts.insert( QStringLiteral( "item_id" ), QCoreApplication::translate( "variable_help", "Layout item user ID (not necessarily unique)." ) );
729  sVariableHelpTexts.insert( QStringLiteral( "item_uuid" ), QCoreApplication::translate( "variable_help", "layout item unique ID." ) );
730  sVariableHelpTexts.insert( QStringLiteral( "item_left" ), QCoreApplication::translate( "variable_help", "Left position of layout item (in mm)." ) );
731  sVariableHelpTexts.insert( QStringLiteral( "item_top" ), QCoreApplication::translate( "variable_help", "Top position of layout item (in mm)." ) );
732  sVariableHelpTexts.insert( QStringLiteral( "item_width" ), QCoreApplication::translate( "variable_help", "Width of layout item (in mm)." ) );
733  sVariableHelpTexts.insert( QStringLiteral( "item_height" ), QCoreApplication::translate( "variable_help", "Height of layout item (in mm)." ) );
734 
735  //map settings item variables
736  sVariableHelpTexts.insert( QStringLiteral( "map_id" ), QCoreApplication::translate( "variable_help", "ID of current map destination. This will be 'canvas' for canvas renders, and the item ID for layout map renders." ) );
737  sVariableHelpTexts.insert( QStringLiteral( "map_rotation" ), QCoreApplication::translate( "variable_help", "Current rotation of map." ) );
738  sVariableHelpTexts.insert( QStringLiteral( "map_scale" ), QCoreApplication::translate( "variable_help", "Current scale of map." ) );
739  sVariableHelpTexts.insert( QStringLiteral( "map_extent" ), QCoreApplication::translate( "variable_help", "Geometry representing the current extent of the map." ) );
740  sVariableHelpTexts.insert( QStringLiteral( "map_extent_center" ), QCoreApplication::translate( "variable_help", "Center of map." ) );
741  sVariableHelpTexts.insert( QStringLiteral( "map_extent_width" ), QCoreApplication::translate( "variable_help", "Width of map." ) );
742  sVariableHelpTexts.insert( QStringLiteral( "map_extent_height" ), QCoreApplication::translate( "variable_help", "Height of map." ) );
743  sVariableHelpTexts.insert( QStringLiteral( "map_crs" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of map (e.g., 'EPSG:4326')." ) );
744  sVariableHelpTexts.insert( QStringLiteral( "map_crs_definition" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of map (full definition)." ) );
745  sVariableHelpTexts.insert( QStringLiteral( "map_units" ), QCoreApplication::translate( "variable_help", "Units for map measurements." ) );
746 
747  sVariableHelpTexts.insert( QStringLiteral( "row_number" ), QCoreApplication::translate( "variable_help", "Stores the number of the current row." ) );
748  sVariableHelpTexts.insert( QStringLiteral( "grid_number" ), QCoreApplication::translate( "variable_help", "Current grid annotation value." ) );
749  sVariableHelpTexts.insert( QStringLiteral( "grid_axis" ), QCoreApplication::translate( "variable_help", "Current grid annotation axis (e.g., 'x' for longitude, 'y' for latitude)." ) );
750 
751  // map canvas item variables
752  sVariableHelpTexts.insert( QStringLiteral( "canvas_cursor_point" ), QCoreApplication::translate( "variable_help", "Last cursor position on the canvas in the project's geographical coordinates." ) );
753 
754  // map tool capture variables
755  sVariableHelpTexts.insert( QStringLiteral( "snapping_results" ), QCoreApplication::translate( "variable_help",
756  "<p>An array with an item for each snapped point.</p>"
757  "<p>Each item is a map with the following keys:</p>"
758  "<dl>"
759  "<dt>valid</dt><dd>Boolean that indicates if the snapping result is valid</dd>"
760  "<dt>layer</dt><dd>The layer on which the snapped feature is</dd>"
761  "<dt>feature_id</dt><dd>The feature id of the snapped feature</dd>"
762  "<dt>vertex_index</dt><dd>The index of the snapped vertex</dd>"
763  "<dt>distance</dt><dd>The distance between the mouse cursor and the snapped point at the time of snapping</dd>"
764  "</dl>" ) );
765 
766 
767  //symbol variables
768  sVariableHelpTexts.insert( QStringLiteral( "geometry_part_count" ), QCoreApplication::translate( "variable_help", "Number of parts in rendered feature's geometry." ) );
769  sVariableHelpTexts.insert( QStringLiteral( "geometry_part_num" ), QCoreApplication::translate( "variable_help", "Current geometry part number for feature being rendered." ) );
770  sVariableHelpTexts.insert( QStringLiteral( "geometry_point_count" ), QCoreApplication::translate( "variable_help", "Number of points in the rendered geometry's part. It is only meaningful for line geometries and for symbol layers that set this variable." ) );
771  sVariableHelpTexts.insert( QStringLiteral( "geometry_point_num" ), QCoreApplication::translate( "variable_help", "Current point number in the rendered geometry's part. It is only meaningful for line geometries and for symbol layers that set this variable." ) );
772 
773  sVariableHelpTexts.insert( QStringLiteral( "symbol_color" ), QCoreApplication::translate( "symbol_color", "Color of symbol used to render the feature." ) );
774  sVariableHelpTexts.insert( QStringLiteral( "symbol_angle" ), QCoreApplication::translate( "symbol_angle", "Angle of symbol used to render the feature (valid for marker symbols only)." ) );
775 
776  //cluster variables
777  sVariableHelpTexts.insert( QStringLiteral( "cluster_color" ), QCoreApplication::translate( "cluster_color", "Color of symbols within a cluster, or NULL if symbols have mixed colors." ) );
778  sVariableHelpTexts.insert( QStringLiteral( "cluster_size" ), QCoreApplication::translate( "cluster_size", "Number of symbols contained within a cluster." ) );
779 
780  //processing variables
781  sVariableHelpTexts.insert( QStringLiteral( "algorithm_id" ), QCoreApplication::translate( "algorithm_id", "Unique ID for algorithm." ) );
782 
783  //provider notification
784  sVariableHelpTexts.insert( QStringLiteral( "notification_message" ), QCoreApplication::translate( "notification_message", "Content of the notification message sent by the provider (available only for actions triggered by provider notifications)." ) );
785 
786  //form context variable
787  sVariableHelpTexts.insert( QStringLiteral( "current_geometry" ), QCoreApplication::translate( "current_geometry", "Represents the geometry of the feature currently being edited in the form or the table row. Can be used in a form/row context to filter the related features." ) );
788  sVariableHelpTexts.insert( QStringLiteral( "current_feature" ), QCoreApplication::translate( "current_feature", "Represents the feature currently being edited in the form or the table row. Can be used in a form/row context to filter the related features." ) );
789 }
790 
791 QString QgsExpression::variableHelpText( const QString &variableName )
792 {
793  QgsExpression::initVariableHelp();
794  return sVariableHelpTexts.value( variableName, QString() );
795 }
796 
797 QString QgsExpression::formatVariableHelp( const QString &description, bool showValue, const QVariant &value )
798 {
799  QString text = !description.isEmpty() ? QStringLiteral( "<p>%1</p>" ).arg( description ) : QString();
800  if ( showValue )
801  {
802  QString valueString;
803  if ( !value.isValid() )
804  {
805  valueString = QCoreApplication::translate( "variable_help", "not set" );
806  }
807  else
808  {
809  valueString = QStringLiteral( "<b>%1</b>" ).arg( formatPreviewString( value ) );
810  }
811  text.append( QCoreApplication::translate( "variable_help", "<p>Current value: %1</p>" ).arg( valueString ) );
812  }
813  return text;
814 }
815 
816 QHash<QString, QString> QgsExpression::sGroups;
817 
818 QString QgsExpression::group( const QString &name )
819 {
820  if ( sGroups.isEmpty() )
821  {
822  sGroups.insert( QStringLiteral( "General" ), tr( "General" ) );
823  sGroups.insert( QStringLiteral( "Operators" ), tr( "Operators" ) );
824  sGroups.insert( QStringLiteral( "Conditionals" ), tr( "Conditionals" ) );
825  sGroups.insert( QStringLiteral( "Fields and Values" ), tr( "Fields and Values" ) );
826  sGroups.insert( QStringLiteral( "Math" ), tr( "Math" ) );
827  sGroups.insert( QStringLiteral( "Conversions" ), tr( "Conversions" ) );
828  sGroups.insert( QStringLiteral( "Date and Time" ), tr( "Date and Time" ) );
829  sGroups.insert( QStringLiteral( "String" ), tr( "String" ) );
830  sGroups.insert( QStringLiteral( "Color" ), tr( "Color" ) );
831  sGroups.insert( QStringLiteral( "GeometryGroup" ), tr( "Geometry" ) );
832  sGroups.insert( QStringLiteral( "Record" ), tr( "Record" ) );
833  sGroups.insert( QStringLiteral( "Variables" ), tr( "Variables" ) );
834  sGroups.insert( QStringLiteral( "Fuzzy Matching" ), tr( "Fuzzy Matching" ) );
835  sGroups.insert( QStringLiteral( "Recent (%1)" ), tr( "Recent (%1)" ) );
836  }
837 
838  //return the translated name for this group. If group does not
839  //have a translated name in the gGroups hash, return the name
840  //unchanged
841  return sGroups.value( name, name );
842 }
843 
844 QString QgsExpression::formatPreviewString( const QVariant &value )
845 {
846  static const int MAX_PREVIEW = 60;
847 
848  if ( value.canConvert<QgsGeometry>() )
849  {
850  //result is a geometry
851  QgsGeometry geom = value.value<QgsGeometry>();
852  if ( geom.isNull() )
853  return tr( "<i>&lt;empty geometry&gt;</i>" );
854  else
855  return tr( "<i>&lt;geometry: %1&gt;</i>" ).arg( QgsWkbTypes::displayString( geom.constGet()->wkbType() ) );
856  }
857  else if ( !value.isValid() )
858  {
859  return tr( "<i>NULL</i>" );
860  }
861  else if ( value.canConvert< QgsFeature >() )
862  {
863  //result is a feature
864  QgsFeature feat = value.value<QgsFeature>();
865  return tr( "<i>&lt;feature: %1&gt;</i>" ).arg( feat.id() );
866  }
867  else if ( value.canConvert< QgsInterval >() )
868  {
869  //result is a feature
870  QgsInterval interval = value.value<QgsInterval>();
871  return tr( "<i>&lt;interval: %1 days&gt;</i>" ).arg( interval.days() );
872  }
873  else if ( value.canConvert< QgsGradientColorRamp >() )
874  {
875  return tr( "<i>&lt;gradient ramp&gt;</i>" );
876  }
877  else if ( value.type() == QVariant::Date )
878  {
879  QDate dt = value.toDate();
880  return tr( "<i>&lt;date: %1&gt;</i>" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd" ) ) );
881  }
882  else if ( value.type() == QVariant::Time )
883  {
884  QTime tm = value.toTime();
885  return tr( "<i>&lt;time: %1&gt;</i>" ).arg( tm.toString( QStringLiteral( "hh:mm:ss" ) ) );
886  }
887  else if ( value.type() == QVariant::DateTime )
888  {
889  QDateTime dt = value.toDateTime();
890  return tr( "<i>&lt;datetime: %1&gt;</i>" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd hh:mm:ss" ) ) );
891  }
892  else if ( value.type() == QVariant::String )
893  {
894  QString previewString = value.toString();
895  if ( previewString.length() > MAX_PREVIEW + 3 )
896  {
897  return tr( "'%1…'" ).arg( previewString.left( MAX_PREVIEW ) );
898  }
899  else
900  {
901  return previewString.prepend( '\'' ).append( '\'' );
902  }
903  }
904  else if ( value.type() == QVariant::Map )
905  {
906  QString mapStr;
907  const QVariantMap map = value.toMap();
908  for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
909  {
910  if ( !mapStr.isEmpty() ) mapStr.append( ", " );
911  mapStr.append( it.key() ).append( ": " ).append( formatPreviewString( it.value() ) );
912  if ( mapStr.length() > MAX_PREVIEW + 3 )
913  {
914  mapStr = QString( tr( "%1…" ) ).arg( mapStr.left( MAX_PREVIEW ) );
915  break;
916  }
917  }
918  return tr( "<i>&lt;map: %1&gt;</i>" ).arg( mapStr );
919  }
920  else if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
921  {
922  QString listStr;
923  const QVariantList list = value.toList();
924  for ( QVariantList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
925  {
926  if ( !listStr.isEmpty() ) listStr.append( ", " );
927  listStr.append( formatPreviewString( *it ) );
928  if ( listStr.length() > MAX_PREVIEW + 3 )
929  {
930  listStr = QString( tr( "%1…" ) ).arg( listStr.left( MAX_PREVIEW ) );
931  break;
932  }
933  }
934  return tr( "<i>&lt;array: %1&gt;</i>" ).arg( listStr );
935  }
936  else
937  {
938  return value.toString();
939  }
940 }
941 
942 QString QgsExpression::createFieldEqualityExpression( const QString &fieldName, const QVariant &value )
943 {
944  QString expr;
945 
946  if ( value.isNull() )
947  expr = QStringLiteral( "%1 IS NULL" ).arg( quotedColumnRef( fieldName ) );
948  else
949  expr = QStringLiteral( "%1 = %2" ).arg( quotedColumnRef( fieldName ), quotedValue( value ) );
950 
951  return expr;
952 }
953 
955 {
956  return d->mRootNode;
957 }
958 
960 {
961  return d->mRootNode && d->mRootNode->nodeType() == QgsExpressionNode::ntColumnRef;
962 }
963 
964 QList<const QgsExpressionNode *> QgsExpression::nodes() const
965 {
966  if ( !d->mRootNode )
967  return QList<const QgsExpressionNode *>();
968 
969  return d->mRootNode->nodes();
970 }
971 
972 
973 
int lookupField(const QString &fieldName) const
Look up field&#39;s index from the field name.
Definition: qgsfields.cpp:299
void setAreaUnits(QgsUnitTypes::AreaUnit unit)
Sets the desired areal units for calculations involving geomCalculator(), e.g., "$area".
static bool isFunctionName(const QString &name)
tells whether the identifier is a name of existing function
Class for parsing and evaluation of expressions (formerly called "search strings").
QgsFeatureId id
Definition: qgsfeature.h:71
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
static QList< QgsExpressionFunction * > sOwnedFunctions
List of functions owned by the expression engine.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
bool isNull() const
Returns true if the geometry is null (ie, contains no underlying geometry accessible via geometry() )...
void setExpression(const QString &expression)
Set the expression string, will reset the whole internal structure.
static QString group(const QString &group)
Returns the translated name for a function group.
bool operator==(const QgsExpression &other) const
Compares two expressions.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QString dump() const
Returns an expression string, constructed from the internal abstract syntax tree. ...
QVariant evaluate()
Evaluate the feature and return the result.
QgsExpression()
Create an empty expression.
static double evaluateToDouble(const QString &text, double fallbackValue)
Attempts to evaluate a text string as an expression to a resultant double value.
QgsExpressionNode * parseExpression(const QString &str, QString &parserErrorMsg, QList< QgsExpression::ParserError > &parserErrors)
QString evalErrorString() const
Returns evaluation error.
Container of fields for a vector layer.
Definition: qgsfields.h:42
QSet< int > referencedAttributeIndexes(const QgsFields &fields) const
Returns a list of field name indexes obtained from the provided fields.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:104
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QList< QgsExpression::ParserError > parserErrors() const
Returns parser error details including location of error.
static QString variableHelpText(const QString &variableName)
Returns the help text for a specified variable.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
static QString formatPreviewString(const QVariant &value)
Formats an expression result for friendly display to the user.
QString parserErrorString() const
Returns parser error.
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
static QStringList sBuiltinFunctions
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions)
static const QStringList & BuiltinFunctions()
static bool unregisterFunction(const QString &name)
Unregisters a function from the expression engine.
QgsUnitTypes::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g., "$area".
static int functionCount()
Returns the number of functions defined in the parser.
static bool checkExpression(const QString &text, const QgsExpressionContext *context, QString &errorMessage)
Tests whether a string is a valid expression.
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QgsAttributeList allAttributesList() const
Utility function to get list of attribute indexes.
Definition: qgsfields.cpp:326
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
bool isValid() const
Checks if this expression is valid.
static void cleanRegisteredFunctions()
Deletes all registered functions whose ownership have been transferred to the expression engine...
QgsExpression & operator=(const QgsExpression &other)
Create a copy of this expression.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static bool registerFunction(QgsExpressionFunction *function, bool transferOwnership=false)
Registers a function to the expression engine.
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
Abstract base class for all nodes that can appear in an expression.
static QList< QgsExpressionFunction * > sFunctions
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
static const QList< QgsExpressionFunction * > & Functions()
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
static QString formatVariableHelp(const QString &description, bool showValue=true, const QVariant &value=QVariant())
Returns formatted help text for a variable.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsUnitTypes::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e.g., "$length" and "$perimeter".
QString expression() const
Returns the original, unmodified expression string.
A representation of the interval between two datetime values.
Definition: qgsinterval.h:39
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:53
double days() const
Returns the interval duration in days.
Definition: qgsinterval.h:112
A general purpose distance and area calculator, capable of performing ellipsoid based calculations...
A abstract base class for defining QgsExpression functions.
void setGeomCalculator(const QgsDistanceArea *calc)
Sets the geometry calculator used for distance and area calculations in expressions.
const QgsExpressionNode * rootNode() const
Returns root node of the expression. Root node is null is parsing has failed.
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
void setDistanceUnits(QgsUnitTypes::DistanceUnit unit)
Sets the desired distance units for calculations involving geomCalculator(), e.g., "$length" and "$perimeter".
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:391
bool isField() const
Checks whether an expression consists only of a single field reference.
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes)
static QString displayString(Type type)
Returns a display string type for a WKB type, e.g., the geometry name used in WKT geometry representa...
static QString helpText(QString name)
Returns the help text for a specified function.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required...
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorramp.h:139
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
QList< const QgsExpressionNode * > nodes() const
Returns a list of all nodes which are used in this expression.
AreaUnit
Units of area.
Definition: qgsunittypes.h:79
QSet< QString > referencedFunctions() const
Returns a list of the names of all functions which are used in this expression.
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...