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