QGIS API Documentation  3.3.0-Master (208571a)
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  {
552  name = group( name );
553  name = name.toLower();
554  }
555 
556  name = name.toHtmlEscaped();
557 
558  QString helpContents( QStringLiteral( "<h3>%1</h3>\n<div class=\"description\"><p>%2</p></div>" )
559  .arg( tr( "%1 %2" ).arg( f.mType, name ),
560  f.mDescription ) );
561 
562  for ( const HelpVariant &v : qgis::as_const( f.mVariants ) )
563  {
564  if ( f.mVariants.size() > 1 )
565  {
566  helpContents += QStringLiteral( "<h3>%1</h3>\n<div class=\"description\">%2</p></div>" ).arg( v.mName, v.mDescription );
567  }
568 
569  if ( f.mType != tr( "group" ) && f.mType != tr( "expression" ) )
570  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"syntax\">\n" ).arg( tr( "Syntax" ) );
571 
572  if ( f.mType == tr( "operator" ) )
573  {
574  if ( v.mArguments.size() == 1 )
575  {
576  helpContents += QStringLiteral( "<code><span class=\"functionname\">%1</span> <span class=\"argument\">%2</span></code>" )
577  .arg( name, v.mArguments[0].mArg );
578  }
579  else if ( v.mArguments.size() == 2 )
580  {
581  helpContents += QStringLiteral( "<code><span class=\"argument\">%1</span> <span class=\"functionname\">%2</span> <span class=\"argument\">%3</span></code>" )
582  .arg( v.mArguments[0].mArg, name, v.mArguments[1].mArg );
583  }
584  }
585  else if ( f.mType != tr( "group" ) && f.mType != tr( "expression" ) )
586  {
587  helpContents += QStringLiteral( "<code><span class=\"functionname\">%1</span>" ).arg( name );
588 
589  bool hasOptionalArgs = false;
590 
591  if ( f.mType == tr( "function" ) && ( f.mName[0] != '$' || !v.mArguments.isEmpty() || v.mVariableLenArguments ) )
592  {
593  helpContents += '(';
594 
595  QString delim;
596  for ( const HelpArg &a : qgis::as_const( v.mArguments ) )
597  {
598  if ( !a.mDescOnly )
599  {
600  if ( a.mOptional )
601  {
602  hasOptionalArgs = true;
603  helpContents += QStringLiteral( "[" );
604  }
605 
606  helpContents += delim;
607  helpContents += QStringLiteral( "<span class=\"argument\">%2%3</span>" ).arg(
608  a.mArg,
609  a.mDefaultVal.isEmpty() ? QString() : '=' + a.mDefaultVal
610  );
611 
612  if ( a.mOptional )
613  helpContents += QStringLiteral( "]" );
614  }
615  delim = QStringLiteral( "," );
616  }
617 
618  if ( v.mVariableLenArguments )
619  {
620  helpContents += QChar( 0x2026 );
621  }
622 
623  helpContents += ')';
624  }
625 
626  helpContents += QLatin1String( "</code>" );
627 
628  if ( hasOptionalArgs )
629  {
630  helpContents += QLatin1String( "<br/><br/>" ) + tr( "[ ] marks optional components" );
631  }
632  }
633 
634  if ( !v.mArguments.isEmpty() )
635  {
636  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"arguments\">\n<table>" ).arg( tr( "Arguments" ) );
637 
638  for ( const HelpArg &a : qgis::as_const( v.mArguments ) )
639  {
640  if ( a.mSyntaxOnly )
641  continue;
642 
643  helpContents += QStringLiteral( "<tr><td class=\"argument\">%1</td><td>%2</td></tr>" ).arg( a.mArg, a.mDescription );
644  }
645 
646  helpContents += QLatin1String( "</table>\n</div>\n" );
647  }
648 
649  if ( !v.mExamples.isEmpty() )
650  {
651  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"examples\">\n<ul>\n" ).arg( tr( "Examples" ) );
652 
653  for ( const HelpExample &e : qgis::as_const( v.mExamples ) )
654  {
655  helpContents += "<li><code>" + e.mExpression + "</code> &rarr; <code>" + e.mReturns + "</code>";
656 
657  if ( !e.mNote.isEmpty() )
658  helpContents += QStringLiteral( " (%1)" ).arg( e.mNote );
659 
660  helpContents += QLatin1String( "</li>\n" );
661  }
662 
663  helpContents += QLatin1String( "</ul>\n</div>\n" );
664  }
665 
666  if ( !v.mNotes.isEmpty() )
667  {
668  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"notes\"><p>%2</p></div>\n" ).arg( tr( "Notes" ), v.mNotes );
669  }
670  }
671 
672  return helpContents;
673 }
674 
675 QHash<QString, QString> QgsExpression::sVariableHelpTexts;
676 
677 void QgsExpression::initVariableHelp()
678 {
679  if ( !sVariableHelpTexts.isEmpty() )
680  return;
681 
682  //global variables
683  sVariableHelpTexts.insert( QStringLiteral( "qgis_version" ), QCoreApplication::translate( "variable_help", "Current QGIS version string." ) );
684  sVariableHelpTexts.insert( QStringLiteral( "qgis_version_no" ), QCoreApplication::translate( "variable_help", "Current QGIS version number." ) );
685  sVariableHelpTexts.insert( QStringLiteral( "qgis_release_name" ), QCoreApplication::translate( "variable_help", "Current QGIS release name." ) );
686  sVariableHelpTexts.insert( QStringLiteral( "qgis_os_name" ), QCoreApplication::translate( "variable_help", "Operating system name, e.g., 'windows', 'linux' or 'osx'." ) );
687  sVariableHelpTexts.insert( QStringLiteral( "qgis_platform" ), QCoreApplication::translate( "variable_help", "QGIS platform, e.g., 'desktop' or 'server'." ) );
688  sVariableHelpTexts.insert( QStringLiteral( "user_account_name" ), QCoreApplication::translate( "variable_help", "Current user's operating system account name." ) );
689  sVariableHelpTexts.insert( QStringLiteral( "user_full_name" ), QCoreApplication::translate( "variable_help", "Current user's operating system user name (if available)." ) );
690 
691  //project variables
692  sVariableHelpTexts.insert( QStringLiteral( "project_title" ), QCoreApplication::translate( "variable_help", "Title of current project." ) );
693  sVariableHelpTexts.insert( QStringLiteral( "project_path" ), QCoreApplication::translate( "variable_help", "Full path (including file name) of current project." ) );
694  sVariableHelpTexts.insert( QStringLiteral( "project_folder" ), QCoreApplication::translate( "variable_help", "Folder for current project." ) );
695  sVariableHelpTexts.insert( QStringLiteral( "project_filename" ), QCoreApplication::translate( "variable_help", "Filename of current project." ) );
696  sVariableHelpTexts.insert( QStringLiteral( "project_basename" ), QCoreApplication::translate( "variable_help", "Base name of current project's filename (without path and extension)." ) );
697  sVariableHelpTexts.insert( QStringLiteral( "project_home" ), QCoreApplication::translate( "variable_help", "Home path of current project." ) );
698  sVariableHelpTexts.insert( QStringLiteral( "project_crs" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of project (e.g., 'EPSG:4326')." ) );
699  sVariableHelpTexts.insert( QStringLiteral( "project_crs_definition" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of project (full definition)." ) );
700  sVariableHelpTexts.insert( QStringLiteral( "project_author" ), QCoreApplication::translate( "variable_help", "Project author, taken from project metadata." ) );
701  sVariableHelpTexts.insert( QStringLiteral( "project_abstract" ), QCoreApplication::translate( "variable_help", "Project abstract, taken from project metadata." ) );
702  sVariableHelpTexts.insert( QStringLiteral( "project_creation_date" ), QCoreApplication::translate( "variable_help", "Project creation date, taken from project metadata." ) );
703  sVariableHelpTexts.insert( QStringLiteral( "project_identifier" ), QCoreApplication::translate( "variable_help", "Project identifier, taken from project metadata." ) );
704  sVariableHelpTexts.insert( QStringLiteral( "project_keywords" ), QCoreApplication::translate( "variable_help", "Project keywords, taken from project metadata." ) );
705 
706  //layer variables
707  sVariableHelpTexts.insert( QStringLiteral( "layer_name" ), QCoreApplication::translate( "variable_help", "Name of current layer." ) );
708  sVariableHelpTexts.insert( QStringLiteral( "layer_id" ), QCoreApplication::translate( "variable_help", "ID of current layer." ) );
709  sVariableHelpTexts.insert( QStringLiteral( "layer" ), QCoreApplication::translate( "variable_help", "The current layer." ) );
710 
711  //composition variables
712  sVariableHelpTexts.insert( QStringLiteral( "layout_name" ), QCoreApplication::translate( "variable_help", "Name of composition." ) );
713  sVariableHelpTexts.insert( QStringLiteral( "layout_numpages" ), QCoreApplication::translate( "variable_help", "Number of pages in composition." ) );
714  sVariableHelpTexts.insert( QStringLiteral( "layout_page" ), QCoreApplication::translate( "variable_help", "Current page number in composition." ) );
715  sVariableHelpTexts.insert( QStringLiteral( "layout_pageheight" ), QCoreApplication::translate( "variable_help", "Composition page height in mm." ) );
716  sVariableHelpTexts.insert( QStringLiteral( "layout_pagewidth" ), QCoreApplication::translate( "variable_help", "Composition page width in mm." ) );
717  sVariableHelpTexts.insert( QStringLiteral( "layout_dpi" ), QCoreApplication::translate( "variable_help", "Composition resolution (DPI)." ) );
718 
719  //atlas variables
720  sVariableHelpTexts.insert( QStringLiteral( "atlas_layerid" ), QCoreApplication::translate( "variable_help", "Current atlas coverage layer ID." ) );
721  sVariableHelpTexts.insert( QStringLiteral( "atlas_layername" ), QCoreApplication::translate( "variable_help", "Current atlas coverage layer name." ) );
722  sVariableHelpTexts.insert( QStringLiteral( "atlas_totalfeatures" ), QCoreApplication::translate( "variable_help", "Total number of features in atlas." ) );
723  sVariableHelpTexts.insert( QStringLiteral( "atlas_featurenumber" ), QCoreApplication::translate( "variable_help", "Current atlas feature number." ) );
724  sVariableHelpTexts.insert( QStringLiteral( "atlas_filename" ), QCoreApplication::translate( "variable_help", "Current atlas file name." ) );
725  sVariableHelpTexts.insert( QStringLiteral( "atlas_pagename" ), QCoreApplication::translate( "variable_help", "Current atlas page name." ) );
726  sVariableHelpTexts.insert( QStringLiteral( "atlas_feature" ), QCoreApplication::translate( "variable_help", "Current atlas feature (as feature object)." ) );
727  sVariableHelpTexts.insert( QStringLiteral( "atlas_featureid" ), QCoreApplication::translate( "variable_help", "Current atlas feature ID." ) );
728  sVariableHelpTexts.insert( QStringLiteral( "atlas_geometry" ), QCoreApplication::translate( "variable_help", "Current atlas feature geometry." ) );
729 
730  //layout item variables
731  sVariableHelpTexts.insert( QStringLiteral( "item_id" ), QCoreApplication::translate( "variable_help", "Layout item user ID (not necessarily unique)." ) );
732  sVariableHelpTexts.insert( QStringLiteral( "item_uuid" ), QCoreApplication::translate( "variable_help", "layout item unique ID." ) );
733  sVariableHelpTexts.insert( QStringLiteral( "item_left" ), QCoreApplication::translate( "variable_help", "Left position of layout item (in mm)." ) );
734  sVariableHelpTexts.insert( QStringLiteral( "item_top" ), QCoreApplication::translate( "variable_help", "Top position of layout item (in mm)." ) );
735  sVariableHelpTexts.insert( QStringLiteral( "item_width" ), QCoreApplication::translate( "variable_help", "Width of layout item (in mm)." ) );
736  sVariableHelpTexts.insert( QStringLiteral( "item_height" ), QCoreApplication::translate( "variable_help", "Height of layout item (in mm)." ) );
737 
738  //map settings item variables
739  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." ) );
740  sVariableHelpTexts.insert( QStringLiteral( "map_rotation" ), QCoreApplication::translate( "variable_help", "Current rotation of map." ) );
741  sVariableHelpTexts.insert( QStringLiteral( "map_scale" ), QCoreApplication::translate( "variable_help", "Current scale of map." ) );
742  sVariableHelpTexts.insert( QStringLiteral( "map_extent" ), QCoreApplication::translate( "variable_help", "Geometry representing the current extent of the map." ) );
743  sVariableHelpTexts.insert( QStringLiteral( "map_extent_center" ), QCoreApplication::translate( "variable_help", "Center of map." ) );
744  sVariableHelpTexts.insert( QStringLiteral( "map_extent_width" ), QCoreApplication::translate( "variable_help", "Width of map." ) );
745  sVariableHelpTexts.insert( QStringLiteral( "map_extent_height" ), QCoreApplication::translate( "variable_help", "Height of map." ) );
746  sVariableHelpTexts.insert( QStringLiteral( "map_crs" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of map (e.g., 'EPSG:4326')." ) );
747  sVariableHelpTexts.insert( QStringLiteral( "map_crs_definition" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of map (full definition)." ) );
748  sVariableHelpTexts.insert( QStringLiteral( "map_units" ), QCoreApplication::translate( "variable_help", "Units for map measurements." ) );
749  sVariableHelpTexts.insert( QStringLiteral( "map_layer_ids" ), QCoreApplication::translate( "variable_help", "List of map layer IDs visible in the map." ) );
750  sVariableHelpTexts.insert( QStringLiteral( "map_layers" ), QCoreApplication::translate( "variable_help", "List of map layers visible in the map." ) );
751 
752  sVariableHelpTexts.insert( QStringLiteral( "row_number" ), QCoreApplication::translate( "variable_help", "Stores the number of the current row." ) );
753  sVariableHelpTexts.insert( QStringLiteral( "grid_number" ), QCoreApplication::translate( "variable_help", "Current grid annotation value." ) );
754  sVariableHelpTexts.insert( QStringLiteral( "grid_axis" ), QCoreApplication::translate( "variable_help", "Current grid annotation axis (e.g., 'x' for longitude, 'y' for latitude)." ) );
755 
756  // map canvas item variables
757  sVariableHelpTexts.insert( QStringLiteral( "canvas_cursor_point" ), QCoreApplication::translate( "variable_help", "Last cursor position on the canvas in the project's geographical coordinates." ) );
758 
759  // map tool capture variables
760  sVariableHelpTexts.insert( QStringLiteral( "snapping_results" ), QCoreApplication::translate( "variable_help",
761  "<p>An array with an item for each snapped point.</p>"
762  "<p>Each item is a map with the following keys:</p>"
763  "<dl>"
764  "<dt>valid</dt><dd>Boolean that indicates if the snapping result is valid</dd>"
765  "<dt>layer</dt><dd>The layer on which the snapped feature is</dd>"
766  "<dt>feature_id</dt><dd>The feature id of the snapped feature</dd>"
767  "<dt>vertex_index</dt><dd>The index of the snapped vertex</dd>"
768  "<dt>distance</dt><dd>The distance between the mouse cursor and the snapped point at the time of snapping</dd>"
769  "</dl>" ) );
770 
771 
772  //symbol variables
773  sVariableHelpTexts.insert( QStringLiteral( "geometry_part_count" ), QCoreApplication::translate( "variable_help", "Number of parts in rendered feature's geometry." ) );
774  sVariableHelpTexts.insert( QStringLiteral( "geometry_part_num" ), QCoreApplication::translate( "variable_help", "Current geometry part number for feature being rendered." ) );
775  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." ) );
776  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." ) );
777 
778  sVariableHelpTexts.insert( QStringLiteral( "symbol_color" ), QCoreApplication::translate( "symbol_color", "Color of symbol used to render the feature." ) );
779  sVariableHelpTexts.insert( QStringLiteral( "symbol_angle" ), QCoreApplication::translate( "symbol_angle", "Angle of symbol used to render the feature (valid for marker symbols only)." ) );
780 
781  //cluster variables
782  sVariableHelpTexts.insert( QStringLiteral( "cluster_color" ), QCoreApplication::translate( "cluster_color", "Color of symbols within a cluster, or NULL if symbols have mixed colors." ) );
783  sVariableHelpTexts.insert( QStringLiteral( "cluster_size" ), QCoreApplication::translate( "cluster_size", "Number of symbols contained within a cluster." ) );
784 
785  //processing variables
786  sVariableHelpTexts.insert( QStringLiteral( "algorithm_id" ), QCoreApplication::translate( "algorithm_id", "Unique ID for algorithm." ) );
787  sVariableHelpTexts.insert( QStringLiteral( "fullextent_minx" ), QCoreApplication::translate( "fullextent_minx", "Minimum x-value from full canvas extent (including all layers)." ) );
788  sVariableHelpTexts.insert( QStringLiteral( "fullextent_miny" ), QCoreApplication::translate( "fullextent_miny", "Minimum y-value from full canvas extent (including all layers)." ) );
789  sVariableHelpTexts.insert( QStringLiteral( "fullextent_maxx" ), QCoreApplication::translate( "fullextent_maxx", "Maximum x-value from full canvas extent (including all layers)." ) );
790  sVariableHelpTexts.insert( QStringLiteral( "fullextent_maxy" ), QCoreApplication::translate( "fullextent_maxy", "Maximum y-value from full canvas extent (including all layers)." ) );
791 
792  //provider notification
793  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)." ) );
794 
795  //form context variable
796  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." ) );
797  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." ) );
798 
799  //form variable
800  sVariableHelpTexts.insert( QStringLiteral( "form_mode" ), QCoreApplication::translate( "form_mode", "What the form is used for, like AddFeatureMode, SingleEditMode, MultiEditMode, SearchMode, AggregateSearchMode or IdentifyMode as string." ) );
801 }
802 
803 QString QgsExpression::variableHelpText( const QString &variableName )
804 {
805  QgsExpression::initVariableHelp();
806  return sVariableHelpTexts.value( variableName, QString() );
807 }
808 
809 QString QgsExpression::formatVariableHelp( const QString &description, bool showValue, const QVariant &value )
810 {
811  QString text = !description.isEmpty() ? QStringLiteral( "<p>%1</p>" ).arg( description ) : QString();
812  if ( showValue )
813  {
814  QString valueString;
815  if ( !value.isValid() )
816  {
817  valueString = QCoreApplication::translate( "variable_help", "not set" );
818  }
819  else
820  {
821  valueString = QStringLiteral( "<b>%1</b>" ).arg( formatPreviewString( value ) );
822  }
823  text.append( QCoreApplication::translate( "variable_help", "<p>Current value: %1</p>" ).arg( valueString ) );
824  }
825  return text;
826 }
827 
828 QHash<QString, QString> QgsExpression::sGroups;
829 
830 QString QgsExpression::group( const QString &name )
831 {
832  if ( sGroups.isEmpty() )
833  {
834  sGroups.insert( QStringLiteral( "General" ), tr( "General" ) );
835  sGroups.insert( QStringLiteral( "Operators" ), tr( "Operators" ) );
836  sGroups.insert( QStringLiteral( "Conditionals" ), tr( "Conditionals" ) );
837  sGroups.insert( QStringLiteral( "Fields and Values" ), tr( "Fields and Values" ) );
838  sGroups.insert( QStringLiteral( "Math" ), tr( "Math" ) );
839  sGroups.insert( QStringLiteral( "Conversions" ), tr( "Conversions" ) );
840  sGroups.insert( QStringLiteral( "Date and Time" ), tr( "Date and Time" ) );
841  sGroups.insert( QStringLiteral( "String" ), tr( "String" ) );
842  sGroups.insert( QStringLiteral( "Color" ), tr( "Color" ) );
843  sGroups.insert( QStringLiteral( "GeometryGroup" ), tr( "Geometry" ) );
844  sGroups.insert( QStringLiteral( "Record" ), tr( "Record" ) );
845  sGroups.insert( QStringLiteral( "Variables" ), tr( "Variables" ) );
846  sGroups.insert( QStringLiteral( "Fuzzy Matching" ), tr( "Fuzzy Matching" ) );
847  sGroups.insert( QStringLiteral( "Recent (%1)" ), tr( "Recent (%1)" ) );
848  }
849 
850  //return the translated name for this group. If group does not
851  //have a translated name in the gGroups hash, return the name
852  //unchanged
853  return sGroups.value( name, name );
854 }
855 
856 QString QgsExpression::formatPreviewString( const QVariant &value, const bool htmlOutput )
857 {
858  static const int MAX_PREVIEW = 60;
859 
860  const QString startToken = htmlOutput ? QStringLiteral( "<i>&lt;" ) : QStringLiteral( "<" );
861  const QString endToken = htmlOutput ? QStringLiteral( "&gt;</i>" ) : QStringLiteral( ">" );
862 
863  if ( value.canConvert<QgsGeometry>() )
864  {
865  //result is a geometry
866  QgsGeometry geom = value.value<QgsGeometry>();
867  if ( geom.isNull() )
868  return startToken + tr( "empty geometry" ) + endToken;
869  else
870  return startToken + tr( "geometry: %1" ).arg( QgsWkbTypes::displayString( geom.constGet()->wkbType() ) )
871  + endToken;
872  }
873  else if ( value.value< QgsWeakMapLayerPointer >().data() )
874  {
875  return startToken + tr( "map layer" ) + endToken;
876  }
877  else if ( !value.isValid() )
878  {
879  return htmlOutput ? tr( "<i>NULL</i>" ) : QString();
880  }
881  else if ( value.canConvert< QgsFeature >() )
882  {
883  //result is a feature
884  QgsFeature feat = value.value<QgsFeature>();
885  return startToken + tr( "feature: %1" ).arg( feat.id() ) + endToken;
886  }
887  else if ( value.canConvert< QgsInterval >() )
888  {
889  //result is a feature
890  QgsInterval interval = value.value<QgsInterval>();
891  return startToken + tr( "interval: %1 days" ).arg( interval.days() ) + endToken;
892  }
893  else if ( value.canConvert< QgsGradientColorRamp >() )
894  {
895  return startToken + tr( "gradient ramp" ) + endToken;
896  }
897  else if ( value.type() == QVariant::Date )
898  {
899  QDate dt = value.toDate();
900  return startToken + tr( "date: %1" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd" ) ) ) + endToken;
901  }
902  else if ( value.type() == QVariant::Time )
903  {
904  QTime tm = value.toTime();
905  return startToken + tr( "time: %1" ).arg( tm.toString( QStringLiteral( "hh:mm:ss" ) ) ) + endToken;
906  }
907  else if ( value.type() == QVariant::DateTime )
908  {
909  QDateTime dt = value.toDateTime();
910  return startToken + tr( "datetime: %1" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd hh:mm:ss" ) ) ) + endToken;
911  }
912  else if ( value.type() == QVariant::String )
913  {
914  const QString previewString = value.toString();
915  if ( previewString.length() > MAX_PREVIEW + 3 )
916  {
917  return tr( "'%1…'" ).arg( previewString.left( MAX_PREVIEW ) );
918  }
919  else
920  {
921  return '\'' + previewString + '\'';
922  }
923  }
924  else if ( value.type() == QVariant::Map )
925  {
926  QString mapStr = QStringLiteral( "{" );
927  const QVariantMap map = value.toMap();
928  QString separator;
929  for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
930  {
931  mapStr.append( separator );
932  if ( separator.isEmpty() )
933  separator = QStringLiteral( "," );
934 
935  mapStr.append( QStringLiteral( " '%1': %2" ).arg( it.key(), formatPreviewString( it.value(), htmlOutput ) ) );
936  if ( mapStr.length() > MAX_PREVIEW - 3 )
937  {
938  mapStr = tr( "%1…" ).arg( mapStr.left( MAX_PREVIEW - 2 ) );
939  break;
940  }
941  }
942  if ( !map.empty() )
943  mapStr += QStringLiteral( " " );
944  mapStr += QStringLiteral( "}" );
945  return mapStr;
946  }
947  else if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
948  {
949  QString listStr = QStringLiteral( "[" );
950  const QVariantList list = value.toList();
951  QString separator;
952  for ( const QVariant &arrayValue : list )
953  {
954  listStr.append( separator );
955  if ( separator.isEmpty() )
956  separator = QStringLiteral( "," );
957 
958  listStr.append( " " );
959  listStr.append( formatPreviewString( arrayValue, htmlOutput ) );
960  if ( listStr.length() > MAX_PREVIEW - 3 )
961  {
962  listStr = QString( tr( "%1…" ) ).arg( listStr.left( MAX_PREVIEW - 2 ) );
963  break;
964  }
965  }
966  if ( !list.empty() )
967  listStr += QStringLiteral( " " );
968  listStr += QStringLiteral( "]" );
969  return listStr;
970  }
971  else
972  {
973  return value.toString();
974  }
975 }
976 
977 QString QgsExpression::createFieldEqualityExpression( const QString &fieldName, const QVariant &value )
978 {
979  QString expr;
980 
981  if ( value.isNull() )
982  expr = QStringLiteral( "%1 IS NULL" ).arg( quotedColumnRef( fieldName ) );
983  else
984  expr = QStringLiteral( "%1 = %2" ).arg( quotedColumnRef( fieldName ), quotedValue( value ) );
985 
986  return expr;
987 }
988 
990 {
991  return d->mRootNode;
992 }
993 
995 {
996  return d->mRootNode && d->mRootNode->nodeType() == QgsExpressionNode::ntColumnRef;
997 }
998 
999 QList<const QgsExpressionNode *> QgsExpression::nodes() const
1000 {
1001  if ( !d->mRootNode )
1002  return QList<const QgsExpressionNode *>();
1003 
1004  return d->mRootNode->nodes();
1005 }
1006 
1007 
1008 
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:63
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
static QString formatPreviewString(const QVariant &value, bool htmlOutput=true)
Formats an expression result for friendly display to the user.
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:54
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:1482
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:393
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...