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