QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 "qgsexpressionnodeimpl.h"
19 #include "qgsfeaturerequest.h"
20 #include "qgscolorrampimpl.h"
21 #include "qgslogger.h"
22 #include "qgsexpressioncontext.h"
23 #include "qgsgeometry.h"
24 #include "qgsproject.h"
26 #include "qgsexpressionutils.h"
27 #include "qgsexpression_p.h"
28 
29 #include <QRegularExpression>
30 
31 // from parser
32 extern QgsExpressionNode *parseExpression( const QString &str, QString &parserErrorMsg, QList<QgsExpression::ParserError> &parserErrors );
33 
34 Q_GLOBAL_STATIC( HelpTextHash, sFunctionHelpTexts )
35 Q_GLOBAL_STATIC( QgsStringMap, sVariableHelpTexts )
36 Q_GLOBAL_STATIC( QgsStringMap, sGroups )
37 
38 HelpTextHash &functionHelpTexts()
39 {
40  return *sFunctionHelpTexts();
41 }
42 
43 bool QgsExpression::checkExpression( const QString &text, const QgsExpressionContext *context, QString &errorMessage )
44 {
45  QgsExpression exp( text );
46  exp.prepare( context );
47  errorMessage = exp.parserErrorString();
48  return !exp.hasParserError();
49 }
50 
51 void QgsExpression::setExpression( const QString &expression )
52 {
53  detach();
54  d->mRootNode = ::parseExpression( expression, d->mParserErrorString, d->mParserErrors );
55  d->mEvalErrorString = QString();
56  d->mExp = expression;
57  d->mIsPrepared = false;
58 }
59 
61 {
62  if ( !d->mExp.isNull() )
63  return d->mExp;
64  else
65  return dump();
66 }
67 
68 QString QgsExpression::quotedColumnRef( QString name )
69 {
70  return QStringLiteral( "\"%1\"" ).arg( name.replace( '\"', QLatin1String( "\"\"" ) ) );
71 }
72 
73 QString QgsExpression::quotedString( QString text )
74 {
75  text.replace( '\'', QLatin1String( "''" ) );
76  text.replace( '\\', QLatin1String( "\\\\" ) );
77  text.replace( '\n', QLatin1String( "\\n" ) );
78  text.replace( '\t', QLatin1String( "\\t" ) );
79  return QStringLiteral( "'%1'" ).arg( text );
80 }
81 
82 QString QgsExpression::quotedValue( const QVariant &value )
83 {
84  return quotedValue( value, value.type() );
85 }
86 
87 QString QgsExpression::quotedValue( const QVariant &value, QVariant::Type type )
88 {
89  if ( value.isNull() )
90  return QStringLiteral( "NULL" );
91 
92  switch ( type )
93  {
94  case QVariant::Int:
95  case QVariant::LongLong:
96  case QVariant::Double:
97  return value.toString();
98 
99  case QVariant::Bool:
100  return value.toBool() ? QStringLiteral( "TRUE" ) : QStringLiteral( "FALSE" );
101 
102  case QVariant::List:
103  case QVariant::StringList:
104  {
105  QStringList quotedValues;
106  const QVariantList values = value.toList();
107  quotedValues.reserve( values.count() );
108  for ( const QVariant &v : values )
109  {
110  quotedValues += quotedValue( v );
111  }
112  return QStringLiteral( "array( %1 )" ).arg( quotedValues.join( QLatin1String( ", " ) ) );
113  }
114 
115  default:
116  case QVariant::String:
117  return quotedString( value.toString() );
118  }
119 
120 }
121 
122 bool QgsExpression::isFunctionName( const QString &name )
123 {
124  return functionIndex( name ) != -1;
125 }
126 
127 int QgsExpression::functionIndex( const QString &name )
128 {
129  int count = functionCount();
130  for ( int i = 0; i < count; i++ )
131  {
132  if ( QString::compare( name, QgsExpression::Functions()[i]->name(), Qt::CaseInsensitive ) == 0 )
133  return i;
134  const QStringList aliases = QgsExpression::Functions()[i]->aliases();
135  for ( const QString &alias : aliases )
136  {
137  if ( QString::compare( name, alias, Qt::CaseInsensitive ) == 0 )
138  return i;
139  }
140  }
141  return -1;
142 }
143 
145 {
146  return Functions().size();
147 }
148 
149 
150 QgsExpression::QgsExpression( const QString &expr )
151  : d( new QgsExpressionPrivate )
152 {
153  d->mRootNode = ::parseExpression( expr, d->mParserErrorString, d->mParserErrors );
154  d->mExp = expr;
155  Q_ASSERT( !d->mParserErrorString.isNull() || d->mRootNode );
156 }
157 
159  : d( other.d )
160 {
161  d->ref.ref();
162 }
163 
165 {
166  if ( this != &other )
167  {
168  if ( !d->ref.deref() )
169  {
170  delete d;
171  }
172 
173  d = other.d;
174  d->ref.ref();
175  }
176  return *this;
177 }
178 
179 QgsExpression::operator QString() const
180 {
181  return d->mExp;
182 }
183 
185  : d( new QgsExpressionPrivate )
186 {
187 }
188 
190 {
191  Q_ASSERT( d );
192  if ( !d->ref.deref() )
193  delete d;
194 }
195 
196 bool QgsExpression::operator==( const QgsExpression &other ) const
197 {
198  return ( d == other.d || d->mExp == other.d->mExp );
199 }
200 
202 {
203  return d->mRootNode;
204 }
205 
207 {
208  return d->mParserErrors.count() > 0;
209 }
210 
212 {
213  return d->mParserErrorString;
214 }
215 
216 QList<QgsExpression::ParserError> QgsExpression::parserErrors() const
217 {
218  return d->mParserErrors;
219 }
220 
221 QSet<QString> QgsExpression::referencedColumns() const
222 {
223  if ( !d->mRootNode )
224  return QSet<QString>();
225 
226  return d->mRootNode->referencedColumns();
227 }
228 
230 {
231  if ( !d->mRootNode )
232  return QSet<QString>();
233 
234  return d->mRootNode->referencedVariables();
235 }
236 
238 {
239  if ( !d->mRootNode )
240  return QSet<QString>();
241 
242  return d->mRootNode->referencedFunctions();
243 }
244 
245 QSet<int> QgsExpression::referencedAttributeIndexes( const QgsFields &fields ) const
246 {
247  if ( !d->mRootNode )
248  return QSet<int>();
249 
250  const QSet<QString> referencedFields = d->mRootNode->referencedColumns();
251  QSet<int> referencedIndexes;
252 
253  for ( const QString &fieldName : referencedFields )
254  {
255  if ( fieldName == QgsFeatureRequest::ALL_ATTRIBUTES )
256  {
257  referencedIndexes = qgis::listToSet( fields.allAttributesList() );
258  break;
259  }
260  const int idx = fields.lookupField( fieldName );
261  if ( idx >= 0 )
262  {
263  referencedIndexes << idx;
264  }
265  }
266 
267  return referencedIndexes;
268 }
269 
271 {
272  if ( !d->mRootNode )
273  return false;
274  return d->mRootNode->needsGeometry();
275 }
276 
277 void QgsExpression::initGeomCalculator( const QgsExpressionContext *context )
278 {
279  // Set the geometry calculator from the context if it has not been set by setGeomCalculator()
280  if ( context && ! d->mCalc )
281  {
282  // actually don't do it right away, cos it's expensive to create and only a very small number of expression
283  // functions actually require it. Let's lazily construct it when needed
284  d->mDaEllipsoid = context->variable( QStringLiteral( "project_ellipsoid" ) ).toString();
285  d->mDaCrs = std::make_unique<QgsCoordinateReferenceSystem>( context->variable( QStringLiteral( "_layer_crs" ) ).value<QgsCoordinateReferenceSystem>() );
286  d->mDaTransformContext = std::make_unique<QgsCoordinateTransformContext>( context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>() );
287  }
288 
289  // Set the distance units from the context if it has not been set by setDistanceUnits()
290  if ( context && distanceUnits() == QgsUnitTypes::DistanceUnknownUnit )
291  {
292  QString distanceUnitsStr = context->variable( QStringLiteral( "project_distance_units" ) ).toString();
293  if ( ! distanceUnitsStr.isEmpty() )
295  }
296 
297  // Set the area units from the context if it has not been set by setAreaUnits()
298  if ( context && areaUnits() == QgsUnitTypes::AreaUnknownUnit )
299  {
300  QString areaUnitsStr = context->variable( QStringLiteral( "project_area_units" ) ).toString();
301  if ( ! areaUnitsStr.isEmpty() )
302  setAreaUnits( QgsUnitTypes::stringToAreaUnit( areaUnitsStr ) );
303  }
304 }
305 
306 void QgsExpression::detach()
307 {
308  Q_ASSERT( d );
309 
310  if ( d->ref > 1 )
311  {
312  ( void )d->ref.deref();
313 
314  d = new QgsExpressionPrivate( *d );
315  }
316 }
317 
319 {
320  detach();
321  if ( calc )
322  d->mCalc = std::shared_ptr<QgsDistanceArea>( new QgsDistanceArea( *calc ) );
323  else
324  d->mCalc.reset();
325 }
326 
328 {
329  detach();
330  d->mEvalErrorString = QString();
331  if ( !d->mRootNode )
332  {
333  //re-parse expression. Creation of QgsExpressionContexts may have added extra
334  //known functions since this expression was created, so we have another try
335  //at re-parsing it now that the context must have been created
336  d->mRootNode = ::parseExpression( d->mExp, d->mParserErrorString, d->mParserErrors );
337  }
338 
339  if ( !d->mRootNode )
340  {
341  d->mEvalErrorString = tr( "No root node! Parsing failed?" );
342  return false;
343  }
344 
345  initGeomCalculator( context );
346  d->mIsPrepared = true;
347  return d->mRootNode->prepare( this, context );
348 }
349 
351 {
352  d->mEvalErrorString = QString();
353  if ( !d->mRootNode )
354  {
355  d->mEvalErrorString = tr( "No root node! Parsing failed?" );
356  return QVariant();
357  }
358 
359  return d->mRootNode->eval( this, static_cast<const QgsExpressionContext *>( nullptr ) );
360 }
361 
363 {
364  d->mEvalErrorString = QString();
365  if ( !d->mRootNode )
366  {
367  d->mEvalErrorString = tr( "No root node! Parsing failed?" );
368  return QVariant();
369  }
370 
371  if ( ! d->mIsPrepared )
372  {
373  prepare( context );
374  }
375  return d->mRootNode->eval( this, context );
376 }
377 
379 {
380  return !d->mEvalErrorString.isNull();
381 }
382 
384 {
385  return d->mEvalErrorString;
386 }
387 
389 {
390  d->mEvalErrorString = str;
391 }
392 
393 QString QgsExpression::dump() const
394 {
395  if ( !d->mRootNode )
396  return QString();
397 
398  return d->mRootNode->dump();
399 }
400 
402 {
403  if ( !d->mCalc && d->mDaCrs && d->mDaCrs->isValid() && d->mDaTransformContext )
404  {
405  // calculator IS required, so initialize it now...
406  d->mCalc = std::shared_ptr<QgsDistanceArea>( new QgsDistanceArea() );
407  d->mCalc->setEllipsoid( d->mDaEllipsoid.isEmpty() ? geoNone() : d->mDaEllipsoid );
408  d->mCalc->setSourceCrs( *d->mDaCrs.get(), *d->mDaTransformContext.get() );
409  }
410 
411  return d->mCalc.get();
412 }
413 
415 {
416  return d->mDistanceUnit;
417 }
418 
420 {
421  d->mDistanceUnit = unit;
422 }
423 
425 {
426  return d->mAreaUnit;
427 }
428 
430 {
431  d->mAreaUnit = unit;
432 }
433 
434 QString QgsExpression::replaceExpressionText( const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea )
435 {
436  QString expr_action;
437 
438  int index = 0;
439  while ( index < action.size() )
440  {
441  static const QRegularExpression sRegEx{ QStringLiteral( "\\[%(.*?)%\\]" ), QRegularExpression::MultilineOption | QRegularExpression::DotMatchesEverythingOption };
442 
443  const QRegularExpressionMatch match = sRegEx.match( action, index );
444  if ( !match.hasMatch() )
445  break;
446 
447  const int pos = action.indexOf( sRegEx, index );
448  const int start = index;
449  index = pos + match.capturedLength( 0 );
450  const QString toReplace = match.captured( 1 ).trimmed();
451  QgsDebugMsgLevel( "Found expression: " + toReplace, 3 );
452 
453  QgsExpression exp( toReplace );
454  if ( exp.hasParserError() )
455  {
456  QgsDebugMsg( "Expression parser error: " + exp.parserErrorString() );
457 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 2)
458  expr_action += action.midRef( start, index - start );
459 #else
460  expr_action += QStringView {action}.mid( start, index - start );
461 #endif
462  continue;
463  }
464 
465  if ( distanceArea )
466  {
467  //if QgsDistanceArea specified for area/distance conversion, use it
468  exp.setGeomCalculator( distanceArea );
469  }
470 
471  QVariant result = exp.evaluate( context );
472 
473  if ( exp.hasEvalError() )
474  {
475  QgsDebugMsg( "Expression parser eval error: " + exp.evalErrorString() );
476 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 2)
477  expr_action += action.midRef( start, index - start );
478 #else
479  expr_action += QStringView {action}.mid( start, index - start );
480 #endif
481  continue;
482  }
483 
484  QgsDebugMsgLevel( "Expression result is: " + result.toString(), 3 );
485  expr_action += action.mid( start, pos - start ) + result.toString();
486  }
487 
488 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 2)
489  expr_action += action.midRef( index );
490 #else
491  expr_action += QStringView {action}.mid( index ).toString();
492 #endif
493 
494  return expr_action;
495 }
496 
497 QSet<QString> QgsExpression::referencedVariables( const QString &text )
498 {
499  QSet<QString> variables;
500  int index = 0;
501  while ( index < text.size() )
502  {
503  const thread_local QRegularExpression rx( "\\[%([^\\]]+)%\\]" );
504  const QRegularExpressionMatch match = rx.match( text );
505  if ( !match.hasMatch() )
506  break;
507 
508  index = match.capturedStart() + match.capturedLength();
509  QString to_replace = match.captured( 1 ).trimmed();
510 
511  QgsExpression exp( to_replace );
512  variables.unite( exp.referencedVariables() );
513  }
514 
515  return variables;
516 }
517 
518 double QgsExpression::evaluateToDouble( const QString &text, const double fallbackValue )
519 {
520  bool ok;
521  //first test if text is directly convertible to double
522  // use system locale: e.g. in German locale, user is presented with numbers "1,23" instead of "1.23" in C locale
523  // so we also want to allow user to rewrite it to "5,23" and it is still accepted
524  double convertedValue = QLocale().toDouble( text, &ok );
525  if ( ok )
526  {
527  return convertedValue;
528  }
529 
530  //otherwise try to evaluate as expression
531  QgsExpression expr( text );
532 
533  QgsExpressionContext context;
536 
537  QVariant result = expr.evaluate( &context );
538  convertedValue = result.toDouble( &ok );
539  if ( expr.hasEvalError() || !ok )
540  {
541  return fallbackValue;
542  }
543  return convertedValue;
544 }
545 
546 QString QgsExpression::helpText( QString name )
547 {
548  QgsExpression::initFunctionHelp();
549 
550  if ( !sFunctionHelpTexts()->contains( name ) )
551  return tr( "function help for %1 missing" ).arg( name );
552 
553  const Help &f = ( *sFunctionHelpTexts() )[ name ];
554 
555  name = f.mName;
556  if ( f.mType == tr( "group" ) )
557  {
558  name = group( name );
559  name = name.toLower();
560  }
561 
562  name = name.toHtmlEscaped();
563 
564  QString helpContents( QStringLiteral( "<h3>%1</h3>\n<div class=\"description\"><p>%2</p></div>" )
565  .arg( tr( "%1 %2" ).arg( f.mType, name ),
566  f.mDescription ) );
567 
568  for ( const HelpVariant &v : std::as_const( f.mVariants ) )
569  {
570  if ( f.mVariants.size() > 1 )
571  {
572  helpContents += QStringLiteral( "<h3>%1</h3>\n<div class=\"description\">%2</p></div>" ).arg( v.mName, v.mDescription );
573  }
574 
575  if ( f.mType != tr( "group" ) && f.mType != tr( "expression" ) )
576  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"syntax\">\n" ).arg( tr( "Syntax" ) );
577 
578  if ( f.mType == tr( "operator" ) )
579  {
580  if ( v.mArguments.size() == 1 )
581  {
582  helpContents += QStringLiteral( "<code><span class=\"functionname\">%1</span> <span class=\"argument\">%2</span></code>" )
583  .arg( name, v.mArguments[0].mArg );
584  }
585  else if ( v.mArguments.size() == 2 )
586  {
587  helpContents += QStringLiteral( "<code><span class=\"argument\">%1</span> <span class=\"functionname\">%2</span> <span class=\"argument\">%3</span></code>" )
588  .arg( v.mArguments[0].mArg, name, v.mArguments[1].mArg );
589  }
590  }
591  else if ( f.mType != tr( "group" ) && f.mType != tr( "expression" ) )
592  {
593  helpContents += QStringLiteral( "<code><span class=\"functionname\">%1</span>" ).arg( name );
594 
595  bool hasOptionalArgs = false;
596 
597  if ( f.mType == tr( "function" ) && ( f.mName[0] != '$' || !v.mArguments.isEmpty() || v.mVariableLenArguments ) )
598  {
599  helpContents += '(';
600 
601  QString delim;
602  for ( const HelpArg &a : std::as_const( v.mArguments ) )
603  {
604  if ( !a.mDescOnly )
605  {
606  if ( a.mOptional )
607  {
608  hasOptionalArgs = true;
609  helpContents += QLatin1Char( '[' );
610  }
611 
612  helpContents += delim;
613  helpContents += QStringLiteral( "<span class=\"argument\">%2%3</span>" ).arg(
614  a.mArg,
615  a.mDefaultVal.isEmpty() ? QString() : '=' + a.mDefaultVal
616  );
617 
618  if ( a.mOptional )
619  helpContents += QLatin1Char( ']' );
620  }
621  delim = QStringLiteral( "," );
622  }
623 
624  if ( v.mVariableLenArguments )
625  {
626  helpContents += QChar( 0x2026 );
627  }
628 
629  helpContents += ')';
630  }
631 
632  helpContents += QLatin1String( "</code>" );
633 
634  if ( hasOptionalArgs )
635  {
636  helpContents += QLatin1String( "<br/><br/>" ) + tr( "[ ] marks optional components" );
637  }
638  }
639 
640  if ( !v.mArguments.isEmpty() )
641  {
642  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"arguments\">\n<table>" ).arg( tr( "Arguments" ) );
643 
644  for ( const HelpArg &a : std::as_const( v.mArguments ) )
645  {
646  if ( a.mSyntaxOnly )
647  continue;
648 
649  helpContents += QStringLiteral( "<tr><td class=\"argument\">%1</td><td>%2</td></tr>" ).arg( a.mArg, a.mDescription );
650  }
651 
652  helpContents += QLatin1String( "</table>\n</div>\n" );
653  }
654 
655  if ( !v.mExamples.isEmpty() )
656  {
657  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"examples\">\n<ul>\n" ).arg( tr( "Examples" ) );
658 
659  for ( const HelpExample &e : std::as_const( v.mExamples ) )
660  {
661  helpContents += "<li><code>" + e.mExpression + "</code> &rarr; <code>" + e.mReturns + "</code>";
662 
663  if ( !e.mNote.isEmpty() )
664  helpContents += QStringLiteral( " (%1)" ).arg( e.mNote );
665 
666  helpContents += QLatin1String( "</li>\n" );
667  }
668 
669  helpContents += QLatin1String( "</ul>\n</div>\n" );
670  }
671 
672  if ( !v.mNotes.isEmpty() )
673  {
674  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"notes\"><p>%2</p></div>\n" ).arg( tr( "Notes" ), v.mNotes );
675  }
676  }
677 
678  return helpContents;
679 }
680 
681 QStringList QgsExpression::tags( const QString &name )
682 {
683  QStringList tags = QStringList();
684 
685  QgsExpression::initFunctionHelp();
686 
687  if ( sFunctionHelpTexts()->contains( name ) )
688  {
689  const Help &f = ( *sFunctionHelpTexts() )[ name ];
690 
691  for ( const HelpVariant &v : std::as_const( f.mVariants ) )
692  {
693  tags << v.mTags;
694  }
695  }
696 
697  return tags;
698 }
699 
700 void QgsExpression::initVariableHelp()
701 {
702  if ( !sVariableHelpTexts()->isEmpty() )
703  return;
704 
705  //global variables
706  sVariableHelpTexts()->insert( QStringLiteral( "qgis_version" ), QCoreApplication::translate( "variable_help", "Current QGIS version string." ) );
707  sVariableHelpTexts()->insert( QStringLiteral( "qgis_version_no" ), QCoreApplication::translate( "variable_help", "Current QGIS version number." ) );
708  sVariableHelpTexts()->insert( QStringLiteral( "qgis_release_name" ), QCoreApplication::translate( "variable_help", "Current QGIS release name." ) );
709  sVariableHelpTexts()->insert( QStringLiteral( "qgis_short_version" ), QCoreApplication::translate( "variable_help", "Short QGIS version string." ) );
710  sVariableHelpTexts()->insert( QStringLiteral( "qgis_os_name" ), QCoreApplication::translate( "variable_help", "Operating system name, e.g., 'windows', 'linux' or 'osx'." ) );
711  sVariableHelpTexts()->insert( QStringLiteral( "qgis_platform" ), QCoreApplication::translate( "variable_help", "QGIS platform, e.g., 'desktop' or 'server'." ) );
712  sVariableHelpTexts()->insert( QStringLiteral( "qgis_locale" ), QCoreApplication::translate( "variable_help", "Two letter identifier for current QGIS locale." ) );
713  sVariableHelpTexts()->insert( QStringLiteral( "user_account_name" ), QCoreApplication::translate( "variable_help", "Current user's operating system account name." ) );
714  sVariableHelpTexts()->insert( QStringLiteral( "user_full_name" ), QCoreApplication::translate( "variable_help", "Current user's operating system user name (if available)." ) );
715 
716  //project variables
717  sVariableHelpTexts()->insert( QStringLiteral( "project_title" ), QCoreApplication::translate( "variable_help", "Title of current project." ) );
718  sVariableHelpTexts()->insert( QStringLiteral( "project_path" ), QCoreApplication::translate( "variable_help", "Full path (including file name) of current project." ) );
719  sVariableHelpTexts()->insert( QStringLiteral( "project_folder" ), QCoreApplication::translate( "variable_help", "Folder for current project." ) );
720  sVariableHelpTexts()->insert( QStringLiteral( "project_filename" ), QCoreApplication::translate( "variable_help", "Filename of current project." ) );
721  sVariableHelpTexts()->insert( QStringLiteral( "project_basename" ), QCoreApplication::translate( "variable_help", "Base name of current project's filename (without path and extension)." ) );
722  sVariableHelpTexts()->insert( QStringLiteral( "project_home" ), QCoreApplication::translate( "variable_help", "Home path of current project." ) );
723  sVariableHelpTexts()->insert( QStringLiteral( "project_crs" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of project (e.g., 'EPSG:4326')." ) );
724  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_definition" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of project (full definition)." ) );
725  sVariableHelpTexts()->insert( QStringLiteral( "project_units" ), QCoreApplication::translate( "variable_help", "Unit of the project's CRS." ) );
726  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_description" ), QCoreApplication::translate( "variable_help", "Name of the coordinate reference system of the project." ) );
727  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_acronym" ), QCoreApplication::translate( "variable_help", "Acronym of the coordinate reference system of the project." ) );
728  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_ellipsoid" ), QCoreApplication::translate( "variable_help", "Acronym of the ellipsoid of the coordinate reference system of the project." ) );
729  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_proj4" ), QCoreApplication::translate( "variable_help", "Proj4 definition of the coordinate reference system of the project." ) );
730  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_wkt" ), QCoreApplication::translate( "variable_help", "WKT definition of the coordinate reference system of the project." ) );
731  sVariableHelpTexts()->insert( QStringLiteral( "project_author" ), QCoreApplication::translate( "variable_help", "Project author, taken from project metadata." ) );
732  sVariableHelpTexts()->insert( QStringLiteral( "project_abstract" ), QCoreApplication::translate( "variable_help", "Project abstract, taken from project metadata." ) );
733  sVariableHelpTexts()->insert( QStringLiteral( "project_creation_date" ), QCoreApplication::translate( "variable_help", "Project creation date, taken from project metadata." ) );
734  sVariableHelpTexts()->insert( QStringLiteral( "project_identifier" ), QCoreApplication::translate( "variable_help", "Project identifier, taken from project metadata." ) );
735  sVariableHelpTexts()->insert( QStringLiteral( "project_last_saved" ), QCoreApplication::translate( "variable_help", "Date/time when project was last saved." ) );
736  sVariableHelpTexts()->insert( QStringLiteral( "project_keywords" ), QCoreApplication::translate( "variable_help", "Project keywords, taken from project metadata." ) );
737  sVariableHelpTexts()->insert( QStringLiteral( "project_area_units" ), QCoreApplication::translate( "variable_help", "Area unit for current project, used when calculating areas of geometries." ) );
738  sVariableHelpTexts()->insert( QStringLiteral( "project_distance_units" ), QCoreApplication::translate( "variable_help", "Distance unit for current project, used when calculating lengths of geometries." ) );
739  sVariableHelpTexts()->insert( QStringLiteral( "project_ellipsoid" ), QCoreApplication::translate( "variable_help", "Name of ellipsoid of current project, used when calculating geodetic areas and lengths of geometries." ) );
740  sVariableHelpTexts()->insert( QStringLiteral( "layer_ids" ), QCoreApplication::translate( "variable_help", "List of all map layer IDs from the current project." ) );
741  sVariableHelpTexts()->insert( QStringLiteral( "layers" ), QCoreApplication::translate( "variable_help", "List of all map layers from the current project." ) );
742 
743  //layer variables
744  sVariableHelpTexts()->insert( QStringLiteral( "layer_name" ), QCoreApplication::translate( "variable_help", "Name of current layer." ) );
745  sVariableHelpTexts()->insert( QStringLiteral( "layer_id" ), QCoreApplication::translate( "variable_help", "ID of current layer." ) );
746  sVariableHelpTexts()->insert( QStringLiteral( "layer_crs" ), QCoreApplication::translate( "variable_help", "CRS Authority ID of current layer." ) );
747  sVariableHelpTexts()->insert( QStringLiteral( "layer" ), QCoreApplication::translate( "variable_help", "The current layer." ) );
748 
749  //composition variables
750  sVariableHelpTexts()->insert( QStringLiteral( "layout_name" ), QCoreApplication::translate( "variable_help", "Name of composition." ) );
751  sVariableHelpTexts()->insert( QStringLiteral( "layout_numpages" ), QCoreApplication::translate( "variable_help", "Number of pages in composition." ) );
752  sVariableHelpTexts()->insert( QStringLiteral( "layout_page" ), QCoreApplication::translate( "variable_help", "Current page number in composition." ) );
753  sVariableHelpTexts()->insert( QStringLiteral( "layout_pageheight" ), QCoreApplication::translate( "variable_help", "Composition page height in mm (or specified custom units)." ) );
754  sVariableHelpTexts()->insert( QStringLiteral( "layout_pagewidth" ), QCoreApplication::translate( "variable_help", "Composition page width in mm (or specified custom units)." ) );
755  sVariableHelpTexts()->insert( QStringLiteral( "layout_pageoffsets" ), QCoreApplication::translate( "variable_help", "Array of Y coordinate of the top of each page." ) );
756  sVariableHelpTexts()->insert( QStringLiteral( "layout_dpi" ), QCoreApplication::translate( "variable_help", "Composition resolution (DPI)." ) );
757 
758  //atlas variables
759  sVariableHelpTexts()->insert( QStringLiteral( "atlas_layerid" ), QCoreApplication::translate( "variable_help", "Current atlas coverage layer ID." ) );
760  sVariableHelpTexts()->insert( QStringLiteral( "atlas_layername" ), QCoreApplication::translate( "variable_help", "Current atlas coverage layer name." ) );
761  sVariableHelpTexts()->insert( QStringLiteral( "atlas_totalfeatures" ), QCoreApplication::translate( "variable_help", "Total number of features in atlas." ) );
762  sVariableHelpTexts()->insert( QStringLiteral( "atlas_featurenumber" ), QCoreApplication::translate( "variable_help", "Current atlas feature number." ) );
763  sVariableHelpTexts()->insert( QStringLiteral( "atlas_filename" ), QCoreApplication::translate( "variable_help", "Current atlas file name." ) );
764  sVariableHelpTexts()->insert( QStringLiteral( "atlas_pagename" ), QCoreApplication::translate( "variable_help", "Current atlas page name." ) );
765  sVariableHelpTexts()->insert( QStringLiteral( "atlas_feature" ), QCoreApplication::translate( "variable_help", "Current atlas feature (as feature object)." ) );
766  sVariableHelpTexts()->insert( QStringLiteral( "atlas_featureid" ), QCoreApplication::translate( "variable_help", "Current atlas feature ID." ) );
767  sVariableHelpTexts()->insert( QStringLiteral( "atlas_geometry" ), QCoreApplication::translate( "variable_help", "Current atlas feature geometry." ) );
768 
769  //layout item variables
770  sVariableHelpTexts()->insert( QStringLiteral( "item_id" ), QCoreApplication::translate( "variable_help", "Layout item user-assigned ID (not necessarily unique)." ) );
771  sVariableHelpTexts()->insert( QStringLiteral( "item_uuid" ), QCoreApplication::translate( "variable_help", "layout item unique ID." ) );
772  sVariableHelpTexts()->insert( QStringLiteral( "item_left" ), QCoreApplication::translate( "variable_help", "Left position of layout item (in mm)." ) );
773  sVariableHelpTexts()->insert( QStringLiteral( "item_top" ), QCoreApplication::translate( "variable_help", "Top position of layout item (in mm)." ) );
774  sVariableHelpTexts()->insert( QStringLiteral( "item_width" ), QCoreApplication::translate( "variable_help", "Width of layout item (in mm)." ) );
775  sVariableHelpTexts()->insert( QStringLiteral( "item_height" ), QCoreApplication::translate( "variable_help", "Height of layout item (in mm)." ) );
776 
777  //map settings item variables
778  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." ) );
779  sVariableHelpTexts()->insert( QStringLiteral( "map_rotation" ), QCoreApplication::translate( "variable_help", "Current rotation of map." ) );
780  sVariableHelpTexts()->insert( QStringLiteral( "map_scale" ), QCoreApplication::translate( "variable_help", "Current scale of map." ) );
781  sVariableHelpTexts()->insert( QStringLiteral( "map_extent" ), QCoreApplication::translate( "variable_help", "Geometry representing the current extent of the map." ) );
782  sVariableHelpTexts()->insert( QStringLiteral( "map_extent_center" ), QCoreApplication::translate( "variable_help", "Center of map." ) );
783  sVariableHelpTexts()->insert( QStringLiteral( "map_extent_width" ), QCoreApplication::translate( "variable_help", "Width of map." ) );
784  sVariableHelpTexts()->insert( QStringLiteral( "map_extent_height" ), QCoreApplication::translate( "variable_help", "Height of map." ) );
785  sVariableHelpTexts()->insert( QStringLiteral( "map_crs" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of map (e.g., 'EPSG:4326')." ) );
786  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_description" ), QCoreApplication::translate( "variable_help", "Name of the coordinate reference system of the map." ) );
787  sVariableHelpTexts()->insert( QStringLiteral( "map_units" ), QCoreApplication::translate( "variable_help", "Units for map measurements." ) );
788  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_definition" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of the map (full definition)." ) );
789  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_acronym" ), QCoreApplication::translate( "variable_help", "Acronym of the coordinate reference system of the map." ) );
790  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_projection" ), QCoreApplication::translate( "variable_help", "Projection method used by the coordinate reference system of the map." ) );
791  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_ellipsoid" ), QCoreApplication::translate( "variable_help", "Acronym of the ellipsoid of the coordinate reference system of the map." ) );
792  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_proj4" ), QCoreApplication::translate( "variable_help", "Proj4 definition of the coordinate reference system of the map." ) );
793  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_wkt" ), QCoreApplication::translate( "variable_help", "WKT definition of the coordinate reference system of the map." ) );
794  sVariableHelpTexts()->insert( QStringLiteral( "map_layer_ids" ), QCoreApplication::translate( "variable_help", "List of map layer IDs visible in the map." ) );
795  sVariableHelpTexts()->insert( QStringLiteral( "map_layers" ), QCoreApplication::translate( "variable_help", "List of map layers visible in the map." ) );
796 
797  sVariableHelpTexts()->insert( QStringLiteral( "map_start_time" ), QCoreApplication::translate( "variable_help", "Start of the map's temporal time range (as a datetime value)" ) );
798  sVariableHelpTexts()->insert( QStringLiteral( "map_end_time" ), QCoreApplication::translate( "variable_help", "End of the map's temporal time range (as a datetime value)" ) );
799  sVariableHelpTexts()->insert( QStringLiteral( "map_interval" ), QCoreApplication::translate( "variable_help", "Duration of the map's temporal time range (as an interval value)" ) );
800 
801  sVariableHelpTexts()->insert( QStringLiteral( "frame_rate" ), QCoreApplication::translate( "variable_help", "Number of frames per second during animation playback" ) );
802  sVariableHelpTexts()->insert( QStringLiteral( "frame_number" ), QCoreApplication::translate( "variable_help", "Current frame number during animation playback" ) );
803  sVariableHelpTexts()->insert( QStringLiteral( "frame_duration" ), QCoreApplication::translate( "variable_help", "Temporal duration of each animation frame (as an interval value)" ) );
804  sVariableHelpTexts()->insert( QStringLiteral( "animation_start_time" ), QCoreApplication::translate( "variable_help", "Start of the animation's overall temporal time range (as a datetime value)" ) );
805  sVariableHelpTexts()->insert( QStringLiteral( "animation_end_time" ), QCoreApplication::translate( "variable_help", "End of the animation's overall temporal time range (as a datetime value)" ) );
806  sVariableHelpTexts()->insert( QStringLiteral( "animation_interval" ), QCoreApplication::translate( "variable_help", "Duration of the animation's overall temporal time range (as an interval value)" ) );
807 
808  // vector tile layer variables
809  sVariableHelpTexts()->insert( QStringLiteral( "zoom_level" ), QCoreApplication::translate( "variable_help", "Vector tile zoom level of the map that is being rendered (derived from the current map scale). Normally in interval [0, 20]." ) );
810  sVariableHelpTexts()->insert( QStringLiteral( "vector_tile_zoom" ), QCoreApplication::translate( "variable_help", "Exact vector tile zoom level of the map that is being rendered (derived from the current map scale). Normally in interval [0, 20]. Unlike @zoom_level, this variable is a floating point value which can be used to interpolate values between two integer zoom levels." ) );
811 
812  sVariableHelpTexts()->insert( QStringLiteral( "row_number" ), QCoreApplication::translate( "variable_help", "Stores the number of the current row." ) );
813  sVariableHelpTexts()->insert( QStringLiteral( "grid_number" ), QCoreApplication::translate( "variable_help", "Current grid annotation value." ) );
814  sVariableHelpTexts()->insert( QStringLiteral( "grid_axis" ), QCoreApplication::translate( "variable_help", "Current grid annotation axis (e.g., 'x' for longitude, 'y' for latitude)." ) );
815  sVariableHelpTexts()->insert( QStringLiteral( "column_number" ), QCoreApplication::translate( "variable_help", "Stores the number of the current column." ) );
816 
817  // map canvas item variables
818  sVariableHelpTexts()->insert( QStringLiteral( "canvas_cursor_point" ), QCoreApplication::translate( "variable_help", "Last cursor position on the canvas in the project's geographical coordinates." ) );
819 
820  // legend canvas item variables
821  sVariableHelpTexts()->insert( QStringLiteral( "legend_title" ), QCoreApplication::translate( "variable_help", "Title of the legend." ) );
822  sVariableHelpTexts()->insert( QStringLiteral( "legend_column_count" ), QCoreApplication::translate( "variable_help", "Number of column in the legend." ) );
823  sVariableHelpTexts()->insert( QStringLiteral( "legend_split_layers" ), QCoreApplication::translate( "variable_help", "Boolean indicating if layers can be split in the legend." ) );
824  sVariableHelpTexts()->insert( QStringLiteral( "legend_wrap_string" ), QCoreApplication::translate( "variable_help", "Characters used to wrap the legend text." ) );
825  sVariableHelpTexts()->insert( QStringLiteral( "legend_filter_by_map" ), QCoreApplication::translate( "variable_help", "Boolean indicating if the content of the legend is filtered by the map." ) );
826  sVariableHelpTexts()->insert( QStringLiteral( "legend_filter_out_atlas" ), QCoreApplication::translate( "variable_help", "Boolean indicating if the Atlas is filtered out of the legend." ) );
827 
828  // scalebar rendering
829  sVariableHelpTexts()->insert( QStringLiteral( "scale_value" ), QCoreApplication::translate( "variable_help", "Current scale bar distance value." ) );
830 
831  // map tool capture variables
832  sVariableHelpTexts()->insert( QStringLiteral( "snapping_results" ), QCoreApplication::translate( "variable_help",
833  "<p>An array with an item for each snapped point.</p>"
834  "<p>Each item is a map with the following keys:</p>"
835  "<dl>"
836  "<dt>valid</dt><dd>Boolean that indicates if the snapping result is valid</dd>"
837  "<dt>layer</dt><dd>The layer on which the snapped feature is</dd>"
838  "<dt>feature_id</dt><dd>The feature id of the snapped feature</dd>"
839  "<dt>vertex_index</dt><dd>The index of the snapped vertex</dd>"
840  "<dt>distance</dt><dd>The distance between the mouse cursor and the snapped point at the time of snapping</dd>"
841  "</dl>" ) );
842 
843 
844  //symbol variables
845  sVariableHelpTexts()->insert( QStringLiteral( "geometry_part_count" ), QCoreApplication::translate( "variable_help", "Number of parts in rendered feature's geometry." ) );
846  sVariableHelpTexts()->insert( QStringLiteral( "geometry_part_num" ), QCoreApplication::translate( "variable_help", "Current geometry part number for feature being rendered." ) );
847  sVariableHelpTexts()->insert( QStringLiteral( "geometry_ring_num" ), QCoreApplication::translate( "variable_help", "Current geometry ring number for feature being rendered (for polygon features only). The exterior ring has a value of 0." ) );
848  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." ) );
849  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." ) );
850 
851  sVariableHelpTexts()->insert( QStringLiteral( "symbol_color" ), QCoreApplication::translate( "symbol_color", "Color of symbol used to render the feature." ) );
852  sVariableHelpTexts()->insert( QStringLiteral( "symbol_angle" ), QCoreApplication::translate( "symbol_angle", "Angle of symbol used to render the feature (valid for marker symbols only)." ) );
853  sVariableHelpTexts()->insert( QStringLiteral( "symbol_layer_count" ), QCoreApplication::translate( "symbol_layer_count", "Total number of symbol layers in the symbol." ) );
854  sVariableHelpTexts()->insert( QStringLiteral( "symbol_layer_index" ), QCoreApplication::translate( "symbol_layer_index", "Current symbol layer index." ) );
855  sVariableHelpTexts()->insert( QStringLiteral( "symbol_marker_row" ), QCoreApplication::translate( "symbol_marker_row", "Row number for marker (valid for point pattern fills only)." ) );
856  sVariableHelpTexts()->insert( QStringLiteral( "symbol_marker_column" ), QCoreApplication::translate( "symbol_marker_column", "Column number for marker (valid for point pattern fills only)." ) );
857  sVariableHelpTexts()->insert( QStringLiteral( "symbol_frame" ), QCoreApplication::translate( "symbol_frame", "Frame number (for animated symbols only)." ) );
858 
859  sVariableHelpTexts()->insert( QStringLiteral( "symbol_label" ), QCoreApplication::translate( "symbol_label", "Label for the symbol (either a user defined label or the default autogenerated label)." ) );
860  sVariableHelpTexts()->insert( QStringLiteral( "symbol_id" ), QCoreApplication::translate( "symbol_id", "Internal ID of the symbol." ) );
861  sVariableHelpTexts()->insert( QStringLiteral( "symbol_count" ), QCoreApplication::translate( "symbol_count", "Total number of features represented by the symbol." ) );
862 
863  //cluster variables
864  sVariableHelpTexts()->insert( QStringLiteral( "cluster_color" ), QCoreApplication::translate( "cluster_color", "Color of symbols within a cluster, or NULL if symbols have mixed colors." ) );
865  sVariableHelpTexts()->insert( QStringLiteral( "cluster_size" ), QCoreApplication::translate( "cluster_size", "Number of symbols contained within a cluster." ) );
866 
867  //processing variables
868  sVariableHelpTexts()->insert( QStringLiteral( "algorithm_id" ), QCoreApplication::translate( "algorithm_id", "Unique ID for algorithm." ) );
869  sVariableHelpTexts()->insert( QStringLiteral( "model_path" ), QCoreApplication::translate( "variable_help", "Full path (including file name) of current model (or project path if model is embedded in a project)." ) );
870  sVariableHelpTexts()->insert( QStringLiteral( "model_folder" ), QCoreApplication::translate( "variable_help", "Folder containing current model (or project folder if model is embedded in a project)." ) );
871  sVariableHelpTexts()->insert( QStringLiteral( "model_name" ), QCoreApplication::translate( "variable_help", "Name of current model." ) );
872  sVariableHelpTexts()->insert( QStringLiteral( "model_group" ), QCoreApplication::translate( "variable_help", "Group for current model." ) );
873  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_minx" ), QCoreApplication::translate( "fullextent_minx", "Minimum x-value from full canvas extent (including all layers)." ) );
874  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_miny" ), QCoreApplication::translate( "fullextent_miny", "Minimum y-value from full canvas extent (including all layers)." ) );
875  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_maxx" ), QCoreApplication::translate( "fullextent_maxx", "Maximum x-value from full canvas extent (including all layers)." ) );
876  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_maxy" ), QCoreApplication::translate( "fullextent_maxy", "Maximum y-value from full canvas extent (including all layers)." ) );
877 
878  //provider notification
879  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)." ) );
880 
881  //form context variable
882  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." ) );
883  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." ) );
884 
885  //parent form context variable
886  sVariableHelpTexts()->insert( QStringLiteral( "current_parent_geometry" ), QCoreApplication::translate( "current_parent_geometry",
887  "Only usable in an embedded form context, "
888  "represents the geometry of the feature currently being edited in the parent form.\n"
889  "Can be used in a form/row context to filter the related features using a value "
890  "from the feature currently edited in the parent form, to make sure that the filter "
891  "still works with standalone forms it is recommended to wrap this variable in a "
892  "'coalesce()'." ) );
893  sVariableHelpTexts()->insert( QStringLiteral( "current_parent_feature" ), QCoreApplication::translate( "current_parent_feature",
894  "Only usable in an embedded form context, "
895  "represents the feature currently being edited in the parent form.\n"
896  "Can be used in a form/row context to filter the related features using a value "
897  "from the feature currently edited in the parent form, to make sure that the filter "
898  "still works with standalone forms it is recommended to wrap this variable in a "
899  "'coalesce()'." ) );
900 
901  //form variable
902  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." ) );
903 
904  // plots
905  sVariableHelpTexts()->insert( QStringLiteral( "plot_axis" ), QCoreApplication::translate( "plot_axis", "The associated plot axis, e.g. 'x' or 'y'." ) );
906  sVariableHelpTexts()->insert( QStringLiteral( "plot_axis_value" ), QCoreApplication::translate( "plot_axis_value", "The current value for the plot axis." ) );
907 }
908 
909 
910 bool QgsExpression::addVariableHelpText( const QString name, const QString &description )
911 {
912  if ( sVariableHelpTexts()->contains( name ) )
913  {
914  return false;
915  }
916  sVariableHelpTexts()->insert( name, description );
917  return true;
918 }
919 
920 QString QgsExpression::variableHelpText( const QString &variableName )
921 {
922  QgsExpression::initVariableHelp();
923  return sVariableHelpTexts()->value( variableName, QString() );
924 }
925 
926 QString QgsExpression::formatVariableHelp( const QString &description, bool showValue, const QVariant &value )
927 {
928  QString text = !description.isEmpty() ? QStringLiteral( "<p>%1</p>" ).arg( description ) : QString();
929  if ( showValue )
930  {
931  QString valueString;
932  if ( !value.isValid() )
933  {
934  valueString = QCoreApplication::translate( "variable_help", "not set" );
935  }
936  else
937  {
938  valueString = QStringLiteral( "<b>%1</b>" ).arg( formatPreviewString( value ) );
939  }
940  text.append( QCoreApplication::translate( "variable_help", "<p>Current value: %1</p>" ).arg( valueString ) );
941  }
942  return text;
943 }
944 
945 QString QgsExpression::group( const QString &name )
946 {
947  if ( sGroups()->isEmpty() )
948  {
949  sGroups()->insert( QStringLiteral( "Aggregates" ), tr( "Aggregates" ) );
950  sGroups()->insert( QStringLiteral( "Arrays" ), tr( "Arrays" ) );
951  sGroups()->insert( QStringLiteral( "Color" ), tr( "Color" ) );
952  sGroups()->insert( QStringLiteral( "Conditionals" ), tr( "Conditionals" ) );
953  sGroups()->insert( QStringLiteral( "Conversions" ), tr( "Conversions" ) );
954  sGroups()->insert( QStringLiteral( "Date and Time" ), tr( "Date and Time" ) );
955  sGroups()->insert( QStringLiteral( "Fields and Values" ), tr( "Fields and Values" ) );
956  sGroups()->insert( QStringLiteral( "Files and Paths" ), tr( "Files and Paths" ) );
957  sGroups()->insert( QStringLiteral( "Fuzzy Matching" ), tr( "Fuzzy Matching" ) );
958  sGroups()->insert( QStringLiteral( "General" ), tr( "General" ) );
959  sGroups()->insert( QStringLiteral( "GeometryGroup" ), tr( "Geometry" ) );
960  sGroups()->insert( QStringLiteral( "Map Layers" ), tr( "Map Layers" ) );
961  sGroups()->insert( QStringLiteral( "Maps" ), tr( "Maps" ) );
962  sGroups()->insert( QStringLiteral( "Math" ), tr( "Math" ) );
963  sGroups()->insert( QStringLiteral( "Operators" ), tr( "Operators" ) );
964  sGroups()->insert( QStringLiteral( "Rasters" ), tr( "Rasters" ) );
965  sGroups()->insert( QStringLiteral( "Record and Attributes" ), tr( "Record and Attributes" ) );
966  sGroups()->insert( QStringLiteral( "String" ), tr( "String" ) );
967  sGroups()->insert( QStringLiteral( "Variables" ), tr( "Variables" ) );
968  sGroups()->insert( QStringLiteral( "Recent (%1)" ), tr( "Recent (%1)" ) );
969  sGroups()->insert( QStringLiteral( "UserGroup" ), tr( "User expressions" ) );
970  }
971 
972  //return the translated name for this group. If group does not
973  //have a translated name in the gGroups hash, return the name
974  //unchanged
975  return sGroups()->value( name, name );
976 }
977 
978 QString QgsExpression::formatPreviewString( const QVariant &value, const bool htmlOutput, int maximumPreviewLength )
979 {
980  const QString startToken = htmlOutput ? QStringLiteral( "<i>&lt;" ) : QStringLiteral( "<" );
981  const QString endToken = htmlOutput ? QStringLiteral( "&gt;</i>" ) : QStringLiteral( ">" );
982 
983  if ( value.canConvert<QgsGeometry>() )
984  {
985  //result is a geometry
986  QgsGeometry geom = value.value<QgsGeometry>();
987  if ( geom.isNull() )
988  return startToken + tr( "empty geometry" ) + endToken;
989  else
990  return startToken + tr( "geometry: %1" ).arg( QgsWkbTypes::displayString( geom.constGet()->wkbType() ) )
991  + endToken;
992  }
993  else if ( value.value< QgsWeakMapLayerPointer >().data() )
994  {
995  return startToken + tr( "map layer" ) + endToken;
996  }
997  else if ( !value.isValid() )
998  {
999  return htmlOutput ? tr( "<i>NULL</i>" ) : QString();
1000  }
1001  else if ( value.canConvert< QgsFeature >() )
1002  {
1003  //result is a feature
1004  QgsFeature feat = value.value<QgsFeature>();
1005  return startToken + tr( "feature: %1" ).arg( feat.id() ) + endToken;
1006  }
1007  else if ( value.canConvert< QgsInterval >() )
1008  {
1009  QgsInterval interval = value.value<QgsInterval>();
1010  if ( interval.days() > 1 )
1011  {
1012  return startToken + tr( "interval: %1 days" ).arg( interval.days() ) + endToken;
1013  }
1014  else if ( interval.hours() > 1 )
1015  {
1016  return startToken + tr( "interval: %1 hours" ).arg( interval.hours() ) + endToken;
1017  }
1018  else if ( interval.minutes() > 1 )
1019  {
1020  return startToken + tr( "interval: %1 minutes" ).arg( interval.minutes() ) + endToken;
1021  }
1022  else
1023  {
1024  return startToken + tr( "interval: %1 seconds" ).arg( interval.seconds() ) + endToken;
1025  }
1026  }
1027  else if ( value.canConvert< QgsGradientColorRamp >() )
1028  {
1029  return startToken + tr( "gradient ramp" ) + endToken;
1030  }
1031  else if ( value.type() == QVariant::Date )
1032  {
1033  const QDate dt = value.toDate();
1034  return startToken + tr( "date: %1" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd" ) ) ) + endToken;
1035  }
1036  else if ( value.type() == QVariant::Time )
1037  {
1038  const QTime tm = value.toTime();
1039  return startToken + tr( "time: %1" ).arg( tm.toString( QStringLiteral( "hh:mm:ss" ) ) ) + endToken;
1040  }
1041  else if ( value.type() == QVariant::DateTime )
1042  {
1043  const QDateTime dt = value.toDateTime();
1044  return startToken + tr( "datetime: %1 (%2)" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd hh:mm:ss" ) ), dt.timeZoneAbbreviation() ) + endToken;
1045  }
1046  else if ( value.type() == QVariant::String )
1047  {
1048  const QString previewString = value.toString();
1049  if ( previewString.length() > maximumPreviewLength + 3 )
1050  {
1051  return tr( "'%1…'" ).arg( previewString.left( maximumPreviewLength ) );
1052  }
1053  else
1054  {
1055  return '\'' + previewString + '\'';
1056  }
1057  }
1058  else if ( value.type() == QVariant::Map )
1059  {
1060  QString mapStr = QStringLiteral( "{" );
1061  const QVariantMap map = value.toMap();
1062  QString separator;
1063  for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1064  {
1065  mapStr.append( separator );
1066  if ( separator.isEmpty() )
1067  separator = QStringLiteral( "," );
1068 
1069  mapStr.append( QStringLiteral( " '%1': %2" ).arg( it.key(), formatPreviewString( it.value(), htmlOutput ) ) );
1070  if ( mapStr.length() > maximumPreviewLength - 3 )
1071  {
1072  mapStr = tr( "%1…" ).arg( mapStr.left( maximumPreviewLength - 2 ) );
1073  break;
1074  }
1075  }
1076  if ( !map.empty() )
1077  mapStr += QLatin1Char( ' ' );
1078  mapStr += QLatin1Char( '}' );
1079  return mapStr;
1080  }
1081  else if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
1082  {
1083  QString listStr = QStringLiteral( "[" );
1084  const QVariantList list = value.toList();
1085  QString separator;
1086  for ( const QVariant &arrayValue : list )
1087  {
1088  listStr.append( separator );
1089  if ( separator.isEmpty() )
1090  separator = QStringLiteral( "," );
1091 
1092  listStr.append( " " );
1093  listStr.append( formatPreviewString( arrayValue, htmlOutput ) );
1094  if ( listStr.length() > maximumPreviewLength - 3 )
1095  {
1096  listStr = QString( tr( "%1…" ) ).arg( listStr.left( maximumPreviewLength - 2 ) );
1097  break;
1098  }
1099  }
1100  if ( !list.empty() )
1101  listStr += QLatin1Char( ' ' );
1102  listStr += QLatin1Char( ']' );
1103  return listStr;
1104  }
1105  else if ( value.type() == QVariant::Int ||
1106  value.type() == QVariant::UInt ||
1107  value.type() == QVariant::LongLong ||
1108  value.type() == QVariant::ULongLong ||
1109  value.type() == QVariant::Double ||
1110  // Qt madness with QMetaType::Float :/
1111  value.type() == static_cast<QVariant::Type>( QMetaType::Float ) )
1112  {
1113  return QgsExpressionUtils::toLocalizedString( value );
1114  }
1115  else
1116  {
1117  QString str { value.toString() };
1118  if ( str.length() > maximumPreviewLength - 3 )
1119  {
1120  str = tr( "%1…" ).arg( str.left( maximumPreviewLength - 2 ) );
1121  }
1122  return str;
1123  }
1124 }
1125 
1126 QString QgsExpression::createFieldEqualityExpression( const QString &fieldName, const QVariant &value, QVariant::Type fieldType )
1127 {
1128  QString expr;
1129 
1130  if ( value.isNull() )
1131  expr = QStringLiteral( "%1 IS NULL" ).arg( quotedColumnRef( fieldName ) );
1132  else if ( fieldType == QVariant::Type::Invalid )
1133  expr = QStringLiteral( "%1 = %2" ).arg( quotedColumnRef( fieldName ), quotedValue( value ) );
1134  else
1135  expr = QStringLiteral( "%1 = %2" ).arg( quotedColumnRef( fieldName ), quotedValue( value, fieldType ) );
1136 
1137  return expr;
1138 }
1139 
1140 bool QgsExpression::isFieldEqualityExpression( const QString &expression, QString &field, QVariant &value )
1141 {
1143 
1144  if ( !e.rootNode() )
1145  return false;
1146 
1147  if ( const QgsExpressionNodeBinaryOperator *binOp = dynamic_cast<const QgsExpressionNodeBinaryOperator *>( e.rootNode() ) )
1148  {
1149  if ( binOp->op() == QgsExpressionNodeBinaryOperator::boEQ )
1150  {
1151  const QgsExpressionNodeColumnRef *columnRef = dynamic_cast<const QgsExpressionNodeColumnRef *>( binOp->opLeft() );
1152  const QgsExpressionNodeLiteral *literal = dynamic_cast<const QgsExpressionNodeLiteral *>( binOp->opRight() );
1153  if ( columnRef && literal )
1154  {
1155  field = columnRef->name();
1156  value = literal->value();
1157  return true;
1158  }
1159  }
1160  }
1161  return false;
1162 }
1163 
1164 bool QgsExpression::attemptReduceToInClause( const QStringList &expressions, QString &result )
1165 {
1166  if ( expressions.empty() )
1167  return false;
1168 
1169  QString inField;
1170  bool first = true;
1171  QStringList values;
1172  for ( const QString &expression : expressions )
1173  {
1174  QString field;
1175  QVariant value;
1177  {
1178  if ( first )
1179  {
1180  inField = field;
1181  first = false;
1182  }
1183  else if ( field != inField )
1184  {
1185  return false;
1186  }
1187  values << QgsExpression::quotedValue( value );
1188  }
1189  else
1190  {
1191  // we also allow reducing similar 'field IN (...)' expressions!
1193 
1194  if ( !e.rootNode() )
1195  return false;
1196 
1197  if ( const QgsExpressionNodeInOperator *inOp = dynamic_cast<const QgsExpressionNodeInOperator *>( e.rootNode() ) )
1198  {
1199  if ( inOp->isNotIn() )
1200  return false;
1201 
1202  const QgsExpressionNodeColumnRef *columnRef = dynamic_cast<const QgsExpressionNodeColumnRef *>( inOp->node() );
1203  if ( !columnRef )
1204  return false;
1205 
1206  if ( first )
1207  {
1208  inField = columnRef->name();
1209  first = false;
1210  }
1211  else if ( columnRef->name() != inField )
1212  {
1213  return false;
1214  }
1215 
1216  if ( QgsExpressionNode::NodeList *nodeList = inOp->list() )
1217  {
1218  const QList<QgsExpressionNode *> nodes = nodeList->list();
1219  for ( const QgsExpressionNode *node : nodes )
1220  {
1221  const QgsExpressionNodeLiteral *literal = dynamic_cast<const QgsExpressionNodeLiteral *>( node );
1222  if ( !literal )
1223  return false;
1224 
1225  values << QgsExpression::quotedValue( literal->value() );
1226  }
1227  }
1228  }
1229  // Collect ORs
1230  else if ( const QgsExpressionNodeBinaryOperator *orOp = dynamic_cast<const QgsExpressionNodeBinaryOperator *>( e.rootNode() ) )
1231  {
1232 
1233  // OR Collector function: returns a possibly empty list of the left and right operands of an OR expression
1234  std::function<QStringList( QgsExpressionNode *, QgsExpressionNode * )> collectOrs = [ &collectOrs ]( QgsExpressionNode * opLeft, QgsExpressionNode * opRight ) -> QStringList
1235  {
1236  QStringList orParts;
1237  if ( const QgsExpressionNodeBinaryOperator *leftOrOp = dynamic_cast<const QgsExpressionNodeBinaryOperator *>( opLeft ) )
1238  {
1239  if ( leftOrOp->op( ) == QgsExpressionNodeBinaryOperator::BinaryOperator::boOr )
1240  {
1241  orParts.append( collectOrs( leftOrOp->opLeft(), leftOrOp->opRight() ) );
1242  }
1243  else
1244  {
1245  orParts.append( leftOrOp->dump() );
1246  }
1247  }
1248  else if ( const QgsExpressionNodeInOperator *leftInOp = dynamic_cast<const QgsExpressionNodeInOperator *>( opLeft ) )
1249  {
1250  orParts.append( leftInOp->dump() );
1251  }
1252  else
1253  {
1254  return {};
1255  }
1256 
1257  if ( const QgsExpressionNodeBinaryOperator *rightOrOp = dynamic_cast<const QgsExpressionNodeBinaryOperator *>( opRight ) )
1258  {
1259  if ( rightOrOp->op( ) == QgsExpressionNodeBinaryOperator::BinaryOperator::boOr )
1260  {
1261  orParts.append( collectOrs( rightOrOp->opLeft(), rightOrOp->opRight() ) );
1262  }
1263  else
1264  {
1265  orParts.append( rightOrOp->dump() );
1266  }
1267  }
1268  else if ( const QgsExpressionNodeInOperator *rightInOp = dynamic_cast<const QgsExpressionNodeInOperator *>( opRight ) )
1269  {
1270  orParts.append( rightInOp->dump() );
1271  }
1272  else
1273  {
1274  return {};
1275  }
1276 
1277  return orParts;
1278  };
1279 
1280  if ( orOp->op( ) == QgsExpressionNodeBinaryOperator::BinaryOperator::boOr )
1281  {
1282  // Try to collect all OR conditions
1283  const QStringList orParts = collectOrs( orOp->opLeft(), orOp->opRight() );
1284  if ( orParts.isEmpty() )
1285  {
1286  return false;
1287  }
1288  else
1289  {
1290  QString orPartsResult;
1291  if ( attemptReduceToInClause( orParts, orPartsResult ) )
1292  {
1293  // Need to check if the IN field is correct,
1294  QgsExpression inExp { orPartsResult };
1295  if ( ! inExp.rootNode() )
1296  {
1297  return false;
1298  }
1299 
1300  if ( const QgsExpressionNodeInOperator *inOpInner = dynamic_cast<const QgsExpressionNodeInOperator *>( inExp.rootNode() ) )
1301  {
1302  if ( inOpInner->node()->nodeType() != QgsExpressionNode::NodeType::ntColumnRef || inOpInner->node()->referencedColumns().size() < 1 )
1303  {
1304  return false;
1305  }
1306 
1307  const QString innerInfield { inOpInner->node()->referencedColumns().values().first() };
1308 
1309  if ( first )
1310  {
1311  inField = innerInfield;
1312  first = false;
1313  }
1314 
1315  if ( innerInfield != inField )
1316  {
1317  return false;
1318  }
1319  else
1320  {
1321  const auto constInnerValuesList { inOpInner->list()->list() };
1322  for ( const auto &innerInValueNode : std::as_const( constInnerValuesList ) )
1323  {
1324  values.append( innerInValueNode->dump() );
1325  }
1326  }
1327 
1328  }
1329  else
1330  {
1331  return false;
1332  }
1333  }
1334  else
1335  {
1336  return false;
1337  }
1338  }
1339  }
1340  else
1341  {
1342  return false;
1343  }
1344  }
1345  else
1346  {
1347  return false;
1348  }
1349  }
1350  }
1351  result = QStringLiteral( "%1 IN (%2)" ).arg( quotedColumnRef( inField ), values.join( ',' ) );
1352  return true;
1353 }
1354 
1356 {
1357  return d->mRootNode;
1358 }
1359 
1361 {
1362  return d->mRootNode && d->mRootNode->nodeType() == QgsExpressionNode::ntColumnRef;
1363 }
1364 
1365 int QgsExpression::expressionToLayerFieldIndex( const QString &expression, const QgsVectorLayer *layer )
1366 {
1367  if ( !layer )
1368  return -1;
1369 
1370  // easy check first -- lookup field directly.
1371  int attrIndex = layer->fields().lookupField( expression.trimmed() );
1372  if ( attrIndex >= 0 )
1373  return attrIndex;
1374 
1375  // may still be a simple field expression, just one which is enclosed in "" or similar
1376  QgsExpression candidate( expression );
1377  if ( candidate.isField() )
1378  {
1379  const QString fieldName = qgis::down_cast<const QgsExpressionNodeColumnRef *>( candidate.rootNode() )->name();
1380  return layer->fields().lookupField( fieldName );
1381  }
1382  return -1;
1383 }
1384 
1385 QString QgsExpression::quoteFieldExpression( const QString &expression, const QgsVectorLayer *layer )
1386 {
1387  if ( !layer )
1388  return expression;
1389 
1390  const int fieldIndex = QgsExpression::expressionToLayerFieldIndex( expression, layer );
1391  if ( !expression.contains( '\"' ) && fieldIndex != -1 )
1392  {
1393  // retrieve actual field name from layer, so that we correctly remove any unwanted leading/trailing whitespace
1394  return QgsExpression::quotedColumnRef( layer->fields().at( fieldIndex ).name() );
1395  }
1396  else
1397  {
1398  return expression;
1399  }
1400 }
1401 
1402 QList<const QgsExpressionNode *> QgsExpression::nodes() const
1403 {
1404  if ( !d->mRootNode )
1405  return QList<const QgsExpressionNode *>();
1406 
1407  return d->mRootNode->nodes();
1408 }
1409 
1410 
1411 
QgsExpression::parserErrors
QList< QgsExpression::ParserError > parserErrors() const
Returns parser error details including location of error.
Definition: qgsexpression.cpp:216
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:406
qgsexpressioncontextutils.h
qgscolorrampimpl.h
qgsexpression_p.h
QgsExpression::quotedString
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes)
Definition: qgsexpression.cpp:73
QgsInterval::seconds
double seconds() const
Returns the interval duration in seconds.
Definition: qgsinterval.h:236
QgsWkbTypes::displayString
static QString displayString(Type type) SIP_HOLDGIL
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
Definition: qgswkbtypes.cpp:145
qgsfeaturerequest.h
QgsGradientColorRamp
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorrampimpl.h:136
QgsExpression::setDistanceUnits
void setDistanceUnits(QgsUnitTypes::DistanceUnit unit)
Sets the desired distance units for calculations involving geomCalculator(), e.g.,...
Definition: qgsexpression.cpp:419
QgsExpressionNodeLiteral
An expression node for literal values.
Definition: qgsexpressionnodeimpl.h:461
QgsExpressionNodeBinaryOperator
A binary expression operator, which operates on two values.
Definition: qgsexpressionnodeimpl.h:99
QgsExpressionContextUtils::globalScope
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Definition: qgsexpressioncontextutils.cpp:40
QgsExpression::operator==
bool operator==(const QgsExpression &other) const
Compares two expressions.
Definition: qgsexpression.cpp:196
QgsExpression::evalErrorString
QString evalErrorString() const
Returns evaluation error.
Definition: qgsexpression.cpp:383
QgsUnitTypes::stringToDistanceUnit
static Q_INVOKABLE QgsUnitTypes::DistanceUnit stringToDistanceUnit(const QString &string, bool *ok=nullptr)
Converts a translated string to a distance unit.
Definition: qgsunittypes.cpp:312
QgsExpression::referencedColumns
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
Definition: qgsexpression.cpp:221
QgsExpression::formatVariableHelp
static QString formatVariableHelp(const QString &description, bool showValue=true, const QVariant &value=QVariant())
Returns formatted help text for a variable.
Definition: qgsexpression.cpp:926
QgsUnitTypes::DistanceUnknownUnit
@ DistanceUnknownUnit
Unknown distance unit.
Definition: qgsunittypes.h:78
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
qgsexpression.h
QgsExpression::dump
QString dump() const
Returns an expression string, constructed from the internal abstract syntax tree.
Definition: qgsexpression.cpp:393
QgsExpressionNode::ntColumnRef
@ ntColumnRef
Definition: qgsexpressionnode.h:84
QgsExpression::geomCalculator
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
Definition: qgsexpression.cpp:401
QgsExpression::attemptReduceToInClause
static bool attemptReduceToInClause(const QStringList &expressions, QString &result)
Attempts to reduce a list of expressions to a single "field IN (val1, val2, ... )" type expression.
Definition: qgsexpression.cpp:1164
QgsExpression::QgsExpression
QgsExpression()
Create an empty expression.
Definition: qgsexpression.cpp:184
QgsExpression::rootNode
const QgsExpressionNode * rootNode() const
Returns the root node of the expression.
Definition: qgsexpression.cpp:1355
QgsExpressionNodeColumnRef
An expression node which takes it value from a feature's field.
Definition: qgsexpressionnodeimpl.h:512
QgsExpressionNodeLiteral::value
QVariant value() const
The value of the literal.
Definition: qgsexpressionnodeimpl.h:481
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:44
QgsExpression::referencedAttributeIndexes
QSet< int > referencedAttributeIndexes(const QgsFields &fields) const
Returns a list of field name indexes obtained from the provided fields.
Definition: qgsexpression.cpp:245
QgsExpression::isValid
bool isValid() const
Checks if this expression is valid.
Definition: qgsexpression.cpp:201
QgsExpression::setExpression
void setExpression(const QString &expression)
Set the expression string, will reset the whole internal structure.
Definition: qgsexpression.cpp:51
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:480
parseExpression
QgsExpressionNode * parseExpression(const QString &str, QString &parserErrorMsg, QList< QgsExpression::ParserError > &parserErrors)
field
const QgsField & field
Definition: qgsfield.h:463
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsExpression::referencedFunctions
QSet< QString > referencedFunctions() const
Returns a list of the names of all functions which are used in this expression.
Definition: qgsexpression.cpp:237
QgsUnitTypes::DistanceUnit
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:67
QgsExpressionContext::variable
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
Definition: qgsexpressioncontext.cpp:300
QgsField::name
QString name
Definition: qgsfield.h:60
Q_GLOBAL_STATIC
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QgsExpression::quoteFieldExpression
static QString quoteFieldExpression(const QString &expression, const QgsVectorLayer *layer)
Validate if the expression is a field in the layer and ensure it is quoted.
Definition: qgsexpression.cpp:1385
QgsExpression::~QgsExpression
~QgsExpression()
Definition: qgsexpression.cpp:189
QgsExpressionNodeInOperator
An expression node for value IN or NOT IN clauses.
Definition: qgsexpressionnodeimpl.h:343
QgsExpression::setAreaUnits
void setAreaUnits(QgsUnitTypes::AreaUnit unit)
Sets the desired areal units for calculations involving geomCalculator(), e.g., "$area".
Definition: qgsexpression.cpp:429
QgsExpression::checkExpression
static bool checkExpression(const QString &text, const QgsExpressionContext *context, QString &errorMessage)
Tests whether a string is a valid expression.
Definition: qgsexpression.cpp:43
QgsExpression::quotedValue
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
Definition: qgsexpression.cpp:82
QgsExpression::setGeomCalculator
void setGeomCalculator(const QgsDistanceArea *calc)
Sets the geometry calculator used for distance and area calculations in expressions.
Definition: qgsexpression.cpp:318
QgsFeatureRequest::ALL_ATTRIBUTES
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
Definition: qgsfeaturerequest.h:317
QgsExpression::isFieldEqualityExpression
static bool isFieldEqualityExpression(const QString &expression, QString &field, QVariant &value)
Returns true if the given expression is a simple "field=value" type expression.
Definition: qgsexpression.cpp:1140
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3436
QgsFeature::id
QgsFeatureId id
Definition: qgsfeature.h:68
QgsExpression::setEvalErrorString
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions)
Definition: qgsexpression.cpp:388
QgsExpressionContextUtils::projectScope
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
Definition: qgsexpressioncontextutils.cpp:291
QgsExpression::hasEvalError
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
Definition: qgsexpression.cpp:378
qgsexpressioncontext.h
QgsUnitTypes::stringToAreaUnit
static Q_INVOKABLE QgsUnitTypes::AreaUnit stringToAreaUnit(const QString &string, bool *ok=nullptr)
Converts a translated string to an areal unit.
Definition: qgsunittypes.cpp:759
QgsAbstractGeometry::wkbType
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
Definition: qgsabstractgeometry.h:206
QgsExpression::parserErrorString
QString parserErrorString() const
Returns parser error.
Definition: qgsexpression.cpp:211
geoNone
CONSTLATIN1STRING geoNone()
Constant that holds the string representation for "No ellips/No CRS".
Definition: qgis.h:2738
qgsexpressionutils.h
QgsExpression::isFunctionName
static bool isFunctionName(const QString &name)
tells whether the identifier is a name of existing function
Definition: qgsexpression.cpp:122
qgsexpressionfunction.h
QgsExpression::evaluateToDouble
static double evaluateToDouble(const QString &text, double fallbackValue)
Attempts to evaluate a text string as an expression to a resultant double value.
Definition: qgsexpression.cpp:518
QgsExpression::group
static QString group(const QString &group)
Returns the translated name for a function group.
Definition: qgsexpression.cpp:945
QgsUnitTypes::AreaUnit
AreaUnit
Units of area.
Definition: qgsunittypes.h:93
QgsExpression::variableHelpText
static QString variableHelpText(const QString &variableName)
Returns the help text for a specified variable.
Definition: qgsexpression.cpp:920
QgsGeometry::isNull
bool isNull
Definition: qgsgeometry.h:127
QgsWeakMapLayerPointer
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:2146
QgsExpression::createFieldEqualityExpression
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QVariant::Type fieldType=QVariant::Type::Invalid)
Create an expression allowing to evaluate if a field is equal to a value.
Definition: qgsexpression.cpp:1126
QgsExpression::prepare
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
Definition: qgsexpression.cpp:327
QgsInterval::minutes
double minutes() const
Returns the interval duration in minutes.
Definition: qgsinterval.cpp:187
QgsGeometry::constGet
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Definition: qgsgeometry.cpp:136
QgsExpression::nodes
QList< const QgsExpressionNode * > nodes() const
Returns a list of all nodes which are used in this expression.
Definition: qgsexpression.cpp:1402
QgsExpression::Functions
static const QList< QgsExpressionFunction * > & Functions()
Definition: qgsexpressionfunction.cpp:7316
QgsExpression::evaluate
QVariant evaluate()
Evaluate the feature and return the result.
Definition: qgsexpression.cpp:350
QgsExpressionNode::NodeList::list
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
Definition: qgsexpressionnode.h:148
QgsExpressionNode
Abstract base class for all nodes that can appear in an expression.
Definition: qgsexpressionnode.h:34
QgsExpression::functionIndex
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
Definition: qgsexpression.cpp:127
QgsExpression::areaUnits
QgsUnitTypes::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g.,...
Definition: qgsexpression.cpp:424
QgsExpression::distanceUnits
QgsUnitTypes::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
Definition: qgsexpression.cpp:414
QgsFields::allAttributesList
QgsAttributeList allAttributesList() const
Utility function to get list of attribute indexes.
Definition: qgsfields.cpp:376
QgsExpressionNodeBinaryOperator::boEQ
@ boEQ
=
Definition: qgsexpressionnodeimpl.h:114
QgsStringMap
QMap< QString, QString > QgsStringMap
Definition: qgis.h:2781
QgsExpression::formatPreviewString
static QString formatPreviewString(const QVariant &value, bool htmlOutput=true, int maximumPreviewLength=60)
Formats an expression result for friendly display to the user.
Definition: qgsexpression.cpp:978
qgsgeometry.h
qgsexpressionnodeimpl.h
QgsExpressionNodeColumnRef::name
QString name() const
The name of the column.
Definition: qgsexpressionnodeimpl.h:534
QgsGeometry
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
str
#define str(x)
Definition: qgis.cpp:37
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsInterval
A representation of the interval between two datetime values.
Definition: qgsinterval.h:41
QgsExpression::helpText
static QString helpText(QString name)
Returns the help text for a specified function.
Definition: qgsexpression.cpp:546
QgsExpression::tags
static QStringList tags(const QString &name)
Returns a string list of search tags for a specified function.
Definition: qgsexpression.cpp:681
QgsExpression::quotedColumnRef
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
Definition: qgsexpression.cpp:68
QgsExpression::needsGeometry
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
Definition: qgsexpression.cpp:270
QgsInterval::days
double days() const
Returns the interval duration in days.
Definition: qgsinterval.cpp:153
functionHelpTexts
HelpTextHash & functionHelpTexts()
Definition: qgsexpression.cpp:38
QgsDistanceArea
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
Definition: qgsdistancearea.h:52
QgsInterval::hours
double hours() const
Returns the interval duration in hours.
Definition: qgsinterval.cpp:170
QgsFeature
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:55
QgsExpression::isField
bool isField() const
Checks whether an expression consists only of a single field reference.
Definition: qgsexpression.cpp:1360
QgsExpression::functionCount
static int functionCount()
Returns the number of functions defined in the parser.
Definition: qgsexpression.cpp:144
QgsExpression::hasParserError
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
Definition: qgsexpression.cpp:206
qgslogger.h
QgsExpression::expressionToLayerFieldIndex
static int expressionToLayerFieldIndex(const QString &expression, const QgsVectorLayer *layer)
Attempts to resolve an expression to a field index from the given layer.
Definition: qgsexpression.cpp:1365
QgsFields::lookupField
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:349
QgsFields::at
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings")....
Definition: qgsexpression.h:102
QgsExpression::addVariableHelpText
static bool addVariableHelpText(const QString name, const QString &description)
Adds a help string for a custom variable.
Definition: qgsexpression.cpp:910
QgsExpression::replaceExpressionText
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...
Definition: qgsexpression.cpp:434
QgsExpression::expression
QString expression() const
Returns the original, unmodified expression string.
Definition: qgsexpression.cpp:60
qgsproject.h
QgsUnitTypes::AreaUnknownUnit
@ AreaUnknownUnit
Unknown areal unit.
Definition: qgsunittypes.h:106
QgsExpressionNode::NodeList
A list of expression nodes.
Definition: qgsexpressionnode.h:121
QgsExpression::referencedVariables
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
Definition: qgsexpression.cpp:229
QgsExpression::operator=
QgsExpression & operator=(const QgsExpression &other)
Create a copy of this expression.
Definition: qgsexpression.cpp:164