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 29, 2021
1 parent 1011cc3 commit 0f17bdd962ebc1b7a2aae940c5a2d842b4e94568
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 0f17bdd

Please sign in to comment.