QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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 
48 void QgsExpression::setExpression( const QString &expression )
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( QLatin1String( ", " ) ) );
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 = qgis::listToSet( fields.allAttributesList() );
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 += QLatin1Char( '[' );
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 += QLatin1Char( ']' );
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_last_saved" ), QCoreApplication::translate( "variable_help", "Date/time when project was last saved." ) );
721  sVariableHelpTexts()->insert( QStringLiteral( "project_keywords" ), QCoreApplication::translate( "variable_help", "Project keywords, taken from project metadata." ) );
722  sVariableHelpTexts()->insert( QStringLiteral( "project_area_units" ), QCoreApplication::translate( "variable_help", "Area unit for current project, used when calculating areas of geometries." ) );
723  sVariableHelpTexts()->insert( QStringLiteral( "project_distance_units" ), QCoreApplication::translate( "variable_help", "Distance unit for current project, used when calculating lengths of geometries." ) );
724  sVariableHelpTexts()->insert( QStringLiteral( "project_ellipsoid" ), QCoreApplication::translate( "variable_help", "Name of ellipsoid of current project, used when calculating geodetic areas and lengths of geometries." ) );
725  sVariableHelpTexts()->insert( QStringLiteral( "layer_ids" ), QCoreApplication::translate( "variable_help", "List of all map layer IDs from the current project." ) );
726  sVariableHelpTexts()->insert( QStringLiteral( "layers" ), QCoreApplication::translate( "variable_help", "List of all map layers from the current project." ) );
727 
728  //layer variables
729  sVariableHelpTexts()->insert( QStringLiteral( "layer_name" ), QCoreApplication::translate( "variable_help", "Name of current layer." ) );
730  sVariableHelpTexts()->insert( QStringLiteral( "layer_id" ), QCoreApplication::translate( "variable_help", "ID of current layer." ) );
731  sVariableHelpTexts()->insert( QStringLiteral( "layer" ), QCoreApplication::translate( "variable_help", "The current layer." ) );
732 
733  //composition variables
734  sVariableHelpTexts()->insert( QStringLiteral( "layout_name" ), QCoreApplication::translate( "variable_help", "Name of composition." ) );
735  sVariableHelpTexts()->insert( QStringLiteral( "layout_numpages" ), QCoreApplication::translate( "variable_help", "Number of pages in composition." ) );
736  sVariableHelpTexts()->insert( QStringLiteral( "layout_page" ), QCoreApplication::translate( "variable_help", "Current page number in composition." ) );
737  sVariableHelpTexts()->insert( QStringLiteral( "layout_pageheight" ), QCoreApplication::translate( "variable_help", "Composition page height in mm." ) );
738  sVariableHelpTexts()->insert( QStringLiteral( "layout_pagewidth" ), QCoreApplication::translate( "variable_help", "Composition page width in mm." ) );
739  sVariableHelpTexts()->insert( QStringLiteral( "layout_pageoffsets" ), QCoreApplication::translate( "variable_help", "Array of Y coordinate of the top of each page." ) );
740  sVariableHelpTexts()->insert( QStringLiteral( "layout_dpi" ), QCoreApplication::translate( "variable_help", "Composition resolution (DPI)." ) );
741 
742  //atlas variables
743  sVariableHelpTexts()->insert( QStringLiteral( "atlas_layerid" ), QCoreApplication::translate( "variable_help", "Current atlas coverage layer ID." ) );
744  sVariableHelpTexts()->insert( QStringLiteral( "atlas_layername" ), QCoreApplication::translate( "variable_help", "Current atlas coverage layer name." ) );
745  sVariableHelpTexts()->insert( QStringLiteral( "atlas_totalfeatures" ), QCoreApplication::translate( "variable_help", "Total number of features in atlas." ) );
746  sVariableHelpTexts()->insert( QStringLiteral( "atlas_featurenumber" ), QCoreApplication::translate( "variable_help", "Current atlas feature number." ) );
747  sVariableHelpTexts()->insert( QStringLiteral( "atlas_filename" ), QCoreApplication::translate( "variable_help", "Current atlas file name." ) );
748  sVariableHelpTexts()->insert( QStringLiteral( "atlas_pagename" ), QCoreApplication::translate( "variable_help", "Current atlas page name." ) );
749  sVariableHelpTexts()->insert( QStringLiteral( "atlas_feature" ), QCoreApplication::translate( "variable_help", "Current atlas feature (as feature object)." ) );
750  sVariableHelpTexts()->insert( QStringLiteral( "atlas_featureid" ), QCoreApplication::translate( "variable_help", "Current atlas feature ID." ) );
751  sVariableHelpTexts()->insert( QStringLiteral( "atlas_geometry" ), QCoreApplication::translate( "variable_help", "Current atlas feature geometry." ) );
752 
753  //layout item variables
754  sVariableHelpTexts()->insert( QStringLiteral( "item_id" ), QCoreApplication::translate( "variable_help", "Layout item user-assigned ID (not necessarily unique)." ) );
755  sVariableHelpTexts()->insert( QStringLiteral( "item_uuid" ), QCoreApplication::translate( "variable_help", "layout item unique ID." ) );
756  sVariableHelpTexts()->insert( QStringLiteral( "item_left" ), QCoreApplication::translate( "variable_help", "Left position of layout item (in mm)." ) );
757  sVariableHelpTexts()->insert( QStringLiteral( "item_top" ), QCoreApplication::translate( "variable_help", "Top position of layout item (in mm)." ) );
758  sVariableHelpTexts()->insert( QStringLiteral( "item_width" ), QCoreApplication::translate( "variable_help", "Width of layout item (in mm)." ) );
759  sVariableHelpTexts()->insert( QStringLiteral( "item_height" ), QCoreApplication::translate( "variable_help", "Height of layout item (in mm)." ) );
760 
761  //map settings item variables
762  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." ) );
763  sVariableHelpTexts()->insert( QStringLiteral( "map_rotation" ), QCoreApplication::translate( "variable_help", "Current rotation of map." ) );
764  sVariableHelpTexts()->insert( QStringLiteral( "map_scale" ), QCoreApplication::translate( "variable_help", "Current scale of map." ) );
765  sVariableHelpTexts()->insert( QStringLiteral( "map_extent" ), QCoreApplication::translate( "variable_help", "Geometry representing the current extent of the map." ) );
766  sVariableHelpTexts()->insert( QStringLiteral( "map_extent_center" ), QCoreApplication::translate( "variable_help", "Center of map." ) );
767  sVariableHelpTexts()->insert( QStringLiteral( "map_extent_width" ), QCoreApplication::translate( "variable_help", "Width of map." ) );
768  sVariableHelpTexts()->insert( QStringLiteral( "map_extent_height" ), QCoreApplication::translate( "variable_help", "Height of map." ) );
769  sVariableHelpTexts()->insert( QStringLiteral( "map_crs" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of map (e.g., 'EPSG:4326')." ) );
770  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_description" ), QCoreApplication::translate( "variable_help", "Name of the coordinate reference system of the map." ) );
771  sVariableHelpTexts()->insert( QStringLiteral( "map_units" ), QCoreApplication::translate( "variable_help", "Units for map measurements." ) );
772  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_definition" ), QCoreApplication::translate( "variable_help", "Coordinate reference system of the map (full definition)." ) );
773  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_acronym" ), QCoreApplication::translate( "variable_help", "Acronym of the coordinate reference system of the map." ) );
774  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_ellipsoid" ), QCoreApplication::translate( "variable_help", "Acronym of the ellipsoid of the coordinate reference system of the map." ) );
775  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_proj4" ), QCoreApplication::translate( "variable_help", "Proj4 definition of the coordinate reference system of the map." ) );
776  sVariableHelpTexts()->insert( QStringLiteral( "map_crs_wkt" ), QCoreApplication::translate( "variable_help", "WKT definition of the coordinate reference system of the map." ) );
777  sVariableHelpTexts()->insert( QStringLiteral( "map_layer_ids" ), QCoreApplication::translate( "variable_help", "List of map layer IDs visible in the map." ) );
778  sVariableHelpTexts()->insert( QStringLiteral( "map_layers" ), QCoreApplication::translate( "variable_help", "List of map layers visible in the map." ) );
779 
780  sVariableHelpTexts()->insert( QStringLiteral( "map_start_time" ), QCoreApplication::translate( "variable_help", "Start of the map's temporal time range (as a datetime value)" ) );
781  sVariableHelpTexts()->insert( QStringLiteral( "map_end_time" ), QCoreApplication::translate( "variable_help", "End of the map's temporal time range (as a datetime value)" ) );
782  sVariableHelpTexts()->insert( QStringLiteral( "map_interval" ), QCoreApplication::translate( "variable_help", "Duration of the map's temporal time range (as an interval value)" ) );
783 
784  sVariableHelpTexts()->insert( QStringLiteral( "frame_rate" ), QCoreApplication::translate( "variable_help", "Number of frames per second during animation playback" ) );
785  sVariableHelpTexts()->insert( QStringLiteral( "frame_number" ), QCoreApplication::translate( "variable_help", "Current frame number during animation playback" ) );
786  sVariableHelpTexts()->insert( QStringLiteral( "frame_duration" ), QCoreApplication::translate( "variable_help", "Temporal duration of each animation frame (as an interval value)" ) );
787  sVariableHelpTexts()->insert( QStringLiteral( "animation_start_time" ), QCoreApplication::translate( "variable_help", "Start of the animation's overall temporal time range (as a datetime value)" ) );
788  sVariableHelpTexts()->insert( QStringLiteral( "animation_end_time" ), QCoreApplication::translate( "variable_help", "End of the animation's overall temporal time range (as a datetime value)" ) );
789  sVariableHelpTexts()->insert( QStringLiteral( "animation_interval" ), QCoreApplication::translate( "variable_help", "Duration of the animation's overall temporal time range (as an interval value)" ) );
790 
791  // vector tile layer variables
792  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]." ) );
793  sVariableHelpTexts()->insert( QStringLiteral( "vector_tile_zoom" ), QCoreApplication::translate( "variable_help", "Exact zoom level of the tile 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 interpolated values between two integer zoom levels." ) );
794 
795  sVariableHelpTexts()->insert( QStringLiteral( "row_number" ), QCoreApplication::translate( "variable_help", "Stores the number of the current row." ) );
796  sVariableHelpTexts()->insert( QStringLiteral( "grid_number" ), QCoreApplication::translate( "variable_help", "Current grid annotation value." ) );
797  sVariableHelpTexts()->insert( QStringLiteral( "grid_axis" ), QCoreApplication::translate( "variable_help", "Current grid annotation axis (e.g., 'x' for longitude, 'y' for latitude)." ) );
798  sVariableHelpTexts()->insert( QStringLiteral( "column_number" ), QCoreApplication::translate( "variable_help", "Stores the number of the current column." ) );
799 
800  // map canvas item variables
801  sVariableHelpTexts()->insert( QStringLiteral( "canvas_cursor_point" ), QCoreApplication::translate( "variable_help", "Last cursor position on the canvas in the project's geographical coordinates." ) );
802 
803  // legend canvas item variables
804  sVariableHelpTexts()->insert( QStringLiteral( "legend_title" ), QCoreApplication::translate( "variable_help", "Title of the legend." ) );
805  sVariableHelpTexts()->insert( QStringLiteral( "legend_column_count" ), QCoreApplication::translate( "variable_help", "Number of column in the legend." ) );
806  sVariableHelpTexts()->insert( QStringLiteral( "legend_split_layers" ), QCoreApplication::translate( "variable_help", "Boolean indicating if layers can be split in the legend." ) );
807  sVariableHelpTexts()->insert( QStringLiteral( "legend_wrap_string" ), QCoreApplication::translate( "variable_help", "Characters used to wrap the legend text." ) );
808  sVariableHelpTexts()->insert( QStringLiteral( "legend_filter_by_map" ), QCoreApplication::translate( "variable_help", "Boolean indicating if the content of the legend is filtered by the map." ) );
809  sVariableHelpTexts()->insert( QStringLiteral( "legend_filter_out_atlas" ), QCoreApplication::translate( "variable_help", "Boolean indicating if the Atlas is filtered out of the legend." ) );
810 
811  // scalebar rendering
812  sVariableHelpTexts()->insert( QStringLiteral( "scale_value" ), QCoreApplication::translate( "variable_help", "Current scale bar distance value." ) );
813 
814  // map tool capture variables
815  sVariableHelpTexts()->insert( QStringLiteral( "snapping_results" ), QCoreApplication::translate( "variable_help",
816  "<p>An array with an item for each snapped point.</p>"
817  "<p>Each item is a map with the following keys:</p>"
818  "<dl>"
819  "<dt>valid</dt><dd>Boolean that indicates if the snapping result is valid</dd>"
820  "<dt>layer</dt><dd>The layer on which the snapped feature is</dd>"
821  "<dt>feature_id</dt><dd>The feature id of the snapped feature</dd>"
822  "<dt>vertex_index</dt><dd>The index of the snapped vertex</dd>"
823  "<dt>distance</dt><dd>The distance between the mouse cursor and the snapped point at the time of snapping</dd>"
824  "</dl>" ) );
825 
826 
827  //symbol variables
828  sVariableHelpTexts()->insert( QStringLiteral( "geometry_part_count" ), QCoreApplication::translate( "variable_help", "Number of parts in rendered feature's geometry." ) );
829  sVariableHelpTexts()->insert( QStringLiteral( "geometry_part_num" ), QCoreApplication::translate( "variable_help", "Current geometry part number for feature being rendered." ) );
830  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." ) );
831  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." ) );
832 
833  sVariableHelpTexts()->insert( QStringLiteral( "symbol_color" ), QCoreApplication::translate( "symbol_color", "Color of symbol used to render the feature." ) );
834  sVariableHelpTexts()->insert( QStringLiteral( "symbol_angle" ), QCoreApplication::translate( "symbol_angle", "Angle of symbol used to render the feature (valid for marker symbols only)." ) );
835  sVariableHelpTexts()->insert( QStringLiteral( "symbol_layer_count" ), QCoreApplication::translate( "symbol_layer_count", "Total number of symbol layers in the symbol." ) );
836  sVariableHelpTexts()->insert( QStringLiteral( "symbol_layer_index" ), QCoreApplication::translate( "symbol_layer_index", "Current symbol layer index." ) );
837  sVariableHelpTexts()->insert( QStringLiteral( "symbol_marker_row" ), QCoreApplication::translate( "symbol_marker_row", "Row number for marker (valid for point pattern fills only)." ) );
838  sVariableHelpTexts()->insert( QStringLiteral( "symbol_marker_column" ), QCoreApplication::translate( "symbol_marker_column", "Column number for marker (valid for point pattern fills only)." ) );
839 
840  sVariableHelpTexts()->insert( QStringLiteral( "symbol_label" ), QCoreApplication::translate( "symbol_label", "Label for the symbol (either a user defined label or the default autogenerated label)." ) );
841  sVariableHelpTexts()->insert( QStringLiteral( "symbol_id" ), QCoreApplication::translate( "symbol_id", "Internal ID of the symbol." ) );
842  sVariableHelpTexts()->insert( QStringLiteral( "symbol_count" ), QCoreApplication::translate( "symbol_count", "Total number of features represented by the symbol." ) );
843 
844  //cluster variables
845  sVariableHelpTexts()->insert( QStringLiteral( "cluster_color" ), QCoreApplication::translate( "cluster_color", "Color of symbols within a cluster, or NULL if symbols have mixed colors." ) );
846  sVariableHelpTexts()->insert( QStringLiteral( "cluster_size" ), QCoreApplication::translate( "cluster_size", "Number of symbols contained within a cluster." ) );
847 
848  //processing variables
849  sVariableHelpTexts()->insert( QStringLiteral( "algorithm_id" ), QCoreApplication::translate( "algorithm_id", "Unique ID for algorithm." ) );
850  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)." ) );
851  sVariableHelpTexts()->insert( QStringLiteral( "model_folder" ), QCoreApplication::translate( "variable_help", "Folder containing current model (or project folder if model is embedded in a project)." ) );
852  sVariableHelpTexts()->insert( QStringLiteral( "model_name" ), QCoreApplication::translate( "variable_help", "Name of current model." ) );
853  sVariableHelpTexts()->insert( QStringLiteral( "model_group" ), QCoreApplication::translate( "variable_help", "Group for current model." ) );
854  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_minx" ), QCoreApplication::translate( "fullextent_minx", "Minimum x-value from full canvas extent (including all layers)." ) );
855  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_miny" ), QCoreApplication::translate( "fullextent_miny", "Minimum y-value from full canvas extent (including all layers)." ) );
856  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_maxx" ), QCoreApplication::translate( "fullextent_maxx", "Maximum x-value from full canvas extent (including all layers)." ) );
857  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_maxy" ), QCoreApplication::translate( "fullextent_maxy", "Maximum y-value from full canvas extent (including all layers)." ) );
858 
859  //provider notification
860  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)." ) );
861 
862  //form context variable
863  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." ) );
864  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." ) );
865 
866  //parent form context variable
867  sVariableHelpTexts()->insert( QStringLiteral( "current_parent_geometry" ), QCoreApplication::translate( "current_parent_geometry",
868  "Only usable in an embedded form context, "
869  "represents the geometry of the feature currently being edited in the parent form.\n"
870  "Can be used in a form/row context to filter the related features using a value "
871  "from the feature currently edited in the parent form, to make sure that the filter "
872  "still works with standalone forms it is recommended to wrap this variable in a "
873  "'coalesce()'." ) );
874  sVariableHelpTexts()->insert( QStringLiteral( "current_parent_feature" ), QCoreApplication::translate( "current_parent_feature",
875  "Only usable in an embedded form context, "
876  "represents the feature currently being edited in the parent form.\n"
877  "Can be used in a form/row context to filter the related features using a value "
878  "from the feature currently edited in the parent form, to make sure that the filter "
879  "still works with standalone forms it is recommended to wrap this variable in a "
880  "'coalesce()'." ) );
881 
882  //form variable
883  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." ) );
884 }
885 
886 QString QgsExpression::variableHelpText( const QString &variableName )
887 {
888  QgsExpression::initVariableHelp();
889  return sVariableHelpTexts()->value( variableName, QString() );
890 }
891 
892 QString QgsExpression::formatVariableHelp( const QString &description, bool showValue, const QVariant &value )
893 {
894  QString text = !description.isEmpty() ? QStringLiteral( "<p>%1</p>" ).arg( description ) : QString();
895  if ( showValue )
896  {
897  QString valueString;
898  if ( !value.isValid() )
899  {
900  valueString = QCoreApplication::translate( "variable_help", "not set" );
901  }
902  else
903  {
904  valueString = QStringLiteral( "<b>%1</b>" ).arg( formatPreviewString( value ) );
905  }
906  text.append( QCoreApplication::translate( "variable_help", "<p>Current value: %1</p>" ).arg( valueString ) );
907  }
908  return text;
909 }
910 
911 QString QgsExpression::group( const QString &name )
912 {
913  if ( sGroups()->isEmpty() )
914  {
915  sGroups()->insert( QStringLiteral( "Aggregates" ), tr( "Aggregates" ) );
916  sGroups()->insert( QStringLiteral( "Arrays" ), tr( "Arrays" ) );
917  sGroups()->insert( QStringLiteral( "Color" ), tr( "Color" ) );
918  sGroups()->insert( QStringLiteral( "Conditionals" ), tr( "Conditionals" ) );
919  sGroups()->insert( QStringLiteral( "Conversions" ), tr( "Conversions" ) );
920  sGroups()->insert( QStringLiteral( "Date and Time" ), tr( "Date and Time" ) );
921  sGroups()->insert( QStringLiteral( "Fields and Values" ), tr( "Fields and Values" ) );
922  sGroups()->insert( QStringLiteral( "Files and Paths" ), tr( "Files and Paths" ) );
923  sGroups()->insert( QStringLiteral( "Fuzzy Matching" ), tr( "Fuzzy Matching" ) );
924  sGroups()->insert( QStringLiteral( "General" ), tr( "General" ) );
925  sGroups()->insert( QStringLiteral( "GeometryGroup" ), tr( "Geometry" ) );
926  sGroups()->insert( QStringLiteral( "Map Layers" ), tr( "Map Layers" ) );
927  sGroups()->insert( QStringLiteral( "Maps" ), tr( "Maps" ) );
928  sGroups()->insert( QStringLiteral( "Math" ), tr( "Math" ) );
929  sGroups()->insert( QStringLiteral( "Operators" ), tr( "Operators" ) );
930  sGroups()->insert( QStringLiteral( "Rasters" ), tr( "Rasters" ) );
931  sGroups()->insert( QStringLiteral( "Record and Attributes" ), tr( "Record and Attributes" ) );
932  sGroups()->insert( QStringLiteral( "String" ), tr( "String" ) );
933  sGroups()->insert( QStringLiteral( "Variables" ), tr( "Variables" ) );
934  sGroups()->insert( QStringLiteral( "Recent (%1)" ), tr( "Recent (%1)" ) );
935  sGroups()->insert( QStringLiteral( "UserGroup" ), tr( "User expressions" ) );
936  }
937 
938  //return the translated name for this group. If group does not
939  //have a translated name in the gGroups hash, return the name
940  //unchanged
941  return sGroups()->value( name, name );
942 }
943 
944 QString QgsExpression::formatPreviewString( const QVariant &value, const bool htmlOutput )
945 {
946  static const int MAX_PREVIEW = 60;
947 
948  const QString startToken = htmlOutput ? QStringLiteral( "<i>&lt;" ) : QStringLiteral( "<" );
949  const QString endToken = htmlOutput ? QStringLiteral( "&gt;</i>" ) : QStringLiteral( ">" );
950 
951  if ( value.canConvert<QgsGeometry>() )
952  {
953  //result is a geometry
954  QgsGeometry geom = value.value<QgsGeometry>();
955  if ( geom.isNull() )
956  return startToken + tr( "empty geometry" ) + endToken;
957  else
958  return startToken + tr( "geometry: %1" ).arg( QgsWkbTypes::displayString( geom.constGet()->wkbType() ) )
959  + endToken;
960  }
961  else if ( value.value< QgsWeakMapLayerPointer >().data() )
962  {
963  return startToken + tr( "map layer" ) + endToken;
964  }
965  else if ( !value.isValid() )
966  {
967  return htmlOutput ? tr( "<i>NULL</i>" ) : QString();
968  }
969  else if ( value.canConvert< QgsFeature >() )
970  {
971  //result is a feature
972  QgsFeature feat = value.value<QgsFeature>();
973  return startToken + tr( "feature: %1" ).arg( feat.id() ) + endToken;
974  }
975  else if ( value.canConvert< QgsInterval >() )
976  {
977  QgsInterval interval = value.value<QgsInterval>();
978  if ( interval.days() > 1 )
979  {
980  return startToken + tr( "interval: %1 days" ).arg( interval.days() ) + endToken;
981  }
982  else if ( interval.hours() > 1 )
983  {
984  return startToken + tr( "interval: %1 hours" ).arg( interval.hours() ) + endToken;
985  }
986  else if ( interval.minutes() > 1 )
987  {
988  return startToken + tr( "interval: %1 minutes" ).arg( interval.minutes() ) + endToken;
989  }
990  else
991  {
992  return startToken + tr( "interval: %1 seconds" ).arg( interval.seconds() ) + endToken;
993  }
994  }
995  else if ( value.canConvert< QgsGradientColorRamp >() )
996  {
997  return startToken + tr( "gradient ramp" ) + endToken;
998  }
999  else if ( value.type() == QVariant::Date )
1000  {
1001  QDate dt = value.toDate();
1002  return startToken + tr( "date: %1" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd" ) ) ) + endToken;
1003  }
1004  else if ( value.type() == QVariant::Time )
1005  {
1006  QTime tm = value.toTime();
1007  return startToken + tr( "time: %1" ).arg( tm.toString( QStringLiteral( "hh:mm:ss" ) ) ) + endToken;
1008  }
1009  else if ( value.type() == QVariant::DateTime )
1010  {
1011  QDateTime dt = value.toDateTime();
1012  return startToken + tr( "datetime: %1" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd hh:mm:ss" ) ) ) + endToken;
1013  }
1014  else if ( value.type() == QVariant::String )
1015  {
1016  const QString previewString = value.toString();
1017  if ( previewString.length() > MAX_PREVIEW + 3 )
1018  {
1019  return tr( "'%1…'" ).arg( previewString.left( MAX_PREVIEW ) );
1020  }
1021  else
1022  {
1023  return '\'' + previewString + '\'';
1024  }
1025  }
1026  else if ( value.type() == QVariant::Map )
1027  {
1028  QString mapStr = QStringLiteral( "{" );
1029  const QVariantMap map = value.toMap();
1030  QString separator;
1031  for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1032  {
1033  mapStr.append( separator );
1034  if ( separator.isEmpty() )
1035  separator = QStringLiteral( "," );
1036 
1037  mapStr.append( QStringLiteral( " '%1': %2" ).arg( it.key(), formatPreviewString( it.value(), htmlOutput ) ) );
1038  if ( mapStr.length() > MAX_PREVIEW - 3 )
1039  {
1040  mapStr = tr( "%1…" ).arg( mapStr.left( MAX_PREVIEW - 2 ) );
1041  break;
1042  }
1043  }
1044  if ( !map.empty() )
1045  mapStr += QLatin1Char( ' ' );
1046  mapStr += QLatin1Char( '}' );
1047  return mapStr;
1048  }
1049  else if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
1050  {
1051  QString listStr = QStringLiteral( "[" );
1052  const QVariantList list = value.toList();
1053  QString separator;
1054  for ( const QVariant &arrayValue : list )
1055  {
1056  listStr.append( separator );
1057  if ( separator.isEmpty() )
1058  separator = QStringLiteral( "," );
1059 
1060  listStr.append( " " );
1061  listStr.append( formatPreviewString( arrayValue, htmlOutput ) );
1062  if ( listStr.length() > MAX_PREVIEW - 3 )
1063  {
1064  listStr = QString( tr( "%1…" ) ).arg( listStr.left( MAX_PREVIEW - 2 ) );
1065  break;
1066  }
1067  }
1068  if ( !list.empty() )
1069  listStr += QLatin1Char( ' ' );
1070  listStr += QLatin1Char( ']' );
1071  return listStr;
1072  }
1073  else
1074  {
1075  return value.toString();
1076  }
1077 }
1078 
1079 QString QgsExpression::createFieldEqualityExpression( const QString &fieldName, const QVariant &value )
1080 {
1081  QString expr;
1082 
1083  if ( value.isNull() )
1084  expr = QStringLiteral( "%1 IS NULL" ).arg( quotedColumnRef( fieldName ) );
1085  else
1086  expr = QStringLiteral( "%1 = %2" ).arg( quotedColumnRef( fieldName ), quotedValue( value ) );
1087 
1088  return expr;
1089 }
1090 
1092 {
1093  return d->mRootNode;
1094 }
1095 
1097 {
1098  return d->mRootNode && d->mRootNode->nodeType() == QgsExpressionNode::ntColumnRef;
1099 }
1100 
1101 QList<const QgsExpressionNode *> QgsExpression::nodes() const
1102 {
1103  if ( !d->mRootNode )
1104  return QList<const QgsExpressionNode *>();
1105 
1106  return d->mRootNode->nodes();
1107 }
1108 
1109 
1110 
QgsExpression::parserErrors
QList< QgsExpression::ParserError > parserErrors() const
Returns parser error details including location of error.
Definition: qgsexpression.cpp:212
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:370
qgsexpressioncontextutils.h
qgsexpression_p.h
QgsFeature::id
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
QgsExpression::quotedString
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes)
Definition: qgsexpression.cpp:70
QgsInterval::seconds
double seconds() const
Returns the interval duration in seconds.
Definition: qgsinterval.h:168
QgsWkbTypes::displayString
static QString displayString(Type type) SIP_HOLDGIL
Returns a display string type for a WKB type, e.g., the geometry name used in WKT geometry representa...
Definition: qgswkbtypes.cpp:145
qgsfeaturerequest.h
QgsGradientColorRamp
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorramp.h:151
QgsExpression::setDistanceUnits
void setDistanceUnits(QgsUnitTypes::DistanceUnit unit)
Sets the desired distance units for calculations involving geomCalculator(), e.g.,...
Definition: qgsexpression.cpp:415
QgsExpressionContextUtils::globalScope
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Definition: qgsexpressioncontextutils.cpp:34
QgsExpression::operator==
bool operator==(const QgsExpression &other) const
Compares two expressions.
Definition: qgsexpression.cpp:192
QgsExpression::evalErrorString
QString evalErrorString() const
Returns evaluation error.
Definition: qgsexpression.cpp:379
QgsUnitTypes::stringToDistanceUnit
static Q_INVOKABLE QgsUnitTypes::DistanceUnit stringToDistanceUnit(const QString &string, bool *ok=nullptr)
Converts a translated string to a distance unit.
Definition: qgsunittypes.cpp:312
QgsExpression::referencedColumns
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
Definition: qgsexpression.cpp:217
QgsExpression::formatVariableHelp
static QString formatVariableHelp(const QString &description, bool showValue=true, const QVariant &value=QVariant())
Returns formatted help text for a variable.
Definition: qgsexpression.cpp:892
QgsUnitTypes::DistanceUnknownUnit
@ DistanceUnknownUnit
Unknown distance unit.
Definition: qgsunittypes.h:78
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
qgsexpression.h
QgsExpression::dump
QString dump() const
Returns an expression string, constructed from the internal abstract syntax tree.
Definition: qgsexpression.cpp:389
QgsExpressionNode::ntColumnRef
@ ntColumnRef
Definition: qgsexpressionnode.h:81
QgsGeometry::isNull
Q_GADGET bool isNull
Definition: qgsgeometry.h:126
QgsExpression::geomCalculator
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
Definition: qgsexpression.cpp:397
QgsExpression::QgsExpression
QgsExpression()
Create an empty expression.
Definition: qgsexpression.cpp:180
QgsExpression::rootNode
const QgsExpressionNode * rootNode() const
Returns the root node of the expression.
Definition: qgsexpression.cpp:1091
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsExpression::referencedAttributeIndexes
QSet< int > referencedAttributeIndexes(const QgsFields &fields) const
Returns a list of field name indexes obtained from the provided fields.
Definition: qgsexpression.cpp:241
QgsExpression::isValid
bool isValid() const
Checks if this expression is valid.
Definition: qgsexpression.cpp:197
QgsExpression::setExpression
void setExpression(const QString &expression)
Set the expression string, will reset the whole internal structure.
Definition: qgsexpression.cpp:48
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:468
parseExpression
QgsExpressionNode * parseExpression(const QString &str, QString &parserErrorMsg, QList< QgsExpression::ParserError > &parserErrors)
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsExpression::referencedFunctions
QSet< QString > referencedFunctions() const
Returns a list of the names of all functions which are used in this expression.
Definition: qgsexpression.cpp:233
QgsUnitTypes::DistanceUnit
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:68
QgsExpressionContext::variable
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
Definition: qgsexpressioncontext.cpp:296
Q_GLOBAL_STATIC
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QgsExpression::~QgsExpression
~QgsExpression()
Definition: qgsexpression.cpp:185
QgsExpression::setAreaUnits
void setAreaUnits(QgsUnitTypes::AreaUnit unit)
Sets the desired areal units for calculations involving geomCalculator(), e.g., "$area".
Definition: qgsexpression.cpp:425
QgsExpression::checkExpression
static bool checkExpression(const QString &text, const QgsExpressionContext *context, QString &errorMessage)
Tests whether a string is a valid expression.
Definition: qgsexpression.cpp:40
QgsExpression::quotedValue
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
Definition: qgsexpression.cpp:79
QgsExpression::setGeomCalculator
void setGeomCalculator(const QgsDistanceArea *calc)
Sets the geometry calculator used for distance and area calculations in expressions.
Definition: qgsexpression.cpp:314
QgsFeatureRequest::ALL_ATTRIBUTES
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
Definition: qgsfeaturerequest.h:282
QgsExpression::formatPreviewString
static QString formatPreviewString(const QVariant &value, bool htmlOutput=true)
Formats an expression result for friendly display to the user.
Definition: qgsexpression.cpp:944
QgsExpression::setEvalErrorString
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions)
Definition: qgsexpression.cpp:384
QgsExpressionContextUtils::projectScope
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
Definition: qgsexpressioncontextutils.cpp:222
QgsExpression::hasEvalError
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
Definition: qgsexpression.cpp:374
qgsexpressioncontext.h
QgsUnitTypes::stringToAreaUnit
static Q_INVOKABLE QgsUnitTypes::AreaUnit stringToAreaUnit(const QString &string, bool *ok=nullptr)
Converts a translated string to an areal unit.
Definition: qgsunittypes.cpp:759
QgsAbstractGeometry::wkbType
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
Definition: qgsabstractgeometry.h:193
QgsExpression::parserErrorString
QString parserErrorString() const
Returns parser error.
Definition: qgsexpression.cpp:207
geoNone
CONSTLATIN1STRING geoNone()
Constant that holds the string representation for "No ellips/No CRS".
Definition: qgis.h:715
qgscolorramp.h
QgsExpression::isFunctionName
static bool isFunctionName(const QString &name)
tells whether the identifier is a name of existing function
Definition: qgsexpression.cpp:118
qgsexpressionfunction.h
QgsExpression::evaluateToDouble
static double evaluateToDouble(const QString &text, double fallbackValue)
Attempts to evaluate a text string as an expression to a resultant double value.
Definition: qgsexpression.cpp:503
QgsExpression::group
static QString group(const QString &group)
Returns the translated name for a function group.
Definition: qgsexpression.cpp:911
QgsUnitTypes::AreaUnit
AreaUnit
Units of area.
Definition: qgsunittypes.h:94
QgsExpression::variableHelpText
static QString variableHelpText(const QString &variableName)
Returns the help text for a specified variable.
Definition: qgsexpression.cpp:886
QgsWeakMapLayerPointer
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:1688
QgsExpression::prepare
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
Definition: qgsexpression.cpp:323
QgsInterval::minutes
double minutes() const
Returns the interval duration in minutes.
Definition: qgsinterval.h:155
QgsGeometry::constGet
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Definition: qgsgeometry.cpp:128
QgsExpression::nodes
QList< const QgsExpressionNode * > nodes() const
Returns a list of all nodes which are used in this expression.
Definition: qgsexpression.cpp:1101
QgsExpression::Functions
static const QList< QgsExpressionFunction * > & Functions()
Definition: qgsexpressionfunction.cpp:6008
QgsCoordinateReferenceSystem
This class represents a coordinate reference system (CRS).
Definition: qgscoordinatereferencesystem.h:206
QgsExpression::evaluate
QVariant evaluate()
Evaluate the feature and return the result.
Definition: qgsexpression.cpp:346
QgsExpressionNode
Abstract base class for all nodes that can appear in an expression.
Definition: qgsexpressionnode.h:35
QgsExpression::functionIndex
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
Definition: qgsexpression.cpp:123
QgsExpression::areaUnits
QgsUnitTypes::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g.,...
Definition: qgsexpression.cpp:420
QgsExpression::distanceUnits
QgsUnitTypes::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
Definition: qgsexpression.cpp:410
QgsFields::allAttributesList
QgsAttributeList allAttributesList() const
Utility function to get list of attribute indexes.
Definition: qgsfields.cpp:371
QgsStringMap
QMap< QString, QString > QgsStringMap
Definition: qgis.h:758
qgsgeometry.h
QgsExpression::createFieldEqualityExpression
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
Definition: qgsexpression.cpp:1079
qgsexpressionnodeimpl.h
QgsGeometry
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsInterval
A representation of the interval between two datetime values.
Definition: qgsinterval.h:41
QgsExpression::helpText
static QString helpText(QString name)
Returns the help text for a specified function.
Definition: qgsexpression.cpp:531
QgsExpression::tags
static QStringList tags(const QString &name)
Returns a string list of search tags for a specified function.
Definition: qgsexpression.cpp:666
QgsExpression::quotedColumnRef
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
Definition: qgsexpression.cpp:65
QgsExpression::needsGeometry
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
Definition: qgsexpression.cpp:266
QgsInterval::days
double days() const
Returns the interval duration in days.
Definition: qgsinterval.h:129
functionHelpTexts
HelpTextHash & functionHelpTexts()
Definition: qgsexpression.cpp:35
QgsDistanceArea
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
Definition: qgsdistancearea.h:50
QgsInterval::hours
double hours() const
Returns the interval duration in hours.
Definition: qgsinterval.h:142
QgsFeature
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
QgsExpression::isField
bool isField() const
Checks whether an expression consists only of a single field reference.
Definition: qgsexpression.cpp:1096
QgsExpression::functionCount
static int functionCount()
Returns the number of functions defined in the parser.
Definition: qgsexpression.cpp:140
QgsExpression::hasParserError
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
Definition: qgsexpression.cpp:202
qgslogger.h
QgsFields::lookupField
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:344
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:105
QgsExpression::replaceExpressionText
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
Definition: qgsexpression.cpp:430
QgsExpression::expression
QString expression() const
Returns the original, unmodified expression string.
Definition: qgsexpression.cpp:57
qgsproject.h
QgsUnitTypes::AreaUnknownUnit
@ AreaUnknownUnit
Unknown areal unit.
Definition: qgsunittypes.h:106
QgsExpression::referencedVariables
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
Definition: qgsexpression.cpp:225
QgsExpression::operator=
QgsExpression & operator=(const QgsExpression &other)
Create a copy of this expression.
Definition: qgsexpression.cpp:160