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