Skip to content
Permalink
Browse files

In cases where we cannot convert the map extent back to a layer's

extent and have had to resort to fetching all features from a layer,
defer the geometry clipping the map extent so that it occurs
AFTER transforming the layer's geometries to the target map extent.

This allows us to correctly clip the feature geometries in the case
that the visible extent available from the render context for the layer
is not accurate (i.e. it's a whole of globe fallback), and avoids
rendering features which fall far outside of the visible map
region.

Fixes #38878
  • Loading branch information
nyalldawson committed Sep 21, 2020
1 parent 3ec1154 commit f3f226aa69a19b31dd8a1b83c82319528e061cfe
@@ -47,6 +47,7 @@ to be rendered etc.
LosslessImageRendering,
ApplyScalingWorkaroundForTextRendering,
Render3DMap,
ApplyClipAfterReprojection,
};
typedef QFlags<QgsRenderContext::Flag> Flags;

@@ -344,9 +344,10 @@ LayerRenderJobs QgsMapRendererJob::prepareJobs( QPainter *painter, QgsLabelingEn
QgsCoordinateTransform ct;

ct = mSettings.layerTransform( ml );
bool haveExtentInLayerCrs = true;
if ( ct.isValid() )
{
reprojectToLayerExtent( ml, ct, r1, r2 );
haveExtentInLayerCrs = reprojectToLayerExtent( ml, ct, r1, r2 );
}
QgsDebugMsgLevel( "extent: " + r1.toString(), 3 );
if ( !r1.isFinite() || !r2.isFinite() )
@@ -382,6 +383,8 @@ LayerRenderJobs QgsMapRendererJob::prepareJobs( QPainter *painter, QgsLabelingEn
job.context.setLabelingEngine( labelingEngine2 );
job.context.setCoordinateTransform( ct );
job.context.setExtent( r1 );
if ( !haveExtentInLayerCrs )
job.context.setFlag( QgsRenderContext::ApplyClipAfterReprojection, true );

if ( mFeatureFilterProvider )
job.context.setFeatureFilterProvider( mFeatureFilterProvider );
@@ -84,6 +84,7 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject
LosslessImageRendering = 0x1000, //!< Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some destination devices (e.g. PDF). This flag only works with builds based on Qt 5.13 or later.
ApplyScalingWorkaroundForTextRendering = 0x2000, //!< Whether a scaling workaround designed to stablise the rendering of small font sizes (or for painters scaled out by a large amount) when rendering text. Generally this is recommended, but it may incur some performance cost.
Render3DMap = 0x4000, //!< Render is for a 3D map
ApplyClipAfterReprojection = 0x8000, //!< Feature geometry clipping to mapExtent() must be performed after the geometries are transformed using coordinateTransform(). Usually feature geometry clipping occurs using the extent() in the layer's CRS prior to geometry transformation, but in some cases when extent() could not be accurately calculated it is necessary to clip geometries to mapExtent() AFTER transforming them using coordinateTransform().
};
Q_DECLARE_FLAGS( Flags, Flag )

@@ -110,9 +110,9 @@ QPolygonF QgsSymbol::_getLineString( QgsRenderContext &context, const QgsCurve &
QPolygonF pts;

//apply clipping for large lines to achieve a better rendering performance
if ( clipToExtent && nPoints > 1 )
if ( clipToExtent && nPoints > 1 && !( context.flags() & QgsRenderContext::ApplyClipAfterReprojection ) )
{
const QgsRectangle &e = context.extent();
const QgsRectangle e = context.extent();
const double cw = e.width() / 10;
const double ch = e.height() / 10;
const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
@@ -143,6 +143,16 @@ QPolygonF QgsSymbol::_getLineString( QgsRenderContext &context, const QgsCurve &
return !std::isfinite( point.x() ) || !std::isfinite( point.y() );
} ), pts.end() );

if ( clipToExtent && nPoints > 1 && context.flags() & QgsRenderContext::ApplyClipAfterReprojection )
{
// early clipping was not possible, so we have to apply it here after transformation
const QgsRectangle e = context.mapExtent();
const double cw = e.width() / 10;
const double ch = e.height() / 10;
const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
pts = QgsClipper::clippedLine( pts, clipRect );
}

QPointF *ptr = pts.data();
for ( int i = 0; i < pts.size(); ++i, ++ptr )
{
@@ -156,10 +166,6 @@ QPolygonF QgsSymbol::_getPolygonRing( QgsRenderContext &context, const QgsCurve
{
const QgsCoordinateTransform ct = context.coordinateTransform();
const QgsMapToPixel &mtp = context.mapToPixel();
const QgsRectangle &e = context.extent();
const double cw = e.width() / 10;
const double ch = e.height() / 10;
QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );

QPolygonF poly = curve.asQPolygonF();

@@ -176,9 +182,12 @@ QPolygonF QgsSymbol::_getPolygonRing( QgsRenderContext &context, const QgsCurve
}

//clip close to view extent, if needed
const QRectF ptsRect = poly.boundingRect();
if ( clipToExtent && !context.extent().contains( ptsRect ) )
if ( clipToExtent && !( context.flags() & QgsRenderContext::ApplyClipAfterReprojection ) && !context.extent().contains( poly.boundingRect() ) )
{
const QgsRectangle e = context.extent();
const double cw = e.width() / 10;
const double ch = e.height() / 10;
const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
QgsClipper::trimPolygon( poly, clipRect );
}

@@ -202,6 +211,16 @@ QPolygonF QgsSymbol::_getPolygonRing( QgsRenderContext &context, const QgsCurve
return !std::isfinite( point.x() ) || !std::isfinite( point.y() );
} ), poly.end() );

if ( clipToExtent && context.flags() & QgsRenderContext::ApplyClipAfterReprojection && !context.mapExtent().contains( poly.boundingRect() ) )
{
// early clipping was not possible, so we have to apply it here after transformation
const QgsRectangle e = context.mapExtent();
const double cw = e.width() / 10;
const double ch = e.height() / 10;
const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
QgsClipper::trimPolygon( poly, clipRect );
}

QPointF *ptr = poly.data();
for ( int i = 0; i < poly.size(); ++i, ++ptr )
{

0 comments on commit f3f226a

Please sign in to comment.
You can’t perform that action at this time.