QGIS API Documentation 3.28.14-Firenze (exported)
Loading...
Searching...
No Matches
qgsexpressionfunction.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsexpressionfunction.cpp
3 -------------------
4 begin : May 2017
5 copyright : (C) 2017 Matthias Kuhn
6 email : matthias@opengis.ch
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
17#include <random>
18
20#include "qgscoordinateutils.h"
22#include "qgsexpressionutils.h"
24#include "qgsexiftools.h"
25#include "qgsfeaturerequest.h"
26#include "qgsgeos.h"
27#include "qgsstringutils.h"
28#include "qgsmultipoint.h"
29#include "qgsgeometryutils.h"
30#include "qgshstoreutils.h"
31#include "qgsmultilinestring.h"
32#include "qgslinestring.h"
33#include "qgscurvepolygon.h"
35#include "qgspolygon.h"
36#include "qgstriangle.h"
37#include "qgscurve.h"
38#include "qgsregularpolygon.h"
39#include "qgsquadrilateral.h"
40#include "qgsvariantutils.h"
41#include "qgsogcutils.h"
42#include "qgsdistancearea.h"
43#include "qgsgeometryengine.h"
45#include "qgssymbollayerutils.h"
46#include "qgsstyle.h"
47#include "qgsexception.h"
48#include "qgsmessagelog.h"
49#include "qgsrasterlayer.h"
50#include "qgsvectorlayer.h"
51#include "qgsrasterbandstats.h"
52#include "qgscolorramp.h"
54#include "qgsfieldformatter.h"
56#include "qgsproviderregistry.h"
57#include "sqlite3.h"
58#include "qgstransaction.h"
59#include "qgsthreadingutils.h"
60#include "qgsapplication.h"
61#include "qgis.h"
63#include "qgsunittypes.h"
64#include "qgsspatialindex.h"
65#include "qgscolorrampimpl.h"
66
67#include <QMimeDatabase>
68#include <QProcessEnvironment>
69#include <QCryptographicHash>
70#include <QRegularExpression>
71#include <QUuid>
72#include <QUrlQuery>
73
74typedef QList<QgsExpressionFunction *> ExpressionFunctionList;
75
77Q_GLOBAL_STATIC( QStringList, sBuiltinFunctions )
79
82Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
83
84const QString QgsExpressionFunction::helpText() const
85{
86 return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
87}
88
90{
91 Q_UNUSED( node )
92 // evaluate arguments
93 QVariantList argValues;
94 if ( args )
95 {
96 int arg = 0;
97 const QList< QgsExpressionNode * > argList = args->list();
98 for ( QgsExpressionNode *n : argList )
99 {
100 QVariant v;
101 if ( lazyEval() )
102 {
103 // Pass in the node for the function to eval as it needs.
104 v = QVariant::fromValue( n );
105 }
106 else
107 {
108 v = n->eval( parent, context );
110 bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
111 if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
112 return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
113 }
114 argValues.append( v );
115 arg++;
116 }
117 }
118
119 return func( argValues, context, parent, node );
120}
121
123{
124 Q_UNUSED( node )
125 return true;
126}
127
129{
130 return QStringList();
131}
132
134{
135 Q_UNUSED( parent )
136 Q_UNUSED( context )
137 Q_UNUSED( node )
138 return false;
139}
140
142{
143 Q_UNUSED( parent )
144 Q_UNUSED( context )
145 Q_UNUSED( node )
146 return true;
147}
148
150{
151 Q_UNUSED( node )
152 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
153}
154
156{
157 return mGroups.isEmpty() ? false : mGroups.contains( QStringLiteral( "deprecated" ) );
158}
159
161{
162 return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
163}
164
166{
167 return mHandlesNull;
168}
169
170// doxygen doesn't like this constructor for some reason (maybe the function arguments?)
173 FcnEval fcn,
174 const QString &group,
175 const QString &helpText,
176 const std::function < bool ( const QgsExpressionNodeFunction *node ) > &usesGeometry,
177 const std::function < QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
178 bool lazyEval,
179 const QStringList &aliases,
180 bool handlesNull )
181 : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
182 , mFnc( fcn )
183 , mAliases( aliases )
184 , mUsesGeometry( false )
185 , mUsesGeometryFunc( usesGeometry )
186 , mReferencedColumnsFunc( referencedColumns )
187{
188}
190
192{
193 return mAliases;
194}
195
197{
198 if ( mUsesGeometryFunc )
199 return mUsesGeometryFunc( node );
200 else
201 return mUsesGeometry;
202}
203
204void QgsStaticExpressionFunction::setUsesGeometryFunction( const std::function<bool ( const QgsExpressionNodeFunction * )> &usesGeometry )
205{
206 mUsesGeometryFunc = usesGeometry;
207}
208
210{
211 if ( mReferencedColumnsFunc )
212 return mReferencedColumnsFunc( node );
213 else
214 return mReferencedColumns;
215}
216
218{
219 if ( mIsStaticFunc )
220 return mIsStaticFunc( node, parent, context );
221 else
222 return mIsStatic;
223}
224
226{
227 if ( mPrepareFunc )
228 return mPrepareFunc( node, parent, context );
229
230 return true;
231}
232
234{
235 mIsStaticFunc = isStatic;
236}
237
239{
240 mIsStaticFunc = nullptr;
241 mIsStatic = isStatic;
242}
243
244void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool ( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
245{
246 mPrepareFunc = prepareFunc;
247}
248
250{
251 if ( node && node->args() )
252 {
253 const QList< QgsExpressionNode * > argList = node->args()->list();
254 for ( QgsExpressionNode *argNode : argList )
255 {
256 if ( !argNode->isStatic( parent, context ) )
257 return false;
258 }
259 }
260
261 return true;
262}
263
264static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
265{
266 double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
267 double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
268 double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
269
270 if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
271 return QVariant();
272
273 QVariantList array;
274 int length = 1;
275
276 array << start;
277 double current = start + step;
278 while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
279 {
280 array << current;
281 current += step;
282 length++;
283 }
284
285 return array;
286}
287
288static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
289{
290 if ( !context )
291 return QVariant();
292
293 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
294
295 if ( name == QLatin1String( "feature" ) )
296 {
297 return context->hasFeature() ? QVariant::fromValue( context->feature() ) : QVariant();
298 }
299 else if ( name == QLatin1String( "id" ) )
300 {
301 return context->hasFeature() ? QVariant::fromValue( context->feature().id() ) : QVariant();
302 }
303 else if ( name == QLatin1String( "geometry" ) )
304 {
305 if ( !context->hasFeature() )
306 return QVariant();
307
308 const QgsFeature feature = context->feature();
309 return feature.hasGeometry() ? QVariant::fromValue( feature.geometry() ) : QVariant();
310 }
311 else
312 {
313 return context->variable( name );
314 }
315}
316
317static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
318{
319 QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
320 return QgsExpression::replaceExpressionText( templateString, context );
321}
322
323static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
324{
325 if ( !context )
326 return QVariant();
327
328 QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
329 QgsExpression expression( expString );
330 return expression.evaluate( context );
331}
332
333static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
334{
335 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
336 return QVariant( std::sqrt( x ) );
337}
338
339static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
340{
341 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
342 return QVariant( std::fabs( val ) );
343}
344
345static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
346{
347 double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
348 return ( deg * M_PI ) / 180;
349}
350static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
351{
352 double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
353 return ( 180 * rad ) / M_PI;
354}
355static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
356{
357 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
358 return QVariant( std::sin( x ) );
359}
360static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
361{
362 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
363 return QVariant( std::cos( x ) );
364}
365static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
366{
367 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
368 return QVariant( std::tan( x ) );
369}
370static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
371{
372 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
373 return QVariant( std::asin( x ) );
374}
375static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
376{
377 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
378 return QVariant( std::acos( x ) );
379}
380static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
381{
382 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
383 return QVariant( std::atan( x ) );
384}
385static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
386{
387 double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
388 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
389 return QVariant( std::atan2( y, x ) );
390}
391static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
392{
393 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
394 return QVariant( std::exp( x ) );
395}
396static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
397{
398 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
399 if ( x <= 0 )
400 return QVariant();
401 return QVariant( std::log( x ) );
402}
403static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
404{
405 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
406 if ( x <= 0 )
407 return QVariant();
408 return QVariant( log10( x ) );
409}
410static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
411{
412 double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
413 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
414 if ( x <= 0 || b <= 0 )
415 return QVariant();
416 return QVariant( std::log( x ) / std::log( b ) );
417}
418static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
419{
420 double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
421 double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
422 if ( max < min )
423 return QVariant();
424
425 std::random_device rd;
426 std::mt19937_64 generator( rd() );
427
428 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
429 {
430 quint32 seed;
431 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
432 {
433 // if seed can be converted to int, we use as is
434 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
435 }
436 else
437 {
438 // if not, we hash string representation to int
439 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
440 std::hash<std::string> hasher;
441 seed = hasher( seedStr.toStdString() );
442 }
443 generator.seed( seed );
444 }
445
446 // Return a random double in the range [min, max] (inclusive)
447 double f = static_cast< double >( generator() ) / static_cast< double >( std::mt19937_64::max() );
448 return QVariant( min + f * ( max - min ) );
449}
450static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
451{
452 qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
453 qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
454 if ( max < min )
455 return QVariant();
456
457 std::random_device rd;
458 std::mt19937_64 generator( rd() );
459
460 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
461 {
462 quint32 seed;
463 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
464 {
465 // if seed can be converted to int, we use as is
466 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
467 }
468 else
469 {
470 // if not, we hash string representation to int
471 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
472 std::hash<std::string> hasher;
473 seed = hasher( seedStr.toStdString() );
474 }
475 generator.seed( seed );
476 }
477
478 qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
479 if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
480 return QVariant( randomInteger );
481
482 // Prevent wrong conversion of QVariant. See #36412
483 return QVariant( int( randomInteger ) );
484}
485
486static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
487{
488 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
489 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
490 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
491 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
492 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
493
494 if ( domainMin >= domainMax )
495 {
496 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
497 return QVariant();
498 }
499
500 // outside of domain?
501 if ( val >= domainMax )
502 {
503 return rangeMax;
504 }
505 else if ( val <= domainMin )
506 {
507 return rangeMin;
508 }
509
510 // calculate linear scale
511 double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
512 double c = rangeMin - ( domainMin * m );
513
514 // Return linearly scaled value
515 return QVariant( m * val + c );
516}
517
518static QVariant fcnExpScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
519{
520 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
521 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
522 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
523 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
524 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
525 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
526
527 if ( domainMin >= domainMax )
528 {
529 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
530 return QVariant();
531 }
532 if ( exponent <= 0 )
533 {
534 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
535 return QVariant();
536 }
537
538 // outside of domain?
539 if ( val >= domainMax )
540 {
541 return rangeMax;
542 }
543 else if ( val <= domainMin )
544 {
545 return rangeMin;
546 }
547
548 // Return exponentially scaled value
549 return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
550}
551
552static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
553{
554 QVariant result( QVariant::Double );
555 double maxVal = std::numeric_limits<double>::quiet_NaN();
556 for ( const QVariant &val : values )
557 {
558 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
559 if ( std::isnan( maxVal ) )
560 {
561 maxVal = testVal;
562 }
563 else if ( !std::isnan( testVal ) )
564 {
565 maxVal = std::max( maxVal, testVal );
566 }
567 }
568
569 if ( !std::isnan( maxVal ) )
570 {
571 result = QVariant( maxVal );
572 }
573 return result;
574}
575
576static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
577{
578 QVariant result( QVariant::Double );
579 double minVal = std::numeric_limits<double>::quiet_NaN();
580 for ( const QVariant &val : values )
581 {
582 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
583 if ( std::isnan( minVal ) )
584 {
585 minVal = testVal;
586 }
587 else if ( !std::isnan( testVal ) )
588 {
589 minVal = std::min( minVal, testVal );
590 }
591 }
592
593 if ( !std::isnan( minVal ) )
594 {
595 result = QVariant( minVal );
596 }
597 return result;
598}
599
600static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
601{
602 //lazy eval, so we need to evaluate nodes now
603
604 //first node is layer id or name
605 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
607 QVariant value = node->eval( parent, context );
609 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, parent );
610 if ( !vl )
611 {
612 parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
613 return QVariant();
614 }
615
616 // second node is aggregate type
617 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
619 value = node->eval( parent, context );
621 bool ok = false;
622 QgsAggregateCalculator::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
623 if ( !ok )
624 {
625 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
626 return QVariant();
627 }
628
629 // third node is subexpression (or field name)
630 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
632 QString subExpression = node->dump();
633
635 //optional forth node is filter
636 if ( values.count() > 3 )
637 {
638 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
640 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
641 if ( !nl || nl->value().isValid() )
642 parameters.filter = node->dump();
643 }
644
645 //optional fifth node is concatenator
646 if ( values.count() > 4 )
647 {
648 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
650 value = node->eval( parent, context );
652 parameters.delimiter = value.toString();
653 }
654
655 //optional sixth node is order by
656 QString orderBy;
657 if ( values.count() > 5 )
658 {
659 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
661 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
662 if ( !nl || nl->value().isValid() )
663 {
664 orderBy = node->dump();
665 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
666 }
667 }
668
669 QString aggregateError;
670 QVariant result;
671 if ( context )
672 {
673 QString cacheKey;
674 QgsExpression subExp( subExpression );
675 QgsExpression filterExp( parameters.filter );
676
677 bool isStatic = true;
678 if ( filterExp.referencedVariables().contains( QStringLiteral( "parent" ) )
679 || filterExp.referencedVariables().contains( QString() )
680 || subExp.referencedVariables().contains( QStringLiteral( "parent" ) )
681 || subExp.referencedVariables().contains( QString() ) )
682 {
683 isStatic = false;
684 }
685 else
686 {
687 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
688 for ( const QString &varName : refVars )
689 {
690 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
691 if ( scope && !scope->isStatic( varName ) )
692 {
693 isStatic = false;
694 break;
695 }
696 }
697 }
698
699 if ( !isStatic )
700 {
701 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5%6:%7" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter,
702 QString::number( context->feature().id() ), QString::number( qHash( context->feature() ) ), orderBy );
703 }
704 else
705 {
706 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter, orderBy );
707 }
708
709 if ( context->hasCachedValue( cacheKey ) )
710 {
711 return context->cachedValue( cacheKey );
712 }
713
714 QgsExpressionContext subContext( *context );
716 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
717 subContext.appendScope( subScope );
718 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
719
720 if ( ok )
721 {
722 // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
723 // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
724 // associated with it's calculation!
725 context->setCachedValue( cacheKey, result );
726 }
727 }
728 else
729 {
730 result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
731 }
732 if ( !ok )
733 {
734 if ( !aggregateError.isEmpty() )
735 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
736 else
737 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
738 return QVariant();
739 }
740
741 return result;
742}
743
744static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
745{
746 if ( !context )
747 {
748 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
749 return QVariant();
750 }
751
752 // first step - find current layer
753 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
754 if ( !vl )
755 {
756 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
757 return QVariant();
758 }
759
760 //lazy eval, so we need to evaluate nodes now
761
762 //first node is relation name
763 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
765 QVariant value = node->eval( parent, context );
767 QString relationId = value.toString();
768 // check relation exists
769 QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId );
770 if ( !relation.isValid() || relation.referencedLayer() != vl )
771 {
772 // check for relations by name
773 QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId );
774 if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
775 {
776 parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
777 return QVariant();
778 }
779 else
780 {
781 relation = relations.at( 0 );
782 }
783 }
784
785 QgsVectorLayer *childLayer = relation.referencingLayer();
786
787 // second node is aggregate type
788 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
790 value = node->eval( parent, context );
792 bool ok = false;
793 QgsAggregateCalculator::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
794 if ( !ok )
795 {
796 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
797 return QVariant();
798 }
799
800 //third node is subexpression (or field name)
801 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
803 QString subExpression = node->dump();
804
805 //optional fourth node is concatenator
807 if ( values.count() > 3 )
808 {
809 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
811 value = node->eval( parent, context );
813 parameters.delimiter = value.toString();
814 }
815
816 //optional fifth node is order by
817 QString orderBy;
818 if ( values.count() > 4 )
819 {
820 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
822 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
823 if ( !nl || nl->value().isValid() )
824 {
825 orderBy = node->dump();
826 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
827 }
828 }
829
830 if ( !context->hasFeature() )
831 return QVariant();
832 QgsFeature f = context->feature();
833
834 parameters.filter = relation.getRelatedFeaturesFilter( f );
835
836 QString cacheKey = QStringLiteral( "relagg:%1:%2:%3:%4:%5" ).arg( vl->id(),
837 QString::number( static_cast< int >( aggregate ) ),
838 subExpression,
839 parameters.filter,
840 orderBy );
841 if ( context->hasCachedValue( cacheKey ) )
842 return context->cachedValue( cacheKey );
843
844 QVariant result;
845 ok = false;
846
847
848 QgsExpressionContext subContext( *context );
849 QString error;
850 result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
851
852 if ( !ok )
853 {
854 if ( !error.isEmpty() )
855 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
856 else
857 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
858 return QVariant();
859 }
860
861 // cache value
862 context->setCachedValue( cacheKey, result );
863 return result;
864}
865
866
867static QVariant fcnAggregateGeneric( QgsAggregateCalculator::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1 )
868{
869 if ( !context )
870 {
871 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
872 return QVariant();
873 }
874
875 // first step - find current layer
876 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
877 if ( !vl )
878 {
879 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
880 return QVariant();
881 }
882
883 //lazy eval, so we need to evaluate nodes now
884
885 //first node is subexpression (or field name)
886 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
888 QString subExpression = node->dump();
889
890 //optional second node is group by
891 QString groupBy;
892 if ( values.count() > 1 )
893 {
894 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
896 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
897 if ( !nl || nl->value().isValid() )
898 groupBy = node->dump();
899 }
900
901 //optional third node is filter
902 if ( values.count() > 2 )
903 {
904 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
906 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
907 if ( !nl || nl->value().isValid() )
908 parameters.filter = node->dump();
909 }
910
911 //optional order by node, if supported
912 QString orderBy;
913 if ( orderByPos >= 0 && values.count() > orderByPos )
914 {
915 node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
917 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
918 if ( !nl || nl->value().isValid() )
919 {
920 orderBy = node->dump();
921 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
922 }
923 }
924
925 // build up filter with group by
926
927 // find current group by value
928 if ( !groupBy.isEmpty() )
929 {
930 QgsExpression groupByExp( groupBy );
931 QVariant groupByValue = groupByExp.evaluate( context );
932 QString groupByClause = QStringLiteral( "%1 %2 %3" ).arg( groupBy,
933 QgsVariantUtils::isNull( groupByValue ) ? QStringLiteral( "is" ) : QStringLiteral( "=" ),
934 QgsExpression::quotedValue( groupByValue ) );
935 if ( !parameters.filter.isEmpty() )
936 parameters.filter = QStringLiteral( "(%1) AND (%2)" ).arg( parameters.filter, groupByClause );
937 else
938 parameters.filter = groupByClause;
939 }
940
941 QgsExpression subExp( subExpression );
942 QgsExpression filterExp( parameters.filter );
943
944 bool isStatic = true;
945 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
946 for ( const QString &varName : refVars )
947 {
948 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
949 if ( scope && !scope->isStatic( varName ) )
950 {
951 isStatic = false;
952 break;
953 }
954 }
955
956 QString cacheKey;
957 if ( !isStatic )
958 {
959 cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5%6:%7" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter,
960 QString::number( context->feature().id() ), QString::number( qHash( context->feature() ) ), orderBy );
961 }
962 else
963 {
964 cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter, orderBy );
965 }
966
967 if ( context->hasCachedValue( cacheKey ) )
968 return context->cachedValue( cacheKey );
969
970 QVariant result;
971 bool ok = false;
972
973 QgsExpressionContext subContext( *context );
975 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
976 subContext.appendScope( subScope );
977 QString error;
978 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
979
980 if ( !ok )
981 {
982 if ( !error.isEmpty() )
983 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
984 else
985 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
986 return QVariant();
987 }
988
989 // cache value
990 context->setCachedValue( cacheKey, result );
991 return result;
992}
993
994
995static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
996{
997 return fcnAggregateGeneric( QgsAggregateCalculator::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
998}
999
1000static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1001{
1002 return fcnAggregateGeneric( QgsAggregateCalculator::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1003}
1004
1005static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1006{
1007 return fcnAggregateGeneric( QgsAggregateCalculator::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1008}
1009
1010static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1011{
1012 return fcnAggregateGeneric( QgsAggregateCalculator::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1013}
1014
1015static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1016{
1017 return fcnAggregateGeneric( QgsAggregateCalculator::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1018}
1019
1020static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1021{
1022 return fcnAggregateGeneric( QgsAggregateCalculator::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1023}
1024
1025static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1026{
1027 return fcnAggregateGeneric( QgsAggregateCalculator::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1028}
1029
1030static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1031{
1032 return fcnAggregateGeneric( QgsAggregateCalculator::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1033}
1034
1035static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1036{
1037 return fcnAggregateGeneric( QgsAggregateCalculator::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1038}
1039
1040static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1041{
1042 return fcnAggregateGeneric( QgsAggregateCalculator::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1043}
1044
1045static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1046{
1047 return fcnAggregateGeneric( QgsAggregateCalculator::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1048}
1049
1050static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1051{
1052 return fcnAggregateGeneric( QgsAggregateCalculator::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1053}
1054
1055static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1056{
1057 return fcnAggregateGeneric( QgsAggregateCalculator::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1058}
1059
1060static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1061{
1062 return fcnAggregateGeneric( QgsAggregateCalculator::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1063}
1064
1065static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1066{
1067 return fcnAggregateGeneric( QgsAggregateCalculator::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1068}
1069
1070static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1071{
1072 return fcnAggregateGeneric( QgsAggregateCalculator::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1073}
1074
1075static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1076{
1077 return fcnAggregateGeneric( QgsAggregateCalculator::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1078}
1079
1080static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1081{
1082 return fcnAggregateGeneric( QgsAggregateCalculator::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1083}
1084
1085static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1086{
1088
1089 //fourth node is concatenator
1090 if ( values.count() > 3 )
1091 {
1092 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1094 QVariant value = node->eval( parent, context );
1096 parameters.delimiter = value.toString();
1097 }
1098
1099 return fcnAggregateGeneric( QgsAggregateCalculator::StringConcatenate, values, parameters, context, parent, 4 );
1100}
1101
1102static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1103{
1105
1106 //fourth node is concatenator
1107 if ( values.count() > 3 )
1108 {
1109 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1111 QVariant value = node->eval( parent, context );
1113 parameters.delimiter = value.toString();
1114 }
1115
1116 return fcnAggregateGeneric( QgsAggregateCalculator::StringConcatenateUnique, values, parameters, context, parent, 4 );
1117}
1118
1119static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1120{
1121 return fcnAggregateGeneric( QgsAggregateCalculator::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1122}
1123
1124static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1125{
1126 if ( !context )
1127 return QVariant();
1128
1129 QVariant scale = context->variable( QStringLiteral( "map_scale" ) );
1130 bool ok = false;
1131 if ( QgsVariantUtils::isNull( scale ) )
1132 return QVariant();
1133
1134 const double v = scale.toDouble( &ok );
1135 if ( ok )
1136 return v;
1137 return QVariant();
1138}
1139
1140static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1141{
1142 double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1143 double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1144 double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1145
1146 // force testValue to sit inside the range specified by the min and max value
1147 if ( testValue <= minValue )
1148 {
1149 return QVariant( minValue );
1150 }
1151 else if ( testValue >= maxValue )
1152 {
1153 return QVariant( maxValue );
1154 }
1155 else
1156 {
1157 return QVariant( testValue );
1158 }
1159}
1160
1161static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1162{
1163 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1164 return QVariant( std::floor( x ) );
1165}
1166
1167static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1168{
1169 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1170 return QVariant( std::ceil( x ) );
1171}
1172
1173static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1174{
1175 return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1176}
1177static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1178{
1179 return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1180}
1181static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1182{
1183 return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1184}
1185
1186static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1187{
1188 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1189 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1190 if ( format.isEmpty() && !language.isEmpty() )
1191 {
1192 parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1193 return QVariant( QDateTime() );
1194 }
1195
1196 if ( format.isEmpty() && language.isEmpty() )
1197 return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1198
1199 QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1200 QLocale locale = QLocale();
1201 if ( !language.isEmpty() )
1202 {
1203 locale = QLocale( language );
1204 }
1205
1206 QDateTime datetime = locale.toDateTime( datetimestring, format );
1207 if ( !datetime.isValid() )
1208 {
1209 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1210 datetime = QDateTime();
1211 }
1212 return QVariant( datetime );
1213}
1214
1215static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1216{
1217 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1218 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1219 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1220
1221 const QDate date( year, month, day );
1222 if ( !date.isValid() )
1223 {
1224 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1225 return QVariant();
1226 }
1227 return QVariant( date );
1228}
1229
1230static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1231{
1232 const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1233 const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1234 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1235
1236 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1237 if ( !time.isValid() )
1238 {
1239 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1240 return QVariant();
1241 }
1242 return QVariant( time );
1243}
1244
1245static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1246{
1247 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1248 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1249 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1250 const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1251 const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1252 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1253
1254 const QDate date( year, month, day );
1255 if ( !date.isValid() )
1256 {
1257 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1258 return QVariant();
1259 }
1260 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1261 if ( !time.isValid() )
1262 {
1263 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1264 return QVariant();
1265 }
1266 return QVariant( QDateTime( date, time ) );
1267}
1268
1269static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1270{
1271 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1272 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1273 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1274 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1275 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1276 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1277 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1278
1279 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1280}
1281
1282static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1283{
1284 for ( const QVariant &value : values )
1285 {
1286 if ( QgsVariantUtils::isNull( value ) )
1287 continue;
1288 return value;
1289 }
1290 return QVariant();
1291}
1292
1293static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1294{
1295 const QVariant val1 = values.at( 0 );
1296 const QVariant val2 = values.at( 1 );
1297
1298 if ( val1 == val2 )
1299 return QVariant();
1300 else
1301 return val1;
1302}
1303
1304static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1305{
1306 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1307 return QVariant( str.toLower() );
1308}
1309static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1310{
1311 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1312 return QVariant( str.toUpper() );
1313}
1314static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1315{
1316 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1317 QStringList elems = str.split( ' ' );
1318 for ( int i = 0; i < elems.size(); i++ )
1319 {
1320 if ( elems[i].size() > 1 )
1321 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1322 }
1323 return QVariant( elems.join( QLatin1Char( ' ' ) ) );
1324}
1325
1326static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1327{
1328 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1329 return QVariant( str.trimmed() );
1330}
1331
1332static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1333{
1334 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1335 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1336 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1337}
1338
1339static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1340{
1341 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1342 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1343 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1344}
1345
1346static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1347{
1348 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1349 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1350 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1351 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1352}
1353
1354static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1355{
1356 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1357 return QVariant( QgsStringUtils::soundex( string ) );
1358}
1359
1360static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1361{
1362 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1363 return QVariant( QString( character ) );
1364}
1365
1366static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1367{
1368 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1369
1370 if ( value.isEmpty() )
1371 {
1372 return QVariant();
1373 }
1374
1375 int res = value.at( 0 ).unicode();
1376 return QVariant( res );
1377}
1378
1379static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1380{
1381 if ( values.length() == 2 || values.length() == 3 )
1382 {
1383 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1384 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1385
1386 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1387
1388 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1389 }
1390
1391 return QVariant();
1392}
1393
1394static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1395{
1396 // two variants, one for geometry, one for string
1397 if ( values.at( 0 ).userType() == QMetaType::type( "QgsGeometry" ) )
1398 {
1399 //geometry variant
1400 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1401 if ( geom.type() != QgsWkbTypes::LineGeometry )
1402 return QVariant();
1403
1404 return QVariant( geom.length() );
1405 }
1406
1407 //otherwise fall back to string variant
1408 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1409 return QVariant( str.length() );
1410}
1411
1412static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1413{
1414 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1415
1416 if ( geom.type() != QgsWkbTypes::LineGeometry )
1417 return QVariant();
1418
1419 double totalLength = 0;
1420 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1421 {
1422 if ( const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( *it ) )
1423 {
1424 totalLength += line->length3D();
1425 }
1426 else
1427 {
1428 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1429 totalLength += segmentized->length3D();
1430 }
1431 }
1432
1433 return totalLength;
1434}
1435
1436static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1437{
1438 if ( values.count() == 2 && values.at( 1 ).type() == QVariant::Map )
1439 {
1440 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1441 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1442 QVector< QPair< QString, QString > > mapItems;
1443
1444 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1445 {
1446 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1447 }
1448
1449 // larger keys should be replaced first since they may contain whole smaller keys
1450 std::sort( mapItems.begin(),
1451 mapItems.end(),
1452 []( const QPair< QString, QString > &pair1,
1453 const QPair< QString, QString > &pair2 )
1454 {
1455 return ( pair1.first.length() > pair2.first.length() );
1456 } );
1457
1458 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1459 {
1460 str = str.replace( it->first, it->second );
1461 }
1462
1463 return QVariant( str );
1464 }
1465 else if ( values.count() == 3 )
1466 {
1467 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1468 QVariantList before;
1469 QVariantList after;
1470 bool isSingleReplacement = false;
1471
1472 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).type() != QVariant::StringList )
1473 {
1474 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1475 }
1476 else
1477 {
1478 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1479 }
1480
1481 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1482 {
1483 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1484 isSingleReplacement = true;
1485 }
1486 else
1487 {
1488 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1489 }
1490
1491 if ( !isSingleReplacement && before.length() != after.length() )
1492 {
1493 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1494 return QVariant();
1495 }
1496
1497 for ( int i = 0; i < before.length(); i++ )
1498 {
1499 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1500 }
1501
1502 return QVariant( str );
1503 }
1504 else
1505 {
1506 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1507 return QVariant();
1508 }
1509}
1510
1511static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1512{
1513 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1514 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1515 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1516
1517 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1518 if ( !re.isValid() )
1519 {
1520 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1521 return QVariant();
1522 }
1523 return QVariant( str.replace( re, after ) );
1524}
1525
1526static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1527{
1528 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1529 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1530
1531 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1532 if ( !re.isValid() )
1533 {
1534 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1535 return QVariant();
1536 }
1537 return QVariant( ( str.indexOf( re ) + 1 ) );
1538}
1539
1540static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1541{
1542 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1543 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1544 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1545
1546 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1547 if ( !re.isValid() )
1548 {
1549 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1550 return QVariant();
1551 }
1552
1553 QRegularExpressionMatch matches = re.match( str );
1554 if ( matches.hasMatch() )
1555 {
1556 QVariantList array;
1557 QStringList list = matches.capturedTexts();
1558
1559 // Skip the first string to only return captured groups
1560 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1561 {
1562 array += ( !( *it ).isEmpty() ) ? *it : empty;
1563 }
1564
1565 return QVariant( array );
1566 }
1567 else
1568 {
1569 return QVariant();
1570 }
1571}
1572
1573static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1574{
1575 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1576 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1577
1578 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1579 if ( !re.isValid() )
1580 {
1581 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1582 return QVariant();
1583 }
1584
1585 // extract substring
1586 QRegularExpressionMatch match = re.match( str );
1587 if ( match.hasMatch() )
1588 {
1589 // return first capture
1590 if ( match.lastCapturedIndex() > 0 )
1591 {
1592 // a capture group was present, so use that
1593 return QVariant( match.captured( 1 ) );
1594 }
1595 else
1596 {
1597 // no capture group, so using all match
1598 return QVariant( match.captured( 0 ) );
1599 }
1600 }
1601 else
1602 {
1603 return QVariant( "" );
1604 }
1605}
1606
1607static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1608{
1609 QString uuid = QUuid::createUuid().toString();
1610 if ( values.at( 0 ).toString().compare( QStringLiteral( "WithoutBraces" ), Qt::CaseInsensitive ) == 0 )
1611 uuid = QUuid::createUuid().toString( QUuid::StringFormat::WithoutBraces );
1612 else if ( values.at( 0 ).toString().compare( QStringLiteral( "Id128" ), Qt::CaseInsensitive ) == 0 )
1613 uuid = QUuid::createUuid().toString( QUuid::StringFormat::Id128 );
1614 return uuid;
1615}
1616
1617static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1618{
1619 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1620 return QVariant();
1621
1622 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1623 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1624
1625 int len = 0;
1626 if ( values.at( 2 ).isValid() )
1627 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1628 else
1629 len = str.size();
1630
1631 if ( from < 0 )
1632 {
1633 from = str.size() + from;
1634 if ( from < 0 )
1635 {
1636 from = 0;
1637 }
1638 }
1639 else if ( from > 0 )
1640 {
1641 //account for the fact that substr() starts at 1
1642 from -= 1;
1643 }
1644
1645 if ( len < 0 )
1646 {
1647 len = str.size() + len - from;
1648 if ( len < 0 )
1649 {
1650 len = 0;
1651 }
1652 }
1653
1654 return QVariant( str.mid( from, len ) );
1655}
1656static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1657{
1658 FEAT_FROM_CONTEXT( context, f )
1659 // TODO: handling of 64-bit feature ids?
1660 return QVariant( static_cast< int >( f.id() ) );
1661}
1662
1663static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1664{
1665 QgsRasterLayer *layer = QgsExpressionUtils::getRasterLayer( values.at( 0 ), parent );
1666 if ( !layer || !layer->dataProvider() )
1667 {
1668 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1669 return QVariant();
1670 }
1671
1672 int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1673 if ( bandNb < 1 || bandNb > layer->bandCount() )
1674 {
1675 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
1676 return QVariant();
1677 }
1678
1679 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1680 if ( geom.isNull() || geom.type() != QgsWkbTypes::PointGeometry )
1681 {
1682 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
1683 return QVariant();
1684 }
1685
1686 QgsPointXY point = geom.asPoint();
1687 if ( geom.isMultipart() )
1688 {
1689 QgsMultiPointXY multiPoint = geom.asMultiPoint();
1690 if ( multiPoint.count() == 1 )
1691 {
1692 point = multiPoint[0];
1693 }
1694 else
1695 {
1696 // if the geometry contains more than one part, return an undefined value
1697 return QVariant();
1698 }
1699 }
1700
1701 double value = layer->dataProvider()->sample( point, bandNb );
1702 return std::isnan( value ) ? QVariant() : value;
1703}
1704
1705static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1706{
1707 if ( !context )
1708 return QVariant();
1709
1710 return context->feature();
1711}
1712
1713static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1714{
1715 QgsFeature feature;
1716 QString attr;
1717 if ( values.size() == 1 )
1718 {
1719 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1720 feature = context->feature();
1721 }
1722 else if ( values.size() == 2 )
1723 {
1724 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1725 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1726 }
1727 else
1728 {
1729 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
1730 return QVariant();
1731 }
1732
1733 return feature.attribute( attr );
1734}
1735
1736static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1737{
1738 QgsFeature feature;
1739 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
1740 {
1741 feature = context->feature();
1742 }
1743 else
1744 {
1745 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1746 }
1747
1748 const QgsFields fields = feature.fields();
1749 QVariantMap result;
1750 for ( int i = 0; i < fields.count(); ++i )
1751 {
1752 result.insert( fields.at( i ).name(), feature.attribute( i ) );
1753 }
1754 return result;
1755}
1756
1757static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1758{
1759 QgsVectorLayer *layer = nullptr;
1760 QgsFeature feature;
1761
1762 if ( values.isEmpty() )
1763 {
1764 feature = context->feature();
1765 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1766 }
1767 else if ( values.size() == 1 )
1768 {
1769 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1770 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1771 }
1772 else if ( values.size() == 2 )
1773 {
1774 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1775 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1776 }
1777 else
1778 {
1779 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
1780 return QVariant();
1781 }
1782
1783 if ( !layer )
1784 {
1785 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
1786 return QVariant();
1787 }
1788
1789 if ( !feature.isValid() )
1790 {
1791 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
1792 return QVariant();
1793 }
1794
1795 const QgsFields fields = feature.fields();
1796 QVariantMap result;
1797 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
1798 {
1799 const QString fieldName { fields.at( fieldIndex ).name() };
1800 const QVariant attributeVal = feature.attribute( fieldIndex );
1801 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer->id(), fieldName, attributeVal.toString() );
1802 if ( context && context->hasCachedValue( cacheValueKey ) )
1803 {
1804 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
1805 }
1806 else
1807 {
1808 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
1810 QVariant cache;
1811 if ( context )
1812 {
1813 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer->id(), fieldName );
1814
1815 if ( !context->hasCachedValue( cacheKey ) )
1816 {
1817 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
1818 context->setCachedValue( cacheKey, cache );
1819 }
1820 else
1821 {
1822 cache = context->cachedValue( cacheKey );
1823 }
1824 }
1825 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
1826
1827 result.insert( fields.at( fieldIndex ).name(), value );
1828
1829 if ( context )
1830 {
1831 context->setCachedValue( cacheValueKey, value );
1832 }
1833
1834 }
1835 }
1836 return result;
1837}
1838
1839static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
1840{
1841 QgsVectorLayer *layer = nullptr;
1842 QgsFeature feature;
1843 bool evaluate = true;
1844
1845 if ( values.isEmpty() )
1846 {
1847 feature = context->feature();
1848 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1849 }
1850 else if ( values.size() == 1 )
1851 {
1852 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1853 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1854 }
1855 else if ( values.size() == 2 )
1856 {
1857 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1858 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1859 }
1860 else if ( values.size() == 3 )
1861 {
1862 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1863 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1864 evaluate = values.value( 2 ).toBool();
1865 }
1866 else
1867 {
1868 if ( isMaptip )
1869 {
1870 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
1871 }
1872 else
1873 {
1874 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
1875 }
1876 return QVariant();
1877 }
1878
1879 if ( !layer )
1880 {
1881 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
1882 return QVariant( );
1883 }
1884
1885 if ( !feature.isValid() )
1886 {
1887 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
1888 return QVariant( );
1889 }
1890
1891 if ( ! evaluate )
1892 {
1893 if ( isMaptip )
1894 {
1895 return layer->mapTipTemplate();
1896 }
1897 else
1898 {
1899 return layer->displayExpression();
1900 }
1901 }
1902
1903 QgsExpressionContext subContext( *context );
1904 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
1905 subContext.setFeature( feature );
1906
1907 if ( isMaptip )
1908 {
1909 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
1910 }
1911 else
1912 {
1913 QgsExpression exp( layer->displayExpression() );
1914 exp.prepare( &subContext );
1915 return exp.evaluate( &subContext ).toString();
1916 }
1917}
1918
1919static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1920{
1921 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
1922}
1923
1924static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1925{
1926 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
1927}
1928
1929static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1930{
1931 QgsVectorLayer *layer = nullptr;
1932 QgsFeature feature;
1933
1934 if ( values.isEmpty() )
1935 {
1936 feature = context->feature();
1937 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1938 }
1939 else if ( values.size() == 1 )
1940 {
1941 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1942 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1943 }
1944 else if ( values.size() == 2 )
1945 {
1946 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1947 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1948 }
1949 else
1950 {
1951 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
1952 return QVariant();
1953 }
1954
1955 if ( !layer || !feature.isValid() )
1956 {
1957 return QVariant( QVariant::Bool );
1958 }
1959
1960 return layer->selectedFeatureIds().contains( feature.id() );
1961}
1962
1963static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1964{
1965 QgsVectorLayer *layer = nullptr;
1966
1967 if ( values.isEmpty() )
1968 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1969 else if ( values.count() == 1 )
1970 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1971 else
1972 {
1973 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
1974 return QVariant();
1975 }
1976
1977 if ( !layer )
1978 {
1979 return QVariant( QVariant::LongLong );
1980 }
1981
1982 return layer->selectedFeatureCount();
1983}
1984
1985static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1986{
1987 static QMap<QString, qlonglong> counterCache;
1988 QVariant functionResult;
1989
1990 std::function<void()> fetchAndIncrementFunc = [ =, &functionResult ]()
1991 {
1992 QString database;
1993 const QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1994
1995 if ( layer )
1996 {
1997 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
1998 database = decodedUri.value( QStringLiteral( "path" ) ).toString();
1999 if ( database.isEmpty() )
2000 {
2001 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2002 }
2003 }
2004 else
2005 {
2006 database = values.at( 0 ).toString();
2007 }
2008
2009 const QString table = values.at( 1 ).toString();
2010 const QString idColumn = values.at( 2 ).toString();
2011 const QString filterAttribute = values.at( 3 ).toString();
2012 const QVariant filterValue = values.at( 4 ).toString();
2013 const QVariantMap defaultValues = values.at( 5 ).toMap();
2014
2015 // read from database
2017 sqlite3_statement_unique_ptr sqliteStatement;
2018
2019 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2020 {
2021 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2022 functionResult = QVariant();
2023 return;
2024 }
2025
2026 QString errorMessage;
2027 QString currentValSql;
2028
2029 qlonglong nextId = 0;
2030 bool cachedMode = false;
2031 bool valueRetrieved = false;
2032
2033 QString cacheString = QStringLiteral( "%1:%2:%3:%4:%5" ).arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2034
2035 // Running in transaction mode, check for cached value first
2036 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2037 {
2038 cachedMode = true;
2039
2040 auto cachedCounter = counterCache.find( cacheString );
2041
2042 if ( cachedCounter != counterCache.end() )
2043 {
2044 qlonglong &cachedValue = cachedCounter.value();
2045 nextId = cachedValue;
2046 nextId += 1;
2047 cachedValue = nextId;
2048 valueRetrieved = true;
2049 }
2050 }
2051
2052 // Either not in cached mode or no cached value found, obtain from DB
2053 if ( !cachedMode || !valueRetrieved )
2054 {
2055 int result = SQLITE_ERROR;
2056
2057 currentValSql = QStringLiteral( "SELECT %1 FROM %2" ).arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2058 if ( !filterAttribute.isNull() )
2059 {
2060 currentValSql += QStringLiteral( " WHERE %1 = %2" ).arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2061 }
2062
2063 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2064
2065 if ( result == SQLITE_OK )
2066 {
2067 nextId = 0;
2068 if ( sqliteStatement.step() == SQLITE_ROW )
2069 {
2070 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2071 }
2072
2073 // If in cached mode: add value to cache and connect to transaction
2074 if ( cachedMode && result == SQLITE_OK )
2075 {
2076 counterCache.insert( cacheString, nextId );
2077
2078 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]()
2079 {
2080 counterCache.remove( cacheString );
2081 } );
2082 }
2083 valueRetrieved = true;
2084 }
2085 }
2086
2087 if ( valueRetrieved )
2088 {
2089 QString upsertSql;
2090 upsertSql = QStringLiteral( "INSERT OR REPLACE INTO %1" ).arg( QgsSqliteUtils::quotedIdentifier( table ) );
2091 QStringList cols;
2092 QStringList vals;
2093 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2094 vals << QgsSqliteUtils::quotedValue( nextId );
2095
2096 if ( !filterAttribute.isNull() )
2097 {
2098 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2099 vals << QgsSqliteUtils::quotedValue( filterValue );
2100 }
2101
2102 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2103 {
2104 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2105 vals << iter.value().toString();
2106 }
2107
2108 upsertSql += QLatin1String( " (" ) + cols.join( ',' ) + ')';
2109 upsertSql += QLatin1String( " VALUES " );
2110 upsertSql += '(' + vals.join( ',' ) + ')';
2111
2112 int result = SQLITE_ERROR;
2113 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2114 {
2115 QgsTransaction *transaction = layer->dataProvider()->transaction();
2116 if ( transaction->executeSql( upsertSql, errorMessage ) )
2117 {
2118 result = SQLITE_OK;
2119 }
2120 }
2121 else
2122 {
2123 result = sqliteDb.exec( upsertSql, errorMessage );
2124 }
2125 if ( result == SQLITE_OK )
2126 {
2127 functionResult = QVariant( nextId );
2128 return;
2129 }
2130 else
2131 {
2132 parent->setEvalErrorString( QStringLiteral( "Could not increment value: SQLite error: \"%1\" (%2)." ).arg( errorMessage, QString::number( result ) ) );
2133 functionResult = QVariant();
2134 return;
2135 }
2136 }
2137
2138 functionResult = QVariant();
2139 };
2140
2141 QgsThreadingUtils::runOnMainThread( fetchAndIncrementFunc );
2142
2143 return functionResult;
2144}
2145
2146static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2147{
2148 QString concat;
2149 for ( const QVariant &value : values )
2150 {
2151 if ( !QgsVariantUtils::isNull( value ) )
2152 concat += QgsExpressionUtils::getStringValue( value, parent );
2153 }
2154 return concat;
2155}
2156
2157static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2158{
2159 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2160 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2161}
2162
2163static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2164{
2165 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2166 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2167 return string.right( pos );
2168}
2169
2170static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2171{
2172 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2173 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2174 return string.left( pos );
2175}
2176
2177static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2178{
2179 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2180 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2181 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2182 return string.leftJustified( length, fill.at( 0 ), true );
2183}
2184
2185static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2186{
2187 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2188 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2189 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2190 return string.rightJustified( length, fill.at( 0 ), true );
2191}
2192
2193static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2194{
2195 if ( values.size() < 1 )
2196 {
2197 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2198 return QVariant();
2199 }
2200
2201 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2202 for ( int n = 1; n < values.length(); n++ )
2203 {
2204 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2205 }
2206 return string;
2207}
2208
2209
2210static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2211{
2212 return QVariant( QDateTime::currentDateTime() );
2213}
2214
2215static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2216{
2217 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2218 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2219 if ( format.isEmpty() && !language.isEmpty() )
2220 {
2221 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2222 return QVariant( QDate() );
2223 }
2224
2225 if ( format.isEmpty() && language.isEmpty() )
2226 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2227
2228 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2229 QLocale locale = QLocale();
2230 if ( !language.isEmpty() )
2231 {
2232 locale = QLocale( language );
2233 }
2234
2235 QDate date = locale.toDate( datestring, format );
2236 if ( !date.isValid() )
2237 {
2238 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2239 date = QDate();
2240 }
2241 return QVariant( date );
2242}
2243
2244static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2245{
2246 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2247 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2248 if ( format.isEmpty() && !language.isEmpty() )
2249 {
2250 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
2251 return QVariant( QTime() );
2252 }
2253
2254 if ( format.isEmpty() && language.isEmpty() )
2255 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
2256
2257 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2258 QLocale locale = QLocale();
2259 if ( !language.isEmpty() )
2260 {
2261 locale = QLocale( language );
2262 }
2263
2264 QTime time = locale.toTime( timestring, format );
2265 if ( !time.isValid() )
2266 {
2267 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
2268 time = QTime();
2269 }
2270 return QVariant( time );
2271}
2272
2273static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2274{
2275 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
2276}
2277
2278/*
2279 * DMS functions
2280 */
2281
2282static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2283{
2284 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2285 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2286 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2287
2288 QString formatString;
2289 if ( values.count() > 3 )
2290 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
2291
2292 QgsCoordinateFormatter::FormatFlags flags = QgsCoordinateFormatter::FormatFlags();
2293 if ( formatString.compare( QLatin1String( "suffix" ), Qt::CaseInsensitive ) == 0 )
2294 {
2296 }
2297 else if ( formatString.compare( QLatin1String( "aligned" ), Qt::CaseInsensitive ) == 0 )
2298 {
2300 }
2301 else if ( ! formatString.isEmpty() )
2302 {
2303 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
2304 return QVariant();
2305 }
2306
2307 if ( axis.compare( QLatin1String( "x" ), Qt::CaseInsensitive ) == 0 )
2308 {
2309 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
2310 }
2311 else if ( axis.compare( QLatin1String( "y" ), Qt::CaseInsensitive ) == 0 )
2312 {
2313 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
2314 }
2315 else
2316 {
2317 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
2318 return QVariant();
2319 }
2320}
2321
2322static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2323{
2325 return floatToDegreeFormat( format, values, context, parent, node );
2326}
2327
2328static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2329{
2330 double value = 0.0;
2331 bool ok = false;
2332 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
2333
2334 return ok ? QVariant( value ) : QVariant();
2335}
2336
2337static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2338{
2340 return floatToDegreeFormat( format, values, context, parent, node );
2341}
2342
2343static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2344{
2345 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2346 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
2347 qint64 seconds = d2.secsTo( d1 );
2348 return QVariant::fromValue( QgsInterval( seconds ) );
2349}
2350
2351static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2352{
2353 if ( !values.at( 0 ).canConvert<QDate>() )
2354 return QVariant();
2355
2356 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
2357 if ( !date.isValid() )
2358 return QVariant();
2359
2360 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
2361 // (to match PostgreSQL behavior)
2362 return date.dayOfWeek() % 7;
2363}
2364
2365static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2366{
2367 QVariant value = values.at( 0 );
2368 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2369 if ( inter.isValid() )
2370 {
2371 return QVariant( inter.days() );
2372 }
2373 else
2374 {
2375 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2376 return QVariant( d1.date().day() );
2377 }
2378}
2379
2380static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2381{
2382 QVariant value = values.at( 0 );
2383 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2384 if ( inter.isValid() )
2385 {
2386 return QVariant( inter.years() );
2387 }
2388 else
2389 {
2390 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2391 return QVariant( d1.date().year() );
2392 }
2393}
2394
2395static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2396{
2397 QVariant value = values.at( 0 );
2398 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2399 if ( inter.isValid() )
2400 {
2401 return QVariant( inter.months() );
2402 }
2403 else
2404 {
2405 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2406 return QVariant( d1.date().month() );
2407 }
2408}
2409
2410static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2411{
2412 QVariant value = values.at( 0 );
2413 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2414 if ( inter.isValid() )
2415 {
2416 return QVariant( inter.weeks() );
2417 }
2418 else
2419 {
2420 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2421 return QVariant( d1.date().weekNumber() );
2422 }
2423}
2424
2425static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2426{
2427 QVariant value = values.at( 0 );
2428 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2429 if ( inter.isValid() )
2430 {
2431 return QVariant( inter.hours() );
2432 }
2433 else
2434 {
2435 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2436 return QVariant( t1.hour() );
2437 }
2438}
2439
2440static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2441{
2442 QVariant value = values.at( 0 );
2443 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2444 if ( inter.isValid() )
2445 {
2446 return QVariant( inter.minutes() );
2447 }
2448 else
2449 {
2450 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2451 return QVariant( t1.minute() );
2452 }
2453}
2454
2455static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2456{
2457 QVariant value = values.at( 0 );
2458 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2459 if ( inter.isValid() )
2460 {
2461 return QVariant( inter.seconds() );
2462 }
2463 else
2464 {
2465 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2466 return QVariant( t1.second() );
2467 }
2468}
2469
2470static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2471{
2472 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2473 if ( dt.isValid() )
2474 {
2475 return QVariant( dt.toMSecsSinceEpoch() );
2476 }
2477 else
2478 {
2479 return QVariant();
2480 }
2481}
2482
2483static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2484{
2485 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
2486 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
2487 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
2488}
2489
2490static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2491{
2492 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
2493 if ( parent->hasEvalError() )
2494 {
2495 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif" ) ) );
2496 return QVariant();
2497 }
2498 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2499 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
2500}
2501
2502static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2503{
2504 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
2505 if ( parent->hasEvalError() )
2506 {
2507 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif_geotag" ) ) );
2508 return QVariant();
2509 }
2510 bool ok;
2511 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
2512}
2513
2514#define ENSURE_GEOM_TYPE(f, g, geomtype) \
2515 if ( !(f).hasGeometry() ) \
2516 return QVariant(); \
2517 QgsGeometry g = (f).geometry(); \
2518 if ( (g).type() != (geomtype) ) \
2519 return QVariant();
2520
2521static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2522{
2523 FEAT_FROM_CONTEXT( context, f )
2525 if ( g.isMultipart() )
2526 {
2527 return g.asMultiPoint().at( 0 ).x();
2528 }
2529 else
2530 {
2531 return g.asPoint().x();
2532 }
2533}
2534
2535static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2536{
2537 FEAT_FROM_CONTEXT( context, f )
2539 if ( g.isMultipart() )
2540 {
2541 return g.asMultiPoint().at( 0 ).y();
2542 }
2543 else
2544 {
2545 return g.asPoint().y();
2546 }
2547}
2548
2549static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2550{
2551 FEAT_FROM_CONTEXT( context, f )
2553
2554 if ( g.isEmpty() )
2555 return QVariant();
2556
2557 const QgsAbstractGeometry *abGeom = g.constGet();
2558
2559 if ( g.isEmpty() || !abGeom->is3D() )
2560 return QVariant();
2561
2562 if ( g.type() == QgsWkbTypes::PointGeometry && !g.isMultipart() )
2563 {
2564 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
2565 if ( point )
2566 return point->z();
2567 }
2568 else if ( g.type() == QgsWkbTypes::PointGeometry && g.isMultipart() )
2569 {
2570 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
2571 {
2572 if ( collection->numGeometries() > 0 )
2573 {
2574 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
2575 return point->z();
2576 }
2577 }
2578 }
2579
2580 return QVariant();
2581}
2582
2583static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2584{
2585 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2586 if ( geom.isNull() )
2587 return QVariant();
2588
2589 bool isValid = geom.isGeosValid();
2590
2591 return QVariant( isValid );
2592}
2593
2594static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2595{
2596 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2597 if ( geom.isNull() )
2598 return QVariant();
2599
2600 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
2601#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
2603#else
2605#endif
2606 if ( methodString.compare( QLatin1String( "linework" ), Qt::CaseInsensitive ) == 0 )
2608 else if ( methodString.compare( QLatin1String( "structure" ), Qt::CaseInsensitive ) == 0 )
2610
2611 const bool keepCollapsed = values.value( 2 ).toBool();
2612
2613 QgsGeometry valid;
2614 try
2615 {
2616 valid = geom.makeValid( method, keepCollapsed );
2617 }
2618 catch ( QgsNotSupportedException & )
2619 {
2620 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
2621 return QVariant();
2622 }
2623
2624 return QVariant::fromValue( valid );
2625}
2626
2627static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2628{
2629 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2630 if ( geom.isNull() )
2631 return QVariant();
2632
2633 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
2634 QVariantList array;
2635 for ( int i = 0; i < multiGeom.size(); ++i )
2636 {
2637 array += QVariant::fromValue( multiGeom.at( i ) );
2638 }
2639
2640 return array;
2641}
2642
2643static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2644{
2645 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2646 if ( geom.isNull() )
2647 return QVariant();
2648
2649 //if single point, return the point's x coordinate
2650 if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2651 {
2652 return geom.asPoint().x();
2653 }
2654
2655 //otherwise return centroid x
2656 QgsGeometry centroid = geom.centroid();
2657 QVariant result( centroid.asPoint().x() );
2658 return result;
2659}
2660
2661static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2662{
2663 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2664 if ( geom.isNull() )
2665 return QVariant();
2666
2667 //if single point, return the point's y coordinate
2668 if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2669 {
2670 return geom.asPoint().y();
2671 }
2672
2673 //otherwise return centroid y
2674 QgsGeometry centroid = geom.centroid();
2675 QVariant result( centroid.asPoint().y() );
2676 return result;
2677}
2678
2679static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2680{
2681 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2682 if ( geom.isNull() )
2683 return QVariant(); //or 0?
2684
2685 if ( !geom.constGet()->is3D() )
2686 return QVariant();
2687
2688 //if single point, return the point's z coordinate
2689 if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2690 {
2691 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2692 if ( point )
2693 return point->z();
2694 }
2695 else if ( geom.type() == QgsWkbTypes::PointGeometry && geom.isMultipart() )
2696 {
2697 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2698 {
2699 if ( collection->numGeometries() == 1 )
2700 {
2701 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
2702 return point->z();
2703 }
2704 }
2705 }
2706
2707 return QVariant();
2708}
2709
2710static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2711{
2712 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2713 if ( geom.isNull() )
2714 return QVariant(); //or 0?
2715
2716 if ( !geom.constGet()->isMeasure() )
2717 return QVariant();
2718
2719 //if single point, return the point's m value
2720 if ( geom.type() == QgsWkbTypes::PointGeometry && !geom.isMultipart() )
2721 {
2722 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
2723 if ( point )
2724 return point->m();
2725 }
2726 else if ( geom.type() == QgsWkbTypes::PointGeometry && geom.isMultipart() )
2727 {
2728 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2729 {
2730 if ( collection->numGeometries() == 1 )
2731 {
2732 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
2733 return point->m();
2734 }
2735 }
2736 }
2737
2738 return QVariant();
2739}
2740
2741static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2742{
2743 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2744
2745 if ( geom.isNull() )
2746 return QVariant();
2747
2748 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2749
2750 if ( idx < 0 )
2751 {
2752 //negative idx
2753 int count = geom.constGet()->nCoordinates();
2754 idx = count + idx;
2755 }
2756 else
2757 {
2758 //positive idx is 1 based
2759 idx -= 1;
2760 }
2761
2762 QgsVertexId vId;
2763 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
2764 {
2765 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
2766 return QVariant();
2767 }
2768
2769 QgsPoint point = geom.constGet()->vertexAt( vId );
2770 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
2771}
2772
2773static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2774{
2775 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2776
2777 if ( geom.isNull() )
2778 return QVariant();
2779
2780 QgsVertexId vId;
2781 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
2782 {
2783 return QVariant();
2784 }
2785
2786 QgsPoint point = geom.constGet()->vertexAt( vId );
2787 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
2788}
2789
2790static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2791{
2792 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2793
2794 if ( geom.isNull() )
2795 return QVariant();
2796
2797 QgsVertexId vId;
2798 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
2799 {
2800 return QVariant();
2801 }
2802
2803 QgsPoint point = geom.constGet()->vertexAt( vId );
2804 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
2805}
2806
2807static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2808{
2809 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2810
2811 if ( geom.isNull() )
2812 return QVariant();
2813
2814 bool ignoreClosing = false;
2815 if ( values.length() > 1 )
2816 {
2817 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
2818 }
2819
2820 QgsMultiPoint *mp = new QgsMultiPoint();
2821
2822 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
2823 for ( const QgsRingSequence &part : sequence )
2824 {
2825 for ( const QgsPointSequence &ring : part )
2826 {
2827 bool skipLast = false;
2828 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
2829 {
2830 skipLast = true;
2831 }
2832
2833 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i )
2834 {
2835 mp->addGeometry( ring.at( i ).clone() );
2836 }
2837 }
2838 }
2839
2840 return QVariant::fromValue( QgsGeometry( mp ) );
2841}
2842
2843static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2844{
2845 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2846
2847 if ( geom.isNull() )
2848 return QVariant();
2849
2850 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
2851
2852 //OK, now we have a complete list of segmentized lines from the geometry
2854 for ( QgsLineString *line : linesToProcess )
2855 {
2856 for ( int i = 0; i < line->numPoints() - 1; ++i )
2857 {
2859 segment->setPoints( QgsPointSequence()
2860 << line->pointN( i )
2861 << line->pointN( i + 1 ) );
2862 ml->addGeometry( segment );
2863 }
2864 delete line;
2865 }
2866
2867 return QVariant::fromValue( QgsGeometry( ml ) );
2868}
2869
2870static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2871{
2872 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2873
2874 if ( geom.isNull() )
2875 return QVariant();
2876
2877 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
2878 if ( !curvePolygon && geom.isMultipart() )
2879 {
2880 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
2881 {
2882 if ( collection->numGeometries() == 1 )
2883 {
2884 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
2885 }
2886 }
2887 }
2888
2889 if ( !curvePolygon )
2890 return QVariant();
2891
2892 //idx is 1 based
2893 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
2894
2895 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
2896 return QVariant();
2897
2898 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
2899 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
2900 return result;
2901}
2902
2903static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2904{
2905 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2906
2907 if ( geom.isNull() )
2908 return QVariant();
2909
2910 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
2911 if ( !collection )
2912 return QVariant();
2913
2914 //idx is 1 based
2915 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
2916
2917 if ( idx < 0 || idx >= collection->numGeometries() )
2918 return QVariant();
2919
2920 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
2921 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
2922 return result;
2923}
2924
2925static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2926{
2927 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2928
2929 if ( geom.isNull() )
2930 return QVariant();
2931
2932 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
2933 if ( !boundary )
2934 return QVariant();
2935
2936 return QVariant::fromValue( QgsGeometry( boundary ) );
2937}
2938
2939static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2940{
2941 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2942
2943 if ( geom.isNull() )
2944 return QVariant();
2945
2946 QgsGeometry merged = geom.mergeLines();
2947 if ( merged.isNull() )
2948 return QVariant();
2949
2950 return QVariant::fromValue( merged );
2951}
2952
2953static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2954{
2955 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2956 if ( geom.isNull() )
2957 return QVariant();
2958
2959 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
2960 if ( geom2.isNull() )
2961 return QVariant();
2962
2963 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
2964 if ( sharedPaths.isNull() )
2965 return QVariant();
2966
2967 return QVariant::fromValue( sharedPaths );
2968}
2969
2970
2971static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2972{
2973 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2974
2975 if ( geom.isNull() )
2976 return QVariant();
2977
2978 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2979
2980 QgsGeometry simplified = geom.simplify( tolerance );
2981 if ( simplified.isNull() )
2982 return QVariant();
2983
2984 return simplified;
2985}
2986
2987static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2988{
2989 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2990
2991 if ( geom.isNull() )
2992 return QVariant();
2993
2994 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
2995
2997
2998 QgsGeometry simplified = simplifier.simplify( geom );
2999 if ( simplified.isNull() )
3000 return QVariant();
3001
3002 return simplified;
3003}
3004
3005static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3006{
3007 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3008
3009 if ( geom.isNull() )
3010 return QVariant();
3011
3012 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
3013 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
3014 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3015 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
3016
3017 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
3018 if ( smoothed.isNull() )
3019 return QVariant();
3020
3021 return smoothed;
3022}
3023
3024static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3025{
3026 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3027
3028 if ( geom.isNull() )
3029 return QVariant();
3030
3031 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3032 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3033 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3034
3035 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
3036 if ( waved.isNull() )
3037 return QVariant();
3038
3039 return waved;
3040}
3041
3042static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3043{
3044 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3045
3046 if ( geom.isNull() )
3047 return QVariant();
3048
3049 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3050 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3051 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3052 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3053 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3054
3055 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength,
3056 minAmplitude, maxAmplitude, seed );
3057 if ( waved.isNull() )
3058 return QVariant();
3059
3060 return waved;
3061}
3062
3063static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3064{
3065 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3066
3067 if ( geom.isNull() )
3068 return QVariant();
3069
3070 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3071 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3072 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3073
3074 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
3075 if ( waved.isNull() )
3076 return QVariant();
3077
3078 return waved;
3079}
3080
3081static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3082{
3083 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3084
3085 if ( geom.isNull() )
3086 return QVariant();
3087
3088 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3089 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3090 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3091 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3092 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3093
3094 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength,
3095 minAmplitude, maxAmplitude, seed );
3096 if ( waved.isNull() )
3097 return QVariant();
3098
3099 return waved;
3100}
3101
3102static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3103{
3104 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3105
3106 if ( geom.isNull() )
3107 return QVariant();
3108
3109 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3110 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3111 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3112
3113 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
3114 if ( waved.isNull() )
3115 return QVariant();
3116
3117 return waved;
3118}
3119
3120static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3121{
3122 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3123
3124 if ( geom.isNull() )
3125 return QVariant();
3126
3127 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3128 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3129 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3130 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3131 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3132
3133 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength,
3134 minAmplitude, maxAmplitude, seed );
3135 if ( waved.isNull() )
3136 return QVariant();
3137
3138 return waved;
3139}
3140
3141static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3142{
3143 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3144
3145 if ( geom.isNull() )
3146 return QVariant();
3147
3148 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
3149 QVector< double > dashPattern;
3150 dashPattern.reserve( pattern.size() );
3151 for ( const QVariant &value : std::as_const( pattern ) )
3152 {
3153 bool ok = false;
3154 double v = value.toDouble( &ok );
3155 if ( ok )
3156 {
3157 dashPattern << v;
3158 }
3159 else
3160 {
3161 parent->setEvalErrorString( QStringLiteral( "Dash pattern must be an array of numbers" ) );
3162 return QgsGeometry();
3163 }
3164 }
3165
3166 if ( dashPattern.size() % 2 != 0 )
3167 {
3168 parent->setEvalErrorString( QStringLiteral( "Dash pattern must contain an even number of elements" ) );
3169 return QgsGeometry();
3170 }
3171
3172 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
3174 if ( startRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3176 else if ( startRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3178 else if ( startRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3180 else if ( startRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3182 else if ( startRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3184 else
3185 {
3186 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( startRuleString ) );
3187 return QgsGeometry();
3188 }
3189
3190 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
3192 if ( endRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3194 else if ( endRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3196 else if ( endRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3198 else if ( endRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3200 else if ( endRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3202 else
3203 {
3204 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( endRuleString ) );
3205 return QgsGeometry();
3206 }
3207
3208 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
3210 if ( adjustString.compare( QLatin1String( "both" ), Qt::CaseInsensitive ) == 0 )
3212 else if ( adjustString.compare( QLatin1String( "dash" ), Qt::CaseInsensitive ) == 0 )
3214 else if ( adjustString.compare( QLatin1String( "gap" ), Qt::CaseInsensitive ) == 0 )
3216 else
3217 {
3218 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern size adjustment" ).arg( adjustString ) );
3219 return QgsGeometry();
3220 }
3221
3222 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
3223
3224 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
3225 if ( result.isNull() )
3226 return QVariant();
3227
3228 return result;
3229}
3230
3231static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3232{
3233 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3234
3235 if ( geom.isNull() )
3236 return QVariant();
3237
3238 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3239 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
3240 if ( densified.isNull() )
3241 return QVariant();
3242
3243 return densified;
3244}
3245
3246static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3247{
3248 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3249
3250 if ( geom.isNull() )
3251 return QVariant();
3252
3253 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3254 const QgsGeometry densified = geom.densifyByDistance( distance );
3255 if ( densified.isNull() )
3256 return QVariant();
3257
3258 return densified;
3259}
3260
3261static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3262{
3263 QVariantList list;
3264 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
3265 {
3266 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
3267 }
3268 else
3269 {
3270 list = values;
3271 }
3272
3273 QVector< QgsGeometry > parts;
3274 parts.reserve( list.size() );
3275 for ( const QVariant &value : std::as_const( list ) )
3276 {
3277 if ( value.userType() == QMetaType::type( "QgsGeometry" ) )
3278 {
3279 parts << value.value<QgsGeometry>();
3280 }
3281 else
3282 {
3283 parent->setEvalErrorString( QStringLiteral( "Cannot convert to geometry" ) );
3284 return QgsGeometry();
3285 }
3286 }
3287
3288 return QgsGeometry::collectGeometry( parts );
3289}
3290
3291static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3292{
3293 if ( values.count() < 2 || values.count() > 4 )
3294 {
3295 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
3296 return QVariant();
3297 }
3298
3299 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3300 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3301 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
3302 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
3303 switch ( values.count() )
3304 {
3305 case 2:
3306 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
3307 case 3:
3308 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsWkbTypes::PointZ, x, y, z ) ) );
3309 case 4:
3310 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsWkbTypes::PointZM, x, y, z, m ) ) );
3311 }
3312 return QVariant(); //avoid warning
3313}
3314
3315static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3316{
3317 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3318 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3319 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3320 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsWkbTypes::PointM, x, y, 0.0, m ) ) );
3321}
3322
3323static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3324{
3325 if ( values.empty() )
3326 {
3327 return QVariant();
3328 }
3329
3330 QVector<QgsPoint> points;
3331 points.reserve( values.count() );
3332
3333 auto addPoint = [&points]( const QgsGeometry & geom )
3334 {
3335 if ( geom.isNull() )
3336 return;
3337
3338 if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
3339 return;
3340
3341 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3342 if ( !point )
3343 return;
3344
3345 points << *point;
3346 };
3347
3348 for ( const QVariant &value : values )
3349 {
3350 if ( value.type() == QVariant::List )
3351 {
3352 const QVariantList list = value.toList();
3353 for ( const QVariant &v : list )
3354 {
3355 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
3356 }
3357 }
3358 else
3359 {
3360 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
3361 }
3362 }
3363
3364 if ( points.count() < 2 )
3365 return QVariant();
3366
3367 return QgsGeometry( new QgsLineString( points ) );
3368}
3369
3370static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3371{
3372 if ( values.count() < 1 )
3373 {
3374 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
3375 return QVariant();
3376 }
3377
3378 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3379
3380 if ( outerRing.type() == QgsWkbTypes::PolygonGeometry )
3381 return outerRing; // if it's already a polygon we have nothing to do
3382
3383 if ( outerRing.type() != QgsWkbTypes::LineGeometry || outerRing.isNull() )
3384 return QVariant();
3385
3386 std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
3387
3388 const QgsCurve *exteriorRing = qgsgeometry_cast< QgsCurve * >( outerRing.constGet() );
3389 if ( !exteriorRing && outerRing.isMultipart() )
3390 {
3391 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( outerRing.constGet() ) )
3392 {
3393 if ( collection->numGeometries() == 1 )
3394 {
3395 exteriorRing = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
3396 }
3397 }
3398 }
3399
3400 if ( !exteriorRing )
3401 return QVariant();
3402
3403 polygon->setExteriorRing( exteriorRing->segmentize() );
3404
3405
3406 for ( int i = 1; i < values.count(); ++i )
3407 {
3408 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
3409 if ( ringGeom.isNull() )
3410 continue;
3411
3412 if ( ringGeom.type() != QgsWkbTypes::LineGeometry || ringGeom.isNull() )
3413 continue;
3414
3415 const QgsCurve *ring = qgsgeometry_cast< QgsCurve * >( ringGeom.constGet() );
3416 if ( !ring && ringGeom.isMultipart() )
3417 {
3418 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( ringGeom.constGet() ) )
3419 {
3420 if ( collection->numGeometries() == 1 )
3421 {
3422 ring = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
3423 }
3424 }
3425 }
3426
3427 if ( !ring )
3428 continue;
3429
3430 polygon->addInteriorRing( ring->segmentize() );
3431 }
3432
3433 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
3434}
3435
3436static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3437{
3438 std::unique_ptr<QgsTriangle> tr( new QgsTriangle() );
3439 std::unique_ptr<QgsLineString> lineString( new QgsLineString() );
3440 lineString->clear();
3441
3442 for ( const QVariant &value : values )
3443 {
3444 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
3445 if ( geom.isNull() )
3446 return QVariant();
3447
3448 if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
3449 return QVariant();
3450
3451 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3452 if ( !point && geom.isMultipart() )
3453 {
3454 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3455 {
3456 if ( collection->numGeometries() == 1 )
3457 {
3458 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3459 }
3460 }
3461 }
3462
3463 if ( !point )
3464 return QVariant();
3465
3466 lineString->addVertex( *point );
3467 }
3468
3469 tr->setExteriorRing( lineString.release() );
3470
3471 return QVariant::fromValue( QgsGeometry( tr.release() ) );
3472}
3473
3474static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3475{
3476 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3477 if ( geom.isNull() )
3478 return QVariant();
3479
3480 if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
3481 return QVariant();
3482
3483 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3484 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3485
3486 if ( segment < 3 )
3487 {
3488 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3489 return QVariant();
3490 }
3491 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3492 if ( !point && geom.isMultipart() )
3493 {
3494 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3495 {
3496 if ( collection->numGeometries() == 1 )
3497 {
3498 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3499 }
3500 }
3501 }
3502 if ( !point )
3503 return QVariant();
3504
3505 QgsCircle circ( *point, radius );
3506 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3507}
3508
3509static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3510{
3511 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3512 if ( geom.isNull() )
3513 return QVariant();
3514
3515 if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
3516 return QVariant();
3517
3518 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3519 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3520 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3521 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
3522 if ( segment < 3 )
3523 {
3524 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3525 return QVariant();
3526 }
3527 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3528 if ( !point && geom.isMultipart() )
3529 {
3530 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3531 {
3532 if ( collection->numGeometries() == 1 )
3533 {
3534 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3535 }
3536 }
3537 }
3538 if ( !point )
3539 return QVariant();
3540
3541 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
3542 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3543}
3544
3545static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3546{
3547
3548 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3549 if ( pt1.isNull() )
3550 return QVariant();
3551
3552 if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
3553 return QVariant();
3554
3555 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3556 if ( pt2.isNull() )
3557 return QVariant();
3558
3559 if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
3560 return QVariant();
3561
3562 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
3563 if ( nbEdges < 3 )
3564 {
3565 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
3566 return QVariant();
3567 }
3568
3569 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3571 {
3572 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
3573 return QVariant();
3574 }
3575
3576 const QgsPoint *center = qgsgeometry_cast< const QgsPoint * >( pt1.constGet() );
3577 if ( !center && pt1.isMultipart() )
3578 {
3579 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt1.constGet() ) )
3580 {
3581 if ( collection->numGeometries() == 1 )
3582 {
3583 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3584 }
3585 }
3586 }
3587 if ( !center )
3588 return QVariant();
3589
3590 const QgsPoint *corner = qgsgeometry_cast< const QgsPoint * >( pt2.constGet() );
3591 if ( !corner && pt2.isMultipart() )
3592 {
3593 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt2.constGet() ) )
3594 {
3595 if ( collection->numGeometries() == 1 )
3596 {
3597 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3598 }
3599 }
3600 }
3601 if ( !corner )
3602 return QVariant();
3603
3604 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
3605
3606 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
3607
3608}
3609
3610static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3611{
3612 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3613 if ( pt1.isNull() )
3614 return QVariant();
3615 if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
3616 return QVariant();
3617
3618 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3619 if ( pt2.isNull() )
3620 return QVariant();
3621 if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
3622 return QVariant();
3623
3624 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
3625 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
3626 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
3627
3628 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
3629}
3630
3631static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3632{
3633 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3634 if ( pt1.isNull() )
3635 return QVariant();
3636 if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
3637 return QVariant();
3638
3639 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3640 if ( pt2.isNull() )
3641 return QVariant();
3642 if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
3643 return QVariant();
3644
3645 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
3646 if ( pt3.isNull() )
3647 return QVariant();
3648 if ( pt3.type() != QgsWkbTypes::PointGeometry || pt3.isMultipart() )
3649 return QVariant();
3650
3651 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3652 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
3653 {
3654 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
3655 return QVariant();
3656 }
3657 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
3658 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
3659 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
3660 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
3661 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
3662}
3663
3664static QVariant pointAt( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) // helper function
3665{
3666 FEAT_FROM_CONTEXT( context, f )
3667 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
3668 QgsGeometry g = f.geometry();
3669 if ( g.isNull() )
3670 return QVariant();
3671
3672 if ( idx < 0 )
3673 {
3674 idx += g.constGet()->nCoordinates();
3675 }
3676 if ( idx < 0 || idx >= g.constGet()->nCoordinates() )
3677 {
3678 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
3679 return QVariant();
3680 }
3681
3682 QgsPointXY p = g.vertexAt( idx );
3683 return QVariant( QPointF( p.x(), p.y() ) );
3684}
3685
3686static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction * )
3687{
3688 QVariant v = pointAt( values, f, parent );
3689 if ( v.type() == QVariant::PointF )
3690 return QVariant( v.toPointF().x() );
3691 else
3692 return QVariant();
3693}
3694static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction * )
3695{
3696 QVariant v = pointAt( values, f, parent );
3697 if ( v.type() == QVariant::PointF )
3698 return QVariant( v.toPointF().y() );
3699 else
3700 return QVariant();
3701}
3702static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3703{
3704 if ( !context )
3705 return QVariant();
3706
3707 // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
3708 if ( context->hasGeometry() )
3709 return context->geometry();
3710 else
3711 {
3712 FEAT_FROM_CONTEXT( context, f )
3713 QgsGeometry geom = f.geometry();
3714 if ( !geom.isNull() )
3715 return QVariant::fromValue( geom );
3716 else
3717 return QVariant();
3718 }
3719}
3720
3721static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3722{
3723 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3724 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
3725 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3726 return result;
3727}
3728
3729static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3730{
3731 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
3732 if ( wkb.isNull() )
3733 return QVariant();
3734
3735 QgsGeometry geom;
3736 geom.fromWkb( wkb );
3737 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3738}
3739
3740static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3741{
3742 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3743 QgsOgcUtils::Context ogcContext;
3744 if ( context )
3745 {
3746 QgsWeakMapLayerPointer mapLayerPtr {context->variable( QStringLiteral( "layer" ) ).value<QgsWeakMapLayerPointer>() };
3747 if ( mapLayerPtr )
3748 {
3749 ogcContext.layer = mapLayerPtr.data();
3750 ogcContext.transformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
3751 }
3752 }
3753 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
3754 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
3755 return result;
3756}
3757
3758static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3759{
3760 FEAT_FROM_CONTEXT( context, f )
3762 QgsDistanceArea *calc = parent->geomCalculator();
3763 if ( calc )
3764 {
3765 double area = calc->measureArea( f.geometry() );
3766 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
3767 return QVariant( area );
3768 }
3769 else
3770 {
3771 return QVariant( f.geometry().area() );
3772 }
3773}
3774
3775static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3776{
3777 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3778
3779 if ( geom.type() != QgsWkbTypes::PolygonGeometry )
3780 return QVariant();
3781
3782 return QVariant( geom.area() );
3783}
3784
3785static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3786{
3787 FEAT_FROM_CONTEXT( context, f )
3789 QgsDistanceArea *calc = parent->geomCalculator();
3790 if ( calc )
3791 {
3792 double len = calc->measureLength( f.geometry() );
3793 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
3794 return QVariant( len );
3795 }
3796 else
3797 {
3798 return QVariant( f.geometry().length() );
3799 }
3800}
3801
3802static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3803{
3804 FEAT_FROM_CONTEXT( context, f )
3806 QgsDistanceArea *calc = parent->geomCalculator();
3807 if ( calc )
3808 {
3809 double len = calc->measurePerimeter( f.geometry() );
3810 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
3811 return QVariant( len );
3812 }
3813 else
3814 {
3815 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
3816 }
3817}
3818
3819static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3820{
3821 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3822
3823 if ( geom.type() != QgsWkbTypes::PolygonGeometry )
3824 return QVariant();
3825
3826 //length for polygons = perimeter
3827 return QVariant( geom.length() );
3828}
3829
3830static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3831{
3832 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3833 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
3834}
3835
3836static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3837{
3838 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3839 if ( geom.isNull() )
3840 return QVariant();
3841
3842 return QVariant( geom.constGet()->partCount() );
3843}
3844
3845static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3846{
3847 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3848 if ( geom.isNull() )
3849 return QVariant();
3850
3851 return QVariant( geom.isMultipart() );
3852}
3853
3854static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3855{
3856 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3857
3858 if ( geom.isNull() )
3859 return QVariant();
3860
3861 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
3862 if ( curvePolygon )
3863 return QVariant( curvePolygon->numInteriorRings() );
3864
3865 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
3866 if ( collection )
3867 {
3868 //find first CurvePolygon in collection
3869 for ( int i = 0; i < collection->numGeometries(); ++i )
3870 {
3871 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
3872 if ( !curvePolygon )
3873 continue;
3874
3875 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
3876 }
3877 }
3878
3879 return QVariant();
3880}
3881
3882static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3883{
3884 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3885
3886 if ( geom.isNull() )
3887 return QVariant();
3888
3889 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
3890 if ( curvePolygon )
3891 return QVariant( curvePolygon->ringCount() );
3892
3893 bool foundPoly = false;
3894 int ringCount = 0;
3895 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
3896 if ( collection )
3897 {
3898 //find CurvePolygons in collection
3899 for ( int i = 0; i < collection->numGeometries(); ++i )
3900 {
3901 curvePolygon = qgsgeometry_cast< QgsCurvePolygon *>( collection->geometryN( i ) );
3902 if ( !curvePolygon )
3903 continue;
3904
3905 foundPoly = true;
3906 ringCount += curvePolygon->ringCount();
3907 }
3908 }
3909
3910 if ( !foundPoly )
3911 return QVariant();
3912
3913 return QVariant( ringCount );
3914}
3915
3916static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3917{
3918 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3919 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
3920 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
3921 return result;
3922}
3923
3924static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3925{
3926 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3927 return QVariant::fromValue( geom.boundingBox().width() );
3928}
3929
3930static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3931{
3932 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3933 return QVariant::fromValue( geom.boundingBox().height() );
3934}
3935
3936static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3937{
3938 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3939 if ( geom.isNull() )
3940 return QVariant();
3941
3943}
3944
3945static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3946{
3947 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3948 return QVariant::fromValue( geom.boundingBox().xMinimum() );
3949}
3950
3951static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3952{
3953 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3954 return QVariant::fromValue( geom.boundingBox().xMaximum() );
3955}
3956
3957static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3958{
3959 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3960 return QVariant::fromValue( geom.boundingBox().yMinimum() );
3961}
3962
3963static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3964{
3965 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3966 return QVariant::fromValue( geom.boundingBox().yMaximum() );
3967}
3968
3969static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3970{
3971 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3972
3973 if ( geom.isNull() || geom.isEmpty( ) )
3974 return QVariant();
3975
3976 if ( !geom.constGet()->is3D() )
3977 return QVariant();
3978
3979 double max = std::numeric_limits< double >::lowest();
3980
3981 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
3982 {
3983 double z = ( *it ).z();
3984
3985 if ( max < z )
3986 max = z;
3987 }
3988
3989 if ( max == std::numeric_limits< double >::lowest() )
3990 return QVariant( QVariant::Double );
3991
3992 return QVariant( max );
3993}
3994
3995static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3996{
3997 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3998
3999 if ( geom.isNull() || geom.isEmpty() )
4000 return QVariant();
4001
4002 if ( !geom.constGet()->is3D() )
4003 return QVariant();
4004
4005 double min = std::numeric_limits< double >::max();
4006
4007 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4008 {
4009 double z = ( *it ).z();
4010
4011 if ( z < min )
4012 min = z;
4013 }
4014
4015 if ( min == std::numeric_limits< double >::max() )
4016 return QVariant( QVariant::Double );
4017
4018 return QVariant( min );
4019}
4020
4021static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4022{
4023 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4024
4025 if ( geom.isNull() || geom.isEmpty() )
4026 return QVariant();
4027
4028 if ( !geom.constGet()->isMeasure() )
4029 return QVariant();
4030
4031 double min = std::numeric_limits< double >::max();
4032
4033 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4034 {
4035 double m = ( *it ).m();
4036
4037 if ( m < min )
4038 min = m;
4039 }
4040
4041 if ( min == std::numeric_limits< double >::max() )
4042 return QVariant( QVariant::Double );
4043
4044 return QVariant( min );
4045}
4046
4047static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4048{
4049 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4050
4051 if ( geom.isNull() || geom.isEmpty() )
4052 return QVariant();
4053
4054 if ( !geom.constGet()->isMeasure() )
4055 return QVariant();
4056
4057 double max = std::numeric_limits< double >::lowest();
4058
4059 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4060 {
4061 double m = ( *it ).m();
4062
4063 if ( max < m )
4064 max = m;
4065 }
4066
4067 if ( max == std::numeric_limits< double >::lowest() )
4068 return QVariant( QVariant::Double );
4069
4070 return QVariant( max );
4071}
4072
4073static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4074{
4075 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4076 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom.constGet() );
4077 if ( !curve )
4078 {
4079 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
4080 return QVariant();
4081 }
4082
4083 return QVariant( curve->sinuosity() );
4084}
4085
4086static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4087{
4088 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4089 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4090 if ( !curve )
4091 {
4092 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
4093 return QVariant();
4094 }
4095
4096 return QVariant( curve->straightDistance2d() );
4097}
4098
4099static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4100{
4101 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4102 const QgsCurvePolygon *poly = geom.constGet() ? qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4103
4104 if ( !poly )
4105 {
4106 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
4107 return QVariant();
4108 }
4109
4110 return QVariant( poly->roundness() );
4111}
4112
4113
4114
4115static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4116{
4117 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4118 if ( geom.isNull() )
4119 return QVariant();
4120
4121 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
4122 flipped->swapXy();
4123 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
4124}
4125
4126static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4127{
4128 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4129 if ( fGeom.isNull() )
4130 return QVariant();
4131
4132 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
4133 if ( !curve && fGeom.isMultipart() )
4134 {
4135 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4136 {
4137 if ( collection->numGeometries() == 1 )
4138 {
4139 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4140 }
4141 }
4142 }
4143
4144 if ( !curve )
4145 return QVariant();
4146
4147 return QVariant::fromValue( curve->isClosed() );
4148}
4149
4150static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4151{
4152 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4153
4154 if ( geom.isNull() )
4155 return QVariant();
4156
4157 QVariant result;
4158 if ( !geom.isMultipart() )
4159 {
4160 const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( geom.constGet() );
4161
4162 if ( !line )
4163 return QVariant();
4164
4165 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4166 closedLine->close();
4167
4168 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
4169 }
4170 else
4171 {
4172 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( geom.constGet() );
4173
4174 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
4175
4176 for ( int i = 0; i < collection->numGeometries(); ++i )
4177 {
4178 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
4179 {
4180 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4181 closedLine->close();
4182
4183 closed->addGeometry( closedLine.release() );
4184 }
4185 }
4186 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
4187 }
4188
4189 return result;
4190}
4191
4192static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4193{
4194 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4195 if ( fGeom.isNull() )
4196 return QVariant();
4197
4198 return QVariant::fromValue( fGeom.isEmpty() );
4199}
4200
4201static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4202{
4203 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
4204 return QVariant::fromValue( true );
4205
4206 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4207 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
4208}
4209
4210static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4211{
4212 if ( values.length() < 2 || values.length() > 3 )
4213 return QVariant();
4214
4215 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4216 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4217
4218 if ( fGeom.isNull() || sGeom.isNull() )
4219 return QVariant();
4220
4221 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
4222
4223 if ( values.length() == 2 )
4224 {
4225 //two geometry arguments, return relation
4226 QString result = engine->relate( sGeom.constGet() );
4227 return QVariant::fromValue( result );
4228 }
4229 else
4230 {
4231 //three arguments, test pattern
4232 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4233 bool result = engine->relatePattern( sGeom.constGet(), pattern );
4234 return QVariant::fromValue( result );
4235 }
4236}
4237
4238static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4239{
4240 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4241 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4242 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
4243}
4244static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4245{
4246 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4247 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4248 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
4249}
4250static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4251{
4252 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4253 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4254 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
4255}
4256static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4257{
4258 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4259 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4260 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
4261}
4262static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4263{
4264 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4265 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4266 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
4267}
4268static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4269{
4270 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4271 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4272 return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
4273}
4274static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4275{
4276 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4277 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4278 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
4279}
4280static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4281{
4282 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4283 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4284 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
4285}
4286
4287static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4288{
4289 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4290 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4291 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4292 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4293 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4294 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4295
4297 if ( endCapString.compare( QLatin1String( "flat" ), Qt::CaseInsensitive ) == 0 )
4298 capStyle = Qgis::EndCapStyle::Flat;
4299 else if ( endCapString.compare( QLatin1String( "square" ), Qt::CaseInsensitive ) == 0 )
4300 capStyle = Qgis::EndCapStyle::Square;
4301
4303 if ( joinString.compare( QLatin1String( "miter" ), Qt::CaseInsensitive ) == 0 )
4304 joinStyle = Qgis::JoinStyle::Miter;
4305 else if ( joinString.compare( QLatin1String( "bevel" ), Qt::CaseInsensitive ) == 0 )
4306 joinStyle = Qgis::JoinStyle::Bevel;
4307
4308 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
4309 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4310 return result;
4311}
4312
4313static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4314{
4315 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4316 const QgsGeometry reoriented = fGeom.forceRHR();
4317 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4318}
4319
4320static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4321{
4322 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4323 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
4324 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4325}
4326
4327static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4328{
4329 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4330 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
4331 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4332}
4333
4334static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4335{
4336 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4337 const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( fGeom.constGet() );
4338 if ( !pt && fGeom.isMultipart() )
4339 {
4340 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4341 {
4342 if ( collection->numGeometries() == 1 )
4343 {
4344 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4345 }
4346 }
4347 }
4348
4349 if ( !pt )
4350 {
4351 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
4352 return QVariant();
4353 }
4354
4355 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4356 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4357 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4358 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4359
4360 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
4361 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4362 return result;
4363}
4364
4365static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4366{
4367 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4368 if ( fGeom.type() != QgsWkbTypes::LineGeometry )
4369 {
4370 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
4371 return QVariant();
4372 }
4373
4374 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4375 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4376 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4377
4378 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
4379 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4380 return result;
4381}
4382
4383static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4384{
4385 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4386 if ( fGeom.type() != QgsWkbTypes::LineGeometry )
4387 {
4388 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
4389 return QVariant();
4390 }
4391
4392 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
4393
4394 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
4395 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4396 return result;
4397}
4398
4399static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4400{
4401 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4402 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4403 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4404 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4405 if ( joinInt < 1 || joinInt > 3 )
4406 return QVariant();
4407 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
4408
4409 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4410
4411 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
4412 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4413 return result;
4414}
4415
4416static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4417{
4418 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4419 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4420 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4421
4422 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4423 if ( joinInt < 1 || joinInt > 3 )
4424 return QVariant();
4425 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
4426
4427 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4428
4429 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
4430 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4431 return result;
4432}
4433
4434static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4435{
4436 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4437 double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4438 double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4439
4440 QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
4441 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4442 return result;
4443}
4444
4445static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4446{
4447 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4448 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4449 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4450 fGeom.translate( dx, dy );
4451 return QVariant::fromValue( fGeom );
4452}
4453
4454static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4455{
4456 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4457 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4458 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent )
4459 : QgsGeometry();
4460 const bool perPart = values.value( 3 ).toBool();
4461
4462 if ( center.isNull() && perPart && fGeom.isMultipart() )
4463 {
4464 // no explicit center, rotating per part
4465 // (note that we only do this branch for multipart geometries -- for singlepart geometries
4466 // the result is equivalent to setting perPart as false anyway)
4467 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
4468 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
4469 {
4470 const QgsPointXY partCenter = ( *it )->boundingBox().center();
4471 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
4472 t.rotate( -rotation );
4473 t.translate( -partCenter.x(), -partCenter.y() );
4474 ( *it )->transform( t );
4475 }
4476 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
4477 }
4478 else
4479 {
4480 QgsPointXY pt;
4481 if ( center.isEmpty() )
4482 {
4483 // if center wasn't specified, use bounding box centroid
4484 pt = fGeom.boundingBox().center();
4485 }
4487 {
4488 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
4489 return QVariant();
4490 }
4491 else
4492 {
4493 pt = QgsPointXY( *qgsgeometry_cast< const QgsPoint * >( center.constGet()->simplifiedTypeRef() ) );
4494 }
4495
4496 fGeom.rotate( rotation, pt );
4497 return QVariant::fromValue( fGeom );
4498 }
4499}
4500
4501static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4502{
4503 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4504 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4505 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4506 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent )
4507 : QgsGeometry();
4508
4509 QgsPointXY pt;
4510 if ( center.isNull() )
4511 {
4512 // if center wasn't specified, use bounding box centroid
4513 pt = fGeom.boundingBox().center();
4514 }
4516 {
4517 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
4518 return QVariant();
4519 }
4520 else
4521 {
4522 pt = center.asPoint();
4523 }
4524
4525 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
4526 t.scale( xScale, yScale );
4527 t.translate( -pt.x(), -pt.y() );
4528 fGeom.transform( t );
4529 return QVariant::fromValue( fGeom );
4530}
4531
4532static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4533{
4534 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4535 if ( fGeom.isNull() )
4536 {
4537 return QVariant();
4538 }
4539
4540 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4541 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4542
4543 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4544
4545 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4546 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4547
4548 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
4549 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
4550 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
4551 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
4552
4553 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
4554 {
4555 fGeom.get()->addZValue( 0 );
4556 }
4557 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
4558 {
4559 fGeom.get()->addMValue( 0 );
4560 }
4561
4562 QTransform transform;
4563 transform.translate( deltaX, deltaY );
4564 transform.rotate( rotationZ );
4565 transform.scale( scaleX, scaleY );
4566 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
4567
4568 return QVariant::fromValue( fGeom );
4569}
4570
4571
4572static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4573{
4574 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4575 QgsGeometry geom = fGeom.centroid();
4576 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4577 return result;
4578}
4579static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4580{
4581 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4582 QgsGeometry geom = fGeom.pointOnSurface();
4583 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4584 return result;
4585}
4586
4587static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4588{
4589 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4590 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4591 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
4592 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4593 return result;
4594}
4595
4596static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4597{
4598 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4599 QgsGeometry geom = fGeom.convexHull();
4600 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4601 return result;
4602}
4603
4604#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
4605static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4606{
4607 try
4608 {
4609 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4610 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4611 const bool allowHoles = values.value( 2 ).toBool();
4612 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
4613 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4614 return result;
4615 }
4616 catch ( QgsCsException &cse )
4617 {
4618 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
4619 return QVariant();
4620 }
4621}
4622#endif
4623
4624static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4625{
4626 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4627 int segments = 36;
4628 if ( values.length() == 2 )
4629 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4630 if ( segments < 0 )
4631 {
4632 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
4633 return QVariant();
4634 }
4635
4636 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
4637 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4638 return result;
4639}
4640
4641static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4642{
4643 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4645 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4646 return result;
4647}
4648
4649static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4650{
4651 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4652
4653 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
4654 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
4655 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
4656
4657 double area, angle, width, height;
4658 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
4659
4660 if ( geom.isNull() )
4661 {
4662 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
4663 return QVariant();
4664 }
4665 return angle;
4666}
4667
4668static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4669{
4670 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4671 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4672 QgsGeometry geom = fGeom.difference( sGeom );
4673 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4674 return result;
4675}
4676
4677static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4678{
4679 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4680 if ( fGeom.isNull() )
4681 return QVariant();
4682
4683 QVariant result;
4684 if ( !fGeom.isMultipart() )
4685 {
4686 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
4687 if ( !curve )
4688 return QVariant();
4689
4690 QgsCurve *reversed = curve->reversed();
4691 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
4692 }
4693 else
4694 {
4695 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( fGeom.constGet() );
4696 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
4697 for ( int i = 0; i < collection->numGeometries(); ++i )
4698 {
4699 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
4700 {
4701 reversed->addGeometry( curve->reversed() );
4702 }
4703 else
4704 {
4705 reversed->addGeometry( collection->geometryN( i )->clone() );
4706 }
4707 }
4708 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
4709 }
4710 return result;
4711}
4712
4713static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4714{
4715 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4716 if ( fGeom.isNull() )
4717 return QVariant();
4718
4719 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( fGeom.constGet() );
4720 if ( !curvePolygon && fGeom.isMultipart() )
4721 {
4722 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4723 {
4724 if ( collection->numGeometries() == 1 )
4725 {
4726 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
4727 }
4728 }
4729 }
4730
4731 if ( !curvePolygon || !curvePolygon->exteriorRing() )
4732 return QVariant();
4733
4734 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
4735 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
4736 return result;
4737}
4738
4739static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4740{
4741 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4742 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4743 return QVariant( fGeom.distance( sGeom ) );
4744}
4745
4746static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4747{
4748 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4749 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4750
4751 double res = -1;
4752 if ( values.length() == 3 && values.at( 2 ).isValid() )
4753 {
4754 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4755 densify = std::clamp( densify, 0.0, 1.0 );
4756 res = g1.hausdorffDistanceDensify( g2, densify );
4757 }
4758 else
4759 {
4760 res = g1.hausdorffDistance( g2 );
4761 }
4762
4763 return res > -1 ? QVariant( res ) : QVariant();
4764}
4765
4766static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4767{
4768 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4769 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4770 QgsGeometry geom = fGeom.intersection( sGeom );
4771 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4772 return result;
4773}
4774static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4775{
4776 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4777 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4778 QgsGeometry geom = fGeom.symDifference( sGeom );
4779 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4780 return result;
4781}
4782static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4783{
4784 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4785 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4786 QgsGeometry geom = fGeom.combine( sGeom );
4787 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4788 return result;
4789}
4790
4791static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4792{
4793 if ( values.length() < 1 || values.length() > 2 )
4794 return QVariant();
4795
4796 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4797 int prec = 8;
4798 if ( values.length() == 2 )
4799 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4800 QString wkt = fGeom.asWkt( prec );
4801 return QVariant( wkt );
4802}
4803
4804static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4805{
4806 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4807 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
4808}
4809
4810static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4811{
4812 if ( values.length() != 2 )
4813 {
4814 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
4815 return QVariant();
4816 }
4817
4818 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4819 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4820
4821 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
4822 if ( !pt1 && fGeom1.isMultipart() )
4823 {
4824 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
4825 {
4826 if ( collection->numGeometries() == 1 )
4827 {
4828 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4829 }
4830 }
4831 }
4832
4833 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
4834 if ( !pt2 && fGeom2.isMultipart() )
4835 {
4836 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
4837 {
4838 if ( collection->numGeometries() == 1 )
4839 {
4840 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4841 }
4842 }
4843 }
4844
4845 if ( !pt1 || !pt2 )
4846 {
4847 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
4848 return QVariant();
4849 }
4850
4851 // Code from PostGIS
4852 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
4853 {
4854 if ( pt1->y() < pt2->y() )
4855 return 0.0;
4856 else if ( pt1->y() > pt2->y() )
4857 return M_PI;
4858 else
4859 return 0;
4860 }
4861
4862 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
4863 {
4864 if ( pt1->x() < pt2->x() )
4865 return M_PI_2;
4866 else if ( pt1->x() > pt2->x() )
4867 return M_PI + ( M_PI_2 );
4868 else
4869 return 0;
4870 }
4871
4872 if ( pt1->x() < pt2->x() )
4873 {
4874 if ( pt1->y() < pt2->y() )
4875 {
4876 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
4877 }
4878 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
4879 {
4880 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
4881 + ( M_PI_2 );
4882 }
4883 }
4884
4885 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
4886 {
4887 if ( pt1->y() > pt2->y() )
4888 {
4889 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) )
4890 + M_PI;
4891 }
4892 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
4893 {
4894 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
4895 + ( M_PI + ( M_PI_2 ) );
4896 }
4897 }
4898}
4899
4900static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4901{
4902 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4903
4905 {
4906 parent->setEvalErrorString( QStringLiteral( "'project' requires a point geometry" ) );
4907 return QVariant();
4908 }
4909
4910 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4911 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4912 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4913
4914 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef( ) );
4915 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
4916
4917 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
4918}
4919
4920static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4921{
4922 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4923 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4924
4925 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
4926 if ( !pt1 && fGeom1.isMultipart() )
4927 {
4928 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
4929 {
4930 if ( collection->numGeometries() == 1 )
4931 {
4932 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4933 }
4934 }
4935 }
4936 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
4937 if ( !pt2 && fGeom2.isMultipart() )
4938 {
4939 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
4940 {
4941 if ( collection->numGeometries() == 1 )
4942 {
4943 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4944 }
4945 }
4946 }
4947
4948 if ( ( fGeom1.type() != QgsWkbTypes::PointGeometry ) || ( fGeom2.type() != QgsWkbTypes::PointGeometry ) ||
4949 !pt1 || !pt2 )
4950 {
4951 parent->setEvalErrorString( QStringLiteral( "Function 'inclination' requires two points as arguments." ) );
4952 return QVariant();
4953 }
4954
4955 return pt1->inclination( *pt2 );
4956
4957}
4958
4959static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4960{
4961 if ( values.length() != 3 )
4962 return QVariant();
4963
4964 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4965 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4966 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4967
4968 QgsGeometry geom = fGeom.extrude( x, y );
4969
4970 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
4971 return result;
4972}
4973
4974static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
4975{
4976 if ( values.length() < 2 )
4977 return QVariant();
4978
4979 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4980
4981 if ( !fGeom.isMultipart() )
4982 return values.at( 0 );
4983
4984 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
4985 QVariant cachedExpression;
4986 if ( ctx )
4987 cachedExpression = ctx->cachedValue( expString );
4988 QgsExpression expression;
4989
4990 if ( cachedExpression.isValid() )
4991 {
4992 expression = cachedExpression.value<QgsExpression>();
4993 }
4994 else
4995 expression = QgsExpression( expString );
4996
4997 bool asc = values.value( 2 ).toBool();
4998
4999 QgsExpressionContext *unconstedContext = nullptr;
5000 QgsFeature f;
5001 if ( ctx )
5002 {
5003 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
5004 // so no reason to worry
5005 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
5006 f = ctx->feature();
5007 }
5008 else
5009 {
5010 // If there's no context provided, create a fake one
5011 unconstedContext = new QgsExpressionContext();
5012 }
5013
5014 const QgsGeometryCollection *collection = qgsgeometry_cast<const QgsGeometryCollection *>( fGeom.constGet() );
5015 Q_ASSERT( collection ); // Should have failed the multipart check above
5016
5018 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
5019 QgsExpressionSorter sorter( orderBy );
5020
5021 QList<QgsFeature> partFeatures;
5022 partFeatures.reserve( collection->partCount() );
5023 for ( int i = 0; i < collection->partCount(); ++i )
5024 {
5025 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
5026 partFeatures << f;
5027 }
5028
5029 sorter.sortFeatures( partFeatures, unconstedContext );
5030
5031 QgsGeometryCollection *orderedGeom = qgsgeometry_cast<QgsGeometryCollection *>( fGeom.constGet()->clone() );
5032
5033 Q_ASSERT( orderedGeom );
5034
5035 while ( orderedGeom->partCount() )
5036 orderedGeom->removeGeometry( 0 );
5037
5038 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
5039 {
5040 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
5041 }
5042
5043 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
5044
5045 if ( !ctx )
5046 delete unconstedContext;
5047
5048 return result;
5049}
5050
5051static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5052{
5053 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5054 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5055
5056 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
5057
5058 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5059 return result;
5060}
5061
5062static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5063{
5064 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5065 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5066
5067 QgsGeometry geom = fromGeom.shortestLine( toGeom );
5068
5069 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5070 return result;
5071}
5072
5073static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5074{
5075 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5076 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5077
5078 QgsGeometry geom = lineGeom.interpolate( distance );
5079
5080 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5081 return result;
5082}
5083
5084static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5085{
5086 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5087 if ( lineGeom.type() != QgsWkbTypes::LineGeometry )
5088 {
5089 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
5090 return QVariant();
5091 }
5092
5093 const QgsCurve *curve = nullptr;
5094 if ( !lineGeom.isMultipart() )
5095 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
5096 else
5097 {
5098 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( lineGeom.constGet() ) )
5099 {
5100 if ( collection->numGeometries() > 0 )
5101 {
5102 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5103 }
5104 }
5105 }
5106 if ( !curve )
5107 return QVariant();
5108
5109 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5110 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5111
5112 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
5113 QgsGeometry result( std::move( substring ) );
5114 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
5115}
5116
5117static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5118{
5119 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5120 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5121
5122 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
5123}
5124
5125static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5126{
5127 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5128 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5129 if ( vertex < 0 )
5130 {
5131 //negative idx
5132 int count = geom.constGet()->nCoordinates();
5133 vertex = count + vertex;
5134 }
5135
5136 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
5137}
5138
5139static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5140{
5141 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5142 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5143 if ( vertex < 0 )
5144 {
5145 //negative idx
5146 int count = geom.constGet()->nCoordinates();
5147 vertex = count + vertex;
5148 }
5149
5150 return geom.distanceToVertex( vertex );
5151}
5152
5153static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5154{
5155 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5156 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5157
5158 double distance = lineGeom.lineLocatePoint( pointGeom );
5159
5160 return distance >= 0 ? distance : QVariant();
5161}
5162
5163static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5164{
5165 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
5166 {
5167 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5168 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5169 }
5170
5171 if ( values.length() >= 1 )
5172 {
5173 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5174 return QVariant( qlonglong( std::round( number ) ) );
5175 }
5176
5177 return QVariant();
5178}
5179
5180static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5181{
5182 Q_UNUSED( values )
5183 Q_UNUSED( parent )
5184 return M_PI;
5185}
5186
5187static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5188{
5189 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5190 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5191 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5192 if ( places < 0 )
5193 {
5194 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
5195 return QVariant();
5196 }
5197
5198 const bool omitGroupSeparator = values.value( 3 ).toBool();
5199 const bool trimTrailingZeros = values.value( 4 ).toBool();
5200
5201 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5202 if ( !omitGroupSeparator )
5203 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
5204 else
5205 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
5206
5207 QString res = locale.toString( value, 'f', places );
5208
5209 if ( trimTrailingZeros )
5210 {
5211#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5212 const QChar decimal = locale.decimalPoint();
5213 const QChar zeroDigit = locale.zeroDigit();
5214#else
5215 const QChar decimal = locale.decimalPoint().at( 0 );
5216 const QChar zeroDigit = locale.zeroDigit().at( 0 );
5217#endif
5218
5219 if ( res.contains( decimal ) )
5220 {
5221 int trimPoint = res.length() - 1;
5222
5223 while ( res.at( trimPoint ) == zeroDigit )
5224 trimPoint--;
5225
5226 if ( res.at( trimPoint ) == decimal )
5227 trimPoint--;
5228
5229 res.truncate( trimPoint + 1 );
5230 }
5231 }
5232
5233 return res;
5234}
5235
5236static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5237{
5238 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
5239 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5240 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5241
5242 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5243 return locale.toString( datetime, format );
5244}
5245
5246static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5247{
5248 QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5249 int avg = ( color.red() + color.green() + color.blue() ) / 3;
5250 int alpha = color.alpha();
5251
5252 color.setRgb( avg, avg, avg, alpha );
5253
5254 return QgsSymbolLayerUtils::encodeColor( color );
5255}
5256
5257static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5258{
5259 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5260 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
5261 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5262 if ( ratio > 1 )
5263 {
5264 ratio = 1;
5265 }
5266 else if ( ratio < 0 )
5267 {
5268 ratio = 0;
5269 }
5270
5271 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
5272 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
5273 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
5274 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
5275
5276 QColor newColor( red, green, blue, alpha );
5277
5278 return QgsSymbolLayerUtils::encodeColor( newColor );
5279}
5280
5281static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5282{
5283 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
5284 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5285 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5286 QColor color = QColor( red, green, blue );
5287 if ( ! color.isValid() )
5288 {
5289 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
5290 color = QColor( 0, 0, 0 );
5291 }
5292
5293 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
5294}
5295
5296static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5297{
5298 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
5299 QVariant value = node->eval( parent, context );
5300 if ( parent->hasEvalError() )
5301 {
5302 parent->setEvalErrorString( QString() );
5303 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
5305 value = node->eval( parent, context );
5307 }
5308 return value;
5309}
5310
5311static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5312{
5313 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
5315 QVariant value = node->eval( parent, context );
5317 if ( value.toBool() )
5318 {
5319 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
5321 value = node->eval( parent, context );
5323 }
5324 else
5325 {
5326 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
5328 value = node->eval( parent, context );
5330 }
5331 return value;
5332}
5333
5334static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5335{
5336 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
5337 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5338 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5339 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
5340 QColor color = QColor( red, green, blue, alpha );
5341 if ( ! color.isValid() )
5342 {
5343 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
5344 color = QColor( 0, 0, 0 );
5345 }
5346 return QgsSymbolLayerUtils::encodeColor( color );
5347}
5348
5349QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5350{
5351 QgsGradientColorRamp expRamp;
5352 const QgsColorRamp *ramp = nullptr;
5353 if ( values.at( 0 ).userType() == QMetaType::type( "QgsGradientColorRamp" ) )
5354 {
5355 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
5356 ramp = &expRamp;
5357 }
5358 else
5359 {
5360 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5361 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
5362 if ( ! ramp )
5363 {
5364 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
5365 return QVariant();
5366 }
5367 }
5368
5369 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5370 QColor color = ramp->color( value );
5371 return QgsSymbolLayerUtils::encodeColor( color );
5372}
5373
5374static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5375{
5376 // Hue ranges from 0 - 360
5377 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
5378 // Saturation ranges from 0 - 100
5379 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5380 // Lightness ranges from 0 - 100
5381 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5382
5383 QColor color = QColor::fromHslF( hue, saturation, lightness );
5384
5385 if ( ! color.isValid() )
5386 {
5387 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
5388 color = QColor( 0, 0, 0 );
5389 }
5390
5391 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
5392}
5393
5394static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5395{
5396 // Hue ranges from 0 - 360
5397 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
5398 // Saturation ranges from 0 - 100
5399 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5400 // Lightness ranges from 0 - 100
5401 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5402 // Alpha ranges from 0 - 255
5403 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
5404
5405 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
5406 if ( ! color.isValid() )
5407 {
5408 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
5409 color = QColor( 0, 0, 0 );
5410 }
5411 return QgsSymbolLayerUtils::encodeColor( color );
5412}
5413
5414static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5415{
5416 // Hue ranges from 0 - 360
5417 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
5418 // Saturation ranges from 0 - 100
5419 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5420 // Value ranges from 0 - 100
5421 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5422
5423 QColor color = QColor::fromHsvF( hue, saturation, value );
5424
5425 if ( ! color.isValid() )
5426 {
5427 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
5428 color = QColor( 0, 0, 0 );
5429 }
5430
5431 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
5432}
5433
5434static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5435{
5436 // Hue ranges from 0 - 360
5437 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
5438 // Saturation ranges from 0 - 100
5439 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5440 // Value ranges from 0 - 100
5441 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5442 // Alpha ranges from 0 - 255
5443 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
5444
5445 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
5446 if ( ! color.isValid() )
5447 {
5448 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
5449 color = QColor( 0, 0, 0 );
5450 }
5451 return QgsSymbolLayerUtils::encodeColor( color );
5452}
5453
5454static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5455{
5456 // Cyan ranges from 0 - 100
5457 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
5458 // Magenta ranges from 0 - 100
5459 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5460 // Yellow ranges from 0 - 100
5461 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5462 // Black ranges from 0 - 100
5463 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
5464
5465 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
5466
5467 if ( ! color.isValid() )
5468 {
5469 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
5470 color = QColor( 0, 0, 0 );
5471 }
5472
5473 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
5474}
5475
5476static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5477{
5478 // Cyan ranges from 0 - 100
5479 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
5480 // Magenta ranges from 0 - 100
5481 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5482 // Yellow ranges from 0 - 100
5483 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5484 // Black ranges from 0 - 100
5485 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
5486 // Alpha ranges from 0 - 255
5487 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
5488
5489 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
5490 if ( ! color.isValid() )
5491 {
5492 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
5493 color = QColor( 0, 0, 0 );
5494 }
5495 return QgsSymbolLayerUtils::encodeColor( color );
5496}
5497
5498static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5499{
5500 QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5501 if ( ! color.isValid() )
5502 {
5503 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
5504 return QVariant();
5505 }
5506
5507 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5508 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
5509 return color.red();
5510 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
5511 return color.green();
5512 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
5513 return color.blue();
5514 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
5515 return color.alpha();
5516 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
5517 return static_cast< double >( color.hsvHueF() * 360 );
5518 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
5519 return static_cast< double >( color.hsvSaturationF() * 100 );
5520 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
5521 return static_cast< double >( color.valueF() * 100 );
5522 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
5523 return static_cast< double >( color.hslHueF() * 360 );
5524 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
5525 return static_cast< double >( color.hslSaturationF() * 100 );
5526 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
5527 return static_cast< double >( color.lightnessF() * 100 );
5528 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
5529 return static_cast< double >( color.cyanF() * 100 );
5530 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
5531 return static_cast< double >( color.magentaF() * 100 );
5532 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
5533 return static_cast< double >( color.yellowF() * 100 );
5534 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
5535 return static_cast< double >( color.blackF() * 100 );
5536
5537 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
5538 return QVariant();
5539}
5540
5541static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5542{
5543 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
5544 if ( map.count() < 1 )
5545 {
5546 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
5547 return QVariant();
5548 }
5549
5550 QList< QColor > colors;
5552 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
5553 {
5554 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
5555 if ( !colors.last().isValid() )
5556 {
5557 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
5558 return QVariant();
5559 }
5560
5561 double step = it.key().toDouble();
5562 if ( it == map.constBegin() )
5563 {
5564 if ( step != 0.0 )
5565 stops << QgsGradientStop( step, colors.last() );
5566 }
5567 else if ( it == map.constEnd() )
5568 {
5569 if ( step != 1.0 )
5570 stops << QgsGradientStop( step, colors.last() );
5571 }
5572 else
5573 {
5574 stops << QgsGradientStop( step, colors.last() );
5575 }
5576 }
5577 bool discrete = values.at( 1 ).toBool();
5578
5579 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
5580}
5581
5582static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5583{
5584 QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5585 if ( ! color.isValid() )
5586 {
5587 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
5588 return QVariant();
5589 }
5590
5591 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5592 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5593 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
5594 color.setRed( value );
5595 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
5596 color.setGreen( value );
5597 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
5598 color.setBlue( value );
5599 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
5600 color.setAlpha( value );
5601 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
5602 color.setHsv( value, color.hsvSaturation(), color.value(), color.alpha() );
5603 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
5604 color.setHsvF( color.hsvHueF(), value / 100.0, color.valueF(), color.alphaF() );
5605 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
5606 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), value / 100.0, color.alphaF() );
5607 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
5608 color.setHsl( value, color.hslSaturation(), color.lightness(), color.alpha() );
5609 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
5610 color.setHslF( color.hslHueF(), value / 100.0, color.lightnessF(), color.alphaF() );
5611 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
5612 color.setHslF( color.hslHueF(), color.hslSaturationF(), value / 100.0, color.alphaF() );
5613 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
5614 color.setCmykF( value / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
5615 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
5616 color.setCmykF( color.cyanF(), value / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
5617 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
5618 color.setCmykF( color.cyanF(), color.magentaF(), value / 100.0, color.blackF(), color.alphaF() );
5619 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
5620 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), value / 100.0, color.alphaF() );
5621 else
5622 {
5623 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
5624 return QVariant();
5625 }
5626 return QgsSymbolLayerUtils::encodeColor( color );
5627}
5628
5629static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5630{
5631 QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5632 if ( ! color.isValid() )
5633 {
5634 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
5635 return QVariant();
5636 }
5637
5638 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5639
5640 return QgsSymbolLayerUtils::encodeColor( color );
5641}
5642
5643static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5644{
5645 QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5646 if ( ! color.isValid() )
5647 {
5648 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
5649 return QVariant();
5650 }
5651
5652 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5653
5654 return QgsSymbolLayerUtils::encodeColor( color );
5655}
5656
5657static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5658{
5659 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
5660 QgsGeometry geom = feat.geometry();
5661 if ( !geom.isNull() )
5662 return QVariant::fromValue( geom );
5663 return QVariant();
5664}
5665
5666static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5667{
5668 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5669 QString sAuthId = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5670 QString dAuthId = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5671
5673 if ( ! s.isValid() )
5674 return QVariant::fromValue( fGeom );
5676 if ( ! d.isValid() )
5677 return QVariant::fromValue( fGeom );
5678
5680 if ( context )
5681 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
5682 QgsCoordinateTransform t( s, d, tContext );
5683 try
5684 {
5686 return QVariant::fromValue( fGeom );
5687 }
5688 catch ( QgsCsException &cse )
5689 {
5690 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
5691 return QVariant();
5692 }
5693 return QVariant();
5694}
5695
5696
5697static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5698{
5699 QVariant result;
5700 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
5701 if ( vl )
5702 {
5703 QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
5704
5706 req.setFilterFid( fid );
5707 req.setTimeout( 10000 );
5708 req.setRequestMayBeNested( true );
5709 if ( context )
5710 req.setFeedback( context->feedback() );
5711 QgsFeatureIterator fIt = vl->getFeatures( req );
5712
5713 QgsFeature fet;
5714 if ( fIt.nextFeature( fet ) )
5715 result = QVariant::fromValue( fet );
5716 }
5717
5718 return result;
5719}
5720
5721static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5722{
5723 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
5724
5725 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), parent );
5726
5727 //no layer found
5728 if ( !featureSource )
5729 {
5730 return QVariant();
5731 }
5733 QString cacheValueKey;
5734 if ( values.at( 1 ).type() == QVariant::Map )
5735 {
5736 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
5737
5738 QMap <QString, QVariant>::const_iterator i = attributeMap.constBegin();
5739 QString filterString;
5740 for ( ; i != attributeMap.constEnd(); ++i )
5741 {
5742 if ( !filterString.isEmpty() )
5743 {
5744 filterString.append( " AND " );
5745 }
5746 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
5747 }
5748 cacheValueKey = QStringLiteral( "getfeature:%1:%2" ).arg( featureSource->id(), filterString );
5749 if ( context && context->hasCachedValue( cacheValueKey ) )
5750 {
5751 return context->cachedValue( cacheValueKey );
5752 }
5753 req.setFilterExpression( filterString );
5754 }
5755 else
5756 {
5757 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5758 int attributeId = featureSource->fields().lookupField( attribute );
5759 if ( attributeId == -1 )
5760 {
5761 return QVariant();
5762 }
5763
5764 const QVariant &attVal = values.at( 2 );
5765
5766 cacheValueKey = QStringLiteral( "getfeature:%1:%2:%3" ).arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
5767 if ( context && context->hasCachedValue( cacheValueKey ) )
5768 {
5769 return context->cachedValue( cacheValueKey );
5770 }
5771
5773 }
5774 req.setLimit( 1 );
5775 req.setTimeout( 10000 );
5776 req.setRequestMayBeNested( true );
5777 if ( context )
5778 req.setFeedback( context->feedback() );
5779 if ( !parent->needsGeometry() )
5780 {
5782 }
5783 QgsFeatureIterator fIt = featureSource->getFeatures( req );
5784
5785 QgsFeature fet;
5786 QVariant res;
5787 if ( fIt.nextFeature( fet ) )
5788 {
5789 res = QVariant::fromValue( fet );
5790 }
5791
5792 if ( context )
5793 context->setCachedValue( cacheValueKey, res );
5794 return res;
5795}
5796
5797static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
5798{
5799 QVariant result;
5800 QString fieldName;
5801
5802 if ( context )
5803 {
5804 if ( !values.isEmpty() )
5805 {
5806 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
5807 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
5808 fieldName = col->name();
5809 else if ( values.size() == 2 )
5810 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5811 }
5812
5813 QVariant value = values.at( 0 );
5814
5815 const QgsFields fields = context->fields();
5816 int fieldIndex = fields.lookupField( fieldName );
5817
5818 if ( fieldIndex == -1 )
5819 {
5820 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( QStringLiteral( "represent_value" ), fieldName ) );
5821 }
5822 else
5823 {
5824 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
5825
5826 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName, value.toString() );
5827 if ( context->hasCachedValue( cacheValueKey ) )
5828 {
5829 return context->cachedValue( cacheValueKey );
5830 }
5831
5832 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
5834
5835 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName );
5836
5837 QVariant cache;
5838 if ( !context->hasCachedValue( cacheKey ) )
5839 {
5840 cache = formatter->createCache( layer, fieldIndex, setup.config() );
5841 context->setCachedValue( cacheKey, cache );
5842 }
5843 else
5844 cache = context->cachedValue( cacheKey );
5845
5846 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
5847
5848 context->setCachedValue( cacheValueKey, result );
5849 }
5850 }
5851 else
5852 {
5853 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( QStringLiteral( "represent_value" ), fieldName ) );
5854 }
5855
5856 return result;
5857}
5858
5859static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5860{
5861 const QVariant data = values.at( 0 );
5862 const QMimeDatabase db;
5863 return db.mimeTypeForData( data.toByteArray() ).name();
5864}
5865
5866static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5867{
5868 QgsMapLayer *layer = QgsExpressionUtils::getMapLayer( values.at( 0 ), parent );
5869
5870 if ( !layer )
5871 return QVariant();
5872
5873 // here, we always prefer the layer metadata values over the older server-specific published values
5874 QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5875 if ( QString::compare( layerProperty, QStringLiteral( "name" ), Qt::CaseInsensitive ) == 0 )
5876 return layer->name();
5877 else if ( QString::compare( layerProperty, QStringLiteral( "id" ), Qt::CaseInsensitive ) == 0 )
5878 return layer->id();
5879 else if ( QString::compare( layerProperty, QStringLiteral( "title" ), Qt::CaseInsensitive ) == 0 )
5880 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->title();
5881 else if ( QString::compare( layerProperty, QStringLiteral( "abstract" ), Qt::CaseInsensitive ) == 0 )
5882 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->abstract();
5883 else if ( QString::compare( layerProperty, QStringLiteral( "keywords" ), Qt::CaseInsensitive ) == 0 )
5884 {
5885 QStringList keywords;
5886 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
5887 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
5888 {
5889 keywords.append( it.value() );
5890 }
5891 if ( !keywords.isEmpty() )
5892 return keywords;
5893 return layer->keywordList();
5894 }
5895 else if ( QString::compare( layerProperty, QStringLiteral( "data_url" ), Qt::CaseInsensitive ) == 0 )
5896 return layer->dataUrl();
5897 else if ( QString::compare( layerProperty, QStringLiteral( "attribution" ), Qt::CaseInsensitive ) == 0 )
5898 {
5899 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->attribution() );
5900 }
5901 else if ( QString::compare( layerProperty, QStringLiteral( "attribution_url" ), Qt::CaseInsensitive ) == 0 )
5902 return layer->attributionUrl();
5903 else if ( QString::compare( layerProperty, QStringLiteral( "source" ), Qt::CaseInsensitive ) == 0 )
5904 return layer->publicSource();
5905 else if ( QString::compare( layerProperty, QStringLiteral( "min_scale" ), Qt::CaseInsensitive ) == 0 )
5906 return layer->minimumScale();
5907 else if ( QString::compare( layerProperty, QStringLiteral( "max_scale" ), Qt::CaseInsensitive ) == 0 )
5908 return layer->maximumScale();
5909 else if ( QString::compare( layerProperty, QStringLiteral( "is_editable" ), Qt::CaseInsensitive ) == 0 )
5910 return layer->isEditable();
5911 else if ( QString::compare( layerProperty, QStringLiteral( "crs" ), Qt::CaseInsensitive ) == 0 )
5912 return layer->crs().authid();
5913 else if ( QString::compare( layerProperty, QStringLiteral( "crs_definition" ), Qt::CaseInsensitive ) == 0 )
5914 return layer->crs().toProj();
5915 else if ( QString::compare( layerProperty, QStringLiteral( "crs_description" ), Qt::CaseInsensitive ) == 0 )
5916 return layer->crs().description();
5917 else if ( QString::compare( layerProperty, QStringLiteral( "extent" ), Qt::CaseInsensitive ) == 0 )
5918 {
5919 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
5920 QVariant result = QVariant::fromValue( extentGeom );
5921 return result;
5922 }
5923 else if ( QString::compare( layerProperty, QStringLiteral( "distance_units" ), Qt::CaseInsensitive ) == 0 )
5924 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
5925 else if ( QString::compare( layerProperty, QStringLiteral( "path" ), Qt::CaseInsensitive ) == 0 )
5926 {
5927 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
5928 return decodedUri.value( QStringLiteral( "path" ) );
5929 }
5930 else if ( QString::compare( layerProperty, QStringLiteral( "type" ), Qt::CaseInsensitive ) == 0 )
5931 {
5932 switch ( layer->type() )
5933 {
5935 return QCoreApplication::translate( "expressions", "Vector" );
5937 return QCoreApplication::translate( "expressions", "Raster" );
5939 return QCoreApplication::translate( "expressions", "Mesh" );
5941 return QCoreApplication::translate( "expressions", "Vector Tile" );
5943 return QCoreApplication::translate( "expressions", "Plugin" );
5945 return QCoreApplication::translate( "expressions", "Annotation" );
5947 return QCoreApplication::translate( "expressions", "Point Cloud" );
5949 return QCoreApplication::translate( "expressions", "Group" );
5950 }
5951 }
5952 else
5953 {
5954 //vector layer methods
5955 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
5956 if ( vLayer )
5957 {
5958 if ( QString::compare( layerProperty, QStringLiteral( "storage_type" ), Qt::CaseInsensitive ) == 0 )
5959 return vLayer->storageType();
5960 else if ( QString::compare( layerProperty, QStringLiteral( "geometry_type" ), Qt::CaseInsensitive ) == 0 )
5962 else if ( QString::compare( layerProperty, QStringLiteral( "feature_count" ), Qt::CaseInsensitive ) == 0 )
5963 return QVariant::fromValue( vLayer->featureCount() );
5964 }
5965 }
5966
5967 return QVariant();
5968}
5969
5970static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5971{
5972 QgsMapLayer *layer = QgsExpressionUtils::getMapLayer( values.at( 0 ), parent );
5973 if ( !layer )
5974 {
5975 parent->setEvalErrorString( QObject::tr( "Cannot find layer %1" ).arg( values.at( 0 ).toString() ) );
5976 return QVariant();
5977 }
5978
5979 if ( !layer->dataProvider() )
5980 {
5981 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
5982 return QVariant();
5983 }
5984
5985 const QString uriPart = values.at( 1 ).toString();
5986
5987 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
5988
5989 if ( !uriPart.isNull() )
5990 {
5991 return decodedUri.value( values.at( 1 ).toString() );
5992 }
5993 else
5994 {
5995 return decodedUri;
5996 }
5997}
5998
5999static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6000{
6001 QString layerIdOrName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6002
6003 //try to find a matching layer by name
6004 QgsMapLayer *layer = QgsProject::instance()->mapLayer( layerIdOrName ); //search by id first
6005 if ( !layer )
6006 {
6007 QList<QgsMapLayer *> layersByName = QgsProject::instance()->mapLayersByName( layerIdOrName );
6008 if ( !layersByName.isEmpty() )
6009 {
6010 layer = layersByName.at( 0 );
6011 }
6012 }
6013
6014 if ( !layer )
6015 return QVariant();
6016
6017 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
6018 if ( !rl )
6019 return QVariant();
6020
6021 int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6022 if ( band < 1 || band > rl->bandCount() )
6023 {
6024 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer %2" ).arg( band ).arg( layerIdOrName ) );
6025 return QVariant();
6026 }
6027
6028 QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6029 int stat = 0;
6030
6031 if ( QString::compare( layerProperty, QStringLiteral( "avg" ), Qt::CaseInsensitive ) == 0 )
6033 else if ( QString::compare( layerProperty, QStringLiteral( "stdev" ), Qt::CaseInsensitive ) == 0 )
6035 else if ( QString::compare( layerProperty, QStringLiteral( "min" ), Qt::CaseInsensitive ) == 0 )
6037 else if ( QString::compare( layerProperty, QStringLiteral( "max" ), Qt::CaseInsensitive ) == 0 )
6039 else if ( QString::compare( layerProperty, QStringLiteral( "range" ), Qt::CaseInsensitive ) == 0 )
6041 else if ( QString::compare( layerProperty, QStringLiteral( "sum" ), Qt::CaseInsensitive ) == 0 )
6043 else
6044 {
6045 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
6046 return QVariant();
6047 }
6048
6049 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
6050 switch ( stat )
6051 {
6053 return stats.mean;
6055 return stats.stdDev;
6057 return stats.minimumValue;
6059 return stats.maximumValue;
6061 return stats.range;
6063 return stats.sum;
6064 }
6065 return QVariant();
6066}
6067
6068static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6069{
6070 return values;
6071}
6072
6073static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6074{
6075 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6076 bool ascending = values.value( 1 ).toBool();
6077 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
6078 return list;
6079}
6080
6081static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6082{
6083 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
6084}
6085
6086static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6087{
6088 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
6089}
6090
6091static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6092{
6093 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
6094}
6095
6096static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6097{
6098 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6099 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
6100 int match = 0;
6101 for ( const auto &item : listB )
6102 {
6103 if ( listA.contains( item ) )
6104 match++;
6105 }
6106
6107 return QVariant( match == listB.count() );
6108}
6109
6110static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6111{
6112 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
6113}
6114
6115static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6116{
6117 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6118 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6119 if ( pos < list.length() && pos >= 0 ) return list.at( pos );
6120 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
6121 return list.at( list.length() + pos );
6122 return QVariant();
6123}
6124
6125static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6126{
6127 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6128 return list.value( 0 );
6129}
6130
6131static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6132{
6133 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6134 return list.value( list.size() - 1 );
6135}
6136
6137static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6138{
6139 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6140 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6141}
6142
6143static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6144{
6145 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6146 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6147}
6148
6149static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6150{
6151 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6152 int i = 0;
6153 double total = 0.0;
6154 for ( const QVariant &item : list )
6155 {
6156 switch ( item.userType() )
6157 {
6158 case QMetaType::Int:
6159 case QMetaType::UInt:
6160 case QMetaType::LongLong:
6161 case QMetaType::ULongLong:
6162 case QMetaType::Float:
6163 case QMetaType::Double:
6164 total += item.toDouble();
6165 ++i;
6166 break;
6167 }
6168 }
6169 return i == 0 ? QVariant() : total / i;
6170}
6171
6172static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6173{
6174 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6175 QVariantList numbers;
6176 for ( const auto &item : list )
6177 {
6178 switch ( item.userType() )
6179 {
6180 case QMetaType::Int:
6181 case QMetaType::UInt:
6182 case QMetaType::LongLong:
6183 case QMetaType::ULongLong:
6184 case QMetaType::Float:
6185 case QMetaType::Double:
6186 numbers.append( item );
6187 break;
6188 }
6189 }
6190 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6191 const int count = numbers.count();
6192 if ( count == 0 )
6193 {
6194 return QVariant();
6195 }
6196 else if ( count % 2 )
6197 {
6198 return numbers.at( count / 2 );
6199 }
6200 else
6201 {
6202 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
6203 }
6204}
6205
6206static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6207{
6208 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6209 int i = 0;
6210 double total = 0.0;
6211 for ( const QVariant &item : list )
6212 {
6213 switch ( item.userType() )
6214 {
6215 case QMetaType::Int:
6216 case QMetaType::UInt:
6217 case QMetaType::LongLong:
6218 case QMetaType::ULongLong:
6219 case QMetaType::Float:
6220 case QMetaType::Double:
6221 total += item.toDouble();
6222 ++i;
6223 break;
6224 }
6225 }
6226 return i == 0 ? QVariant() : total;
6227}
6228
6229static QVariant convertToSameType( const QVariant &value, QVariant::Type type )
6230{
6231 QVariant result = value;
6232 result.convert( static_cast<int>( type ) );
6233 return result;
6234}
6235
6236static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6237{
6238 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6239 QHash< QVariant, int > hash;
6240 for ( const auto &item : list )
6241 {
6242 ++hash[item];
6243 }
6244 const QList< int > occurrences = hash.values();
6245 if ( occurrences.empty() )
6246 return QVariantList();
6247
6248 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
6249
6250 const QString option = values.at( 1 ).toString();
6251 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
6252 {
6253 return convertToSameType( hash.keys( maxValue ), values.at( 0 ).type() );
6254 }
6255 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
6256 {
6257 if ( hash.isEmpty() )
6258 return QVariant();
6259
6260 return QVariant( hash.keys( maxValue ).first() );
6261 }
6262 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
6263 {
6264 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), values.at( 0 ).type() ), context, parent, node );
6265 }
6266 else if ( option.compare( QLatin1String( "real_majority" ), Qt::CaseInsensitive ) == 0 )
6267 {
6268 if ( maxValue * 2 <= list.size() )
6269 return QVariant();
6270
6271 return QVariant( hash.keys( maxValue ).first() );
6272 }
6273 else
6274 {
6275 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
6276 return QVariant();
6277 }
6278}
6279
6280static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6281{
6282 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6283 QHash< QVariant, int > hash;
6284 for ( const auto &item : list )
6285 {
6286 ++hash[item];
6287 }
6288 const QList< int > occurrences = hash.values();
6289 if ( occurrences.empty() )
6290 return QVariantList();
6291
6292 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
6293
6294 const QString option = values.at( 1 ).toString();
6295 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
6296 {
6297 return convertToSameType( hash.keys( minValue ), values.at( 0 ).type() );
6298 }
6299 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
6300 {
6301 if ( hash.isEmpty() )
6302 return QVariant();
6303
6304 return QVariant( hash.keys( minValue ).first() );
6305 }
6306 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
6307 {
6308 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), values.at( 0 ).type() ), context, parent, node );
6309 }
6310 else if ( option.compare( QLatin1String( "real_minority" ), Qt::CaseInsensitive ) == 0 )
6311 {
6312 if ( hash.keys().isEmpty() )
6313 return QVariant();
6314
6315 // Remove the majority, all others are minority
6316 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
6317 if ( maxValue * 2 > list.size() )
6318 hash.remove( hash.key( maxValue ) );
6319
6320 return convertToSameType( hash.keys(), values.at( 0 ).type() );
6321 }
6322 else
6323 {
6324 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
6325 return QVariant();
6326 }
6327}
6328
6329static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6330{
6331 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6332 list.append( values.at( 1 ) );
6333 return convertToSameType( list, values.at( 0 ).type() );
6334}
6335
6336static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6337{
6338 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6339 list.prepend( values.at( 1 ) );
6340 return convertToSameType( list, values.at( 0 ).type() );
6341}
6342
6343static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6344{
6345 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6346 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
6347 return convertToSameType( list, values.at( 0 ).type() );
6348}
6349
6350static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6351{
6352 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6353 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6354 if ( position < 0 )
6355 position = position + list.length();
6356 if ( position >= 0 && position < list.length() )
6357 list.removeAt( position );
6358 return convertToSameType( list, values.at( 0 ).type() );
6359}
6360
6361static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6362{
6363 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
6364 return QVariant();
6365
6366 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6367
6368 const QVariant toRemove = values.at( 1 );
6369 if ( QgsVariantUtils::isNull( toRemove ) )
6370 {
6371 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant & element )
6372 {
6373 return QgsVariantUtils::isNull( element );
6374 } ), list.end() );
6375 }
6376 else
6377 {
6378 list.removeAll( toRemove );
6379 }
6380 return convertToSameType( list, values.at( 0 ).type() );
6381}
6382
6383static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6384{
6385 if ( values.count() == 2 && values.at( 1 ).type() == QVariant::Map )
6386 {
6387 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
6388
6389 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6390 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
6391 {
6392 int index = list.indexOf( it.key() );
6393 while ( index >= 0 )
6394 {
6395 list.replace( index, it.value() );
6396 index = list.indexOf( it.key() );
6397 }
6398 }
6399
6400 return convertToSameType( list, values.at( 0 ).type() );
6401 }
6402 else if ( values.count() == 3 )
6403 {
6404 QVariantList before;
6405 QVariantList after;
6406 bool isSingleReplacement = false;
6407
6408 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).type() != QVariant::StringList )
6409 {
6410 before = QVariantList() << values.at( 1 );
6411 }
6412 else
6413 {
6414 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
6415 }
6416
6417 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
6418 {
6419 after = QVariantList() << values.at( 2 );
6420 isSingleReplacement = true;
6421 }
6422 else
6423 {
6424 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
6425 }
6426
6427 if ( !isSingleReplacement && before.length() != after.length() )
6428 {
6429 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
6430 return QVariant();
6431 }
6432
6433 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6434 for ( int i = 0; i < before.length(); i++ )
6435 {
6436 int index = list.indexOf( before.at( i ) );
6437 while ( index >= 0 )
6438 {
6439 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
6440 index = list.indexOf( before.at( i ) );
6441 }
6442 }
6443
6444 return convertToSameType( list, values.at( 0 ).type() );
6445 }
6446 else
6447 {
6448 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
6449 return QVariant();
6450 }
6451}
6452
6453static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6454{
6455 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6456 QVariantList list_new;
6457
6458 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
6459 {
6460 while ( list.removeOne( cur ) )
6461 {
6462 list_new.append( cur );
6463 }
6464 }
6465
6466 list_new.append( list );
6467
6468 return convertToSameType( list_new, values.at( 0 ).type() );
6469}
6470
6471static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6472{
6473 QVariantList list;
6474 for ( const QVariant &cur : values )
6475 {
6476 list += QgsExpressionUtils::getListValue( cur, parent );
6477 }
6478 return convertToSameType( list, values.at( 0 ).type() );
6479}
6480
6481static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6482{
6483 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6484 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6485 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6486 int slice_length = 0;
6487 // negative positions means positions taken relative to the end of the array
6488 if ( start_pos < 0 )
6489 {
6490 start_pos = list.length() + start_pos;
6491 }
6492 if ( end_pos >= 0 )
6493 {
6494 slice_length = end_pos - start_pos + 1;
6495 }
6496 else
6497 {
6498 slice_length = list.length() + end_pos - start_pos + 1;
6499 }
6500 //avoid negative lengths in QList.mid function
6501 if ( slice_length < 0 )
6502 {
6503 slice_length = 0;
6504 }
6505 list = list.mid( start_pos, slice_length );
6506 return list;
6507}
6508
6509static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6510{
6511 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6512 std::reverse( list.begin(), list.end() );
6513 return list;
6514}
6515
6516static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6517{
6518 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6519 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
6520 for ( const QVariant &cur : array2 )
6521 {
6522 if ( array1.contains( cur ) )
6523 return QVariant( true );
6524 }
6525 return QVariant( false );
6526}
6527
6528static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6529{
6530 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6531
6532 QVariantList distinct;
6533
6534 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
6535 {
6536 if ( !distinct.contains( *it ) )
6537 {
6538 distinct += ( *it );
6539 }
6540 }
6541
6542 return distinct;
6543}
6544
6545static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6546{
6547 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6548 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6549 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6550
6551 QString str;
6552
6553 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
6554 {
6555 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
6556 if ( it != ( array.constEnd() - 1 ) )
6557 {
6558 str += delimiter;
6559 }
6560 }
6561
6562 return QVariant( str );
6563}
6564
6565static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6566{
6567 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6568 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6569 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6570
6571 QStringList list = str.split( delimiter );
6572 QVariantList array;
6573
6574 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
6575 {
6576 array += ( !( *it ).isEmpty() ) ? *it : empty;
6577 }
6578
6579 return array;
6580}
6581
6582static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6583{
6584 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6585 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
6586 if ( document.isNull() )
6587 return QVariant();
6588
6589 return document.toVariant();
6590}
6591
6592static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6593{
6594 Q_UNUSED( parent )
6595 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
6596 return QString( document.toJson( QJsonDocument::Compact ) );
6597}
6598
6599static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6600{
6601 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6602 if ( str.isEmpty() )
6603 return QVariantMap();
6604 str = str.trimmed();
6605
6606 return QgsHstoreUtils::parse( str );
6607}
6608
6609static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6610{
6611 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6612 return QgsHstoreUtils::build( map );
6613}
6614
6615static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6616{
6617 QVariantMap result;
6618 for ( int i = 0; i + 1 < values.length(); i += 2 )
6619 {
6620 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
6621 }
6622 return result;
6623}
6624
6625static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6626{
6627 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6628 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6629 QVariantMap resultMap;
6630
6631 for ( auto it = map.cbegin(); it != map.cend(); it++ )
6632 {
6633 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
6634 }
6635
6636 return resultMap;
6637}
6638
6639static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6640{
6641 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
6642}
6643
6644static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6645{
6646 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
6647}
6648
6649static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6650{
6651 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6652 map.remove( values.at( 1 ).toString() );
6653 return map;
6654}
6655
6656static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6657{
6658 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6659 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
6660 return map;
6661}
6662
6663static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6664{
6665 QVariantMap result;
6666 for ( const QVariant &cur : values )
6667 {
6668 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
6669 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
6670 result.insert( it.key(), it.value() );
6671 }
6672 return result;
6673}
6674
6675static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6676{
6677 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
6678}
6679
6680static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6681{
6682 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
6683}
6684
6685static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6686{
6687 const QString envVarName = values.at( 0 ).toString();
6688 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
6689 return QVariant();
6690
6691 return QProcessEnvironment::systemEnvironment().value( envVarName );
6692}
6693
6694static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6695{
6696 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6697 if ( parent->hasEvalError() )
6698 {
6699 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "base_file_name" ) ) );
6700 return QVariant();
6701 }
6702 return QFileInfo( file ).completeBaseName();
6703}
6704
6705static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6706{
6707 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6708 if ( parent->hasEvalError() )
6709 {
6710 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_suffix" ) ) );
6711 return QVariant();
6712 }
6713 return QFileInfo( file ).completeSuffix();
6714}
6715
6716static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6717{
6718 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6719 if ( parent->hasEvalError() )
6720 {
6721 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_exists" ) ) );
6722 return QVariant();
6723 }
6724 return QFileInfo::exists( file );
6725}
6726
6727static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6728{
6729 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6730 if ( parent->hasEvalError() )
6731 {
6732 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_name" ) ) );
6733 return QVariant();
6734 }
6735 return QFileInfo( file ).fileName();
6736}
6737
6738static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6739{
6740 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6741 if ( parent->hasEvalError() )
6742 {
6743 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_file" ) ) );
6744 return QVariant();
6745 }
6746 return QFileInfo( file ).isFile();
6747}
6748
6749static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6750{
6751 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6752 if ( parent->hasEvalError() )
6753 {
6754 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_directory" ) ) );
6755 return QVariant();
6756 }
6757 return QFileInfo( file ).isDir();
6758}
6759
6760static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6761{
6762 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6763 if ( parent->hasEvalError() )
6764 {
6765 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_path" ) ) );
6766 return QVariant();
6767 }
6768 return QDir::toNativeSeparators( QFileInfo( file ).path() );
6769}
6770
6771static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6772{
6773 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6774 if ( parent->hasEvalError() )
6775 {
6776 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_size" ) ) );
6777 return QVariant();
6778 }
6779 return QFileInfo( file ).size();
6780}
6781
6782static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
6783{
6784 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
6785}
6786
6787static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6788{
6789 QVariant hash;
6790 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6791 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
6792
6793 if ( method == QLatin1String( "md4" ) )
6794 {
6795 hash = fcnHash( str, QCryptographicHash::Md4 );
6796 }
6797 else if ( method == QLatin1String( "md5" ) )
6798 {
6799 hash = fcnHash( str, QCryptographicHash::Md5 );
6800 }
6801 else if ( method == QLatin1String( "sha1" ) )
6802 {
6803 hash = fcnHash( str, QCryptographicHash::Sha1 );
6804 }
6805 else if ( method == QLatin1String( "sha224" ) )
6806 {
6807 hash = fcnHash( str, QCryptographicHash::Sha224 );
6808 }
6809 else if ( method == QLatin1String( "sha256" ) )
6810 {
6811 hash = fcnHash( str, QCryptographicHash::Sha256 );
6812 }
6813 else if ( method == QLatin1String( "sha384" ) )
6814 {
6815 hash = fcnHash( str, QCryptographicHash::Sha384 );
6816 }
6817 else if ( method == QLatin1String( "sha512" ) )
6818 {
6819 hash = fcnHash( str, QCryptographicHash::Sha512 );
6820 }
6821 else if ( method == QLatin1String( "sha3_224" ) )
6822 {
6823 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
6824 }
6825 else if ( method == QLatin1String( "sha3_256" ) )
6826 {
6827 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
6828 }
6829 else if ( method == QLatin1String( "sha3_384" ) )
6830 {
6831 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
6832 }
6833 else if ( method == QLatin1String( "sha3_512" ) )
6834 {
6835 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
6836 }
6837 else if ( method == QLatin1String( "keccak_224" ) )
6838 {
6839 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
6840 }
6841 else if ( method == QLatin1String( "keccak_256" ) )
6842 {
6843 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
6844 }
6845 else if ( method == QLatin1String( "keccak_384" ) )
6846 {
6847 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
6848 }
6849 else if ( method == QLatin1String( "keccak_512" ) )
6850 {
6851 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
6852 }
6853 else
6854 {
6855 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
6856 }
6857 return hash;
6858}
6859
6860static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6861{
6862 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
6863}
6864
6865static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6866{
6867 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
6868}
6869
6870static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6871{
6872 const QByteArray input = values.at( 0 ).toByteArray();
6873 return QVariant( QString( input.toBase64() ) );
6874}
6875
6876static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6877{
6878 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6879 QUrlQuery query;
6880 for ( auto it = map.cbegin(); it != map.cend(); it++ )
6881 {
6882 query.addQueryItem( it.key(), it.value().toString() );
6883 }
6884 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
6885}
6886
6887static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6888{
6889 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6890 const QByteArray base64 = value.toLocal8Bit();
6891 const QByteArray decoded = QByteArray::fromBase64( base64 );
6892 return QVariant( decoded );
6893}
6894
6895typedef bool ( QgsGeometry::*RelationFunction )( const QgsGeometry &geometry ) const;
6896
6897static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const RelationFunction &relationFunction, bool invert = false, double bboxGrow = 0, bool isNearestFunc = false, bool isIntersectsFunc = false )
6898{
6899
6900 const QVariant sourceLayerRef = context->variable( QStringLiteral( "layer" ) ); //used to detect if sourceLayer and targetLayer are the same
6901 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, parent );
6902
6903 QgsFeatureRequest request;
6904 request.setTimeout( 10000 );
6905 request.setRequestMayBeNested( true );
6906 request.setFeedback( context->feedback() );
6907
6908 // First parameter is the overlay layer
6909 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6911
6912 const bool layerCanBeCached = node->isStatic( parent, context );
6913 QVariant targetLayerValue = node->eval( parent, context );
6915
6916 // Second parameter is the expression to evaluate (or null for testonly)
6917 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6919 QString subExpString = node->dump();
6920
6921 bool testOnly = ( subExpString == "NULL" );
6922 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, parent );
6923 if ( !targetLayer ) // No layer, no joy
6924 {
6925 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
6926 return QVariant();
6927 }
6928
6929 // Third parameter is the filtering expression
6930 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6932 QString filterString = node->dump();
6933 if ( filterString != "NULL" )
6934 {
6935 request.setFilterExpression( filterString ); //filter cached features
6936 }
6937
6938 // Fourth parameter is the limit
6939 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
6941 QVariant limitValue = node->eval( parent, context );
6943 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
6944
6945 // Fifth parameter (for nearest only) is the max distance
6946 double max_distance = 0;
6947 if ( isNearestFunc ) //maxdistance param handling
6948 {
6949 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
6951 QVariant distanceValue = node->eval( parent, context );
6953 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
6954 }
6955
6956 // Fifth or sixth (for nearest only) parameter is the cache toggle
6957 node = QgsExpressionUtils::getNode( values.at( isNearestFunc ? 5 : 4 ), parent );
6959 QVariant cacheValue = node->eval( parent, context );
6961 bool cacheEnabled = cacheValue.toBool();
6962
6963 // Sixth parameter (for intersects only) is the min overlap (area or length)
6964 // Seventh parameter (for intersects only) is the min inscribed circle radius
6965 // Eighth parameter (for intersects only) is the return_details
6966 // Ninth parameter (for intersects only) is the sort_by_intersection_size flag
6967 double minOverlap { -1 };
6968 double minInscribedCircleRadius { -1 };
6969 bool returnDetails = false; //#spellok
6970 bool sortByMeasure = false;
6971 bool sortAscending = false;
6972 bool requireMeasures = false;
6973 bool overlapOrRadiusFilter = false;
6974 if ( isIntersectsFunc )
6975 {
6976
6977 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
6979 const QVariant minOverlapValue = node->eval( parent, context );
6981 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
6982 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
6984 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
6986 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
6987 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
6988 // Return measures is only effective when an expression is set
6989 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
6990 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
6991 // Sort by measures is only effective when an expression is set
6992 const QString sorting { node->eval( parent, context ).toString().toLower() };
6993 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
6994 sortAscending = sorting.startsWith( "asc" );
6995 requireMeasures = sortByMeasure || returnDetails; //#spellok
6996 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
6997 }
6998
6999
7000 FEAT_FROM_CONTEXT( context, feat )
7001 const QgsGeometry geometry = feat.geometry();
7002
7003 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
7004 {
7005 QgsCoordinateTransformContext TransformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
7006 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
7007 }
7008
7009 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
7010
7011 QgsRectangle intDomain = geometry.boundingBox();
7012 if ( bboxGrow != 0 )
7013 {
7014 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
7015 }
7016
7017 const QString cacheBase { QStringLiteral( "%1:%2:%3" ).arg( targetLayer->id(), subExpString, filterString ) };
7018
7019 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
7020 // Otherwise, it can be toggled by the user
7021 QgsSpatialIndex spatialIndex;
7022 QgsVectorLayer *cachedTarget;
7023 QList<QgsFeature> features;
7024 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
7025 {
7026 // If the cache (local spatial index) is enabled, we materialize the whole
7027 // layer, then do the request on that layer instead.
7028 const QString cacheLayer { QStringLiteral( "ovrlaylyr:%1" ).arg( cacheBase ) };
7029 const QString cacheIndex { QStringLiteral( "ovrlayidx:%1" ).arg( cacheBase ) };
7030
7031 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
7032 {
7033 cachedTarget = targetLayer->materialize( request );
7034 if ( layerCanBeCached )
7035 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
7036 }
7037 else
7038 {
7039 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
7040 }
7041
7042 if ( !context->hasCachedValue( cacheIndex ) )
7043 {
7044 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
7045 if ( layerCanBeCached )
7046 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
7047 }
7048 else
7049 {
7050 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
7051 }
7052
7053 QList<QgsFeatureId> fidsList;
7054 if ( isNearestFunc )
7055 {
7056 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
7057 }
7058 else
7059 {
7060 fidsList = spatialIndex.intersects( intDomain );
7061 }
7062
7063 QListIterator<QgsFeatureId> i( fidsList );
7064 while ( i.hasNext() )
7065 {
7066 QgsFeatureId fId2 = i.next();
7067 if ( sameLayers && feat.id() == fId2 )
7068 continue;
7069 features.append( cachedTarget->getFeature( fId2 ) );
7070 }
7071
7072 }
7073 else
7074 {
7075 // If the cache (local spatial index) is not enabled, we directly
7076 // get the features from the target layer
7077 request.setFilterRect( intDomain );
7078 QgsFeatureIterator fit = targetLayer->getFeatures( request );
7079 QgsFeature feat2;
7080 while ( fit.nextFeature( feat2 ) )
7081 {
7082 if ( sameLayers && feat.id() == feat2.id() )
7083 continue;
7084 features.append( feat2 );
7085 }
7086 }
7087
7088 QgsExpression subExpression;
7089 QgsExpressionContext subContext;
7090 if ( !testOnly )
7091 {
7092 const QString expCacheKey { QStringLiteral( "exp:%1" ).arg( cacheBase ) };
7093 const QString ctxCacheKey { QStringLiteral( "ctx:%1" ).arg( cacheBase ) };
7094
7095 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
7096 {
7097 subExpression = QgsExpression( subExpString );
7099 subExpression.prepare( &subContext );
7100 }
7101 else
7102 {
7103 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
7104 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
7105 }
7106 }
7107
7108 // //////////////////////////////////////////////////////////////////
7109 // Helper functions for geometry tests
7110
7111 // Test function for linestring geometries, returns TRUE if test passes
7112 auto testLinestring = [ = ]( const QgsGeometry intersection, double & overlapValue ) -> bool
7113 {
7114 bool testResult { false };
7115 // For return measures:
7116 QVector<double> overlapValues;
7117 for ( auto it = intersection.const_parts_begin(); ! testResult && it != intersection.const_parts_end(); ++it )
7118 {
7119 const QgsCurve *geom = qgsgeometry_cast< const QgsCurve * >( *it );
7120 // Check min overlap for intersection (if set)
7121 if ( minOverlap != -1 || requireMeasures )
7122 {
7123 overlapValue = geom->length();
7124 overlapValues.append( overlapValue );
7125 if ( minOverlap != -1 )
7126 {
7127 if ( overlapValue >= minOverlap )
7128 {
7129 testResult = true;
7130 }
7131 else
7132 {
7133 continue;
7134 }
7135 }
7136 }
7137 }
7138
7139 if ( ! overlapValues.isEmpty() )
7140 {
7141 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
7142 }
7143
7144 return testResult;
7145 };
7146
7147 // Test function for polygon geometries, returns TRUE if test passes
7148 auto testPolygon = [ = ]( const QgsGeometry intersection, double & radiusValue, double & overlapValue ) -> bool
7149 {
7150 // overlap and inscribed circle tests must be checked both (if the values are != -1)
7151 bool testResult { false };
7152 // For return measures:
7153 QVector<double> overlapValues;
7154 QVector<double> radiusValues;
7155 for ( auto it = intersection.const_parts_begin(); ( ! testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
7156 {
7157 const QgsCurvePolygon *geom = qgsgeometry_cast< const QgsCurvePolygon * >( *it );
7158 // Check min overlap for intersection (if set)
7159 if ( minOverlap != -1 || requireMeasures )
7160 {
7161 overlapValue = geom->area();
7162 overlapValues.append( geom->area() );
7163 if ( minOverlap != - 1 )
7164 {
7165 if ( overlapValue >= minOverlap )
7166 {
7167 testResult = true;
7168 }
7169 else
7170 {
7171 continue;
7172 }
7173 }
7174 }
7175
7176 // Check min inscribed circle radius for intersection (if set)
7177 if ( minInscribedCircleRadius != -1 || requireMeasures )
7178 {
7179 const QgsRectangle bbox = geom->boundingBox();
7180 const double width = bbox.width();
7181 const double height = bbox.height();
7182 const double size = width > height ? width : height;
7183 const double tolerance = size / 100.0;
7184 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
7185 testResult = radiusValue >= minInscribedCircleRadius;
7186 radiusValues.append( radiusValues );
7187 }
7188 } // end for parts
7189
7190 // Get the max values
7191 if ( !radiusValues.isEmpty() )
7192 {
7193 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
7194 }
7195
7196 if ( ! overlapValues.isEmpty() )
7197 {
7198 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
7199 }
7200
7201 return testResult;
7202
7203 };
7204
7205
7206 bool found = false;
7207 int foundCount = 0;
7208 QVariantList results;
7209
7210 QListIterator<QgsFeature> i( features );
7211 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
7212 {
7213
7214 QgsFeature feat2 = i.next();
7215
7216
7217 if ( ! relationFunction || ( geometry.*relationFunction )( feat2.geometry() ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
7218 {
7219
7220 double overlapValue = -1;
7221 double radiusValue = -1;
7222
7223 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
7224 {
7225 const QgsGeometry intersection { geometry.intersection( feat2.geometry() ) };
7226
7227 // Depending on the intersection geometry type and on the geometry type of
7228 // the tested geometry we can run different tests and collect different measures
7229 // that can be used for sorting (if required).
7230 switch ( intersection.type() )
7231 {
7232
7234 {
7235
7236 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
7237 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
7238
7239 if ( ! testResult && overlapOrRadiusFilter )
7240 {
7241 continue;
7242 }
7243
7244 break;
7245 }
7246
7248 {
7249
7250 // If the intersection is a linestring and a minimum circle is required
7251 // we can discard this result immediately.
7252 if ( minInscribedCircleRadius != -1 )
7253 {
7254 continue;
7255 }
7256
7257 // Otherwise a test for the overlap value is performed.
7258 const bool testResult { testLinestring( intersection, overlapValue ) };
7259
7260 if ( ! testResult && overlapOrRadiusFilter )
7261 {
7262 continue;
7263 }
7264
7265 break;
7266 }
7267
7269 {
7270
7271 // If the intersection is a point and a minimum circle is required
7272 // we can discard this result immediately.
7273 if ( minInscribedCircleRadius != -1 )
7274 {
7275 continue;
7276 }
7277
7278 bool testResult { false };
7279 if ( minOverlap != -1 || requireMeasures )
7280 {
7281 // Initially set this to 0 because it's a point intersection...
7282 overlapValue = 0;
7283 // ... but if the target geometry is not a point and the source
7284 // geometry is a point, we must record the length or the area
7285 // of the intersected geometry and use that as a measure for
7286 // sorting or reporting.
7288 {
7289 switch ( feat2.geometry().type() )
7290 {
7294 {
7295 break;
7296 }
7298 {
7299 testResult = testLinestring( feat2.geometry(), overlapValue );
7300 break;
7301 }
7303 {
7304 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
7305 break;
7306 }
7307 }
7308 }
7309
7310 if ( ! testResult && overlapOrRadiusFilter )
7311 {
7312 continue;
7313 }
7314
7315 }
7316 break;
7317 }
7318
7321 {
7322 continue;
7323 }
7324 }
7325 }
7326
7327 found = true;
7328 foundCount++;
7329
7330 // We just want a single boolean result if there is any intersect: finish and return true
7331 if ( testOnly )
7332 break;
7333
7334 if ( !invert )
7335 {
7336 // We want a list of attributes / geometries / other expression values, evaluate now
7337 subContext.setFeature( feat2 );
7338 const QVariant expResult = subExpression.evaluate( &subContext );
7339
7340 if ( requireMeasures )
7341 {
7342 QVariantMap resultRecord;
7343 resultRecord.insert( QStringLiteral( "id" ), feat2.id() );
7344 resultRecord.insert( QStringLiteral( "result" ), expResult );
7345 // Overlap is always added because return measures was set
7346 resultRecord.insert( QStringLiteral( "overlap" ), overlapValue );
7347 // Radius is only added when is different than -1 (because for linestrings is not set)
7348 if ( radiusValue != -1 )
7349 {
7350 resultRecord.insert( QStringLiteral( "radius" ), radiusValue );
7351 }
7352 results.append( resultRecord );
7353 }
7354 else
7355 {
7356 results.append( expResult );
7357 }
7358 }
7359 else
7360 {
7361 // If not, results is a list of found ids, which we'll inverse and evaluate below
7362 results.append( feat2.id() );
7363 }
7364 }
7365 }
7366
7367 if ( testOnly )
7368 {
7369 if ( invert )
7370 found = !found;//for disjoint condition
7371 return found;
7372 }
7373
7374 if ( !invert )
7375 {
7376 if ( requireMeasures )
7377 {
7378 if ( sortByMeasure )
7379 {
7380 std::sort( results.begin(), results.end(), [ sortAscending ]( const QVariant & recordA, const QVariant & recordB ) -> bool
7381 {
7382 return sortAscending ?
7383 recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble()
7384 : recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble();
7385 } );
7386 }
7387 // Resize
7388 if ( limit > 0 && results.size() > limit )
7389 {
7390 results.erase( results.begin() + limit );
7391 }
7392
7393 if ( ! returnDetails ) //#spellok
7394 {
7395 QVariantList expResults;
7396 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
7397 {
7398 expResults.append( it->toMap().value( QStringLiteral( "result" ) ) );
7399 }
7400 return expResults;
7401 }
7402 }
7403
7404 return results;
7405 }
7406
7407 // for disjoint condition returns the results for cached layers not intersected feats
7408 QVariantList disjoint_results;
7409 QgsFeature feat2;
7410 QgsFeatureRequest request2;
7411 request2.setLimit( limit );
7412 if ( context )
7413 request2.setFeedback( context->feedback() );
7414 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
7415 while ( fi.nextFeature( feat2 ) )
7416 {
7417 if ( !results.contains( feat2.id() ) )
7418 {
7419 subContext.setFeature( feat2 );
7420 disjoint_results.append( subExpression.evaluate( &subContext ) );
7421 }
7422 }
7423 return disjoint_results;
7424
7425}
7426
7427// Intersect functions:
7428
7429static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7430{
7431 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, false, 0, false, true );
7432}
7433
7434static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7435{
7436 return executeGeomOverlay( values, context, parent, &QgsGeometry::contains );
7437}
7438
7439static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7440{
7441 return executeGeomOverlay( values, context, parent, &QgsGeometry::crosses );
7442}
7443
7444static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7445{
7446 return executeGeomOverlay( values, context, parent, &QgsGeometry::equals, false, 0.01 ); //grow amount should adapt to current units
7447}
7448
7449static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7450{
7451 return executeGeomOverlay( values, context, parent, &QgsGeometry::touches, false, 0.01 ); //grow amount should adapt to current units
7452}
7453
7454static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7455{
7456 return executeGeomOverlay( values, context, parent, &QgsGeometry::within );
7457}
7458
7459static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7460{
7461 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, true, 0, false, true );
7462}
7463
7464static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7465{
7466 return executeGeomOverlay( values, context, parent, nullptr, false, 0, true );
7467}
7468
7469const QList<QgsExpressionFunction *> &QgsExpression::Functions()
7470{
7471 // The construction of the list isn't thread-safe, and without the mutex,
7472 // crashes in the WFS provider may occur, since it can parse expressions
7473 // in parallel.
7474 // The mutex needs to be recursive.
7475 static QRecursiveMutex sFunctionsMutex;
7476 QMutexLocker locker( &sFunctionsMutex );
7477
7478 QList<QgsExpressionFunction *> &functions = *sFunctions();
7479
7480 if ( functions.isEmpty() )
7481 {
7483 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
7484 << QgsExpressionFunction::Parameter( QStringLiteral( "group_by" ), true )
7485 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true );
7486
7487 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
7488 aggParamsConcat << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
7489 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
7490
7491 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
7492 aggParamsArray << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
7493
7494 functions
7495 << new QgsStaticExpressionFunction( QStringLiteral( "sqrt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnSqrt, QStringLiteral( "Math" ) )
7496 << new QgsStaticExpressionFunction( QStringLiteral( "radians" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "degrees" ) ), fcnRadians, QStringLiteral( "Math" ) )
7497 << new QgsStaticExpressionFunction( QStringLiteral( "degrees" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "radians" ) ), fcnDegrees, QStringLiteral( "Math" ) )
7498 << new QgsStaticExpressionFunction( QStringLiteral( "azimuth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnAzimuth, QStringLiteral( "GeometryGroup" ) )
7499 << new QgsStaticExpressionFunction( QStringLiteral( "inclination" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnInclination, QStringLiteral( "GeometryGroup" ) )
7500 << new QgsStaticExpressionFunction( QStringLiteral( "project" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "elevation" ), true, M_PI_2 ), fcnProject, QStringLiteral( "GeometryGroup" ) )
7501 << new QgsStaticExpressionFunction( QStringLiteral( "abs" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAbs, QStringLiteral( "Math" ) )
7502 << new QgsStaticExpressionFunction( QStringLiteral( "cos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnCos, QStringLiteral( "Math" ) )
7503 << new QgsStaticExpressionFunction( QStringLiteral( "sin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnSin, QStringLiteral( "Math" ) )
7504 << new QgsStaticExpressionFunction( QStringLiteral( "tan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnTan, QStringLiteral( "Math" ) )
7505 << new QgsStaticExpressionFunction( QStringLiteral( "asin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAsin, QStringLiteral( "Math" ) )
7506 << new QgsStaticExpressionFunction( QStringLiteral( "acos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAcos, QStringLiteral( "Math" ) )
7507 << new QgsStaticExpressionFunction( QStringLiteral( "atan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAtan, QStringLiteral( "Math" ) )
7508 << new QgsStaticExpressionFunction( QStringLiteral( "atan2" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ), fcnAtan2, QStringLiteral( "Math" ) )
7509 << new QgsStaticExpressionFunction( QStringLiteral( "exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnExp, QStringLiteral( "Math" ) )
7510 << new QgsStaticExpressionFunction( QStringLiteral( "ln" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLn, QStringLiteral( "Math" ) )
7511 << new QgsStaticExpressionFunction( QStringLiteral( "log10" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog10, QStringLiteral( "Math" ) )
7512 << new QgsStaticExpressionFunction( QStringLiteral( "log" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "base" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog, QStringLiteral( "Math" ) )
7513 << new QgsStaticExpressionFunction( QStringLiteral( "round" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ), fcnRound, QStringLiteral( "Math" ) );
7514
7515 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( QStringLiteral( "rand" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRnd, QStringLiteral( "Math" ) );
7516 randFunc->setIsStatic( false );
7517 functions << randFunc;
7518
7519 QgsStaticExpressionFunction *randfFunc = new QgsStaticExpressionFunction( QStringLiteral( "randf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ), true, 0.0 ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ), true, 1.0 ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRndF, QStringLiteral( "Math" ) );
7520 randfFunc->setIsStatic( false );
7521 functions << randfFunc;
7522
7523 functions
7524 << new QgsStaticExpressionFunction( QStringLiteral( "max" ), -1, fcnMax, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
7525 << new QgsStaticExpressionFunction( QStringLiteral( "min" ), -1, fcnMin, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
7526 << new QgsStaticExpressionFunction( QStringLiteral( "clamp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnClamp, QStringLiteral( "Math" ) )
7527 << new QgsStaticExpressionFunction( QStringLiteral( "scale_linear" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_max" ) ), fcnLinearScale, QStringLiteral( "Math" ) )
7528 << new QgsStaticExpressionFunction( QStringLiteral( "scale_exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "exponent" ) ), fcnExpScale, QStringLiteral( "Math" ) )
7529 << new QgsStaticExpressionFunction( QStringLiteral( "floor" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnFloor, QStringLiteral( "Math" ) )
7530 << new QgsStaticExpressionFunction( QStringLiteral( "ceil" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnCeil, QStringLiteral( "Math" ) )
7531 << new QgsStaticExpressionFunction( QStringLiteral( "pi" ), 0, fcnPi, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$pi" ) )
7532 << new QgsStaticExpressionFunction( QStringLiteral( "to_int" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInt, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toint" ) )
7533 << new QgsStaticExpressionFunction( QStringLiteral( "to_real" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToReal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toreal" ) )
7534 << new QgsStaticExpressionFunction( QStringLiteral( "to_string" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToString, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "tostring" ) )
7535 << new QgsStaticExpressionFunction( QStringLiteral( "to_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToDateTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todatetime" ) )
7536 << new QgsStaticExpressionFunction( QStringLiteral( "to_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToDate, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todate" ) )
7537 << new QgsStaticExpressionFunction( QStringLiteral( "to_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "totime" ) )
7538 << new QgsStaticExpressionFunction( QStringLiteral( "to_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInterval, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "tointerval" ) )
7539 << new QgsStaticExpressionFunction( QStringLiteral( "to_dm" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "axis" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "formatting" ), true ), fcnToDegreeMinute, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todm" ) )
7540 << new QgsStaticExpressionFunction( QStringLiteral( "to_dms" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "axis" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "formatting" ), true ), fcnToDegreeMinuteSecond, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todms" ) )
7541 << new QgsStaticExpressionFunction( QStringLiteral( "to_decimal" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToDecimal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todecimal" ) )
7542 << new QgsStaticExpressionFunction( QStringLiteral( "coalesce" ), -1, fcnCoalesce, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), false, QStringList(), true )
7543 << new QgsStaticExpressionFunction( QStringLiteral( "nullif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value2" ) ), fcnNullIf, QStringLiteral( "Conditionals" ) )
7544 << new QgsStaticExpressionFunction( QStringLiteral( "if" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "condition" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "result_when_true" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "result_when_false" ) ), fcnIf, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
7545 << new QgsStaticExpressionFunction( QStringLiteral( "try" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "alternative" ), true, QVariant() ), fcnTry, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
7546
7547 << new QgsStaticExpressionFunction( QStringLiteral( "aggregate" ),
7549 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
7550 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
7551 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
7552 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
7553 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
7554 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
7555 fcnAggregate,
7556 QStringLiteral( "Aggregates" ),
7557 QString(),
7558 []( const QgsExpressionNodeFunction * node )
7559 {
7560 // usesGeometry callback: return true if @parent variable is referenced
7561
7562 if ( !node )
7563 return true;
7564
7565 if ( !node->args() )
7566 return false;
7567
7568 QSet<QString> referencedVars;
7569 if ( node->args()->count() > 2 )
7570 {
7571 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
7572 referencedVars = subExpressionNode->referencedVariables();
7573 }
7574
7575 if ( node->args()->count() > 3 )
7576 {
7577 QgsExpressionNode *filterNode = node->args()->at( 3 );
7578 referencedVars.unite( filterNode->referencedVariables() );
7579 }
7580 return referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() );
7581 },
7582 []( const QgsExpressionNodeFunction * node )
7583 {
7584 // referencedColumns callback: return AllAttributes if @parent variable is referenced
7585
7586 if ( !node )
7587 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
7588
7589 if ( !node->args() )
7590 return QSet<QString>();
7591
7592 QSet<QString> referencedCols;
7593 QSet<QString> referencedVars;
7594
7595 if ( node->args()->count() > 2 )
7596 {
7597 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
7598 referencedVars = subExpressionNode->referencedVariables();
7599 referencedCols = subExpressionNode->referencedColumns();
7600 }
7601 if ( node->args()->count() > 3 )
7602 {
7603 QgsExpressionNode *filterNode = node->args()->at( 3 );
7604 referencedVars = filterNode->referencedVariables();
7605 referencedCols.unite( filterNode->referencedColumns() );
7606 }
7607
7608 if ( referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() ) )
7609 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
7610 else
7611 return referencedCols;
7612 },
7613 true
7614 )
7615
7616 << new QgsStaticExpressionFunction( QStringLiteral( "relation_aggregate" ), QgsExpressionFunction::ParameterList()
7617 << QgsExpressionFunction::Parameter( QStringLiteral( "relation" ) )
7618 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
7619 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
7620 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
7621 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
7622 fcnAggregateRelation, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true )
7623
7624 << new QgsStaticExpressionFunction( QStringLiteral( "count" ), aggParams, fcnAggregateCount, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7625 << new QgsStaticExpressionFunction( QStringLiteral( "count_distinct" ), aggParams, fcnAggregateCountDistinct, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7626 << new QgsStaticExpressionFunction( QStringLiteral( "count_missing" ), aggParams, fcnAggregateCountMissing, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7627 << new QgsStaticExpressionFunction( QStringLiteral( "minimum" ), aggParams, fcnAggregateMin, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7628 << new QgsStaticExpressionFunction( QStringLiteral( "maximum" ), aggParams, fcnAggregateMax, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7629 << new QgsStaticExpressionFunction( QStringLiteral( "sum" ), aggParams, fcnAggregateSum, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7630 << new QgsStaticExpressionFunction( QStringLiteral( "mean" ), aggParams, fcnAggregateMean, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7631 << new QgsStaticExpressionFunction( QStringLiteral( "median" ), aggParams, fcnAggregateMedian, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7632 << new QgsStaticExpressionFunction( QStringLiteral( "stdev" ), aggParams, fcnAggregateStdev, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7633 << new QgsStaticExpressionFunction( QStringLiteral( "range" ), aggParams, fcnAggregateRange, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7634 << new QgsStaticExpressionFunction( QStringLiteral( "minority" ), aggParams, fcnAggregateMinority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7635 << new QgsStaticExpressionFunction( QStringLiteral( "majority" ), aggParams, fcnAggregateMajority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7636 << new QgsStaticExpressionFunction( QStringLiteral( "q1" ), aggParams, fcnAggregateQ1, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7637 << new QgsStaticExpressionFunction( QStringLiteral( "q3" ), aggParams, fcnAggregateQ3, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7638 << new QgsStaticExpressionFunction( QStringLiteral( "iqr" ), aggParams, fcnAggregateIQR, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7639 << new QgsStaticExpressionFunction( QStringLiteral( "min_length" ), aggParams, fcnAggregateMinLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7640 << new QgsStaticExpressionFunction( QStringLiteral( "max_length" ), aggParams, fcnAggregateMaxLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7641 << new QgsStaticExpressionFunction( QStringLiteral( "collect" ), aggParams, fcnAggregateCollectGeometry, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7642 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate" ), aggParamsConcat, fcnAggregateStringConcat, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7643 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate_unique" ), aggParamsConcat, fcnAggregateStringConcatUnique, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7644 << new QgsStaticExpressionFunction( QStringLiteral( "array_agg" ), aggParamsArray, fcnAggregateArray, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7645
7646 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_match" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpMatch, QStringList() << QStringLiteral( "Conditionals" ) << QStringLiteral( "String" ) )
7647 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_matches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnRegexpMatches, QStringLiteral( "Arrays" ) )
7648
7649 << new QgsStaticExpressionFunction( QStringLiteral( "now" ), 0, fcnNow, QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$now" ) )
7650 << new QgsStaticExpressionFunction( QStringLiteral( "age" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime1" ) )
7651 << QgsExpressionFunction::Parameter( QStringLiteral( "datetime2" ) ),
7652 fcnAge, QStringLiteral( "Date and Time" ) )
7653 << new QgsStaticExpressionFunction( QStringLiteral( "year" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnYear, QStringLiteral( "Date and Time" ) )
7654 << new QgsStaticExpressionFunction( QStringLiteral( "month" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnMonth, QStringLiteral( "Date and Time" ) )
7655 << new QgsStaticExpressionFunction( QStringLiteral( "week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnWeek, QStringLiteral( "Date and Time" ) )
7656 << new QgsStaticExpressionFunction( QStringLiteral( "day" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDay, QStringLiteral( "Date and Time" ) )
7657 << new QgsStaticExpressionFunction( QStringLiteral( "hour" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnHour, QStringLiteral( "Date and Time" ) )
7658 << new QgsStaticExpressionFunction( QStringLiteral( "minute" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnMinute, QStringLiteral( "Date and Time" ) )
7659 << new QgsStaticExpressionFunction( QStringLiteral( "second" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnSeconds, QStringLiteral( "Date and Time" ) )
7660 << new QgsStaticExpressionFunction( QStringLiteral( "epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnEpoch, QStringLiteral( "Date and Time" ) )
7661 << new QgsStaticExpressionFunction( QStringLiteral( "datetime_from_epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "long" ) ), fcnDateTimeFromEpoch, QStringLiteral( "Date and Time" ) )
7662 << new QgsStaticExpressionFunction( QStringLiteral( "day_of_week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDayOfWeek, QStringLiteral( "Date and Time" ) )
7663 << new QgsStaticExpressionFunction( QStringLiteral( "make_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
7664 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
7665 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) ),
7666 fcnMakeDate, QStringLiteral( "Date and Time" ) )
7667 << new QgsStaticExpressionFunction( QStringLiteral( "make_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
7668 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
7669 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
7670 fcnMakeTime, QStringLiteral( "Date and Time" ) )
7671 << new QgsStaticExpressionFunction( QStringLiteral( "make_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
7672 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
7673 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) )
7674 << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
7675 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
7676 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
7677 fcnMakeDateTime, QStringLiteral( "Date and Time" ) )
7678 << new QgsStaticExpressionFunction( QStringLiteral( "make_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "years" ), true, 0 )
7679 << QgsExpressionFunction::Parameter( QStringLiteral( "months" ), true, 0 )
7680 << QgsExpressionFunction::Parameter( QStringLiteral( "weeks" ), true, 0 )
7681 << QgsExpressionFunction::Parameter( QStringLiteral( "days" ), true, 0 )
7682 << QgsExpressionFunction::Parameter( QStringLiteral( "hours" ), true, 0 )
7683 << QgsExpressionFunction::Parameter( QStringLiteral( "minutes" ), true, 0 )
7684 << QgsExpressionFunction::Parameter( QStringLiteral( "seconds" ), true, 0 ),
7685 fcnMakeInterval, QStringLiteral( "Date and Time" ) )
7686 << new QgsStaticExpressionFunction( QStringLiteral( "lower" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnLower, QStringLiteral( "String" ) )
7687 << new QgsStaticExpressionFunction( QStringLiteral( "upper" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnUpper, QStringLiteral( "String" ) )
7688 << new QgsStaticExpressionFunction( QStringLiteral( "title" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTitle, QStringLiteral( "String" ) )
7689 << new QgsStaticExpressionFunction( QStringLiteral( "trim" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTrim, QStringLiteral( "String" ) )
7690 << new QgsStaticExpressionFunction( QStringLiteral( "levenshtein" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLevenshtein, QStringLiteral( "Fuzzy Matching" ) )
7691 << new QgsStaticExpressionFunction( QStringLiteral( "longest_common_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLCS, QStringLiteral( "Fuzzy Matching" ) )
7692 << new QgsStaticExpressionFunction( QStringLiteral( "hamming_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnHamming, QStringLiteral( "Fuzzy Matching" ) )
7693 << new QgsStaticExpressionFunction( QStringLiteral( "soundex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnSoundex, QStringLiteral( "Fuzzy Matching" ) )
7694 << new QgsStaticExpressionFunction( QStringLiteral( "char" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "code" ) ), fcnChar, QStringLiteral( "String" ) )
7695 << new QgsStaticExpressionFunction( QStringLiteral( "ascii" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnAscii, QStringLiteral( "String" ) )
7696 << new QgsStaticExpressionFunction( QStringLiteral( "wordwrap" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "" ), fcnWordwrap, QStringLiteral( "String" ) )
7697 << new QgsStaticExpressionFunction( QStringLiteral( "length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ), true, "" ), fcnLength, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "GeometryGroup" ) )
7698 << new QgsStaticExpressionFunction( QStringLiteral( "length3D" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLength3D, QStringLiteral( "GeometryGroup" ) )
7699 << new QgsStaticExpressionFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) )
7700 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_replace" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) )
7701 << QgsExpressionFunction::Parameter( QStringLiteral( "replacement" ) ), fcnRegexpReplace, QStringLiteral( "String" ) )
7702 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpSubstr, QStringLiteral( "String" ) )
7703 << new QgsStaticExpressionFunction( QStringLiteral( "substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ), true ), fcnSubstr, QStringLiteral( "String" ), QString(),
7704 false, QSet< QString >(), false, QStringList(), true )
7705 << new QgsStaticExpressionFunction( QStringLiteral( "concat" ), -1, fcnConcat, QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList(), true )
7706 << new QgsStaticExpressionFunction( QStringLiteral( "strpos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "haystack" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "needle" ) ), fcnStrpos, QStringLiteral( "String" ) )
7707 << new QgsStaticExpressionFunction( QStringLiteral( "left" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnLeft, QStringLiteral( "String" ) )
7708 << new QgsStaticExpressionFunction( QStringLiteral( "right" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnRight, QStringLiteral( "String" ) )
7709 << new QgsStaticExpressionFunction( QStringLiteral( "rpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnRPad, QStringLiteral( "String" ) )
7710 << new QgsStaticExpressionFunction( QStringLiteral( "lpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnLPad, QStringLiteral( "String" ) )
7711 << new QgsStaticExpressionFunction( QStringLiteral( "format" ), -1, fcnFormatString, QStringLiteral( "String" ) )
7712 << new QgsStaticExpressionFunction( QStringLiteral( "format_number" ), QgsExpressionFunction::ParameterList()
7713 << QgsExpressionFunction::Parameter( QStringLiteral( "number" ) )
7714 << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 )
7715 << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() )
7716 << QgsExpressionFunction::Parameter( QStringLiteral( "omit_group_separators" ), true, false )
7717 << QgsExpressionFunction::Parameter( QStringLiteral( "trim_trailing_zeroes" ), true, false ), fcnFormatNumber, QStringLiteral( "String" ) )
7718 << new QgsStaticExpressionFunction( QStringLiteral( "format_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnFormatDate, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "Date and Time" ) )
7719 << new QgsStaticExpressionFunction( QStringLiteral( "color_grayscale_average" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ), fcnColorGrayscaleAverage, QStringLiteral( "Color" ) )
7720 << new QgsStaticExpressionFunction( QStringLiteral( "color_mix_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
7721 << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
7722 << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
7723 fcnColorMixRgb, QStringLiteral( "Color" ) )
7724 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
7725 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
7726 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) ),
7727 fcnColorRgb, QStringLiteral( "Color" ) )
7728 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgba" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
7729 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
7730 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
7731 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
7732 fncColorRgba, QStringLiteral( "Color" ) )
7733 << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
7734 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
7735 fcnRampColor, QStringLiteral( "Color" ) )
7736 << new QgsStaticExpressionFunction( QStringLiteral( "create_ramp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
7737 << QgsExpressionFunction::Parameter( QStringLiteral( "discrete" ), true, false ),
7738 fcnCreateRamp, QStringLiteral( "Color" ) )
7739 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
7740 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
7741 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) ),
7742 fcnColorHsl, QStringLiteral( "Color" ) )
7743 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsla" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
7744 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
7745 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
7746 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
7747 fncColorHsla, QStringLiteral( "Color" ) )
7748 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsv" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
7749 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
7750 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
7751 fcnColorHsv, QStringLiteral( "Color" ) )
7752 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsva" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
7753 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
7754 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
7755 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
7756 fncColorHsva, QStringLiteral( "Color" ) )
7757 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyk" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
7758 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
7759 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
7760 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) ),
7761 fcnColorCmyk, QStringLiteral( "Color" ) )
7762 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyka" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
7763 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
7764 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
7765 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
7766 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
7767 fncColorCmyka, QStringLiteral( "Color" ) )
7768 << new QgsStaticExpressionFunction( QStringLiteral( "color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
7769 << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ),
7770 fncColorPart, QStringLiteral( "Color" ) )
7771 << new QgsStaticExpressionFunction( QStringLiteral( "darker" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
7772 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
7773 fncDarker, QStringLiteral( "Color" ) )
7774 << new QgsStaticExpressionFunction( QStringLiteral( "lighter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
7775 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
7776 fncLighter, QStringLiteral( "Color" ) )
7777 << new QgsStaticExpressionFunction( QStringLiteral( "set_color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fncSetColorPart, QStringLiteral( "Color" ) )
7778
7779 // file info
7780 << new QgsStaticExpressionFunction( QStringLiteral( "base_file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7781 fcnBaseFileName, QStringLiteral( "Files and Paths" ) )
7782 << new QgsStaticExpressionFunction( QStringLiteral( "file_suffix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7783 fcnFileSuffix, QStringLiteral( "Files and Paths" ) )
7784 << new QgsStaticExpressionFunction( QStringLiteral( "file_exists" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7785 fcnFileExists, QStringLiteral( "Files and Paths" ) )
7786 << new QgsStaticExpressionFunction( QStringLiteral( "file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7787 fcnFileName, QStringLiteral( "Files and Paths" ) )
7788 << new QgsStaticExpressionFunction( QStringLiteral( "is_file" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7789 fcnPathIsFile, QStringLiteral( "Files and Paths" ) )
7790 << new QgsStaticExpressionFunction( QStringLiteral( "is_directory" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7791 fcnPathIsDir, QStringLiteral( "Files and Paths" ) )
7792 << new QgsStaticExpressionFunction( QStringLiteral( "file_path" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7793 fcnFilePath, QStringLiteral( "Files and Paths" ) )
7794 << new QgsStaticExpressionFunction( QStringLiteral( "file_size" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7795 fcnFileSize, QStringLiteral( "Files and Paths" ) )
7796
7797 << new QgsStaticExpressionFunction( QStringLiteral( "exif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tag" ), true ),
7798 fcnExif, QStringLiteral( "Files and Paths" ) )
7799 << new QgsStaticExpressionFunction( QStringLiteral( "exif_geotag" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7800 fcnExifGeoTag, QStringLiteral( "GeometryGroup" ) )
7801
7802 // hash
7803 << new QgsStaticExpressionFunction( QStringLiteral( "hash" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "method" ) ),
7804 fcnGenericHash, QStringLiteral( "Conversions" ) )
7805 << new QgsStaticExpressionFunction( QStringLiteral( "md5" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
7806 fcnHashMd5, QStringLiteral( "Conversions" ) )
7807 << new QgsStaticExpressionFunction( QStringLiteral( "sha256" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
7808 fcnHashSha256, QStringLiteral( "Conversions" ) )
7809
7810 //base64
7811 << new QgsStaticExpressionFunction( QStringLiteral( "to_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
7812 fcnToBase64, QStringLiteral( "Conversions" ) )
7813 << new QgsStaticExpressionFunction( QStringLiteral( "from_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
7814 fcnFromBase64, QStringLiteral( "Conversions" ) )
7815
7816 // deprecated stuff - hidden from users
7817 << new QgsStaticExpressionFunction( QStringLiteral( "$scale" ), QgsExpressionFunction::ParameterList(), fcnMapScale, QStringLiteral( "deprecated" ) );
7818
7819 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( QStringLiteral( "$geometry" ), 0, fcnGeometry, QStringLiteral( "GeometryGroup" ), QString(), true );
7820 geomFunc->setIsStatic( false );
7821 functions << geomFunc;
7822
7823 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( QStringLiteral( "$area" ), 0, fcnGeomArea, QStringLiteral( "GeometryGroup" ), QString(), true );
7824 areaFunc->setIsStatic( false );
7825 functions << areaFunc;
7826
7827 functions << new QgsStaticExpressionFunction( QStringLiteral( "area" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnArea, QStringLiteral( "GeometryGroup" ) );
7828
7829 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( QStringLiteral( "$length" ), 0, fcnGeomLength, QStringLiteral( "GeometryGroup" ), QString(), true );
7830 lengthFunc->setIsStatic( false );
7831 functions << lengthFunc;
7832
7833 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( QStringLiteral( "$perimeter" ), 0, fcnGeomPerimeter, QStringLiteral( "GeometryGroup" ), QString(), true );
7834 perimeterFunc->setIsStatic( false );
7835 functions << perimeterFunc;
7836
7837 functions << new QgsStaticExpressionFunction( QStringLiteral( "perimeter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPerimeter, QStringLiteral( "GeometryGroup" ) );
7838
7839 functions << new QgsStaticExpressionFunction( QStringLiteral( "roundness" ),
7841 fcnRoundness, QStringLiteral( "GeometryGroup" ) );
7842
7843 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x" ), 0, fcnX, QStringLiteral( "GeometryGroup" ), QString(), true );
7844 xFunc->setIsStatic( false );
7845 functions << xFunc;
7846
7847 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y" ), 0, fcnY, QStringLiteral( "GeometryGroup" ), QString(), true );
7848 yFunc->setIsStatic( false );
7849 functions << yFunc;
7850
7851 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( QStringLiteral( "$z" ), 0, fcnZ, QStringLiteral( "GeometryGroup" ), QString(), true );
7852 zFunc->setIsStatic( false );
7853 functions << zFunc;
7854
7855 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions
7856 {
7857 { QStringLiteral( "overlay_intersects" ), fcnGeomOverlayIntersects },
7858 { QStringLiteral( "overlay_contains" ), fcnGeomOverlayContains },
7859 { QStringLiteral( "overlay_crosses" ), fcnGeomOverlayCrosses },
7860 { QStringLiteral( "overlay_equals" ), fcnGeomOverlayEquals },
7861 { QStringLiteral( "overlay_touches" ), fcnGeomOverlayTouches },
7862 { QStringLiteral( "overlay_disjoint" ), fcnGeomOverlayDisjoint },
7863 { QStringLiteral( "overlay_within" ), fcnGeomOverlayWithin },
7864 };
7865 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
7866 while ( i.hasNext() )
7867 {
7868 i.next();
7870 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
7871 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
7872 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
7873 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( -1 ), true )
7874 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false )
7875 << QgsExpressionFunction::Parameter( QStringLiteral( "min_overlap" ), true, QVariant( -1 ), false )
7876 << QgsExpressionFunction::Parameter( QStringLiteral( "min_inscribed_circle_radius" ), true, QVariant( -1 ), false )
7877 << QgsExpressionFunction::Parameter( QStringLiteral( "return_details" ), true, false, false )
7878 << QgsExpressionFunction::Parameter( QStringLiteral( "sort_by_intersection_size" ), true, QString(), false ),
7879 i.value(), QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
7880
7881 // The current feature is accessed for the geometry, so this should not be cached
7882 fcnGeomOverlayFunc->setIsStatic( false );
7883 functions << fcnGeomOverlayFunc;
7884 }
7885
7886 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction( QStringLiteral( "overlay_nearest" ), QgsExpressionFunction::ParameterList()
7887 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
7888 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
7889 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
7890 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( 1 ), true )
7891 << QgsExpressionFunction::Parameter( QStringLiteral( "max_distance" ), true, 0 )
7892 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false ),
7893 fcnGeomOverlayNearest, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
7894 // The current feature is accessed for the geometry, so this should not be cached
7895 fcnGeomOverlayNearestFunc->setIsStatic( false );
7896 functions << fcnGeomOverlayNearestFunc;
7897
7898 functions
7899 << new QgsStaticExpressionFunction( QStringLiteral( "is_valid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomIsValid, QStringLiteral( "GeometryGroup" ) )
7900 << new QgsStaticExpressionFunction( QStringLiteral( "x" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomX, QStringLiteral( "GeometryGroup" ) )
7901 << new QgsStaticExpressionFunction( QStringLiteral( "y" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomY, QStringLiteral( "GeometryGroup" ) )
7902 << new QgsStaticExpressionFunction( QStringLiteral( "z" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomZ, QStringLiteral( "GeometryGroup" ) )
7903 << new QgsStaticExpressionFunction( QStringLiteral( "m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomM, QStringLiteral( "GeometryGroup" ) )
7904 << new QgsStaticExpressionFunction( QStringLiteral( "point_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ), fcnPointN, QStringLiteral( "GeometryGroup" ) )
7905 << new QgsStaticExpressionFunction( QStringLiteral( "start_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnStartPoint, QStringLiteral( "GeometryGroup" ) )
7906 << new QgsStaticExpressionFunction( QStringLiteral( "end_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnEndPoint, QStringLiteral( "GeometryGroup" ) )
7907 << new QgsStaticExpressionFunction( QStringLiteral( "nodes_to_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7908 << QgsExpressionFunction::Parameter( QStringLiteral( "ignore_closing_nodes" ), true, false ),
7909 fcnNodesToPoints, QStringLiteral( "GeometryGroup" ) )
7910 << new QgsStaticExpressionFunction( QStringLiteral( "segments_to_lines" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnSegmentsToLines, QStringLiteral( "GeometryGroup" ) )
7911 << new QgsStaticExpressionFunction( QStringLiteral( "collect_geometries" ), -1, fcnCollectGeometries, QStringLiteral( "GeometryGroup" ) )
7912 << new QgsStaticExpressionFunction( QStringLiteral( "make_point" ), -1, fcnMakePoint, QStringLiteral( "GeometryGroup" ) )
7913 << new QgsStaticExpressionFunction( QStringLiteral( "make_point_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
7914 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) )
7915 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ),
7916 fcnMakePointM, QStringLiteral( "GeometryGroup" ) )
7917 << new QgsStaticExpressionFunction( QStringLiteral( "make_line" ), -1, fcnMakeLine, QStringLiteral( "GeometryGroup" ) )
7918 << new QgsStaticExpressionFunction( QStringLiteral( "make_polygon" ), -1, fcnMakePolygon, QStringLiteral( "GeometryGroup" ) )
7919 << new QgsStaticExpressionFunction( QStringLiteral( "make_triangle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
7920 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
7921 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) ),
7922 fcnMakeTriangle, QStringLiteral( "GeometryGroup" ) )
7923 << new QgsStaticExpressionFunction( QStringLiteral( "make_circle" ), QgsExpressionFunction::ParameterList()
7924 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
7925 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
7926 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
7927 fcnMakeCircle, QStringLiteral( "GeometryGroup" ) )
7928 << new QgsStaticExpressionFunction( QStringLiteral( "make_ellipse" ), QgsExpressionFunction::ParameterList()
7929 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
7930 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_major_axis" ) )
7931 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_minor_axis" ) )
7932 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
7933 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
7934 fcnMakeEllipse, QStringLiteral( "GeometryGroup" ) )
7935 << new QgsStaticExpressionFunction( QStringLiteral( "make_regular_polygon" ), QgsExpressionFunction::ParameterList()
7936 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
7937 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
7938 << QgsExpressionFunction::Parameter( QStringLiteral( "number_sides" ) )
7939 << QgsExpressionFunction::Parameter( QStringLiteral( "circle" ), true, 0 ),
7940 fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) )
7941 << new QgsStaticExpressionFunction( QStringLiteral( "make_square" ), QgsExpressionFunction::ParameterList()
7942 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
7943 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) ),
7944 fcnMakeSquare, QStringLiteral( "GeometryGroup" ) )
7945 << new QgsStaticExpressionFunction( QStringLiteral( "make_rectangle_3points" ), QgsExpressionFunction::ParameterList()
7946 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
7947 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
7948 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) )
7949 << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, 0 ),
7950 fcnMakeRectangleFrom3Points, QStringLiteral( "GeometryGroup" ) )
7951 << new QgsStaticExpressionFunction( QStringLiteral( "make_valid" ), QgsExpressionFunction::ParameterList
7952 {
7953 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7954#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
7955 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "linework" ) ),
7956#else
7957 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "structure" ) ),
7958#endif
7959 QgsExpressionFunction::Parameter( QStringLiteral( "keep_collapsed" ), true, false )
7960 }, fcnGeomMakeValid, QStringLiteral( "GeometryGroup" ) );
7961 QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "i" ) ), fcnXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "xat" ) << QStringLiteral( "x_at" ) );
7962 xAtFunc->setIsStatic( false );
7963 functions << xAtFunc;
7964
7965 QgsStaticExpressionFunction *yAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "i" ) ), fcnYat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "yat" ) << QStringLiteral( "y_at" ) );
7966 yAtFunc->setIsStatic( false );
7967 functions << yAtFunc;
7968
7969 functions
7970 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_type" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeometryType, QStringLiteral( "GeometryGroup" ) )
7971 << new QgsStaticExpressionFunction( QStringLiteral( "x_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmin" ) )
7972 << new QgsStaticExpressionFunction( QStringLiteral( "x_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmax" ) )
7973 << new QgsStaticExpressionFunction( QStringLiteral( "y_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymin" ) )
7974 << new QgsStaticExpressionFunction( QStringLiteral( "y_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymax" ) )
7975 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ), fcnGeomFromWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromWKT" ) )
7976 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "binary" ) ), fcnGeomFromWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
7977 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_gml" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "gml" ) ), fcnGeomFromGML, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromGML" ) )
7978 << new QgsStaticExpressionFunction( QStringLiteral( "flip_coordinates" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnFlipCoordinates, QStringLiteral( "GeometryGroup" ) )
7979 << new QgsStaticExpressionFunction( QStringLiteral( "relate" ), -1, fcnRelate, QStringLiteral( "GeometryGroup" ) )
7980 << new QgsStaticExpressionFunction( QStringLiteral( "intersects_bbox" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ), fcnBbox, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "bbox" ) )
7981 << new QgsStaticExpressionFunction( QStringLiteral( "disjoint" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7982 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7983 fcnDisjoint, QStringLiteral( "GeometryGroup" ) )
7984 << new QgsStaticExpressionFunction( QStringLiteral( "intersects" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7985 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7986 fcnIntersects, QStringLiteral( "GeometryGroup" ) )
7987 << new QgsStaticExpressionFunction( QStringLiteral( "touches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7988 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7989 fcnTouches, QStringLiteral( "GeometryGroup" ) )
7990 << new QgsStaticExpressionFunction( QStringLiteral( "crosses" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7991 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7992 fcnCrosses, QStringLiteral( "GeometryGroup" ) )
7993 << new QgsStaticExpressionFunction( QStringLiteral( "contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7994 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7995 fcnContains, QStringLiteral( "GeometryGroup" ) )
7996 << new QgsStaticExpressionFunction( QStringLiteral( "overlaps" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7997 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7998 fcnOverlaps, QStringLiteral( "GeometryGroup" ) )
7999 << new QgsStaticExpressionFunction( QStringLiteral( "within" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8000 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8001 fcnWithin, QStringLiteral( "GeometryGroup" ) )
8002 << new QgsStaticExpressionFunction( QStringLiteral( "translate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8003 << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) )
8004 << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ),
8005 fcnTranslate, QStringLiteral( "GeometryGroup" ) )
8006 << new QgsStaticExpressionFunction( QStringLiteral( "rotate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8007 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation" ) )
8008 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true )
8009 << QgsExpressionFunction::Parameter( QStringLiteral( "per_part" ), true, false ),
8010 fcnRotate, QStringLiteral( "GeometryGroup" ) )
8011 << new QgsStaticExpressionFunction( QStringLiteral( "scale" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8012 << QgsExpressionFunction::Parameter( QStringLiteral( "x_scale" ) )
8013 << QgsExpressionFunction::Parameter( QStringLiteral( "y_scale" ) )
8014 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true ),
8015 fcnScale, QStringLiteral( "GeometryGroup" ) )
8016 << new QgsStaticExpressionFunction( QStringLiteral( "affine_transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8017 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_x" ) )
8018 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_y" ) )
8019 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation_z" ) )
8020 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_x" ) )
8021 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_y" ) )
8022 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_z" ), true, 0 )
8023 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_m" ), true, 0 )
8024 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_z" ), true, 1 )
8025 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_m" ), true, 1 ),
8026 fcnAffineTransform, QStringLiteral( "GeometryGroup" ) )
8027 << new QgsStaticExpressionFunction( QStringLiteral( "buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8028 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8029 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8 )
8030 << QgsExpressionFunction::Parameter( QStringLiteral( "cap" ), true, QStringLiteral( "round" ) )
8031 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QStringLiteral( "round" ) )
8032 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2 ),
8033 fcnBuffer, QStringLiteral( "GeometryGroup" ) )
8034 << new QgsStaticExpressionFunction( QStringLiteral( "force_rhr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8035 fcnForceRHR, QStringLiteral( "GeometryGroup" ) )
8036 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_cw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8037 fcnForcePolygonCW, QStringLiteral( "GeometryGroup" ) )
8038 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_ccw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8039 fcnForcePolygonCCW, QStringLiteral( "GeometryGroup" ) )
8040 << new QgsStaticExpressionFunction( QStringLiteral( "wedge_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8041 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
8042 << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) )
8043 << QgsExpressionFunction::Parameter( QStringLiteral( "outer_radius" ) )
8044 << QgsExpressionFunction::Parameter( QStringLiteral( "inner_radius" ), true, 0.0 ), fcnWedgeBuffer, QStringLiteral( "GeometryGroup" ) )
8045 << new QgsStaticExpressionFunction( QStringLiteral( "tapered_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8046 << QgsExpressionFunction::Parameter( QStringLiteral( "start_width" ) )
8047 << QgsExpressionFunction::Parameter( QStringLiteral( "end_width" ) )
8048 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
8049 , fcnTaperedBuffer, QStringLiteral( "GeometryGroup" ) )
8050 << new QgsStaticExpressionFunction( QStringLiteral( "buffer_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8051 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
8052 , fcnBufferByM, QStringLiteral( "GeometryGroup" ) )
8053 << new QgsStaticExpressionFunction( QStringLiteral( "offset_curve" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8054 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8055 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
8056 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
8057 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
8058 fcnOffsetCurve, QStringLiteral( "GeometryGroup" ) )
8059 << new QgsStaticExpressionFunction( QStringLiteral( "single_sided_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8060 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8061 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
8062 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
8063 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
8064 fcnSingleSidedBuffer, QStringLiteral( "GeometryGroup" ) )
8065 << new QgsStaticExpressionFunction( QStringLiteral( "extend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8066 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) )
8067 << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ),
8068 fcnExtend, QStringLiteral( "GeometryGroup" ) )
8069 << new QgsStaticExpressionFunction( QStringLiteral( "centroid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCentroid, QStringLiteral( "GeometryGroup" ) )
8070 << new QgsStaticExpressionFunction( QStringLiteral( "point_on_surface" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPointOnSurface, QStringLiteral( "GeometryGroup" ) )
8071 << new QgsStaticExpressionFunction( QStringLiteral( "pole_of_inaccessibility" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8072 << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnPoleOfInaccessibility, QStringLiteral( "GeometryGroup" ) )
8073 << new QgsStaticExpressionFunction( QStringLiteral( "reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnReverse, QStringLiteral( "GeometryGroup" ) )
8074 << new QgsStaticExpressionFunction( QStringLiteral( "exterior_ring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnExteriorRing, QStringLiteral( "GeometryGroup" ) )
8075 << new QgsStaticExpressionFunction( QStringLiteral( "interior_ring_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8076 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
8077 fcnInteriorRingN, QStringLiteral( "GeometryGroup" ) )
8078 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8079 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
8080 fcnGeometryN, QStringLiteral( "GeometryGroup" ) )
8081 << new QgsStaticExpressionFunction( QStringLiteral( "boundary" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundary, QStringLiteral( "GeometryGroup" ) )
8082 << new QgsStaticExpressionFunction( QStringLiteral( "line_merge" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLineMerge, QStringLiteral( "GeometryGroup" ) )
8083 << new QgsStaticExpressionFunction( QStringLiteral( "shared_paths" ), QgsExpressionFunction::ParameterList
8084 {
8085 QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ),
8086 QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
8087 }, fcnSharedPaths, QStringLiteral( "GeometryGroup" ) )
8088 << new QgsStaticExpressionFunction( QStringLiteral( "bounds" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBounds, QStringLiteral( "GeometryGroup" ) )
8089 << new QgsStaticExpressionFunction( QStringLiteral( "simplify" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplify, QStringLiteral( "GeometryGroup" ) )
8090 << new QgsStaticExpressionFunction( QStringLiteral( "simplify_vw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplifyVW, QStringLiteral( "GeometryGroup" ) )
8091 << new QgsStaticExpressionFunction( QStringLiteral( "smooth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "iterations" ), true, 1 )
8092 << QgsExpressionFunction::Parameter( QStringLiteral( "offset" ), true, 0.25 )
8093 << QgsExpressionFunction::Parameter( QStringLiteral( "min_length" ), true, -1 )
8094 << QgsExpressionFunction::Parameter( QStringLiteral( "max_angle" ), true, 180 ), fcnSmooth, QStringLiteral( "GeometryGroup" ) )
8095 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave" ),
8096 {
8097 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8098 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
8099 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
8100 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
8101 }, fcnTriangularWave, QStringLiteral( "GeometryGroup" ) )
8102 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave_randomized" ),
8103 {
8104 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8105 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
8106 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
8107 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
8108 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
8109 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
8110 }, fcnTriangularWaveRandomized, QStringLiteral( "GeometryGroup" ) )
8111 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave" ),
8112 {
8113 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8114 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
8115 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
8116 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
8117 }, fcnSquareWave, QStringLiteral( "GeometryGroup" ) )
8118 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave_randomized" ),
8119 {
8120 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8121 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
8122 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
8123 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
8124 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
8125 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
8126 }, fcnSquareWaveRandomized, QStringLiteral( "GeometryGroup" ) )
8127 << new QgsStaticExpressionFunction( QStringLiteral( "wave" ),
8128 {
8129 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8130 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
8131 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
8132 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
8133 }, fcnRoundWave, QStringLiteral( "GeometryGroup" ) )
8134 << new QgsStaticExpressionFunction( QStringLiteral( "wave_randomized" ),
8135 {
8136 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8137 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
8138 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
8139 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
8140 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
8141 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
8142 }, fcnRoundWaveRandomized, QStringLiteral( "GeometryGroup" ) )
8143 << new QgsStaticExpressionFunction( QStringLiteral( "apply_dash_pattern" ),
8144 {
8145 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8146 QgsExpressionFunction::Parameter( QStringLiteral( "pattern" ) ),
8147 QgsExpressionFunction::Parameter( QStringLiteral( "start_rule" ), true, QStringLiteral( "no_rule" ) ),
8148 QgsExpressionFunction::Parameter( QStringLiteral( "end_rule" ), true, QStringLiteral( "no_rule" ) ),
8149 QgsExpressionFunction::Parameter( QStringLiteral( "adjustment" ), true, QStringLiteral( "both" ) ),
8150 QgsExpressionFunction::Parameter( QStringLiteral( "pattern_offset" ), true, 0 ),
8151 }, fcnApplyDashPattern, QStringLiteral( "GeometryGroup" ) )
8152 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_count" ),
8153 {
8154 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8155 QgsExpressionFunction::Parameter( QStringLiteral( "vertices" ) )
8156 }, fcnDensifyByCount, QStringLiteral( "GeometryGroup" ) )
8157 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_distance" ),
8158 {
8159 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8160 QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8161 }, fcnDensifyByDistance, QStringLiteral( "GeometryGroup" ) )
8162 << new QgsStaticExpressionFunction( QStringLiteral( "num_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumPoints, QStringLiteral( "GeometryGroup" ) )
8163 << new QgsStaticExpressionFunction( QStringLiteral( "num_interior_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumInteriorRings, QStringLiteral( "GeometryGroup" ) )
8164 << new QgsStaticExpressionFunction( QStringLiteral( "num_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumRings, QStringLiteral( "GeometryGroup" ) )
8165 << new QgsStaticExpressionFunction( QStringLiteral( "num_geometries" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumGeometries, QStringLiteral( "GeometryGroup" ) )
8166 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_width" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsWidth, QStringLiteral( "GeometryGroup" ) )
8167 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_height" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsHeight, QStringLiteral( "GeometryGroup" ) )
8168 << new QgsStaticExpressionFunction( QStringLiteral( "is_closed" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsClosed, QStringLiteral( "GeometryGroup" ) )
8169 << new QgsStaticExpressionFunction( QStringLiteral( "close_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCloseLine, QStringLiteral( "GeometryGroup" ) )
8170 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmpty, QStringLiteral( "GeometryGroup" ) )
8171 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty_or_null" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmptyOrNull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8172 << new QgsStaticExpressionFunction( QStringLiteral( "convex_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnConvexHull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "convexHull" ) )
8173#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
8174 << new QgsStaticExpressionFunction( QStringLiteral( "concave_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8175 << QgsExpressionFunction::Parameter( QStringLiteral( "target_percent" ) )
8176 << QgsExpressionFunction::Parameter( QStringLiteral( "allow_holes" ), true, false ), fcnConcaveHull, QStringLiteral( "GeometryGroup" ) )
8177#endif
8178 << new QgsStaticExpressionFunction( QStringLiteral( "oriented_bbox" ), QgsExpressionFunction::ParameterList()
8179 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8180 fcnOrientedBBox, QStringLiteral( "GeometryGroup" ) )
8181 << new QgsStaticExpressionFunction( QStringLiteral( "main_angle" ), QgsExpressionFunction::ParameterList()
8182 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8183 fcnMainAngle, QStringLiteral( "GeometryGroup" ) )
8184 << new QgsStaticExpressionFunction( QStringLiteral( "minimal_circle" ), QgsExpressionFunction::ParameterList()
8185 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8186 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8187 fcnMinimalCircle, QStringLiteral( "GeometryGroup" ) )
8188 << new QgsStaticExpressionFunction( QStringLiteral( "difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8189 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8190 fcnDifference, QStringLiteral( "GeometryGroup" ) )
8191 << new QgsStaticExpressionFunction( QStringLiteral( "distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8192 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8193 fcnDistance, QStringLiteral( "GeometryGroup" ) )
8194 << new QgsStaticExpressionFunction( QStringLiteral( "hausdorff_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
8195 << QgsExpressionFunction::Parameter( QStringLiteral( "densify_fraction" ), true ),
8196 fcnHausdorffDistance, QStringLiteral( "GeometryGroup" ) )
8197 << new QgsStaticExpressionFunction( QStringLiteral( "intersection" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8198 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8199 fcnIntersection, QStringLiteral( "GeometryGroup" ) )
8200 << new QgsStaticExpressionFunction( QStringLiteral( "sym_difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8201 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8202 fcnSymDifference, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "symDifference" ) )
8203 << new QgsStaticExpressionFunction( QStringLiteral( "combine" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8204 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8205 fcnCombine, QStringLiteral( "GeometryGroup" ) )
8206 << new QgsStaticExpressionFunction( QStringLiteral( "union" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8207 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8208 fcnCombine, QStringLiteral( "GeometryGroup" ) )
8209 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8210 << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ), true, 8.0 ),
8211 fcnGeomToWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomToWKT" ) )
8212 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8213 fcnGeomToWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
8214 << new QgsStaticExpressionFunction( QStringLiteral( "geometry" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetGeometry, QStringLiteral( "GeometryGroup" ), QString(), true )
8215 << new QgsStaticExpressionFunction( QStringLiteral( "transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8216 << QgsExpressionFunction::Parameter( QStringLiteral( "source_auth_id" ) )
8217 << QgsExpressionFunction::Parameter( QStringLiteral( "dest_auth_id" ) ),
8218 fcnTransformGeometry, QStringLiteral( "GeometryGroup" ) )
8219 << new QgsStaticExpressionFunction( QStringLiteral( "extrude" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8220 << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
8221 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) ),
8222 fcnExtrude, QStringLiteral( "GeometryGroup" ), QString() )
8223 << new QgsStaticExpressionFunction( QStringLiteral( "is_multipart" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8224 fcnGeomIsMultipart, QStringLiteral( "GeometryGroup" ) )
8225 << new QgsStaticExpressionFunction( QStringLiteral( "z_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8226 fcnZMax, QStringLiteral( "GeometryGroup" ) )
8227 << new QgsStaticExpressionFunction( QStringLiteral( "z_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8228 fcnZMin, QStringLiteral( "GeometryGroup" ) )
8229 << new QgsStaticExpressionFunction( QStringLiteral( "m_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8230 fcnMMax, QStringLiteral( "GeometryGroup" ) )
8231 << new QgsStaticExpressionFunction( QStringLiteral( "m_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8232 fcnMMin, QStringLiteral( "GeometryGroup" ) )
8233 << new QgsStaticExpressionFunction( QStringLiteral( "sinuosity" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8234 fcnSinuosity, QStringLiteral( "GeometryGroup" ) )
8235 << new QgsStaticExpressionFunction( QStringLiteral( "straight_distance_2d" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8236 fcnStraightDistance2d, QStringLiteral( "GeometryGroup" ) );
8237
8238
8239 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction( QStringLiteral( "order_parts" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8240 << QgsExpressionFunction::Parameter( QStringLiteral( "orderby" ) )
8241 << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ),
8242 fcnOrderParts, QStringLiteral( "GeometryGroup" ), QString() );
8243
8244 orderPartsFunc->setIsStaticFunction(
8245 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8246 {
8247 const QList< QgsExpressionNode *> argList = node->args()->list();
8248 for ( QgsExpressionNode *argNode : argList )
8249 {
8250 if ( !argNode->isStatic( parent, context ) )
8251 return false;
8252 }
8253
8254 if ( node->args()->count() > 1 )
8255 {
8256 QgsExpressionNode *argNode = node->args()->at( 1 );
8257
8258 QString expString = argNode->eval( parent, context ).toString();
8259
8260 QgsExpression e( expString );
8261
8262 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
8263 return true;
8264 }
8265
8266 return true;
8267 } );
8268
8269 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8270 {
8271 if ( node->args()->count() > 1 )
8272 {
8273 QgsExpressionNode *argNode = node->args()->at( 1 );
8274 QString expression = argNode->eval( parent, context ).toString();
8276 e.prepare( context );
8277 context->setCachedValue( expression, QVariant::fromValue( e ) );
8278 }
8279 return true;
8280 }
8281 );
8282 functions << orderPartsFunc;
8283
8284 functions
8285 << new QgsStaticExpressionFunction( QStringLiteral( "closest_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8286 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8287 fcnClosestPoint, QStringLiteral( "GeometryGroup" ) )
8288 << new QgsStaticExpressionFunction( QStringLiteral( "shortest_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8289 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8290 fcnShortestLine, QStringLiteral( "GeometryGroup" ) )
8291 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8292 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolatePoint, QStringLiteral( "GeometryGroup" ) )
8293 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_angle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8294 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolateAngle, QStringLiteral( "GeometryGroup" ) )
8295 << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8296 << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnLineLocatePoint, QStringLiteral( "GeometryGroup" ) )
8297 << new QgsStaticExpressionFunction( QStringLiteral( "angle_at_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8298 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnAngleAtVertex, QStringLiteral( "GeometryGroup" ) )
8299 << new QgsStaticExpressionFunction( QStringLiteral( "distance_to_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8300 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnDistanceToVertex, QStringLiteral( "GeometryGroup" ) )
8301 << new QgsStaticExpressionFunction( QStringLiteral( "line_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8302 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ), fcnLineSubset, QStringLiteral( "GeometryGroup" ) );
8303
8304
8305 // **Record** functions
8306
8307 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( QStringLiteral( "$id" ), 0, fcnFeatureId, QStringLiteral( "Record and Attributes" ) );
8308 idFunc->setIsStatic( false );
8309 functions << idFunc;
8310
8311 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( QStringLiteral( "$currentfeature" ), 0, fcnFeature, QStringLiteral( "Record and Attributes" ) );
8312 currentFeatureFunc->setIsStatic( false );
8313 functions << currentFeatureFunc;
8314
8315 QgsStaticExpressionFunction *uuidFunc = new QgsStaticExpressionFunction( QStringLiteral( "uuid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QStringLiteral( "WithBraces" ) ), fcnUuid, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$uuid" ) );
8316 uuidFunc->setIsStatic( false );
8317 functions << uuidFunc;
8318
8319 functions
8320 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8321 << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) )
8322 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ), true ),
8323 fcnGetFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "QgsExpressionUtils::getFeature" ) )
8324 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature_by_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8325 << QgsExpressionFunction::Parameter( QStringLiteral( "feature_id" ) ),
8326 fcnGetFeatureById, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false );
8327
8328 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true ),
8329 fcnAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
8330 attributesFunc->setIsStatic( false );
8331 functions << attributesFunc;
8332 QgsStaticExpressionFunction *representAttributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_attributes" ), -1,
8333 fcnRepresentAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
8334 representAttributesFunc->setIsStatic( false );
8335 functions << representAttributesFunc;
8336
8338 QStringLiteral( "maptip" ),
8339 -1,
8340 fcnFeatureMaptip,
8341 QStringLiteral( "Record and Attributes" ),
8342 QString(),
8343 false,
8344 QSet<QString>()
8345 );
8346 maptipFunc->setIsStatic( false );
8347 functions << maptipFunc;
8348
8350 QStringLiteral( "display_expression" ),
8351 -1,
8352 fcnFeatureDisplayExpression,
8353 QStringLiteral( "Record and Attributes" ),
8354 QString(),
8355 false,
8356 QSet<QString>()
8357 );
8358 displayFunc->setIsStatic( false );
8359 functions << displayFunc;
8360
8362 QStringLiteral( "is_selected" ),
8363 -1,
8364 fcnIsSelected,
8365 QStringLiteral( "Record and Attributes" ),
8366 QString(),
8367 false,
8368 QSet<QString>()
8369 );
8370 isSelectedFunc->setIsStatic( false );
8371 functions << isSelectedFunc;
8372
8373 functions
8375 QStringLiteral( "num_selected" ),
8376 -1,
8377 fcnNumSelected,
8378 QStringLiteral( "Record and Attributes" ),
8379 QString(),
8380 false,
8381 QSet<QString>()
8382 );
8383
8384 functions
8386 QStringLiteral( "sqlite_fetch_and_increment" ),
8388 << QgsExpressionFunction::Parameter( QStringLiteral( "database" ) )
8389 << QgsExpressionFunction::Parameter( QStringLiteral( "table" ) )
8390 << QgsExpressionFunction::Parameter( QStringLiteral( "id_field" ) )
8391 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_attribute" ) )
8392 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_value" ) )
8393 << QgsExpressionFunction::Parameter( QStringLiteral( "default_values" ), true ),
8394 fcnSqliteFetchAndIncrement,
8395 QStringLiteral( "Record and Attributes" )
8396 );
8397
8398 // **Fields and Values** functions
8399 QgsStaticExpressionFunction *representValueFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "field_name" ), true ), fcnRepresentValue, QStringLiteral( "Record and Attributes" ) );
8400
8401 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8402 {
8403 Q_UNUSED( context )
8404 if ( node->args()->count() == 1 )
8405 {
8406 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
8407 if ( colRef )
8408 {
8409 return true;
8410 }
8411 else
8412 {
8413 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
8414 return false;
8415 }
8416 }
8417 else if ( node->args()->count() == 2 )
8418 {
8419 return true;
8420 }
8421 else
8422 {
8423 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
8424 return false;
8425 }
8426 }
8427 );
8428
8429 functions << representValueFunc;
8430
8431 // **General** functions
8432 functions
8433 << new QgsStaticExpressionFunction( QStringLiteral( "layer_property" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8434 << QgsExpressionFunction::Parameter( QStringLiteral( "property" ) ),
8435 fcnGetLayerProperty, QStringLiteral( "Map Layers" ) )
8436 << new QgsStaticExpressionFunction( QStringLiteral( "decode_uri" ),
8438 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8439 << QgsExpressionFunction::Parameter( QStringLiteral( "part" ), true ),
8440 fcnDecodeUri, QStringLiteral( "Map Layers" ) )
8441 << new QgsStaticExpressionFunction( QStringLiteral( "mime_type" ),
8443 << QgsExpressionFunction::Parameter( QStringLiteral( "binary_data" ) ),
8444 fcnMimeType, QStringLiteral( "General" ) )
8445 << new QgsStaticExpressionFunction( QStringLiteral( "raster_statistic" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8446 << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) )
8447 << QgsExpressionFunction::Parameter( QStringLiteral( "statistic" ) ), fcnGetRasterBandStat, QStringLiteral( "Rasters" ) );
8448
8449 // **var** function
8450 QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( QStringLiteral( "var" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnGetVariable, QStringLiteral( "General" ) );
8451 varFunction->setIsStaticFunction(
8452 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8453 {
8454 /* A variable node is static if it has a static name and the name can be found at prepare
8455 * time and is tagged with isStatic.
8456 * It is not static if a variable is set during iteration or not tagged isStatic.
8457 * (e.g. geom_part variable)
8458 */
8459 if ( node->args()->count() > 0 )
8460 {
8461 QgsExpressionNode *argNode = node->args()->at( 0 );
8462
8463 if ( !argNode->isStatic( parent, context ) )
8464 return false;
8465
8466 const QString varName = argNode->eval( parent, context ).toString();
8467 if ( varName == QLatin1String( "feature" ) || varName == QLatin1String( "id" ) || varName == QLatin1String( "geometry" ) )
8468 return false;
8469
8470 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
8471 return scope ? scope->isStatic( varName ) : false;
8472 }
8473 return false;
8474 }
8475 );
8476 varFunction->setUsesGeometryFunction(
8477 []( const QgsExpressionNodeFunction * node ) -> bool
8478 {
8479 if ( node && node->args()->count() > 0 )
8480 {
8481 QgsExpressionNode *argNode = node->args()->at( 0 );
8482 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
8483 {
8484 if ( literal->value() == QLatin1String( "geometry" ) || literal->value() == QLatin1String( "feature" ) )
8485 return true;
8486 }
8487 }
8488 return false;
8489 }
8490 );
8491
8492 functions
8493 << varFunction;
8494
8495 functions << new QgsStaticExpressionFunction( QStringLiteral( "eval_template" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "template" ) ), fcnEvalTemplate, QStringLiteral( "General" ), QString(), true );
8496
8497 QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( QStringLiteral( "eval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ), fcnEval, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
8498 evalFunc->setIsStaticFunction(
8499 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8500 {
8501 if ( node->args()->count() > 0 )
8502 {
8503 QgsExpressionNode *argNode = node->args()->at( 0 );
8504
8505 if ( argNode->isStatic( parent, context ) )
8506 {
8507 QString expString = argNode->eval( parent, context ).toString();
8508
8509 QgsExpression e( expString );
8510
8511 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
8512 return true;
8513 }
8514 }
8515
8516 return false;
8517 } );
8518
8519 functions << evalFunc;
8520
8521 QgsStaticExpressionFunction *attributeFunc = new QgsStaticExpressionFunction( QStringLiteral( "attribute" ), -1, fcnAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
8522 attributeFunc->setIsStaticFunction(
8523 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8524 {
8525 const QList< QgsExpressionNode *> argList = node->args()->list();
8526 for ( QgsExpressionNode *argNode : argList )
8527 {
8528 if ( !argNode->isStatic( parent, context ) )
8529 return false;
8530 }
8531
8532 if ( node->args()->count() == 1 )
8533 {
8534 // not static -- this is the variant which uses the current feature taken direct from the expression context
8535 return false;
8536 }
8537
8538 return true;
8539 } );
8540 functions << attributeFunc;
8541
8542 functions
8543 << new QgsStaticExpressionFunction( QStringLiteral( "env" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnEnvVar, QStringLiteral( "General" ), QString() )
8545 << new QgsStaticExpressionFunction( QStringLiteral( "raster_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterValue, QStringLiteral( "Rasters" ) )
8546
8547 // functions for arrays
8550 << new QgsStaticExpressionFunction( QStringLiteral( "array" ), -1, fcnArray, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8551 << new QgsStaticExpressionFunction( QStringLiteral( "array_sort" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ), fcnArraySort, QStringLiteral( "Arrays" ) )
8552 << new QgsStaticExpressionFunction( QStringLiteral( "array_length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLength, QStringLiteral( "Arrays" ) )
8553 << new QgsStaticExpressionFunction( QStringLiteral( "array_contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayContains, QStringLiteral( "Arrays" ) )
8554 << new QgsStaticExpressionFunction( QStringLiteral( "array_count" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayCount, QStringLiteral( "Arrays" ) )
8555 << new QgsStaticExpressionFunction( QStringLiteral( "array_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_b" ) ), fcnArrayAll, QStringLiteral( "Arrays" ) )
8556 << new QgsStaticExpressionFunction( QStringLiteral( "array_find" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayFind, QStringLiteral( "Arrays" ) )
8557 << new QgsStaticExpressionFunction( QStringLiteral( "array_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayGet, QStringLiteral( "Arrays" ) )
8558 << new QgsStaticExpressionFunction( QStringLiteral( "array_first" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayFirst, QStringLiteral( "Arrays" ) )
8559 << new QgsStaticExpressionFunction( QStringLiteral( "array_last" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLast, QStringLiteral( "Arrays" ) )
8560 << new QgsStaticExpressionFunction( QStringLiteral( "array_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMinimum, QStringLiteral( "Arrays" ) )
8561 << new QgsStaticExpressionFunction( QStringLiteral( "array_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMaximum, QStringLiteral( "Arrays" ) )
8562 << new QgsStaticExpressionFunction( QStringLiteral( "array_mean" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMean, QStringLiteral( "Arrays" ) )
8563 << new QgsStaticExpressionFunction( QStringLiteral( "array_median" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMedian, QStringLiteral( "Arrays" ) )
8564 << new QgsStaticExpressionFunction( QStringLiteral( "array_majority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMajority, QStringLiteral( "Arrays" ) )
8565 << new QgsStaticExpressionFunction( QStringLiteral( "array_minority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMinority, QStringLiteral( "Arrays" ) )
8566 << new QgsStaticExpressionFunction( QStringLiteral( "array_sum" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArraySum, QStringLiteral( "Arrays" ) )
8567 << new QgsStaticExpressionFunction( QStringLiteral( "array_append" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayAppend, QStringLiteral( "Arrays" ) )
8568 << new QgsStaticExpressionFunction( QStringLiteral( "array_prepend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayPrepend, QStringLiteral( "Arrays" ) )
8569 << new QgsStaticExpressionFunction( QStringLiteral( "array_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayInsert, QStringLiteral( "Arrays" ) )
8570 << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayRemoveAt, QStringLiteral( "Arrays" ) )
8571 << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayRemoveAll, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8572 << new QgsStaticExpressionFunction( QStringLiteral( "array_replace" ), -1, fcnArrayReplace, QStringLiteral( "Arrays" ) )
8573 << new QgsStaticExpressionFunction( QStringLiteral( "array_prioritize" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_prioritize" ) ), fcnArrayPrioritize, QStringLiteral( "Arrays" ) )
8574 << new QgsStaticExpressionFunction( QStringLiteral( "array_cat" ), -1, fcnArrayCat, QStringLiteral( "Arrays" ) )
8575 << new QgsStaticExpressionFunction( QStringLiteral( "array_slice" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start_pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_pos" ) ), fcnArraySlice, QStringLiteral( "Arrays" ) )
8576 << new QgsStaticExpressionFunction( QStringLiteral( "array_reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayReverse, QStringLiteral( "Arrays" ) )
8577 << new QgsStaticExpressionFunction( QStringLiteral( "array_intersect" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array2" ) ), fcnArrayIntersect, QStringLiteral( "Arrays" ) )
8578 << new QgsStaticExpressionFunction( QStringLiteral( "array_distinct" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayDistinct, QStringLiteral( "Arrays" ) )
8579 << new QgsStaticExpressionFunction( QStringLiteral( "array_to_string" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "," ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnArrayToString, QStringLiteral( "Arrays" ) )
8580 << new QgsStaticExpressionFunction( QStringLiteral( "string_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "," ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnStringToArray, QStringLiteral( "Arrays" ) )
8581 << new QgsStaticExpressionFunction( QStringLiteral( "generate_series" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "stop" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "step" ), true, 1.0 ), fcnGenerateSeries, QStringLiteral( "Arrays" ) )
8582 << new QgsStaticExpressionFunction( QStringLiteral( "geometries_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometries" ) ), fcnGeometryCollectionAsArray, QStringLiteral( "Arrays" ) )
8583
8584 //functions for maps
8585 << new QgsStaticExpressionFunction( QStringLiteral( "from_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLoadJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "json_to_map" ) )
8586 << new QgsStaticExpressionFunction( QStringLiteral( "to_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "json_string" ) ), fcnWriteJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "map_to_json" ) )
8587 << new QgsStaticExpressionFunction( QStringLiteral( "hstore_to_map" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnHstoreToMap, QStringLiteral( "Maps" ) )
8588 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_hstore" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapToHstore, QStringLiteral( "Maps" ) )
8589 << new QgsStaticExpressionFunction( QStringLiteral( "map" ), -1, fcnMap, QStringLiteral( "Maps" ) )
8590 << new QgsStaticExpressionFunction( QStringLiteral( "map_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapGet, QStringLiteral( "Maps" ) )
8591 << new QgsStaticExpressionFunction( QStringLiteral( "map_exist" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapExist, QStringLiteral( "Maps" ) )
8592 << new QgsStaticExpressionFunction( QStringLiteral( "map_delete" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapDelete, QStringLiteral( "Maps" ) )
8593 << new QgsStaticExpressionFunction( QStringLiteral( "map_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnMapInsert, QStringLiteral( "Maps" ) )
8594 << new QgsStaticExpressionFunction( QStringLiteral( "map_concat" ), -1, fcnMapConcat, QStringLiteral( "Maps" ) )
8595 << new QgsStaticExpressionFunction( QStringLiteral( "map_akeys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAKeys, QStringLiteral( "Maps" ) )
8596 << new QgsStaticExpressionFunction( QStringLiteral( "map_avals" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAVals, QStringLiteral( "Maps" ) )
8597 << new QgsStaticExpressionFunction( QStringLiteral( "map_prefix_keys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
8598 << QgsExpressionFunction::Parameter( QStringLiteral( "prefix" ) ),
8599 fcnMapPrefixKeys, QStringLiteral( "Maps" ) )
8600 << new QgsStaticExpressionFunction( QStringLiteral( "url_encode" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
8601 fcnToFormUrlEncode, QStringLiteral( "Maps" ) )
8602
8603 ;
8604
8606
8607 //QgsExpression has ownership of all built-in functions
8608 for ( QgsExpressionFunction *func : std::as_const( functions ) )
8609 {
8610 *sOwnedFunctions() << func;
8611 *sBuiltinFunctions() << func->name();
8612 sBuiltinFunctions()->append( func->aliases() );
8613 }
8614 }
8615 return functions;
8616}
8617
8618bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
8619{
8620 int fnIdx = functionIndex( function->name() );
8621 if ( fnIdx != -1 )
8622 {
8623 return false;
8624 }
8625 sFunctions()->append( function );
8626 if ( transferOwnership )
8627 sOwnedFunctions()->append( function );
8628 return true;
8629}
8630
8631bool QgsExpression::unregisterFunction( const QString &name )
8632{
8633 // You can never override the built in functions.
8634 if ( QgsExpression::BuiltinFunctions().contains( name ) )
8635 {
8636 return false;
8637 }
8638 int fnIdx = functionIndex( name );
8639 if ( fnIdx != -1 )
8640 {
8641 sFunctions()->removeAt( fnIdx );
8642 return true;
8643 }
8644 return false;
8645}
8646
8648{
8649 qDeleteAll( *sOwnedFunctions() );
8650 sOwnedFunctions()->clear();
8651}
8652
8654{
8655 if ( sBuiltinFunctions()->isEmpty() )
8656 {
8657 Functions(); // this method builds the gmBuiltinFunctions as well
8658 }
8659 return *sBuiltinFunctions();
8660}
8661
8662
8664 : QgsExpressionFunction( QStringLiteral( "array_foreach" ), QgsExpressionFunction::ParameterList() // skip-keyword-check
8665 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
8666 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
8667 QStringLiteral( "Arrays" ) )
8668{
8669
8670}
8671
8673{
8674 bool isStatic = false;
8675
8676 QgsExpressionNode::NodeList *args = node->args();
8677
8678 if ( args->count() < 2 )
8679 return false;
8680
8681 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
8682 {
8683 isStatic = true;
8684 }
8685 return isStatic;
8686}
8687
8689{
8690 Q_UNUSED( node )
8691 QVariantList result;
8692
8693 if ( args->count() < 2 )
8694 // error
8695 return result;
8696
8697 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
8698
8699 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
8700 std::unique_ptr< QgsExpressionContext > tempContext;
8701 if ( !subContext )
8702 {
8703 tempContext = std::make_unique< QgsExpressionContext >();
8704 subContext = tempContext.get();
8705 }
8706
8708 subContext->appendScope( subScope );
8709
8710 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
8711 {
8712 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), *it, true ) );
8713 result << args->at( 1 )->eval( parent, subContext );
8714 }
8715
8716 if ( context )
8717 delete subContext->popScope();
8718
8719 return result;
8720}
8721
8722QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
8723{
8724 // This is a dummy function, all the real handling is in run
8725 Q_UNUSED( values )
8726 Q_UNUSED( context )
8727 Q_UNUSED( parent )
8728 Q_UNUSED( node )
8729
8730 Q_ASSERT( false );
8731 return QVariant();
8732}
8733
8735{
8736 QgsExpressionNode::NodeList *args = node->args();
8737
8738 if ( args->count() < 2 )
8739 // error
8740 return false;
8741
8742 args->at( 0 )->prepare( parent, context );
8743
8744 QgsExpressionContext subContext;
8745 if ( context )
8746 subContext = *context;
8747
8749 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
8750 subContext.appendScope( subScope );
8751
8752 args->at( 1 )->prepare( parent, &subContext );
8753
8754 return true;
8755}
8756
8758 : QgsExpressionFunction( QStringLiteral( "array_filter" ), QgsExpressionFunction::ParameterList()
8759 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
8760 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
8761 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
8762 QStringLiteral( "Arrays" ) )
8763{
8764
8765}
8766
8768{
8769 bool isStatic = false;
8770
8771 QgsExpressionNode::NodeList *args = node->args();
8772
8773 if ( args->count() < 2 )
8774 return false;
8775
8776 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
8777 {
8778 isStatic = true;
8779 }
8780 return isStatic;
8781}
8782
8784{
8785 Q_UNUSED( node )
8786 QVariantList result;
8787
8788 if ( args->count() < 2 )
8789 // error
8790 return result;
8791
8792 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
8793
8794 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
8795 std::unique_ptr< QgsExpressionContext > tempContext;
8796 if ( !subContext )
8797 {
8798 tempContext = std::make_unique< QgsExpressionContext >();
8799 subContext = tempContext.get();
8800 }
8801
8803 subContext->appendScope( subScope );
8804
8805 int limit = 0;
8806 if ( args->count() >= 3 )
8807 {
8808 const QVariant limitVar = args->at( 2 )->eval( parent, context );
8809
8810 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
8811 {
8812 limit = limitVar.toInt();
8813 }
8814 else
8815 {
8816 return result;
8817 }
8818 }
8819
8820 for ( const QVariant &value : array )
8821 {
8822 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), value, true ) );
8823 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
8824 {
8825 result << value;
8826
8827 if ( limit > 0 && limit == result.size() )
8828 break;
8829 }
8830 }
8831
8832 if ( context )
8833 delete subContext->popScope();
8834
8835 return result;
8836}
8837
8838QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
8839{
8840 // This is a dummy function, all the real handling is in run
8841 Q_UNUSED( values )
8842 Q_UNUSED( context )
8843 Q_UNUSED( parent )
8844 Q_UNUSED( node )
8845
8846 Q_ASSERT( false );
8847 return QVariant();
8848}
8849
8851{
8852 QgsExpressionNode::NodeList *args = node->args();
8853
8854 if ( args->count() < 2 )
8855 // error
8856 return false;
8857
8858 args->at( 0 )->prepare( parent, context );
8859
8860 QgsExpressionContext subContext;
8861 if ( context )
8862 subContext = *context;
8863
8865 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
8866 subContext.appendScope( subScope );
8867
8868 args->at( 1 )->prepare( parent, &subContext );
8869
8870 return true;
8871}
8873 : QgsExpressionFunction( QStringLiteral( "with_variable" ), QgsExpressionFunction::ParameterList() <<
8874 QgsExpressionFunction::Parameter( QStringLiteral( "name" ) )
8875 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8876 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
8877 QStringLiteral( "General" ) )
8878{
8879
8880}
8881
8883{
8884 bool isStatic = false;
8885
8886 QgsExpressionNode::NodeList *args = node->args();
8887
8888 if ( args->count() < 3 )
8889 return false;
8890
8891 // We only need to check if the node evaluation is static, if both - name and value - are static.
8892 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
8893 {
8894 QVariant name = args->at( 0 )->eval( parent, context );
8895 QVariant value = args->at( 1 )->eval( parent, context );
8896
8897 // Temporarily append a new scope to provide the variable
8898 appendTemporaryVariable( context, name.toString(), value );
8899 if ( args->at( 2 )->isStatic( parent, context ) )
8900 isStatic = true;
8901 popTemporaryVariable( context );
8902 }
8903
8904 return isStatic;
8905}
8906
8908{
8909 Q_UNUSED( node )
8910 QVariant result;
8911
8912 if ( args->count() < 3 )
8913 // error
8914 return result;
8915
8916 QVariant name = args->at( 0 )->eval( parent, context );
8917 QVariant value = args->at( 1 )->eval( parent, context );
8918
8919 const QgsExpressionContext *updatedContext = context;
8920 std::unique_ptr< QgsExpressionContext > tempContext;
8921 if ( !updatedContext )
8922 {
8923 tempContext = std::make_unique< QgsExpressionContext >();
8924 updatedContext = tempContext.get();
8925 }
8926
8927 appendTemporaryVariable( updatedContext, name.toString(), value );
8928 result = args->at( 2 )->eval( parent, updatedContext );
8929
8930 if ( context )
8931 popTemporaryVariable( updatedContext );
8932
8933 return result;
8934}
8935
8936QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
8937{
8938 // This is a dummy function, all the real handling is in run
8939 Q_UNUSED( values )
8940 Q_UNUSED( context )
8941 Q_UNUSED( parent )
8942 Q_UNUSED( node )
8943
8944 Q_ASSERT( false );
8945 return QVariant();
8946}
8947
8949{
8950 QgsExpressionNode::NodeList *args = node->args();
8951
8952 if ( args->count() < 3 )
8953 // error
8954 return false;
8955
8956 QVariant name = args->at( 0 )->prepare( parent, context );
8957 QVariant value = args->at( 1 )->prepare( parent, context );
8958
8959 const QgsExpressionContext *updatedContext = context;
8960 std::unique_ptr< QgsExpressionContext > tempContext;
8961 if ( !updatedContext )
8962 {
8963 tempContext = std::make_unique< QgsExpressionContext >();
8964 updatedContext = tempContext.get();
8965 }
8966
8967 appendTemporaryVariable( updatedContext, name.toString(), value );
8968 args->at( 2 )->prepare( parent, updatedContext );
8969
8970 if ( context )
8971 popTemporaryVariable( updatedContext );
8972
8973 return true;
8974}
8975
8976void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
8977{
8978 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
8979 delete updatedContext->popScope();
8980}
8981
8982void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
8983{
8986
8987 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
8988 updatedContext->appendScope( scope );
8989}
@ Left
Buffer to left of line.
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:1716
@ ScaleDashOnly
Only dash lengths are adjusted.
@ ScaleBothDashAndGap
Both the dash and gap lengths are adjusted equally.
@ ScaleGapOnly
Only gap lengths are adjusted.
@ Success
Operation succeeded.
JoinStyle
Join styles for buffers.
Definition qgis.h:1033
@ Bevel
Use beveled joins.
@ Round
Use rounded joins.
@ Miter
Use mitered joins.
EndCapStyle
End cap styles for buffers.
Definition qgis.h:1020
@ Flat
Flat cap (in line with start/end of line)
@ Round
Round cap.
@ Square
Square cap (extends past start/end of line by buffer distance)
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:1701
@ HalfDash
Start or finish the pattern with a half length dash.
@ HalfGap
Start or finish the pattern with a half length gap.
@ FullGap
Start or finish the pattern with a full gap.
@ FullDash
Start or finish the pattern with a full dash.
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:1046
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
Abstract base class for all geometries.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
bool isMeasure() const
Returns true if the geometry contains m values.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
part_iterator parts_end()
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
virtual QgsCoordinateSequence coordinateSequence() const =0
Retrieves the sequence of geometries, rings and nodes.
virtual int partCount() const =0
Returns count of parts contained in the geometry.
part_iterator parts_begin()
Returns STL-style iterator pointing to the first part of the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
QMap< QString, QStringList > KeywordMap
Map of vocabulary string to keyword list.
QString abstract() const
Returns a free-form description of the resource.
QString title() const
Returns the human readable name of the resource, typically displayed in search results.
QgsAbstractMetadataBase::KeywordMap keywords() const
Returns the keywords map, which is a set of descriptive keywords associated with the resource.
Aggregate
Available aggregates to calculate.
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
@ StringMaximumLength
Maximum length of string (string fields only)
@ ThirdQuartile
Third quartile (numeric fields only)
@ Range
Range of values (max - min) (numeric and datetime fields only)
@ ArrayAggregate
Create an array of values.
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only)
@ FirstQuartile
First quartile (numeric fields only)
@ Median
Median of values (numeric fields only)
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
@ CountMissing
Number of missing (null) values.
@ StDevSample
Sample standard deviation of values (numeric fields only)
@ Majority
Majority of values.
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
@ Mean
Mean of values (numeric fields only)
@ StringMinimumLength
Minimum length of string (string fields only)
@ CountDistinct
Number of distinct values.
@ Minority
Minority of values.
static Aggregate stringToAggregate(const QString &string, bool *ok=nullptr)
Converts a string to a aggregate type.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
Handles the array_filter(array, expression) expression function.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
Handles the array loopingarray_Foreach(array, expression) expression function.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
Circle geometry type.
Definition qgscircle.h:44
Abstract base class for color ramps.
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
Format
Available formats for displaying coordinates.
@ FormatDegreesMinutes
Degrees and decimal minutes, eg 30degrees 45.55'.
@ FormatDegreesMinutesSeconds
Degrees, minutes and seconds, eg 30 degrees 45'30".
static QString formatY(double y, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a y coordinate value according to the specified parameters.
@ FlagDegreesUseStringSuffix
Include a direction suffix (eg 'N', 'E', 'S' or 'W'), otherwise a "-" prefix is used for west and sou...
@ FlagDegreesPadMinutesSeconds
Pad minute and second values with leading zeros, eg '05' instead of '5'.
static QString formatX(double x, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats an x coordinate value according to the specified parameters.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toProj() const
Returns a Proj string representation of this CRS.
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
bool isEmpty() const override
Returns true if the geometry is empty.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
double area() const override
Returns the planar, 2-dimensional area of the geometry.
double roundness() const
Returns the roundness of the curve polygon.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
double sinuosity() const
Returns the curve sinuosity, which is the ratio of the curve length() to curve straightDistance2d().
Definition qgscurve.cpp:277
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition qgscurve.cpp:175
virtual QgsCurve * curveSubstring(double startDistance, double endDistance) const =0
Returns a new curve representing a substring of this curve.
virtual bool isClosed() const
Returns true if the curve is closed.
Definition qgscurve.cpp:53
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
double straightDistance2d() const
Returns the straight distance of the curve, i.e.
Definition qgscurve.cpp:272
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
double convertAreaMeasurement(double area, QgsUnitTypes::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
double convertLengthMeasurement(double length, QgsUnitTypes::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
Holder for the widget type and its configuration for a field.
QVariantMap config() const
Ellipse geometry type.
Definition qgsellipse.h:40
QString what() const
Contains utilities for working with EXIF tags in images.
static QgsPoint getGeoTag(const QString &imagePath, bool &ok)
Returns the geotagged coordinate stored in the image at imagePath.
static QVariant readTag(const QString &imagePath, const QString &key)
Returns the value of of an exif tag key stored in the image at imagePath.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
bool isStatic(const QString &name) const
Tests whether the variable with the specified name is static and can be cached.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
static void registerContextFunctions()
Registers all known core functions provided by QgsExpressionContextScope objects.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
void setCachedValue(const QString &key, const QVariant &value) const
Sets a value to cache within the expression context.
QgsGeometry geometry() const
Convenience function for retrieving the geometry for the context, if set.
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
QgsExpressionContextScope * activeScopeForVariable(const QString &name)
Returns the currently active scope from the context for a specified variable name.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the expression to check if evaluation sh...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
bool hasGeometry() const
Returns true if the context has a geometry associated with it.
bool hasCachedValue(const QString &key) const
Returns true if the expression context contains a cached value with a matching key.
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
QVariant cachedValue(const QString &key) const
Returns the matching cached value, if set.
bool hasFeature() const
Returns true if the context has a feature associated with it.
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
Represents a single parameter passed to a function.
A abstract base class for defining QgsExpression functions.
QList< QgsExpressionFunction::Parameter > ParameterList
List of parameters, used for function definition.
bool operator==(const QgsExpressionFunction &other) const
virtual bool isDeprecated() const
Returns true if the function is deprecated and should not be presented as a valid option to users in ...
virtual bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
Will be called during prepare to determine if the function is static.
virtual QStringList aliases() const
Returns a list of possible aliases for the function.
bool lazyEval() const
true if this function should use lazy evaluation.
static bool allParamsStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context)
This will return true if all the params for the provided function node are static within the constrai...
QString name() const
The name of the function.
virtual QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
Evaluates the function, first evaluating all required arguments before passing them to the function's...
virtual QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)=0
Returns result of evaluating the function.
virtual QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const
Returns a set of field names which are required for this function.
virtual bool handlesNull() const
Returns true if the function handles NULL values in arguments by itself, and the default NULL value h...
virtual bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
This will be called during the prepare step() of an expression if it is not static.
virtual bool usesGeometry(const QgsExpressionNodeFunction *node) const
Does this function use a geometry object.
An expression node which takes it value from a feature's field.
QString name() const
The name of the column.
An expression node for expression functions.
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
An expression node for literal values.
A list of expression nodes.
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
QgsExpressionNode * at(int i)
Gets the node at position i in the list.
int count() const
Returns the number of nodes in the list.
Abstract base class for all nodes that can appear in an expression.
virtual QString dump() const =0
Dump this node into a serialized (part) of an expression.
QVariant eval(QgsExpression *parent, const QgsExpressionContext *context)
Evaluate this node with the given context and parent.
virtual bool isStatic(QgsExpression *parent, const QgsExpressionContext *context) const =0
Returns true if this node can be evaluated for a static value.
bool prepare(QgsExpression *parent, const QgsExpressionContext *context)
Prepare this node for evaluation.
virtual QSet< QString > referencedColumns() const =0
Abstract virtual method which returns a list of columns required to evaluate this node.
virtual QSet< QString > referencedVariables() const =0
Returns a set of all variables which are used in this expression.
A set of expression-related functions.
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()
QString expression() const
Returns the original, unmodified expression string.
static void cleanRegisteredFunctions()
Deletes all registered functions whose ownership have been transferred to the expression engine.
static bool registerFunction(QgsExpressionFunction *function, bool transferOwnership=false)
Registers a function to the expression engine.
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
static const QStringList & BuiltinFunctions()
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.
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...
QgsUnitTypes::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g.,...
static QString helpText(QString name)
Returns the help text for a specified function.
static bool unregisterFunction(const QString &name)
Unregisters a function from the expression engine.
QgsUnitTypes::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions)
const QgsExpressionNode * rootNode() const
Returns the root node of the expression.
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QVariant evaluate()
Evaluate the feature and return the result.
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
The OrderByClause class represents an order by clause for a QgsFeatureRequest.
Represents a list of OrderByClauses, with the most important first and the least important last.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsFeatureRequest & setRequestMayBeNested(bool requestMayBeNested)
In case this request may be run nested within another already running iteration on the same connectio...
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setTimeout(int timeout)
Sets the timeout (in milliseconds) for the maximum time we should wait during feature requests before...
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature's geometries.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
void setFeedback(QgsFeedback *feedback)
Attach a feedback object that can be queried regularly by the iterator to check if it should be cance...
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
QgsVectorLayer * materialize(const QgsFeatureRequest &request, QgsFeedback *feedback=nullptr)
Materializes a request (query) made against this feature source, by running it over the source and re...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:56
QgsFields fields
Definition qgsfeature.h:66
QgsFeatureId id
Definition qgsfeature.h:64
QgsGeometry geometry
Definition qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
bool isValid() const
Returns the validity of this feature.
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
A field formatter helps to handle and display values for a field.
virtual QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
QString name
Definition qgsfield.h:60
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:672
Container of fields for a vector layer.
Definition qgsfields.h:45
int count() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int partCount() const override
Returns count of parts contained in the geometry.
int numGeometries() const
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
static QVector< QgsLineString * > extractLineStrings(const QgsAbstractGeometry *geom)
Returns list of linestrings extracted from the passed geometry.
A geometry is the spatial representation of a feature.
double hausdorffDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Returns a copy of the geometry which has been densified by adding the specified number of extra nodes...
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
double lineLocatePoint(const QgsGeometry &point) const
Returns a distance representing the location along this linestring of the closest point on this lines...
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
double length() const
Returns the planar, 2-dimensional length of geometry.
QgsGeometry offsetCurve(double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit) const
Returns an offset line at a given distance and side from an input line.
QgsGeometry densifyByDistance(double distance) const
Densifies the geometry by adding regularly placed extra nodes inside each segment so that the maximum...
QgsGeometry poleOfInaccessibility(double precision, double *distanceToBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
QgsGeometry squareWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs square waves along the boundary of the geometry, with the specified wavelength and amplitu...
QgsGeometry triangularWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs triangular waves along the boundary of the geometry, with the specified wavelength and amp...
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
QgsGeometry pointOnSurface() const
Returns a point guaranteed to lie on the surface of a geometry.
bool touches(const QgsGeometry &geometry) const
Returns true if the geometry touches another geometry.
QgsGeometry applyDashPattern(const QVector< double > &pattern, Qgis::DashPatternLineEndingRule startRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternLineEndingRule endRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternSizeAdjustment adjustment=Qgis::DashPatternSizeAdjustment::ScaleBothDashAndGap, double patternOffset=0) const
Applies a dash pattern to a geometry, returning a MultiLineString geometry which is the input geometr...
QgsGeometry roundWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs rounded (sine-like) waves along the boundary of the geometry, with the specified wavelengt...
QgsGeometry nearestPoint(const QgsGeometry &other) const
Returns the nearest (closest) point on this geometry to another geometry.
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
QgsGeometry makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false) const
Attempts to make an invalid geometry valid without losing vertices.
QString lastError() const
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry combine(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsMultiPointXY asMultiPoint() const
Returns the contents of the geometry as a multi-point.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsGeometry roundWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized rounded (sine-like) waves along the boundary of the geometry,...
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
QgsGeometry singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle=Qgis::JoinStyle::Round, double miterLimit=2.0) const
Returns a single sided buffer for a (multi)line geometry.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
QgsGeometry forceRHR() const
Forces geometries to respect the Right-Hand-Rule, in which the area that is bounded by a polygon is t...
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
bool equals(const QgsGeometry &geometry) const
Test if this geometry is exactly equal to another geometry.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
QgsWkbTypes::GeometryType type
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a variable width buffer ("tapered buffer") for a (multi)curve geometry.
bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
QgsGeometry orientedMinimumBoundingBox(double &area, double &angle, double &width, double &height) const
Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) rotated r...
double area() const
Returns the planar, 2-dimensional area of the geometry.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry concaveHull(double targetPercent, bool allowHoles=false) const
Returns a possibly concave polygon that contains all the points in the geometry.
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry.
QgsGeometry sharedPaths(const QgsGeometry &other) const
Find paths shared between the two given lineal geometries (this and other).
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
QgsGeometry symDifference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
QgsGeometry minimalEnclosingCircle(QgsPointXY &center, double &radius, unsigned int segments=36) const
Returns the minimal enclosing circle for the geometry.
QgsGeometry mergeLines() const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
QgsGeometry buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
double distanceToVertex(int vertex) const
Returns the distance along this geometry from its first vertex to the specified vertex.
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
QgsGeometry forcePolygonClockwise() const
Forces geometries to respect the exterior ring is clockwise, interior rings are counter-clockwise con...
static QgsGeometry createWedgeBuffer(const QgsPoint &center, double azimuth, double angularWidth, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
QgsGeometry extendLine(double startDistance, double endDistance) const
Extends a (multi)line geometry by extrapolating out the start or end of the line by a specified dista...
QgsGeometry triangularWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized triangular waves along the boundary of the geometry, with the specified wavelen...
QgsGeometry squareWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized square waves along the boundary of the geometry, with the specified wavelength ...
QgsGeometry simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Qgis::GeometryOperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
double interpolateAngle(double distance) const
Returns the angle parallel to the linestring or polygon boundary at the specified distance along the ...
double angleAtVertex(int vertex) const
Returns the bisector angle for this geometry at the specified vertex.
QgsGeometry smooth(unsigned int iterations=1, double offset=0.25, double minimumDistance=-1.0, double maxAngle=180.0) const
Smooths a geometry by rounding off corners using the Chaikin algorithm.
QgsGeometry forcePolygonCounterClockwise() const
Forces geometries to respect the exterior ring is counter-clockwise, interior rings are clockwise con...
QString asWkt(int precision=17) const
Exports the geometry to WKT.
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
bool overlaps(const QgsGeometry &geometry) const
Returns true if the geometry overlaps another geometry.
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition qgsgeos.h:99
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr) const
Returns the maximum inscribed circle.
Definition qgsgeos.cpp:2471
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Represents a color stop within a QgsGradientColorRamp color ramp.
A representation of the interval between two datetime values.
Definition qgsinterval.h:42
bool isValid() const
Returns true if the interval is valid.
double days() const
Returns the interval duration in days.
double weeks() const
Returns the interval duration in weeks.
double months() const
Returns the interval duration in months (based on a 30 day month).
double seconds() const
Returns the interval duration in seconds.
double years() const
Returns the interval duration in years (based on an average year length)
double hours() const
Returns the interval duration in hours.
double minutes() const
Returns the interval duration in minutes.
QStringList rights() const
Returns a list of attribution or copyright strings associated with the resource.
Line string geometry type, with support for z-dimension and m-values.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Base class for all map layer types.
Definition qgsmaplayer.h:73
QString name
Definition qgsmaplayer.h:76
virtual QgsRectangle extent() const
Returns the extent of the layer.
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsMapLayerType type
Definition qgsmaplayer.h:80
QString publicSource() const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:79
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QgsLayerMetadata metadata
Definition qgsmaplayer.h:78
QString abstract() const
Returns the abstract of the layer used by QGIS Server in GetCapabilities request.
virtual bool isEditable() const
Returns true if the layer can be edited.
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
double minimumScale() const
Returns the minimum map scale (i.e.
virtual Q_INVOKABLE QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
double maximumScale() const
Returns the maximum map scale (i.e.
QString keywordList() const
Returns the keyword list of the layer used by QGIS Server in GetCapabilities request.
Implementation of GeometrySimplifier using the "MapToPixel" algorithm.
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
@ SimplifyGeometry
The geometries can be simplified using the current map2pixel context state.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Multi line string geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Multi point geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Custom exception class which is raised when an operation is not supported.
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
A class to represent a 2D point.
Definition qgspointxy.h:59
double y
Definition qgspointxy.h:63
double x
Definition qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double inclination(const QgsPoint &other) const
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition qgspoint.cpp:723
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
Definition qgspoint.cpp:424
double z
Definition qgspoint.h:54
double x
Definition qgspoint.h:52
double m
Definition qgspoint.h:55
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition qgspoint.cpp:735
double y
Definition qgspoint.h:53
QgsRelationManager * relationManager
Definition qgsproject.h:114
static QgsProject * instance()
Returns the QgsProject singleton instance.
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Quadrilateral geometry type.
static QgsQuadrilateral squareFromDiagonal(const QgsPoint &p1, const QgsPoint &p2)
Construct a QgsQuadrilateral as a square from a diagonal.
QgsPolygon * toPolygon(bool force2D=false) const
Returns the quadrilateral as a new polygon.
static QgsQuadrilateral rectangleFrom3Points(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode)
Construct a QgsQuadrilateral as a Rectangle from 3 points.
ConstructionOption
A quadrilateral can be constructed from 3 points where the second distance can be determined by the t...
@ Distance
Second distance is equal to the distance between 2nd and 3rd point.
@ Projected
Second distance is equal to the distance of the perpendicular projection of the 3rd point on the segm...
The RasterBandStats struct is a container for statistics about a single raster band.
double mean
The mean cell value for the band. NO_DATA values are excluded.
double stdDev
The standard deviation of the cell values.
double minimumValue
The minimum cell value in the raster band.
double sum
The sum of all cells in the band. NO_DATA values are excluded.
double maximumValue
The maximum cell value in the raster band.
double range
The range is the distance between min & max.
virtual double sample(const QgsPointXY &point, int band, bool *ok=nullptr, const QgsRectangle &boundingBox=QgsRectangle(), int width=0, int height=0, int dpi=96)
Samples a raster value from the specified band found at the point position.
virtual QgsRasterBandStats bandStatistics(int bandNo, int stats=QgsRasterBandStats::All, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, QgsRasterBlockFeedback *feedback=nullptr)
Returns the band statistics.
Represents a raster layer.
int bandCount() const
Returns the number of bands in this layer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
A rectangle specified with double values.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double width() const
Returns the width of the rectangle.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
QgsPointXY center() const
Returns the center point of the rectangle.
void grow(double delta)
Grows the rectangle in place by the specified amount.
double height() const
Returns the height of the rectangle.
Regular Polygon geometry type.
ConstructionOption
A regular polygon can be constructed inscribed in a circle or circumscribed about a circle.
@ CircumscribedCircle
Circumscribed about a circle (the radius is the distance from the center to the midpoints of the side...
@ InscribedCircle
Inscribed in a circle (the radius is the distance between the center and vertices)
QgsPolygon * toPolygon() const
Returns as a polygon.
QList< QgsRelation > relationsByName(const QString &name) const
Returns a list of relations with matching names.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:47
QgsVectorLayer * referencingLayer
Definition qgsrelation.h:46
QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
A spatial index for QgsFeature objects.
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
QList< QgsFeatureId > nearestNeighbor(const QgsPointXY &point, int neighbors=1, double maxDistance=0) const
Returns nearest neighbors to a point.
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
static QString quotedIdentifier(const QString &identifier)
Returns a properly quoted version of identifier.
static QString quotedValue(const QVariant &value)
Returns a properly quoted and escaped version of value for use in SQL strings.
c++ helper class for defining QgsExpression functions.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
void setIsStaticFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *) > &isStatic)
Set a function that will be called in the prepare step to determine if the function is static or not.
QStringList aliases() const override
Returns a list of possible aliases for the function.
void setPrepareFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *)> &prepareFunc)
Set a function that will be called in the prepare step to determine if the function is static or not.
void setUsesGeometryFunction(const std::function< bool(const QgsExpressionNodeFunction *node)> &usesGeometry)
Set a function that will be called when determining if the function requires feature geometry or not.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
void setIsStatic(bool isStatic)
Tag this function as either static or not static.
QgsStaticExpressionFunction(const QString &fnname, int params, FcnEval fcn, const QString &group, const QString &helpText=QString(), bool usesGeometry=false, const QSet< QString > &referencedColumns=QSet< QString >(), bool lazyEval=false, const QStringList &aliases=QStringList(), bool handlesNull=false)
Static function for evaluation against a QgsExpressionContext, using an unnamed list of parameter val...
QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const override
Returns a set of field names which are required for this function.
bool usesGeometry(const QgsExpressionNodeFunction *node) const override
Does this function use a geometry object.
Utility functions for working with strings.
static int hammingDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Hamming distance between two strings.
static QString soundex(const QString &string)
Returns the Soundex representation of a string.
static int levenshteinDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Levenshtein edit distance between two strings.
static QString longestCommonSubstring(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the longest common substring between two strings.
static QString wordWrap(const QString &string, int length, bool useMaxLineLength=true, const QString &customDelimiter=QString())
Automatically wraps a string by inserting new line characters at appropriate locations in the string.
const QgsColorRamp * colorRampRef(const QString &name) const
Returns a const pointer to a symbol (doesn't create new instance)
Definition qgsstyle.cpp:468
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition qgsstyle.cpp:145
QgsRectangle boundingBox() const override
Returns the minimal bounding box for the geometry.
Definition qgssurface.h:43
static QColor decodeColor(const QString &str)
static QString encodeColor(const QColor &color)
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.
This class allows including a set of layers in a database-side transaction, provided the layer data p...
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
Triangle geometry type.
Definition qgstriangle.h:34
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QString displayExpression
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
QgsEditorWidgetSetup editorWidgetSetup(int index) const
The editor widget setup defines which QgsFieldFormatter and editor widget will be used for the field ...
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
QVariant aggregate(QgsAggregateCalculator::Aggregate aggregate, const QString &fieldOrExpression, const QgsAggregateCalculator::AggregateParameters &parameters=QgsAggregateCalculator::AggregateParameters(), QgsExpressionContext *context=nullptr, bool *ok=nullptr, QgsFeatureIds *fids=nullptr, QgsFeedback *feedback=nullptr, QString *error=nullptr) const
Calculates an aggregated value from the layer's features.
Handles the with_variable(name, value, node) expression function.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
static QString geometryDisplayString(GeometryType type)
Returns a display string for a geometry type.
static Type flatType(Type type)
Returns the flat type for a WKB type.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
QString errorMessage() const
Returns the most recent error message encountered by the database.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
int exec(const QString &sql, QString &errorMessage) const
Executes the sql command in the database.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
@ VectorLayer
Vector layer.
@ RasterLayer
Raster layer.
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ PluginLayer
Plugin based layer.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
CORE_EXPORT QString build(const QVariantMap &map)
Build a hstore-formatted string from a QVariantMap.
CORE_EXPORT QVariantMap parse(const QString &string)
Returns a QVariantMap object containing the key and values from a hstore-formatted string.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
uint qHash(const QVariant &variant)
Hash for QVariant.
Definition qgis.cpp:217
#define str(x)
Definition qgis.cpp:37
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition qgis.cpp:119
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:2581
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:2527
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Q_DECLARE_METATYPE(QgsDatabaseQueryLogEntry)
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QList< QgsExpressionFunction * > ExpressionFunctionList
#define ENSURE_GEOM_TYPE(f, g, geomtype)
QVariant fcnRampColor(const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction *)
bool(QgsGeometry::* RelationFunction)(const QgsGeometry &geometry) const
#define ENSURE_NO_EVAL_ERROR
#define FEAT_FROM_CONTEXT(c, f)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition qgsgeometry.h:82
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
QLineF segment(int index, QRectF rect, double radius)
int precision
A bundle of parameters controlling aggregate calculation.
QString filter
Optional filter for calculating aggregate over a subset of features, or an empty string to use all fe...
QString delimiter
Delimiter to use for joining values with the StringConcatenate aggregate.
QgsFeatureRequest::OrderBy orderBy
Optional order by clauses.
Single variable definition for use within a QgsExpressionContextScope.
The Context struct stores the current layer and coordinate transform context.
Definition qgsogcutils.h:60
const QgsMapLayer * layer
Definition qgsogcutils.h:70
QgsCoordinateTransformContext transformContext
Definition qgsogcutils.h:71
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:31