QGIS API Documentation 3.40.0-Bratislava (b56115d8743)
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
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 "qgsvectorlayerutils.h"
52#include "qgsrasterbandstats.h"
53#include "qgscolorramp.h"
55#include "qgsfieldformatter.h"
57#include "qgsproviderregistry.h"
58#include "sqlite3.h"
59#include "qgstransaction.h"
60#include "qgsthreadingutils.h"
61#include "qgsapplication.h"
62#include "qgis.h"
64#include "qgsunittypes.h"
65#include "qgsspatialindex.h"
66#include "qgscolorrampimpl.h"
67
68#include <QMimeDatabase>
69#include <QProcessEnvironment>
70#include <QCryptographicHash>
71#include <QRegularExpression>
72#include <QUuid>
73#include <QUrlQuery>
74
75typedef QList<QgsExpressionFunction *> ExpressionFunctionList;
76
78Q_GLOBAL_STATIC( QStringList, sBuiltinFunctions )
80
83Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
84
85const QString QgsExpressionFunction::helpText() const
86{
87 return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
88}
89
91{
92 Q_UNUSED( node )
93 // evaluate arguments
94 QVariantList argValues;
95 if ( args )
96 {
97 int arg = 0;
98 const QList< QgsExpressionNode * > argList = args->list();
99 for ( QgsExpressionNode *n : argList )
100 {
101 QVariant v;
102 if ( lazyEval() )
103 {
104 // Pass in the node for the function to eval as it needs.
105 v = QVariant::fromValue( n );
106 }
107 else
108 {
109 v = n->eval( parent, context );
111 bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
112 if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
113 return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
114 }
115 argValues.append( v );
116 arg++;
117 }
118 }
119
120 return func( argValues, context, parent, node );
121}
122
124{
125 Q_UNUSED( node )
126 return true;
127}
128
130{
131 return QStringList();
132}
133
135{
136 Q_UNUSED( parent )
137 Q_UNUSED( context )
138 Q_UNUSED( node )
139 return false;
140}
141
143{
144 Q_UNUSED( parent )
145 Q_UNUSED( context )
146 Q_UNUSED( node )
147 return true;
148}
149
151{
152 Q_UNUSED( node )
153 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
154}
155
157{
158 return mGroups.isEmpty() ? false : mGroups.contains( QStringLiteral( "deprecated" ) );
159}
160
162{
163 return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
164}
165
167{
168 return mHandlesNull;
169}
170
171// doxygen doesn't like this constructor for some reason (maybe the function arguments?)
174 FcnEval fcn,
175 const QString &group,
176 const QString &helpText,
177 const std::function < bool ( const QgsExpressionNodeFunction *node ) > &usesGeometry,
178 const std::function < QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
179 bool lazyEval,
180 const QStringList &aliases,
181 bool handlesNull )
182 : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
183 , mFnc( fcn )
184 , mAliases( aliases )
185 , mUsesGeometry( false )
186 , mUsesGeometryFunc( usesGeometry )
187 , mReferencedColumnsFunc( referencedColumns )
188{
189}
191
193{
194 return mAliases;
195}
196
198{
199 if ( mUsesGeometryFunc )
200 return mUsesGeometryFunc( node );
201 else
202 return mUsesGeometry;
203}
204
205void QgsStaticExpressionFunction::setUsesGeometryFunction( const std::function<bool ( const QgsExpressionNodeFunction * )> &usesGeometry )
206{
207 mUsesGeometryFunc = usesGeometry;
208}
209
211{
212 if ( mReferencedColumnsFunc )
213 return mReferencedColumnsFunc( node );
214 else
215 return mReferencedColumns;
216}
217
219{
220 if ( mIsStaticFunc )
221 return mIsStaticFunc( node, parent, context );
222 else
223 return mIsStatic;
224}
225
227{
228 if ( mPrepareFunc )
229 return mPrepareFunc( node, parent, context );
230
231 return true;
232}
233
235{
236 mIsStaticFunc = isStatic;
237}
238
240{
241 mIsStaticFunc = nullptr;
242 mIsStatic = isStatic;
243}
244
245void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool ( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
246{
247 mPrepareFunc = prepareFunc;
248}
249
251{
252 if ( node && node->args() )
253 {
254 const QList< QgsExpressionNode * > argList = node->args()->list();
255 for ( QgsExpressionNode *argNode : argList )
256 {
257 if ( !argNode->isStatic( parent, context ) )
258 return false;
259 }
260 }
261
262 return true;
263}
264
265static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
266{
267 double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
268 double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
269 double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
270
271 if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
272 return QVariant();
273
274 QVariantList array;
275 int length = 1;
276
277 array << start;
278 double current = start + step;
279 while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
280 {
281 array << current;
282 current += step;
283 length++;
284 }
285
286 return array;
287}
288
289static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
290{
291 if ( !context )
292 return QVariant();
293
294 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
295
296 if ( name == QLatin1String( "feature" ) )
297 {
298 return context->hasFeature() ? QVariant::fromValue( context->feature() ) : QVariant();
299 }
300 else if ( name == QLatin1String( "id" ) )
301 {
302 return context->hasFeature() ? QVariant::fromValue( context->feature().id() ) : QVariant();
303 }
304 else if ( name == QLatin1String( "geometry" ) )
305 {
306 if ( !context->hasFeature() )
307 return QVariant();
308
309 const QgsFeature feature = context->feature();
310 return feature.hasGeometry() ? QVariant::fromValue( feature.geometry() ) : QVariant();
311 }
312 else
313 {
314 return context->variable( name );
315 }
316}
317
318static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
319{
320 QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
321 return QgsExpression::replaceExpressionText( templateString, context );
322}
323
324static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
325{
326 if ( !context )
327 return QVariant();
328
329 QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
330 QgsExpression expression( expString );
331 return expression.evaluate( context );
332}
333
334static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
335{
336 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
337 return QVariant( std::sqrt( x ) );
338}
339
340static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
341{
342 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
343 return QVariant( std::fabs( val ) );
344}
345
346static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
347{
348 double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
349 return ( deg * M_PI ) / 180;
350}
351static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
352{
353 double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
354 return ( 180 * rad ) / M_PI;
355}
356static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
357{
358 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
359 return QVariant( std::sin( x ) );
360}
361static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
362{
363 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
364 return QVariant( std::cos( x ) );
365}
366static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
367{
368 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
369 return QVariant( std::tan( x ) );
370}
371static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
372{
373 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
374 return QVariant( std::asin( x ) );
375}
376static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
377{
378 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
379 return QVariant( std::acos( x ) );
380}
381static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
382{
383 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
384 return QVariant( std::atan( x ) );
385}
386static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
387{
388 double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
389 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
390 return QVariant( std::atan2( y, x ) );
391}
392static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
393{
394 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
395 return QVariant( std::exp( x ) );
396}
397static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
398{
399 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
400 if ( x <= 0 )
401 return QVariant();
402 return QVariant( std::log( x ) );
403}
404static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
405{
406 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
407 if ( x <= 0 )
408 return QVariant();
409 return QVariant( log10( x ) );
410}
411static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
412{
413 double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
414 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
415 if ( x <= 0 || b <= 0 )
416 return QVariant();
417 return QVariant( std::log( x ) / std::log( b ) );
418}
419static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
420{
421 double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
422 double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
423 if ( max < min )
424 return QVariant();
425
426 std::random_device rd;
427 std::mt19937_64 generator( rd() );
428
429 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
430 {
431 quint32 seed;
432 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
433 {
434 // if seed can be converted to int, we use as is
435 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
436 }
437 else
438 {
439 // if not, we hash string representation to int
440 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
441 std::hash<std::string> hasher;
442 seed = hasher( seedStr.toStdString() );
443 }
444 generator.seed( seed );
445 }
446
447 // Return a random double in the range [min, max] (inclusive)
448 double f = static_cast< double >( generator() ) / static_cast< double >( std::mt19937_64::max() );
449 return QVariant( min + f * ( max - min ) );
450}
451static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
452{
453 qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
454 qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
455 if ( max < min )
456 return QVariant();
457
458 std::random_device rd;
459 std::mt19937_64 generator( rd() );
460
461 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
462 {
463 quint32 seed;
464 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
465 {
466 // if seed can be converted to int, we use as is
467 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
468 }
469 else
470 {
471 // if not, we hash string representation to int
472 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
473 std::hash<std::string> hasher;
474 seed = hasher( seedStr.toStdString() );
475 }
476 generator.seed( seed );
477 }
478
479 qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
480 if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
481 return QVariant( randomInteger );
482
483 // Prevent wrong conversion of QVariant. See #36412
484 return QVariant( int( randomInteger ) );
485}
486
487static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
488{
489 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
490 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
491 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
492 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
493 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
494
495 if ( domainMin >= domainMax )
496 {
497 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
498 return QVariant();
499 }
500
501 // outside of domain?
502 if ( val >= domainMax )
503 {
504 return rangeMax;
505 }
506 else if ( val <= domainMin )
507 {
508 return rangeMin;
509 }
510
511 // calculate linear scale
512 double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
513 double c = rangeMin - ( domainMin * m );
514
515 // Return linearly scaled value
516 return QVariant( m * val + c );
517}
518
519static QVariant fcnPolynomialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
520{
521 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
522 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
523 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
524 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
525 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
526 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
527
528 if ( domainMin >= domainMax )
529 {
530 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
531 return QVariant();
532 }
533 if ( exponent <= 0 )
534 {
535 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
536 return QVariant();
537 }
538
539 // outside of domain?
540 if ( val >= domainMax )
541 {
542 return rangeMax;
543 }
544 else if ( val <= domainMin )
545 {
546 return rangeMin;
547 }
548
549 // Return polynomially scaled value
550 return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
551}
552
553static QVariant fcnExponentialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
554{
555 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
556 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
557 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
558 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
559 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
560 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
561
562 if ( domainMin >= domainMax )
563 {
564 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
565 return QVariant();
566 }
567 if ( exponent <= 0 )
568 {
569 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
570 return QVariant();
571 }
572
573 // outside of domain?
574 if ( val >= domainMax )
575 {
576 return rangeMax;
577 }
578 else if ( val <= domainMin )
579 {
580 return rangeMin;
581 }
582
583 // Return exponentially scaled value
584 double ratio = ( std::pow( exponent, val - domainMin ) - 1 ) / ( std::pow( exponent, domainMax - domainMin ) - 1 );
585 return QVariant( ( rangeMax - rangeMin ) * ratio + rangeMin );
586}
587
588static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
589{
590 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
591 double maxVal = std::numeric_limits<double>::quiet_NaN();
592 for ( const QVariant &val : values )
593 {
594 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
595 if ( std::isnan( maxVal ) )
596 {
597 maxVal = testVal;
598 }
599 else if ( !std::isnan( testVal ) )
600 {
601 maxVal = std::max( maxVal, testVal );
602 }
603 }
604
605 if ( !std::isnan( maxVal ) )
606 {
607 result = QVariant( maxVal );
608 }
609 return result;
610}
611
612static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
613{
614 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
615 double minVal = std::numeric_limits<double>::quiet_NaN();
616 for ( const QVariant &val : values )
617 {
618 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
619 if ( std::isnan( minVal ) )
620 {
621 minVal = testVal;
622 }
623 else if ( !std::isnan( testVal ) )
624 {
625 minVal = std::min( minVal, testVal );
626 }
627 }
628
629 if ( !std::isnan( minVal ) )
630 {
631 result = QVariant( minVal );
632 }
633 return result;
634}
635
636static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
637{
638 //lazy eval, so we need to evaluate nodes now
639
640 //first node is layer id or name
641 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
643 QVariant value = node->eval( parent, context );
645
646 // TODO this expression function is NOT thread safe
648 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, context, parent );
650 if ( !vl )
651 {
652 parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
653 return QVariant();
654 }
655
656 // second node is aggregate type
657 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
659 value = node->eval( parent, context );
661 bool ok = false;
662 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
663 if ( !ok )
664 {
665 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
666 return QVariant();
667 }
668
669 // third node is subexpression (or field name)
670 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
672 QString subExpression = node->dump();
673
675 //optional forth node is filter
676 if ( values.count() > 3 )
677 {
678 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
680 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
681 if ( !nl || nl->value().isValid() )
682 parameters.filter = node->dump();
683 }
684
685 //optional fifth node is concatenator
686 if ( values.count() > 4 )
687 {
688 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
690 value = node->eval( parent, context );
692 parameters.delimiter = value.toString();
693 }
694
695 //optional sixth node is order by
696 QString orderBy;
697 if ( values.count() > 5 )
698 {
699 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
701 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
702 if ( !nl || nl->value().isValid() )
703 {
704 orderBy = node->dump();
705 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
706 }
707 }
708
709 QString aggregateError;
710 QVariant result;
711 if ( context )
712 {
713 QString cacheKey;
714 QgsExpression subExp( subExpression );
715 QgsExpression filterExp( parameters.filter );
716
717 const QSet< QString > filterVars = filterExp.referencedVariables();
718 const QSet< QString > subExpVars = subExp.referencedVariables();
719 QSet<QString> allVars = filterVars + subExpVars;
720
721 bool isStatic = true;
722 if ( filterVars.contains( QStringLiteral( "parent" ) )
723 || filterVars.contains( QString() )
724 || subExpVars.contains( QStringLiteral( "parent" ) )
725 || subExpVars.contains( QString() ) )
726 {
727 isStatic = false;
728 }
729 else
730 {
731 for ( const QString &varName : allVars )
732 {
733 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
734 if ( scope && !scope->isStatic( varName ) )
735 {
736 isStatic = false;
737 break;
738 }
739 }
740 }
741
742 if ( isStatic && ! parameters.orderBy.isEmpty() )
743 {
744 for ( const auto &orderByClause : std::as_const( parameters.orderBy ) )
745 {
746 const QgsExpression &orderByExpression { orderByClause.expression() };
747 if ( orderByExpression.referencedVariables().contains( QStringLiteral( "parent" ) ) || orderByExpression.referencedVariables().contains( QString() ) )
748 {
749 isStatic = false;
750 break;
751 }
752 }
753 }
754
755 if ( !isStatic )
756 {
757 bool ok = false;
758 const QString contextHash = context->uniqueHash( ok, allVars );
759 if ( ok )
760 {
761 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5:%6" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
762 orderBy, contextHash );
763 }
764 }
765 else
766 {
767 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
768 }
769
770 if ( !cacheKey.isEmpty() && context->hasCachedValue( cacheKey ) )
771 {
772 return context->cachedValue( cacheKey );
773 }
774
775 QgsExpressionContext subContext( *context );
777 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
778 subContext.appendScope( subScope );
779 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
780
781 if ( ok && !cacheKey.isEmpty() )
782 {
783 // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
784 // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
785 // associated with it's calculation!
786 context->setCachedValue( cacheKey, result );
787 }
788 }
789 else
790 {
791 result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
792 }
793 if ( !ok )
794 {
795 if ( !aggregateError.isEmpty() )
796 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
797 else
798 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
799 return QVariant();
800 }
801
802 return result;
803}
804
805static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
806{
807 if ( !context )
808 {
809 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
810 return QVariant();
811 }
812
813 // first step - find current layer
814
815 // TODO this expression function is NOT thread safe
817 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
819 if ( !vl )
820 {
821 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
822 return QVariant();
823 }
824
825 //lazy eval, so we need to evaluate nodes now
826
827 //first node is relation name
828 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
830 QVariant value = node->eval( parent, context );
832 QString relationId = value.toString();
833 // check relation exists
834 QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId ); // skip-keyword-check
835 if ( !relation.isValid() || relation.referencedLayer() != vl )
836 {
837 // check for relations by name
838 QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId ); // skip-keyword-check
839 if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
840 {
841 parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
842 return QVariant();
843 }
844 else
845 {
846 relation = relations.at( 0 );
847 }
848 }
849
850 QgsVectorLayer *childLayer = relation.referencingLayer();
851
852 // second node is aggregate type
853 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
855 value = node->eval( parent, context );
857 bool ok = false;
858 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
859 if ( !ok )
860 {
861 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
862 return QVariant();
863 }
864
865 //third node is subexpression (or field name)
866 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
868 QString subExpression = node->dump();
869
870 //optional fourth node is concatenator
872 if ( values.count() > 3 )
873 {
874 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
876 value = node->eval( parent, context );
878 parameters.delimiter = value.toString();
879 }
880
881 //optional fifth node is order by
882 QString orderBy;
883 if ( values.count() > 4 )
884 {
885 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
887 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
888 if ( !nl || nl->value().isValid() )
889 {
890 orderBy = node->dump();
891 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
892 }
893 }
894
895 if ( !context->hasFeature() )
896 return QVariant();
897 QgsFeature f = context->feature();
898
899 parameters.filter = relation.getRelatedFeaturesFilter( f );
900
901 const QString cacheKey = QStringLiteral( "relagg:%1%:%2:%3:%4:%5:%6" ).arg( relationId, vl->id(),
902 QString::number( static_cast< int >( aggregate ) ),
903 subExpression,
904 parameters.filter,
905 orderBy );
906 if ( context->hasCachedValue( cacheKey ) )
907 return context->cachedValue( cacheKey );
908
909 QVariant result;
910 ok = false;
911
912
913 QgsExpressionContext subContext( *context );
914 QString error;
915 result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
916
917 if ( !ok )
918 {
919 if ( !error.isEmpty() )
920 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
921 else
922 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
923 return QVariant();
924 }
925
926 // cache value
927 context->setCachedValue( cacheKey, result );
928 return result;
929}
930
931
932static QVariant fcnAggregateGeneric( Qgis::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1 )
933{
934 if ( !context )
935 {
936 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
937 return QVariant();
938 }
939
940 // first step - find current layer
941
942 // TODO this expression function is NOT thread safe
944 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
946 if ( !vl )
947 {
948 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
949 return QVariant();
950 }
951
952 //lazy eval, so we need to evaluate nodes now
953
954 //first node is subexpression (or field name)
955 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
957 QString subExpression = node->dump();
958
959 //optional second node is group by
960 QString groupBy;
961 if ( values.count() > 1 )
962 {
963 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
965 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
966 if ( !nl || nl->value().isValid() )
967 groupBy = node->dump();
968 }
969
970 //optional third node is filter
971 if ( values.count() > 2 )
972 {
973 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
975 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
976 if ( !nl || nl->value().isValid() )
977 parameters.filter = node->dump();
978 }
979
980 //optional order by node, if supported
981 QString orderBy;
982 if ( orderByPos >= 0 && values.count() > orderByPos )
983 {
984 node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
986 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
987 if ( !nl || nl->value().isValid() )
988 {
989 orderBy = node->dump();
990 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
991 }
992 }
993
994 // build up filter with group by
995
996 // find current group by value
997 if ( !groupBy.isEmpty() )
998 {
999 QgsExpression groupByExp( groupBy );
1000 QVariant groupByValue = groupByExp.evaluate( context );
1001 QString groupByClause = QStringLiteral( "%1 %2 %3" ).arg( groupBy,
1002 QgsVariantUtils::isNull( groupByValue ) ? QStringLiteral( "is" ) : QStringLiteral( "=" ),
1003 QgsExpression::quotedValue( groupByValue ) );
1004 if ( !parameters.filter.isEmpty() )
1005 parameters.filter = QStringLiteral( "(%1) AND (%2)" ).arg( parameters.filter, groupByClause );
1006 else
1007 parameters.filter = groupByClause;
1008 }
1009
1010 QgsExpression subExp( subExpression );
1011 QgsExpression filterExp( parameters.filter );
1012
1013 bool isStatic = true;
1014 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
1015 for ( const QString &varName : refVars )
1016 {
1017 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
1018 if ( scope && !scope->isStatic( varName ) )
1019 {
1020 isStatic = false;
1021 break;
1022 }
1023 }
1024
1025 QString cacheKey;
1026 if ( !isStatic )
1027 {
1028 bool ok = false;
1029 const QString contextHash = context->uniqueHash( ok, refVars );
1030 if ( ok )
1031 {
1032 cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5:%6" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
1033 orderBy, contextHash );
1034 }
1035 }
1036 else
1037 {
1038 cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1039 }
1040
1041 if ( context->hasCachedValue( cacheKey ) )
1042 return context->cachedValue( cacheKey );
1043
1044 QVariant result;
1045 bool ok = false;
1046
1047 QgsExpressionContext subContext( *context );
1049 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
1050 subContext.appendScope( subScope );
1051 QString error;
1052 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1053
1054 if ( !ok )
1055 {
1056 if ( !error.isEmpty() )
1057 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1058 else
1059 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1060 return QVariant();
1061 }
1062
1063 // cache value
1064 context->setCachedValue( cacheKey, result );
1065 return result;
1066}
1067
1068
1069static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1070{
1071 return fcnAggregateGeneric( Qgis::Aggregate::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1072}
1073
1074static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1075{
1076 return fcnAggregateGeneric( Qgis::Aggregate::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1077}
1078
1079static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1080{
1081 return fcnAggregateGeneric( Qgis::Aggregate::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1082}
1083
1084static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1085{
1086 return fcnAggregateGeneric( Qgis::Aggregate::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1087}
1088
1089static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1090{
1091 return fcnAggregateGeneric( Qgis::Aggregate::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1092}
1093
1094static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1095{
1096 return fcnAggregateGeneric( Qgis::Aggregate::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1097}
1098
1099static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1100{
1101 return fcnAggregateGeneric( Qgis::Aggregate::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1102}
1103
1104static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1105{
1106 return fcnAggregateGeneric( Qgis::Aggregate::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1107}
1108
1109static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1110{
1111 return fcnAggregateGeneric( Qgis::Aggregate::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1112}
1113
1114static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1115{
1116 return fcnAggregateGeneric( Qgis::Aggregate::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1117}
1118
1119static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1120{
1121 return fcnAggregateGeneric( Qgis::Aggregate::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1122}
1123
1124static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1125{
1126 return fcnAggregateGeneric( Qgis::Aggregate::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1127}
1128
1129static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1130{
1131 return fcnAggregateGeneric( Qgis::Aggregate::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1132}
1133
1134static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1135{
1136 return fcnAggregateGeneric( Qgis::Aggregate::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1137}
1138
1139static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1140{
1141 return fcnAggregateGeneric( Qgis::Aggregate::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1142}
1143
1144static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1145{
1146 return fcnAggregateGeneric( Qgis::Aggregate::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1147}
1148
1149static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1150{
1151 return fcnAggregateGeneric( Qgis::Aggregate::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1152}
1153
1154static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1155{
1156 return fcnAggregateGeneric( Qgis::Aggregate::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1157}
1158
1159static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1160{
1162
1163 //fourth node is concatenator
1164 if ( values.count() > 3 )
1165 {
1166 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1168 QVariant value = node->eval( parent, context );
1170 parameters.delimiter = value.toString();
1171 }
1172
1173 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenate, values, parameters, context, parent, 4 );
1174}
1175
1176static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1177{
1179
1180 //fourth node is concatenator
1181 if ( values.count() > 3 )
1182 {
1183 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1185 QVariant value = node->eval( parent, context );
1187 parameters.delimiter = value.toString();
1188 }
1189
1190 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenateUnique, values, parameters, context, parent, 4 );
1191}
1192
1193static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1194{
1195 return fcnAggregateGeneric( Qgis::Aggregate::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1196}
1197
1198static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1199{
1200 if ( !context )
1201 return QVariant();
1202
1203 QVariant scale = context->variable( QStringLiteral( "map_scale" ) );
1204 bool ok = false;
1205 if ( QgsVariantUtils::isNull( scale ) )
1206 return QVariant();
1207
1208 const double v = scale.toDouble( &ok );
1209 if ( ok )
1210 return v;
1211 return QVariant();
1212}
1213
1214static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1215{
1216 double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1217 double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1218 double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1219
1220 // force testValue to sit inside the range specified by the min and max value
1221 if ( testValue <= minValue )
1222 {
1223 return QVariant( minValue );
1224 }
1225 else if ( testValue >= maxValue )
1226 {
1227 return QVariant( maxValue );
1228 }
1229 else
1230 {
1231 return QVariant( testValue );
1232 }
1233}
1234
1235static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1236{
1237 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1238 return QVariant( std::floor( x ) );
1239}
1240
1241static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1242{
1243 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1244 return QVariant( std::ceil( x ) );
1245}
1246
1247static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1248{
1249 return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1250}
1251static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1252{
1253 return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1254}
1255static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1256{
1257 return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1258}
1259
1260static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1261{
1262 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1263 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1264 if ( format.isEmpty() && !language.isEmpty() )
1265 {
1266 parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1267 return QVariant( QDateTime() );
1268 }
1269
1270 if ( format.isEmpty() && language.isEmpty() )
1271 return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1272
1273 QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1274 QLocale locale = QLocale();
1275 if ( !language.isEmpty() )
1276 {
1277 locale = QLocale( language );
1278 }
1279
1280 QDateTime datetime = locale.toDateTime( datetimestring, format );
1281 if ( !datetime.isValid() )
1282 {
1283 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1284 datetime = QDateTime();
1285 }
1286 return QVariant( datetime );
1287}
1288
1289static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1290{
1291 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1292 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1293 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1294
1295 const QDate date( year, month, day );
1296 if ( !date.isValid() )
1297 {
1298 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1299 return QVariant();
1300 }
1301 return QVariant( date );
1302}
1303
1304static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1305{
1306 const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1307 const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1308 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1309
1310 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1311 if ( !time.isValid() )
1312 {
1313 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1314 return QVariant();
1315 }
1316 return QVariant( time );
1317}
1318
1319static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1320{
1321 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1322 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1323 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1324 const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1325 const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1326 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1327
1328 const QDate date( year, month, day );
1329 if ( !date.isValid() )
1330 {
1331 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1332 return QVariant();
1333 }
1334 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1335 if ( !time.isValid() )
1336 {
1337 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1338 return QVariant();
1339 }
1340 return QVariant( QDateTime( date, time ) );
1341}
1342
1343static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1344{
1345 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1346 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1347 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1348 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1349 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1350 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1351 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1352
1353 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1354}
1355
1356static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1357{
1358 for ( const QVariant &value : values )
1359 {
1360 if ( QgsVariantUtils::isNull( value ) )
1361 continue;
1362 return value;
1363 }
1364 return QVariant();
1365}
1366
1367static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1368{
1369 const QVariant val1 = values.at( 0 );
1370 const QVariant val2 = values.at( 1 );
1371
1372 if ( val1 == val2 )
1373 return QVariant();
1374 else
1375 return val1;
1376}
1377
1378static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1379{
1380 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1381 return QVariant( str.toLower() );
1382}
1383static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1384{
1385 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1386 return QVariant( str.toUpper() );
1387}
1388static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1389{
1390 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1391 QStringList elems = str.split( ' ' );
1392 for ( int i = 0; i < elems.size(); i++ )
1393 {
1394 if ( elems[i].size() > 1 )
1395 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1396 }
1397 return QVariant( elems.join( QLatin1Char( ' ' ) ) );
1398}
1399
1400static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1401{
1402 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1403 return QVariant( str.trimmed() );
1404}
1405
1406static QVariant fcnLTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1407{
1408 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1409
1410 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1411
1412 const QRegularExpression re( QStringLiteral( "^([%1]*)" ).arg( QRegularExpression::escape( characters ) ) );
1413 str.replace( re, QString() );
1414 return QVariant( str );
1415}
1416
1417static QVariant fcnRTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1418{
1419 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1420
1421 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1422
1423 const QRegularExpression re( QStringLiteral( "([%1]*)$" ).arg( QRegularExpression::escape( characters ) ) );
1424 str.replace( re, QString() );
1425 return QVariant( str );
1426}
1427
1428static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1429{
1430 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1431 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1432 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1433}
1434
1435static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1436{
1437 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1438 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1439 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1440}
1441
1442static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1443{
1444 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1445 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1446 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1447 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1448}
1449
1450static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1451{
1452 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1453 return QVariant( QgsStringUtils::soundex( string ) );
1454}
1455
1456static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1457{
1458 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1459 return QVariant( QString( character ) );
1460}
1461
1462static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1463{
1464 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1465
1466 if ( value.isEmpty() )
1467 {
1468 return QVariant();
1469 }
1470
1471 int res = value.at( 0 ).unicode();
1472 return QVariant( res );
1473}
1474
1475static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1476{
1477 if ( values.length() == 2 || values.length() == 3 )
1478 {
1479 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1480 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1481
1482 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1483
1484 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1485 }
1486
1487 return QVariant();
1488}
1489
1490static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1491{
1492 // two variants, one for geometry, one for string
1493
1494 //geometry variant
1495 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
1496 if ( !geom.isNull() )
1497 {
1498 if ( geom.type() == Qgis::GeometryType::Line )
1499 return QVariant( geom.length() );
1500 else
1501 return QVariant();
1502 }
1503
1504 //otherwise fall back to string variant
1505 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1506 return QVariant( str.length() );
1507}
1508
1509static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1510{
1511 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1512
1513 if ( geom.type() != Qgis::GeometryType::Line )
1514 return QVariant();
1515
1516 double totalLength = 0;
1517 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1518 {
1519 if ( const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( *it ) )
1520 {
1521 totalLength += line->length3D();
1522 }
1523 else
1524 {
1525 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1526 totalLength += segmentized->length3D();
1527 }
1528 }
1529
1530 return totalLength;
1531}
1532
1533static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1534{
1535 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
1536 {
1537 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1538 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1539 QVector< QPair< QString, QString > > mapItems;
1540
1541 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1542 {
1543 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1544 }
1545
1546 // larger keys should be replaced first since they may contain whole smaller keys
1547 std::sort( mapItems.begin(),
1548 mapItems.end(),
1549 []( const QPair< QString, QString > &pair1,
1550 const QPair< QString, QString > &pair2 )
1551 {
1552 return ( pair1.first.length() > pair2.first.length() );
1553 } );
1554
1555 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1556 {
1557 str = str.replace( it->first, it->second );
1558 }
1559
1560 return QVariant( str );
1561 }
1562 else if ( values.count() == 3 )
1563 {
1564 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1565 QVariantList before;
1566 QVariantList after;
1567 bool isSingleReplacement = false;
1568
1569 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
1570 {
1571 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1572 }
1573 else
1574 {
1575 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1576 }
1577
1578 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1579 {
1580 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1581 isSingleReplacement = true;
1582 }
1583 else
1584 {
1585 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1586 }
1587
1588 if ( !isSingleReplacement && before.length() != after.length() )
1589 {
1590 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1591 return QVariant();
1592 }
1593
1594 for ( int i = 0; i < before.length(); i++ )
1595 {
1596 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1597 }
1598
1599 return QVariant( str );
1600 }
1601 else
1602 {
1603 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1604 return QVariant();
1605 }
1606}
1607
1608static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1609{
1610 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1611 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1612 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1613
1614 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1615 if ( !re.isValid() )
1616 {
1617 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1618 return QVariant();
1619 }
1620 return QVariant( str.replace( re, after ) );
1621}
1622
1623static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1624{
1625 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1626 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1627
1628 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1629 if ( !re.isValid() )
1630 {
1631 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1632 return QVariant();
1633 }
1634 return QVariant( ( str.indexOf( re ) + 1 ) );
1635}
1636
1637static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1638{
1639 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1640 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1641 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1642
1643 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1644 if ( !re.isValid() )
1645 {
1646 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1647 return QVariant();
1648 }
1649
1650 QRegularExpressionMatch matches = re.match( str );
1651 if ( matches.hasMatch() )
1652 {
1653 QVariantList array;
1654 QStringList list = matches.capturedTexts();
1655
1656 // Skip the first string to only return captured groups
1657 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1658 {
1659 array += ( !( *it ).isEmpty() ) ? *it : empty;
1660 }
1661
1662 return QVariant( array );
1663 }
1664 else
1665 {
1666 return QVariant();
1667 }
1668}
1669
1670static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1671{
1672 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1673 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1674
1675 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1676 if ( !re.isValid() )
1677 {
1678 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1679 return QVariant();
1680 }
1681
1682 // extract substring
1683 QRegularExpressionMatch match = re.match( str );
1684 if ( match.hasMatch() )
1685 {
1686 // return first capture
1687 if ( match.lastCapturedIndex() > 0 )
1688 {
1689 // a capture group was present, so use that
1690 return QVariant( match.captured( 1 ) );
1691 }
1692 else
1693 {
1694 // no capture group, so using all match
1695 return QVariant( match.captured( 0 ) );
1696 }
1697 }
1698 else
1699 {
1700 return QVariant( "" );
1701 }
1702}
1703
1704static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1705{
1706 QString uuid = QUuid::createUuid().toString();
1707 if ( values.at( 0 ).toString().compare( QStringLiteral( "WithoutBraces" ), Qt::CaseInsensitive ) == 0 )
1708 uuid = QUuid::createUuid().toString( QUuid::StringFormat::WithoutBraces );
1709 else if ( values.at( 0 ).toString().compare( QStringLiteral( "Id128" ), Qt::CaseInsensitive ) == 0 )
1710 uuid = QUuid::createUuid().toString( QUuid::StringFormat::Id128 );
1711 return uuid;
1712}
1713
1714static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1715{
1716 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1717 return QVariant();
1718
1719 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1720 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1721
1722 int len = 0;
1723 if ( values.at( 2 ).isValid() )
1724 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1725 else
1726 len = str.size();
1727
1728 if ( from < 0 )
1729 {
1730 from = str.size() + from;
1731 if ( from < 0 )
1732 {
1733 from = 0;
1734 }
1735 }
1736 else if ( from > 0 )
1737 {
1738 //account for the fact that substr() starts at 1
1739 from -= 1;
1740 }
1741
1742 if ( len < 0 )
1743 {
1744 len = str.size() + len - from;
1745 if ( len < 0 )
1746 {
1747 len = 0;
1748 }
1749 }
1750
1751 return QVariant( str.mid( from, len ) );
1752}
1753static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1754{
1755 FEAT_FROM_CONTEXT( context, f )
1756 // TODO: handling of 64-bit feature ids?
1757 return QVariant( static_cast< int >( f.id() ) );
1758}
1759
1760static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1761{
1762 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1763 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1764 bool foundLayer = false;
1765 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, geom]( QgsMapLayer * mapLayer )
1766 {
1767 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
1768 if ( !layer || !layer->dataProvider() )
1769 {
1770 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1771 return QVariant();
1772 }
1773
1774 if ( bandNb < 1 || bandNb > layer->bandCount() )
1775 {
1776 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
1777 return QVariant();
1778 }
1779
1780 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
1781 {
1782 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
1783 return QVariant();
1784 }
1785
1786 QgsPointXY point = geom.asPoint();
1787 if ( geom.isMultipart() )
1788 {
1789 QgsMultiPointXY multiPoint = geom.asMultiPoint();
1790 if ( multiPoint.count() == 1 )
1791 {
1792 point = multiPoint[0];
1793 }
1794 else
1795 {
1796 // if the geometry contains more than one part, return an undefined value
1797 return QVariant();
1798 }
1799 }
1800
1801 double value = layer->dataProvider()->sample( point, bandNb );
1802 return std::isnan( value ) ? QVariant() : value;
1803 },
1804 foundLayer );
1805
1806 if ( !foundLayer )
1807 {
1808 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1809 return QVariant();
1810 }
1811 else
1812 {
1813 return res;
1814 }
1815}
1816
1817static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1818{
1819 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1820 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1821
1822 bool foundLayer = false;
1823 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, value]( QgsMapLayer * mapLayer )-> QVariant
1824 {
1825 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
1826 if ( !layer || !layer->dataProvider() )
1827 {
1828 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1829 return QVariant();
1830 }
1831
1832 if ( bandNb < 1 || bandNb > layer->bandCount() )
1833 {
1834 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
1835 return QVariant();
1836 }
1837
1838 if ( std::isnan( value ) )
1839 {
1840 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
1841 return QVariant();
1842 }
1843
1844 if ( ! layer->dataProvider()->attributeTable( bandNb ) )
1845 {
1846 return QVariant();
1847 }
1848
1849 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
1850 if ( data.isEmpty() )
1851 {
1852 return QVariant();
1853 }
1854
1855 QVariantMap result;
1856 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
1857 for ( int idx = 0; idx < static_cast<int>( fields.count( ) ) && idx < static_cast<int>( data.count() ); ++idx )
1858 {
1859 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
1860 if ( field.isColor() || field.isRamp() )
1861 {
1862 continue;
1863 }
1864 result.insert( fields.at( idx ).name, data.at( idx ) );
1865 }
1866
1867 return result;
1868 }, foundLayer );
1869
1870 if ( !foundLayer )
1871 {
1872 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1873 return QVariant();
1874 }
1875 else
1876 {
1877 return res;
1878 }
1879}
1880
1881static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1882{
1883 if ( !context )
1884 return QVariant();
1885
1886 return context->feature();
1887}
1888
1889static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1890{
1891 QgsFeature feature;
1892 QString attr;
1893 if ( values.size() == 1 )
1894 {
1895 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1896 feature = context->feature();
1897 }
1898 else if ( values.size() == 2 )
1899 {
1900 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1901 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1902 }
1903 else
1904 {
1905 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
1906 return QVariant();
1907 }
1908
1909 return feature.attribute( attr );
1910}
1911
1912static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1913{
1914 QString table { R"html(
1915 <table>
1916 <thead>
1917 <tr><th>%1</th></tr>
1918 </thead>
1919 <tbody>
1920 <tr><td>%2</td></tr>
1921 </tbody>
1922 </table>)html" };
1923 QVariantMap dict;
1924 if ( values.size() == 1 )
1925 {
1926 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
1927 }
1928 else
1929 {
1930 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
1931 return QVariant();
1932 }
1933
1934 if ( dict.isEmpty() )
1935 {
1936 return QVariant();
1937 }
1938
1939 QStringList headers;
1940 QStringList cells;
1941
1942 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
1943 {
1944 headers.push_back( it.key().toHtmlEscaped() );
1945 cells.push_back( it.value().toString( ).toHtmlEscaped() );
1946 }
1947
1948 return table.arg( headers.join( QLatin1String( "</th><th>" ) ), cells.join( QLatin1String( "</td><td>" ) ) );
1949}
1950
1951static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1952{
1953 QString table { R"html(
1954 <dl>
1955 %1
1956 </dl>)html" };
1957 QVariantMap dict;
1958 if ( values.size() == 1 )
1959 {
1960 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
1961 }
1962 else
1963 {
1964 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
1965 return QVariant();
1966 }
1967
1968 if ( dict.isEmpty() )
1969 {
1970 return QVariant();
1971 }
1972
1973 QString rows;
1974
1975 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
1976 {
1977 rows.append( QStringLiteral( "<dt>%1</dt><dd>%2</dd>" ).arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
1978 }
1979
1980 return table.arg( rows );
1981}
1982
1983static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1984{
1985 QVariant layer;
1986 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
1987 {
1988 layer = context->variable( QStringLiteral( "layer" ) );
1989 }
1990 else
1991 {
1992 //first node is layer id or name
1993 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
1995 layer = node->eval( parent, context );
1997 }
1998
1999 QgsFeature feature;
2000 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2001 {
2002 feature = context->feature();
2003 }
2004 else
2005 {
2006 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2007 }
2008
2010 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
2011 if ( strength == QLatin1String( "hard" ) )
2012 {
2014 }
2015 else if ( strength == QLatin1String( "soft" ) )
2016 {
2018 }
2019
2020 bool foundLayer = false;
2021 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2022 {
2023 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2024 if ( !layer )
2025 {
2026 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2027 return QVariant();
2028 }
2029
2030 const QgsFields fields = layer->fields();
2031 bool valid = true;
2032 for ( int i = 0; i < fields.size(); i++ )
2033 {
2034 QStringList errors;
2035 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
2036 if ( !valid )
2037 {
2038 break;
2039 }
2040 }
2041
2042 return valid;
2043 }, foundLayer );
2044
2045 if ( !foundLayer )
2046 {
2047 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2048 return QVariant();
2049 }
2050
2051 return res;
2052}
2053
2054static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2055{
2056 QVariant layer;
2057 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2058 {
2059 layer = context->variable( QStringLiteral( "layer" ) );
2060 }
2061 else
2062 {
2063 //first node is layer id or name
2064 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
2066 layer = node->eval( parent, context );
2068 }
2069
2070 QgsFeature feature;
2071 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
2072 {
2073 feature = context->feature();
2074 }
2075 else
2076 {
2077 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
2078 }
2079
2081 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
2082 if ( strength == QLatin1String( "hard" ) )
2083 {
2085 }
2086 else if ( strength == QLatin1String( "soft" ) )
2087 {
2089 }
2090
2091 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2092
2093 bool foundLayer = false;
2094 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, attributeName, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2095 {
2096 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2097 if ( !layer )
2098 {
2099 return QVariant();
2100 }
2101
2102 const int fieldIndex = layer->fields().indexFromName( attributeName );
2103 if ( fieldIndex == -1 )
2104 {
2105 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2106 return QVariant();
2107 }
2108
2109 QStringList errors;
2110 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2111 return valid;
2112 }, foundLayer );
2113
2114 if ( !foundLayer )
2115 {
2116 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2117 return QVariant();
2118 }
2119
2120 return res;
2121}
2122
2123static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2124{
2125 QgsFeature feature;
2126 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2127 {
2128 feature = context->feature();
2129 }
2130 else
2131 {
2132 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2133 }
2134
2135 const QgsFields fields = feature.fields();
2136 QVariantMap result;
2137 for ( int i = 0; i < fields.count(); ++i )
2138 {
2139 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2140 }
2141 return result;
2142}
2143
2144static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2145{
2146 QgsVectorLayer *layer = nullptr;
2147 QgsFeature feature;
2148
2149 // TODO this expression function is NOT thread safe
2151 if ( values.isEmpty() )
2152 {
2153 feature = context->feature();
2154 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2155 }
2156 else if ( values.size() == 1 )
2157 {
2158 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2159 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2160 }
2161 else if ( values.size() == 2 )
2162 {
2163 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2164 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2165 }
2166 else
2167 {
2168 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2169 return QVariant();
2170 }
2172
2173 if ( !layer )
2174 {
2175 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2176 return QVariant();
2177 }
2178
2179 if ( !feature.isValid() )
2180 {
2181 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2182 return QVariant();
2183 }
2184
2185 const QgsFields fields = feature.fields();
2186 QVariantMap result;
2187 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2188 {
2189 const QString fieldName { fields.at( fieldIndex ).name() };
2190 const QVariant attributeVal = feature.attribute( fieldIndex );
2191 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer->id(), fieldName, attributeVal.toString() );
2192 if ( context && context->hasCachedValue( cacheValueKey ) )
2193 {
2194 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2195 }
2196 else
2197 {
2198 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2200 QVariant cache;
2201 if ( context )
2202 {
2203 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer->id(), fieldName );
2204
2205 if ( !context->hasCachedValue( cacheKey ) )
2206 {
2207 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2208 context->setCachedValue( cacheKey, cache );
2209 }
2210 else
2211 {
2212 cache = context->cachedValue( cacheKey );
2213 }
2214 }
2215 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2216
2217 result.insert( fields.at( fieldIndex ).name(), value );
2218
2219 if ( context )
2220 {
2221 context->setCachedValue( cacheValueKey, value );
2222 }
2223
2224 }
2225 }
2226 return result;
2227}
2228
2229static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2230{
2231 QgsVectorLayer *layer = nullptr;
2232 QgsFeature feature;
2233 bool evaluate = true;
2234
2235 // TODO this expression function is NOT thread safe
2237 if ( values.isEmpty() )
2238 {
2239 feature = context->feature();
2240 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2241 }
2242 else if ( values.size() == 1 )
2243 {
2244 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2245 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2246 }
2247 else if ( values.size() == 2 )
2248 {
2249 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2250 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2251 }
2252 else if ( values.size() == 3 )
2253 {
2254 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2255 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2256 evaluate = values.value( 2 ).toBool();
2257 }
2258 else
2259 {
2260 if ( isMaptip )
2261 {
2262 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2263 }
2264 else
2265 {
2266 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2267 }
2268 return QVariant();
2269 }
2270
2271 if ( !layer )
2272 {
2273 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2274 return QVariant( );
2275 }
2277
2278 if ( !feature.isValid() )
2279 {
2280 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2281 return QVariant( );
2282 }
2283
2284 if ( ! evaluate )
2285 {
2286 if ( isMaptip )
2287 {
2288 return layer->mapTipTemplate();
2289 }
2290 else
2291 {
2292 return layer->displayExpression();
2293 }
2294 }
2295
2296 QgsExpressionContext subContext( *context );
2297 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2298 subContext.setFeature( feature );
2299
2300 if ( isMaptip )
2301 {
2302 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2303 }
2304 else
2305 {
2306 QgsExpression exp( layer->displayExpression() );
2307 exp.prepare( &subContext );
2308 return exp.evaluate( &subContext ).toString();
2309 }
2310}
2311
2312static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2313{
2314 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2315}
2316
2317static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2318{
2319 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2320}
2321
2322static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2323{
2324 QgsFeature feature;
2325 QVariant layer;
2326 if ( values.isEmpty() )
2327 {
2328 feature = context->feature();
2329 layer = context->variable( QStringLiteral( "layer" ) );
2330 }
2331 else if ( values.size() == 1 )
2332 {
2333 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2334 layer = context->variable( QStringLiteral( "layer" ) );
2335 }
2336 else if ( values.size() == 2 )
2337 {
2338 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2339 layer = values.at( 0 );
2340 }
2341 else
2342 {
2343 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2344 return QVariant();
2345 }
2346
2347 bool foundLayer = false;
2348 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [feature]( QgsMapLayer * mapLayer ) -> QVariant
2349 {
2350 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2351 if ( !layer || !feature.isValid() )
2352 {
2353 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2354 }
2355
2356 return layer->selectedFeatureIds().contains( feature.id() );
2357 }, foundLayer );
2358 if ( !foundLayer )
2359 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2360 else
2361 return res;
2362}
2363
2364static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2365{
2366 QVariant layer;
2367
2368 if ( values.isEmpty() )
2369 layer = context->variable( QStringLiteral( "layer" ) );
2370 else if ( values.count() == 1 )
2371 layer = values.at( 0 );
2372 else
2373 {
2374 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2375 return QVariant();
2376 }
2377
2378 bool foundLayer = false;
2379 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, []( QgsMapLayer * mapLayer ) -> QVariant
2380 {
2381 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2382 if ( !layer )
2383 {
2384 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2385 }
2386
2387 return layer->selectedFeatureCount();
2388 }, foundLayer );
2389 if ( !foundLayer )
2390 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2391 else
2392 return res;
2393}
2394
2395static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2396{
2397 static QMap<QString, qlonglong> counterCache;
2398 QVariant functionResult;
2399
2400 auto fetchAndIncrementFunc = [ values, parent, &functionResult ]( QgsMapLayer * mapLayer, const QString & databaseArgument )
2401 {
2402 QString database;
2403
2404 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2405
2406 if ( layer )
2407 {
2408 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2409 database = decodedUri.value( QStringLiteral( "path" ) ).toString();
2410 if ( database.isEmpty() )
2411 {
2412 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2413 }
2414 }
2415 else
2416 {
2417 database = databaseArgument;
2418 }
2419
2420 const QString table = values.at( 1 ).toString();
2421 const QString idColumn = values.at( 2 ).toString();
2422 const QString filterAttribute = values.at( 3 ).toString();
2423 const QVariant filterValue = values.at( 4 ).toString();
2424 const QVariantMap defaultValues = values.at( 5 ).toMap();
2425
2426 // read from database
2428 sqlite3_statement_unique_ptr sqliteStatement;
2429
2430 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2431 {
2432 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2433 functionResult = QVariant();
2434 return;
2435 }
2436
2437 QString errorMessage;
2438 QString currentValSql;
2439
2440 qlonglong nextId = 0;
2441 bool cachedMode = false;
2442 bool valueRetrieved = false;
2443
2444 QString cacheString = QStringLiteral( "%1:%2:%3:%4:%5" ).arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2445
2446 // Running in transaction mode, check for cached value first
2447 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2448 {
2449 cachedMode = true;
2450
2451 auto cachedCounter = counterCache.find( cacheString );
2452
2453 if ( cachedCounter != counterCache.end() )
2454 {
2455 qlonglong &cachedValue = cachedCounter.value();
2456 nextId = cachedValue;
2457 nextId += 1;
2458 cachedValue = nextId;
2459 valueRetrieved = true;
2460 }
2461 }
2462
2463 // Either not in cached mode or no cached value found, obtain from DB
2464 if ( !cachedMode || !valueRetrieved )
2465 {
2466 int result = SQLITE_ERROR;
2467
2468 currentValSql = QStringLiteral( "SELECT %1 FROM %2" ).arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2469 if ( !filterAttribute.isNull() )
2470 {
2471 currentValSql += QStringLiteral( " WHERE %1 = %2" ).arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2472 }
2473
2474 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2475
2476 if ( result == SQLITE_OK )
2477 {
2478 nextId = 0;
2479 if ( sqliteStatement.step() == SQLITE_ROW )
2480 {
2481 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2482 }
2483
2484 // If in cached mode: add value to cache and connect to transaction
2485 if ( cachedMode && result == SQLITE_OK )
2486 {
2487 counterCache.insert( cacheString, nextId );
2488
2489 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]()
2490 {
2491 counterCache.remove( cacheString );
2492 } );
2493 }
2494 valueRetrieved = true;
2495 }
2496 }
2497
2498 if ( valueRetrieved )
2499 {
2500 QString upsertSql;
2501 upsertSql = QStringLiteral( "INSERT OR REPLACE INTO %1" ).arg( QgsSqliteUtils::quotedIdentifier( table ) );
2502 QStringList cols;
2503 QStringList vals;
2504 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2505 vals << QgsSqliteUtils::quotedValue( nextId );
2506
2507 if ( !filterAttribute.isNull() )
2508 {
2509 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2510 vals << QgsSqliteUtils::quotedValue( filterValue );
2511 }
2512
2513 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2514 {
2515 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2516 vals << iter.value().toString();
2517 }
2518
2519 upsertSql += QLatin1String( " (" ) + cols.join( ',' ) + ')';
2520 upsertSql += QLatin1String( " VALUES " );
2521 upsertSql += '(' + vals.join( ',' ) + ')';
2522
2523 int result = SQLITE_ERROR;
2524 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2525 {
2526 QgsTransaction *transaction = layer->dataProvider()->transaction();
2527 if ( transaction->executeSql( upsertSql, errorMessage ) )
2528 {
2529 result = SQLITE_OK;
2530 }
2531 }
2532 else
2533 {
2534 result = sqliteDb.exec( upsertSql, errorMessage );
2535 }
2536 if ( result == SQLITE_OK )
2537 {
2538 functionResult = QVariant( nextId );
2539 return;
2540 }
2541 else
2542 {
2543 parent->setEvalErrorString( QStringLiteral( "Could not increment value: SQLite error: \"%1\" (%2)." ).arg( errorMessage, QString::number( result ) ) );
2544 functionResult = QVariant();
2545 return;
2546 }
2547 }
2548
2549 functionResult = QVariant();
2550 };
2551
2552 bool foundLayer = false;
2553 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer * layer )
2554 {
2555 fetchAndIncrementFunc( layer, QString() );
2556 }, foundLayer );
2557 if ( !foundLayer )
2558 {
2559 const QString databasePath = values.at( 0 ).toString();
2560 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath]
2561 {
2562 fetchAndIncrementFunc( nullptr, databasePath );
2563 } );
2564 }
2565
2566 return functionResult;
2567}
2568
2569static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2570{
2571 QString concat;
2572 for ( const QVariant &value : values )
2573 {
2574 if ( !QgsVariantUtils::isNull( value ) )
2575 concat += QgsExpressionUtils::getStringValue( value, parent );
2576 }
2577 return concat;
2578}
2579
2580static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2581{
2582 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2583 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2584}
2585
2586static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2587{
2588 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2589 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2590 return string.right( pos );
2591}
2592
2593static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2594{
2595 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2596 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2597 return string.left( pos );
2598}
2599
2600static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2601{
2602 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2603 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2604 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2605 return string.leftJustified( length, fill.at( 0 ), true );
2606}
2607
2608static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2609{
2610 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2611 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2612 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2613 return string.rightJustified( length, fill.at( 0 ), true );
2614}
2615
2616static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2617{
2618 if ( values.size() < 1 )
2619 {
2620 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2621 return QVariant();
2622 }
2623
2624 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2625 for ( int n = 1; n < values.length(); n++ )
2626 {
2627 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2628 }
2629 return string;
2630}
2631
2632
2633static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2634{
2635 return QVariant( QDateTime::currentDateTime() );
2636}
2637
2638static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2639{
2640 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2641 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2642 if ( format.isEmpty() && !language.isEmpty() )
2643 {
2644 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2645 return QVariant( QDate() );
2646 }
2647
2648 if ( format.isEmpty() && language.isEmpty() )
2649 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2650
2651 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2652 QLocale locale = QLocale();
2653 if ( !language.isEmpty() )
2654 {
2655 locale = QLocale( language );
2656 }
2657
2658 QDate date = locale.toDate( datestring, format );
2659 if ( !date.isValid() )
2660 {
2661 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2662 date = QDate();
2663 }
2664 return QVariant( date );
2665}
2666
2667static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2668{
2669 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2670 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2671 if ( format.isEmpty() && !language.isEmpty() )
2672 {
2673 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
2674 return QVariant( QTime() );
2675 }
2676
2677 if ( format.isEmpty() && language.isEmpty() )
2678 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
2679
2680 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2681 QLocale locale = QLocale();
2682 if ( !language.isEmpty() )
2683 {
2684 locale = QLocale( language );
2685 }
2686
2687 QTime time = locale.toTime( timestring, format );
2688 if ( !time.isValid() )
2689 {
2690 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
2691 time = QTime();
2692 }
2693 return QVariant( time );
2694}
2695
2696static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2697{
2698 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
2699}
2700
2701/*
2702 * DMS functions
2703 */
2704
2705static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2706{
2707 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2708 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2709 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2710
2711 QString formatString;
2712 if ( values.count() > 3 )
2713 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
2714
2716 if ( formatString.compare( QLatin1String( "suffix" ), Qt::CaseInsensitive ) == 0 )
2717 {
2719 }
2720 else if ( formatString.compare( QLatin1String( "aligned" ), Qt::CaseInsensitive ) == 0 )
2721 {
2723 }
2724 else if ( ! formatString.isEmpty() )
2725 {
2726 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
2727 return QVariant();
2728 }
2729
2730 if ( axis.compare( QLatin1String( "x" ), Qt::CaseInsensitive ) == 0 )
2731 {
2732 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
2733 }
2734 else if ( axis.compare( QLatin1String( "y" ), Qt::CaseInsensitive ) == 0 )
2735 {
2736 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
2737 }
2738 else
2739 {
2740 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
2741 return QVariant();
2742 }
2743}
2744
2745static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2746{
2748 return floatToDegreeFormat( format, values, context, parent, node );
2749}
2750
2751static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2752{
2753 double value = 0.0;
2754 bool ok = false;
2755 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
2756
2757 return ok ? QVariant( value ) : QVariant();
2758}
2759
2760static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2761{
2763 return floatToDegreeFormat( format, values, context, parent, node );
2764}
2765
2766static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2767{
2768 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2769 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
2770 qint64 seconds = d2.secsTo( d1 );
2771 return QVariant::fromValue( QgsInterval( seconds ) );
2772}
2773
2774static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2775{
2776 if ( !values.at( 0 ).canConvert<QDate>() )
2777 return QVariant();
2778
2779 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
2780 if ( !date.isValid() )
2781 return QVariant();
2782
2783 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
2784 // (to match PostgreSQL behavior)
2785 return date.dayOfWeek() % 7;
2786}
2787
2788static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2789{
2790 QVariant value = values.at( 0 );
2791 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2792 if ( inter.isValid() )
2793 {
2794 return QVariant( inter.days() );
2795 }
2796 else
2797 {
2798 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2799 return QVariant( d1.date().day() );
2800 }
2801}
2802
2803static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2804{
2805 QVariant value = values.at( 0 );
2806 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2807 if ( inter.isValid() )
2808 {
2809 return QVariant( inter.years() );
2810 }
2811 else
2812 {
2813 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2814 return QVariant( d1.date().year() );
2815 }
2816}
2817
2818static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2819{
2820 QVariant value = values.at( 0 );
2821 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2822 if ( inter.isValid() )
2823 {
2824 return QVariant( inter.months() );
2825 }
2826 else
2827 {
2828 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2829 return QVariant( d1.date().month() );
2830 }
2831}
2832
2833static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2834{
2835 QVariant value = values.at( 0 );
2836 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2837 if ( inter.isValid() )
2838 {
2839 return QVariant( inter.weeks() );
2840 }
2841 else
2842 {
2843 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2844 return QVariant( d1.date().weekNumber() );
2845 }
2846}
2847
2848static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2849{
2850 QVariant value = values.at( 0 );
2851 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2852 if ( inter.isValid() )
2853 {
2854 return QVariant( inter.hours() );
2855 }
2856 else
2857 {
2858 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2859 return QVariant( t1.hour() );
2860 }
2861}
2862
2863static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2864{
2865 QVariant value = values.at( 0 );
2866 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2867 if ( inter.isValid() )
2868 {
2869 return QVariant( inter.minutes() );
2870 }
2871 else
2872 {
2873 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2874 return QVariant( t1.minute() );
2875 }
2876}
2877
2878static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2879{
2880 QVariant value = values.at( 0 );
2881 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2882 if ( inter.isValid() )
2883 {
2884 return QVariant( inter.seconds() );
2885 }
2886 else
2887 {
2888 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2889 return QVariant( t1.second() );
2890 }
2891}
2892
2893static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2894{
2895 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2896 if ( dt.isValid() )
2897 {
2898 return QVariant( dt.toMSecsSinceEpoch() );
2899 }
2900 else
2901 {
2902 return QVariant();
2903 }
2904}
2905
2906static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2907{
2908 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
2909 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
2910 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
2911}
2912
2913static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2914{
2915 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
2916 if ( parent->hasEvalError() )
2917 {
2918 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif" ) ) );
2919 return QVariant();
2920 }
2921 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2922 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
2923}
2924
2925static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2927 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
2928 if ( parent->hasEvalError() )
2929 {
2930 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif_geotag" ) ) );
2931 return QVariant();
2932 }
2933 bool ok;
2934 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
2935}
2936
2937#define ENSURE_GEOM_TYPE(f, g, geomtype) \
2938 if ( !(f).hasGeometry() ) \
2939 return QVariant(); \
2940 QgsGeometry g = (f).geometry(); \
2941 if ( (g).type() != (geomtype) ) \
2942 return QVariant();
2943
2944static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2945{
2946 FEAT_FROM_CONTEXT( context, f )
2948 if ( g.isMultipart() )
2949 {
2950 return g.asMultiPoint().at( 0 ).x();
2951 }
2952 else
2953 {
2954 return g.asPoint().x();
2955 }
2956}
2957
2958static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2959{
2960 FEAT_FROM_CONTEXT( context, f )
2962 if ( g.isMultipart() )
2963 {
2964 return g.asMultiPoint().at( 0 ).y();
2965 }
2966 else
2967 {
2968 return g.asPoint().y();
2969 }
2970}
2971
2972static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2973{
2974 FEAT_FROM_CONTEXT( context, f )
2976
2977 if ( g.isEmpty() )
2978 return QVariant();
2979
2980 const QgsAbstractGeometry *abGeom = g.constGet();
2981
2982 if ( g.isEmpty() || !abGeom->is3D() )
2983 return QVariant();
2984
2985 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
2986 {
2987 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
2988 if ( point )
2989 return point->z();
2990 }
2991 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
2992 {
2993 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
2994 {
2995 if ( collection->numGeometries() > 0 )
2996 {
2997 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
2998 return point->z();
2999 }
3000 }
3001 }
3002
3003 return QVariant();
3004}
3005
3006static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3007{
3008 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3009 if ( geom.isNull() )
3010 return QVariant();
3011
3012 bool isValid = geom.isGeosValid();
3013
3014 return QVariant( isValid );
3015}
3016
3017static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3018{
3019 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3020 if ( geom.isNull() )
3021 return QVariant();
3022
3023 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
3024#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
3026#else
3028#endif
3029 if ( methodString.compare( QLatin1String( "linework" ), Qt::CaseInsensitive ) == 0 )
3031 else if ( methodString.compare( QLatin1String( "structure" ), Qt::CaseInsensitive ) == 0 )
3033
3034 const bool keepCollapsed = values.value( 2 ).toBool();
3035
3036 QgsGeometry valid;
3037 try
3038 {
3039 valid = geom.makeValid( method, keepCollapsed );
3040 }
3041 catch ( QgsNotSupportedException & )
3042 {
3043 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
3044 return QVariant();
3045 }
3046
3047 return QVariant::fromValue( valid );
3048}
3049
3050static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3051{
3052 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3053 if ( geom.isNull() )
3054 return QVariant();
3055
3056 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
3057 QVariantList array;
3058 for ( int i = 0; i < multiGeom.size(); ++i )
3059 {
3060 array += QVariant::fromValue( multiGeom.at( i ) );
3061 }
3062
3063 return array;
3064}
3065
3066static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3067{
3068 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3069 if ( geom.isNull() )
3070 return QVariant();
3071
3072 //if single point, return the point's x coordinate
3073 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3074 {
3075 return geom.asPoint().x();
3076 }
3077
3078 //otherwise return centroid x
3079 QgsGeometry centroid = geom.centroid();
3080 QVariant result( centroid.asPoint().x() );
3081 return result;
3082}
3083
3084static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3085{
3086 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3087 if ( geom.isNull() )
3088 return QVariant();
3089
3090 //if single point, return the point's y coordinate
3091 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3092 {
3093 return geom.asPoint().y();
3094 }
3095
3096 //otherwise return centroid y
3097 QgsGeometry centroid = geom.centroid();
3098 QVariant result( centroid.asPoint().y() );
3099 return result;
3100}
3101
3102static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3103{
3104 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3105 if ( geom.isNull() )
3106 return QVariant(); //or 0?
3107
3108 if ( !geom.constGet()->is3D() )
3109 return QVariant();
3110
3111 //if single point, return the point's z coordinate
3112 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3113 {
3114 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3115 if ( point )
3116 return point->z();
3117 }
3118 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3119 {
3120 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3121 {
3122 if ( collection->numGeometries() == 1 )
3123 {
3124 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3125 return point->z();
3126 }
3127 }
3128 }
3129
3130 return QVariant();
3131}
3132
3133static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3134{
3135 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3136 if ( geom.isNull() )
3137 return QVariant(); //or 0?
3138
3139 if ( !geom.constGet()->isMeasure() )
3140 return QVariant();
3141
3142 //if single point, return the point's m value
3143 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3144 {
3145 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3146 if ( point )
3147 return point->m();
3148 }
3149 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3150 {
3151 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3152 {
3153 if ( collection->numGeometries() == 1 )
3154 {
3155 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3156 return point->m();
3157 }
3158 }
3159 }
3160
3161 return QVariant();
3162}
3163
3164static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3165{
3166 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3167
3168 if ( geom.isNull() )
3169 return QVariant();
3170
3171 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3172
3173 if ( idx < 0 )
3174 {
3175 //negative idx
3176 int count = geom.constGet()->nCoordinates();
3177 idx = count + idx;
3178 }
3179 else
3180 {
3181 //positive idx is 1 based
3182 idx -= 1;
3183 }
3184
3185 QgsVertexId vId;
3186 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3187 {
3188 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3189 return QVariant();
3190 }
3191
3192 QgsPoint point = geom.constGet()->vertexAt( vId );
3193 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3194}
3195
3196static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3197{
3198 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3199
3200 if ( geom.isNull() )
3201 return QVariant();
3202
3203 QgsVertexId vId;
3204 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3205 {
3206 return QVariant();
3207 }
3208
3209 QgsPoint point = geom.constGet()->vertexAt( vId );
3210 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3211}
3212
3213static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3214{
3215 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3216
3217 if ( geom.isNull() )
3218 return QVariant();
3219
3220 QgsVertexId vId;
3221 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3222 {
3223 return QVariant();
3224 }
3225
3226 QgsPoint point = geom.constGet()->vertexAt( vId );
3227 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3228}
3229
3230static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3231{
3232 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3233
3234 if ( geom.isNull() )
3235 return QVariant();
3236
3237 bool ignoreClosing = false;
3238 if ( values.length() > 1 )
3239 {
3240 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3241 }
3242
3243 QgsMultiPoint *mp = new QgsMultiPoint();
3244
3245 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3246 for ( const QgsRingSequence &part : sequence )
3247 {
3248 for ( const QgsPointSequence &ring : part )
3249 {
3250 bool skipLast = false;
3251 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3252 {
3253 skipLast = true;
3254 }
3255
3256 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i )
3257 {
3258 mp->addGeometry( ring.at( i ).clone() );
3259 }
3260 }
3261 }
3262
3263 return QVariant::fromValue( QgsGeometry( mp ) );
3264}
3265
3266static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3267{
3268 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3269
3270 if ( geom.isNull() )
3271 return QVariant();
3272
3273 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3274
3275 //OK, now we have a complete list of segmentized lines from the geometry
3277 for ( QgsLineString *line : linesToProcess )
3278 {
3279 for ( int i = 0; i < line->numPoints() - 1; ++i )
3280 {
3282 segment->setPoints( QgsPointSequence()
3283 << line->pointN( i )
3284 << line->pointN( i + 1 ) );
3285 ml->addGeometry( segment );
3286 }
3287 delete line;
3288 }
3289
3290 return QVariant::fromValue( QgsGeometry( ml ) );
3291}
3292
3293static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3294{
3295 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3296
3297 if ( geom.isNull() )
3298 return QVariant();
3299
3300 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
3301 if ( !curvePolygon && geom.isMultipart() )
3302 {
3303 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3304 {
3305 if ( collection->numGeometries() == 1 )
3306 {
3307 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3308 }
3309 }
3310 }
3311
3312 if ( !curvePolygon )
3313 return QVariant();
3314
3315 //idx is 1 based
3316 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3317
3318 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3319 return QVariant();
3320
3321 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3322 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3323 return result;
3324}
3325
3326static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3327{
3328 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3329
3330 if ( geom.isNull() )
3331 return QVariant();
3332
3333 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
3334 if ( !collection )
3335 return QVariant();
3336
3337 //idx is 1 based
3338 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3339
3340 if ( idx < 0 || idx >= collection->numGeometries() )
3341 return QVariant();
3342
3343 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3344 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3345 return result;
3346}
3347
3348static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3349{
3350 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3351
3352 if ( geom.isNull() )
3353 return QVariant();
3354
3355 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3356 if ( !boundary )
3357 return QVariant();
3358
3359 return QVariant::fromValue( QgsGeometry( boundary ) );
3360}
3361
3362static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3363{
3364 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3365
3366 if ( geom.isNull() )
3367 return QVariant();
3368
3369 QgsGeometry merged = geom.mergeLines();
3370 if ( merged.isNull() )
3371 return QVariant();
3372
3373 return QVariant::fromValue( merged );
3374}
3375
3376static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3377{
3378 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3379 if ( geom.isNull() )
3380 return QVariant();
3381
3382 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3383 if ( geom2.isNull() )
3384 return QVariant();
3385
3386 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
3387 if ( sharedPaths.isNull() )
3388 return QVariant();
3389
3390 return QVariant::fromValue( sharedPaths );
3391}
3392
3393
3394static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3395{
3396 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3397
3398 if ( geom.isNull() )
3399 return QVariant();
3400
3401 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3402
3403 QgsGeometry simplified = geom.simplify( tolerance );
3404 if ( simplified.isNull() )
3405 return QVariant();
3406
3407 return simplified;
3408}
3409
3410static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3411{
3412 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3413
3414 if ( geom.isNull() )
3415 return QVariant();
3416
3417 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3418
3420
3421 QgsGeometry simplified = simplifier.simplify( geom );
3422 if ( simplified.isNull() )
3423 return QVariant();
3424
3425 return simplified;
3426}
3427
3428static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3429{
3430 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3431
3432 if ( geom.isNull() )
3433 return QVariant();
3434
3435 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
3436 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
3437 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3438 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
3439
3440 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
3441 if ( smoothed.isNull() )
3442 return QVariant();
3443
3444 return smoothed;
3445}
3446
3447static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3448{
3449 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3450
3451 if ( geom.isNull() )
3452 return QVariant();
3453
3454 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3455 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3456 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3457
3458 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
3459 if ( waved.isNull() )
3460 return QVariant();
3461
3462 return waved;
3463}
3464
3465static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3466{
3467 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3468
3469 if ( geom.isNull() )
3470 return QVariant();
3471
3472 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3473 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3474 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3475 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3476 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3477
3478 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength,
3479 minAmplitude, maxAmplitude, seed );
3480 if ( waved.isNull() )
3481 return QVariant();
3482
3483 return waved;
3484}
3485
3486static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3487{
3488 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3489
3490 if ( geom.isNull() )
3491 return QVariant();
3492
3493 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3494 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3495 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3496
3497 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
3498 if ( waved.isNull() )
3499 return QVariant();
3500
3501 return waved;
3502}
3503
3504static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3505{
3506 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3507
3508 if ( geom.isNull() )
3509 return QVariant();
3510
3511 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3512 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3513 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3514 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3515 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3516
3517 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength,
3518 minAmplitude, maxAmplitude, seed );
3519 if ( waved.isNull() )
3520 return QVariant();
3521
3522 return waved;
3523}
3524
3525static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3526{
3527 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3528
3529 if ( geom.isNull() )
3530 return QVariant();
3531
3532 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3533 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3534 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3535
3536 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
3537 if ( waved.isNull() )
3538 return QVariant();
3539
3540 return waved;
3541}
3542
3543static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3544{
3545 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3546
3547 if ( geom.isNull() )
3548 return QVariant();
3549
3550 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3551 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3552 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3553 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3554 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3555
3556 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength,
3557 minAmplitude, maxAmplitude, seed );
3558 if ( waved.isNull() )
3559 return QVariant();
3560
3561 return waved;
3562}
3563
3564static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3565{
3566 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3567
3568 if ( geom.isNull() )
3569 return QVariant();
3570
3571 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
3572 QVector< double > dashPattern;
3573 dashPattern.reserve( pattern.size() );
3574 for ( const QVariant &value : std::as_const( pattern ) )
3575 {
3576 bool ok = false;
3577 double v = value.toDouble( &ok );
3578 if ( ok )
3579 {
3580 dashPattern << v;
3581 }
3582 else
3583 {
3584 parent->setEvalErrorString( QStringLiteral( "Dash pattern must be an array of numbers" ) );
3585 return QgsGeometry();
3586 }
3587 }
3588
3589 if ( dashPattern.size() % 2 != 0 )
3590 {
3591 parent->setEvalErrorString( QStringLiteral( "Dash pattern must contain an even number of elements" ) );
3592 return QgsGeometry();
3593 }
3594
3595 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
3597 if ( startRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3599 else if ( startRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3601 else if ( startRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3603 else if ( startRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3605 else if ( startRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3607 else
3608 {
3609 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( startRuleString ) );
3610 return QgsGeometry();
3611 }
3612
3613 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
3615 if ( endRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3617 else if ( endRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3619 else if ( endRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3621 else if ( endRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3623 else if ( endRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3625 else
3626 {
3627 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( endRuleString ) );
3628 return QgsGeometry();
3629 }
3630
3631 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
3633 if ( adjustString.compare( QLatin1String( "both" ), Qt::CaseInsensitive ) == 0 )
3635 else if ( adjustString.compare( QLatin1String( "dash" ), Qt::CaseInsensitive ) == 0 )
3637 else if ( adjustString.compare( QLatin1String( "gap" ), Qt::CaseInsensitive ) == 0 )
3639 else
3640 {
3641 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern size adjustment" ).arg( adjustString ) );
3642 return QgsGeometry();
3643 }
3644
3645 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
3646
3647 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
3648 if ( result.isNull() )
3649 return QVariant();
3650
3651 return result;
3652}
3653
3654static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3655{
3656 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3657
3658 if ( geom.isNull() )
3659 return QVariant();
3660
3661 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3662 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
3663 if ( densified.isNull() )
3664 return QVariant();
3665
3666 return densified;
3667}
3668
3669static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3670{
3671 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3672
3673 if ( geom.isNull() )
3674 return QVariant();
3675
3676 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3677 const QgsGeometry densified = geom.densifyByDistance( distance );
3678 if ( densified.isNull() )
3679 return QVariant();
3680
3681 return densified;
3682}
3683
3684static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3685{
3686 QVariantList list;
3687 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
3688 {
3689 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
3690 }
3691 else
3692 {
3693 list = values;
3694 }
3695
3696 QVector< QgsGeometry > parts;
3697 parts.reserve( list.size() );
3698 for ( const QVariant &value : std::as_const( list ) )
3699 {
3700 QgsGeometry part = QgsExpressionUtils::getGeometry( value, parent );
3701 if ( part.isNull() )
3702 return QgsGeometry();
3703 parts << part;
3704 }
3705
3706 return QgsGeometry::collectGeometry( parts );
3707}
3708
3709static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3710{
3711 if ( values.count() < 2 || values.count() > 4 )
3712 {
3713 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
3714 return QVariant();
3715 }
3716
3717 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3718 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3719 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
3720 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
3721 switch ( values.count() )
3722 {
3723 case 2:
3724 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
3725 case 3:
3726 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
3727 case 4:
3728 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
3729 }
3730 return QVariant(); //avoid warning
3731}
3732
3733static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3734{
3735 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3736 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3737 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3738 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
3739}
3740
3741static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3742{
3743 if ( values.empty() )
3744 {
3745 return QVariant();
3746 }
3747
3748 QVector<QgsPoint> points;
3749 points.reserve( values.count() );
3750
3751 auto addPoint = [&points]( const QgsGeometry & geom )
3752 {
3753 if ( geom.isNull() )
3754 return;
3755
3756 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3757 return;
3758
3759 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3760 if ( !point )
3761 return;
3762
3763 points << *point;
3764 };
3765
3766 for ( const QVariant &value : values )
3767 {
3768 if ( value.userType() == QMetaType::Type::QVariantList )
3769 {
3770 const QVariantList list = value.toList();
3771 for ( const QVariant &v : list )
3772 {
3773 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
3774 }
3775 }
3776 else
3777 {
3778 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
3779 }
3780 }
3781
3782 if ( points.count() < 2 )
3783 return QVariant();
3784
3785 return QgsGeometry( new QgsLineString( points ) );
3786}
3787
3788static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3789{
3790 if ( values.count() < 1 )
3791 {
3792 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
3793 return QVariant();
3794 }
3795
3796 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3797
3798 if ( outerRing.type() == Qgis::GeometryType::Polygon )
3799 return outerRing; // if it's already a polygon we have nothing to do
3800
3801 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
3802 return QVariant();
3803
3804 std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
3805
3806 const QgsCurve *exteriorRing = qgsgeometry_cast< QgsCurve * >( outerRing.constGet() );
3807 if ( !exteriorRing && outerRing.isMultipart() )
3808 {
3809 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( outerRing.constGet() ) )
3810 {
3811 if ( collection->numGeometries() == 1 )
3812 {
3813 exteriorRing = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
3814 }
3815 }
3816 }
3817
3818 if ( !exteriorRing )
3819 return QVariant();
3820
3821 polygon->setExteriorRing( exteriorRing->segmentize() );
3822
3823
3824 for ( int i = 1; i < values.count(); ++i )
3825 {
3826 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
3827 if ( ringGeom.isNull() )
3828 continue;
3829
3830 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
3831 continue;
3832
3833 const QgsCurve *ring = qgsgeometry_cast< QgsCurve * >( ringGeom.constGet() );
3834 if ( !ring && ringGeom.isMultipart() )
3835 {
3836 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( ringGeom.constGet() ) )
3837 {
3838 if ( collection->numGeometries() == 1 )
3839 {
3840 ring = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
3841 }
3842 }
3843 }
3844
3845 if ( !ring )
3846 continue;
3847
3848 polygon->addInteriorRing( ring->segmentize() );
3849 }
3850
3851 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
3852}
3853
3854static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3855{
3856 std::unique_ptr<QgsTriangle> tr( new QgsTriangle() );
3857 std::unique_ptr<QgsLineString> lineString( new QgsLineString() );
3858 lineString->clear();
3859
3860 for ( const QVariant &value : values )
3861 {
3862 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
3863 if ( geom.isNull() )
3864 return QVariant();
3865
3866 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3867 return QVariant();
3868
3869 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3870 if ( !point && geom.isMultipart() )
3871 {
3872 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3873 {
3874 if ( collection->numGeometries() == 1 )
3875 {
3876 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3877 }
3878 }
3879 }
3880
3881 if ( !point )
3882 return QVariant();
3883
3884 lineString->addVertex( *point );
3885 }
3886
3887 tr->setExteriorRing( lineString.release() );
3888
3889 return QVariant::fromValue( QgsGeometry( tr.release() ) );
3890}
3891
3892static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3893{
3894 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3895 if ( geom.isNull() )
3896 return QVariant();
3897
3898 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3899 return QVariant();
3900
3901 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3902 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3903
3904 if ( segment < 3 )
3905 {
3906 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3907 return QVariant();
3908 }
3909 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3910 if ( !point && geom.isMultipart() )
3911 {
3912 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3913 {
3914 if ( collection->numGeometries() == 1 )
3915 {
3916 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3917 }
3918 }
3919 }
3920 if ( !point )
3921 return QVariant();
3922
3923 QgsCircle circ( *point, radius );
3924 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3925}
3926
3927static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3928{
3929 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3930 if ( geom.isNull() )
3931 return QVariant();
3932
3933 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3934 return QVariant();
3935
3936 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3937 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3938 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3939 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
3940 if ( segment < 3 )
3941 {
3942 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3943 return QVariant();
3944 }
3945 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3946 if ( !point && geom.isMultipart() )
3947 {
3948 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3949 {
3950 if ( collection->numGeometries() == 1 )
3951 {
3952 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3953 }
3954 }
3955 }
3956 if ( !point )
3957 return QVariant();
3958
3959 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
3960 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3961}
3962
3963static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3964{
3965
3966 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3967 if ( pt1.isNull() )
3968 return QVariant();
3969
3970 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
3971 return QVariant();
3972
3973 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3974 if ( pt2.isNull() )
3975 return QVariant();
3976
3977 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
3978 return QVariant();
3979
3980 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
3981 if ( nbEdges < 3 )
3982 {
3983 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
3984 return QVariant();
3985 }
3986
3987 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3989 {
3990 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
3991 return QVariant();
3992 }
3993
3994 const QgsPoint *center = qgsgeometry_cast< const QgsPoint * >( pt1.constGet() );
3995 if ( !center && pt1.isMultipart() )
3996 {
3997 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt1.constGet() ) )
3998 {
3999 if ( collection->numGeometries() == 1 )
4000 {
4001 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4002 }
4003 }
4004 }
4005 if ( !center )
4006 return QVariant();
4007
4008 const QgsPoint *corner = qgsgeometry_cast< const QgsPoint * >( pt2.constGet() );
4009 if ( !corner && pt2.isMultipart() )
4010 {
4011 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt2.constGet() ) )
4012 {
4013 if ( collection->numGeometries() == 1 )
4014 {
4015 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4016 }
4017 }
4018 }
4019 if ( !corner )
4020 return QVariant();
4021
4022 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
4023
4024 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
4025
4026}
4027
4028static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4029{
4030 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4031 if ( pt1.isNull() )
4032 return QVariant();
4033 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4034 return QVariant();
4035
4036 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4037 if ( pt2.isNull() )
4038 return QVariant();
4039 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4040 return QVariant();
4041
4042 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4043 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4044 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
4045
4046 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
4047}
4048
4049static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4050{
4051 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4052 if ( pt1.isNull() )
4053 return QVariant();
4054 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4055 return QVariant();
4056
4057 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4058 if ( pt2.isNull() )
4059 return QVariant();
4060 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4061 return QVariant();
4062
4063 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
4064 if ( pt3.isNull() )
4065 return QVariant();
4066 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
4067 return QVariant();
4068
4069 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4070 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
4071 {
4072 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
4073 return QVariant();
4074 }
4075 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4076 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4077 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
4078 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4079 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4080}
4081
4082static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4083{
4084 if ( geom.isNull() )
4085 return QVariant();
4086
4087 if ( idx < 0 )
4088 {
4089 idx += geom.constGet()->nCoordinates();
4090 }
4091 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4092 {
4093 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4094 return QVariant();
4095 }
4096 return QVariant::fromValue( geom.vertexAt( idx ) );
4097}
4098
4099// function used for the old $ style
4100static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4101{
4102 FEAT_FROM_CONTEXT( context, feature )
4103 const QgsGeometry geom = feature.geometry();
4104 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4105
4106 const QVariant v = pointAt( geom, idx, parent );
4107
4108 if ( !v.isNull() )
4109 return QVariant( v.value<QgsPoint>().x() );
4110 else
4111 return QVariant();
4112}
4113static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4114{
4115 if ( values.at( 1 ).isNull() && !values.at( 0 ).isNull() ) // the case where the alias x_at function is called like a $ function (x_at(i))
4116 {
4117 return fcnOldXat( values, f, parent, node );
4118 }
4119 else if ( values.at( 0 ).isNull() && !values.at( 1 ).isNull() ) // same as above with x_at(i:=0) (vertex value is at the second position)
4120 {
4121 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4122 }
4123
4124 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4125 if ( geom.isNull() )
4126 {
4127 return QVariant();
4128 }
4129
4130 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4131
4132 const QVariant v = pointAt( geom, vertexNumber, parent );
4133 if ( !v.isNull() )
4134 return QVariant( v.value<QgsPoint>().x() );
4135 else
4136 return QVariant();
4137}
4138
4139// function used for the old $ style
4140static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4141{
4142 FEAT_FROM_CONTEXT( context, feature )
4143 const QgsGeometry geom = feature.geometry();
4144 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4145
4146 const QVariant v = pointAt( geom, idx, parent );
4147
4148 if ( !v.isNull() )
4149 return QVariant( v.value<QgsPoint>().y() );
4150 else
4151 return QVariant();
4152}
4153static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4154{
4155 if ( values.at( 1 ).isNull() && !values.at( 0 ).isNull() ) // the case where the alias y_at function is called like a $ function (y_at(i))
4156 {
4157 return fcnOldYat( values, f, parent, node );
4158 }
4159 else if ( values.at( 0 ).isNull() && !values.at( 1 ).isNull() ) // same as above with x_at(i:=0) (vertex value is at the second position)
4160 {
4161 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4162 }
4163
4164 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4165 if ( geom.isNull() )
4166 {
4167 return QVariant();
4168 }
4169
4170 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4171
4172 const QVariant v = pointAt( geom, vertexNumber, parent );
4173 if ( !v.isNull() )
4174 return QVariant( v.value<QgsPoint>().y() );
4175 else
4176 return QVariant();
4177}
4178
4179static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4180{
4181 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4182 if ( geom.isNull() )
4183 {
4184 return QVariant();
4185 }
4186
4187 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4188
4189 const QVariant v = pointAt( geom, vertexNumber, parent );
4190 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4191 return QVariant( v.value<QgsPoint>().z() );
4192 else
4193 return QVariant();
4194}
4195
4196static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4197{
4198 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4199 if ( geom.isNull() )
4200 {
4201 return QVariant();
4202 }
4203
4204 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4205
4206 const QVariant v = pointAt( geom, vertexNumber, parent );
4207 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4208 return QVariant( v.value<QgsPoint>().m() );
4209 else
4210 return QVariant();
4211}
4212
4213
4214static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
4215{
4216 if ( !context )
4217 return QVariant();
4218
4219 // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
4220 if ( context->hasGeometry() )
4221 return context->geometry();
4222 else
4223 {
4224 FEAT_FROM_CONTEXT( context, f )
4225 QgsGeometry geom = f.geometry();
4226 if ( !geom.isNull() )
4227 return QVariant::fromValue( geom );
4228 else
4229 return QVariant();
4230 }
4231}
4232
4233static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4234{
4235 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4236 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4237 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4238 return result;
4239}
4240
4241static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4242{
4243 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4244 if ( wkb.isNull() )
4245 return QVariant();
4246
4247 QgsGeometry geom;
4248 geom.fromWkb( wkb );
4249 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4250}
4251
4252static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4253{
4254 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4255 QgsOgcUtils::Context ogcContext;
4256 if ( context )
4257 {
4258 QgsWeakMapLayerPointer mapLayerPtr {context->variable( QStringLiteral( "layer" ) ).value<QgsWeakMapLayerPointer>() };
4259 if ( mapLayerPtr )
4260 {
4261 ogcContext.layer = mapLayerPtr.data();
4262 ogcContext.transformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
4263 }
4264 }
4265 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4266 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4267 return result;
4268}
4269
4270static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4271{
4272 FEAT_FROM_CONTEXT( context, f )
4274 QgsDistanceArea *calc = parent->geomCalculator();
4275 if ( calc )
4276 {
4277 try
4278 {
4279 double area = calc->measureArea( f.geometry() );
4280 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4281 return QVariant( area );
4282 }
4283 catch ( QgsCsException & )
4284 {
4285 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating area" ) );
4286 return QVariant();
4287 }
4288 }
4289 else
4290 {
4291 return QVariant( f.geometry().area() );
4292 }
4293}
4294
4295static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4296{
4297 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4298
4299 if ( geom.type() != Qgis::GeometryType::Polygon )
4300 return QVariant();
4301
4302 return QVariant( geom.area() );
4303}
4304
4305static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4306{
4307 FEAT_FROM_CONTEXT( context, f )
4309 QgsDistanceArea *calc = parent->geomCalculator();
4310 if ( calc )
4311 {
4312 try
4313 {
4314 double len = calc->measureLength( f.geometry() );
4315 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4316 return QVariant( len );
4317 }
4318 catch ( QgsCsException & )
4319 {
4320 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating length" ) );
4321 return QVariant();
4322 }
4323 }
4324 else
4325 {
4326 return QVariant( f.geometry().length() );
4327 }
4328}
4329
4330static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4331{
4332 FEAT_FROM_CONTEXT( context, f )
4334 QgsDistanceArea *calc = parent->geomCalculator();
4335 if ( calc )
4336 {
4337 try
4338 {
4339 double len = calc->measurePerimeter( f.geometry() );
4340 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4341 return QVariant( len );
4342 }
4343 catch ( QgsCsException & )
4344 {
4345 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating perimeter" ) );
4346 return QVariant();
4347 }
4348 }
4349 else
4350 {
4351 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4352 }
4353}
4354
4355static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4356{
4357 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4358
4359 if ( geom.type() != Qgis::GeometryType::Polygon )
4360 return QVariant();
4361
4362 //length for polygons = perimeter
4363 return QVariant( geom.length() );
4364}
4365
4366static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4367{
4368 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4369 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4370}
4371
4372static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4373{
4374 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4375 if ( geom.isNull() )
4376 return QVariant();
4377
4378 return QVariant( geom.constGet()->partCount() );
4379}
4380
4381static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4382{
4383 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4384 if ( geom.isNull() )
4385 return QVariant();
4386
4387 return QVariant( geom.isMultipart() );
4388}
4389
4390static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4391{
4392 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4393
4394 if ( geom.isNull() )
4395 return QVariant();
4396
4397 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
4398 if ( curvePolygon )
4399 return QVariant( curvePolygon->numInteriorRings() );
4400
4401 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
4402 if ( collection )
4403 {
4404 //find first CurvePolygon in collection
4405 for ( int i = 0; i < collection->numGeometries(); ++i )
4406 {
4407 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4408 if ( !curvePolygon )
4409 continue;
4410
4411 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
4412 }
4413 }
4414
4415 return QVariant();
4416}
4417
4418static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4419{
4420 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4421
4422 if ( geom.isNull() )
4423 return QVariant();
4424
4425 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
4426 if ( curvePolygon )
4427 return QVariant( curvePolygon->ringCount() );
4428
4429 bool foundPoly = false;
4430 int ringCount = 0;
4431 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
4432 if ( collection )
4433 {
4434 //find CurvePolygons in collection
4435 for ( int i = 0; i < collection->numGeometries(); ++i )
4436 {
4437 curvePolygon = qgsgeometry_cast< QgsCurvePolygon *>( collection->geometryN( i ) );
4438 if ( !curvePolygon )
4439 continue;
4440
4441 foundPoly = true;
4442 ringCount += curvePolygon->ringCount();
4443 }
4444 }
4445
4446 if ( !foundPoly )
4447 return QVariant();
4448
4449 return QVariant( ringCount );
4450}
4451
4452static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4453{
4454 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4455 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
4456 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
4457 return result;
4458}
4459
4460static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4461{
4462 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4463 return QVariant::fromValue( geom.boundingBox().width() );
4464}
4465
4466static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4467{
4468 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4469 return QVariant::fromValue( geom.boundingBox().height() );
4470}
4471
4472static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4473{
4474 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4475 if ( geom.isNull() )
4476 return QVariant();
4477
4479}
4480
4481static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4482{
4483 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4484 return QVariant::fromValue( geom.boundingBox().xMinimum() );
4485}
4486
4487static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4488{
4489 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4490 return QVariant::fromValue( geom.boundingBox().xMaximum() );
4491}
4492
4493static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4494{
4495 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4496 return QVariant::fromValue( geom.boundingBox().yMinimum() );
4497}
4498
4499static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4500{
4501 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4502 return QVariant::fromValue( geom.boundingBox().yMaximum() );
4503}
4504
4505static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4506{
4507 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4508
4509 if ( geom.isNull() || geom.isEmpty( ) )
4510 return QVariant();
4511
4512 if ( !geom.constGet()->is3D() )
4513 return QVariant();
4514
4515 double max = std::numeric_limits< double >::lowest();
4516
4517 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4518 {
4519 double z = ( *it ).z();
4520
4521 if ( max < z )
4522 max = z;
4523 }
4524
4525 if ( max == std::numeric_limits< double >::lowest() )
4526 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4527
4528 return QVariant( max );
4529}
4530
4531static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4532{
4533 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4534
4535 if ( geom.isNull() || geom.isEmpty() )
4536 return QVariant();
4537
4538 if ( !geom.constGet()->is3D() )
4539 return QVariant();
4540
4541 double min = std::numeric_limits< double >::max();
4542
4543 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4544 {
4545 double z = ( *it ).z();
4546
4547 if ( z < min )
4548 min = z;
4549 }
4550
4551 if ( min == std::numeric_limits< double >::max() )
4552 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4553
4554 return QVariant( min );
4555}
4556
4557static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4558{
4559 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4560
4561 if ( geom.isNull() || geom.isEmpty() )
4562 return QVariant();
4563
4564 if ( !geom.constGet()->isMeasure() )
4565 return QVariant();
4566
4567 double min = std::numeric_limits< double >::max();
4568
4569 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4570 {
4571 double m = ( *it ).m();
4572
4573 if ( m < min )
4574 min = m;
4575 }
4576
4577 if ( min == std::numeric_limits< double >::max() )
4578 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4579
4580 return QVariant( min );
4581}
4582
4583static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4584{
4585 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4586
4587 if ( geom.isNull() || geom.isEmpty() )
4588 return QVariant();
4589
4590 if ( !geom.constGet()->isMeasure() )
4591 return QVariant();
4592
4593 double max = std::numeric_limits< double >::lowest();
4594
4595 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4596 {
4597 double m = ( *it ).m();
4598
4599 if ( max < m )
4600 max = m;
4601 }
4602
4603 if ( max == std::numeric_limits< double >::lowest() )
4604 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4605
4606 return QVariant( max );
4607}
4608
4609static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4610{
4611 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4612 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom.constGet() );
4613 if ( !curve )
4614 {
4615 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
4616 return QVariant();
4617 }
4618
4619 return QVariant( curve->sinuosity() );
4620}
4621
4622static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4623{
4624 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4625 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4626 if ( !curve )
4627 {
4628 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
4629 return QVariant();
4630 }
4631
4632 return QVariant( curve->straightDistance2d() );
4633}
4634
4635static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4636{
4637 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4638 const QgsCurvePolygon *poly = geom.constGet() ? qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4639
4640 if ( !poly )
4641 {
4642 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
4643 return QVariant();
4644 }
4645
4646 return QVariant( poly->roundness() );
4647}
4648
4649
4650
4651static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4652{
4653 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4654 if ( geom.isNull() )
4655 return QVariant();
4656
4657 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
4658 flipped->swapXy();
4659 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
4660}
4661
4662static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4663{
4664 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4665 if ( fGeom.isNull() )
4666 return QVariant();
4667
4668 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
4669 if ( !curve && fGeom.isMultipart() )
4670 {
4671 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4672 {
4673 if ( collection->numGeometries() == 1 )
4674 {
4675 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4676 }
4677 }
4678 }
4679
4680 if ( !curve )
4681 return QVariant();
4682
4683 return QVariant::fromValue( curve->isClosed() );
4684}
4685
4686static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4687{
4688 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4689
4690 if ( geom.isNull() )
4691 return QVariant();
4692
4693 QVariant result;
4694 if ( !geom.isMultipart() )
4695 {
4696 const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( geom.constGet() );
4697
4698 if ( !line )
4699 return QVariant();
4700
4701 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4702 closedLine->close();
4703
4704 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
4705 }
4706 else
4707 {
4708 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( geom.constGet() );
4709
4710 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
4711
4712 for ( int i = 0; i < collection->numGeometries(); ++i )
4713 {
4714 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
4715 {
4716 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4717 closedLine->close();
4718
4719 closed->addGeometry( closedLine.release() );
4720 }
4721 }
4722 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
4723 }
4724
4725 return result;
4726}
4727
4728static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4729{
4730 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4731 if ( fGeom.isNull() )
4732 return QVariant();
4733
4734 return QVariant::fromValue( fGeom.isEmpty() );
4735}
4736
4737static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4738{
4739 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
4740 return QVariant::fromValue( true );
4741
4742 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4743 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
4744}
4745
4746static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4747{
4748 if ( values.length() < 2 || values.length() > 3 )
4749 return QVariant();
4750
4751 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4752 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4753
4754 if ( fGeom.isNull() || sGeom.isNull() )
4755 return QVariant();
4756
4757 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
4758
4759 if ( values.length() == 2 )
4760 {
4761 //two geometry arguments, return relation
4762 QString result = engine->relate( sGeom.constGet() );
4763 return QVariant::fromValue( result );
4764 }
4765 else
4766 {
4767 //three arguments, test pattern
4768 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4769 bool result = engine->relatePattern( sGeom.constGet(), pattern );
4770 return QVariant::fromValue( result );
4771 }
4772}
4773
4774static QVariant fcnBbox( 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 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
4779}
4780static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4781{
4782 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4783 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4784 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
4785}
4786static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4787{
4788 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4789 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4790 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
4791}
4792static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4793{
4794 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4795 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4796 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
4797}
4798static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4799{
4800 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4801 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4802 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
4803}
4804static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4805{
4806 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4807 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4808 return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
4809}
4810static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4811{
4812 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4813 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4814 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
4815}
4816static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4817{
4818 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4819 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4820 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
4821}
4822
4823static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4824{
4825 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4826 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4827 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4828 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4829 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4830 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4831
4833 if ( endCapString.compare( QLatin1String( "flat" ), Qt::CaseInsensitive ) == 0 )
4834 capStyle = Qgis::EndCapStyle::Flat;
4835 else if ( endCapString.compare( QLatin1String( "square" ), Qt::CaseInsensitive ) == 0 )
4836 capStyle = Qgis::EndCapStyle::Square;
4837
4839 if ( joinString.compare( QLatin1String( "miter" ), Qt::CaseInsensitive ) == 0 )
4840 joinStyle = Qgis::JoinStyle::Miter;
4841 else if ( joinString.compare( QLatin1String( "bevel" ), Qt::CaseInsensitive ) == 0 )
4842 joinStyle = Qgis::JoinStyle::Bevel;
4843
4844 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
4845 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4846 return result;
4847}
4848
4849static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4850{
4851 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4852 const QgsGeometry reoriented = fGeom.forceRHR();
4853 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4854}
4855
4856static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4857{
4858 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4859 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
4860 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4861}
4862
4863static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4864{
4865 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4866 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
4867 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4868}
4869
4870static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4871{
4872 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4873 const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( fGeom.constGet() );
4874 if ( !pt && fGeom.isMultipart() )
4875 {
4876 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4877 {
4878 if ( collection->numGeometries() == 1 )
4879 {
4880 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4881 }
4882 }
4883 }
4884
4885 if ( !pt )
4886 {
4887 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
4888 return QVariant();
4889 }
4890
4891 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4892 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4893 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4894 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4895
4896 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
4897 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4898 return result;
4899}
4900
4901static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4902{
4903 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4904 if ( fGeom.type() != Qgis::GeometryType::Line )
4905 {
4906 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
4907 return QVariant();
4908 }
4909
4910 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4911 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4912 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4913
4914 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
4915 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4916 return result;
4917}
4918
4919static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4920{
4921 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4922 if ( fGeom.type() != Qgis::GeometryType::Line )
4923 {
4924 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
4925 return QVariant();
4926 }
4927
4928 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
4929
4930 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
4931 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4932 return result;
4933}
4934
4935static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4936{
4937 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4938 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4939 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4940 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4941 if ( joinInt < 1 || joinInt > 3 )
4942 return QVariant();
4943 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
4944
4945 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4946
4947 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
4948 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4949 return result;
4950}
4951
4952static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4953{
4954 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4955 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4956 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4957
4958 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4959 if ( joinInt < 1 || joinInt > 3 )
4960 return QVariant();
4961 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
4962
4963 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4964
4965 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
4966 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4967 return result;
4968}
4969
4970static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4971{
4972 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4973 double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4974 double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4975
4976 QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
4977 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4978 return result;
4979}
4980
4981static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4982{
4983 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4984 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4985 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4986 fGeom.translate( dx, dy );
4987 return QVariant::fromValue( fGeom );
4988}
4989
4990static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4991{
4992 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4993 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4994 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent )
4995 : QgsGeometry();
4996 const bool perPart = values.value( 3 ).toBool();
4997
4998 if ( center.isNull() && perPart && fGeom.isMultipart() )
4999 {
5000 // no explicit center, rotating per part
5001 // (note that we only do this branch for multipart geometries -- for singlepart geometries
5002 // the result is equivalent to setting perPart as false anyway)
5003 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
5004 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
5005 {
5006 const QgsPointXY partCenter = ( *it )->boundingBox().center();
5007 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
5008 t.rotate( -rotation );
5009 t.translate( -partCenter.x(), -partCenter.y() );
5010 ( *it )->transform( t );
5011 }
5012 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
5013 }
5014 else
5015 {
5016 QgsPointXY pt;
5017 if ( center.isEmpty() )
5018 {
5019 // if center wasn't specified, use bounding box centroid
5020 pt = fGeom.boundingBox().center();
5021 }
5023 {
5024 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
5025 return QVariant();
5026 }
5027 else
5028 {
5029 pt = QgsPointXY( *qgsgeometry_cast< const QgsPoint * >( center.constGet()->simplifiedTypeRef() ) );
5030 }
5031
5032 fGeom.rotate( rotation, pt );
5033 return QVariant::fromValue( fGeom );
5034 }
5035}
5036
5037static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5038{
5039 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5040 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5041 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5042 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent )
5043 : QgsGeometry();
5044
5045 QgsPointXY pt;
5046 if ( center.isNull() )
5047 {
5048 // if center wasn't specified, use bounding box centroid
5049 pt = fGeom.boundingBox().center();
5050 }
5052 {
5053 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
5054 return QVariant();
5055 }
5056 else
5057 {
5058 pt = center.asPoint();
5059 }
5060
5061 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
5062 t.scale( xScale, yScale );
5063 t.translate( -pt.x(), -pt.y() );
5064 fGeom.transform( t );
5065 return QVariant::fromValue( fGeom );
5066}
5067
5068static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5069{
5070 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5071 if ( fGeom.isNull() )
5072 {
5073 return QVariant();
5074 }
5075
5076 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5077 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5078
5079 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5080
5081 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5082 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5083
5084 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
5085 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
5086 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
5087 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
5088
5089 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
5090 {
5091 fGeom.get()->addZValue( 0 );
5092 }
5093 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
5094 {
5095 fGeom.get()->addMValue( 0 );
5096 }
5097
5098 QTransform transform;
5099 transform.translate( deltaX, deltaY );
5100 transform.rotate( rotationZ );
5101 transform.scale( scaleX, scaleY );
5102 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5103
5104 return QVariant::fromValue( fGeom );
5105}
5106
5107
5108static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5109{
5110 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5111 QgsGeometry geom = fGeom.centroid();
5112 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5113 return result;
5114}
5115static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5116{
5117 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5118 QgsGeometry geom = fGeom.pointOnSurface();
5119 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5120 return result;
5121}
5122
5123static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5124{
5125 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5126 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5127 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5128 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5129 return result;
5130}
5131
5132static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5133{
5134 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5135 QgsGeometry geom = fGeom.convexHull();
5136 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5137 return result;
5138}
5139
5140#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
5141static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5142{
5143 try
5144 {
5145 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5146 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5147 const bool allowHoles = values.value( 2 ).toBool();
5148 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5149 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5150 return result;
5151 }
5152 catch ( QgsCsException &cse )
5153 {
5154 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5155 return QVariant();
5156 }
5157}
5158#endif
5159
5160static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5161{
5162 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5163 int segments = 36;
5164 if ( values.length() == 2 )
5165 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5166 if ( segments < 0 )
5167 {
5168 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5169 return QVariant();
5170 }
5171
5172 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5173 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5174 return result;
5175}
5176
5177static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5178{
5179 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5181 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5182 return result;
5183}
5184
5185static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5186{
5187 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5188
5189 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5190 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5191 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5192
5193 double area, angle, width, height;
5194 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5195
5196 if ( geom.isNull() )
5197 {
5198 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5199 return QVariant();
5200 }
5201 return angle;
5202}
5203
5204static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5205{
5206 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5207 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5208 QgsGeometry geom = fGeom.difference( sGeom );
5209 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5210 return result;
5211}
5212
5213static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5214{
5215 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5216 if ( fGeom.isNull() )
5217 return QVariant();
5218
5219 QVariant result;
5220 if ( !fGeom.isMultipart() )
5221 {
5222 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5223 if ( !curve )
5224 return QVariant();
5225
5226 QgsCurve *reversed = curve->reversed();
5227 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5228 }
5229 else
5230 {
5231 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( fGeom.constGet() );
5232 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5233 for ( int i = 0; i < collection->numGeometries(); ++i )
5234 {
5235 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5236 {
5237 reversed->addGeometry( curve->reversed() );
5238 }
5239 else
5240 {
5241 reversed->addGeometry( collection->geometryN( i )->clone() );
5242 }
5243 }
5244 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5245 }
5246 return result;
5247}
5248
5249static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5250{
5251 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5252 if ( fGeom.isNull() )
5253 return QVariant();
5254
5255 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( fGeom.constGet() );
5256 if ( !curvePolygon && fGeom.isMultipart() )
5257 {
5258 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
5259 {
5260 if ( collection->numGeometries() == 1 )
5261 {
5262 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5263 }
5264 }
5265 }
5266
5267 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5268 return QVariant();
5269
5270 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5271 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5272 return result;
5273}
5274
5275static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5276{
5277 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5278 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5279 return QVariant( fGeom.distance( sGeom ) );
5280}
5281
5282static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5283{
5284 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5285 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5286
5287 double res = -1;
5288 if ( values.length() == 3 && values.at( 2 ).isValid() )
5289 {
5290 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5291 densify = std::clamp( densify, 0.0, 1.0 );
5292 res = g1.hausdorffDistanceDensify( g2, densify );
5293 }
5294 else
5295 {
5296 res = g1.hausdorffDistance( g2 );
5297 }
5298
5299 return res > -1 ? QVariant( res ) : QVariant();
5300}
5301
5302static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5303{
5304 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5305 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5306 QgsGeometry geom = fGeom.intersection( sGeom );
5307 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5308 return result;
5309}
5310static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5311{
5312 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5313 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5314 QgsGeometry geom = fGeom.symDifference( sGeom );
5315 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5316 return result;
5317}
5318static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5319{
5320 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5321 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5322 QgsGeometry geom = fGeom.combine( sGeom );
5323 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5324 return result;
5325}
5326
5327static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5328{
5329 if ( values.length() < 1 || values.length() > 2 )
5330 return QVariant();
5331
5332 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5333 int prec = 8;
5334 if ( values.length() == 2 )
5335 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5336 QString wkt = fGeom.asWkt( prec );
5337 return QVariant( wkt );
5338}
5339
5340static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5341{
5342 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5343 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
5344}
5345
5346static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5347{
5348 if ( values.length() != 2 )
5349 {
5350 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
5351 return QVariant();
5352 }
5353
5354 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5355 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5356
5357 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5358 if ( !pt1 && fGeom1.isMultipart() )
5359 {
5360 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
5361 {
5362 if ( collection->numGeometries() == 1 )
5363 {
5364 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5365 }
5366 }
5367 }
5368
5369 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5370 if ( !pt2 && fGeom2.isMultipart() )
5371 {
5372 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
5373 {
5374 if ( collection->numGeometries() == 1 )
5375 {
5376 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5377 }
5378 }
5379 }
5380
5381 if ( !pt1 || !pt2 )
5382 {
5383 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
5384 return QVariant();
5385 }
5386
5387 // Code from PostGIS
5388 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
5389 {
5390 if ( pt1->y() < pt2->y() )
5391 return 0.0;
5392 else if ( pt1->y() > pt2->y() )
5393 return M_PI;
5394 else
5395 return 0;
5396 }
5397
5398 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
5399 {
5400 if ( pt1->x() < pt2->x() )
5401 return M_PI_2;
5402 else if ( pt1->x() > pt2->x() )
5403 return M_PI + ( M_PI_2 );
5404 else
5405 return 0;
5406 }
5407
5408 if ( pt1->x() < pt2->x() )
5409 {
5410 if ( pt1->y() < pt2->y() )
5411 {
5412 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
5413 }
5414 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
5415 {
5416 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5417 + ( M_PI_2 );
5418 }
5419 }
5420
5421 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
5422 {
5423 if ( pt1->y() > pt2->y() )
5424 {
5425 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) )
5426 + M_PI;
5427 }
5428 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
5429 {
5430 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5431 + ( M_PI + ( M_PI_2 ) );
5432 }
5433 }
5434}
5435
5436static QVariant fcnBearing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5437{
5438 const QgsGeometry geom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5439 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5440 QString sourceCrs = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5441 QString ellipsoid = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
5442
5443 if ( geom1.isNull() || geom2.isNull() || geom1.type() != Qgis::GeometryType::Point || geom2.type() != Qgis::GeometryType::Point )
5444 {
5445 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires two valid point geometries." ) );
5446 return QVariant();
5447 }
5448
5449 const QgsPointXY point1 = geom1.asPoint();
5450 const QgsPointXY point2 = geom2.asPoint();
5451 if ( point1.isEmpty() || point2.isEmpty() )
5452 {
5453 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires point geometries or multi point geometries with a single part." ) );
5454 return QVariant();
5455 }
5456
5458 if ( context )
5459 {
5460 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
5461
5462 if ( sourceCrs.isEmpty() )
5463 {
5464 sourceCrs = context->variable( QStringLiteral( "layer_crs" ) ).toString();
5465 }
5466
5467 if ( ellipsoid.isEmpty() )
5468 {
5469 ellipsoid = context->variable( QStringLiteral( "project_ellipsoid" ) ).toString();
5470 }
5471 }
5472
5474 if ( !sCrs.isValid() )
5475 {
5476 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid source CRS." ) );
5477 return QVariant();
5478 }
5479
5480 QgsDistanceArea da;
5481 da.setSourceCrs( sCrs, tContext );
5482 if ( !da.setEllipsoid( ellipsoid ) )
5483 {
5484 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid ellipsoid acronym or ellipsoid authority ID." ) );
5485 return QVariant();
5486 }
5487
5488 try
5489 {
5490 const double bearing = da.bearing( point1, point2 );
5491 if ( std::isfinite( bearing ) )
5492 {
5493 return std::fmod( bearing + 2 * M_PI, 2 * M_PI );
5494 }
5495 }
5496 catch ( QgsCsException &cse )
5497 {
5498 QgsMessageLog::logMessage( QObject::tr( "Error caught in bearing() function: %1" ).arg( cse.what() ) );
5499 return QVariant();
5500 }
5501 return QVariant();
5502}
5503
5504static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5505{
5506 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5507
5509 {
5510 parent->setEvalErrorString( QStringLiteral( "'project' requires a point geometry" ) );
5511 return QVariant();
5512 }
5513
5514 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5515 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5516 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5517
5518 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef( ) );
5519 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
5520
5521 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
5522}
5523
5524static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5525{
5526 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5527 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5528
5529 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5530 if ( !pt1 && fGeom1.isMultipart() )
5531 {
5532 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
5533 {
5534 if ( collection->numGeometries() == 1 )
5535 {
5536 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5537 }
5538 }
5539 }
5540 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5541 if ( !pt2 && fGeom2.isMultipart() )
5542 {
5543 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
5544 {
5545 if ( collection->numGeometries() == 1 )
5546 {
5547 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5548 }
5549 }
5550 }
5551
5552 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) ||
5553 !pt1 || !pt2 )
5554 {
5555 parent->setEvalErrorString( QStringLiteral( "Function 'inclination' requires two points as arguments." ) );
5556 return QVariant();
5557 }
5558
5559 return pt1->inclination( *pt2 );
5560
5561}
5562
5563static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5564{
5565 if ( values.length() != 3 )
5566 return QVariant();
5567
5568 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5569 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5570 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5571
5572 QgsGeometry geom = fGeom.extrude( x, y );
5573
5574 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
5575 return result;
5576}
5577
5578static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
5579{
5580 if ( values.length() < 2 )
5581 return QVariant();
5582
5583 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5584
5585 if ( !fGeom.isMultipart() )
5586 return values.at( 0 );
5587
5588 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5589 QVariant cachedExpression;
5590 if ( ctx )
5591 cachedExpression = ctx->cachedValue( expString );
5592 QgsExpression expression;
5593
5594 if ( cachedExpression.isValid() )
5595 {
5596 expression = cachedExpression.value<QgsExpression>();
5597 }
5598 else
5599 expression = QgsExpression( expString );
5600
5601 bool asc = values.value( 2 ).toBool();
5602
5603 QgsExpressionContext *unconstedContext = nullptr;
5604 QgsFeature f;
5605 if ( ctx )
5606 {
5607 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
5608 // so no reason to worry
5609 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
5610 f = ctx->feature();
5611 }
5612 else
5613 {
5614 // If there's no context provided, create a fake one
5615 unconstedContext = new QgsExpressionContext();
5616 }
5617
5618 const QgsGeometryCollection *collection = qgsgeometry_cast<const QgsGeometryCollection *>( fGeom.constGet() );
5619 Q_ASSERT( collection ); // Should have failed the multipart check above
5620
5622 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
5623 QgsExpressionSorter sorter( orderBy );
5624
5625 QList<QgsFeature> partFeatures;
5626 partFeatures.reserve( collection->partCount() );
5627 for ( int i = 0; i < collection->partCount(); ++i )
5628 {
5629 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
5630 partFeatures << f;
5631 }
5632
5633 sorter.sortFeatures( partFeatures, unconstedContext );
5634
5635 QgsGeometryCollection *orderedGeom = qgsgeometry_cast<QgsGeometryCollection *>( fGeom.constGet()->clone() );
5636
5637 Q_ASSERT( orderedGeom );
5638
5639 while ( orderedGeom->partCount() )
5640 orderedGeom->removeGeometry( 0 );
5641
5642 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
5643 {
5644 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
5645 }
5646
5647 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
5648
5649 if ( !ctx )
5650 delete unconstedContext;
5651
5652 return result;
5653}
5654
5655static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5656{
5657 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5658 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5659
5660 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
5661
5662 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5663 return result;
5664}
5665
5666static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5667{
5668 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5669 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5670
5671 QgsGeometry geom = fromGeom.shortestLine( toGeom );
5672
5673 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5674 return result;
5675}
5676
5677static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5678{
5679 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5680 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5681
5682 QgsGeometry geom = lineGeom.interpolate( distance );
5683
5684 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5685 return result;
5686}
5687
5688static QVariant fcnLineInterpolatePointByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5689{
5690 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5691 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5692 const bool use3DDistance = values.at( 2 ).toBool();
5693
5694 double x, y, z, distance;
5695
5696 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( lineGeom.constGet() );
5697 if ( !line )
5698 {
5699 return QVariant();
5700 }
5701
5702 if ( line->lineLocatePointByM( m, x, y, z, distance, use3DDistance ) )
5703 {
5704 QgsPoint point( x, y );
5705 if ( use3DDistance && QgsWkbTypes::hasZ( lineGeom.wkbType() ) )
5706 {
5707 point.addZValue( z );
5708 }
5709 return QVariant::fromValue( QgsGeometry( point.clone() ) );
5710 }
5711
5712 return QVariant();
5713}
5714
5715static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5716{
5717 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5718 if ( lineGeom.type() != Qgis::GeometryType::Line )
5719 {
5720 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
5721 return QVariant();
5722 }
5723
5724 const QgsCurve *curve = nullptr;
5725 if ( !lineGeom.isMultipart() )
5726 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
5727 else
5728 {
5729 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( lineGeom.constGet() ) )
5730 {
5731 if ( collection->numGeometries() > 0 )
5732 {
5733 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5734 }
5735 }
5736 }
5737 if ( !curve )
5738 return QVariant();
5739
5740 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5741 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5742
5743 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
5744 QgsGeometry result( std::move( substring ) );
5745 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
5746}
5747
5748static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5749{
5750 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5751 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5752
5753 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
5754}
5755
5756static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5757{
5758 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5759 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5760 if ( vertex < 0 )
5761 {
5762 //negative idx
5763 int count = geom.constGet()->nCoordinates();
5764 vertex = count + vertex;
5765 }
5766
5767 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
5768}
5769
5770static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5771{
5772 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5773 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5774 if ( vertex < 0 )
5775 {
5776 //negative idx
5777 int count = geom.constGet()->nCoordinates();
5778 vertex = count + vertex;
5779 }
5780
5781 return geom.distanceToVertex( vertex );
5782}
5783
5784static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5785{
5786 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5787 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5788
5789 double distance = lineGeom.lineLocatePoint( pointGeom );
5790
5791 return distance >= 0 ? distance : QVariant();
5792}
5793
5794static QVariant fcnLineLocateM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5795{
5796 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5797 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5798 const bool use3DDistance = values.at( 2 ).toBool();
5799
5800 double x, y, z, distance;
5801
5802 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( lineGeom.constGet() );
5803 if ( !line )
5804 {
5805 return QVariant();
5806 }
5807
5808 const bool found = line->lineLocatePointByM( m, x, y, z, distance, use3DDistance );
5809 return found ? distance : QVariant();
5810}
5811
5812static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5813{
5814 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
5815 {
5816 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5817 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5818 }
5819
5820 if ( values.length() >= 1 )
5821 {
5822 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5823 return QVariant( qlonglong( std::round( number ) ) );
5824 }
5825
5826 return QVariant();
5827}
5828
5829static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5830{
5831 Q_UNUSED( values )
5832 Q_UNUSED( parent )
5833 return M_PI;
5834}
5835
5836static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5837{
5838 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5839 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5840 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5841 if ( places < 0 )
5842 {
5843 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
5844 return QVariant();
5845 }
5846
5847 const bool omitGroupSeparator = values.value( 3 ).toBool();
5848 const bool trimTrailingZeros = values.value( 4 ).toBool();
5849
5850 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5851 if ( !omitGroupSeparator )
5852 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
5853 else
5854 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
5855
5856 QString res = locale.toString( value, 'f', places );
5857
5858 if ( trimTrailingZeros )
5859 {
5860#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5861 const QChar decimal = locale.decimalPoint();
5862 const QChar zeroDigit = locale.zeroDigit();
5863#else
5864 const QChar decimal = locale.decimalPoint().at( 0 );
5865 const QChar zeroDigit = locale.zeroDigit().at( 0 );
5866#endif
5867
5868 if ( res.contains( decimal ) )
5869 {
5870 int trimPoint = res.length() - 1;
5871
5872 while ( res.at( trimPoint ) == zeroDigit )
5873 trimPoint--;
5874
5875 if ( res.at( trimPoint ) == decimal )
5876 trimPoint--;
5877
5878 res.truncate( trimPoint + 1 );
5879 }
5880 }
5881
5882 return res;
5883}
5884
5885static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5886{
5887 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
5888 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5889 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5890
5891 // Convert to UTC if the format string includes a Z, as QLocale::toString() doesn't do it
5892 if ( format.indexOf( "Z" ) > 0 )
5893 datetime = datetime.toUTC();
5894
5895 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5896 return locale.toString( datetime, format );
5897}
5898
5899static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5900{
5901 const QVariant variant = values.at( 0 );
5902 bool isQColor;
5903 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
5904 if ( !color.isValid() )
5905 return QVariant();
5906
5907 const float alpha = color.alphaF(); // NOLINT(bugprone-narrowing-conversions): TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
5908 if ( color.spec() == QColor::Spec::Cmyk )
5909 {
5910 const float avg = ( color.cyanF() + color.magentaF() + color.yellowF() ) / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
5911 color = QColor::fromCmykF( avg, avg, avg, color.blackF(), alpha );
5912 }
5913 else
5914 {
5915 const float avg = ( color.redF() + color.greenF() + color.blueF() ) / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
5916 color.setRgbF( avg, avg, avg, alpha );
5917 }
5918
5919 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
5920}
5921
5922static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5923{
5924 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5925 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
5926 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5927 if ( ratio > 1 )
5928 {
5929 ratio = 1;
5930 }
5931 else if ( ratio < 0 )
5932 {
5933 ratio = 0;
5934 }
5935
5936 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
5937 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
5938 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
5939 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
5940
5941 QColor newColor( red, green, blue, alpha );
5942
5943 return QgsSymbolLayerUtils::encodeColor( newColor );
5944}
5945
5946static QVariant fcnColorMix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5947{
5948 const QVariant variant1 = values.at( 0 );
5949 const QVariant variant2 = values.at( 1 );
5950
5951 if ( variant1.userType() != variant2.userType() )
5952 {
5953 parent->setEvalErrorString( QObject::tr( "Both color arguments must have the same type (string or color object)" ) );
5954 return QVariant();
5955 }
5956
5957 bool isQColor;
5958 const QColor color1 = QgsExpressionUtils::getColorValue( variant1, parent, isQColor );
5959 if ( !color1.isValid() )
5960 return QVariant();
5961
5962 const QColor color2 = QgsExpressionUtils::getColorValue( variant2, parent, isQColor );
5963 if ( !color2.isValid() )
5964 return QVariant();
5965
5966 if ( ( color1.spec() == QColor::Cmyk ) != ( color2.spec() == QColor::Cmyk ) )
5967 {
5968 parent->setEvalErrorString( QObject::tr( "Both color arguments must have compatible color type (CMYK or RGB/HSV/HSL)" ) );
5969 return QVariant();
5970 }
5971
5972 const float ratio = static_cast<float>( std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0., 1. ) );
5973
5974 // TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
5975 // NOLINTBEGIN(bugprone-narrowing-conversions)
5976
5977 QColor newColor;
5978 const float alpha = color1.alphaF() * ( 1 - ratio ) + color2.alphaF() * ratio;
5979 if ( color1.spec() == QColor::Spec::Cmyk )
5980 {
5981 float cyan = color1.cyanF() * ( 1 - ratio ) + color2.cyanF() * ratio;
5982 float magenta = color1.magentaF() * ( 1 - ratio ) + color2.magentaF() * ratio;
5983 float yellow = color1.yellowF() * ( 1 - ratio ) + color2.yellowF() * ratio;
5984 float black = color1.blackF() * ( 1 - ratio ) + color2.blackF() * ratio;
5985 newColor = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
5986 }
5987 else
5988 {
5989 float red = color1.redF() * ( 1 - ratio ) + color2.redF() * ratio;
5990 float green = color1.greenF() * ( 1 - ratio ) + color2.greenF() * ratio;
5991 float blue = color1.blueF() * ( 1 - ratio ) + color2.blueF() * ratio;
5992 newColor = QColor::fromRgbF( red, green, blue, alpha );
5993 }
5994
5995 // NOLINTEND(bugprone-narrowing-conversions)
5996
5997 return isQColor ? QVariant( newColor ) : QVariant( QgsSymbolLayerUtils::encodeColor( newColor ) );
5998}
5999
6000static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6001{
6002 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6003 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6004 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6005 QColor color = QColor( red, green, blue );
6006 if ( ! color.isValid() )
6007 {
6008 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
6009 color = QColor( 0, 0, 0 );
6010 }
6011
6012 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6013}
6014
6015static QVariant fcnColorRgbF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6016{
6017 const float red = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6018 const float green = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6019 const float blue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6020 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6021 QColor color = QColor::fromRgbF( red, green, blue, alpha );
6022 if ( ! color.isValid() )
6023 {
6024 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6025 return QVariant();
6026 }
6027
6028 return color;
6029}
6030
6031static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6032{
6033 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6034 QVariant value = node->eval( parent, context );
6035 if ( parent->hasEvalError() )
6036 {
6037 parent->setEvalErrorString( QString() );
6038 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6040 value = node->eval( parent, context );
6042 }
6043 return value;
6044}
6045
6046static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6047{
6048 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6050 QVariant value = node->eval( parent, context );
6052 if ( value.toBool() )
6053 {
6054 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6056 value = node->eval( parent, context );
6058 }
6059 else
6060 {
6061 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6063 value = node->eval( parent, context );
6065 }
6066 return value;
6067}
6068
6069static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6070{
6071 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6072 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6073 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6074 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
6075 QColor color = QColor( red, green, blue, alpha );
6076 if ( ! color.isValid() )
6077 {
6078 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6079 color = QColor( 0, 0, 0 );
6080 }
6081 return QgsSymbolLayerUtils::encodeColor( color );
6082}
6083
6084QVariant fcnRampColorObject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6085{
6086 QgsGradientColorRamp expRamp;
6087 const QgsColorRamp *ramp = nullptr;
6088 if ( values.at( 0 ).userType() == qMetaTypeId< QgsGradientColorRamp>() )
6089 {
6090 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
6091 ramp = &expRamp;
6092 }
6093 else
6094 {
6095 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6096 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
6097 if ( ! ramp )
6099 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
6100 return QVariant();
6101 }
6102 }
6103
6104 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6105 QColor color = ramp->color( value );
6106 return color;
6107}
6108
6109QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6110{
6111 QColor color = fcnRampColorObject( values, context, parent, node ).value<QColor>();
6112 return color.isValid() ? QgsSymbolLayerUtils::encodeColor( color ) : QVariant();
6113}
6114
6115static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6116{
6117 // Hue ranges from 0 - 360
6118 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6119 // Saturation ranges from 0 - 100
6120 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6121 // Lightness ranges from 0 - 100
6122 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6123
6124 QColor color = QColor::fromHslF( hue, saturation, lightness );
6125
6126 if ( ! color.isValid() )
6127 {
6128 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
6129 color = QColor( 0, 0, 0 );
6130 }
6131
6132 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6133}
6134
6135static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6136{
6137 // Hue ranges from 0 - 360
6138 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6139 // Saturation ranges from 0 - 100
6140 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6141 // Lightness ranges from 0 - 100
6142 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6143 // Alpha ranges from 0 - 255
6144 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6145
6146 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6147 if ( ! color.isValid() )
6148 {
6149 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6150 color = QColor( 0, 0, 0 );
6151 }
6152 return QgsSymbolLayerUtils::encodeColor( color );
6153}
6154
6155static QVariant fcnColorHslF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6156{
6157 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6158 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6159 float lightness = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6160 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6161
6162 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6163 if ( ! color.isValid() )
6164 {
6165 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6166 return QVariant();
6167 }
6168
6169 return color;
6170}
6171
6172static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6173{
6174 // Hue ranges from 0 - 360
6175 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6176 // Saturation ranges from 0 - 100
6177 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6178 // Value ranges from 0 - 100
6179 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6180
6181 QColor color = QColor::fromHsvF( hue, saturation, value );
6182
6183 if ( ! color.isValid() )
6184 {
6185 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
6186 color = QColor( 0, 0, 0 );
6187 }
6188
6189 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6190}
6191
6192static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6193{
6194 // Hue ranges from 0 - 360
6195 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6196 // Saturation ranges from 0 - 100
6197 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6198 // Value ranges from 0 - 100
6199 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6200 // Alpha ranges from 0 - 255
6201 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6202
6203 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6204 if ( ! color.isValid() )
6205 {
6206 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6207 color = QColor( 0, 0, 0 );
6208 }
6209 return QgsSymbolLayerUtils::encodeColor( color );
6210}
6211
6212static QVariant fcnColorHsvF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6213{
6214 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6215 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6216 float value = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6217 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6218 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6219
6220 if ( ! color.isValid() )
6221 {
6222 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6223 return QVariant();
6224 }
6225
6226 return color;
6227}
6228
6229static QVariant fcnColorCmykF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6230{
6231 const float cyan = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6232 const float magenta = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6233 const float yellow = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6234 const float black = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6235 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ) ), 0.f, 1.f );
6236
6237 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6238 if ( ! color.isValid() )
6239 {
6240 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6241 return QVariant();
6242 }
6243
6244 return color;
6245}
6246
6247static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6248{
6249 // Cyan ranges from 0 - 100
6250 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6251 // Magenta ranges from 0 - 100
6252 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6253 // Yellow ranges from 0 - 100
6254 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6255 // Black ranges from 0 - 100
6256 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6257
6258 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
6259
6260 if ( ! color.isValid() )
6261 {
6262 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
6263 color = QColor( 0, 0, 0 );
6264 }
6265
6266 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6267}
6268
6269static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6270{
6271 // Cyan ranges from 0 - 100
6272 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6273 // Magenta ranges from 0 - 100
6274 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6275 // Yellow ranges from 0 - 100
6276 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6277 // Black ranges from 0 - 100
6278 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6279 // Alpha ranges from 0 - 255
6280 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
6281
6282 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6283 if ( ! color.isValid() )
6284 {
6285 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6286 color = QColor( 0, 0, 0 );
6287 }
6288 return QgsSymbolLayerUtils::encodeColor( color );
6289}
6290
6291static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6292{
6293 const QVariant variant = values.at( 0 );
6294 bool isQColor;
6295 const QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6296 if ( !color.isValid() )
6297 return QVariant();
6298
6299 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6300 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
6301 return color.red();
6302 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
6303 return color.green();
6304 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
6305 return color.blue();
6306 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
6307 return color.alpha();
6308 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
6309 return static_cast< double >( color.hsvHueF() * 360 );
6310 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
6311 return static_cast< double >( color.hsvSaturationF() * 100 );
6312 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
6313 return static_cast< double >( color.valueF() * 100 );
6314 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
6315 return static_cast< double >( color.hslHueF() * 360 );
6316 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
6317 return static_cast< double >( color.hslSaturationF() * 100 );
6318 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
6319 return static_cast< double >( color.lightnessF() * 100 );
6320 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
6321 return static_cast< double >( color.cyanF() * 100 );
6322 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
6323 return static_cast< double >( color.magentaF() * 100 );
6324 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
6325 return static_cast< double >( color.yellowF() * 100 );
6326 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
6327 return static_cast< double >( color.blackF() * 100 );
6328
6329 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6330 return QVariant();
6331}
6332
6333static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6334{
6335 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6336 if ( map.empty() )
6337 {
6338 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
6339 return QVariant();
6340 }
6341
6342 QList< QColor > colors;
6344 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
6345 {
6346 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
6347 if ( !colors.last().isValid() )
6348 {
6349 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
6350 return QVariant();
6351 }
6352
6353 double step = it.key().toDouble();
6354 if ( it == map.constBegin() )
6355 {
6356 if ( step != 0.0 )
6357 stops << QgsGradientStop( step, colors.last() );
6358 }
6359 else if ( it == map.constEnd() )
6360 {
6361 if ( step != 1.0 )
6362 stops << QgsGradientStop( step, colors.last() );
6363 }
6364 else
6365 {
6366 stops << QgsGradientStop( step, colors.last() );
6367 }
6368 }
6369 bool discrete = values.at( 1 ).toBool();
6370
6371 if ( colors.empty() )
6372 return QVariant();
6373
6374 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
6375}
6376
6377static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6378{
6379 const QVariant variant = values.at( 0 );
6380 bool isQColor;
6381 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6382 if ( !color.isValid() )
6383 return QVariant();
6384
6385 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6386 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6387 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
6388 color.setRed( std::clamp( value, 0, 255 ) );
6389 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
6390 color.setGreen( std::clamp( value, 0, 255 ) );
6391 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
6392 color.setBlue( std::clamp( value, 0, 255 ) );
6393 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
6394 color.setAlpha( std::clamp( value, 0, 255 ) );
6395 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
6396 color.setHsv( std::clamp( value, 0, 359 ), color.hsvSaturation(), color.value(), color.alpha() );
6397 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
6398 color.setHsvF( color.hsvHueF(), std::clamp( value, 0, 100 ) / 100.0, color.valueF(), color.alphaF() );
6399 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
6400 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6401 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
6402 color.setHsl( std::clamp( value, 0, 359 ), color.hslSaturation(), color.lightness(), color.alpha() );
6403 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
6404 color.setHslF( color.hslHueF(), std::clamp( value, 0, 100 ) / 100.0, color.lightnessF(), color.alphaF() );
6405 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
6406 color.setHslF( color.hslHueF(), color.hslSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6407 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
6408 color.setCmykF( std::clamp( value, 0, 100 ) / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
6409 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
6410 color.setCmykF( color.cyanF(), std::clamp( value, 0, 100 ) / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
6411 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
6412 color.setCmykF( color.cyanF(), color.magentaF(), std::clamp( value, 0, 100 ) / 100.0, color.blackF(), color.alphaF() );
6413 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
6414 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6415 else
6416 {
6417 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6418 return QVariant();
6419 }
6420 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6421}
6422
6423static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6424{
6425 const QVariant variant = values.at( 0 );
6426 bool isQColor;
6427 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6428 if ( !color.isValid() )
6429 return QVariant();
6430
6431 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6432
6433 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6434}
6435
6436static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6437{
6438 const QVariant variant = values.at( 0 );
6439 bool isQColor;
6440 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6441 if ( !color.isValid() )
6442 return QVariant();
6443
6444 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6445
6446 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6447}
6448
6449static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6450{
6451 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6452 QgsGeometry geom = feat.geometry();
6453 if ( !geom.isNull() )
6454 return QVariant::fromValue( geom );
6455 return QVariant();
6456}
6457
6458static QVariant fcnGetFeatureId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6459{
6460 const QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6461 if ( !feat.isValid() )
6462 return QVariant();
6463 return feat.id();
6464}
6465
6466static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6467{
6468 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6469 QString sAuthId = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6470 QString dAuthId = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6471
6473 if ( ! s.isValid() )
6474 return QVariant::fromValue( fGeom );
6476 if ( ! d.isValid() )
6477 return QVariant::fromValue( fGeom );
6478
6480 if ( context )
6481 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
6482 QgsCoordinateTransform t( s, d, tContext );
6483 try
6484 {
6486 return QVariant::fromValue( fGeom );
6487 }
6488 catch ( QgsCsException &cse )
6489 {
6490 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
6491 return QVariant();
6492 }
6493 return QVariant();
6494}
6495
6496
6497static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6498{
6499 bool foundLayer = false;
6500 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6501
6502 //no layer found
6503 if ( !featureSource || !foundLayer )
6504 {
6505 return QVariant();
6506 }
6507
6508 const QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
6509
6511 req.setFilterFid( fid );
6512 req.setTimeout( 10000 );
6513 req.setRequestMayBeNested( true );
6514 if ( context )
6515 req.setFeedback( context->feedback() );
6516 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6517
6518 QgsFeature fet;
6519 QVariant result;
6520 if ( fIt.nextFeature( fet ) )
6521 result = QVariant::fromValue( fet );
6522
6523 return result;
6524}
6525
6526static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6527{
6528 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
6529 bool foundLayer = false;
6530 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6531
6532 //no layer found
6533 if ( !featureSource || !foundLayer )
6534 {
6535 return QVariant();
6536 }
6538 QString cacheValueKey;
6539 if ( values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
6540 {
6541 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
6542
6543 QMap <QString, QVariant>::const_iterator i = attributeMap.constBegin();
6544 QString filterString;
6545 for ( ; i != attributeMap.constEnd(); ++i )
6546 {
6547 if ( !filterString.isEmpty() )
6548 {
6549 filterString.append( " AND " );
6550 }
6551 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
6552 }
6553 cacheValueKey = QStringLiteral( "getfeature:%1:%2" ).arg( featureSource->id(), filterString );
6554 if ( context && context->hasCachedValue( cacheValueKey ) )
6555 {
6556 return context->cachedValue( cacheValueKey );
6557 }
6558 req.setFilterExpression( filterString );
6559 }
6560 else
6561 {
6562 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6563 int attributeId = featureSource->fields().lookupField( attribute );
6564 if ( attributeId == -1 )
6565 {
6566 return QVariant();
6567 }
6568
6569 const QVariant &attVal = values.at( 2 );
6570
6571 cacheValueKey = QStringLiteral( "getfeature:%1:%2:%3" ).arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
6572 if ( context && context->hasCachedValue( cacheValueKey ) )
6573 {
6574 return context->cachedValue( cacheValueKey );
6575 }
6576
6578 }
6579 req.setLimit( 1 );
6580 req.setTimeout( 10000 );
6581 req.setRequestMayBeNested( true );
6582 if ( context )
6583 req.setFeedback( context->feedback() );
6584 if ( !parent->needsGeometry() )
6585 {
6587 }
6588 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6589
6590 QgsFeature fet;
6591 QVariant res;
6592 if ( fIt.nextFeature( fet ) )
6593 {
6594 res = QVariant::fromValue( fet );
6595 }
6596
6597 if ( context )
6598 context->setCachedValue( cacheValueKey, res );
6599 return res;
6600}
6601
6602static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6603{
6604 QVariant result;
6605 QString fieldName;
6606
6607 if ( context )
6608 {
6609 if ( !values.isEmpty() )
6610 {
6611 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
6612 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
6613 fieldName = col->name();
6614 else if ( values.size() == 2 )
6615 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6616 }
6617
6618 QVariant value = values.at( 0 );
6619
6620 const QgsFields fields = context->fields();
6621 int fieldIndex = fields.lookupField( fieldName );
6622
6623 if ( fieldIndex == -1 )
6624 {
6625 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( QStringLiteral( "represent_value" ), fieldName ) );
6626 }
6627 else
6628 {
6629 // TODO this function is NOT thread safe
6631 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
6633
6634 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName, value.toString() );
6635 if ( context->hasCachedValue( cacheValueKey ) )
6636 {
6637 return context->cachedValue( cacheValueKey );
6638 }
6639
6640 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
6642
6643 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName );
6644
6645 QVariant cache;
6646 if ( !context->hasCachedValue( cacheKey ) )
6647 {
6648 cache = formatter->createCache( layer, fieldIndex, setup.config() );
6649 context->setCachedValue( cacheKey, cache );
6650 }
6651 else
6652 cache = context->cachedValue( cacheKey );
6653
6654 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
6655
6656 context->setCachedValue( cacheValueKey, result );
6657 }
6658 }
6659 else
6660 {
6661 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( QStringLiteral( "represent_value" ), fieldName ) );
6662 }
6663
6664 return result;
6665}
6666
6667static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6668{
6669 const QVariant data = values.at( 0 );
6670 const QMimeDatabase db;
6671 return db.mimeTypeForData( data.toByteArray() ).name();
6672}
6673
6674static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6675{
6676 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6677
6678 bool foundLayer = false;
6679 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [layerProperty]( QgsMapLayer * layer )-> QVariant
6680 {
6681 if ( !layer )
6682 return QVariant();
6683
6684 // here, we always prefer the layer metadata values over the older server-specific published values
6685 if ( QString::compare( layerProperty, QStringLiteral( "name" ), Qt::CaseInsensitive ) == 0 )
6686 return layer->name();
6687 else if ( QString::compare( layerProperty, QStringLiteral( "id" ), Qt::CaseInsensitive ) == 0 )
6688 return layer->id();
6689 else if ( QString::compare( layerProperty, QStringLiteral( "title" ), Qt::CaseInsensitive ) == 0 )
6690 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->serverProperties()->title();
6691 else if ( QString::compare( layerProperty, QStringLiteral( "abstract" ), Qt::CaseInsensitive ) == 0 )
6692 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
6693 else if ( QString::compare( layerProperty, QStringLiteral( "keywords" ), Qt::CaseInsensitive ) == 0 )
6694 {
6695 QStringList keywords;
6696 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
6697 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
6698 {
6699 keywords.append( it.value() );
6700 }
6701 if ( !keywords.isEmpty() )
6702 return keywords;
6703 return layer->serverProperties()->keywordList();
6704 }
6705 else if ( QString::compare( layerProperty, QStringLiteral( "data_url" ), Qt::CaseInsensitive ) == 0 )
6706 return layer->serverProperties()->dataUrl();
6707 else if ( QString::compare( layerProperty, QStringLiteral( "attribution" ), Qt::CaseInsensitive ) == 0 )
6708 {
6709 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->serverProperties()->attribution() );
6710 }
6711 else if ( QString::compare( layerProperty, QStringLiteral( "attribution_url" ), Qt::CaseInsensitive ) == 0 )
6712 return layer->serverProperties()->attributionUrl();
6713 else if ( QString::compare( layerProperty, QStringLiteral( "source" ), Qt::CaseInsensitive ) == 0 )
6714 return layer->publicSource();
6715 else if ( QString::compare( layerProperty, QStringLiteral( "min_scale" ), Qt::CaseInsensitive ) == 0 )
6716 return layer->minimumScale();
6717 else if ( QString::compare( layerProperty, QStringLiteral( "max_scale" ), Qt::CaseInsensitive ) == 0 )
6718 return layer->maximumScale();
6719 else if ( QString::compare( layerProperty, QStringLiteral( "is_editable" ), Qt::CaseInsensitive ) == 0 )
6720 return layer->isEditable();
6721 else if ( QString::compare( layerProperty, QStringLiteral( "crs" ), Qt::CaseInsensitive ) == 0 )
6722 return layer->crs().authid();
6723 else if ( QString::compare( layerProperty, QStringLiteral( "crs_definition" ), Qt::CaseInsensitive ) == 0 )
6724 return layer->crs().toProj();
6725 else if ( QString::compare( layerProperty, QStringLiteral( "crs_description" ), Qt::CaseInsensitive ) == 0 )
6726 return layer->crs().description();
6727 else if ( QString::compare( layerProperty, QStringLiteral( "crs_ellipsoid" ), Qt::CaseInsensitive ) == 0 )
6728 return layer->crs().ellipsoidAcronym();
6729 else if ( QString::compare( layerProperty, QStringLiteral( "extent" ), Qt::CaseInsensitive ) == 0 )
6730 {
6731 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
6732 QVariant result = QVariant::fromValue( extentGeom );
6733 return result;
6734 }
6735 else if ( QString::compare( layerProperty, QStringLiteral( "distance_units" ), Qt::CaseInsensitive ) == 0 )
6736 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
6737 else if ( QString::compare( layerProperty, QStringLiteral( "path" ), Qt::CaseInsensitive ) == 0 )
6738 {
6739 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
6740 return decodedUri.value( QStringLiteral( "path" ) );
6741 }
6742 else if ( QString::compare( layerProperty, QStringLiteral( "type" ), Qt::CaseInsensitive ) == 0 )
6743 {
6744 switch ( layer->type() )
6745 {
6747 return QCoreApplication::translate( "expressions", "Vector" );
6749 return QCoreApplication::translate( "expressions", "Raster" );
6751 return QCoreApplication::translate( "expressions", "Mesh" );
6753 return QCoreApplication::translate( "expressions", "Vector Tile" );
6755 return QCoreApplication::translate( "expressions", "Plugin" );
6757 return QCoreApplication::translate( "expressions", "Annotation" );
6759 return QCoreApplication::translate( "expressions", "Point Cloud" );
6761 return QCoreApplication::translate( "expressions", "Group" );
6763 return QCoreApplication::translate( "expressions", "Tiled Scene" );
6764 }
6765 }
6766 else
6767 {
6768 //vector layer methods
6769 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
6770 if ( vLayer )
6771 {
6772 if ( QString::compare( layerProperty, QStringLiteral( "storage_type" ), Qt::CaseInsensitive ) == 0 )
6773 return vLayer->storageType();
6774 else if ( QString::compare( layerProperty, QStringLiteral( "geometry_type" ), Qt::CaseInsensitive ) == 0 )
6776 else if ( QString::compare( layerProperty, QStringLiteral( "feature_count" ), Qt::CaseInsensitive ) == 0 )
6777 return QVariant::fromValue( vLayer->featureCount() );
6778 }
6779 }
6780
6781 return QVariant();
6782 }, foundLayer );
6783
6784 if ( !foundLayer )
6785 return QVariant();
6786 else
6787 return res;
6788}
6789
6790static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6791{
6792 const QString uriPart = values.at( 1 ).toString();
6793
6794 bool foundLayer = false;
6795
6796 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, uriPart]( QgsMapLayer * layer )-> QVariant
6797 {
6798 if ( !layer->dataProvider() )
6799 {
6800 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
6801 return QVariant();
6802 }
6803
6804 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
6805
6806 if ( !uriPart.isNull() )
6807 {
6808 return decodedUri.value( uriPart );
6809 }
6810 else
6811 {
6812 return decodedUri;
6813 }
6814 }, foundLayer );
6815
6816 if ( !foundLayer )
6817 {
6818 parent->setEvalErrorString( QObject::tr( "Function `decode_uri` requires a valid layer." ) );
6819 return QVariant();
6820 }
6821 else
6822 {
6823 return res;
6824 }
6825}
6826
6827static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6828{
6829 const int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6830 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6831
6832 bool foundLayer = false;
6833 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, band, layerProperty]( QgsMapLayer * layer )-> QVariant
6834 {
6835 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
6836 if ( !rl )
6837 return QVariant();
6838
6839 if ( band < 1 || band > rl->bandCount() )
6840 {
6841 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer" ).arg( band ) );
6842 return QVariant();
6843 }
6844
6846
6847 if ( QString::compare( layerProperty, QStringLiteral( "avg" ), Qt::CaseInsensitive ) == 0 )
6849 else if ( QString::compare( layerProperty, QStringLiteral( "stdev" ), Qt::CaseInsensitive ) == 0 )
6851 else if ( QString::compare( layerProperty, QStringLiteral( "min" ), Qt::CaseInsensitive ) == 0 )
6853 else if ( QString::compare( layerProperty, QStringLiteral( "max" ), Qt::CaseInsensitive ) == 0 )
6855 else if ( QString::compare( layerProperty, QStringLiteral( "range" ), Qt::CaseInsensitive ) == 0 )
6857 else if ( QString::compare( layerProperty, QStringLiteral( "sum" ), Qt::CaseInsensitive ) == 0 )
6859 else
6860 {
6861 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
6862 return QVariant();
6863 }
6864
6865 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
6866 switch ( stat )
6867 {
6869 return stats.mean;
6871 return stats.stdDev;
6873 return stats.minimumValue;
6875 return stats.maximumValue;
6877 return stats.range;
6879 return stats.sum;
6880 default:
6881 break;
6882 }
6883 return QVariant();
6884 }, foundLayer );
6885
6886 if ( !foundLayer )
6887 {
6888#if 0 // for consistency with other functions we should raise an error here, but for compatibility with old projects we don't
6889 parent->setEvalErrorString( QObject::tr( "Function `raster_statistic` requires a valid raster layer." ) );
6890#endif
6891 return QVariant();
6892 }
6893 else
6894 {
6895 return res;
6896 }
6897}
6898
6899static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6900{
6901 return values;
6902}
6903
6904static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6905{
6906 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6907 bool ascending = values.value( 1 ).toBool();
6908 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
6909 return list;
6910}
6911
6912static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6913{
6914 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
6915}
6916
6917static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6918{
6919 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
6920}
6921
6922static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6923{
6924 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
6925}
6926
6927static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6928{
6929 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6930 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
6931 int match = 0;
6932 for ( const auto &item : listB )
6933 {
6934 if ( listA.contains( item ) )
6935 match++;
6936 }
6937
6938 return QVariant( match == listB.count() );
6939}
6940
6941static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6942{
6943 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
6944}
6945
6946static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6947{
6948 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6949 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6950 if ( pos < list.length() && pos >= 0 ) return list.at( pos );
6951 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
6952 return list.at( list.length() + pos );
6953 return QVariant();
6954}
6955
6956static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6957{
6958 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6959 return list.value( 0 );
6960}
6961
6962static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6963{
6964 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6965 return list.value( list.size() - 1 );
6966}
6967
6968static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6969{
6970 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6971 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6972}
6973
6974static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6975{
6976 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6977 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6978}
6979
6980static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6981{
6982 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6983 int i = 0;
6984 double total = 0.0;
6985 for ( const QVariant &item : list )
6986 {
6987 switch ( item.userType() )
6988 {
6989 case QMetaType::Int:
6990 case QMetaType::UInt:
6991 case QMetaType::LongLong:
6992 case QMetaType::ULongLong:
6993 case QMetaType::Float:
6994 case QMetaType::Double:
6995 total += item.toDouble();
6996 ++i;
6997 break;
6998 }
6999 }
7000 return i == 0 ? QVariant() : total / i;
7001}
7002
7003static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7004{
7005 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7006 QVariantList numbers;
7007 for ( const auto &item : list )
7008 {
7009 switch ( item.userType() )
7010 {
7011 case QMetaType::Int:
7012 case QMetaType::UInt:
7013 case QMetaType::LongLong:
7014 case QMetaType::ULongLong:
7015 case QMetaType::Float:
7016 case QMetaType::Double:
7017 numbers.append( item );
7018 break;
7019 }
7020 }
7021 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7022 const int count = numbers.count();
7023 if ( count == 0 )
7024 {
7025 return QVariant();
7026 }
7027 else if ( count % 2 )
7028 {
7029 return numbers.at( count / 2 );
7030 }
7031 else
7032 {
7033 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
7034 }
7035}
7036
7037static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7038{
7039 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7040 int i = 0;
7041 double total = 0.0;
7042 for ( const QVariant &item : list )
7043 {
7044 switch ( item.userType() )
7045 {
7046 case QMetaType::Int:
7047 case QMetaType::UInt:
7048 case QMetaType::LongLong:
7049 case QMetaType::ULongLong:
7050 case QMetaType::Float:
7051 case QMetaType::Double:
7052 total += item.toDouble();
7053 ++i;
7054 break;
7055 }
7056 }
7057 return i == 0 ? QVariant() : total;
7058}
7059
7060static QVariant convertToSameType( const QVariant &value, QMetaType::Type type )
7061{
7062 QVariant result = value;
7063 result.convert( static_cast<int>( type ) );
7064 return result;
7065}
7066
7067static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7068{
7069 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7070 QHash< QVariant, int > hash;
7071 for ( const auto &item : list )
7072 {
7073 ++hash[item];
7074 }
7075 const QList< int > occurrences = hash.values();
7076 if ( occurrences.empty() )
7077 return QVariantList();
7078
7079 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7080
7081 const QString option = values.at( 1 ).toString();
7082 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
7083 {
7084 return convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7085 }
7086 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
7087 {
7088 if ( hash.isEmpty() )
7089 return QVariant();
7090
7091 return QVariant( hash.key( maxValue ) );
7092 }
7093 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
7094 {
7095 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7096 }
7097 else if ( option.compare( QLatin1String( "real_majority" ), Qt::CaseInsensitive ) == 0 )
7098 {
7099 if ( maxValue * 2 <= list.size() )
7100 return QVariant();
7101
7102 return QVariant( hash.key( maxValue ) );
7103 }
7104 else
7105 {
7106 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7107 return QVariant();
7108 }
7109}
7110
7111static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7112{
7113 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7114 QHash< QVariant, int > hash;
7115 for ( const auto &item : list )
7116 {
7117 ++hash[item];
7118 }
7119 const QList< int > occurrences = hash.values();
7120 if ( occurrences.empty() )
7121 return QVariantList();
7122
7123 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
7124
7125 const QString option = values.at( 1 ).toString();
7126 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
7127 {
7128 return convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7129 }
7130 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
7131 {
7132 if ( hash.isEmpty() )
7133 return QVariant();
7134
7135 return QVariant( hash.key( minValue ) );
7136 }
7137 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
7138 {
7139 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7140 }
7141 else if ( option.compare( QLatin1String( "real_minority" ), Qt::CaseInsensitive ) == 0 )
7142 {
7143 if ( hash.isEmpty() )
7144 return QVariant();
7145
7146 // Remove the majority, all others are minority
7147 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7148 if ( maxValue * 2 > list.size() )
7149 hash.remove( hash.key( maxValue ) );
7150
7151 return convertToSameType( hash.keys(), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7152 }
7153 else
7154 {
7155 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7156 return QVariant();
7157 }
7158}
7159
7160static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7161{
7162 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7163 list.append( values.at( 1 ) );
7164 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7165}
7166
7167static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7168{
7169 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7170 list.prepend( values.at( 1 ) );
7171 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7172}
7173
7174static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7175{
7176 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7177 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
7178 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7179}
7180
7181static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7182{
7183 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7184 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7185 if ( position < 0 )
7186 position = position + list.length();
7187 if ( position >= 0 && position < list.length() )
7188 list.removeAt( position );
7189 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7190}
7191
7192static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7193{
7194 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
7195 return QVariant();
7196
7197 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7198
7199 const QVariant toRemove = values.at( 1 );
7200 if ( QgsVariantUtils::isNull( toRemove ) )
7201 {
7202 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant & element )
7203 {
7204 return QgsVariantUtils::isNull( element );
7205 } ), list.end() );
7206 }
7207 else
7208 {
7209 list.removeAll( toRemove );
7210 }
7211 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7212}
7213
7214static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7215{
7216 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7217 {
7218 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7219
7220 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7221 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7222 {
7223 int index = list.indexOf( it.key() );
7224 while ( index >= 0 )
7225 {
7226 list.replace( index, it.value() );
7227 index = list.indexOf( it.key() );
7228 }
7229 }
7230
7231 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7232 }
7233 else if ( values.count() == 3 )
7234 {
7235 QVariantList before;
7236 QVariantList after;
7237 bool isSingleReplacement = false;
7238
7239 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
7240 {
7241 before = QVariantList() << values.at( 1 );
7242 }
7243 else
7244 {
7245 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7246 }
7247
7248 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
7249 {
7250 after = QVariantList() << values.at( 2 );
7251 isSingleReplacement = true;
7252 }
7253 else
7254 {
7255 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
7256 }
7257
7258 if ( !isSingleReplacement && before.length() != after.length() )
7259 {
7260 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
7261 return QVariant();
7262 }
7263
7264 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7265 for ( int i = 0; i < before.length(); i++ )
7266 {
7267 int index = list.indexOf( before.at( i ) );
7268 while ( index >= 0 )
7269 {
7270 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
7271 index = list.indexOf( before.at( i ) );
7272 }
7273 }
7274
7275 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7276 }
7277 else
7278 {
7279 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
7280 return QVariant();
7281 }
7282}
7283
7284static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7285{
7286 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7287 QVariantList list_new;
7288
7289 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
7290 {
7291 while ( list.removeOne( cur ) )
7292 {
7293 list_new.append( cur );
7294 }
7295 }
7296
7297 list_new.append( list );
7298
7299 return convertToSameType( list_new, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7300}
7301
7302static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7303{
7304 QVariantList list;
7305 for ( const QVariant &cur : values )
7306 {
7307 list += QgsExpressionUtils::getListValue( cur, parent );
7308 }
7309 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7310}
7311
7312static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7313{
7314 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7315 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7316 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7317 int slice_length = 0;
7318 // negative positions means positions taken relative to the end of the array
7319 if ( start_pos < 0 )
7320 {
7321 start_pos = list.length() + start_pos;
7322 }
7323 if ( end_pos >= 0 )
7324 {
7325 slice_length = end_pos - start_pos + 1;
7326 }
7327 else
7328 {
7329 slice_length = list.length() + end_pos - start_pos + 1;
7330 }
7331 //avoid negative lengths in QList.mid function
7332 if ( slice_length < 0 )
7333 {
7334 slice_length = 0;
7335 }
7336 list = list.mid( start_pos, slice_length );
7337 return list;
7338}
7339
7340static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7341{
7342 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7343 std::reverse( list.begin(), list.end() );
7344 return list;
7345}
7346
7347static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7348{
7349 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7350 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7351 for ( const QVariant &cur : array2 )
7352 {
7353 if ( array1.contains( cur ) )
7354 return QVariant( true );
7355 }
7356 return QVariant( false );
7357}
7358
7359static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7360{
7361 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7362
7363 QVariantList distinct;
7364
7365 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7366 {
7367 if ( !distinct.contains( *it ) )
7368 {
7369 distinct += ( *it );
7370 }
7371 }
7372
7373 return distinct;
7374}
7375
7376static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7377{
7378 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7379 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7380 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7381
7382 QString str;
7383
7384 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7385 {
7386 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
7387 if ( it != ( array.constEnd() - 1 ) )
7388 {
7389 str += delimiter;
7390 }
7391 }
7392
7393 return QVariant( str );
7394}
7395
7396static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7397{
7398 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7399 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7400 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7401
7402 QStringList list = str.split( delimiter );
7403 QVariantList array;
7404
7405 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
7406 {
7407 array += ( !( *it ).isEmpty() ) ? *it : empty;
7408 }
7409
7410 return array;
7411}
7412
7413static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7414{
7415 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7416 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
7417 if ( document.isNull() )
7418 return QVariant();
7419
7420 return document.toVariant();
7421}
7422
7423static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7424{
7425 Q_UNUSED( parent )
7426 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
7427 return QString( document.toJson( QJsonDocument::Compact ) );
7428}
7429
7430static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7431{
7432 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7433 if ( str.isEmpty() )
7434 return QVariantMap();
7435 str = str.trimmed();
7436
7437 return QgsHstoreUtils::parse( str );
7438}
7439
7440static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7441{
7442 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7443 return QgsHstoreUtils::build( map );
7444}
7445
7446static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7447{
7448 QVariantMap result;
7449 for ( int i = 0; i + 1 < values.length(); i += 2 )
7450 {
7451 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
7452 }
7453 return result;
7454}
7455
7456static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7457{
7458 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7459 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7460 QVariantMap resultMap;
7461
7462 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7463 {
7464 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
7465 }
7466
7467 return resultMap;
7468}
7469
7470static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7471{
7472 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
7473}
7474
7475static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7476{
7477 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
7478}
7479
7480static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7481{
7482 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7483 map.remove( values.at( 1 ).toString() );
7484 return map;
7485}
7486
7487static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7488{
7489 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7490 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
7491 return map;
7492}
7493
7494static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7495{
7496 QVariantMap result;
7497 for ( const QVariant &cur : values )
7498 {
7499 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
7500 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
7501 result.insert( it.key(), it.value() );
7502 }
7503 return result;
7504}
7505
7506static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7507{
7508 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
7509}
7510
7511static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7512{
7513 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
7514}
7515
7516static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7517{
7518 const QString envVarName = values.at( 0 ).toString();
7519 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
7520 return QVariant();
7521
7522 return QProcessEnvironment::systemEnvironment().value( envVarName );
7523}
7524
7525static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7526{
7527 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7528 if ( parent->hasEvalError() )
7529 {
7530 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "base_file_name" ) ) );
7531 return QVariant();
7532 }
7533 return QFileInfo( file ).completeBaseName();
7534}
7535
7536static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7537{
7538 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7539 if ( parent->hasEvalError() )
7540 {
7541 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_suffix" ) ) );
7542 return QVariant();
7543 }
7544 return QFileInfo( file ).completeSuffix();
7545}
7546
7547static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7548{
7549 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7550 if ( parent->hasEvalError() )
7551 {
7552 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_exists" ) ) );
7553 return QVariant();
7554 }
7555 return QFileInfo::exists( file );
7556}
7557
7558static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7559{
7560 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7561 if ( parent->hasEvalError() )
7562 {
7563 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_name" ) ) );
7564 return QVariant();
7565 }
7566 return QFileInfo( file ).fileName();
7567}
7568
7569static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7570{
7571 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7572 if ( parent->hasEvalError() )
7573 {
7574 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_file" ) ) );
7575 return QVariant();
7576 }
7577 return QFileInfo( file ).isFile();
7578}
7579
7580static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7581{
7582 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7583 if ( parent->hasEvalError() )
7584 {
7585 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_directory" ) ) );
7586 return QVariant();
7587 }
7588 return QFileInfo( file ).isDir();
7589}
7590
7591static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7592{
7593 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7594 if ( parent->hasEvalError() )
7595 {
7596 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_path" ) ) );
7597 return QVariant();
7598 }
7599 return QDir::toNativeSeparators( QFileInfo( file ).path() );
7600}
7601
7602static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7603{
7604 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7605 if ( parent->hasEvalError() )
7606 {
7607 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_size" ) ) );
7608 return QVariant();
7609 }
7610 return QFileInfo( file ).size();
7611}
7612
7613static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
7614{
7615 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
7616}
7617
7618static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7619{
7620 QVariant hash;
7621 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7622 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
7623
7624 if ( method == QLatin1String( "md4" ) )
7625 {
7626 hash = fcnHash( str, QCryptographicHash::Md4 );
7627 }
7628 else if ( method == QLatin1String( "md5" ) )
7629 {
7630 hash = fcnHash( str, QCryptographicHash::Md5 );
7631 }
7632 else if ( method == QLatin1String( "sha1" ) )
7633 {
7634 hash = fcnHash( str, QCryptographicHash::Sha1 );
7635 }
7636 else if ( method == QLatin1String( "sha224" ) )
7637 {
7638 hash = fcnHash( str, QCryptographicHash::Sha224 );
7639 }
7640 else if ( method == QLatin1String( "sha256" ) )
7641 {
7642 hash = fcnHash( str, QCryptographicHash::Sha256 );
7643 }
7644 else if ( method == QLatin1String( "sha384" ) )
7645 {
7646 hash = fcnHash( str, QCryptographicHash::Sha384 );
7647 }
7648 else if ( method == QLatin1String( "sha512" ) )
7649 {
7650 hash = fcnHash( str, QCryptographicHash::Sha512 );
7651 }
7652 else if ( method == QLatin1String( "sha3_224" ) )
7653 {
7654 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
7655 }
7656 else if ( method == QLatin1String( "sha3_256" ) )
7657 {
7658 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
7659 }
7660 else if ( method == QLatin1String( "sha3_384" ) )
7661 {
7662 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
7663 }
7664 else if ( method == QLatin1String( "sha3_512" ) )
7665 {
7666 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
7667 }
7668 else if ( method == QLatin1String( "keccak_224" ) )
7669 {
7670 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
7671 }
7672 else if ( method == QLatin1String( "keccak_256" ) )
7673 {
7674 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
7675 }
7676 else if ( method == QLatin1String( "keccak_384" ) )
7677 {
7678 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
7679 }
7680 else if ( method == QLatin1String( "keccak_512" ) )
7681 {
7682 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
7683 }
7684 else
7685 {
7686 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
7687 }
7688 return hash;
7689}
7690
7691static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7692{
7693 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
7694}
7695
7696static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7697{
7698 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
7699}
7700
7701static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7702{
7703 const QByteArray input = values.at( 0 ).toByteArray();
7704 return QVariant( QString( input.toBase64() ) );
7705}
7706
7707static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7708{
7709 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7710 QUrlQuery query;
7711 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7712 {
7713 query.addQueryItem( it.key(), it.value().toString() );
7714 }
7715 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
7716}
7717
7718static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7719{
7720 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7721 const QByteArray base64 = value.toLocal8Bit();
7722 const QByteArray decoded = QByteArray::fromBase64( base64 );
7723 return QVariant( decoded );
7724}
7725
7726typedef bool ( QgsGeometry::*RelationFunction )( const QgsGeometry &geometry ) const;
7727
7728static 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 )
7729{
7730
7731 const QVariant sourceLayerRef = context->variable( QStringLiteral( "layer" ) ); //used to detect if sourceLayer and targetLayer are the same
7732 // TODO this function is NOT thread safe
7734 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, context, parent );
7736
7737 QgsFeatureRequest request;
7738 request.setTimeout( 10000 );
7739 request.setRequestMayBeNested( true );
7740 request.setFeedback( context->feedback() );
7741
7742 // First parameter is the overlay layer
7743 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
7745
7746 const bool layerCanBeCached = node->isStatic( parent, context );
7747 QVariant targetLayerValue = node->eval( parent, context );
7749
7750 // Second parameter is the expression to evaluate (or null for testonly)
7751 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
7753 QString subExpString = node->dump();
7754
7755 bool testOnly = ( subExpString == "NULL" );
7756 // TODO this function is NOT thread safe
7758 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, context, parent );
7760 if ( !targetLayer ) // No layer, no joy
7761 {
7762 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
7763 return QVariant();
7764 }
7765
7766 // Third parameter is the filtering expression
7767 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
7769 QString filterString = node->dump();
7770 if ( filterString != "NULL" )
7771 {
7772 request.setFilterExpression( filterString ); //filter cached features
7773 }
7774
7775 // Fourth parameter is the limit
7776 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7778 QVariant limitValue = node->eval( parent, context );
7780 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
7781
7782 // Fifth parameter (for nearest only) is the max distance
7783 double max_distance = 0;
7784 if ( isNearestFunc ) //maxdistance param handling
7785 {
7786 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
7788 QVariant distanceValue = node->eval( parent, context );
7790 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
7791 }
7792
7793 // Fifth or sixth (for nearest only) parameter is the cache toggle
7794 node = QgsExpressionUtils::getNode( values.at( isNearestFunc ? 5 : 4 ), parent );
7796 QVariant cacheValue = node->eval( parent, context );
7798 bool cacheEnabled = cacheValue.toBool();
7799
7800 // Sixth parameter (for intersects only) is the min overlap (area or length)
7801 // Seventh parameter (for intersects only) is the min inscribed circle radius
7802 // Eighth parameter (for intersects only) is the return_details
7803 // Ninth parameter (for intersects only) is the sort_by_intersection_size flag
7804 double minOverlap { -1 };
7805 double minInscribedCircleRadius { -1 };
7806 bool returnDetails = false; //#spellok
7807 bool sortByMeasure = false;
7808 bool sortAscending = false;
7809 bool requireMeasures = false;
7810 bool overlapOrRadiusFilter = false;
7811 if ( isIntersectsFunc )
7812 {
7813
7814 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7816 const QVariant minOverlapValue = node->eval( parent, context );
7818 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
7819 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7821 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
7823 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
7824 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
7825 // Return measures is only effective when an expression is set
7826 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
7827 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
7828 // Sort by measures is only effective when an expression is set
7829 const QString sorting { node->eval( parent, context ).toString().toLower() };
7830 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
7831 sortAscending = sorting.startsWith( "asc" );
7832 requireMeasures = sortByMeasure || returnDetails; //#spellok
7833 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
7834 }
7835
7836
7837 FEAT_FROM_CONTEXT( context, feat )
7838 const QgsGeometry geometry = feat.geometry();
7839
7840 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
7841 {
7842 QgsCoordinateTransformContext TransformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
7843 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
7844 }
7845
7846 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
7847
7848 QgsRectangle intDomain = geometry.boundingBox();
7849 if ( bboxGrow != 0 )
7850 {
7851 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
7852 }
7853
7854 const QString cacheBase { QStringLiteral( "%1:%2:%3" ).arg( targetLayer->id(), subExpString, filterString ) };
7855
7856 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
7857 // Otherwise, it can be toggled by the user
7858 QgsSpatialIndex spatialIndex;
7859 QgsVectorLayer *cachedTarget;
7860 QList<QgsFeature> features;
7861 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
7862 {
7863 // If the cache (local spatial index) is enabled, we materialize the whole
7864 // layer, then do the request on that layer instead.
7865 const QString cacheLayer { QStringLiteral( "ovrlaylyr:%1" ).arg( cacheBase ) };
7866 const QString cacheIndex { QStringLiteral( "ovrlayidx:%1" ).arg( cacheBase ) };
7867
7868 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
7869 {
7870 cachedTarget = targetLayer->materialize( request );
7871 if ( layerCanBeCached )
7872 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
7873 }
7874 else
7875 {
7876 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
7877 }
7878
7879 if ( !context->hasCachedValue( cacheIndex ) )
7880 {
7881 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
7882 if ( layerCanBeCached )
7883 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
7884 }
7885 else
7886 {
7887 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
7888 }
7889
7890 QList<QgsFeatureId> fidsList;
7891 if ( isNearestFunc )
7892 {
7893 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
7894 }
7895 else
7896 {
7897 fidsList = spatialIndex.intersects( intDomain );
7898 }
7899
7900 QListIterator<QgsFeatureId> i( fidsList );
7901 while ( i.hasNext() )
7902 {
7903 QgsFeatureId fId2 = i.next();
7904 if ( sameLayers && feat.id() == fId2 )
7905 continue;
7906 features.append( cachedTarget->getFeature( fId2 ) );
7907 }
7908
7909 }
7910 else
7911 {
7912 // If the cache (local spatial index) is not enabled, we directly
7913 // get the features from the target layer
7914 request.setFilterRect( intDomain );
7915 QgsFeatureIterator fit = targetLayer->getFeatures( request );
7916 QgsFeature feat2;
7917 while ( fit.nextFeature( feat2 ) )
7918 {
7919 if ( sameLayers && feat.id() == feat2.id() )
7920 continue;
7921 features.append( feat2 );
7922 }
7923 }
7924
7925 QgsExpression subExpression;
7926 QgsExpressionContext subContext;
7927 if ( !testOnly )
7928 {
7929 const QString expCacheKey { QStringLiteral( "exp:%1" ).arg( cacheBase ) };
7930 const QString ctxCacheKey { QStringLiteral( "ctx:%1" ).arg( cacheBase ) };
7931
7932 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
7933 {
7934 subExpression = QgsExpression( subExpString );
7936 subExpression.prepare( &subContext );
7937 }
7938 else
7939 {
7940 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
7941 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
7942 }
7943 }
7944
7945 // //////////////////////////////////////////////////////////////////
7946 // Helper functions for geometry tests
7947
7948 // Test function for linestring geometries, returns TRUE if test passes
7949 auto testLinestring = [ = ]( const QgsGeometry intersection, double & overlapValue ) -> bool
7950 {
7951 bool testResult { false };
7952 // For return measures:
7953 QVector<double> overlapValues;
7954 for ( auto it = intersection.const_parts_begin(); ! testResult && it != intersection.const_parts_end(); ++it )
7955 {
7956 const QgsCurve *geom = qgsgeometry_cast< const QgsCurve * >( *it );
7957 // Check min overlap for intersection (if set)
7958 if ( minOverlap != -1 || requireMeasures )
7959 {
7960 overlapValue = geom->length();
7961 overlapValues.append( overlapValue );
7962 if ( minOverlap != -1 )
7963 {
7964 if ( overlapValue >= minOverlap )
7965 {
7966 testResult = true;
7967 }
7968 else
7969 {
7970 continue;
7971 }
7972 }
7973 }
7974 }
7975
7976 if ( ! overlapValues.isEmpty() )
7977 {
7978 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
7979 }
7980
7981 return testResult;
7982 };
7983
7984 // Test function for polygon geometries, returns TRUE if test passes
7985 auto testPolygon = [ = ]( const QgsGeometry intersection, double & radiusValue, double & overlapValue ) -> bool
7986 {
7987 // overlap and inscribed circle tests must be checked both (if the values are != -1)
7988 bool testResult { false };
7989 // For return measures:
7990 QVector<double> overlapValues;
7991 QVector<double> radiusValues;
7992 for ( auto it = intersection.const_parts_begin(); ( ! testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
7993 {
7994 const QgsCurvePolygon *geom = qgsgeometry_cast< const QgsCurvePolygon * >( *it );
7995 // Check min overlap for intersection (if set)
7996 if ( minOverlap != -1 || requireMeasures )
7997 {
7998 overlapValue = geom->area();
7999 overlapValues.append( geom->area() );
8000 if ( minOverlap != - 1 )
8001 {
8002 if ( overlapValue >= minOverlap )
8003 {
8004 testResult = true;
8005 }
8006 else
8007 {
8008 continue;
8009 }
8010 }
8011 }
8012
8013 // Check min inscribed circle radius for intersection (if set)
8014 if ( minInscribedCircleRadius != -1 || requireMeasures )
8015 {
8016 const QgsRectangle bbox = geom->boundingBox();
8017 const double width = bbox.width();
8018 const double height = bbox.height();
8019 const double size = width > height ? width : height;
8020 const double tolerance = size / 100.0;
8021 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
8022 testResult = radiusValue >= minInscribedCircleRadius;
8023 radiusValues.append( radiusValues );
8024 }
8025 } // end for parts
8026
8027 // Get the max values
8028 if ( !radiusValues.isEmpty() )
8029 {
8030 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
8031 }
8032
8033 if ( ! overlapValues.isEmpty() )
8034 {
8035 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8036 }
8037
8038 return testResult;
8039
8040 };
8041
8042
8043 bool found = false;
8044 int foundCount = 0;
8045 QVariantList results;
8046
8047 QListIterator<QgsFeature> i( features );
8048 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
8049 {
8050
8051 QgsFeature feat2 = i.next();
8052
8053
8054 if ( ! relationFunction || ( geometry.*relationFunction )( feat2.geometry() ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
8055 {
8056
8057 double overlapValue = -1;
8058 double radiusValue = -1;
8059
8060 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
8061 {
8062 const QgsGeometry intersection { geometry.intersection( feat2.geometry() ) };
8063
8064 // Depending on the intersection geometry type and on the geometry type of
8065 // the tested geometry we can run different tests and collect different measures
8066 // that can be used for sorting (if required).
8067 switch ( intersection.type() )
8068 {
8069
8071 {
8072
8073 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
8074 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
8075
8076 if ( ! testResult && overlapOrRadiusFilter )
8077 {
8078 continue;
8079 }
8080
8081 break;
8082 }
8083
8085 {
8086
8087 // If the intersection is a linestring and a minimum circle is required
8088 // we can discard this result immediately.
8089 if ( minInscribedCircleRadius != -1 )
8090 {
8091 continue;
8092 }
8093
8094 // Otherwise a test for the overlap value is performed.
8095 const bool testResult { testLinestring( intersection, overlapValue ) };
8096
8097 if ( ! testResult && overlapOrRadiusFilter )
8098 {
8099 continue;
8100 }
8101
8102 break;
8103 }
8104
8106 {
8107
8108 // If the intersection is a point and a minimum circle is required
8109 // we can discard this result immediately.
8110 if ( minInscribedCircleRadius != -1 )
8111 {
8112 continue;
8113 }
8114
8115 bool testResult { false };
8116 if ( minOverlap != -1 || requireMeasures )
8117 {
8118 // Initially set this to 0 because it's a point intersection...
8119 overlapValue = 0;
8120 // ... but if the target geometry is not a point and the source
8121 // geometry is a point, we must record the length or the area
8122 // of the intersected geometry and use that as a measure for
8123 // sorting or reporting.
8124 if ( geometry.type() == Qgis::GeometryType::Point )
8125 {
8126 switch ( feat2.geometry().type() )
8127 {
8131 {
8132 break;
8133 }
8135 {
8136 testResult = testLinestring( feat2.geometry(), overlapValue );
8137 break;
8138 }
8140 {
8141 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
8142 break;
8143 }
8144 }
8145 }
8146
8147 if ( ! testResult && overlapOrRadiusFilter )
8148 {
8149 continue;
8150 }
8151
8152 }
8153 break;
8154 }
8155
8158 {
8159 continue;
8160 }
8161 }
8162 }
8163
8164 found = true;
8165 foundCount++;
8166
8167 // We just want a single boolean result if there is any intersect: finish and return true
8168 if ( testOnly )
8169 break;
8170
8171 if ( !invert )
8172 {
8173 // We want a list of attributes / geometries / other expression values, evaluate now
8174 subContext.setFeature( feat2 );
8175 const QVariant expResult = subExpression.evaluate( &subContext );
8176
8177 if ( requireMeasures )
8178 {
8179 QVariantMap resultRecord;
8180 resultRecord.insert( QStringLiteral( "id" ), feat2.id() );
8181 resultRecord.insert( QStringLiteral( "result" ), expResult );
8182 // Overlap is always added because return measures was set
8183 resultRecord.insert( QStringLiteral( "overlap" ), overlapValue );
8184 // Radius is only added when is different than -1 (because for linestrings is not set)
8185 if ( radiusValue != -1 )
8186 {
8187 resultRecord.insert( QStringLiteral( "radius" ), radiusValue );
8188 }
8189 results.append( resultRecord );
8190 }
8191 else
8192 {
8193 results.append( expResult );
8194 }
8195 }
8196 else
8197 {
8198 // If not, results is a list of found ids, which we'll inverse and evaluate below
8199 results.append( feat2.id() );
8200 }
8201 }
8202 }
8203
8204 if ( testOnly )
8205 {
8206 if ( invert )
8207 found = !found;//for disjoint condition
8208 return found;
8209 }
8210
8211 if ( !invert )
8212 {
8213 if ( requireMeasures )
8214 {
8215 if ( sortByMeasure )
8216 {
8217 std::sort( results.begin(), results.end(), [ sortAscending ]( const QVariant & recordA, const QVariant & recordB ) -> bool
8218 {
8219 return sortAscending ?
8220 recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble()
8221 : recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble();
8222 } );
8223 }
8224 // Resize
8225 if ( limit > 0 && results.size() > limit )
8226 {
8227 results.erase( results.begin() + limit );
8228 }
8229
8230 if ( ! returnDetails ) //#spellok
8231 {
8232 QVariantList expResults;
8233 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
8234 {
8235 expResults.append( it->toMap().value( QStringLiteral( "result" ) ) );
8236 }
8237 return expResults;
8238 }
8239 }
8240
8241 return results;
8242 }
8243
8244 // for disjoint condition returns the results for cached layers not intersected feats
8245 QVariantList disjoint_results;
8246 QgsFeature feat2;
8247 QgsFeatureRequest request2;
8248 request2.setLimit( limit );
8249 if ( context )
8250 request2.setFeedback( context->feedback() );
8251 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
8252 while ( fi.nextFeature( feat2 ) )
8253 {
8254 if ( !results.contains( feat2.id() ) )
8255 {
8256 subContext.setFeature( feat2 );
8257 disjoint_results.append( subExpression.evaluate( &subContext ) );
8258 }
8259 }
8260 return disjoint_results;
8261
8262}
8263
8264// Intersect functions:
8265
8266static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8267{
8268 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, false, 0, false, true );
8269}
8270
8271static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8272{
8273 return executeGeomOverlay( values, context, parent, &QgsGeometry::contains );
8274}
8275
8276static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8277{
8278 return executeGeomOverlay( values, context, parent, &QgsGeometry::crosses );
8279}
8280
8281static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8282{
8283 return executeGeomOverlay( values, context, parent, &QgsGeometry::equals, false, 0.01 ); //grow amount should adapt to current units
8284}
8285
8286static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8287{
8288 return executeGeomOverlay( values, context, parent, &QgsGeometry::touches, false, 0.01 ); //grow amount should adapt to current units
8289}
8290
8291static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8292{
8293 return executeGeomOverlay( values, context, parent, &QgsGeometry::within );
8294}
8296static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8297{
8298 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, true, 0, false, true );
8299}
8300
8301static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8302{
8303 return executeGeomOverlay( values, context, parent, nullptr, false, 0, true );
8304}
8305
8306const QList<QgsExpressionFunction *> &QgsExpression::Functions()
8307{
8308 // The construction of the list isn't thread-safe, and without the mutex,
8309 // crashes in the WFS provider may occur, since it can parse expressions
8310 // in parallel.
8311 // The mutex needs to be recursive.
8312 QMutexLocker locker( &sFunctionsMutex );
8313
8314 QList<QgsExpressionFunction *> &functions = *sFunctions();
8315
8316 if ( functions.isEmpty() )
8317 {
8319 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
8320 << QgsExpressionFunction::Parameter( QStringLiteral( "group_by" ), true )
8321 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true );
8322
8323 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
8324 aggParamsConcat << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8325 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
8326
8327 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
8328 aggParamsArray << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
8329
8330 functions
8331 << new QgsStaticExpressionFunction( QStringLiteral( "sqrt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnSqrt, QStringLiteral( "Math" ) )
8332 << new QgsStaticExpressionFunction( QStringLiteral( "radians" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "degrees" ) ), fcnRadians, QStringLiteral( "Math" ) )
8333 << new QgsStaticExpressionFunction( QStringLiteral( "degrees" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "radians" ) ), fcnDegrees, QStringLiteral( "Math" ) )
8334 << new QgsStaticExpressionFunction( QStringLiteral( "azimuth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnAzimuth, QStringLiteral( "GeometryGroup" ) )
8335 << new QgsStaticExpressionFunction( QStringLiteral( "bearing" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "source_crs" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "ellipsoid" ), true, QVariant() ), fcnBearing, QStringLiteral( "GeometryGroup" ) )
8336 << new QgsStaticExpressionFunction( QStringLiteral( "inclination" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnInclination, QStringLiteral( "GeometryGroup" ) )
8337 << 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" ) )
8338 << new QgsStaticExpressionFunction( QStringLiteral( "abs" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAbs, QStringLiteral( "Math" ) )
8339 << new QgsStaticExpressionFunction( QStringLiteral( "cos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnCos, QStringLiteral( "Math" ) )
8340 << new QgsStaticExpressionFunction( QStringLiteral( "sin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnSin, QStringLiteral( "Math" ) )
8341 << new QgsStaticExpressionFunction( QStringLiteral( "tan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnTan, QStringLiteral( "Math" ) )
8342 << new QgsStaticExpressionFunction( QStringLiteral( "asin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAsin, QStringLiteral( "Math" ) )
8343 << new QgsStaticExpressionFunction( QStringLiteral( "acos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAcos, QStringLiteral( "Math" ) )
8344 << new QgsStaticExpressionFunction( QStringLiteral( "atan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAtan, QStringLiteral( "Math" ) )
8345 << new QgsStaticExpressionFunction( QStringLiteral( "atan2" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ), fcnAtan2, QStringLiteral( "Math" ) )
8346 << new QgsStaticExpressionFunction( QStringLiteral( "exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnExp, QStringLiteral( "Math" ) )
8347 << new QgsStaticExpressionFunction( QStringLiteral( "ln" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLn, QStringLiteral( "Math" ) )
8348 << new QgsStaticExpressionFunction( QStringLiteral( "log10" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog10, QStringLiteral( "Math" ) )
8349 << new QgsStaticExpressionFunction( QStringLiteral( "log" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "base" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog, QStringLiteral( "Math" ) )
8350 << new QgsStaticExpressionFunction( QStringLiteral( "round" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ), fcnRound, QStringLiteral( "Math" ) );
8351
8352 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( QStringLiteral( "rand" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRnd, QStringLiteral( "Math" ) );
8353 randFunc->setIsStatic( false );
8354 functions << randFunc;
8355
8356 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" ) );
8357 randfFunc->setIsStatic( false );
8358 functions << randfFunc;
8359
8360 functions
8361 << new QgsStaticExpressionFunction( QStringLiteral( "max" ), -1, fcnMax, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8362 << new QgsStaticExpressionFunction( QStringLiteral( "min" ), -1, fcnMin, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8363 << new QgsStaticExpressionFunction( QStringLiteral( "clamp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnClamp, QStringLiteral( "Math" ) )
8364 << 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" ) )
8365 << new QgsStaticExpressionFunction( QStringLiteral( "scale_polynomial" ), 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" ) ), fcnPolynomialScale, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "scale_exp" ) )
8366 << new QgsStaticExpressionFunction( QStringLiteral( "scale_exponential" ), 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" ) ), fcnExponentialScale, QStringLiteral( "Math" ) )
8367 << new QgsStaticExpressionFunction( QStringLiteral( "floor" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnFloor, QStringLiteral( "Math" ) )
8368 << new QgsStaticExpressionFunction( QStringLiteral( "ceil" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnCeil, QStringLiteral( "Math" ) )
8369 << new QgsStaticExpressionFunction( QStringLiteral( "pi" ), 0, fcnPi, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$pi" ) )
8370 << new QgsStaticExpressionFunction( QStringLiteral( "to_int" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInt, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toint" ) )
8371 << new QgsStaticExpressionFunction( QStringLiteral( "to_real" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToReal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toreal" ) )
8372 << 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" ) )
8373 << 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" ) )
8374 << 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" ) )
8375 << 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" ) )
8376 << 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" ) )
8377 << 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" ) )
8378 << 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" ) )
8379 << new QgsStaticExpressionFunction( QStringLiteral( "to_decimal" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToDecimal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todecimal" ) )
8380 << new QgsStaticExpressionFunction( QStringLiteral( "coalesce" ), -1, fcnCoalesce, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8381 << new QgsStaticExpressionFunction( QStringLiteral( "nullif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value2" ) ), fcnNullIf, QStringLiteral( "Conditionals" ) )
8382 << 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 )
8383 << new QgsStaticExpressionFunction( QStringLiteral( "try" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "alternative" ), true, QVariant() ), fcnTry, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
8384
8385 << new QgsStaticExpressionFunction( QStringLiteral( "aggregate" ),
8387 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8388 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
8389 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
8390 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8391 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8392 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
8393 fcnAggregate,
8394 QStringLiteral( "Aggregates" ),
8395 QString(),
8396 []( const QgsExpressionNodeFunction * node )
8397 {
8398 // usesGeometry callback: return true if @parent variable is referenced
8399
8400 if ( !node )
8401 return true;
8402
8403 if ( !node->args() )
8404 return false;
8405
8406 QSet<QString> referencedVars;
8407 if ( node->args()->count() > 2 )
8408 {
8409 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8410 referencedVars = subExpressionNode->referencedVariables();
8411 }
8412
8413 if ( node->args()->count() > 3 )
8414 {
8415 QgsExpressionNode *filterNode = node->args()->at( 3 );
8416 referencedVars.unite( filterNode->referencedVariables() );
8417 }
8418 return referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() );
8419 },
8420 []( const QgsExpressionNodeFunction * node )
8421 {
8422 // referencedColumns callback: return AllAttributes if @parent variable is referenced
8423
8424 if ( !node )
8425 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8426
8427 if ( !node->args() )
8428 return QSet<QString>();
8429
8430 QSet<QString> referencedCols;
8431 QSet<QString> referencedVars;
8432
8433 if ( node->args()->count() > 2 )
8434 {
8435 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8436 referencedVars = subExpressionNode->referencedVariables();
8437 referencedCols = subExpressionNode->referencedColumns();
8438 }
8439 if ( node->args()->count() > 3 )
8440 {
8441 QgsExpressionNode *filterNode = node->args()->at( 3 );
8442 referencedVars = filterNode->referencedVariables();
8443 referencedCols.unite( filterNode->referencedColumns() );
8444 }
8445
8446 if ( referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() ) )
8447 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8448 else
8449 return referencedCols;
8450 },
8451 true
8452 )
8453
8454 << new QgsStaticExpressionFunction( QStringLiteral( "relation_aggregate" ), QgsExpressionFunction::ParameterList()
8455 << QgsExpressionFunction::Parameter( QStringLiteral( "relation" ) )
8456 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
8457 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
8458 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8459 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
8460 fcnAggregateRelation, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true )
8461
8462 << new QgsStaticExpressionFunction( QStringLiteral( "count" ), aggParams, fcnAggregateCount, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8463 << new QgsStaticExpressionFunction( QStringLiteral( "count_distinct" ), aggParams, fcnAggregateCountDistinct, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8464 << new QgsStaticExpressionFunction( QStringLiteral( "count_missing" ), aggParams, fcnAggregateCountMissing, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8465 << new QgsStaticExpressionFunction( QStringLiteral( "minimum" ), aggParams, fcnAggregateMin, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8466 << new QgsStaticExpressionFunction( QStringLiteral( "maximum" ), aggParams, fcnAggregateMax, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8467 << new QgsStaticExpressionFunction( QStringLiteral( "sum" ), aggParams, fcnAggregateSum, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8468 << new QgsStaticExpressionFunction( QStringLiteral( "mean" ), aggParams, fcnAggregateMean, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8469 << new QgsStaticExpressionFunction( QStringLiteral( "median" ), aggParams, fcnAggregateMedian, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8470 << new QgsStaticExpressionFunction( QStringLiteral( "stdev" ), aggParams, fcnAggregateStdev, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8471 << new QgsStaticExpressionFunction( QStringLiteral( "range" ), aggParams, fcnAggregateRange, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8472 << new QgsStaticExpressionFunction( QStringLiteral( "minority" ), aggParams, fcnAggregateMinority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8473 << new QgsStaticExpressionFunction( QStringLiteral( "majority" ), aggParams, fcnAggregateMajority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8474 << new QgsStaticExpressionFunction( QStringLiteral( "q1" ), aggParams, fcnAggregateQ1, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8475 << new QgsStaticExpressionFunction( QStringLiteral( "q3" ), aggParams, fcnAggregateQ3, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8476 << new QgsStaticExpressionFunction( QStringLiteral( "iqr" ), aggParams, fcnAggregateIQR, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8477 << new QgsStaticExpressionFunction( QStringLiteral( "min_length" ), aggParams, fcnAggregateMinLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8478 << new QgsStaticExpressionFunction( QStringLiteral( "max_length" ), aggParams, fcnAggregateMaxLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8479 << new QgsStaticExpressionFunction( QStringLiteral( "collect" ), aggParams, fcnAggregateCollectGeometry, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8480 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate" ), aggParamsConcat, fcnAggregateStringConcat, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8481 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate_unique" ), aggParamsConcat, fcnAggregateStringConcatUnique, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8482 << new QgsStaticExpressionFunction( QStringLiteral( "array_agg" ), aggParamsArray, fcnAggregateArray, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8483
8484 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_match" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpMatch, QStringList() << QStringLiteral( "Conditionals" ) << QStringLiteral( "String" ) )
8485 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_matches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnRegexpMatches, QStringLiteral( "Arrays" ) )
8486
8487 << new QgsStaticExpressionFunction( QStringLiteral( "now" ), 0, fcnNow, QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$now" ) )
8488 << new QgsStaticExpressionFunction( QStringLiteral( "age" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime1" ) )
8489 << QgsExpressionFunction::Parameter( QStringLiteral( "datetime2" ) ),
8490 fcnAge, QStringLiteral( "Date and Time" ) )
8491 << new QgsStaticExpressionFunction( QStringLiteral( "year" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnYear, QStringLiteral( "Date and Time" ) )
8492 << new QgsStaticExpressionFunction( QStringLiteral( "month" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnMonth, QStringLiteral( "Date and Time" ) )
8493 << new QgsStaticExpressionFunction( QStringLiteral( "week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnWeek, QStringLiteral( "Date and Time" ) )
8494 << new QgsStaticExpressionFunction( QStringLiteral( "day" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDay, QStringLiteral( "Date and Time" ) )
8495 << new QgsStaticExpressionFunction( QStringLiteral( "hour" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnHour, QStringLiteral( "Date and Time" ) )
8496 << new QgsStaticExpressionFunction( QStringLiteral( "minute" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnMinute, QStringLiteral( "Date and Time" ) )
8497 << new QgsStaticExpressionFunction( QStringLiteral( "second" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnSeconds, QStringLiteral( "Date and Time" ) )
8498 << new QgsStaticExpressionFunction( QStringLiteral( "epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnEpoch, QStringLiteral( "Date and Time" ) )
8499 << new QgsStaticExpressionFunction( QStringLiteral( "datetime_from_epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "long" ) ), fcnDateTimeFromEpoch, QStringLiteral( "Date and Time" ) )
8500 << new QgsStaticExpressionFunction( QStringLiteral( "day_of_week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDayOfWeek, QStringLiteral( "Date and Time" ) )
8501 << new QgsStaticExpressionFunction( QStringLiteral( "make_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
8502 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
8503 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) ),
8504 fcnMakeDate, QStringLiteral( "Date and Time" ) )
8505 << new QgsStaticExpressionFunction( QStringLiteral( "make_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
8506 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
8507 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
8508 fcnMakeTime, QStringLiteral( "Date and Time" ) )
8509 << new QgsStaticExpressionFunction( QStringLiteral( "make_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
8510 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
8511 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) )
8512 << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
8513 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
8514 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
8515 fcnMakeDateTime, QStringLiteral( "Date and Time" ) )
8516 << new QgsStaticExpressionFunction( QStringLiteral( "make_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "years" ), true, 0 )
8517 << QgsExpressionFunction::Parameter( QStringLiteral( "months" ), true, 0 )
8518 << QgsExpressionFunction::Parameter( QStringLiteral( "weeks" ), true, 0 )
8519 << QgsExpressionFunction::Parameter( QStringLiteral( "days" ), true, 0 )
8520 << QgsExpressionFunction::Parameter( QStringLiteral( "hours" ), true, 0 )
8521 << QgsExpressionFunction::Parameter( QStringLiteral( "minutes" ), true, 0 )
8522 << QgsExpressionFunction::Parameter( QStringLiteral( "seconds" ), true, 0 ),
8523 fcnMakeInterval, QStringLiteral( "Date and Time" ) )
8524 << new QgsStaticExpressionFunction( QStringLiteral( "lower" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnLower, QStringLiteral( "String" ) )
8525 << new QgsStaticExpressionFunction( QStringLiteral( "upper" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnUpper, QStringLiteral( "String" ) )
8526 << new QgsStaticExpressionFunction( QStringLiteral( "title" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTitle, QStringLiteral( "String" ) )
8527 << new QgsStaticExpressionFunction( QStringLiteral( "trim" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTrim, QStringLiteral( "String" ) )
8528 << new QgsStaticExpressionFunction( QStringLiteral( "ltrim" ), QgsExpressionFunction::ParameterList()
8529 << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) )
8530 << QgsExpressionFunction::Parameter( QStringLiteral( "characters" ), true, QStringLiteral( " " ) ), fcnLTrim, QStringLiteral( "String" ) )
8531 << new QgsStaticExpressionFunction( QStringLiteral( "rtrim" ), QgsExpressionFunction::ParameterList()
8532 << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) )
8533 << QgsExpressionFunction::Parameter( QStringLiteral( "characters" ), true, QStringLiteral( " " ) ), fcnRTrim, QStringLiteral( "String" ) )
8534 << new QgsStaticExpressionFunction( QStringLiteral( "levenshtein" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLevenshtein, QStringLiteral( "Fuzzy Matching" ) )
8535 << new QgsStaticExpressionFunction( QStringLiteral( "longest_common_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLCS, QStringLiteral( "Fuzzy Matching" ) )
8536 << new QgsStaticExpressionFunction( QStringLiteral( "hamming_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnHamming, QStringLiteral( "Fuzzy Matching" ) )
8537 << new QgsStaticExpressionFunction( QStringLiteral( "soundex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnSoundex, QStringLiteral( "Fuzzy Matching" ) )
8538 << new QgsStaticExpressionFunction( QStringLiteral( "char" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "code" ) ), fcnChar, QStringLiteral( "String" ) )
8539 << new QgsStaticExpressionFunction( QStringLiteral( "ascii" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnAscii, QStringLiteral( "String" ) )
8540 << new QgsStaticExpressionFunction( QStringLiteral( "wordwrap" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "" ), fcnWordwrap, QStringLiteral( "String" ) )
8541 << new QgsStaticExpressionFunction( QStringLiteral( "length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ), true, "" ), fcnLength, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "GeometryGroup" ) )
8542 << new QgsStaticExpressionFunction( QStringLiteral( "length3D" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLength3D, QStringLiteral( "GeometryGroup" ) )
8543 << new QgsStaticExpressionFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) )
8544 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_replace" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) )
8545 << QgsExpressionFunction::Parameter( QStringLiteral( "replacement" ) ), fcnRegexpReplace, QStringLiteral( "String" ) )
8546 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpSubstr, QStringLiteral( "String" ) )
8547 << new QgsStaticExpressionFunction( QStringLiteral( "substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ), true ), fcnSubstr, QStringLiteral( "String" ), QString(),
8548 false, QSet< QString >(), false, QStringList(), true )
8549 << new QgsStaticExpressionFunction( QStringLiteral( "concat" ), -1, fcnConcat, QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8550 << new QgsStaticExpressionFunction( QStringLiteral( "strpos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "haystack" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "needle" ) ), fcnStrpos, QStringLiteral( "String" ) )
8551 << new QgsStaticExpressionFunction( QStringLiteral( "left" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnLeft, QStringLiteral( "String" ) )
8552 << new QgsStaticExpressionFunction( QStringLiteral( "right" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnRight, QStringLiteral( "String" ) )
8553 << new QgsStaticExpressionFunction( QStringLiteral( "rpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnRPad, QStringLiteral( "String" ) )
8554 << new QgsStaticExpressionFunction( QStringLiteral( "lpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnLPad, QStringLiteral( "String" ) )
8555 << new QgsStaticExpressionFunction( QStringLiteral( "format" ), -1, fcnFormatString, QStringLiteral( "String" ) )
8556 << new QgsStaticExpressionFunction( QStringLiteral( "format_number" ), QgsExpressionFunction::ParameterList()
8557 << QgsExpressionFunction::Parameter( QStringLiteral( "number" ) )
8558 << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 )
8559 << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() )
8560 << QgsExpressionFunction::Parameter( QStringLiteral( "omit_group_separators" ), true, false )
8561 << QgsExpressionFunction::Parameter( QStringLiteral( "trim_trailing_zeroes" ), true, false ), fcnFormatNumber, QStringLiteral( "String" ) )
8562 << 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" ) )
8563 << new QgsStaticExpressionFunction( QStringLiteral( "color_grayscale_average" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ), fcnColorGrayscaleAverage, QStringLiteral( "Color" ) )
8564 << new QgsStaticExpressionFunction( QStringLiteral( "color_mix_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
8565 << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
8566 << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
8567 fcnColorMixRgb, QStringLiteral( "Color" ) )
8568 << new QgsStaticExpressionFunction( QStringLiteral( "color_mix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
8569 << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
8570 << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
8571 fcnColorMix, QStringLiteral( "Color" ) )
8572 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8573 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8574 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) ),
8575 fcnColorRgb, QStringLiteral( "Color" ) )
8576 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgbf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8577 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8578 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
8579 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8580 fcnColorRgbF, QStringLiteral( "Color" ) )
8581 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgba" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8582 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8583 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
8584 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8585 fncColorRgba, QStringLiteral( "Color" ) )
8586 << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
8587 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8588 fcnRampColor, QStringLiteral( "Color" ) )
8589 << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color_object" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
8590 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8591 fcnRampColorObject, QStringLiteral( "Color" ) )
8592 << new QgsStaticExpressionFunction( QStringLiteral( "create_ramp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
8593 << QgsExpressionFunction::Parameter( QStringLiteral( "discrete" ), true, false ),
8594 fcnCreateRamp, QStringLiteral( "Color" ) )
8595 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8596 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8597 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) ),
8598 fcnColorHsl, QStringLiteral( "Color" ) )
8599 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsla" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8600 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8601 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
8602 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8603 fncColorHsla, QStringLiteral( "Color" ) )
8604 << new QgsStaticExpressionFunction( QStringLiteral( "color_hslf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8605 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8606 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
8607 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8608 fcnColorHslF, QStringLiteral( "Color" ) )
8609 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsv" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8610 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8611 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8612 fcnColorHsv, QStringLiteral( "Color" ) )
8613 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsva" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8614 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8615 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8616 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8617 fncColorHsva, QStringLiteral( "Color" ) )
8618 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsvf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8619 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8620 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8621 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8622 fcnColorHsvF, QStringLiteral( "Color" ) )
8623 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyk" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8624 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8625 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8626 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) ),
8627 fcnColorCmyk, QStringLiteral( "Color" ) )
8628 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyka" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8629 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8630 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8631 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
8632 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8633 fncColorCmyka, QStringLiteral( "Color" ) )
8634 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmykf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8635 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8636 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8637 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
8638 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8639 fcnColorCmykF, QStringLiteral( "Color" ) )
8640 << new QgsStaticExpressionFunction( QStringLiteral( "color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8641 << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ),
8642 fncColorPart, QStringLiteral( "Color" ) )
8643 << new QgsStaticExpressionFunction( QStringLiteral( "darker" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8644 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
8645 fncDarker, QStringLiteral( "Color" ) )
8646 << new QgsStaticExpressionFunction( QStringLiteral( "lighter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8647 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
8648 fncLighter, QStringLiteral( "Color" ) )
8649 << new QgsStaticExpressionFunction( QStringLiteral( "set_color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fncSetColorPart, QStringLiteral( "Color" ) )
8650
8651 // file info
8652 << new QgsStaticExpressionFunction( QStringLiteral( "base_file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8653 fcnBaseFileName, QStringLiteral( "Files and Paths" ) )
8654 << new QgsStaticExpressionFunction( QStringLiteral( "file_suffix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8655 fcnFileSuffix, QStringLiteral( "Files and Paths" ) )
8656 << new QgsStaticExpressionFunction( QStringLiteral( "file_exists" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8657 fcnFileExists, QStringLiteral( "Files and Paths" ) )
8658 << new QgsStaticExpressionFunction( QStringLiteral( "file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8659 fcnFileName, QStringLiteral( "Files and Paths" ) )
8660 << new QgsStaticExpressionFunction( QStringLiteral( "is_file" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8661 fcnPathIsFile, QStringLiteral( "Files and Paths" ) )
8662 << new QgsStaticExpressionFunction( QStringLiteral( "is_directory" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8663 fcnPathIsDir, QStringLiteral( "Files and Paths" ) )
8664 << new QgsStaticExpressionFunction( QStringLiteral( "file_path" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8665 fcnFilePath, QStringLiteral( "Files and Paths" ) )
8666 << new QgsStaticExpressionFunction( QStringLiteral( "file_size" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8667 fcnFileSize, QStringLiteral( "Files and Paths" ) )
8668
8669 << new QgsStaticExpressionFunction( QStringLiteral( "exif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tag" ), true ),
8670 fcnExif, QStringLiteral( "Files and Paths" ) )
8671 << new QgsStaticExpressionFunction( QStringLiteral( "exif_geotag" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8672 fcnExifGeoTag, QStringLiteral( "GeometryGroup" ) )
8673
8674 // hash
8675 << new QgsStaticExpressionFunction( QStringLiteral( "hash" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "method" ) ),
8676 fcnGenericHash, QStringLiteral( "Conversions" ) )
8677 << new QgsStaticExpressionFunction( QStringLiteral( "md5" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8678 fcnHashMd5, QStringLiteral( "Conversions" ) )
8679 << new QgsStaticExpressionFunction( QStringLiteral( "sha256" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8680 fcnHashSha256, QStringLiteral( "Conversions" ) )
8681
8682 //base64
8683 << new QgsStaticExpressionFunction( QStringLiteral( "to_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8684 fcnToBase64, QStringLiteral( "Conversions" ) )
8685 << new QgsStaticExpressionFunction( QStringLiteral( "from_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8686 fcnFromBase64, QStringLiteral( "Conversions" ) )
8687
8688 // deprecated stuff - hidden from users
8689 << new QgsStaticExpressionFunction( QStringLiteral( "$scale" ), QgsExpressionFunction::ParameterList(), fcnMapScale, QStringLiteral( "deprecated" ) );
8690
8691 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( QStringLiteral( "$geometry" ), 0, fcnGeometry, QStringLiteral( "GeometryGroup" ), QString(), true );
8692 geomFunc->setIsStatic( false );
8693 functions << geomFunc;
8694
8695 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( QStringLiteral( "$area" ), 0, fcnGeomArea, QStringLiteral( "GeometryGroup" ), QString(), true );
8696 areaFunc->setIsStatic( false );
8697 functions << areaFunc;
8698
8699 functions << new QgsStaticExpressionFunction( QStringLiteral( "area" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnArea, QStringLiteral( "GeometryGroup" ) );
8700
8701 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( QStringLiteral( "$length" ), 0, fcnGeomLength, QStringLiteral( "GeometryGroup" ), QString(), true );
8702 lengthFunc->setIsStatic( false );
8703 functions << lengthFunc;
8704
8705 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( QStringLiteral( "$perimeter" ), 0, fcnGeomPerimeter, QStringLiteral( "GeometryGroup" ), QString(), true );
8706 perimeterFunc->setIsStatic( false );
8707 functions << perimeterFunc;
8708
8709 functions << new QgsStaticExpressionFunction( QStringLiteral( "perimeter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPerimeter, QStringLiteral( "GeometryGroup" ) );
8710
8711 functions << new QgsStaticExpressionFunction( QStringLiteral( "roundness" ),
8713 fcnRoundness, QStringLiteral( "GeometryGroup" ) );
8714
8715 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x" ), 0, fcnX, QStringLiteral( "GeometryGroup" ), QString(), true );
8716 xFunc->setIsStatic( false );
8717 functions << xFunc;
8718
8719 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y" ), 0, fcnY, QStringLiteral( "GeometryGroup" ), QString(), true );
8720 yFunc->setIsStatic( false );
8721 functions << yFunc;
8722
8723 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( QStringLiteral( "$z" ), 0, fcnZ, QStringLiteral( "GeometryGroup" ), QString(), true );
8724 zFunc->setIsStatic( false );
8725 functions << zFunc;
8726
8727 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions
8728 {
8729 { QStringLiteral( "overlay_intersects" ), fcnGeomOverlayIntersects },
8730 { QStringLiteral( "overlay_contains" ), fcnGeomOverlayContains },
8731 { QStringLiteral( "overlay_crosses" ), fcnGeomOverlayCrosses },
8732 { QStringLiteral( "overlay_equals" ), fcnGeomOverlayEquals },
8733 { QStringLiteral( "overlay_touches" ), fcnGeomOverlayTouches },
8734 { QStringLiteral( "overlay_disjoint" ), fcnGeomOverlayDisjoint },
8735 { QStringLiteral( "overlay_within" ), fcnGeomOverlayWithin },
8736 };
8737 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
8738 while ( i.hasNext() )
8739 {
8740 i.next();
8742 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8743 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
8744 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8745 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( -1 ), true )
8746 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false )
8747 << QgsExpressionFunction::Parameter( QStringLiteral( "min_overlap" ), true, QVariant( -1 ), false )
8748 << QgsExpressionFunction::Parameter( QStringLiteral( "min_inscribed_circle_radius" ), true, QVariant( -1 ), false )
8749 << QgsExpressionFunction::Parameter( QStringLiteral( "return_details" ), true, false, false )
8750 << QgsExpressionFunction::Parameter( QStringLiteral( "sort_by_intersection_size" ), true, QString(), false ),
8751 i.value(), QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
8752
8753 // The current feature is accessed for the geometry, so this should not be cached
8754 fcnGeomOverlayFunc->setIsStatic( false );
8755 functions << fcnGeomOverlayFunc;
8756 }
8757
8758 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction( QStringLiteral( "overlay_nearest" ), QgsExpressionFunction::ParameterList()
8759 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8760 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
8761 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8762 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( 1 ), true )
8763 << QgsExpressionFunction::Parameter( QStringLiteral( "max_distance" ), true, 0 )
8764 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false ),
8765 fcnGeomOverlayNearest, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
8766 // The current feature is accessed for the geometry, so this should not be cached
8767 fcnGeomOverlayNearestFunc->setIsStatic( false );
8768 functions << fcnGeomOverlayNearestFunc;
8769
8770 functions
8771 << new QgsStaticExpressionFunction( QStringLiteral( "is_valid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomIsValid, QStringLiteral( "GeometryGroup" ) )
8772 << new QgsStaticExpressionFunction( QStringLiteral( "x" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomX, QStringLiteral( "GeometryGroup" ) )
8773 << new QgsStaticExpressionFunction( QStringLiteral( "y" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomY, QStringLiteral( "GeometryGroup" ) )
8774 << new QgsStaticExpressionFunction( QStringLiteral( "z" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomZ, QStringLiteral( "GeometryGroup" ) )
8775 << new QgsStaticExpressionFunction( QStringLiteral( "m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomM, QStringLiteral( "GeometryGroup" ) )
8776 << new QgsStaticExpressionFunction( QStringLiteral( "point_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ), fcnPointN, QStringLiteral( "GeometryGroup" ) )
8777 << new QgsStaticExpressionFunction( QStringLiteral( "start_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnStartPoint, QStringLiteral( "GeometryGroup" ) )
8778 << new QgsStaticExpressionFunction( QStringLiteral( "end_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnEndPoint, QStringLiteral( "GeometryGroup" ) )
8779 << new QgsStaticExpressionFunction( QStringLiteral( "nodes_to_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8780 << QgsExpressionFunction::Parameter( QStringLiteral( "ignore_closing_nodes" ), true, false ),
8781 fcnNodesToPoints, QStringLiteral( "GeometryGroup" ) )
8782 << new QgsStaticExpressionFunction( QStringLiteral( "segments_to_lines" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnSegmentsToLines, QStringLiteral( "GeometryGroup" ) )
8783 << new QgsStaticExpressionFunction( QStringLiteral( "collect_geometries" ), -1, fcnCollectGeometries, QStringLiteral( "GeometryGroup" ) )
8784 << new QgsStaticExpressionFunction( QStringLiteral( "make_point" ), -1, fcnMakePoint, QStringLiteral( "GeometryGroup" ) )
8785 << new QgsStaticExpressionFunction( QStringLiteral( "make_point_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
8786 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) )
8787 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ),
8788 fcnMakePointM, QStringLiteral( "GeometryGroup" ) )
8789 << new QgsStaticExpressionFunction( QStringLiteral( "make_line" ), -1, fcnMakeLine, QStringLiteral( "GeometryGroup" ) )
8790 << new QgsStaticExpressionFunction( QStringLiteral( "make_polygon" ), -1, fcnMakePolygon, QStringLiteral( "GeometryGroup" ) )
8791 << new QgsStaticExpressionFunction( QStringLiteral( "make_triangle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8792 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
8793 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) ),
8794 fcnMakeTriangle, QStringLiteral( "GeometryGroup" ) )
8795 << new QgsStaticExpressionFunction( QStringLiteral( "make_circle" ), QgsExpressionFunction::ParameterList()
8796 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8797 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
8798 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8799 fcnMakeCircle, QStringLiteral( "GeometryGroup" ) )
8800 << new QgsStaticExpressionFunction( QStringLiteral( "make_ellipse" ), QgsExpressionFunction::ParameterList()
8801 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8802 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_major_axis" ) )
8803 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_minor_axis" ) )
8804 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
8805 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8806 fcnMakeEllipse, QStringLiteral( "GeometryGroup" ) )
8807 << new QgsStaticExpressionFunction( QStringLiteral( "make_regular_polygon" ), QgsExpressionFunction::ParameterList()
8808 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8809 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
8810 << QgsExpressionFunction::Parameter( QStringLiteral( "number_sides" ) )
8811 << QgsExpressionFunction::Parameter( QStringLiteral( "circle" ), true, 0 ),
8812 fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) )
8813 << new QgsStaticExpressionFunction( QStringLiteral( "make_square" ), QgsExpressionFunction::ParameterList()
8814 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8815 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) ),
8816 fcnMakeSquare, QStringLiteral( "GeometryGroup" ) )
8817 << new QgsStaticExpressionFunction( QStringLiteral( "make_rectangle_3points" ), QgsExpressionFunction::ParameterList()
8818 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8819 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
8820 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) )
8821 << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, 0 ),
8822 fcnMakeRectangleFrom3Points, QStringLiteral( "GeometryGroup" ) )
8823 << new QgsStaticExpressionFunction( QStringLiteral( "make_valid" ), QgsExpressionFunction::ParameterList
8824 {
8825 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8826#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
8827 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "linework" ) ),
8828#else
8829 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "structure" ) ),
8830#endif
8831 QgsExpressionFunction::Parameter( QStringLiteral( "keep_collapsed" ), true, false )
8832 }, fcnGeomMakeValid, QStringLiteral( "GeometryGroup" ) );
8833
8834 functions << new QgsStaticExpressionFunction( QStringLiteral( "x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnXat, QStringLiteral( "GeometryGroup" ) );
8835 functions << new QgsStaticExpressionFunction( QStringLiteral( "y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnYat, QStringLiteral( "GeometryGroup" ) );
8836 functions << new QgsStaticExpressionFunction( QStringLiteral( "z_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnZat, QStringLiteral( "GeometryGroup" ) );
8837 functions << new QgsStaticExpressionFunction( QStringLiteral( "m_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnMat, QStringLiteral( "GeometryGroup" ) );
8838
8839 QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnOldXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "xat" ) );
8840 xAtFunc->setIsStatic( false );
8841 functions << xAtFunc;
8842
8843
8844 QgsStaticExpressionFunction *yAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnOldYat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "yat" ) );
8845 yAtFunc->setIsStatic( false );
8846 functions << yAtFunc;
8847
8848 functions
8849 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_type" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeometryType, QStringLiteral( "GeometryGroup" ) )
8850 << new QgsStaticExpressionFunction( QStringLiteral( "x_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmin" ) )
8851 << new QgsStaticExpressionFunction( QStringLiteral( "x_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmax" ) )
8852 << new QgsStaticExpressionFunction( QStringLiteral( "y_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymin" ) )
8853 << new QgsStaticExpressionFunction( QStringLiteral( "y_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymax" ) )
8854 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ), fcnGeomFromWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromWKT" ) )
8855 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "binary" ) ), fcnGeomFromWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
8856 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_gml" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "gml" ) ), fcnGeomFromGML, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromGML" ) )
8857 << new QgsStaticExpressionFunction( QStringLiteral( "flip_coordinates" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnFlipCoordinates, QStringLiteral( "GeometryGroup" ) )
8858 << new QgsStaticExpressionFunction( QStringLiteral( "relate" ), -1, fcnRelate, QStringLiteral( "GeometryGroup" ) )
8859 << 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" ) )
8860 << new QgsStaticExpressionFunction( QStringLiteral( "disjoint" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8861 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8862 fcnDisjoint, QStringLiteral( "GeometryGroup" ) )
8863 << new QgsStaticExpressionFunction( QStringLiteral( "intersects" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8864 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8865 fcnIntersects, QStringLiteral( "GeometryGroup" ) )
8866 << new QgsStaticExpressionFunction( QStringLiteral( "touches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8867 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8868 fcnTouches, QStringLiteral( "GeometryGroup" ) )
8869 << new QgsStaticExpressionFunction( QStringLiteral( "crosses" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8870 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8871 fcnCrosses, QStringLiteral( "GeometryGroup" ) )
8872 << new QgsStaticExpressionFunction( QStringLiteral( "contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8873 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8874 fcnContains, QStringLiteral( "GeometryGroup" ) )
8875 << new QgsStaticExpressionFunction( QStringLiteral( "overlaps" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8876 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8877 fcnOverlaps, QStringLiteral( "GeometryGroup" ) )
8878 << new QgsStaticExpressionFunction( QStringLiteral( "within" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8879 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8880 fcnWithin, QStringLiteral( "GeometryGroup" ) )
8881 << new QgsStaticExpressionFunction( QStringLiteral( "translate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8882 << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) )
8883 << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ),
8884 fcnTranslate, QStringLiteral( "GeometryGroup" ) )
8885 << new QgsStaticExpressionFunction( QStringLiteral( "rotate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8886 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation" ) )
8887 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true )
8888 << QgsExpressionFunction::Parameter( QStringLiteral( "per_part" ), true, false ),
8889 fcnRotate, QStringLiteral( "GeometryGroup" ) )
8890 << new QgsStaticExpressionFunction( QStringLiteral( "scale" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8891 << QgsExpressionFunction::Parameter( QStringLiteral( "x_scale" ) )
8892 << QgsExpressionFunction::Parameter( QStringLiteral( "y_scale" ) )
8893 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true ),
8894 fcnScale, QStringLiteral( "GeometryGroup" ) )
8895 << new QgsStaticExpressionFunction( QStringLiteral( "affine_transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8896 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_x" ) )
8897 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_y" ) )
8898 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation_z" ) )
8899 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_x" ) )
8900 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_y" ) )
8901 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_z" ), true, 0 )
8902 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_m" ), true, 0 )
8903 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_z" ), true, 1 )
8904 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_m" ), true, 1 ),
8905 fcnAffineTransform, QStringLiteral( "GeometryGroup" ) )
8906 << new QgsStaticExpressionFunction( QStringLiteral( "buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8907 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8908 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8 )
8909 << QgsExpressionFunction::Parameter( QStringLiteral( "cap" ), true, QStringLiteral( "round" ) )
8910 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QStringLiteral( "round" ) )
8911 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2 ),
8912 fcnBuffer, QStringLiteral( "GeometryGroup" ) )
8913 << new QgsStaticExpressionFunction( QStringLiteral( "force_rhr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8914 fcnForceRHR, QStringLiteral( "GeometryGroup" ) )
8915 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_cw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8916 fcnForcePolygonCW, QStringLiteral( "GeometryGroup" ) )
8917 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_ccw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8918 fcnForcePolygonCCW, QStringLiteral( "GeometryGroup" ) )
8919 << new QgsStaticExpressionFunction( QStringLiteral( "wedge_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8920 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
8921 << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) )
8922 << QgsExpressionFunction::Parameter( QStringLiteral( "outer_radius" ) )
8923 << QgsExpressionFunction::Parameter( QStringLiteral( "inner_radius" ), true, 0.0 ), fcnWedgeBuffer, QStringLiteral( "GeometryGroup" ) )
8924 << new QgsStaticExpressionFunction( QStringLiteral( "tapered_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8925 << QgsExpressionFunction::Parameter( QStringLiteral( "start_width" ) )
8926 << QgsExpressionFunction::Parameter( QStringLiteral( "end_width" ) )
8927 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
8928 , fcnTaperedBuffer, QStringLiteral( "GeometryGroup" ) )
8929 << new QgsStaticExpressionFunction( QStringLiteral( "buffer_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8930 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
8931 , fcnBufferByM, QStringLiteral( "GeometryGroup" ) )
8932 << new QgsStaticExpressionFunction( QStringLiteral( "offset_curve" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8933 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8934 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
8935 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
8936 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
8937 fcnOffsetCurve, QStringLiteral( "GeometryGroup" ) )
8938 << new QgsStaticExpressionFunction( QStringLiteral( "single_sided_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8939 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8940 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
8941 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
8942 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
8943 fcnSingleSidedBuffer, QStringLiteral( "GeometryGroup" ) )
8944 << new QgsStaticExpressionFunction( QStringLiteral( "extend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8945 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) )
8946 << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ),
8947 fcnExtend, QStringLiteral( "GeometryGroup" ) )
8948 << new QgsStaticExpressionFunction( QStringLiteral( "centroid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCentroid, QStringLiteral( "GeometryGroup" ) )
8949 << new QgsStaticExpressionFunction( QStringLiteral( "point_on_surface" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPointOnSurface, QStringLiteral( "GeometryGroup" ) )
8950 << new QgsStaticExpressionFunction( QStringLiteral( "pole_of_inaccessibility" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8951 << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnPoleOfInaccessibility, QStringLiteral( "GeometryGroup" ) )
8952 << new QgsStaticExpressionFunction( QStringLiteral( "reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnReverse, QStringLiteral( "GeometryGroup" ) )
8953 << new QgsStaticExpressionFunction( QStringLiteral( "exterior_ring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnExteriorRing, QStringLiteral( "GeometryGroup" ) )
8954 << new QgsStaticExpressionFunction( QStringLiteral( "interior_ring_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8955 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
8956 fcnInteriorRingN, QStringLiteral( "GeometryGroup" ) )
8957 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8958 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
8959 fcnGeometryN, QStringLiteral( "GeometryGroup" ) )
8960 << new QgsStaticExpressionFunction( QStringLiteral( "boundary" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundary, QStringLiteral( "GeometryGroup" ) )
8961 << new QgsStaticExpressionFunction( QStringLiteral( "line_merge" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLineMerge, QStringLiteral( "GeometryGroup" ) )
8962 << new QgsStaticExpressionFunction( QStringLiteral( "shared_paths" ), QgsExpressionFunction::ParameterList
8963 {
8964 QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ),
8965 QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
8966 }, fcnSharedPaths, QStringLiteral( "GeometryGroup" ) )
8967 << new QgsStaticExpressionFunction( QStringLiteral( "bounds" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBounds, QStringLiteral( "GeometryGroup" ) )
8968 << new QgsStaticExpressionFunction( QStringLiteral( "simplify" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplify, QStringLiteral( "GeometryGroup" ) )
8969 << new QgsStaticExpressionFunction( QStringLiteral( "simplify_vw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplifyVW, QStringLiteral( "GeometryGroup" ) )
8970 << new QgsStaticExpressionFunction( QStringLiteral( "smooth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "iterations" ), true, 1 )
8971 << QgsExpressionFunction::Parameter( QStringLiteral( "offset" ), true, 0.25 )
8972 << QgsExpressionFunction::Parameter( QStringLiteral( "min_length" ), true, -1 )
8973 << QgsExpressionFunction::Parameter( QStringLiteral( "max_angle" ), true, 180 ), fcnSmooth, QStringLiteral( "GeometryGroup" ) )
8974 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave" ),
8975 {
8976 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8977 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
8978 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
8979 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
8980 }, fcnTriangularWave, QStringLiteral( "GeometryGroup" ) )
8981 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave_randomized" ),
8982 {
8983 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8984 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
8985 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
8986 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
8987 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
8988 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
8989 }, fcnTriangularWaveRandomized, QStringLiteral( "GeometryGroup" ) )
8990 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave" ),
8991 {
8992 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8993 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
8994 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
8995 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
8996 }, fcnSquareWave, QStringLiteral( "GeometryGroup" ) )
8997 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave_randomized" ),
8998 {
8999 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9000 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
9001 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
9002 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
9003 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
9004 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
9005 }, fcnSquareWaveRandomized, QStringLiteral( "GeometryGroup" ) )
9006 << new QgsStaticExpressionFunction( QStringLiteral( "wave" ),
9007 {
9008 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9009 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
9010 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
9011 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
9012 }, fcnRoundWave, QStringLiteral( "GeometryGroup" ) )
9013 << new QgsStaticExpressionFunction( QStringLiteral( "wave_randomized" ),
9014 {
9015 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9016 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
9017 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
9018 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
9019 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
9020 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
9021 }, fcnRoundWaveRandomized, QStringLiteral( "GeometryGroup" ) )
9022 << new QgsStaticExpressionFunction( QStringLiteral( "apply_dash_pattern" ),
9023 {
9024 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9025 QgsExpressionFunction::Parameter( QStringLiteral( "pattern" ) ),
9026 QgsExpressionFunction::Parameter( QStringLiteral( "start_rule" ), true, QStringLiteral( "no_rule" ) ),
9027 QgsExpressionFunction::Parameter( QStringLiteral( "end_rule" ), true, QStringLiteral( "no_rule" ) ),
9028 QgsExpressionFunction::Parameter( QStringLiteral( "adjustment" ), true, QStringLiteral( "both" ) ),
9029 QgsExpressionFunction::Parameter( QStringLiteral( "pattern_offset" ), true, 0 ),
9030 }, fcnApplyDashPattern, QStringLiteral( "GeometryGroup" ) )
9031 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_count" ),
9032 {
9033 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9034 QgsExpressionFunction::Parameter( QStringLiteral( "vertices" ) )
9035 }, fcnDensifyByCount, QStringLiteral( "GeometryGroup" ) )
9036 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_distance" ),
9037 {
9038 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9039 QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9040 }, fcnDensifyByDistance, QStringLiteral( "GeometryGroup" ) )
9041 << new QgsStaticExpressionFunction( QStringLiteral( "num_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumPoints, QStringLiteral( "GeometryGroup" ) )
9042 << new QgsStaticExpressionFunction( QStringLiteral( "num_interior_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumInteriorRings, QStringLiteral( "GeometryGroup" ) )
9043 << new QgsStaticExpressionFunction( QStringLiteral( "num_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumRings, QStringLiteral( "GeometryGroup" ) )
9044 << new QgsStaticExpressionFunction( QStringLiteral( "num_geometries" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumGeometries, QStringLiteral( "GeometryGroup" ) )
9045 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_width" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsWidth, QStringLiteral( "GeometryGroup" ) )
9046 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_height" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsHeight, QStringLiteral( "GeometryGroup" ) )
9047 << new QgsStaticExpressionFunction( QStringLiteral( "is_closed" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsClosed, QStringLiteral( "GeometryGroup" ) )
9048 << new QgsStaticExpressionFunction( QStringLiteral( "close_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCloseLine, QStringLiteral( "GeometryGroup" ) )
9049 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmpty, QStringLiteral( "GeometryGroup" ) )
9050 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty_or_null" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmptyOrNull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9051 << new QgsStaticExpressionFunction( QStringLiteral( "convex_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnConvexHull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "convexHull" ) )
9052#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
9053 << new QgsStaticExpressionFunction( QStringLiteral( "concave_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9054 << QgsExpressionFunction::Parameter( QStringLiteral( "target_percent" ) )
9055 << QgsExpressionFunction::Parameter( QStringLiteral( "allow_holes" ), true, false ), fcnConcaveHull, QStringLiteral( "GeometryGroup" ) )
9056#endif
9057 << new QgsStaticExpressionFunction( QStringLiteral( "oriented_bbox" ), QgsExpressionFunction::ParameterList()
9058 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9059 fcnOrientedBBox, QStringLiteral( "GeometryGroup" ) )
9060 << new QgsStaticExpressionFunction( QStringLiteral( "main_angle" ), QgsExpressionFunction::ParameterList()
9061 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9062 fcnMainAngle, QStringLiteral( "GeometryGroup" ) )
9063 << new QgsStaticExpressionFunction( QStringLiteral( "minimal_circle" ), QgsExpressionFunction::ParameterList()
9064 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9065 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
9066 fcnMinimalCircle, QStringLiteral( "GeometryGroup" ) )
9067 << new QgsStaticExpressionFunction( QStringLiteral( "difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9068 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9069 fcnDifference, QStringLiteral( "GeometryGroup" ) )
9070 << new QgsStaticExpressionFunction( QStringLiteral( "distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9071 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9072 fcnDistance, QStringLiteral( "GeometryGroup" ) )
9073 << new QgsStaticExpressionFunction( QStringLiteral( "hausdorff_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
9074 << QgsExpressionFunction::Parameter( QStringLiteral( "densify_fraction" ), true ),
9075 fcnHausdorffDistance, QStringLiteral( "GeometryGroup" ) )
9076 << new QgsStaticExpressionFunction( QStringLiteral( "intersection" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9077 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9078 fcnIntersection, QStringLiteral( "GeometryGroup" ) )
9079 << new QgsStaticExpressionFunction( QStringLiteral( "sym_difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9080 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9081 fcnSymDifference, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "symDifference" ) )
9082 << new QgsStaticExpressionFunction( QStringLiteral( "combine" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9083 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9084 fcnCombine, QStringLiteral( "GeometryGroup" ) )
9085 << new QgsStaticExpressionFunction( QStringLiteral( "union" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9086 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9087 fcnCombine, QStringLiteral( "GeometryGroup" ) )
9088 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9089 << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ), true, 8.0 ),
9090 fcnGeomToWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomToWKT" ) )
9091 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9092 fcnGeomToWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
9093 << new QgsStaticExpressionFunction( QStringLiteral( "geometry" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetGeometry, QStringLiteral( "GeometryGroup" ), QString(), true )
9094 << new QgsStaticExpressionFunction( QStringLiteral( "transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9095 << QgsExpressionFunction::Parameter( QStringLiteral( "source_auth_id" ) )
9096 << QgsExpressionFunction::Parameter( QStringLiteral( "dest_auth_id" ) ),
9097 fcnTransformGeometry, QStringLiteral( "GeometryGroup" ) )
9098 << new QgsStaticExpressionFunction( QStringLiteral( "extrude" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9099 << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
9100 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) ),
9101 fcnExtrude, QStringLiteral( "GeometryGroup" ), QString() )
9102 << new QgsStaticExpressionFunction( QStringLiteral( "is_multipart" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9103 fcnGeomIsMultipart, QStringLiteral( "GeometryGroup" ) )
9104 << new QgsStaticExpressionFunction( QStringLiteral( "z_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9105 fcnZMax, QStringLiteral( "GeometryGroup" ) )
9106 << new QgsStaticExpressionFunction( QStringLiteral( "z_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9107 fcnZMin, QStringLiteral( "GeometryGroup" ) )
9108 << new QgsStaticExpressionFunction( QStringLiteral( "m_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9109 fcnMMax, QStringLiteral( "GeometryGroup" ) )
9110 << new QgsStaticExpressionFunction( QStringLiteral( "m_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9111 fcnMMin, QStringLiteral( "GeometryGroup" ) )
9112 << new QgsStaticExpressionFunction( QStringLiteral( "sinuosity" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9113 fcnSinuosity, QStringLiteral( "GeometryGroup" ) )
9114 << new QgsStaticExpressionFunction( QStringLiteral( "straight_distance_2d" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9115 fcnStraightDistance2d, QStringLiteral( "GeometryGroup" ) );
9116
9117
9118 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction( QStringLiteral( "order_parts" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9119 << QgsExpressionFunction::Parameter( QStringLiteral( "orderby" ) )
9120 << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ),
9121 fcnOrderParts, QStringLiteral( "GeometryGroup" ), QString() );
9122
9123 orderPartsFunc->setIsStaticFunction(
9124 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9125 {
9126 const QList< QgsExpressionNode *> argList = node->args()->list();
9127 for ( QgsExpressionNode *argNode : argList )
9128 {
9129 if ( !argNode->isStatic( parent, context ) )
9130 return false;
9131 }
9132
9133 if ( node->args()->count() > 1 )
9134 {
9135 QgsExpressionNode *argNode = node->args()->at( 1 );
9136
9137 QString expString = argNode->eval( parent, context ).toString();
9138
9139 QgsExpression e( expString );
9140
9141 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9142 return true;
9143 }
9144
9145 return true;
9146 } );
9147
9148 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9149 {
9150 if ( node->args()->count() > 1 )
9151 {
9152 QgsExpressionNode *argNode = node->args()->at( 1 );
9153 QString expression = argNode->eval( parent, context ).toString();
9155 e.prepare( context );
9156 context->setCachedValue( expression, QVariant::fromValue( e ) );
9157 }
9158 return true;
9159 }
9160 );
9161 functions << orderPartsFunc;
9162
9163 functions
9164 << new QgsStaticExpressionFunction( QStringLiteral( "closest_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9165 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9166 fcnClosestPoint, QStringLiteral( "GeometryGroup" ) )
9167 << new QgsStaticExpressionFunction( QStringLiteral( "shortest_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9168 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9169 fcnShortestLine, QStringLiteral( "GeometryGroup" ) )
9170 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9171 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolatePoint, QStringLiteral( "GeometryGroup" ) )
9172 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9173 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "use_3d_distance" ), true, false ),
9174 fcnLineInterpolatePointByM, QStringLiteral( "GeometryGroup" ) )
9175 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_angle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9176 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolateAngle, QStringLiteral( "GeometryGroup" ) )
9177 << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9178 << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnLineLocatePoint, QStringLiteral( "GeometryGroup" ) )
9179 << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9180 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "use_3d_distance" ), true, false ),
9181 fcnLineLocateM, QStringLiteral( "GeometryGroup" ) )
9182 << new QgsStaticExpressionFunction( QStringLiteral( "angle_at_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9183 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnAngleAtVertex, QStringLiteral( "GeometryGroup" ) )
9184 << new QgsStaticExpressionFunction( QStringLiteral( "distance_to_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9185 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnDistanceToVertex, QStringLiteral( "GeometryGroup" ) )
9186 << new QgsStaticExpressionFunction( QStringLiteral( "line_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9187 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ), fcnLineSubset, QStringLiteral( "GeometryGroup" ) );
9188
9189
9190 // **Record** functions
9191
9192 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( QStringLiteral( "$id" ), 0, fcnFeatureId, QStringLiteral( "Record and Attributes" ) );
9193 idFunc->setIsStatic( false );
9194 functions << idFunc;
9195
9196 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( QStringLiteral( "$currentfeature" ), 0, fcnFeature, QStringLiteral( "Record and Attributes" ) );
9197 currentFeatureFunc->setIsStatic( false );
9198 functions << currentFeatureFunc;
9199
9200 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" ) );
9201 uuidFunc->setIsStatic( false );
9202 functions << uuidFunc;
9203
9204 functions
9205 << new QgsStaticExpressionFunction( QStringLiteral( "feature_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetFeatureId, QStringLiteral( "Record and Attributes" ), QString(), true )
9206 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9207 << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) )
9208 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ), true ),
9209 fcnGetFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "QgsExpressionUtils::getFeature" ) )
9210 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature_by_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9211 << QgsExpressionFunction::Parameter( QStringLiteral( "feature_id" ) ),
9212 fcnGetFeatureById, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false );
9213
9214 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true ),
9215 fcnAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9216 attributesFunc->setIsStatic( false );
9217 functions << attributesFunc;
9218 QgsStaticExpressionFunction *representAttributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_attributes" ), -1,
9219 fcnRepresentAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9220 representAttributesFunc->setIsStatic( false );
9221 functions << representAttributesFunc;
9222
9223 QgsStaticExpressionFunction *validateFeature = new QgsStaticExpressionFunction( QStringLiteral( "is_feature_valid" ),
9224 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true )
9225 << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true )
9226 << QgsExpressionFunction::Parameter( QStringLiteral( "strength" ), true ),
9227 fcnValidateFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9228 validateFeature->setIsStatic( false );
9229 functions << validateFeature;
9230
9231 QgsStaticExpressionFunction *validateAttribute = new QgsStaticExpressionFunction( QStringLiteral( "is_attribute_valid" ),
9232 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ), false )
9233 << QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true )
9234 << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true )
9235 << QgsExpressionFunction::Parameter( QStringLiteral( "strength" ), true ),
9236 fcnValidateAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9237 validateAttribute->setIsStatic( false );
9238 functions << validateAttribute;
9239
9241 QStringLiteral( "maptip" ),
9242 -1,
9243 fcnFeatureMaptip,
9244 QStringLiteral( "Record and Attributes" ),
9245 QString(),
9246 false,
9247 QSet<QString>()
9248 );
9249 maptipFunc->setIsStatic( false );
9250 functions << maptipFunc;
9251
9253 QStringLiteral( "display_expression" ),
9254 -1,
9255 fcnFeatureDisplayExpression,
9256 QStringLiteral( "Record and Attributes" ),
9257 QString(),
9258 false,
9259 QSet<QString>()
9260 );
9261 displayFunc->setIsStatic( false );
9262 functions << displayFunc;
9263
9265 QStringLiteral( "is_selected" ),
9266 -1,
9267 fcnIsSelected,
9268 QStringLiteral( "Record and Attributes" ),
9269 QString(),
9270 false,
9271 QSet<QString>()
9272 );
9273 isSelectedFunc->setIsStatic( false );
9274 functions << isSelectedFunc;
9275
9276 functions
9278 QStringLiteral( "num_selected" ),
9279 -1,
9280 fcnNumSelected,
9281 QStringLiteral( "Record and Attributes" ),
9282 QString(),
9283 false,
9284 QSet<QString>()
9285 );
9286
9287 functions
9289 QStringLiteral( "sqlite_fetch_and_increment" ),
9291 << QgsExpressionFunction::Parameter( QStringLiteral( "database" ) )
9292 << QgsExpressionFunction::Parameter( QStringLiteral( "table" ) )
9293 << QgsExpressionFunction::Parameter( QStringLiteral( "id_field" ) )
9294 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_attribute" ) )
9295 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_value" ) )
9296 << QgsExpressionFunction::Parameter( QStringLiteral( "default_values" ), true ),
9297 fcnSqliteFetchAndIncrement,
9298 QStringLiteral( "Record and Attributes" )
9299 );
9300
9301 // **Fields and Values** functions
9302 QgsStaticExpressionFunction *representValueFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "field_name" ), true ), fcnRepresentValue, QStringLiteral( "Record and Attributes" ) );
9303
9304 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9305 {
9306 Q_UNUSED( context )
9307 if ( node->args()->count() == 1 )
9308 {
9309 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
9310 if ( colRef )
9311 {
9312 return true;
9313 }
9314 else
9315 {
9316 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
9317 return false;
9318 }
9319 }
9320 else if ( node->args()->count() == 2 )
9321 {
9322 return true;
9323 }
9324 else
9325 {
9326 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
9327 return false;
9328 }
9329 }
9330 );
9331
9332 functions << representValueFunc;
9333
9334 // **General** functions
9335 functions
9336 << new QgsStaticExpressionFunction( QStringLiteral( "layer_property" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9337 << QgsExpressionFunction::Parameter( QStringLiteral( "property" ) ),
9338 fcnGetLayerProperty, QStringLiteral( "Map Layers" ) )
9339 << new QgsStaticExpressionFunction( QStringLiteral( "decode_uri" ),
9341 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9342 << QgsExpressionFunction::Parameter( QStringLiteral( "part" ), true ),
9343 fcnDecodeUri, QStringLiteral( "Map Layers" ) )
9344 << new QgsStaticExpressionFunction( QStringLiteral( "mime_type" ),
9346 << QgsExpressionFunction::Parameter( QStringLiteral( "binary_data" ) ),
9347 fcnMimeType, QStringLiteral( "General" ) )
9348 << new QgsStaticExpressionFunction( QStringLiteral( "raster_statistic" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9349 << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) )
9350 << QgsExpressionFunction::Parameter( QStringLiteral( "statistic" ) ), fcnGetRasterBandStat, QStringLiteral( "Rasters" ) );
9351
9352 // **var** function
9353 QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( QStringLiteral( "var" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnGetVariable, QStringLiteral( "General" ) );
9354 varFunction->setIsStaticFunction(
9355 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9356 {
9357 /* A variable node is static if it has a static name and the name can be found at prepare
9358 * time and is tagged with isStatic.
9359 * It is not static if a variable is set during iteration or not tagged isStatic.
9360 * (e.g. geom_part variable)
9361 */
9362 if ( node->args()->count() > 0 )
9363 {
9364 QgsExpressionNode *argNode = node->args()->at( 0 );
9365
9366 if ( !argNode->isStatic( parent, context ) )
9367 return false;
9368
9369 const QString varName = argNode->eval( parent, context ).toString();
9370 if ( varName == QLatin1String( "feature" ) || varName == QLatin1String( "id" ) || varName == QLatin1String( "geometry" ) )
9371 return false;
9372
9373 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
9374 return scope ? scope->isStatic( varName ) : false;
9375 }
9376 return false;
9377 }
9378 );
9379 varFunction->setUsesGeometryFunction(
9380 []( const QgsExpressionNodeFunction * node ) -> bool
9381 {
9382 if ( node && node->args()->count() > 0 )
9383 {
9384 QgsExpressionNode *argNode = node->args()->at( 0 );
9385 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
9386 {
9387 if ( literal->value() == QLatin1String( "geometry" ) || literal->value() == QLatin1String( "feature" ) )
9388 return true;
9389 }
9390 }
9391 return false;
9392 }
9393 );
9394
9395 functions
9396 << varFunction;
9397
9398 QgsStaticExpressionFunction *evalTemplateFunction = new QgsStaticExpressionFunction( QStringLiteral( "eval_template" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "template" ) ), fcnEvalTemplate, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9399 evalTemplateFunction->setIsStaticFunction(
9400 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9401 {
9402 if ( node->args()->count() > 0 )
9403 {
9404 QgsExpressionNode *argNode = node->args()->at( 0 );
9405
9406 if ( argNode->isStatic( parent, context ) )
9407 {
9408 QString expString = argNode->eval( parent, context ).toString();
9409
9410 QgsExpression e( expString );
9411
9412 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9413 return true;
9414 }
9415 }
9416
9417 return false;
9418 } );
9419 functions << evalTemplateFunction;
9420
9421 QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( QStringLiteral( "eval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ), fcnEval, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9422 evalFunc->setIsStaticFunction(
9423 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9424 {
9425 if ( node->args()->count() > 0 )
9426 {
9427 QgsExpressionNode *argNode = node->args()->at( 0 );
9428
9429 if ( argNode->isStatic( parent, context ) )
9430 {
9431 QString expString = argNode->eval( parent, context ).toString();
9432
9433 QgsExpression e( expString );
9434
9435 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9436 return true;
9437 }
9438 }
9439
9440 return false;
9441 } );
9442
9443 functions << evalFunc;
9444
9445 QgsStaticExpressionFunction *attributeFunc = new QgsStaticExpressionFunction( QStringLiteral( "attribute" ), -1, fcnAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9446 attributeFunc->setIsStaticFunction(
9447 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9448 {
9449 const QList< QgsExpressionNode *> argList = node->args()->list();
9450 for ( QgsExpressionNode *argNode : argList )
9451 {
9452 if ( !argNode->isStatic( parent, context ) )
9453 return false;
9454 }
9455
9456 if ( node->args()->count() == 1 )
9457 {
9458 // not static -- this is the variant which uses the current feature taken direct from the expression context
9459 return false;
9460 }
9461
9462 return true;
9463 } );
9464 functions << attributeFunc;
9465
9466 functions
9467 << new QgsStaticExpressionFunction( QStringLiteral( "env" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnEnvVar, QStringLiteral( "General" ), QString() )
9469 << new QgsStaticExpressionFunction( QStringLiteral( "raster_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterValue, QStringLiteral( "Rasters" ) )
9470 << new QgsStaticExpressionFunction( QStringLiteral( "raster_attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterAttributes, QStringLiteral( "Rasters" ) )
9471
9472 // functions for arrays
9475 << new QgsStaticExpressionFunction( QStringLiteral( "array" ), -1, fcnArray, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9476 << new QgsStaticExpressionFunction( QStringLiteral( "array_sort" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ), fcnArraySort, QStringLiteral( "Arrays" ) )
9477 << new QgsStaticExpressionFunction( QStringLiteral( "array_length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLength, QStringLiteral( "Arrays" ) )
9478 << new QgsStaticExpressionFunction( QStringLiteral( "array_contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayContains, QStringLiteral( "Arrays" ) )
9479 << new QgsStaticExpressionFunction( QStringLiteral( "array_count" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayCount, QStringLiteral( "Arrays" ) )
9480 << new QgsStaticExpressionFunction( QStringLiteral( "array_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_b" ) ), fcnArrayAll, QStringLiteral( "Arrays" ) )
9481 << new QgsStaticExpressionFunction( QStringLiteral( "array_find" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayFind, QStringLiteral( "Arrays" ) )
9482 << new QgsStaticExpressionFunction( QStringLiteral( "array_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayGet, QStringLiteral( "Arrays" ) )
9483 << new QgsStaticExpressionFunction( QStringLiteral( "array_first" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayFirst, QStringLiteral( "Arrays" ) )
9484 << new QgsStaticExpressionFunction( QStringLiteral( "array_last" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLast, QStringLiteral( "Arrays" ) )
9485 << new QgsStaticExpressionFunction( QStringLiteral( "array_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMinimum, QStringLiteral( "Arrays" ) )
9486 << new QgsStaticExpressionFunction( QStringLiteral( "array_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMaximum, QStringLiteral( "Arrays" ) )
9487 << new QgsStaticExpressionFunction( QStringLiteral( "array_mean" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMean, QStringLiteral( "Arrays" ) )
9488 << new QgsStaticExpressionFunction( QStringLiteral( "array_median" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMedian, QStringLiteral( "Arrays" ) )
9489 << new QgsStaticExpressionFunction( QStringLiteral( "array_majority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMajority, QStringLiteral( "Arrays" ) )
9490 << new QgsStaticExpressionFunction( QStringLiteral( "array_minority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMinority, QStringLiteral( "Arrays" ) )
9491 << new QgsStaticExpressionFunction( QStringLiteral( "array_sum" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArraySum, QStringLiteral( "Arrays" ) )
9492 << new QgsStaticExpressionFunction( QStringLiteral( "array_append" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayAppend, QStringLiteral( "Arrays" ) )
9493 << new QgsStaticExpressionFunction( QStringLiteral( "array_prepend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayPrepend, QStringLiteral( "Arrays" ) )
9494 << new QgsStaticExpressionFunction( QStringLiteral( "array_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayInsert, QStringLiteral( "Arrays" ) )
9495 << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayRemoveAt, QStringLiteral( "Arrays" ) )
9496 << 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 )
9497 << new QgsStaticExpressionFunction( QStringLiteral( "array_replace" ), -1, fcnArrayReplace, QStringLiteral( "Arrays" ) )
9498 << new QgsStaticExpressionFunction( QStringLiteral( "array_prioritize" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_prioritize" ) ), fcnArrayPrioritize, QStringLiteral( "Arrays" ) )
9499 << new QgsStaticExpressionFunction( QStringLiteral( "array_cat" ), -1, fcnArrayCat, QStringLiteral( "Arrays" ) )
9500 << new QgsStaticExpressionFunction( QStringLiteral( "array_slice" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start_pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_pos" ) ), fcnArraySlice, QStringLiteral( "Arrays" ) )
9501 << new QgsStaticExpressionFunction( QStringLiteral( "array_reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayReverse, QStringLiteral( "Arrays" ) )
9502 << new QgsStaticExpressionFunction( QStringLiteral( "array_intersect" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array2" ) ), fcnArrayIntersect, QStringLiteral( "Arrays" ) )
9503 << new QgsStaticExpressionFunction( QStringLiteral( "array_distinct" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayDistinct, QStringLiteral( "Arrays" ) )
9504 << 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" ) )
9505 << 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" ) )
9506 << 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" ) )
9507 << new QgsStaticExpressionFunction( QStringLiteral( "geometries_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometries" ) ), fcnGeometryCollectionAsArray, QStringLiteral( "Arrays" ) )
9508
9509 //functions for maps
9510 << new QgsStaticExpressionFunction( QStringLiteral( "from_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLoadJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "json_to_map" ) )
9511 << 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" ) )
9512 << new QgsStaticExpressionFunction( QStringLiteral( "hstore_to_map" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnHstoreToMap, QStringLiteral( "Maps" ) )
9513 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_hstore" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapToHstore, QStringLiteral( "Maps" ) )
9514 << new QgsStaticExpressionFunction( QStringLiteral( "map" ), -1, fcnMap, QStringLiteral( "Maps" ) )
9515 << new QgsStaticExpressionFunction( QStringLiteral( "map_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapGet, QStringLiteral( "Maps" ) )
9516 << new QgsStaticExpressionFunction( QStringLiteral( "map_exist" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapExist, QStringLiteral( "Maps" ) )
9517 << new QgsStaticExpressionFunction( QStringLiteral( "map_delete" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapDelete, QStringLiteral( "Maps" ) )
9518 << new QgsStaticExpressionFunction( QStringLiteral( "map_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnMapInsert, QStringLiteral( "Maps" ) )
9519 << new QgsStaticExpressionFunction( QStringLiteral( "map_concat" ), -1, fcnMapConcat, QStringLiteral( "Maps" ) )
9520 << new QgsStaticExpressionFunction( QStringLiteral( "map_akeys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAKeys, QStringLiteral( "Maps" ) )
9521 << new QgsStaticExpressionFunction( QStringLiteral( "map_avals" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAVals, QStringLiteral( "Maps" ) )
9522 << new QgsStaticExpressionFunction( QStringLiteral( "map_prefix_keys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
9523 << QgsExpressionFunction::Parameter( QStringLiteral( "prefix" ) ),
9524 fcnMapPrefixKeys, QStringLiteral( "Maps" ) )
9525 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_html_table" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9526 fcnMapToHtmlTable, QStringLiteral( "Maps" ) )
9527 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_html_dl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9528 fcnMapToHtmlDefinitionList, QStringLiteral( "Maps" ) )
9529 << new QgsStaticExpressionFunction( QStringLiteral( "url_encode" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9530 fcnToFormUrlEncode, QStringLiteral( "Maps" ) )
9531
9532 ;
9533
9535
9536 //QgsExpression has ownership of all built-in functions
9537 for ( QgsExpressionFunction *func : std::as_const( functions ) )
9538 {
9539 *sOwnedFunctions() << func;
9540 *sBuiltinFunctions() << func->name();
9541 sBuiltinFunctions()->append( func->aliases() );
9542 }
9543 }
9544 return functions;
9545}
9546
9547bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
9548{
9549 int fnIdx = functionIndex( function->name() );
9550 if ( fnIdx != -1 )
9551 {
9552 return false;
9553 }
9554
9555 QMutexLocker locker( &sFunctionsMutex );
9556 sFunctions()->append( function );
9557 if ( transferOwnership )
9558 sOwnedFunctions()->append( function );
9559
9560 return true;
9561}
9562
9563bool QgsExpression::unregisterFunction( const QString &name )
9564{
9565 // You can never override the built in functions.
9566 if ( QgsExpression::BuiltinFunctions().contains( name ) )
9567 {
9568 return false;
9569 }
9570 int fnIdx = functionIndex( name );
9571 if ( fnIdx != -1 )
9572 {
9573 QMutexLocker locker( &sFunctionsMutex );
9574 sFunctions()->removeAt( fnIdx );
9575 sFunctionIndexMap.clear();
9576 return true;
9577 }
9578 return false;
9579}
9580
9582{
9583 qDeleteAll( *sOwnedFunctions() );
9584 sOwnedFunctions()->clear();
9586
9587const QStringList &QgsExpression::BuiltinFunctions()
9588{
9589 if ( sBuiltinFunctions()->isEmpty() )
9590 {
9591 Functions(); // this method builds the gmBuiltinFunctions as well
9592 }
9593 return *sBuiltinFunctions();
9595
9597 : QgsExpressionFunction( QStringLiteral( "array_foreach" ), QgsExpressionFunction::ParameterList() // skip-keyword-check
9598 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
9599 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
9600 QStringLiteral( "Arrays" ) )
9601{
9602
9603}
9604
9606{
9607 bool isStatic = false;
9608
9609 QgsExpressionNode::NodeList *args = node->args();
9611 if ( args->count() < 2 )
9612 return false;
9613
9614 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9615 {
9616 isStatic = true;
9617 }
9618 return isStatic;
9619}
9620
9622{
9623 Q_UNUSED( node )
9624 QVariantList result;
9625
9626 if ( args->count() < 2 )
9627 // error
9628 return result;
9629
9630 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
9631
9632 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
9633 std::unique_ptr< QgsExpressionContext > tempContext;
9634 if ( !subContext )
9635 {
9636 tempContext = std::make_unique< QgsExpressionContext >();
9637 subContext = tempContext.get();
9638 }
9639
9641 subContext->appendScope( subScope );
9642
9643 int i = 0;
9644 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it, ++i )
9645 {
9646 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), *it, true ) );
9647 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "counter" ), i, true ) );
9648 result << args->at( 1 )->eval( parent, subContext );
9649 }
9650
9651 if ( context )
9652 delete subContext->popScope();
9653
9654 return result;
9655}
9656
9657QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9659 // This is a dummy function, all the real handling is in run
9660 Q_UNUSED( values )
9661 Q_UNUSED( context )
9662 Q_UNUSED( parent )
9663 Q_UNUSED( node )
9664
9665 Q_ASSERT( false );
9666 return QVariant();
9667}
9668
9670{
9671 QgsExpressionNode::NodeList *args = node->args();
9672
9673 if ( args->count() < 2 )
9674 // error
9675 return false;
9676
9677 args->at( 0 )->prepare( parent, context );
9678
9679 QgsExpressionContext subContext;
9680 if ( context )
9681 subContext = *context;
9684 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
9685 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "counter" ), QVariant(), true ) );
9686 subContext.appendScope( subScope );
9687
9688 args->at( 1 )->prepare( parent, &subContext );
9689
9690 return true;
9691}
9694 : QgsExpressionFunction( QStringLiteral( "array_filter" ), QgsExpressionFunction::ParameterList()
9695 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
9696 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
9697 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
9698 QStringLiteral( "Arrays" ) )
9699{
9700
9701}
9702
9704{
9705 bool isStatic = false;
9706
9707 QgsExpressionNode::NodeList *args = node->args();
9709 if ( args->count() < 2 )
9710 return false;
9711
9712 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9713 {
9714 isStatic = true;
9715 }
9716 return isStatic;
9717}
9718
9720{
9721 Q_UNUSED( node )
9722 QVariantList result;
9723
9724 if ( args->count() < 2 )
9725 // error
9726 return result;
9727
9728 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
9729
9730 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
9731 std::unique_ptr< QgsExpressionContext > tempContext;
9732 if ( !subContext )
9733 {
9734 tempContext = std::make_unique< QgsExpressionContext >();
9735 subContext = tempContext.get();
9736 }
9737
9739 subContext->appendScope( subScope );
9740
9741 int limit = 0;
9742 if ( args->count() >= 3 )
9743 {
9744 const QVariant limitVar = args->at( 2 )->eval( parent, context );
9745
9746 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
9747 {
9748 limit = limitVar.toInt();
9749 }
9750 else
9751 {
9752 return result;
9753 }
9754 }
9755
9756 for ( const QVariant &value : array )
9757 {
9758 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), value, true ) );
9759 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
9760 {
9761 result << value;
9762
9763 if ( limit > 0 && limit == result.size() )
9764 break;
9765 }
9766 }
9767
9768 if ( context )
9769 delete subContext->popScope();
9770
9771 return result;
9772}
9773
9774QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9776 // This is a dummy function, all the real handling is in run
9777 Q_UNUSED( values )
9778 Q_UNUSED( context )
9779 Q_UNUSED( parent )
9780 Q_UNUSED( node )
9781
9782 Q_ASSERT( false );
9783 return QVariant();
9784}
9785
9787{
9788 QgsExpressionNode::NodeList *args = node->args();
9789
9790 if ( args->count() < 2 )
9791 // error
9792 return false;
9793
9794 args->at( 0 )->prepare( parent, context );
9795
9796 QgsExpressionContext subContext;
9797 if ( context )
9798 subContext = *context;
9799
9801 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
9802 subContext.appendScope( subScope );
9803
9804 args->at( 1 )->prepare( parent, &subContext );
9805
9806 return true;
9809 : QgsExpressionFunction( QStringLiteral( "with_variable" ), QgsExpressionFunction::ParameterList() <<
9810 QgsExpressionFunction::Parameter( QStringLiteral( "name" ) )
9811 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
9812 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
9813 QStringLiteral( "General" ) )
9814{
9815
9816}
9817
9819{
9820 bool isStatic = false;
9821
9822 QgsExpressionNode::NodeList *args = node->args();
9823
9824 if ( args->count() < 3 )
9825 return false;
9826
9827 // We only need to check if the node evaluation is static, if both - name and value - are static.
9828 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9829 {
9830 QVariant name = args->at( 0 )->eval( parent, context );
9831 QVariant value = args->at( 1 )->eval( parent, context );
9833 // Temporarily append a new scope to provide the variable
9834 appendTemporaryVariable( context, name.toString(), value );
9835 if ( args->at( 2 )->isStatic( parent, context ) )
9836 isStatic = true;
9837 popTemporaryVariable( context );
9838 }
9839
9840 return isStatic;
9841}
9842
9844{
9845 Q_UNUSED( node )
9846 QVariant result;
9847
9848 if ( args->count() < 3 )
9849 // error
9850 return result;
9851
9852 QVariant name = args->at( 0 )->eval( parent, context );
9853 QVariant value = args->at( 1 )->eval( parent, context );
9854
9855 const QgsExpressionContext *updatedContext = context;
9856 std::unique_ptr< QgsExpressionContext > tempContext;
9857 if ( !updatedContext )
9858 {
9859 tempContext = std::make_unique< QgsExpressionContext >();
9860 updatedContext = tempContext.get();
9862
9863 appendTemporaryVariable( updatedContext, name.toString(), value );
9864 result = args->at( 2 )->eval( parent, updatedContext );
9865
9866 if ( context )
9867 popTemporaryVariable( updatedContext );
9868
9869 return result;
9870}
9871
9872QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9874 // This is a dummy function, all the real handling is in run
9875 Q_UNUSED( values )
9876 Q_UNUSED( context )
9877 Q_UNUSED( parent )
9878 Q_UNUSED( node )
9879
9880 Q_ASSERT( false );
9881 return QVariant();
9882}
9883
9885{
9886 QgsExpressionNode::NodeList *args = node->args();
9887
9888 if ( args->count() < 3 )
9889 // error
9890 return false;
9891
9892 QVariant name = args->at( 0 )->prepare( parent, context );
9893 QVariant value = args->at( 1 )->prepare( parent, context );
9894
9895 const QgsExpressionContext *updatedContext = context;
9896 std::unique_ptr< QgsExpressionContext > tempContext;
9897 if ( !updatedContext )
9898 {
9899 tempContext = std::make_unique< QgsExpressionContext >();
9900 updatedContext = tempContext.get();
9901 }
9902
9903 appendTemporaryVariable( updatedContext, name.toString(), value );
9904 args->at( 2 )->prepare( parent, updatedContext );
9905
9906 if ( context )
9907 popTemporaryVariable( updatedContext );
9908
9909 return true;
9910}
9911
9912void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
9913{
9914 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
9915 delete updatedContext->popScope();
9916}
9917
9918void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
9919{
9922
9923 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
9924 updatedContext->appendScope( scope );
9925}
@ Left
Buffer to left of line.
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3060
@ 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.
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
JoinStyle
Join styles for buffers.
Definition qgis.h:1968
@ Bevel
Use beveled joins.
@ Round
Use rounded joins.
@ Miter
Use mitered joins.
RasterBandStatistic
Available raster band statistics.
Definition qgis.h:5523
@ StdDev
Standard deviation.
@ NoStatistic
No statistic.
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ Vector
Vector layer.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ Raster
Raster layer.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
EndCapStyle
End cap styles for buffers.
Definition qgis.h:1955
@ 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)
Aggregate
Available aggregates to calculate.
Definition qgis.h:5400
@ StringMinimumLength
Minimum length of string (string fields only)
@ FirstQuartile
First quartile (numeric fields only)
@ Mean
Mean of values (numeric fields only)
@ Median
Median of values (numeric fields only)
@ Max
Max of values.
@ Min
Min of values.
@ StringMaximumLength
Maximum length of string (string fields only)
@ Range
Range of values (max - min) (numeric and datetime fields only)
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
@ Sum
Sum of values.
@ Minority
Minority of values.
@ CountMissing
Number of missing (null) values.
@ ArrayAggregate
Create an array of values.
@ Majority
Majority of values.
@ StDevSample
Sample standard deviation of values (numeric fields only)
@ ThirdQuartile
Third quartile (numeric fields only)
@ CountDistinct
Number of distinct values.
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only)
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3045
@ 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:2014
@ 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...
@ PointM
PointM.
@ PointZ
PointZ.
@ PointZM
PointZM.
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.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
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.
Qgis::WkbType 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.
static Qgis::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:43
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.
QFlags< FormatFlag > FormatFlags
@ 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.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the 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:35
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 convertLengthMeasurement(double length, Qgis::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
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 bearing(const QgsPointXY &p1, const QgsPointXY &p2) const
Computes the bearing (in radians) between two points.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
double convertAreaMeasurement(double area, Qgis::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
Holder for the widget type and its configuration for a field.
QVariantMap config() const
Ellipse geometry type.
Definition qgsellipse.h:39
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.
QString uniqueHash(bool &ok, const QSet< QString > &variables=QSet< QString >()) const
Returns a unique hash representing the current state of the 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.
Qgis::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
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 replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
static PRIVATE QString helpText(QString name)
Returns the help text for a specified function.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
Create an expression allowing to evaluate if a field is equal to a value.
static bool unregisterFunction(const QString &name)
Unregisters a function from the expression engine.
Qgis::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g.,...
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions)
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)
Fetch next feature and stores in f, returns true on success.
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 & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
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 & 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 & 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...
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
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:58
QgsFields fields
Definition qgsfeature.h:68
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
bool isValid() const
Returns the validity of this feature.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
ConstraintStrength
Strength of constraints.
@ ConstraintStrengthNotSet
Constraint is not set.
@ ConstraintStrengthSoft
User is warned if constraint is violated but feature can still be accepted.
@ ConstraintStrengthHard
Constraint must be honored before feature can be accepted.
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:62
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:739
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
Q_INVOKABLE int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
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 Q_INVOKABLE 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.
Qgis::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.
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...
Q_INVOKABLE QString asWkt(int precision=17) const
Exports the geometry to WKT.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
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:137
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr) const
Returns the maximum inscribed circle.
Definition qgsgeos.cpp:2792
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:46
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.
bool lineLocatePointByM(double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance=true) const
Attempts to locate a point on the linestring by m value.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
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.
Base class for all map layer types.
Definition qgsmaplayer.h:76
QString name
Definition qgsmaplayer.h:80
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.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:83
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
QString id
Definition qgsmaplayer.h:79
QgsLayerMetadata metadata
Definition qgsmaplayer.h:82
Qgis::LayerType type
Definition qgsmaplayer.h:86
QString publicSource(bool hidePassword=false) const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
virtual bool isEditable() const
Returns true if the layer can be edited.
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 mapTipTemplate
Definition qgsmaplayer.h:89
Implementation of GeometrySimplifier using the "MapToPixel" algorithm.
@ 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:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:242
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:693
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition qgspoint.cpp:557
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:425
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition qgspoint.cpp:104
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:705
double y
Definition qgspoint.h:53
QgsRelationManager * relationManager
Definition qgsproject.h:117
static QgsProject * instance()
Returns the QgsProject singleton instance.
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 Field class represents a Raster Attribute Table field, including its name, usage and type.
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.
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.
Represents a relationship between two vector layers.
Definition qgsrelation.h:44
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:49
QgsVectorLayer * referencingLayer
Definition qgsrelation.h:48
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:500
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:145
Contains utility functions for working with symbols and symbol layers.
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:33
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
static bool validateAttribute(const QgsVectorLayer *layer, const QgsFeature &feature, int attributeIndex, QStringList &errors, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthNotSet, QgsFieldConstraints::ConstraintOrigin origin=QgsFieldConstraints::ConstraintOriginNotSet)
Tests a feature attribute value to check whether it passes all constraints which are present on the c...
Represents a vector layer which manages a vector based data sets.
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.
QVariant aggregate(Qgis::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.
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
Returns the editor widget setup for the field at the specified index.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
Q_INVOKABLE QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
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(Qgis::GeometryType type)
Returns a display string for a geometry type.
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Qgis::WkbType flatType(Qgis::WkbType 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).
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
#define str(x)
Definition qgis.cpp:38
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:120
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:6494
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6493
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:5958
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5917
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
QVariant fcnRampColor(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
#define ENSURE_GEOM_TYPE(f, g, geomtype)
QVariant fcnRampColorObject(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:80
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:62
const QgsMapLayer * layer
Definition qgsogcutils.h:72
QgsCoordinateTransformContext transformContext
Definition qgsogcutils.h:73
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:30