mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-08 00:05:09 -04:00
Mesh expressions: add $vertex_as_point and $vertex_z functions (#44786)
* mesh expression $vertex_as_point $vertex_Z_value * functions help * SIP, doc and indentation * fix SIP * fix help file * change function name and add Meshes group description * fix typo * fix strings and docs * typo
This commit is contained in:
parent
fb33167d9c
commit
bc192a60b2
@ -365,6 +365,13 @@ Creates a new scope which contains variables and functions relating to provider
|
||||
static void registerContextFunctions();
|
||||
%Docstring
|
||||
Registers all known core functions provided by :py:class:`QgsExpressionContextScope` objects.
|
||||
%End
|
||||
|
||||
static QgsExpressionContextScope *meshExpressionScope() /Factory/;
|
||||
%Docstring
|
||||
Creates a new scope which contains functions relating to mesh layer elements (face, vertex, ...)
|
||||
|
||||
.. versionadded:: 3.22
|
||||
%End
|
||||
|
||||
public:
|
||||
|
@ -658,6 +658,16 @@ The returned position is in map coordinates.
|
||||
|
||||
|
||||
.. versionadded:: 3.14
|
||||
%End
|
||||
|
||||
QList<int> selectVerticesByExpression( const QString &expression, const QgsExpressionContext &expressionContext = QgsExpressionContext() );
|
||||
%Docstring
|
||||
Returns a list of vertex indexes that meet the condition defined by ``expression`` with the context ``expressionContext``
|
||||
|
||||
To express the relation with a vertex, the expression can be defined with function returning value
|
||||
linked to the current vertex, like " $vertex_z ", "$vertex_as_point"
|
||||
|
||||
.. versionadded:: 3.22
|
||||
%End
|
||||
|
||||
QgsMeshDatasetGroupTreeItem *datasetGroupTreeRootItem() const;
|
||||
|
8
resources/function_help/json/$vertex_as_point
Normal file
8
resources/function_help/json/$vertex_as_point
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "$vertex_as_point",
|
||||
"type": "function",
|
||||
"groups": ["Meshes"],
|
||||
"description": "Returns the current vertex as a point geometry.",
|
||||
"examples": [ { "expression":"geom_to_wkt( $vertex_as_point )", "returns":"'POINT(800 1500 41)'"}
|
||||
]
|
||||
}
|
8
resources/function_help/json/$vertex_z
Normal file
8
resources/function_help/json/$vertex_z
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "$vertex_z",
|
||||
"type": "function",
|
||||
"groups": ["Meshes"],
|
||||
"description": "Returns the Z value of the current mesh vertex.",
|
||||
"examples": [ { "expression":"$vertex_z", "returns":"42"}
|
||||
]
|
||||
}
|
5
resources/function_help/json/Meshes
Normal file
5
resources/function_help/json/Meshes
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Meshes",
|
||||
"type": "group",
|
||||
"description": "Contains functions which calculate or return mesh related values."
|
||||
}
|
@ -975,3 +975,97 @@ QgsScopedExpressionFunction *QgsExpressionContextUtils::GetLayerVisibility::clon
|
||||
func->mScaleBasedVisibilityDetails = mScaleBasedVisibilityDetails;
|
||||
return func;
|
||||
}
|
||||
|
||||
//
|
||||
// mesh expression context
|
||||
//
|
||||
|
||||
/// @cond PRIVATE
|
||||
class CurrentVertexZValueExpressionFunction: public QgsScopedExpressionFunction
|
||||
{
|
||||
public:
|
||||
CurrentVertexZValueExpressionFunction():
|
||||
QgsScopedExpressionFunction( "$vertex_z",
|
||||
0,
|
||||
QStringLiteral( "Meshes" ) )
|
||||
{}
|
||||
|
||||
QgsScopedExpressionFunction *clone() const override {return new CurrentVertexZValueExpressionFunction();}
|
||||
|
||||
QVariant func( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * ) override
|
||||
{
|
||||
if ( !context )
|
||||
return QVariant();
|
||||
|
||||
if ( !context->hasVariable( QStringLiteral( "_mesh_vertex_index" ) ) || !context->hasVariable( QStringLiteral( "_mesh_layer" ) ) )
|
||||
return QVariant();
|
||||
|
||||
int vertexIndex = context->variable( QStringLiteral( "_mesh_vertex_index" ) ).toInt();
|
||||
|
||||
QgsMeshLayer *layer = qobject_cast<QgsMeshLayer *>( qvariant_cast<QgsMapLayer *>( context->variable( QStringLiteral( "_mesh_layer" ) ) ) );
|
||||
if ( !layer || !layer->nativeMesh() || layer->nativeMesh()->vertexCount() <= vertexIndex )
|
||||
return QVariant();
|
||||
|
||||
const QgsMeshVertex &vertex = layer->nativeMesh()->vertex( vertexIndex );
|
||||
if ( !vertex.isEmpty() )
|
||||
return vertex.z();
|
||||
else
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool isStatic( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * ) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class CurrentVertexExpressionFunction: public QgsScopedExpressionFunction
|
||||
{
|
||||
public:
|
||||
CurrentVertexExpressionFunction():
|
||||
QgsScopedExpressionFunction( "$vertex_as_point",
|
||||
0,
|
||||
QStringLiteral( "Meshes" ) )
|
||||
{}
|
||||
|
||||
QgsScopedExpressionFunction *clone() const override {return new CurrentVertexExpressionFunction();}
|
||||
|
||||
QVariant func( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * ) override
|
||||
{
|
||||
if ( !context )
|
||||
return QVariant();
|
||||
|
||||
if ( !context->hasVariable( QStringLiteral( "_mesh_vertex_index" ) ) || !context->hasVariable( QStringLiteral( "_mesh_layer" ) ) )
|
||||
return QVariant();
|
||||
|
||||
int vertexIndex = context->variable( QStringLiteral( "_mesh_vertex_index" ) ).toInt();
|
||||
|
||||
QgsMeshLayer *layer = qobject_cast<QgsMeshLayer *>( qvariant_cast<QgsMapLayer *>( context->variable( QStringLiteral( "_mesh_layer" ) ) ) );
|
||||
if ( !layer || !layer->nativeMesh() || layer->nativeMesh()->vertexCount() <= vertexIndex )
|
||||
return QVariant();
|
||||
|
||||
const QgsMeshVertex &vertex = layer->nativeMesh()->vertex( vertexIndex );
|
||||
if ( !vertex.isEmpty() )
|
||||
return QVariant::fromValue( QgsGeometry( new QgsPoint( vertex ) ) );
|
||||
else
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool isStatic( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * ) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
QgsExpressionContextScope *QgsExpressionContextUtils::meshExpressionScope()
|
||||
{
|
||||
QgsExpression::registerFunction( new CurrentVertexExpressionFunction, true );
|
||||
QgsExpression::registerFunction( new CurrentVertexZValueExpressionFunction, true );
|
||||
|
||||
std::unique_ptr<QgsExpressionContextScope> scope = std::make_unique<QgsExpressionContextScope>();
|
||||
scope->addFunction( "$vertex_as_point", new CurrentVertexExpressionFunction );
|
||||
scope->addFunction( "$vertex_z", new CurrentVertexZValueExpressionFunction );
|
||||
|
||||
return scope.release();
|
||||
}
|
||||
///@endcond
|
||||
|
@ -321,6 +321,12 @@ class CORE_EXPORT QgsExpressionContextUtils
|
||||
*/
|
||||
static void registerContextFunctions();
|
||||
|
||||
/**
|
||||
* Creates a new scope which contains functions relating to mesh layer elements (face, vertex, ...)
|
||||
* \since QGIS 3.22
|
||||
*/
|
||||
static QgsExpressionContextScope *meshExpressionScope() SIP_FACTORY;
|
||||
|
||||
private:
|
||||
|
||||
class GetLayerVisibility : public QgsScopedExpressionFunction
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "qgsproject.h"
|
||||
#include "qgsrelationmanager.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsmeshlayer.h"
|
||||
|
||||
#include <QThread>
|
||||
#include <QLocale>
|
||||
@ -405,6 +406,11 @@ class QgsExpressionUtils
|
||||
return qobject_cast<QgsRasterLayer *>( getMapLayer( value, e ) );
|
||||
}
|
||||
|
||||
static QgsMeshLayer *getMeshLayer( const QVariant &value, QgsExpression *e )
|
||||
{
|
||||
return qobject_cast<QgsMeshLayer *>( getMapLayer( value, e ) );
|
||||
}
|
||||
|
||||
static QVariantList getListValue( const QVariant &value, QgsExpression *parent )
|
||||
{
|
||||
if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "qgslayermetadataformatter.h"
|
||||
#include "qgsmesheditor.h"
|
||||
#include "qgsmessagelog.h"
|
||||
#include "qgsexpressioncontextutils.h"
|
||||
|
||||
QgsMeshLayer::QgsMeshLayer( const QString &meshLayerPath,
|
||||
const QString &baseName,
|
||||
@ -1138,6 +1139,40 @@ QgsPointXY QgsMeshLayer::snapOnElement( QgsMesh::ElementType elementType, const
|
||||
return QgsPointXY(); // avoid warnings
|
||||
}
|
||||
|
||||
QList<int> QgsMeshLayer::selectVerticesByExpression( const QString &expressionString, const QgsExpressionContext &expressionContext )
|
||||
{
|
||||
if ( !mNativeMesh )
|
||||
{
|
||||
// lazy loading of mesh data
|
||||
fillNativeMesh();
|
||||
}
|
||||
|
||||
QList<int> ret;
|
||||
|
||||
if ( !mNativeMesh )
|
||||
return ret;
|
||||
|
||||
QgsExpression expression( expressionString );
|
||||
QgsExpressionContext context = expressionContext;
|
||||
|
||||
std::unique_ptr<QgsExpressionContextScope> expScope( QgsExpressionContextUtils::meshExpressionScope() );
|
||||
|
||||
context.appendScope( expScope.release() );
|
||||
context.lastScope()->setVariable( QStringLiteral( "_mesh_layer" ), QVariant::fromValue( this ) );
|
||||
|
||||
expression.prepare( &context );
|
||||
|
||||
for ( int i = 0; i < mNativeMesh->vertexCount(); ++i )
|
||||
{
|
||||
context.lastScope()->setVariable( QStringLiteral( "_mesh_vertex_index" ), i, false );
|
||||
|
||||
if ( expression.evaluate( &context ).toBool() )
|
||||
ret.append( i );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QgsMeshDatasetIndex QgsMeshLayer::staticScalarDatasetIndex() const
|
||||
{
|
||||
return QgsMeshDatasetIndex( mRendererSettings.activeScalarDatasetGroup(), mStaticScalarDatasetIndex );
|
||||
|
@ -670,6 +670,16 @@ class CORE_EXPORT QgsMeshLayer : public QgsMapLayer
|
||||
*/
|
||||
QgsPointXY snapOnElement( QgsMesh::ElementType elementType, const QgsPointXY &point, double searchRadius );
|
||||
|
||||
/**
|
||||
* Returns a list of vertex indexes that meet the condition defined by \a expression with the context \a expressionContext
|
||||
*
|
||||
* To express the relation with a vertex, the expression can be defined with function returning value
|
||||
* linked to the current vertex, like " $vertex_z ", "$vertex_as_point"
|
||||
*
|
||||
* \since QGIS 3.22
|
||||
*/
|
||||
QList<int> selectVerticesByExpression( const QString &expression, const QgsExpressionContext &expressionContext = QgsExpressionContext() );
|
||||
|
||||
/**
|
||||
* Returns the root items of the dataset group tree item
|
||||
*
|
||||
|
@ -65,6 +65,7 @@ class TestQgsExpression: public QObject
|
||||
QgsVectorLayer *mChildLayer2 = nullptr; // relation with composite keys
|
||||
QgsVectorLayer *mChildLayer = nullptr;
|
||||
QgsRasterLayer *mRasterLayer = nullptr;
|
||||
QgsMeshLayer *mMeshLayer = nullptr;
|
||||
|
||||
private slots:
|
||||
|
||||
@ -123,6 +124,11 @@ class TestQgsExpression: public QObject
|
||||
rasterFileInfo.completeBaseName() );
|
||||
QgsProject::instance()->addMapLayer( mRasterLayer );
|
||||
|
||||
QString meshFileName = testDataDir + "/mesh/quad_flower.2dm";
|
||||
mMeshLayer = new QgsMeshLayer( meshFileName, "mesh layer", "mdal" );
|
||||
mMeshLayer->updateTriangularMesh();
|
||||
QgsProject::instance()->addMapLayer( mMeshLayer );
|
||||
|
||||
// test memory layer for get_feature tests
|
||||
mMemoryLayer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:integer&field=col2:string" ), QStringLiteral( "test" ), QStringLiteral( "memory" ) );
|
||||
QVERIFY( mMemoryLayer->isValid() );
|
||||
@ -257,6 +263,23 @@ class TestQgsExpression: public QObject
|
||||
QgsProject::instance()->relationManager()->addRelation( rel_ck );
|
||||
}
|
||||
|
||||
void evalMeshElement()
|
||||
{
|
||||
QgsExpressionContext context;
|
||||
context.appendScope( QgsExpressionContextUtils::meshExpressionScope() );
|
||||
context.lastScope()->setVariable( QStringLiteral( "_mesh_vertex_index" ), 2 );
|
||||
context.lastScope()->setVariable( QStringLiteral( "_mesh_layer" ), QVariant::fromValue( mMeshLayer ) );
|
||||
|
||||
QgsExpression expression( QStringLiteral( "$vertex_z" ) );
|
||||
QCOMPARE( expression.evaluate( &context ).toDouble(), 800.0 );
|
||||
|
||||
expression = QgsExpression( QStringLiteral( "$vertex_as_point" ) );
|
||||
QVariant out = expression.evaluate( &context );
|
||||
QgsGeometry outGeom = out.value<QgsGeometry>();
|
||||
QgsGeometry geom( new QgsPoint( 2500, 2500, 800 ) );
|
||||
QCOMPARE( geom.equals( outGeom ), true );
|
||||
}
|
||||
|
||||
void cleanupTestCase()
|
||||
{
|
||||
QgsApplication::exitQgis();
|
||||
|
@ -97,6 +97,8 @@ class TestQgsMeshLayer : public QObject
|
||||
|
||||
void testMdalProviderQuerySublayers();
|
||||
void testMdalProviderQuerySublayersFastScan();
|
||||
|
||||
void testSelectByExpression();
|
||||
};
|
||||
|
||||
QString TestQgsMeshLayer::readFile( const QString &fname ) const
|
||||
@ -1707,6 +1709,19 @@ void TestQgsMeshLayer::testMdalProviderQuerySublayersFastScan()
|
||||
QVERIFY( res.at( 0 ).skippedContainerScan() );
|
||||
}
|
||||
|
||||
void TestQgsMeshLayer::testSelectByExpression()
|
||||
{
|
||||
mMdalLayer->updateTriangularMesh();
|
||||
QgsExpressionContext expressionContext;
|
||||
|
||||
QList<int> selectedVerticesIndexes = mMdalLayer->selectVerticesByExpression( QStringLiteral( " $vertex_z > 30" ) );
|
||||
QCOMPARE( selectedVerticesIndexes, QList( {2, 3} ) );
|
||||
|
||||
selectedVerticesIndexes = mMdalLayer->selectVerticesByExpression( QStringLiteral( " x($vertex_as_point) > 1500" ) );
|
||||
QCOMPARE( selectedVerticesIndexes.count(), 3 );
|
||||
QCOMPARE( selectedVerticesIndexes, QList( {1, 2, 3} ) );
|
||||
}
|
||||
|
||||
void TestQgsMeshLayer::test_temporal()
|
||||
{
|
||||
const qint64 relativeTime_0 = -1000;
|
||||
|
Loading…
x
Reference in New Issue
Block a user