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