Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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 92aefdc
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 4 deletions.
43 changes: 39 additions & 4 deletions src/core/symbology/qgsgeometrygeneratorsymbollayer.cpp
Expand Up @@ -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 );
Expand Down Expand Up @@ -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 )
Expand Down Expand Up @@ -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() )
{
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/core/symbology/qgsgeometrygeneratorsymbollayer.h
Expand Up @@ -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;
Expand Down

0 comments on commit 92aefdc

Please sign in to comment.