Skip to content
Permalink
Browse files
When a geometry generator expression result is an incorrect geometry
type for the generator symbol, try to coerce the result geometry
to the correct type

E.g. if the generator is set to a line symbol type but the expression
gives a polygon result, then take the rings of the polygon as the
geometry and render using the line symbol

At worst this is better then rendering absolutely nothing (which can
be very confusing!!), and at best is desirable behaviour anyway.
  • Loading branch information
nyalldawson committed Oct 28, 2021
1 parent 5195032 commit 92aefdc817d46f73434d69c4a3adaf66f84856aa
Showing with 45 additions and 4 deletions.
  1. +39 −4 src/core/symbology/qgsgeometrygeneratorsymbollayer.cpp
  2. +6 −0 src/core/symbology/qgsgeometrygeneratorsymbollayer.h
@@ -243,7 +243,7 @@ void QgsGeometryGeneratorSymbolLayer::drawPreviewIcon( QgsSymbolRenderContext &c
feature.setGeometry( patchShapeGeometry );
const QgsGeometry iconGeometry = evaluateGeometryInPainterUnits( patchShapeGeometry, feature, context.renderContext(), context.renderContext().expressionContext() );

QgsLegendPatchShape evaluatedPatchShape( mSymbol->type(), iconGeometry );
QgsLegendPatchShape evaluatedPatchShape( mSymbol->type(), coerceToExpectedType( iconGeometry ) );
// we don't want to rescale the patch shape to fit the legend symbol size -- we've already considered that here,
// and we don't want to undo the effects of a geometry generator which modifies the symbol bounds
evaluatedPatchShape.setScaleToOutputSize( false );
@@ -322,9 +322,44 @@ QgsGeometry QgsGeometryGeneratorSymbolLayer::evaluateGeometryInPainterUnits( con

// step 4 - transform geometry back from target units to painter units
geom.transform( painterToTargetUnits.inverted( ) );

return geom;
}

QgsGeometry QgsGeometryGeneratorSymbolLayer::coerceToExpectedType( const QgsGeometry &geometry ) const
{
switch ( mSymbolType )
{
case Qgis::SymbolType::Marker:
if ( geometry.type() != QgsWkbTypes::PointGeometry )
{
QVector< QgsGeometry > geoms = geometry.coerceToType( QgsWkbTypes::MultiPoint );
if ( !geoms.empty() )
return geoms.at( 0 );
}
break;
case Qgis::SymbolType::Line:
if ( geometry.type() != QgsWkbTypes::LineGeometry )
{
QVector< QgsGeometry > geoms = geometry.coerceToType( QgsWkbTypes::MultiLineString );
if ( !geoms.empty() )
return geoms.at( 0 );
}
break;
case Qgis::SymbolType::Fill:
if ( geometry.type() != QgsWkbTypes::PolygonGeometry )
{
QVector< QgsGeometry > geoms = geometry.coerceToType( QgsWkbTypes::MultiPolygon );
if ( !geoms.empty() )
return geoms.at( 0 );
}
break;
case Qgis::SymbolType::Hybrid:
break;
}
return geometry;
}

void QgsGeometryGeneratorSymbolLayer::render( QgsSymbolRenderContext &context, QgsWkbTypes::GeometryType geometryType, const QPolygonF *points, const QVector<QPolygonF> *rings )
{
if ( mRenderingFeature && mHasRenderedFeature )
@@ -396,7 +431,7 @@ void QgsGeometryGeneratorSymbolLayer::render( QgsSymbolRenderContext &context, Q
QgsDebugMsg( QStringLiteral( "Could no transform generated geometry to layer CRS" ) );
}

f.setGeometry( result );
f.setGeometry( coerceToExpectedType( result ) );
}
else if ( context.feature() )
{
@@ -408,7 +443,7 @@ void QgsGeometryGeneratorSymbolLayer::render( QgsSymbolRenderContext &context, Q
case QgsUnitTypes::RenderPercentage: // unsupported, not exposed as an option
{
QgsGeometry geom = mExpression->evaluate( &expressionContext ).value<QgsGeometry>();
f.setGeometry( geom );
f.setGeometry( coerceToExpectedType( geom ) );
break;
}

@@ -438,7 +473,7 @@ void QgsGeometryGeneratorSymbolLayer::render( QgsSymbolRenderContext &context, Q
{
QgsDebugMsg( QStringLiteral( "Could no transform generated geometry to layer CRS" ) );
}
f.setGeometry( result );
f.setGeometry( coerceToExpectedType( result ) );
break;
}
}
@@ -143,6 +143,12 @@ class CORE_EXPORT QgsGeometryGeneratorSymbolLayer : public QgsSymbolLayer
*/
QgsGeometry evaluateGeometryInPainterUnits( const QgsGeometry &input, const QgsFeature &feature, const QgsRenderContext &renderContext, QgsExpressionContext &expressionContext ) const;

/**
* Tries to coerce the geometry output by the generator expression into
* a type usable by the symbol.
*/
QgsGeometry coerceToExpectedType( const QgsGeometry &geometry ) const;

std::unique_ptr<QgsExpression> mExpression;
std::unique_ptr<QgsFillSymbol> mFillSymbol;
std::unique_ptr<QgsLineSymbol> mLineSymbol;

0 comments on commit 92aefdc

Please sign in to comment.