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