Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
convert point cloud expressions to PDAL expressions
  • Loading branch information
alexbruy authored and wonder-sk committed May 2, 2023
1 parent d62b746 commit 45791b1
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 6 deletions.
8 changes: 8 additions & 0 deletions src/core/pointcloud/expression/qgspointcloudexpression.cpp
Expand Up @@ -207,3 +207,11 @@ bool QgsPointCloudExpression::checkExpression( const QgsExpression &expression,
errorMessage = exp.parserErrorString();
return !exp.hasParserError();
}

QString QgsPointCloudExpression::asPdalExpression() const
{
if ( !d->mRootNode )
return QString();

return d->mRootNode->toPdal();
}
7 changes: 7 additions & 0 deletions src/core/pointcloud/expression/qgspointcloudexpression.h
Expand Up @@ -205,6 +205,13 @@ class CORE_EXPORT QgsPointCloudExpression
*/
static bool checkExpression( const QgsExpression &expression, const QgsPointCloudBlock *block, QString &errorMessage );

/**
* Returns an expression string converted to PDAL expression format.
*
* \since QGIS 3.32
*/
QString asPdalExpression() const;

private:

/**
Expand Down
7 changes: 7 additions & 0 deletions src/core/pointcloud/expression/qgspointcloudexpressionnode.h
Expand Up @@ -165,6 +165,13 @@ class CORE_EXPORT QgsPointCloudExpressionNode
*/
bool prepare( QgsPointCloudExpression *parent, const QgsPointCloudBlock *block );

/**
* Converts this node into a PDAL expression format.
*
* \since QGIS 3.32
*/
virtual QString toPdal() const = 0;

/**
* First line in the parser this node was found.
* \note This might not be complete for all nodes. Currently
Expand Down
Expand Up @@ -143,6 +143,14 @@ bool QgsPointCloudExpressionNodeUnaryOperator::convert( const QgsExpressionNodeU
return true;
}

QString QgsPointCloudExpressionNodeUnaryOperator::toPdal() const
{
if ( dynamic_cast<QgsPointCloudExpressionNodeBinaryOperator *>( mOperand ) )
return UNARY_OPERATOR_TEXT[mOp] == QStringLiteral( "NOT" ) ? QStringLiteral( "!(%1)" ).arg( mOperand->toPdal() ) : QStringLiteral( "-(%1)" ).arg( mOperand->dump() );
else
return UNARY_OPERATOR_TEXT[mOp] == QStringLiteral( "NOT" ) ? QStringLiteral( "!%1" ).arg( mOperand->toPdal() ) : QStringLiteral( "-%1" ).arg( mOperand->dump() );
}

//

double QgsPointCloudExpressionNodeBinaryOperator::evalNode( QgsPointCloudExpression *parent, int pointIndex )
Expand Down Expand Up @@ -513,6 +521,47 @@ bool QgsPointCloudExpressionNodeBinaryOperator::convert( const QgsExpressionNode
return true;
}

QString QgsPointCloudExpressionNodeBinaryOperator::toPdal() const
{
QgsPointCloudExpressionNodeBinaryOperator *lOp = dynamic_cast<QgsPointCloudExpressionNodeBinaryOperator *>( mOpLeft );
QgsPointCloudExpressionNodeBinaryOperator *rOp = dynamic_cast<QgsPointCloudExpressionNodeBinaryOperator *>( mOpRight );

QString rdump( mOpRight->toPdal() );

QString fmt;
if ( leftAssociative() )
{
fmt += lOp && ( lOp->precedence() < precedence() ) ? QStringLiteral( "(%1)" ) : QStringLiteral( "%1" );
fmt += QLatin1String( " %2 " );
fmt += rOp && ( rOp->precedence() <= precedence() ) ? QStringLiteral( "(%3)" ) : QStringLiteral( "%3" );
}
else
{
fmt += lOp && ( lOp->precedence() <= precedence() ) ? QStringLiteral( "(%1)" ) : QStringLiteral( "%1" );
fmt += QLatin1String( " %2 " );
fmt += rOp && ( rOp->precedence() < precedence() ) ? QStringLiteral( "(%3)" ) : QStringLiteral( "%3" );
}

QString opText = BINARY_OPERATOR_TEXT[mOp];
if ( opText == QStringLiteral( "AND" ) )
{
opText = QStringLiteral( "&&" );
}
else if ( opText == QStringLiteral( "OR" ) )
{
opText = QStringLiteral( "||" );
}
else if ( opText == QStringLiteral( "<>" ) )
{
opText = QStringLiteral( "!=" );
}
else if ( opText == QStringLiteral( "=" ) )
{
opText = QStringLiteral( "==" );
}
return fmt.arg( mOpLeft->toPdal(), opText, rdump );
}

//

double QgsPointCloudExpressionNodeInOperator::evalNode( QgsPointCloudExpression *parent, int pointIndex )
Expand Down Expand Up @@ -608,6 +657,16 @@ bool QgsPointCloudExpressionNodeInOperator::isStatic( QgsPointCloudExpression *p
return true;
}

QString QgsPointCloudExpressionNodeInOperator::toPdal() const
{
QStringList values;
for ( QgsPointCloudExpressionNode *n : mList->list() )
{
values << QStringLiteral( "(%1 %2 %3)" ).arg( mNode->toPdal(), mNotIn ? "!=" : "==", n->toPdal() );
}
return QStringLiteral( "(%1)" ).arg( values.join( mNotIn ? QStringLiteral( " && " ) : QStringLiteral( " || " ) ) );
}

//

double QgsPointCloudExpressionNodeLiteral::evalNode( QgsPointCloudExpression *parent, int pointIndex )
Expand All @@ -629,7 +688,6 @@ bool QgsPointCloudExpressionNodeLiteral::prepareNode( QgsPointCloudExpression *p
return true;
}


QString QgsPointCloudExpressionNodeLiteral::valueAsString() const
{
return QString( "%1" ).arg( mValue );
Expand Down Expand Up @@ -666,6 +724,11 @@ bool QgsPointCloudExpressionNodeLiteral::isStatic( QgsPointCloudExpression *pare
return true;
}

QString QgsPointCloudExpressionNodeLiteral::toPdal() const
{
return valueAsString();
}

//

double QgsPointCloudExpressionNodeAttributeRef::evalNode( QgsPointCloudExpression *parent, int pointIndex )
Expand Down Expand Up @@ -742,3 +805,8 @@ bool QgsPointCloudExpressionNodeAttributeRef::isStatic( QgsPointCloudExpression
Q_UNUSED( block )
return false;
}

QString QgsPointCloudExpressionNodeAttributeRef::toPdal() const
{
return mName;
}
35 changes: 35 additions & 0 deletions src/core/pointcloud/expression/qgspointcloudexpressionnodeimpl.h
Expand Up @@ -68,6 +68,13 @@ class CORE_EXPORT QgsPointCloudExpressionNodeUnaryOperator : public QgsPointClou
double evalNode( QgsPointCloudExpression *parent, int pointIndex ) override;
QString dump() const override;

/**
* Returns PDAL expression representing the node
*
* \since QGIS 3.32
*/
QString toPdal() const override;

