QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 
858  sVariableHelpTexts()->insert( QStringLiteral( "symbol_label" ), QCoreApplication::translate( "symbol_label", "Label for the symbol (either a user defined label or the default autogenerated label)." ) );
859  sVariableHelpTexts()->insert( QStringLiteral( "symbol_id" ), QCoreApplication::translate( "symbol_id", "Internal ID of the symbol." ) );
860  sVariableHelpTexts()->insert( QStringLiteral( "symbol_count" ), QCoreApplication::translate( "symbol_count", "Total number of features represented by the symbol." ) );
861 
862  //cluster variables
863  sVariableHelpTexts()->insert( QStringLiteral( "cluster_color" ), QCoreApplication::translate( "cluster_color", "Color of symbols within a cluster, or NULL if symbols have mixed colors." ) );
864  sVariableHelpTexts()->insert( QStringLiteral( "cluster_size" ), QCoreApplication::translate( "cluster_size", "Number of symbols contained within a cluster." ) );
865 
866  //processing variables
867  sVariableHelpTexts()->insert( QStringLiteral( "algorithm_id" ), QCoreApplication::translate( "algorithm_id", "Unique ID for algorithm." ) );
868  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)." ) );
869  sVariableHelpTexts()->insert( QStringLiteral( "model_folder" ), QCoreApplication::translate( "variable_help", "Folder containing current model (or project folder if model is embedded in a project)." ) );
870  sVariableHelpTexts()->insert( QStringLiteral( "model_name" ), QCoreApplication::translate( "variable_help", "Name of current model." ) );
871  sVariableHelpTexts()->insert( QStringLiteral( "model_group" ), QCoreApplication::translate( "variable_help", "Group for current model." ) );
872  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_minx" ), QCoreApplication::translate( "fullextent_minx", "Minimum x-value from full canvas extent (including all layers)." ) );
873  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_miny" ), QCoreApplication::translate( "fullextent_miny", "Minimum y-value from full canvas extent (including all layers)." ) );
874  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_maxx" ), QCoreApplication::translate( "fullextent_maxx", "Maximum x-value from full canvas extent (including all layers)." ) );
875  sVariableHelpTexts()->insert( QStringLiteral( "fullextent_maxy" ), QCoreApplication::translate( "fullextent_maxy", "Maximum y-value from full canvas extent (including all layers)." ) );
876 
877  //provider notification
878  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)." ) );
879 
880  //form context variable
881  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." ) );
882  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." ) );
883 
884  //parent form context variable
885  sVariableHelpTexts()->insert( QStringLiteral( "current_parent_geometry" ), QCoreApplication::translate( "current_parent_geometry",
886  "Only usable in an embedded form context, "
887  "represents the geometry of the feature currently being edited in the parent form.\n"
888  "Can be used in a form/row context to filter the related features using a value "
889  "from the feature currently edited in the parent form, to make sure that the filter "
890  "still works with standalone forms it is recommended to wrap this variable in a "
891  "'coalesce()'." ) );
892  sVariableHelpTexts()->insert( QStringLiteral( "current_parent_feature" ), QCoreApplication::translate( "current_parent_feature",
893  "Only usable in an embedded form context, "
894  "represents the feature currently being edited in the parent form.\n"
895  "Can be used in a form/row context to filter the related features using a value "
896  "from the feature currently edited in the parent form, to make sure that the filter "
897  "still works with standalone forms it is recommended to wrap this variable in a "
898  "'coalesce()'." ) );
899 
900  //form variable
901  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." ) );
902 }
903 
904 
905 bool QgsExpression::addVariableHelpText( const QString name, const QString &description )
906 {
907  if ( sVariableHelpTexts()->contains( name ) )
908  {
909  return false;
910  }
911  sVariableHelpTexts()->insert( name, description );
912  return true;
913 }
914 
915 QString QgsExpression::variableHelpText( const QString &variableName )
916 {
917  QgsExpression::initVariableHelp();
918  return sVariableHelpTexts()->value( variableName, QString() );
919 }
920 
921 QString QgsExpression::formatVariableHelp( const QString &description, bool showValue, const QVariant &value )
922 {
923  QString text = !description.isEmpty() ? QStringLiteral( "<p>%1</p>" ).arg( description ) : QString();
924  if ( showValue )
925  {
926  QString valueString;
927  if ( !value.isValid() )
928  {
929  valueString = QCoreApplication::translate( "variable_help", "not set" );
930  }
931  else
932  {
933  valueString = QStringLiteral( "<b>%1</b>" ).arg( formatPreviewString( value ) );
934  }
935  text.append( QCoreApplication::translate( "variable_help", "<p>Current value: %1</p>" ).arg( valueString ) );
936  }
937  return text;
938 }
939 
940 QString QgsExpression::group( const QString &name )
941 {
942  if ( sGroups()->isEmpty() )
943  {
944  sGroups()->insert( QStringLiteral( "Aggregates" ), tr( "Aggregates" ) );
945  sGroups()->insert( QStringLiteral( "Arrays" ), tr( "Arrays" ) );
946  sGroups()->insert( QStringLiteral( "Color" ), tr( "Color" ) );
947  sGroups()->insert( QStringLiteral( "Conditionals" ), tr( "Conditionals" ) );
948  sGroups()->insert( QStringLiteral( "Conversions" ), tr( "Conversions" ) );
949  sGroups()->insert( QStringLiteral( "Date and Time" ), tr( "Date and Time" ) );
950  sGroups()->insert( QStringLiteral( "Fields and Values" ), tr( "Fields and Values" ) );
951  sGroups()->insert( QStringLiteral( "Files and Paths" ), tr( "Files and Paths" ) );
952  sGroups()->insert( QStringLiteral( "Fuzzy Matching" ), tr( "Fuzzy Matching" ) );
953  sGroups()->insert( QStringLiteral( "General" ), tr( "General" ) );
954  sGroups()->insert( QStringLiteral( "GeometryGroup" ), tr( "Geometry" ) );
955  sGroups()->insert( QStringLiteral( "Map Layers" ), tr( "Map Layers" ) );
956  sGroups()->insert( QStringLiteral( "Maps" ), tr( "Maps" ) );
957  sGroups()->insert( QStringLiteral( "Math" ), tr( "Math" ) );
958  sGroups()->insert( QStringLiteral( "Operators" ), tr( "Operators" ) );
959  sGroups()->insert( QStringLiteral( "Rasters" ), tr( "Rasters" ) );
960  sGroups()->insert( QStringLiteral( "Record and Attributes" ), tr( "Record and Attributes" ) );
961  sGroups()->insert( QStringLiteral( "String" ), tr( "String" ) );
962  sGroups()->insert( QStringLiteral( "Variables" ), tr( "Variables" ) );
963  sGroups()->insert( QStringLiteral( "Recent (%1)" ), tr( "Recent (%1)" ) );
964  sGroups()->insert( QStringLiteral( "UserGroup" ), tr( "User expressions" ) );
965  }
966 
967  //return the translated name for this group. If group does not
968  //have a translated name in the gGroups hash, return the name
969  //unchanged
970  return sGroups()->value( name, name );
971 }
972 
973 QString QgsExpression::formatPreviewString( const QVariant &value, const bool htmlOutput, int maximumPreviewLength )
974 {
975  const QString startToken = htmlOutput ? QStringLiteral( "<i>&lt;" ) : QStringLiteral( "<" );
976  const QString endToken = htmlOutput ? QStringLiteral( "&gt;</i>" ) : QStringLiteral( ">" );
977 
978  if ( value.canConvert<QgsGeometry>() )
979  {
980  //result is a geometry
981  QgsGeometry geom = value.value<QgsGeometry>();
982  if ( geom.isNull() )
983  return startToken + tr( "empty geometry" ) + endToken;
984  else
985  return startToken + tr( "geometry: %1" ).arg( QgsWkbTypes::displayString( geom.constGet()->wkbType() ) )
986  + endToken;
987  }
988  else if ( value.value< QgsWeakMapLayerPointer >().data() )
989  {
990  return startToken + tr( "map layer" ) + endToken;
991  }
992  else if ( !value.isValid() )
993  {
994  return htmlOutput ? tr( "<i>NULL</i>" ) : QString();
995  }
996  else if ( value.canConvert< QgsFeature >() )
997  {
998  //result is a feature
999  QgsFeature feat = value.value<QgsFeature>();
1000  return startToken + tr( "feature: %1" ).arg( feat.id() ) + endToken;
1001  }
1002  else if ( value.canConvert< QgsInterval >() )
1003  {
1004  QgsInterval interval = value.value<QgsInterval>();
1005  if ( interval.days() > 1 )
1006  {
1007  return startToken + tr( "interval: %1 days" ).arg( interval.days() ) + endToken;
1008  }
1009  else if ( interval.hours() > 1 )
1010  {
1011  return startToken + tr( "interval: %1 hours" ).arg( interval.hours() ) + endToken;
1012  }
1013  else if ( interval.minutes() > 1 )
1014  {
1015  return startToken + tr( "interval: %1 minutes" ).arg( interval.minutes() ) + endToken;
1016  }
1017  else
1018  {
1019  return startToken + tr( "interval: %1 seconds" ).arg( interval.seconds() ) + endToken;
1020  }
1021  }
1022  else if ( value.canConvert< QgsGradientColorRamp >() )
1023  {
1024  return startToken + tr( "gradient ramp" ) + endToken;
1025  }
1026  else if ( value.type() == QVariant::Date )
1027  {
1028  const QDate dt = value.toDate();
1029  return startToken + tr( "date: %1" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd" ) ) ) + endToken;
1030  }
1031  else if ( value.type() == QVariant::Time )
1032  {
1033  const QTime tm = value.toTime();
1034  return startToken + tr( "time: %1" ).arg( tm.toString( QStringLiteral( "hh:mm:ss" ) ) ) + endToken;
1035  }
1036  else if ( value.type() == QVariant::DateTime )
1037  {
1038  const QDateTime dt = value.toDateTime();
1039  return startToken + tr( "datetime: %1 (%2)" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd hh:mm:ss" ) ), dt.timeZoneAbbreviation() ) + endToken;
1040  }
1041  else if ( value.type() == QVariant::String )
1042  {
1043  const QString previewString = value.toString();
1044  if ( previewString.length() > maximumPreviewLength + 3 )
1045  {
1046  return tr( "'%1…'" ).arg( previewString.left( maximumPreviewLength ) );
1047  }
1048  else
1049  {
1050  return '\'' + previewString + '\'';
1051  }
1052  }
1053  else if ( value.type() == QVariant::Map )
1054  {
1055  QString mapStr = QStringLiteral( "{" );
1056  const QVariantMap map = value.toMap();
1057  QString separator;
1058  for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1059  {
1060  mapStr.append( separator );
1061  if ( separator.isEmpty() )
1062  separator = QStringLiteral( "," );
1063 
1064  mapStr.append( QStringLiteral( " '%1': %2" ).arg( it.key(), formatPreviewString( it.value(), htmlOutput ) ) );
1065  if ( mapStr.length() > maximumPreviewLength - 3 )
1066  {
1067  mapStr = tr( "%1…" ).arg( mapStr.left( maximumPreviewLength - 2 ) );
1068  break;
1069  }
1070  }
1071  if ( !map.empty() )
1072  mapStr += QLatin1Char( ' ' );
1073  mapStr += QLatin1Char( '}' );
1074  return mapStr;
1075  }
1076  else if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
1077  {
1078  QString listStr = QStringLiteral( "[" );
1079  const QVariantList list = value.toList();
1080  QString separator;
1081  for ( const QVariant &arrayValue : list )
1082  {
1083  listStr.append( separator );
1084  if ( separator.isEmpty() )
1085  separator = QStringLiteral( "," );
1086 
1087  listStr.append( " " );
1088  listStr.append( formatPreviewString( arrayValue, htmlOutput ) );
1089  if ( listStr.length() > maximumPreviewLength - 3 )
1090  {
1091  listStr = QString( tr( "%1…" ) ).arg( listStr.left( maximumPreviewLength - 2 ) );
1092  break;
1093  }
1094  }
1095  if ( !list.empty() )
1096  listStr += QLatin1Char( ' ' );
1097  listStr += QLatin1Char( ']' );
1098  return listStr;
1099  }
1100  else if ( value.type() == QVariant::Int ||
1101  value.type() == QVariant::UInt ||
1102  value.type() == QVariant::LongLong ||
1103  value.type() == QVariant::ULongLong ||
1104  value.type() == QVariant::Double ||
1105  // Qt madness with QMetaType::Float :/
1106  value.type() == static_cast<QVariant::Type>( QMetaType::Float ) )
1107  {
1108  return QgsExpressionUtils::toLocalizedString( value );
1109  }
1110  else
1111  {
1112  QString str { value.toString() };
1113  if ( str.length() > maximumPreviewLength - 3 )
1114  {
1115  str = tr( "%1…" ).arg( str.left( maximumPreviewLength - 2 ) );
1116  }
1117  return str;
1118  }
1119 }
1120 
1121 QString QgsExpression::createFieldEqualityExpression( const QString &fieldName, const QVariant &value, QVariant::Type fieldType )
1122 {
1123  QString expr;
1124 
1125  if ( value.isNull() )
1126  expr = QStringLiteral( "%1 IS NULL" ).arg( quotedColumnRef( fieldName ) );
1127  else if ( fieldType == QVariant::Type::Invalid )
1128  expr = QStringLiteral( "%1 = %2" ).arg( quotedColumnRef( fieldName ), quotedValue( value ) );
1129  else
1130  expr = QStringLiteral( "%1 = %2" ).arg( quotedColumnRef( fieldName ), quotedValue( value, fieldType ) );
1131 
1132  return expr;
1133 }
1134 
1135 bool QgsExpression::isFieldEqualityExpression( const QString &expression, QString &field, QVariant &value )
1136 {
1138 
1139  if ( !e.rootNode() )
1140  return false;
1141 
1142  if ( const QgsExpressionNodeBinaryOperator *binOp = dynamic_cast<const QgsExpressionNodeBinaryOperator *>( e.rootNode() ) )
1143  {
1144  if ( binOp->op() == QgsExpressionNodeBinaryOperator::boEQ )
1145  {
1146  const QgsExpressionNodeColumnRef *columnRef = dynamic_cast<const QgsExpressionNodeColumnRef *>( binOp->opLeft() );
1147  const QgsExpressionNodeLiteral *literal = dynamic_cast<const QgsExpressionNodeLiteral *>( binOp->opRight() );
1148  if ( columnRef && literal )
1149  {
1150  field = columnRef->name();
1151  value = literal->value();
1152  return true;
1153  }
1154  }
1155  }
1156  return false;
1157 }
1158 
1159 bool QgsExpression::attemptReduceToInClause( const QStringList &expressions, QString &result )
1160 {
1161  if ( expressions.empty() )
1162  return false;
1163 
1164  QString inField;
1165  bool first = true;
1166  QStringList values;
1167  for ( const QString &expression : expressions )
1168  {
1169  QString field;
1170  QVariant value;
1172  {
1173  if ( first )
1174  {
1175  inField = field;
1176  first = false;
1177  }
1178  else if ( field != inField )
1179  {
1180  return false;
1181  }
1182  values << QgsExpression::quotedValue( value );
1183  }
1184  else
1185  {
1186  // we also allow reducing similar 'field IN (...)' expressions!
1188 
1189  if ( !e.rootNode() )
1190  return false;
1191 
1192  if ( const QgsExpressionNodeInOperator *inOp = dynamic_cast<const QgsExpressionNodeInOperator *>( e.rootNode() ) )
1193  {
1194  if ( inOp->isNotIn() )
1195  return false;
1196 
1197  const QgsExpressionNodeColumnRef *columnRef = dynamic_cast<const QgsExpressionNodeColumnRef *>( inOp->node() );
1198  if ( !columnRef )
1199  return false;
1200 
1201  if ( first )
1202  {
1203  inField = columnRef->name();
1204  first = false;
1205  }
1206  else if ( columnRef->name() != inField )
1207  {
1208  return false;
1209  }
1210 
1211  if ( QgsExpressionNode::NodeList *nodeList = inOp->list() )
1212  {
1213  const QList<QgsExpressionNode *> nodes = nodeList->list();
1214  for ( const QgsExpressionNode *node : nodes )
1215  {
1216  const QgsExpressionNodeLiteral *literal = dynamic_cast<const QgsExpressionNodeLiteral *>( node );
1217  if ( !literal )
1218  return false;
1219 
1220  values << QgsExpression::quotedValue( literal->value() );
1221  }
1222  }
1223  }
1224  // Collect ORs
1225  else if ( const QgsExpressionNodeBinaryOperator *orOp = dynamic_cast<const QgsExpressionNodeBinaryOperator *>( e.rootNode() ) )
1226  {
1227 
1228  // OR Collector function: returns a possibly empty list of the left and right operands of an OR expression
1229  std::function<QStringList( QgsExpressionNode *, QgsExpressionNode * )> collectOrs = [ &collectOrs ]( QgsExpressionNode * opLeft, QgsExpressionNode * opRight ) -> QStringList
1230  {
1231  QStringList orParts;
1232  if ( const QgsExpressionNodeBinaryOperator *leftOrOp = dynamic_cast<const QgsExpressionNodeBinaryOperator *>( opLeft ) )
1233  {
1234  if ( leftOrOp->op( ) == QgsExpressionNodeBinaryOperator::BinaryOperator::boOr )
1235  {
1236  orParts.append( collectOrs( leftOrOp->opLeft(), leftOrOp->opRight() ) );
1237  }
1238  else
1239  {
1240  orParts.append( leftOrOp->dump() );
1241  }
1242  }
1243  else if ( const QgsExpressionNodeInOperator *leftInOp = dynamic_cast<const QgsExpressionNodeInOperator *>( opLeft ) )
1244  {
1245  orParts.append( leftInOp->dump() );
1246  }
1247  else
1248  {
1249  return {};
1250  }
1251 
1252  if ( const QgsExpressionNodeBinaryOperator *rightOrOp = dynamic_cast<const QgsExpressionNodeBinaryOperator *>( opRight ) )
1253  {
1254  if ( rightOrOp->op( ) == QgsExpressionNodeBinaryOperator::BinaryOperator::boOr )
1255  {
1256  orParts.append( collectOrs( rightOrOp->opLeft(), rightOrOp->opRight() ) );
1257  }
1258  else
1259  {
1260  orParts.append( rightOrOp->dump() );
1261  }
1262  }
1263  else if ( const QgsExpressionNodeInOperator *rightInOp = dynamic_cast<const QgsExpressionNodeInOperator *>( opRight ) )
1264  {
1265  orParts.append( rightInOp->dump() );
1266  }
1267  else
1268  {
1269  return {};
1270  }
1271 
1272  return orParts;
1273  };
1274 
1275  if ( orOp->op( ) == QgsExpressionNodeBinaryOperator::BinaryOperator::boOr )
1276  {
1277  // Try to collect all OR conditions
1278  const QStringList orParts = collectOrs( orOp->opLeft(), orOp->opRight() );
1279  if ( orParts.isEmpty() )
1280  {
1281  return false;
1282  }
1283  else
1284  {
1285  QString orPartsResult;
1286  if ( attemptReduceToInClause( orParts, orPartsResult ) )
1287  {
1288  // Need to check if the IN field is correct,
1289  QgsExpression inExp { orPartsResult };
1290  if ( ! inExp.rootNode() )
1291  {
1292  return false;
1293  }
1294 
1295  if ( const QgsExpressionNodeInOperator *inOpInner = dynamic_cast<const QgsExpressionNodeInOperator *>( inExp.rootNode() ) )
1296  {
1297  if ( inOpInner->node()->nodeType() != QgsExpressionNode::NodeType::ntColumnRef || inOpInner->node()->referencedColumns().size() < 1 )
1298  {
1299  return false;
1300  }
1301 
1302  const QString innerInfield { inOpInner->node()->referencedColumns().values().first() };
1303 
1304  if ( first )
1305  {
1306  inField = innerInfield;
1307  first = false;
1308  }
1309 
1310  if ( innerInfield != inField )
1311  {
1312  return false;
1313  }
1314  else
1315  {
1316  const auto constInnerValuesList { inOpInner->list()->list() };
1317  for ( const auto &innerInValueNode : std::as_const( constInnerValuesList ) )
1318  {
1319  values.append( innerInValueNode->dump() );
1320  }
1321  }
1322 
1323  }
1324  else
1325  {
1326  return false;
1327  }
1328  }
1329  else
1330  {
1331  return false;
1332  }
1333  }
1334  }
1335  else
1336  {
1337  return false;
1338  }
1339  }
1340  else
1341  {
1342  return false;
1343  }
1344  }
1345  }
1346  result = QStringLiteral( "%1 IN (%2)" ).arg( quotedColumnRef( inField ), values.join( ',' ) );
1347  return true;
1348 }
1349 
1351 {
1352  return d->mRootNode;
1353 }
1354 
1356 {
1357  return d->mRootNode && d->mRootNode->nodeType() == QgsExpressionNode::ntColumnRef;
1358 }
1359 
1360 int QgsExpression::expressionToLayerFieldIndex( const QString &expression, const QgsVectorLayer *layer )
1361 {
1362  if ( !layer )
1363  return -1;
1364 
1365  // easy check first -- lookup field directly.
1366  int attrIndex = layer->fields().lookupField( expression.trimmed() );
1367  if ( attrIndex >= 0 )
1368  return attrIndex;
1369 
1370  // may still be a simple field expression, just one which is enclosed in "" or similar
1371  QgsExpression candidate( expression );
1372  if ( candidate.isField() )
1373  {
1374  const QString fieldName = qgis::down_cast<const QgsExpressionNodeColumnRef *>( candidate.rootNode() )->name();
1375  return layer->fields().lookupField( fieldName );
1376  }
1377  return -1;
1378 }
1379 
1380 QString QgsExpression::quoteFieldExpression( const QString &expression, const QgsVectorLayer *layer )
1381 {
1382  if ( !layer )
1383  return expression;
1384 
1385  const int fieldIndex = QgsExpression::expressionToLayerFieldIndex( expression, layer );
1386  if ( !expression.contains( '\"' ) && fieldIndex != -1 )
1387  {
1388  // retrieve actual field name from layer, so that we correctly remove any unwanted leading/trailing whitespace
1389  return QgsExpression::quotedColumnRef( layer->fields().at( fieldIndex ).name() );
1390  }
1391  else
1392  {
1393  return expression;
1394  }
1395 }
1396 
1397 QList<const QgsExpressionNode *> QgsExpression::nodes() const
1398 {
1399  if ( !d->mRootNode )
1400  return QList<const QgsExpressionNode *>();
1401 
1402  return d->mRootNode->nodes();
1403 }
1404 
1405 
1406 
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:470
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:1983
QMap< QString, QString > QgsStringMap
Definition: qgis.h:2026
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