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