mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-15 00:02:52 -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();
|
static void registerContextFunctions();
|
||||||
%Docstring
|
%Docstring
|
||||||
Registers all known core functions provided by :py:class:`QgsExpressionContextScope` objects.
|
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
|
%End
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -658,6 +658,16 @@ The returned position is in map coordinates.
|
|||||||
|
|
||||||
|
|
||||||
.. versionadded:: 3.14
|
.. 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
|
%End
|
||||||
|
|
||||||
QgsMeshDatasetGroupTreeItem *datasetGroupTreeRootItem() const;
|
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;
|
func->mScaleBasedVisibilityDetails = mScaleBasedVisibilityDetails;
|
||||||
return func;
|
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();
|
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:
|
private:
|
||||||
|
|
||||||
class GetLayerVisibility : public QgsScopedExpressionFunction
|
class GetLayerVisibility : public QgsScopedExpressionFunction
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "qgsproject.h"
|
#include "qgsproject.h"
|
||||||
#include "qgsrelationmanager.h"
|
#include "qgsrelationmanager.h"
|
||||||
#include "qgsvectorlayer.h"
|
#include "qgsvectorlayer.h"
|
||||||
|
#include "qgsmeshlayer.h"
|
||||||
|
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QLocale>
|
#include <QLocale>
|
||||||
@ -405,6 +406,11 @@ class QgsExpressionUtils
|
|||||||
return qobject_cast<QgsRasterLayer *>( getMapLayer( value, e ) );
|
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 )
|
static QVariantList getListValue( const QVariant &value, QgsExpression *parent )
|
||||||
{
|
{
|
||||||
if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
|
if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
#include "qgslayermetadataformatter.h"
|
#include "qgslayermetadataformatter.h"
|
||||||
#include "qgsmesheditor.h"
|
#include "qgsmesheditor.h"
|
||||||
#include "qgsmessagelog.h"
|
#include "qgsmessagelog.h"
|
||||||
|
#include "qgsexpressioncontextutils.h"
|
||||||
|
|
||||||
QgsMeshLayer::QgsMeshLayer( const QString &meshLayerPath,
|
QgsMeshLayer::QgsMeshLayer( const QString &meshLayerPath,
|
||||||
const QString &baseName,
|
const QString &baseName,
|
||||||
@ -1138,6 +1139,40 @@ QgsPointXY QgsMeshLayer::snapOnElement( QgsMesh::ElementType elementType, const
|
|||||||
return QgsPointXY(); // avoid warnings
|
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
|
QgsMeshDatasetIndex QgsMeshLayer::staticScalarDatasetIndex() const
|
||||||
{
|
{
|
||||||
return QgsMeshDatasetIndex( mRendererSettings.activeScalarDatasetGroup(), mStaticScalarDatasetIndex );
|
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 );
|
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
|
* 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 *mChildLayer2 = nullptr; // relation with composite keys
|
||||||
QgsVectorLayer *mChildLayer = nullptr;
|
QgsVectorLayer *mChildLayer = nullptr;
|
||||||
QgsRasterLayer *mRasterLayer = nullptr;
|
QgsRasterLayer *mRasterLayer = nullptr;
|
||||||
|
QgsMeshLayer *mMeshLayer = nullptr;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
||||||
@ -123,6 +124,11 @@ class TestQgsExpression: public QObject
|
|||||||
rasterFileInfo.completeBaseName() );
|
rasterFileInfo.completeBaseName() );
|
||||||
QgsProject::instance()->addMapLayer( mRasterLayer );
|
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
|
// test memory layer for get_feature tests
|
||||||
mMemoryLayer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:integer&field=col2:string" ), QStringLiteral( "test" ), QStringLiteral( "memory" ) );
|
mMemoryLayer = new QgsVectorLayer( QStringLiteral( "Point?field=col1:integer&field=col2:string" ), QStringLiteral( "test" ), QStringLiteral( "memory" ) );
|
||||||
QVERIFY( mMemoryLayer->isValid() );
|
QVERIFY( mMemoryLayer->isValid() );
|
||||||
@ -257,6 +263,23 @@ class TestQgsExpression: public QObject
|
|||||||
QgsProject::instance()->relationManager()->addRelation( rel_ck );
|
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()
|
void cleanupTestCase()
|
||||||
{
|
{
|
||||||
QgsApplication::exitQgis();
|
QgsApplication::exitQgis();
|
||||||
|
@ -97,6 +97,8 @@ class TestQgsMeshLayer : public QObject
|
|||||||
|
|
||||||
void testMdalProviderQuerySublayers();
|
void testMdalProviderQuerySublayers();
|
||||||
void testMdalProviderQuerySublayersFastScan();
|
void testMdalProviderQuerySublayersFastScan();
|
||||||
|
|
||||||
|
void testSelectByExpression();
|
||||||
};
|
};
|
||||||
|
|
||||||
QString TestQgsMeshLayer::readFile( const QString &fname ) const
|
QString TestQgsMeshLayer::readFile( const QString &fname ) const
|
||||||
@ -1707,6 +1709,19 @@ void TestQgsMeshLayer::testMdalProviderQuerySublayersFastScan()
|
|||||||
QVERIFY( res.at( 0 ).skippedContainerScan() );
|
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()
|
void TestQgsMeshLayer::test_temporal()
|
||||||
{
|
{
|
||||||
const qint64 relativeTime_0 = -1000;
|
const qint64 relativeTime_0 = -1000;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user