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