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