QSet<QString> referencedAttributes() const override;
QList<const QgsPointCloudExpressionNode *> nodes() const override;
QgsPointCloudExpressionNode *clone() const override;
Expand Down Expand Up @@ -159,6 +166,13 @@ class CORE_EXPORT QgsPointCloudExpressionNodeBinaryOperator : public QgsPointClo
double evalNode( QgsPointCloudExpression *parent, int pointIndex ) override;
QString dump() const override;

/**
* Returns PDAL expression representing the node
*
* \since QGIS 3.32
*/
QString toPdal() const override;

QSet<QString> referencedAttributes() const override;
QList<const QgsPointCloudExpressionNode *> nodes( ) const override;

Expand Down Expand Up @@ -233,6 +247,13 @@ class CORE_EXPORT QgsPointCloudExpressionNodeInOperator : public QgsPointCloudEx
double evalNode( QgsPointCloudExpression *parent, int pointIndex ) override;
QString dump() const override;

/**
* Returns PDAL expression representing the node
*
* \since QGIS 3.32
*/
QString toPdal() const override;

QSet<QString> referencedAttributes() const override;
QList<const QgsPointCloudExpressionNode *> nodes() const override;
QgsPointCloudExpressionNode *clone() const override;
Expand Down Expand Up @@ -269,6 +290,13 @@ class CORE_EXPORT QgsPointCloudExpressionNodeLiteral : public QgsPointCloudExpre
double evalNode( QgsPointCloudExpression *parent, int pointIndex ) override;
QString dump() const override;

/**
* Returns PDAL expression representing the node
*
* \since QGIS 3.32
*/
QString toPdal() const override;

QSet<QString> referencedAttributes() const override;

QList<const QgsPointCloudExpressionNode *> nodes() const override;
Expand Down Expand Up @@ -310,6 +338,13 @@ class CORE_EXPORT QgsPointCloudExpressionNodeAttributeRef : public QgsPointCloud
double evalNode( QgsPointCloudExpression *parent, int pointIndex ) override;
QString dump() const override;

/**
* Returns PDAL expression representing the node
*
* \since QGIS 3.32
*/
QString toPdal() const override;

QSet<QString> referencedAttributes() const override;
QList<const QgsPointCloudExpressionNode *> nodes( ) const override;

