mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
[FEATURE][expressions] allow to seed random functions
useful to get deterministic random values
This commit is contained in:
parent
af1c087919
commit
65fed42213
@ -1,8 +1,9 @@
|
||||
{
|
||||
"name": "rand",
|
||||
"type": "function",
|
||||
"description": "Returns a random integer within the range specified by the minimum and maximum argument (inclusive).",
|
||||
"description": "Returns a random integer within the range specified by the minimum and maximum argument (inclusive). If a seed is provided, the returned will always be the same, depending on the seed.",
|
||||
"arguments": [ {"arg":"min","description":"an integer representing the smallest possible random number desired"},
|
||||
{"arg":"max","description":"an integer representing the largest possible random number desired"}],
|
||||
{"arg":"max","description":"an integer representing the largest possible random number desired"},
|
||||
{"arg":"seed","optional":true,"default":"null","description":"any value to use as seed"}],
|
||||
"examples": [ { "expression":"rand(1, 10)", "returns":"8"}]
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
{
|
||||
"name": "randf",
|
||||
"type": "function",
|
||||
"description": "Returns a random float within the range specified by the minimum and maximum argument (inclusive).",
|
||||
"description": "Returns a random float within the range specified by the minimum and maximum argument (inclusive). If a seed is provided, the returned will always be the same, depending on the seed.",
|
||||
"arguments": [ {"arg":"min","optional":true,"default":"0.0","description":"an float representing the smallest possible random number desired"},
|
||||
{"arg":"max","optional":true,"default":"1.0","description":"an float representing the largest possible random number desired"}],
|
||||
"examples": [ { "expression":"randf(1, 10)", "returns":"4.59258286403147"}]
|
||||
{"arg":"max","optional":true,"default":"1.0","description":"an float representing the largest possible random number desired"},
|
||||
{"arg":"seed","optional":true,"default":"null","description":"any value to use as seed"}],
|
||||
"examples": [ { "expression":"randf(1, 10)", "returns":"4.59258286403147"} ]
|
||||
}
|
||||
|
@ -368,22 +368,68 @@ static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext
|
||||
{
|
||||
double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
|
||||
double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
|
||||
|
||||
if ( max < min )
|
||||
return QVariant();
|
||||
|
||||
QRandomGenerator *generator;
|
||||
if ( QgsExpressionUtils::isNull( values.at( 2 ) ) )
|
||||
{
|
||||
generator = QRandomGenerator::global();
|
||||
}
|
||||
else
|
||||
{
|
||||
quint32 seed;
|
||||
if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
|
||||
{
|
||||
// if seed can be converted to int, we use as is
|
||||
seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
|
||||
}
|
||||
else
|
||||
{
|
||||
// if not, we hash string representation to int
|
||||
QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
|
||||
std::hash<std::string> hasher;
|
||||
seed = hasher( seedStr.toStdString() );
|
||||
}
|
||||
generator = &QRandomGenerator::QRandomGenerator( seed );
|
||||
}
|
||||
|
||||
// Return a random double in the range [min, max] (inclusive)
|
||||
double f = static_cast< double >( qrand() ) / RAND_MAX;
|
||||
return QVariant( min + f * ( max - min ) );
|
||||
return QVariant( min + generator->generateDouble() * ( max - min ) );
|
||||
}
|
||||
static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
|
||||
{
|
||||
qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
|
||||
qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
|
||||
quint32 min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
|
||||
quint32 max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
|
||||
if ( max < min )
|
||||
return QVariant();
|
||||
|
||||
QRandomGenerator *generator;
|
||||
if ( QgsExpressionUtils::isNull( values.at( 2 ) ) )
|
||||
{
|
||||
generator = QRandomGenerator::global();
|
||||
}
|
||||
else
|
||||
{
|
||||
quint32 seed;
|
||||
if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
|
||||
{
|
||||
// if seed can be converted to int, we use as is
|
||||
seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
|
||||
}
|
||||
else
|
||||
{
|
||||
// if not, we hash string representation to int
|
||||
QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
|
||||
std::hash<std::string> hasher;
|
||||
seed = hasher( seedStr.toStdString() );
|
||||
}
|
||||
generator = &QRandomGenerator::QRandomGenerator( seed );
|
||||
}
|
||||
|
||||
// Return a random integer in the range [min, max] (inclusive)
|
||||
return QVariant( min + ( qrand() % static_cast< qlonglong >( max - min + 1 ) ) );
|
||||
return QVariant( generator->bounded( min, max + 1 ) );
|
||||
}
|
||||
|
||||
static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
|
||||
@ -5216,11 +5262,11 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
|
||||
<< new QgsStaticExpressionFunction( QStringLiteral( "log" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "base" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog, QStringLiteral( "Math" ) )
|
||||
<< new QgsStaticExpressionFunction( QStringLiteral( "round" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ), fcnRound, QStringLiteral( "Math" ) );
|
||||
|
||||
QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( QStringLiteral( "rand" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnRnd, QStringLiteral( "Math" ) );
|
||||
QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( QStringLiteral( "rand" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRnd, QStringLiteral( "Math" ) );
|
||||
randFunc->setIsStatic( false );
|
||||
functions << randFunc;
|
||||
|
||||
QgsStaticExpressionFunction *randfFunc = new QgsStaticExpressionFunction( QStringLiteral( "randf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ), true, 0.0 ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ), true, 1.0 ), fcnRndF, QStringLiteral( "Math" ) );
|
||||
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" ) );
|
||||
randfFunc->setIsStatic( false );
|
||||
functions << randfFunc;
|
||||
|
||||
|
@ -2160,6 +2160,34 @@ class TestQgsExpression: public QObject
|
||||
QgsExpression exp3( QStringLiteral( "rand(10,1)" ) );
|
||||
QVariant v3 = exp3.evaluate();
|
||||
QCOMPARE( v3.type(), QVariant::Invalid );
|
||||
|
||||
// Supports multiple type of seeds
|
||||
QgsExpression exp4( QStringLiteral( "rand(1,100,123)" ) );
|
||||
QVariant v4 = exp4.evaluate();
|
||||
QCOMPARE( v4.type(), QVariant::Int );
|
||||
QgsExpression exp5( QStringLiteral( "rand(1,100,1.23)" ) );
|
||||
QVariant v5 = exp5.evaluate();
|
||||
QCOMPARE( v5.type(), QVariant::Int );
|
||||
QgsExpression exp6( QStringLiteral( "rand(1,100,'123')" ) );
|
||||
QVariant v6 = exp6.evaluate();
|
||||
QCOMPARE( v6.type(), QVariant::Int );
|
||||
QgsExpression exp7( QStringLiteral( "rand(1,100,'abc')" ) );
|
||||
QVariant v7 = exp7.evaluate();
|
||||
QCOMPARE( v7.type(), QVariant::Int );
|
||||
|
||||
// Two calls with the same seed return the same numer
|
||||
QgsExpression exp8( QStringLiteral( "rand(1,100000000000,1)" ) );
|
||||
QVariant v8 = exp8.evaluate();
|
||||
QgsExpression exp9( QStringLiteral( "rand(1,100000000000,1)" ) );
|
||||
QVariant v9 = exp9.evaluate();
|
||||
QCOMPARE( v8.toInt() == v9.toInt(), true );
|
||||
|
||||
// Two calls with a different seed return a different number
|
||||
QgsExpression exp10( QStringLiteral( "rand(1,100000000000,1)" ) );
|
||||
QVariant v10 = exp10.evaluate();
|
||||
QgsExpression exp11( QStringLiteral( "rand(1,100000000000,2)" ) );
|
||||
QVariant v11 = exp11.evaluate();
|
||||
QCOMPARE( v10.toInt() != v11.toInt(), true );
|
||||
}
|
||||
|
||||
void eval_randf()
|
||||
@ -2177,6 +2205,34 @@ class TestQgsExpression: public QObject
|
||||
QgsExpression exp3( QStringLiteral( "randf(9.3333,1.784)" ) );
|
||||
QVariant v3 = exp3.evaluate();
|
||||
QCOMPARE( v3.type(), QVariant::Invalid );
|
||||
|
||||
// Supports multiple type of seeds
|
||||
QgsExpression exp4( QStringLiteral( "randf(1,100,123)" ) );
|
||||
QVariant v4 = exp4.evaluate();
|
||||
QCOMPARE( v4.type(), QVariant::Float );
|
||||
QgsExpression exp5( QStringLiteral( "randf(1,100,1.23)" ) );
|
||||
QVariant v5 = exp5.evaluate();
|
||||
QCOMPARE( v5.type(), QVariant::Float );
|
||||
QgsExpression exp6( QStringLiteral( "randf(1,100,'123')" ) );
|
||||
QVariant v6 = exp6.evaluate();
|
||||
QCOMPARE( v6.type(), QVariant::Float );
|
||||
QgsExpression exp7( QStringLiteral( "randf(1,100,'abc')" ) );
|
||||
QVariant v7 = exp7.evaluate();
|
||||
QCOMPARE( v7.type(), QVariant::Float );
|
||||
|
||||
// Two calls with the same seed return the same numer
|
||||
QgsExpression exp8( QStringLiteral( "randf(seed:=1)" ) );
|
||||
QVariant v8 = exp8.evaluate();
|
||||
QgsExpression exp9( QStringLiteral( "randf(seed:=1)" ) );
|
||||
QVariant v9 = exp9.evaluate();
|
||||
QCOMPARE( v8.toFloat() == v9.toFloat(), true );
|
||||
|
||||
// Two calls with a different seed return a different number
|
||||
QgsExpression exp10( QStringLiteral( "randf(seed:=1)" ) );
|
||||
QVariant v10 = exp10.evaluate();
|
||||
QgsExpression exp11( QStringLiteral( "randf(seed:=2)" ) );
|
||||
QVariant v11 = exp11.evaluate();
|
||||
QCOMPARE( v10.toFloat() != v11.toFloat(), true );
|
||||
}
|
||||
|
||||
void referenced_columns()
|
||||
|
Loading…
x
Reference in New Issue
Block a user