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