Expand Down
44 changes: 39 additions & 5 deletions tests/src/core/testqgspointcloudexpression.cpp
Expand Up @@ -25,7 +25,6 @@
#include "qgspointcloudexpression.h"



template <typename T>
bool _storeToStream( char *s, size_t position, QgsPointCloudAttribute::DataType type, T value )
{
Expand Down Expand Up @@ -118,6 +117,8 @@ class TestQgsPointCloudExpression: public QObject
void testEvaluating_data();
void testEvaluating();
void testBlockResize();
void testConversionToPdal_data();
void testConversionToPdal();

private:
QString mTestDataDir;
Expand Down Expand Up @@ -316,12 +317,10 @@ void TestQgsPointCloudExpression::cleanupTestCase()

void TestQgsPointCloudExpression::init()
{

}

void TestQgsPointCloudExpression::cleanup()
{

}

void TestQgsPointCloudExpression::testCreateBlock()
Expand Down Expand Up @@ -387,7 +386,6 @@ void TestQgsPointCloudExpression::testParsing_data()
QTest::newRow( "pow" ) << "2 ^ 8" << true;
QTest::newRow( "arithmetic" ) << "1+2*3" << true;
QTest::newRow( "logic" ) << "be or not be" << true;

}

void TestQgsPointCloudExpression::testParsing()
Expand Down Expand Up @@ -506,7 +504,6 @@ void TestQgsPointCloudExpression::testEvaluating_data()
QTest::newRow( "multiple attributes compared arithmetic" ) << "ReturnNumber = NumberOfReturns -1" << 2 << true;
QTest::newRow( "multiple attributes compared arithmetic" ) << "ReturnNumber = NumberOfReturns -1" << 3 << false;
QTest::newRow( "multiple attributes compared arithmetic" ) << "ReturnNumber = NumberOfReturns -1" << 4 << false;

}

void TestQgsPointCloudExpression::testEvaluating()
Expand Down Expand Up @@ -550,5 +547,42 @@ void TestQgsPointCloudExpression::testBlockResize()
QCOMPARE( QByteArray( mBlock->data(), mBlock->pointCount() * recordSize ), data );
}

void TestQgsPointCloudExpression::testConversionToPdal_data()
{
QTest::addColumn<QString>( "string" );
QTest::addColumn<QString>( "converted" );

QTest::newRow( "single attribute comparison" ) << "X > 100" << "X > 100";
QTest::newRow( "single attribute comparison" ) << "X >= 100" << "X >= 100";
QTest::newRow( "single attribute comparison" ) << "X < 100" << "X < 100";
QTest::newRow( "single attribute comparison" ) << "X <= 100" << "X <= 100";
QTest::newRow( "single attribute comparison" ) << "X = 100" << "X == 100";
QTest::newRow( "single attribute comparison" ) << "X <> 100" << "X != 100";
QTest::newRow( "single attribute comparison" ) << "NOT (NumberOfReturns = 1)" << "!(NumberOfReturns == 1)";

QTest::newRow( "multiple attribute comparison" ) << "X > 100 AND ReturnNumber = 1" << "X > 100 && ReturnNumber == 1";
QTest::newRow( "multiple attribute comparison" ) << "X > 100 OR ReturnNumber = 1" << "X > 100 || ReturnNumber == 1";
QTest::newRow( "multiple attribute comparison" ) << "(X > 100 OR ReturnNumber = 1) AND Classification <> 2" << "(X > 100 || ReturnNumber == 1) && Classification != 2";

QTest::newRow( "single attribute in()" ) << "Classification in (1, 3, 5)" << "((Classification == 1) || (Classification == 3) || (Classification == 5))";

QTest::newRow( "single attribute not in()" ) << "Classification not in (1, 3, 5)" << "((Classification != 1) && (Classification != 3) && (Classification != 5))";

QTest::newRow( "arifmetic operators" ) << "Red > Green + 50" << "Red > Green + 50";
QTest::newRow( "arifmetic operators" ) << "Red > Green - 50" << "Red > Green - 50";
QTest::newRow( "arifmetic operators" ) << "Red = Intensity * 64.0" << "Red == Intensity * 64";
QTest::newRow( "arifmetic operators" ) << "Red = Green / 128" << "Red == Green / 128";
}

void TestQgsPointCloudExpression::testConversionToPdal()
{
QFETCH( QString, string );
QFETCH( QString, converted );

QgsPointCloudExpression exp( string );

QCOMPARE( exp.asPdalExpression(), converted );
}

QGSTEST_MAIN( TestQgsPointCloudExpression )
#include "testqgspointcloudexpression.moc"

0 comments on commit 45791b1

Please sign in to comment.