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 authored and github-actions committed Oct 28, 2021
1 parent 1011cc3 commit eedfa0c19c7fb3a2e52c4678af56a38a69f6c548
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 eedfa0c

Please sign in to comment.