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