[FEATURE] Add expression functions for converting to/from wkb

Adds geom_from_wkb and geom_to_wkb, which mirror the existing
geom_from_wkt/geom_to_wkt functions but for WKB representations
of geometries.

Since QGIS 3.6 we've had good support for binary blob values in
expressions and field values, so adding these functions allows
users to work with binary blob fields containing WKB representations
of geometries (e.g. with a geometry generator showing the encoded
geometries)
This commit is contained in:
Nyall Dawson 2019-11-01 09:14:27 +10:00
parent 13ead42f5e
commit fed75afa95
5 changed files with 58 additions and 0 deletions

View File

@ -0,0 +1,7 @@
{
"name": "geom_from_wkb",
"type": "function",
"description": "Returns a geometry created from a Well-Known Binary (WKB) representation.",
"arguments": [ {"arg":"binary","description":"Well-Known Binary (WKB) representation of a geometry (as a binary blob)"}],
"examples": [ { "expression":"geom_from_wkb( geom_to_wkb( make_point(4,5) ) )", "returns":"a point geometry object"}]
}

View File

@ -0,0 +1,8 @@
{
"name": "geom_to_wkb",
"type": "function",
"description": "Returns the Well-Known Binary (WKB) representation of a geometry as a binary blob.",
"arguments": [ {"arg":"geometry","description":"a geometry"}],
"examples": [ { "expression":"geom_to_wkb( $geometry )", "returns":"binary blob containing a geometry object"}
]
}

View File

@ -2812,6 +2812,7 @@ static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *c
else
return QVariant( QVariant::UserType );
}
static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
@ -2819,6 +2820,18 @@ static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionC
QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
return result;
}
static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
if ( wkb.isNull() )
return QVariant();
QgsGeometry geom;
geom.fromWkb( wkb );
return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
}
static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
@ -3448,6 +3461,7 @@ static QVariant fcnCombine( const QVariantList &values, const QgsExpressionConte
QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
return result;
}
static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
if ( values.length() < 1 || values.length() > 2 )
@ -3461,6 +3475,12 @@ static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionCon
return QVariant( wkt );
}
static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
}
static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
if ( values.length() != 2 )
@ -5486,6 +5506,7 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< new QgsStaticExpressionFunction( QStringLiteral( "y_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnYMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymin" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "y_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnYMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymax" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ), fcnGeomFromWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromWKT" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "binary" ) ), fcnGeomFromWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
<< new QgsStaticExpressionFunction( QStringLiteral( "geom_from_gml" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "gml" ) ), fcnGeomFromGML, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromGML" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "flip_coordinates" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) ), fcnFlipCoordinates, QStringLiteral( "GeometryGroup" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "relate" ), -1, fcnRelate, QStringLiteral( "GeometryGroup" ) )
@ -5605,6 +5626,7 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
fcnCombine, QStringLiteral( "GeometryGroup" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkt" ), -1, fcnGeomToWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomToWKT" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomToWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
<< new QgsStaticExpressionFunction( QStringLiteral( "geometry" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetGeometry, QStringLiteral( "GeometryGroup" ), QString(), true )
<< new QgsStaticExpressionFunction( QStringLiteral( "transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geom" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "source_auth_id" ) )

View File

@ -188,6 +188,23 @@ class QgsExpressionUtils
return value.toString();
}
/**
* Returns an expression value converted to binary (byte array) value.
*
* An empty byte array will be returned if the value is NULL.
*
* \since QGIS 3.12
*/
static QByteArray getBinaryValue( const QVariant &value, QgsExpression *parent )
{
if ( value.type() != QVariant::ByteArray )
{
parent->setEvalErrorString( QObject::tr( "Value is not a binary value" ) );
return QByteArray();
}
return value.toByteArray();
}
static double getDoubleValue( const QVariant &value, QgsExpression *parent )
{
bool ok;

View File

@ -788,6 +788,10 @@ class TestQgsExpression: public QObject
QTest::newRow( "Y coordinate to degree minute second" ) << "to_dms(6.3545681,'y',2)" << false << QVariant( "6°2116.45″" );
// geometry functions
QTest::newRow( "geom_to_wkb" ) << "geom_to_wkt(geom_from_wkb(geom_to_wkb(make_point(4,5))))" << false << QVariant( "Point (4 5)" );
QTest::newRow( "geom_to_wkb not geom" ) << "geom_to_wkt(geom_from_wkb(geom_to_wkb('a')))" << true << QVariant();
QTest::newRow( "geom_from_wkb not geom" ) << "geom_to_wkt(geom_from_wkb(make_point(4,5)))" << true << QVariant();
QTest::newRow( "geom_from_wkb null" ) << "geom_to_wkt(geom_from_wkb(NULL))" << false << QVariant();
QTest::newRow( "num_points" ) << "num_points(geom_from_wkt('GEOMETRYCOLLECTION(LINESTRING(0 0, 1 0),POINT(6 5))'))" << false << QVariant( 3 );
QTest::newRow( "num_interior_rings not geom" ) << "num_interior_rings('g')" << true << QVariant();
QTest::newRow( "num_interior_rings null" ) << "num_interior_rings(NULL)" << false << QVariant();