QGIS API Documentation  3.13.0-Master (9729a43df7)
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 "qgscolorramp.h"
21 #include "qgslogger.h"
22 #include "qgsexpressioncontext.h"
23 #include "qgsgeometry.h"
24 #include "qgsproject.h"
26 #include "qgsexpression_p.h"
27 
28 // from parser
29 extern QgsExpressionNode *parseExpression( const QString &str, QString &parserErrorMsg, QList<QgsExpression::ParserError> &parserErrors );
30 
31 Q_GLOBAL_STATIC( HelpTextHash, sFunctionHelpTexts )
32 Q_GLOBAL_STATIC( QgsStringMap, sVariableHelpTexts )
33 Q_GLOBAL_STATIC( QgsStringMap, sGroups )
34 
35 HelpTextHash &functionHelpTexts()
36 {
37  return *sFunctionHelpTexts();
38 }
39 
40 bool QgsExpression::checkExpression( const QString &text, const QgsExpressionContext *context, QString &errorMessage )
41 {
42  QgsExpression exp( text );
43  exp.prepare( context );
44  errorMessage = exp.parserErrorString();
45  return !exp.hasParserError();
46 }
47 
49 {
50  detach();
51  d->mRootNode = ::parseExpression( expression, d->mParserErrorString, d->mParserErrors );
52  d->mEvalErrorString = QString();
53  d->mExp = expression;
54  d->mIsPrepared = false;
55 }
56 
58 {
59  if ( !d->mExp.isNull() )
60  return d->mExp;
61  else
62  return dump();
63 }
64 
65 QString QgsExpression::quotedColumnRef( QString name )
66 {
67  return QStringLiteral( "\"%1\"" ).arg( name.replace( '\"', QLatin1String( "\"\"" ) ) );
68 }
69 
70 QString QgsExpression::quotedString( QString text )
71 {
72  text.replace( '\'', QLatin1String( "''" ) );
73  text.replace( '\\', QLatin1String( "\\\\" ) );
74  text.replace( '\n', QLatin1String( "\\n" ) );
75  text.replace( '\t', QLatin1String( "\\t" ) );
76  return QStringLiteral( "'%1'" ).arg( text );
77 }
78 
79 QString QgsExpression::quotedValue( const QVariant &value )
80 {
81  return quotedValue( value, value.type() );
82 }
83 
84 QString QgsExpression::quotedValue( const QVariant &value, QVariant::Type type )
85 {
86  if ( value.isNull() )
87  return QStringLiteral( "NULL" );
88 
89  switch ( type )
90  {
91  case QVariant::Int:
92  case QVariant::LongLong:
93  case QVariant::Double:
94  return value.toString();
95 
96  case QVariant::Bool:
97  return value.toBool() ? QStringLiteral( "TRUE" ) : QStringLiteral( "FALSE" );
98 
99  case QVariant::List:
100  {
101  QStringList quotedValues;
102  const QVariantList values = value.toList();
103  quotedValues.reserve( values.count() );
104  for ( const QVariant &v : values )
105  {
106  quotedValues += quotedValue( v );
107  }
108  return QStringLiteral( "array( %1 )" ).arg( quotedValues.join( QStringLiteral( ", " ) ) );
109  }
110 
111  default:
112  case QVariant::String:
113  return quotedString( value.toString() );
114  }
115 
116 }
117 
118 bool QgsExpression::isFunctionName( const QString &name )
119 {
120  return functionIndex( name ) != -1;
121 }
122 
123 int QgsExpression::functionIndex( const QString &name )
124 {
125  int count = functionCount();
126  for ( int i = 0; i < count; i++ )
127  {
128  if ( QString::compare( name, QgsExpression::Functions()[i]->name(), Qt::CaseInsensitive ) == 0 )
129  return i;
130  const QStringList aliases = QgsExpression::Functions()[i]->aliases();
131  for ( const QString &alias : aliases )
132  {
133  if ( QString::compare( name, alias, Qt::CaseInsensitive ) == 0 )
134  return i;
135  }
136  }
137  return -1;
138 }
139 
141 {
142  return Functions().size();
143 }
144 
145 
146 QgsExpression::QgsExpression( const QString &expr )
147  : d( new QgsExpressionPrivate )
148 {
149  d->mRootNode = ::parseExpression( expr, d->mParserErrorString, d->mParserErrors );
150  d->mExp = expr;
151  Q_ASSERT( !d->mParserErrorString.isNull() || d->mRootNode );
152 }
153 
155  : d( other.d )
156 {
157  d->ref.ref();
158 }
159 
161 {
162  if ( this != &other )
163  {
164  if ( !d->ref.deref() )
165  {
166  delete d;
167  }
168 
169  d = other.d;
170  d->ref.ref();
171  }
172  return *this;
173 }
174 
175 QgsExpression::operator QString() const
176 {
177  return d->mExp;
178 }
179 
181  : d( new QgsExpressionPrivate )
182 {
183 }
184 
186 {
187  Q_ASSERT( d );
188  if ( !d->ref.deref() )
189  delete d;
190 }
191 
192 bool QgsExpression::operator==( const QgsExpression &other ) const
193 {
194  return ( d == other.d || d->mExp == other.d->mExp );
195 }
196 
198 {
199  return d->mRootNode;
200 }
201 
203 {
204  return d->mParserErrors.count() > 0;
205 }
206 
208 {
209  return d->mParserErrorString;
210 }
211 
212 QList<QgsExpression::ParserError> QgsExpression::parserErrors() const
213 {
214  return d->mParserErrors;
215 }
216 
217 QSet<QString> QgsExpression::referencedColumns() const
218 {
219  if ( !d->mRootNode )
220  return QSet<QString>();
221 
222  return d->mRootNode->referencedColumns();
223 }
224 
226 {
227  if ( !d->mRootNode )
228  return QSet<QString>();
229 
230  return d->mRootNode->referencedVariables();
231 }
232 
234 {
235  if ( !d->mRootNode )
236  return QSet<QString>();
237 
238  return d->mRootNode->referencedFunctions();
239 }
240 
241 QSet<int> QgsExpression::referencedAttributeIndexes( const QgsFields &fields ) const
242 {
243  if ( !d->mRootNode )
244  return QSet<int>();
245 
246  const QSet<QString> referencedFields = d->mRootNode->referencedColumns();
247  QSet<int> referencedIndexes;
248 
249  for ( const QString &fieldName : referencedFields )
250  {
251  if ( fieldName == QgsFeatureRequest::ALL_ATTRIBUTES )
252  {
253  referencedIndexes = fields.allAttributesList().toSet();
254  break;
255  }
256  const int idx = fields.lookupField( fieldName );
257  if ( idx >= 0 )
258  {
259  referencedIndexes << idx;
260  }
261  }
262 
263  return referencedIndexes;
264 }
265 
267 {
268  if ( !d->mRootNode )
269  return false;
270  return d->mRootNode->needsGeometry();
271 }
272 
273 void QgsExpression::initGeomCalculator( const QgsExpressionContext *context )
274 {
275  // Set the geometry calculator from the context if it has not been set by setGeomCalculator()
276  if ( context && ! d->mCalc )
277  {
278  // actually don't do it right away, cos it's expensive to create and only a very small number of expression
279  // functions actually require it. Let's lazily construct it when needed
280  d->mDaEllipsoid = context->variable( QStringLiteral( "project_ellipsoid" ) ).toString();
281  d->mDaCrs = context->variable( QStringLiteral( "_layer_crs" ) ).value<QgsCoordinateReferenceSystem>();
282  d->mDaTransformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
283  }
284 
285  // Set the distance units from the context if it has not been set by setDistanceUnits()
286  if ( context && distanceUnits() == QgsUnitTypes::DistanceUnknownUnit )
287  {
288  QString distanceUnitsStr = context->variable( QStringLiteral( "project_distance_units" ) ).toString();
289  if ( ! distanceUnitsStr.isEmpty() )
291  }
292 
293  // Set the area units from the context if it has not been set by setAreaUnits()
294  if ( context && areaUnits() == QgsUnitTypes::AreaUnknownUnit )
295  {
296  QString areaUnitsStr = context->variable( QStringLiteral( "project_area_units" ) ).toString();
297  if ( ! areaUnitsStr.isEmpty() )
298  setAreaUnits( QgsUnitTypes::stringToAreaUnit( areaUnitsStr ) );
299  }
300 }
301 
302 void QgsExpression::detach()
303 {
304  Q_ASSERT( d );
305 
306  if ( d->ref > 1 )
307  {
308  ( void )d->ref.deref();
309 
310  d = new QgsExpressionPrivate( *d );
311  }
312 }
313 
315 {
316  detach();
317  if ( calc )
318  d->mCalc = std::shared_ptr<QgsDistanceArea>( new QgsDistanceArea( *calc ) );
319  else
320  d->mCalc.reset();
321 }
322 
324 {
325  detach();
326  d->mEvalErrorString = QString();
327  if ( !d->mRootNode )
328  {
329  //re-parse expression. Creation of QgsExpressionContexts may have added extra
330  //known functions since this expression was created, so we have another try
331  //at re-parsing it now that the context must have been created
332  d->mRootNode = ::parseExpression( d->mExp, d->mParserErrorString, d->mParserErrors );
333  }
334 
335  if ( !d->mRootNode )
336  {
337  d->mEvalErrorString = tr( "No root node! Parsing failed?" );
338  return false;
339  }
340 
341  initGeomCalculator( context );
342  d->mIsPrepared = true;
343  return d->mRootNode->prepare( this, context );
344 }
345 
347 {
348  d->mEvalErrorString = QString();
349  if ( !d->mRootNode )
350  {
351  d->mEvalErrorString = tr( "No root node! Parsing failed?" );
352  return QVariant();
353  }
354 
355  return d->mRootNode->eval( this, static_cast<const QgsExpressionContext *>( nullptr ) );
356 }
357 
359 {
360  d->mEvalErrorString = QString();
361  if ( !d->mRootNode )
362  {
363  d->mEvalErrorString = tr( "No root node! Parsing failed?" );
364  return QVariant();
365  }
366 
367  if ( ! d->mIsPrepared )
368  {
369  prepare( context );
370  }
371  return d->mRootNode->eval( this, context );
372 }
373 
375 {
376  return !d->mEvalErrorString.isNull();
377 }
378 
380 {
381  return d->mEvalErrorString;
382 }
383 
384 void QgsExpression::setEvalErrorString( const QString &str )
385 {
386  d->mEvalErrorString = str;
387 }
388 
389 QString QgsExpression::dump() const
390 {
391  if ( !d->mRootNode )
392  return QString();
393 
394  return d->mRootNode->dump();
395 }
396 
398 {
399  if ( !d->mCalc && d->mDaCrs.isValid() )
400  {
401  // calculator IS required, so initialize it now...
402  d->mCalc = std::shared_ptr<QgsDistanceArea>( new QgsDistanceArea() );
403  d->mCalc->setEllipsoid( d->mDaEllipsoid.isEmpty() ? geoNone() : d->mDaEllipsoid );
404  d->mCalc->setSourceCrs( d->mDaCrs, d->mDaTransformContext );
405  }
406 
407  return d->mCalc.get();
408 }
409 
411 {
412  return d->mDistanceUnit;
413 }
414 
416 {
417  d->mDistanceUnit = unit;
418 }
419 
421 {
422  return d->mAreaUnit;
423 }
424 
426 {
427  d->mAreaUnit = unit;
428 }
429 
430 QString QgsExpression::replaceExpressionText( const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea )
431 {
432  QString expr_action;
433 
434  int index = 0;
435  while ( index < action.size() )
436  {
437  static const QRegularExpression sRegEx{ QStringLiteral( "\\[%(.*?)%\\]" ), QRegularExpression::MultilineOption | QRegularExpression::DotMatchesEverythingOption };
438 
439  const QRegularExpressionMatch match = sRegEx.match( action, index );
440  if ( !match.hasMatch() )
441  break;
442 
443  const int pos = action.indexOf( sRegEx, index );
444  const int start = index;
445  index = pos + match.capturedLength( 0 );
446  const QString toReplace = match.captured( 1 ).trimmed();
447  QgsDebugMsgLevel( "Found expression: " + toReplace, 3 );
448 
449  QgsExpression exp( toReplace );
450  if ( exp.hasParserError() )
451  {
452  QgsDebugMsg( "Expression parser error: " + exp.parserErrorString() );
453  expr_action += action.midRef( start, index - start );
454  continue;
455  }
456 
457  if ( distanceArea )
458  {
459  //if QgsDistanceArea specified for area/distance conversion, use it
460  exp.setGeomCalculator( distanceArea );
461  }
462 
463  QVariant result = exp.evaluate( context );
464 
465  if ( exp.hasEvalError() )
466  {
467  QgsDebugMsg( "Expression parser eval error: " + exp.evalErrorString() );
468  expr_action += action.midRef( start, index - start );
469  continue;
470  }
471 
472  QgsDebugMsgLevel( "Expression result is: " + result.toString(), 3 );
473  expr_action += action.mid( start, pos - start ) + result.toString();
474  }
475 
476  expr_action += action.midRef( index );
477 
478  return expr_action;
479 }
480 
481 QSet<QString> QgsExpression::referencedVariables( const QString &text )
482 {
483  QSet<QString> variables;
484  int index = 0;
485  while ( index < text.size() )
486  {
487  QRegExp rx = QRegExp( "\\[%([^\\]]+)%\\]" );
488 
489  int pos = rx.indexIn( text, index );
490  if ( pos < 0 )
491  break;
492 
493  index = pos + rx.matchedLength();
494  QString to_replace = rx.cap( 1 ).trimmed();
495 
496  QgsExpression exp( to_replace );
497  variables.unite( exp.referencedVariables() );
498  }
499 
500  return variables;
501 }
502 
503 double QgsExpression::evaluateToDouble( const QString &text, const double fallbackValue )
504 {
505  bool ok;
506  //first test if text is directly convertible to double
507  // use system locale: e.g. in German locale, user is presented with numbers "1,23" instead of "1.23" in C locale
508  // so we also want to allow user to rewrite it to "5,23" and it is still accepted
509  double convertedValue = QLocale().toDouble( text, &ok );
510  if ( ok )
511  {
512  return convertedValue;
513  }
514 
515  //otherwise try to evaluate as expression
516  QgsExpression expr( text );
517 
518  QgsExpressionContext context;
521 
522  QVariant result = expr.evaluate( &context );
523  convertedValue = result.toDouble( &ok );
524  if ( expr.hasEvalError() || !ok )
525  {
526  return fallbackValue;
527  }
528  return convertedValue;
529 }
530 
531 QString QgsExpression::helpText( QString name )
532 {
533  QgsExpression::initFunctionHelp();
534 
535  if ( !sFunctionHelpTexts()->contains( name ) )
536  return tr( "function help for %1 missing" ).arg( name );
537 
538  const Help &f = ( *sFunctionHelpTexts() )[ name ];
539 
540  name = f.mName;
541  if ( f.mType == tr( "group" ) )
542  {
543  name = group( name );
544  name = name.toLower();
545  }
546 
547  name = name.toHtmlEscaped();
548 
549  QString helpContents( QStringLiteral( "<h3>%1</h3>\n<div class=\"description\"><p>%2</p></div>" )
550  .arg( tr( "%1 %2" ).arg( f.mType, name ),
551  f.mDescription ) );
552 
553  for ( const HelpVariant &v : qgis::as_const( f.mVariants ) )
554  {
555  if ( f.mVariants.size() > 1 )
556  {
557  helpContents += QStringLiteral( "<h3>%1</h3>\n<div class=\"description\">%2</p></div>" ).arg( v.mName, v.mDescription );
558  }
559 
560  if ( f.mType != tr( "group" ) && f.mType != tr( "expression" ) )
561  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"syntax\">\n" ).arg( tr( "Syntax" ) );
562 
563  if ( f.mType == tr( "operator" ) )
564  {
565  if ( v.mArguments.size() == 1 )
566  {
567  helpContents += QStringLiteral( "<code><span class=\"functionname\">%1</span> <span class=\"argument\">%2</span></code>" )
568  .arg( name, v.mArguments[0].mArg );
569  }
570  else if ( v.mArguments.size() == 2 )
571  {
572  helpContents += QStringLiteral( "<code><span class=\"argument\">%1</span> <span class=\"functionname\">%2</span> <span class=\"argument\">%3</span></code>" )
573  .arg( v.mArguments[0].mArg, name, v.mArguments[1].mArg );
574  }
575  }
576  else if ( f.mType != tr( "group" ) && f.mType != tr( "expression" ) )
577  {
578  helpContents += QStringLiteral( "<code><span class=\"functionname\">%1</span>" ).arg( name );
579 
580  bool hasOptionalArgs = false;
581 
582  if ( f.mType == tr( "function" ) && ( f.mName[0] != '$' || !v.mArguments.isEmpty() || v.mVariableLenArguments ) )
583  {
584  helpContents += '(';
585 
586  QString delim;
587  for ( const HelpArg &a : qgis::as_const( v.mArguments ) )
588  {
589  if ( !a.mDescOnly )
590  {
591  if ( a.mOptional )
592  {
593  hasOptionalArgs = true;
594  helpContents += QStringLiteral( "[" );
595  }
596 
597  helpContents += delim;
598  helpContents += QStringLiteral( "<span class=\"argument\">%2%3</span>" ).arg(
599  a.mArg,
600  a.mDefaultVal.isEmpty() ? QString() : '=' + a.mDefaultVal
601  );
602 
603  if ( a.mOptional )
604  helpContents += QStringLiteral( "]" );
605  }
606  delim = QStringLiteral( "," );
607  }
608 
609  if ( v.mVariableLenArguments )
610  {
611  helpContents += QChar( 0x2026 );
612  }
613 
614  helpContents += ')';
615  }
616 
617  helpContents += QLatin1String( "</code>" );
618 
619  if ( hasOptionalArgs )
620  {
621  helpContents += QLatin1String( "<br/><br/>" ) + tr( "[ ] marks optional components" );
622  }
623  }
624 
625  if ( !v.mArguments.isEmpty() )
626  {
627  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"arguments\">\n<table>" ).arg( tr( "Arguments" ) );
628 
629  for ( const HelpArg &a : qgis::as_const( v.mArguments ) )
630  {
631  if ( a.mSyntaxOnly )
632  continue;
633 
634  helpContents += QStringLiteral( "<tr><td class=\"argument\">%1</td><td>%2</td></tr>" ).arg( a.mArg, a.mDescription );
635  }
636 
637  helpContents += QLatin1String( "</table>\n</div>\n" );
638  }
639 
640  if ( !v.mExamples.isEmpty() )
641  {
642  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"examples\">\n<ul>\n" ).arg( tr( "Examples" ) );
643 
644  for ( const HelpExample &e : qgis::as_const( v.mExamples ) )
645  {
646  helpContents += "<li><code>" + e.mExpression + "</code> &rarr; <code>" + e.mReturns + "</code>";
647 
648  if ( !e.mNote.isEmpty() )
649  helpContents += QStringLiteral( " (%1)" ).arg( e.mNote );
650 
651  helpContents += QLatin1String( "</li>\n" );
652  }
653 
654  helpContents += QLatin1String( "</ul>\n</div>\n" );
655  }
656 
657  if ( !v.mNotes.isEmpty() )
658  {
659  helpContents += QStringLiteral( "<h4>%1</h4>\n<div class=\"notes\"><p>%2</p></div>\n" ).arg( tr( "Notes" ), v.mNotes );
660  }
661  }
662 
663  return helpContents;
664 }
665 
666 QStringList QgsExpression::tags( const QString &name )
667 {
668  QStringList tags = QStringList();
669 
670  QgsExpression::initFunctionHelp();
671 
672  if ( sFunctionHelpTexts()->contains( name ) )
673  {
674  const Help &f = ( *sFunctionHelpTexts() )[ name ];
675 
676  for ( const HelpVariant &v : qgis::as_const( f.mVariants ) )
677  {
678  tags << v.mTags;
679  }
680  }
681 
682  return tags;
683 }
684 
685 void QgsExpression::initVariableHelp()
686 {
687  if ( !sVariableHelpTexts()->isEmpty() )
688  return;
689 
690  //global variables
691  sVariableHelpTexts()->insert( QStringLiteral( "qgis_version" ), QCoreApplication::translate( "variable_help", "Current QGIS version string." ) );
692  sVariableHelpTexts()->insert( QStringLiteral( "qgis_version_no" ), QCoreApplication::translate( "variable_help", "Current QGIS version number." ) );
693  sVariableHelpTexts()->insert( QStringLiteral( "qgis_release_name" ), QCoreApplication::translate( "variable_help", "Current QGIS release name." ) );
694  sVariableHelpTexts()->insert( QStringLiteral( "qgis_short_version" ), QCoreApplication::translate( "variable_help", "Short QGIS version string." ) );
695  sVariableHelpTexts()->insert( QStringLiteral( "qgis_os_name" ), QCoreApplication::translate( "variable_help", "Operating system name, e.g., 'windows', 'linux' or 'osx'." ) );
696  sVariableHelpTexts()->insert( QStringLiteral( "qgis_platform" ), QCoreApplication::translate( "variable_help", "QGIS platform, e.g., 'desktop' or 'server'." ) );
697  sVariableHelpTexts()->insert( QStringLiteral( "qgis_locale" ), QCoreApplication::translate( "variable_help", "Two letter identifier for current QGIS locale." ) );
698  sVariableHelpTexts()->insert( QStringLiteral( "user_account_name" ), QCoreApplication::translate( "variable_help", "Current user's operating system account name." ) );
699  sVariableHelpTexts()->insert( QStringLiteral( "user_full_name" ), QCoreApplication::translate( "variable_help", "Current user's operating system user name (if available)." ) );
700 
701  //project variables
702  sVariableHelpTexts()->insert( QStringLiteral( "project_title" ), QCoreApplication::translate( "variable_help", "Title of current project." ) );
703  sVariableHelpTexts()->insert( QStringLiteral( "project_path" ), QCoreApplication::translate( "variable_help", "Full path (including file name) of current project." ) );
704  sVariableHelpTexts()->insert( QStringLiteral( "project_folder" ), QCoreApplication::translate( "variable_help", "Folder for current project." ) );
705  sVariableHelpTexts()->insert( QStringLiteral( "project_filename" ), QCoreApplication::translate( "variable_help", "Filename of current project." ) );
706  sVariableHelpTexts()->insert( QStringLiteral( "project_basename" ), QCoreApplication::translate( "variable_help", "Base name of current project's filename (without path and extension)." ) );
707  sVariableHelpTexts()->insert( QStringLiteral( "project_home" ), QCoreApplication::translate( "variable_help", "Home path of current project." ) );
708  sVariableHelpTexts()->insert( QStringLiteral( "project_crs" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of project (e.g., 'EPSG:4326')." ) );
709  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_definition" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of project (full definition)." ) );
710  sVariableHelpTexts()->insert( QStringLiteral( "project_units" ), QCoreApplication::translate( "variable_help", "Unit of the project's CRS." ) );
711  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_description" ), QCoreApplication::translate( "variable_help", "Name of the coordinate reference system of the project." ) );
712  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_acronym" ), QCoreApplication::translate( "variable_help", "Acronym of the coordinate reference system of the project." ) );
713  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_ellipsoid" ), QCoreApplication::translate( "variable_help", "Acronym of the ellipsoid of the coordinate reference system of the project." ) );
714  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_proj4" ), QCoreApplication::translate( "variable_help", "Proj4 definition of the coordinate reference system of the project." ) );
715  sVariableHelpTexts()->insert( QStringLiteral( "project_crs_wkt" ), QCoreApplication::translate( "variable_help", "WKT definition of the coordinate reference system of the project." ) );
716  sVariableHelpTexts()->insert( QStringLiteral( "project_author" ), QCoreApplication::translate( "variable_help", "Project author, taken from project metadata." ) );
717  sVariableHelpTexts()->insert( QStringLiteral( "project_abstract" ), QCoreApplication::translate( "variable_help", "Project abstract, taken from project metadata." ) );
718  sVariableHelpTexts()->insert( QStringLiteral( "project_creation_date" ), QCoreApplication::translate( "variable_help", "Project creation date, taken from project metadata." ) );
719  sVariableHelpTexts()->insert( QStringLiteral( "project_identifier" ), QCoreApplication::translate( "variable_help", "Project identifier, taken from project metadata." ) );
720  sVariableHelpTexts()->insert( QStringLiteral( "project_keywords" ), QCoreApplication::translate( "variable_help", "Project keywords, taken from project metadata." ) );
721  sVariableHelpTexts()->insert( QStringLiteral( "project_area_units" ), QCoreApplication::translate( "variable_help", "Area unit for current project, used when calculating areas of geometries." ) );
722  sVariableHelpTexts()->insert( QStringLiteral( "project_distance_units" ), QCoreApplication::translate( "variable_help", "Distance unit for current project, used when calculating lengths of geometries." ) );
723  sVariableHelpTexts()->insert( QStringLiteral( "project_ellipsoid" ), QCoreApplication::translate( "variable_help", "Name of ellipsoid of current project, used when calculating geodetic areas and lengths of geometries." ) );
724  sVariableHelpTexts()->insert( QStringLiteral( "layer_ids" ), QCoreApplication::translate( "variable_help", "List of all map layer IDs from the current project." ) );
725  sVariableHelpTexts()->insert( QStringLiteral( "layers" ), QCoreApplication::translate( "variable_help", "List of all map layers from the current project." ) );
726 
727  //layer variables
728  sVariableHelpTexts()->insert( QStringLiteral( "layer_name" ), QCoreApplication::translate( "variable_help", "Name of current layer." ) );
729  sVariableHelpTexts()->insert( QStringLiteral( "layer_id" ), QCoreApplication::translate( "variable_help", "ID of current layer." ) );
730  sVariableHelpTexts()->insert( QStringLiteral( "layer" ), QCoreApplication::translate( "variable_help", "The current layer." ) );
731 
732  //composition variables
733  sVariableHelpTexts()->insert( QStringLiteral( "layout_name" ), QCoreApplication::translate( "variable_help", "Name of composition." ) );
734  sVariableHelpTexts()->insert( QStringLiteral( "layout_numpages" ), QCoreApplication::translate( "variable_help", "Number of pages in composition." ) );
735  sVariableHelpTexts()->insert( QStringLiteral( "layout_page" ), QCoreApplication::translate( "variable_help", "Current page number in composition." ) );
736  sVariableHelpTexts()->insert( QStringLiteral( "layout_pageheight" ), QCoreApplication::translate( "variable_help", "Composition page height in mm." ) );
737  sVariableHelpTexts()->insert( QStringLiteral( "layout_pagewidth" ), QCoreApplication::translate( "variable_help", "Composition page width in mm." ) );
738  sVariableHelpTexts()->insert( QStringLiteral( "layout_dpi" ), QCoreApplication::translate( "variable_help", "Composition resolution (DPI)." ) );
739 
740  //atlas variables
741  sVariableHelpTexts()->insert( QStringLiteral( "atlas_layerid" ), QCoreApplication::translate( "variable_help", "Current atlas coverage layer ID." ) );
742  sVariableHelpTexts()->insert( QStringLiteral( "atlas_layername" ), QCoreApplication::translate( "variable_help", "Current atlas coverage layer name." ) );
743  sVariableHelpTexts()->insert( QStringLiteral( "atlas_totalfeatures" ), QCoreApplication::translate( "variable_help", "Total number of features in atlas." ) );
744  sVariableHelpTexts()->insert( QStringLiteral( "atlas_featurenumber" ), QCoreApplication::translate( "variable_help", "Current atlas feature number." ) );
745  sVariableHelpTexts()->insert( QStringLiteral( "atlas_filename" ), QCoreApplication::translate( "variable_help", "Current atlas file name." ) );
746  sVariableHelpTexts()->insert( QStringLiteral( "atlas_pagename" ), QCoreApplication::translate( "variable_help", "Current atlas page name." ) );
747  sVariableHelpTexts()->insert( QStringLiteral( "atlas_feature" ), QCoreApplication::translate( "variable_help", "Current atlas feature (as feature object)." ) );
748  sVariableHelpTexts()->insert( QStringLiteral( "atlas_featureid" ), QCoreApplication::translate( "variable_help", "Current atlas feature ID." ) );
749  sVariableHelpTexts()->insert( QStringLiteral( "atlas_geometry" ), QCoreApplication::translate( "variable_help", "Current atlas feature geometry." ) );
750 
751  //layout item variables
752  sVariableHelpTexts()->insert( QStringLiteral( "item_id" ), QCoreApplication::translate( "variable_help", "Layout item user ID (not necessarily unique)." ) );
753  sVariableHelpTexts()->insert( QStringLiteral( "item_uuid" ), QCoreApplication::translate( "variable_help", "layout item unique ID." ) );
754  sVariableHelpTexts()->insert( QStringLiteral( "item_left" ), QCoreApplication::translate( "variable_help", "Left position of layout item (in mm)." ) );
755  sVariableHelpTexts()->insert( QStringLiteral( "item_top" ), QCoreApplication::translate( "variable_help", "Top position of layout item (in mm)." ) );
756  sVariableHelpTexts()->insert( QStringLiteral( "item_width" ), QCoreApplication::translate( "variable_help", "Width of layout item (in mm)." ) );
757  sVariableHelpTexts()->insert( QStringLiteral( "item_height" ), QCoreApplication::translate( "variable_help", "Height of layout item (in mm)." ) );
758 
759  //map settings item variables
760  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." ) );
761  sVariableHelpTexts()->insert( QStringLiteral( "map_rotation" ), QCoreApplication::translate( "variable_help", "Current rotation of map." ) );
762  sVariableHelpTexts()->insert( QStringLiteral( "map_scale" ), QCoreApplication::translate( "variable_help", "Current scale of map." ) );
763  sVariableHelpTexts()->insert( QStringLiteral( "map_extent" ), QCoreApplication::translate( "variable_help", "Geometry representing the current extent of the map." ) );
764  sVariableHelpTexts()->insert( QStringLiteral( "map_extent_center" ), QCoreApplication::translate( "variable_help", "Center of map." ) );
765  sVariableHelpTexts()->insert( QStringLiteral( "map_extent_width" ), QCoreApplication::translate( "variable_help", "Width of map." ) );
766  sVariableHelpTexts()->insert( QStringLiteral( "map_extent_height" ), QCoreApplication::translate( "variable_help", "Height of map." ) );
767  sVariableHelpTexts()->insert( QStringLiteral( "map_crs" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of map (e.g., 'EPSG:4326')." ) );
768  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_description" ), QCoreApplication::translate( "variable_help", "Name of the coordinate reference system of the map." ) );
769  sVariableHelpTexts()->insert( QStringLiteral( "map_units" ), QCoreApplication::translate( "variable_help", "Units for map measurements." ) );
770  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_definition" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of the map (full definition)." ) );
771  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_acronym" ), QCoreApplication::translate( "variable_help", "Acronym of the coordinate reference system of the map." ) );
772  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_ellipsoid" ), QCoreApplication::translate( "variable_help", "Acronym of the ellipsoid of the coordinate reference system of the map." ) );
773  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_proj4" ), QCoreApplication::translate( "variable_help", "Proj4 definition of the coordinate reference system of the map." ) );
774  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_wkt" ), QCoreApplication::translate( "variable_help", "WKT definition of the coordinate reference system of the map." ) );
775  sVariableHelpTexts()->insert( QStringLiteral( "map_layer_ids" ), QCoreApplication::translate( "variable_help", "List of map layer IDs visible in the map." ) );
776  sVariableHelpTexts()->insert( QStringLiteral( "map_layers" ), QCoreApplication::translate( "variable_help", "List of map layers visible in the map." ) );
777 
778  sVariableHelpTexts()->insert( QStringLiteral( "map_start_time" ), QCoreApplication::translate( "variable_help", "Start of the map's temporal time range (as a datetime value)" ) );
779  sVariableHelpTexts()->insert( QStringLiteral( "map_end_time" ), QCoreApplication::translate( "variable_help", "End of the map's temporal time range (as a datetime value)" ) );
780  sVariableHelpTexts()->insert( QStringLiteral( "map_interval" ), QCoreApplication::translate( "variable_help", "Duration of the map's temporal time range (as an interval value)" ) );
781 
782  sVariableHelpTexts()->insert( QStringLiteral( "frame_rate" ), QCoreApplication::translate( "variable_help", "Number of frames per second during animation playback" ) );
783  sVariableHelpTexts()->insert( QStringLiteral( "frame_number" ), QCoreApplication::translate( "variable_help", "Current frame number during animation playback" ) );
784  sVariableHelpTexts()->insert( QStringLiteral( "frame_duration" ), QCoreApplication::translate( "variable_help", "Temporal duration of each animation frame (as an interval value)" ) );
785  sVariableHelpTexts()->insert( QStringLiteral( "animation_start_time" ), QCoreApplication::translate( "variable_help", "Start of the animation's overall temporal time range (as a datetime value)" ) );
786  sVariableHelpTexts()->insert( QStringLiteral( "animation_end_time" ), QCoreApplication::translate( "variable_help", "End of the animation's overall temporal time range (as a datetime value)" ) );
787  sVariableHelpTexts()->insert( QStringLiteral( "animation_interval" ), QCoreApplication::translate( "variable_help", "Duration of the animation's overall temporal time range (as an interval value)" ) );
788 
789  // vector tile layer variables
790  sVariableHelpTexts()->insert( QStringLiteral( "zoom_level" ), QCoreApplication::translate( "variable_help", "Zoom level of the tile that is being rendered (derived from the current map scale). Normally in interval [0, 20]." ) );
791 
792  sVariableHelpTexts()->insert( QStringLiteral( "row_number" ), QCoreApplication::translate( "variable_help", "Stores the number of the current row." ) );
793  sVariableHelpTexts()->insert( QStringLiteral( "grid_number" ), QCoreApplication::translate( "variable_help", "Current grid annotation value." ) );
794  sVariableHelpTexts()->insert( QStringLiteral( "grid_axis" ), QCoreApplication::translate( "variable_help", "Current grid annotation axis (e.g., 'x' for longitude, 'y' for latitude)." ) );
795 
796  // map canvas item variables
797  sVariableHelpTexts()->insert( QStringLiteral( "canvas_cursor_point" ), QCoreApplication::translate( "variable_help", "Last cursor position on the canvas in the project's geographical coordinates." ) );
798 
799  // legend canvas item variables
800  sVariableHelpTexts()->insert( QStringLiteral( "legend_title" ), QCoreApplication::translate( "variable_help", "Title of the legend." ) );
801  sVariableHelpTexts()->insert( QStringLiteral( "legend_column_count" ), QCoreApplication::translate( "variable_help", "Number of column in the legend." ) );
802  sVariableHelpTexts()->insert( QStringLiteral( "legend_split_layers" ), QCoreApplication::translate( "variable_help", "Boolean indicating if layers can be split in the legend." ) );
803  sVariableHelpTexts()->insert( QStringLiteral( "legend_wrap_string" ), QCoreApplication::translate( "variable_help", "Characters used to wrap the legend text." ) );
804  sVariableHelpTexts()->insert( QStringLiteral( "legend_filter_by_map" ), QCoreApplication::translate( "variable_help", "Boolean indicating if the content of the legend is filtered by the map." ) );
805  sVariableHelpTexts()->insert( QStringLiteral( "legend_filter_out_atlas" ), QCoreApplication::translate( "variable_help", "Boolean indicating if the Atlas is filtered out of the legend." ) );
806 
807  // scalebar rendering
808  sVariableHelpTexts()->insert( QStringLiteral( "scale_value" ), QCoreApplication::translate( "variable_help", "Current scale bar distance value." ) );
809 
810  // map tool capture variables
811  sVariableHelpTexts()->insert( QStringLiteral( "snapping_results" ), QCoreApplication::translate( "variable_help",
812  "<p>An array with an item for each snapped point.</p>"
813  "<p>Each item is a map with the following keys:</p>"
814  "<dl>"
815  "<dt>valid</dt><dd>Boolean that indicates if the snapping result is valid</dd>"
816  "<dt>layer</dt><dd>The layer on which the snapped feature is</dd>"
817  "<dt>feature_id</dt><dd>The feature id of the snapped feature</dd>"
818  "<dt>vertex_index</dt><dd>The index of the snapped vertex</dd>"
819  "<dt>distance</dt><dd>The distance between the mouse cursor and the snapped point at the time of snapping</dd>"
820  "</dl>" ) );
821 
822 
823  //symbol variables
824  sVariableHelpTexts()->insert( QStringLiteral( "geometry_part_count" ), QCoreApplication::translate( "variable_help", "Number of parts in rendered feature's geometry." ) );
825  sVariableHelpTexts()->insert( QStringLiteral( "geometry_part_num" ), QCoreApplication::translate( "variable_help", "Current geometry part number for feature being rendered." ) );
826  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." ) );
827  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." ) );
828 
829  sVariableHelpTexts()->insert( QStringLiteral( "symbol_color" ), QCoreApplication::translate( "symbol_color", "Color of symbol used to render the feature." ) );
830  sVariableHelpTexts()->insert( QStringLiteral( "symbol_angle" ), QCoreApplication::translate( "symbol_angle", "Angle of symbol used to render the feature (valid for marker symbols only)." ) );
831 
832  sVariableHelpTexts()->insert( QStringLiteral( "symbol_label" ), QCoreApplication::translate( "symbol_label", "Label for the symbol (either a user defined label or the default autogenerated label)." ) );
833  sVariableHelpTexts()->insert( QStringLiteral( "symbol_id" ), QCoreApplication::translate( "symbol_id", "Internal ID of the symbol." ) );
834  sVariableHelpTexts()->insert( QStringLiteral( "symbol_count" ), QCoreApplication::translate( "symbol_count", "Total number of features represented by the symbol." ) );
835 
836  //cluster variables
837  sVariableHelpTexts()->insert( QStringLiteral( "cluster_color" ), QCoreApplication::translate( "cluster_color", "Color of symbols within a cluster, or NULL if symbols have mixed colors." ) );
838  sVariableHelpTexts()->insert( QStringLiteral( "cluster_size" ), QCoreApplication::translate( "cluster_size", "Number of symbols contained within a cluster." ) );
839 
840  //processing variables
841  sVariableHelpTexts()->insert( QStringLiteral( "algorithm_id" ), QCoreApplication::translate( "algorithm_id", "Unique ID for algorithm." ) );
842  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)." ) );
843  sVariableHelpTexts()->insert( QStringLiteral( "model_folder" ), QCoreApplication::translate( "variable_help", "Folder containing current model (or project folder if model is embedded in a project)." ) );
844  sVariableHelpTexts()->insert( QStringLiteral( "model_name" ), QCoreApplication::translate( "variable_help", "Name of current model." ) );
845  sVariableHelpTexts()->insert( QStringLiteral( "model_group" ), QCoreApplication::translate( "variable_help", "Group for current model." ) );
846  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_minx" ), QCoreApplication::translate( "fullextent_minx", "Minimum x-value from full canvas extent (including all layers)." ) );
847  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_miny" ), QCoreApplication::translate( "fullextent_miny", "Minimum y-value from full canvas extent (including all layers)." ) );
848  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_maxx" ), QCoreApplication::translate( "fullextent_maxx", "Maximum x-value from full canvas extent (including all layers)." ) );
849  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_maxy" ), QCoreApplication::translate( "fullextent_maxy", "Maximum y-value from full canvas extent (including all layers)." ) );
850 
851  //provider notification
852  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)." ) );
853 
854  //form context variable
855  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." ) );
856  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." ) );
857 
858  //parent form context variable
859  sVariableHelpTexts()->insert( QStringLiteral( "current_parent_geometry" ), QCoreApplication::translate( "current_parent_geometry",
860  "Only usable in an embedded form context, "
861  "represents the geometry of the feature currently being edited in the parent form.\n"
862  "Can be used in a form/row context to filter the related features using a value "
863  "from the feature currently edited in the parent form, to make sure that the filter "
864  "still works with standalone forms it is recommended to wrap this variable in a "
865  "'coalesce()'." ) );
866  sVariableHelpTexts()->insert( QStringLiteral( "current_parent_feature" ), QCoreApplication::translate( "current_parent_feature",
867  "Only usable in an embedded form context, "
868  "represents the feature currently being edited in the parent form.\n"
869  "Can be used in a form/row context to filter the related features using a value "
870  "from the feature currently edited in the parent form, to make sure that the filter "
871  "still works with standalone forms it is recommended to wrap this variable in a "
872  "'coalesce()'." ) );
873 
874  //form variable
875  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." ) );
876 }
877 
878 QString QgsExpression::variableHelpText( const QString &variableName )
879 {
880  QgsExpression::initVariableHelp();
881  return sVariableHelpTexts()->value( variableName, QString() );
882 }
883 
884 QString QgsExpression::formatVariableHelp( const QString &description, bool showValue, const QVariant &value )
885 {
886  QString text = !description.isEmpty() ? QStringLiteral( "<p>%1</p>" ).arg( description ) : QString();
887  if ( showValue )
888  {
889  QString valueString;
890  if ( !value.isValid() )
891  {
892  valueString = QCoreApplication::translate( "variable_help", "not set" );
893  }
894  else
895  {
896  valueString = QStringLiteral( "<b>%1</b>" ).arg( formatPreviewString( value ) );
897  }
898  text.append( QCoreApplication::translate( "variable_help", "<p>Current value: %1</p>" ).arg( valueString ) );
899  }
900  return text;
901 }
902 
903 QString QgsExpression::group( const QString &name )
904 {
905  if ( sGroups()->isEmpty() )
906  {
907  sGroups()->insert( QStringLiteral( "Aggregates" ), tr( "Aggregates" ) );
908  sGroups()->insert( QStringLiteral( "Arrays" ), tr( "Arrays" ) );
909  sGroups()->insert( QStringLiteral( "Color" ), tr( "Color" ) );
910  sGroups()->insert( QStringLiteral( "Conditionals" ), tr( "Conditionals" ) );
911  sGroups()->insert( QStringLiteral( "Conversions" ), tr( "Conversions" ) );
912  sGroups()->insert( QStringLiteral( "Date and Time" ), tr( "Date and Time" ) );
913  sGroups()->insert( QStringLiteral( "Fields and Values" ), tr( "Fields and Values" ) );
914  sGroups()->insert( QStringLiteral( "Files and Paths" ), tr( "Files and Paths" ) );
915  sGroups()->insert( QStringLiteral( "Fuzzy Matching" ), tr( "Fuzzy Matching" ) );
916  sGroups()->insert( QStringLiteral( "General" ), tr( "General" ) );
917  sGroups()->insert( QStringLiteral( "GeometryGroup" ), tr( "Geometry" ) );
918  sGroups()->insert( QStringLiteral( "Map Layers" ), tr( "Map Layers" ) );
919  sGroups()->insert( QStringLiteral( "Maps" ), tr( "Maps" ) );
920  sGroups()->insert( QStringLiteral( "Math" ), tr( "Math" ) );
921  sGroups()->insert( QStringLiteral( "Operators" ), tr( "Operators" ) );
922  sGroups()->insert( QStringLiteral( "Rasters" ), tr( "Rasters" ) );
923  sGroups()->insert( QStringLiteral( "Record and Attributes" ), tr( "Record and Attributes" ) );
924  sGroups()->insert( QStringLiteral( "String" ), tr( "String" ) );
925  sGroups()->insert( QStringLiteral( "Variables" ), tr( "Variables" ) );
926  sGroups()->insert( QStringLiteral( "Recent (%1)" ), tr( "Recent (%1)" ) );
927  sGroups()->insert( QStringLiteral( "UserGroup" ), tr( "User expressions" ) );
928  }
929 
930  //return the translated name for this group. If group does not
931  //have a translated name in the gGroups hash, return the name
932  //unchanged
933  return sGroups()->value( name, name );
934 }
935 
936 QString QgsExpression::formatPreviewString( const QVariant &value, const bool htmlOutput )
937 {
938  static const int MAX_PREVIEW = 60;
939 
940  const QString startToken = htmlOutput ? QStringLiteral( "<i>&lt;" ) : QStringLiteral( "<" );
941  const QString endToken = htmlOutput ? QStringLiteral( "&gt;</i>" ) : QStringLiteral( ">" );
942 
943  if ( value.canConvert<QgsGeometry>() )
944  {
945  //result is a geometry
946  QgsGeometry geom = value.value<QgsGeometry>();
947  if ( geom.isNull() )
948  return startToken + tr( "empty geometry" ) + endToken;
949  else
950  return startToken + tr( "geometry: %1" ).arg( QgsWkbTypes::displayString( geom.constGet()->wkbType() ) )
951  + endToken;
952  }
953  else if ( value.value< QgsWeakMapLayerPointer >().data() )
954  {
955  return startToken + tr( "map layer" ) + endToken;
956  }
957  else if ( !value.isValid() )
958  {
959  return htmlOutput ? tr( "<i>NULL</i>" ) : QString();
960  }
961  else if ( value.canConvert< QgsFeature >() )
962  {
963  //result is a feature
964  QgsFeature feat = value.value<QgsFeature>();
965  return startToken + tr( "feature: %1" ).arg( feat.id() ) + endToken;
966  }
967  else if ( value.canConvert< QgsInterval >() )
968  {
969  //result is a feature
970  QgsInterval interval = value.value<QgsInterval>();
971  return startToken + tr( "interval: %1 days" ).arg( interval.days() ) + endToken;
972  }
973  else if ( value.canConvert< QgsGradientColorRamp >() )
974  {
975  return startToken + tr( "gradient ramp" ) + endToken;
976  }
977  else if ( value.type() == QVariant::Date )
978  {
979  QDate dt = value.toDate();
980  return startToken + tr( "date: %1" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd" ) ) ) + endToken;
981  }
982  else if ( value.type() == QVariant::Time )
983  {
984  QTime tm = value.toTime();
985  return startToken + tr( "time: %1" ).arg( tm.toString( QStringLiteral( "hh:mm:ss" ) ) ) + endToken;
986  }
987  else if ( value.type() == QVariant::DateTime )
988  {
989  QDateTime dt = value.toDateTime();
990  return startToken + tr( "datetime: %1" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd hh:mm:ss" ) ) ) + endToken;
991  }
992  else if ( value.type() == QVariant::String )
993  {
994  const QString previewString = value.toString();
995  if ( previewString.length() > MAX_PREVIEW + 3 )
996  {
997  return tr( "'%1…'" ).arg( previewString.left( MAX_PREVIEW ) );
998  }
999  else
1000  {
1001  return '\'' + previewString + '\'';
1002  }
1003  }
1004  else if ( value.type() == QVariant::Map )
1005  {
1006  QString mapStr = QStringLiteral( "{" );
1007  const QVariantMap map = value.toMap();
1008  QString separator;
1009  for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1010  {
1011  mapStr.append( separator );
1012  if ( separator.isEmpty() )
1013  separator = QStringLiteral( "," );
1014 
1015  mapStr.append( QStringLiteral( " '%1': %2" ).arg( it.key(), formatPreviewString( it.value(), htmlOutput ) ) );
1016  if ( mapStr.length() > MAX_PREVIEW - 3 )
1017  {
1018  mapStr = tr( "%1…" ).arg( mapStr.left( MAX_PREVIEW - 2 ) );
1019  break;
1020  }
1021  }
1022  if ( !map.empty() )
1023  mapStr += QStringLiteral( " " );
1024  mapStr += QStringLiteral( "}" );
1025  return mapStr;
1026  }
1027  else if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
1028  {
1029  QString listStr = QStringLiteral( "[" );
1030  const QVariantList list = value.toList();
1031  QString separator;
1032  for ( const QVariant &arrayValue : list )
1033  {
1034  listStr.append( separator );
1035  if ( separator.isEmpty() )
1036  separator = QStringLiteral( "," );
1037 
1038  listStr.append( " " );
1039  listStr.append( formatPreviewString( arrayValue, htmlOutput ) );
1040  if ( listStr.length() > MAX_PREVIEW - 3 )
1041  {
1042  listStr = QString( tr( "%1…" ) ).arg( listStr.left( MAX_PREVIEW - 2 ) );
1043  break;
1044  }
1045  }
1046  if ( !list.empty() )
1047  listStr += QStringLiteral( " " );
1048  listStr += QStringLiteral( "]" );
1049  return listStr;
1050  }
1051  else
1052  {
1053  return value.toString();
1054  }
1055 }
1056 
1057 QString QgsExpression::createFieldEqualityExpression( const QString &fieldName, const QVariant &value )
1058 {
1059  QString expr;
1060 
1061  if ( value.isNull() )
1062  expr = QStringLiteral( "%1 IS NULL" ).arg( quotedColumnRef( fieldName ) );
1063  else
1064  expr = QStringLiteral( "%1 = %2" ).arg( quotedColumnRef( fieldName ), quotedValue( value ) );
1065 
1066  return expr;
1067 }
1068 
1070 {
1071  return d->mRootNode;
1072 }
1073 
1075 {
1076  return d->mRootNode && d->mRootNode->nodeType() == QgsExpressionNode::ntColumnRef;
1077 }
1078 
1079 QList<const QgsExpressionNode *> QgsExpression::nodes() const
1080 {
1081  if ( !d->mRootNode )
1082  return QList<const QgsExpressionNode *>();
1083 
1084  return d->mRootNode->nodes();
1085 }
1086 
1087 
1088 
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:324
void setAreaUnits(QgsUnitTypes::AreaUnit unit)
Sets the desired areal units for calculations involving geomCalculator(), e.g., "$area".
static bool isFunctionName(const QString &name)
tells whether the identifier is a name of existing function
Class for parsing and evaluation of expressions (formerly called "search strings").
QgsFeatureId id
Definition: qgsfeature.h:64
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
static QString formatPreviewString(const QVariant &value, bool htmlOutput=true)
Formats an expression result for friendly display to the user.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
void setExpression(const QString &expression)
Set the expression string, will reset the whole internal structure.
static QString group(const QString &group)
Returns the translated name for a function group.
bool operator==(const QgsExpression &other) const
Compares two expressions.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
static Q_INVOKABLE QgsUnitTypes::AreaUnit stringToAreaUnit(const QString &string, bool *ok=nullptr)
Converts a translated string to an areal unit.
QString dump() const
Returns an expression string, constructed from the internal abstract syntax tree. ...
QVariant evaluate()
Evaluate the feature and return the result.
CONSTLATIN1STRING geoNone()
Constant that holds the string representation for "No ellips/No CRS".
Definition: qgis.h:662
QgsExpression()
Create an empty expression.
static double evaluateToDouble(const QString &text, double fallbackValue)
Attempts to evaluate a text string as an expression to a resultant double value.
QgsExpressionNode * parseExpression(const QString &str, QString &parserErrorMsg, QList< QgsExpression::ParserError > &parserErrors)
QString evalErrorString() const
Returns evaluation error.
Container of fields for a vector layer.
Definition: qgsfields.h:42
Unknown areal unit.
Definition: qgsunittypes.h:106
QSet< int > referencedAttributeIndexes(const QgsFields &fields) const
Returns a list of field name indexes obtained from the provided fields.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QList< QgsExpression::ParserError > parserErrors() const
Returns parser error details including location of error.
static QString variableHelpText(const QString &variableName)
Returns the help text for a specified variable.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QStringList tags(const QString &name)
Returns a string list of search tags for a specified function.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:1670
QString parserErrorString() const
Returns parser error.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:704
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions)
QgsUnitTypes::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g., "$area".
static int functionCount()
Returns the number of functions defined in the parser.
static bool checkExpression(const QString &text, const QgsExpressionContext *context, QString &errorMessage)
Tests whether a string is a valid expression.
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QgsAttributeList allAttributesList() const
Utility function to get list of attribute indexes.
Definition: qgsfields.cpp:351
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
bool isValid() const
Checks if this expression is valid.
QgsExpression & operator=(const QgsExpression &other)
Create a copy of this expression.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
Abstract base class for all nodes that can appear in an expression.
Contains information about the context in which a coordinate transform is executed.
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
static const QList< QgsExpressionFunction * > & Functions()
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
static QString formatVariableHelp(const QString &description, bool showValue=true, const QVariant &value=QVariant())
Returns formatted help text for a variable.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsUnitTypes::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e.g., "$length" and "$perimeter".
QString expression() const
Returns the original, unmodified expression string.
A representation of the interval between two datetime values.
Definition: qgsinterval.h:40
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:67
double days() const
Returns the interval duration in days.
Definition: qgsinterval.h:129
Unknown distance unit.
Definition: qgsunittypes.h:78
A general purpose distance and area calculator, capable of performing ellipsoid based calculations...
static Q_INVOKABLE QgsUnitTypes::DistanceUnit stringToDistanceUnit(const QString &string, bool *ok=nullptr)
Converts a translated string to a distance unit.
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 prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
void setDistanceUnits(QgsUnitTypes::DistanceUnit unit)
Sets the desired distance units for calculations involving geomCalculator(), e.g., "$length" and "$perimeter".
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:458
This class represents a coordinate reference system (CRS).
bool isField() const
Checks whether an expression consists only of a single field reference.
HelpTextHash & functionHelpTexts()
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes)
static QString displayString(Type type)
Returns a display string type for a WKB type, e.g., the geometry name used in WKT geometry representa...
static QString helpText(QString name)
Returns the help text for a specified function.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required...
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorramp.h:139
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
QList< const QgsExpressionNode * > nodes() const
Returns a list of all nodes which are used in this expression.
AreaUnit
Units of area.
Definition: qgsunittypes.h:93
QSet< QString > referencedFunctions() const
Returns a list of the names of all functions which are used in this expression.
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...