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