Skip to content
Permalink
Browse files

[FEATURE] Add option to symbology to prevent clipping of features

This option (located under the symbol advanced menu) disables the
automatic clipping of lines/polygons to the canvas extent. In
some cases this clipping results in unfavourable symbology (eg
centroid fills where the centroid must always be the actual
feature's centroid). (fix #9757)
  • Loading branch information
nyalldawson committed Mar 24, 2015
1 parent 742f323 commit 8b37ea2b05a644c1c52ecf223ad8f67ca315b8df
@@ -229,8 +229,8 @@ class QgsFeatureRendererV2
void renderVertexMarkerPolygon( QPolygonF& pts, QList<QPolygonF>* rings, QgsRenderContext& context );

static const unsigned char* _getPoint( QPointF& pt, QgsRenderContext& context, const unsigned char* wkb );
static const unsigned char* _getLineString( QPolygonF& pts, QgsRenderContext& context, const unsigned char* wkb );
static const unsigned char* _getPolygon( QPolygonF& pts, QList<QPolygonF>& holes, QgsRenderContext& context, const unsigned char* wkb );
static const unsigned char* _getLineString( QPolygonF& pts, QgsRenderContext& context, const unsigned char* wkb, bool clipToExtent = true );
static const unsigned char* _getPolygon( QPolygonF& pts, QList<QPolygonF>& holes, QgsRenderContext& context, const unsigned char* wkb, bool clipToExtent = true );

void setScaleMethodToSymbol( QgsSymbolV2* symbol, int scaleMethod );

@@ -130,6 +130,26 @@ class QgsSymbolV2
void setRenderHints( int hints );
int renderHints() const;

/**Sets whether features drawn by the symbol should be clipped to the render context's
* extent. If this option is enabled then features which are partially outside the extent
* will be clipped. This speeds up rendering of the feature, but may have undesirable
* side effects for certain symbol types.
* @param clipFeaturesToExtent set to true to enable clipping (defaults to true)
* @note added in QGIS 2.9
* @see clipFeaturesToExtent
*/
void setClipFeaturesToExtent( bool clipFeaturesToExtent );

/**Returns whether features drawn by the symbol will be clipped to the render context's
* extent. If this option is enabled then features which are partially outside the extent
* will be clipped. This speeds up rendering of the feature, but may have undesirable
* side effects for certain symbol types.
* @returns true if features will be clipped
* @note added in QGIS 2.9
* @see setClipFeaturesToExtent
*/
double clipFeaturesToExtent() const;

QSet<QString> usedAttributes() const;

void setLayer( const QgsVectorLayer* layer );
@@ -58,20 +58,21 @@ const unsigned char* QgsFeatureRendererV2::_getPoint( QPointF& pt, QgsRenderCont
return wkbPtr;
}

const unsigned char* QgsFeatureRendererV2::_getLineString( QPolygonF& pts, QgsRenderContext& context, const unsigned char* wkb )
const unsigned char* QgsFeatureRendererV2::_getLineString( QPolygonF& pts, QgsRenderContext& context, const unsigned char* wkb, bool clipToExtent )
{
QgsConstWkbPtr wkbPtr( wkb );
QgsConstWkbPtr wkbPtr( wkb + 1 );
unsigned int wkbType, nPoints;
wkbPtr >> wkbType >> nPoints;

bool hasZValue = wkbType == QGis::WKBLineString25D;

double x, y;
double x = 0.0;
double y = 0.0;
const QgsCoordinateTransform* ct = context.coordinateTransform();
const QgsMapToPixel& mtp = context.mapToPixel();

//apply clipping for large lines to achieve a better rendering performance
if ( nPoints > 1 )
if ( clipToExtent && nPoints > 1 )
{
const QgsRectangle& e = context.extent();
double cw = e.width() / 10; double ch = e.height() / 10;
@@ -108,7 +109,7 @@ const unsigned char* QgsFeatureRendererV2::_getLineString( QPolygonF& pts, QgsRe
return wkbPtr;
}

const unsigned char* QgsFeatureRendererV2::_getPolygon( QPolygonF& pts, QList<QPolygonF>& holes, QgsRenderContext& context, const unsigned char* wkb )
const unsigned char* QgsFeatureRendererV2::_getPolygon( QPolygonF& pts, QList<QPolygonF>& holes, QgsRenderContext& context, const unsigned char* wkb, bool clipToExtent )
{
QgsConstWkbPtr wkbPtr( wkb + 1 );

@@ -152,7 +153,7 @@ const unsigned char* QgsFeatureRendererV2::_getPolygon( QPolygonF& pts, QList<QP

//clip close to view extent, if needed
QRectF ptsRect = poly.boundingRect();
if ( !context.extent().contains( ptsRect ) ) QgsClipper::trimPolygon( poly, clipRect );
if ( clipToExtent && !context.extent().contains( ptsRect ) ) QgsClipper::trimPolygon( poly, clipRect );

//transform the QPolygonF to screen coordinates
if ( ct )
@@ -271,7 +272,7 @@ void QgsFeatureRendererV2::renderFeatureWithSymbol( QgsFeature& feature, QgsSymb
break;
}
QPolygonF pts;
_getLineString( pts, context, geom->asWkb() );
_getLineString( pts, context, geom->asWkb(), symbol->clipFeaturesToExtent() );
(( QgsLineSymbolV2* )symbol )->renderPolyline( pts, &feature, context, layer, selected );

if ( drawVertexMarker )
@@ -289,7 +290,7 @@ void QgsFeatureRendererV2::renderFeatureWithSymbol( QgsFeature& feature, QgsSymb
}
QPolygonF pts;
QList<QPolygonF> holes;
_getPolygon( pts, holes, context, geom->asWkb() );
_getPolygon( pts, holes, context, geom->asWkb(), symbol->clipFeaturesToExtent() );
(( QgsFillSymbolV2* )symbol )->renderPolygon( pts, ( holes.count() ? &holes : NULL ), &feature, context, layer, selected );

if ( drawVertexMarker )
@@ -306,7 +307,7 @@ void QgsFeatureRendererV2::renderFeatureWithSymbol( QgsFeature& feature, QgsSymb
break;
}

QgsConstWkbPtr wkbPtr( geom->asWkb() + 5 );
QgsConstWkbPtr wkbPtr( geom->asWkb() + 1 + sizeof( int ) );
unsigned int num;
wkbPtr >> num;
const unsigned char* ptr = wkbPtr;
@@ -332,15 +333,15 @@ void QgsFeatureRendererV2::renderFeatureWithSymbol( QgsFeature& feature, QgsSymb
break;
}

QgsConstWkbPtr wkbPtr( geom->asWkb() + 5 );
QgsConstWkbPtr wkbPtr( geom->asWkb() + 1 + sizeof( int ) );
unsigned int num;
wkbPtr >> num;
const unsigned char* ptr = wkbPtr;
QPolygonF pts;

for ( unsigned int i = 0; i < num; ++i )
{
ptr = QgsConstWkbPtr( _getLineString( pts, context, ptr ) );
ptr = QgsConstWkbPtr( _getLineString( pts, context, ptr, symbol->clipFeaturesToExtent() ) );
(( QgsLineSymbolV2* )symbol )->renderPolyline( pts, &feature, context, layer, selected );

if ( drawVertexMarker )
@@ -358,7 +359,7 @@ void QgsFeatureRendererV2::renderFeatureWithSymbol( QgsFeature& feature, QgsSymb
break;
}

QgsConstWkbPtr wkbPtr( geom->asWkb() + 5 );
QgsConstWkbPtr wkbPtr( geom->asWkb() + 1 + sizeof( int ) );
unsigned int num;
wkbPtr >> num;
const unsigned char* ptr = wkbPtr;
@@ -367,7 +368,7 @@ void QgsFeatureRendererV2::renderFeatureWithSymbol( QgsFeature& feature, QgsSymb

for ( unsigned int i = 0; i < num; ++i )
{
ptr = _getPolygon( pts, holes, context, ptr );
ptr = _getPolygon( pts, holes, context, ptr, symbol->clipFeaturesToExtent() );
(( QgsFillSymbolV2* )symbol )->renderPolygon( pts, ( holes.count() ? &holes : NULL ), &feature, context, layer, selected );

if ( drawVertexMarker )
@@ -251,8 +251,8 @@ class CORE_EXPORT QgsFeatureRendererV2
void renderVertexMarkerPolygon( QPolygonF& pts, QList<QPolygonF>* rings, QgsRenderContext& context );

static const unsigned char* _getPoint( QPointF& pt, QgsRenderContext& context, const unsigned char* wkb );
static const unsigned char* _getLineString( QPolygonF& pts, QgsRenderContext& context, const unsigned char* wkb );
static const unsigned char* _getPolygon( QPolygonF& pts, QList<QPolygonF>& holes, QgsRenderContext& context, const unsigned char* wkb );
static const unsigned char* _getLineString( QPolygonF& pts, QgsRenderContext& context, const unsigned char* wkb, bool clipToExtent = true );
static const unsigned char* _getPolygon( QPolygonF& pts, QList<QPolygonF>& holes, QgsRenderContext& context, const unsigned char* wkb, bool clipToExtent = true );

void setScaleMethodToSymbol( QgsSymbolV2* symbol, int scaleMethod );

@@ -940,6 +940,7 @@ QgsSymbolV2* QgsSymbolLayerV2Utils::loadSymbol( const QDomElement &element )
symbol->setMapUnitScale( mapUnitScale );
}
symbol->setAlpha( element.attribute( "alpha", "1.0" ).toDouble() );
symbol->setClipFeaturesToExtent( element.attribute( "clip_to_extent", "1" ).toInt() );

return symbol;
}
@@ -993,6 +994,7 @@ QDomElement QgsSymbolLayerV2Utils::saveSymbol( QString name, QgsSymbolV2* symbol
symEl.setAttribute( "type", _nameForSymbolType( symbol->type() ) );
symEl.setAttribute( "name", name );
symEl.setAttribute( "alpha", QString::number( symbol->alpha() ) );
symEl.setAttribute( "clip_to_extent", symbol->clipFeaturesToExtent() ? "1" : "0" );
QgsDebugMsg( "num layers " + QString::number( symbol->symbolLayerCount() ) );

for ( int i = 0; i < symbol->symbolLayerCount(); i++ )
@@ -40,6 +40,7 @@ QgsSymbolV2::QgsSymbolV2( SymbolType type, QgsSymbolLayerV2List layers )
, mLayers( layers )
, mAlpha( 1.0 )
, mRenderHints( 0 )
, mClipFeaturesToExtent( true )
, mLayer( 0 )
{

@@ -631,6 +632,7 @@ QgsSymbolV2* QgsMarkerSymbolV2::clone() const
QgsSymbolV2* cloneSymbol = new QgsMarkerSymbolV2( cloneLayers() );
cloneSymbol->setAlpha( mAlpha );
cloneSymbol->setLayer( mLayer );
cloneSymbol->setClipFeaturesToExtent( mClipFeaturesToExtent );
return cloneSymbol;
}

@@ -728,6 +730,7 @@ QgsSymbolV2* QgsLineSymbolV2::clone() const
QgsSymbolV2* cloneSymbol = new QgsLineSymbolV2( cloneLayers() );
cloneSymbol->setAlpha( mAlpha );
cloneSymbol->setLayer( mLayer );
cloneSymbol->setClipFeaturesToExtent( mClipFeaturesToExtent );
return cloneSymbol;
}

@@ -834,6 +837,7 @@ QgsSymbolV2* QgsFillSymbolV2::clone() const
QgsSymbolV2* cloneSymbol = new QgsFillSymbolV2( cloneLayers() );
cloneSymbol->setAlpha( mAlpha );
cloneSymbol->setLayer( mLayer );
cloneSymbol->setClipFeaturesToExtent( mClipFeaturesToExtent );
return cloneSymbol;
}

@@ -161,6 +161,26 @@ class CORE_EXPORT QgsSymbolV2
void setRenderHints( int hints ) { mRenderHints = hints; }
int renderHints() const { return mRenderHints; }

/**Sets whether features drawn by the symbol should be clipped to the render context's
* extent. If this option is enabled then features which are partially outside the extent
* will be clipped. This speeds up rendering of the feature, but may have undesirable
* side effects for certain symbol types.
* @param clipFeaturesToExtent set to true to enable clipping (defaults to true)
* @note added in QGIS 2.9
* @see clipFeaturesToExtent
*/
void setClipFeaturesToExtent( bool clipFeaturesToExtent ) { mClipFeaturesToExtent = clipFeaturesToExtent; }

/**Returns whether features drawn by the symbol will be clipped to the render context's
* extent. If this option is enabled then features which are partially outside the extent
* will be clipped. This speeds up rendering of the feature, but may have undesirable
* side effects for certain symbol types.
* @returns true if features will be clipped
* @note added in QGIS 2.9
* @see setClipFeaturesToExtent
*/
double clipFeaturesToExtent() const { return mClipFeaturesToExtent; }

QSet<QString> usedAttributes() const;

void setLayer( const QgsVectorLayer* layer ) { mLayer = layer; }
@@ -182,6 +202,7 @@ class CORE_EXPORT QgsSymbolV2
qreal mAlpha;

int mRenderHints;
bool mClipFeaturesToExtent;

const QgsVectorLayer* mLayer; //current vectorlayer

@@ -35,7 +35,10 @@
#include <QMenu>


QgsSymbolsListWidget::QgsSymbolsListWidget( QgsSymbolV2* symbol, QgsStyleV2* style, QMenu* menu, QWidget* parent ) : QWidget( parent )
QgsSymbolsListWidget::QgsSymbolsListWidget( QgsSymbolV2* symbol, QgsStyleV2* style, QMenu* menu, QWidget* parent )
: QWidget( parent )
, mAdvancedMenu( 0 )
, mClipFeaturesAction( 0 )
{
mSymbol = symbol;
mStyle = style;
@@ -47,9 +50,17 @@ QgsSymbolsListWidget::QgsSymbolsListWidget( QgsSymbolV2* symbol, QgsStyleV2* sty
btnAdvanced->hide(); // advanced button is hidden by default
if ( menu ) // show it if there is a menu pointer
{
btnAdvanced->setMenu( menu );
mAdvancedMenu = menu;
btnAdvanced->show();
btnAdvanced->setMenu( mAdvancedMenu );
}
else
{
btnAdvanced->setMenu( new QMenu( this ) );
}
mClipFeaturesAction = new QAction( tr( "Clip features to canvas extent" ), this );
mClipFeaturesAction->setCheckable( true );
connect( mClipFeaturesAction, SIGNAL( toggled( bool ) ), this, SLOT( clipFeaturesToggled( bool ) ) );

// populate the groups
groupsCombo->addItem( "" );
@@ -163,6 +174,15 @@ void QgsSymbolsListWidget::openStyleManager()
populateSymbolView();
}

void QgsSymbolsListWidget::clipFeaturesToggled( bool checked )
{
if ( !mSymbol )
return;

mSymbol->setClipFeaturesToExtent( checked );
emit changed();
}

void QgsSymbolsListWidget::setSymbolColor( const QColor& color )
{
mSymbol->setColor( color );
@@ -294,6 +314,21 @@ void QgsSymbolsListWidget::updateSymbolInfo()
mTransparencySlider->setValue( transparency * 255 );
displayTransparency( mSymbol->alpha() );
mTransparencySlider->blockSignals( false );

if ( mSymbol->type() == QgsSymbolV2::Line || mSymbol->type() == QgsSymbolV2::Fill )
{
//add clip features option for line or fill symbols
btnAdvanced->menu()->addAction( mClipFeaturesAction );
}
else
{
btnAdvanced->menu()->removeAction( mClipFeaturesAction );
}
btnAdvanced->setVisible( mAdvancedMenu || !btnAdvanced->menu()->isEmpty() );

mClipFeaturesAction->blockSignals( true );
mClipFeaturesAction->setChecked( mSymbol->clipFeaturesToExtent() );
mClipFeaturesAction->blockSignals( false );
}

void QgsSymbolsListWidget::setSymbolFromStyle( const QModelIndex & index )
@@ -47,13 +47,16 @@ class GUI_EXPORT QgsSymbolsListWidget : public QWidget, private Ui::SymbolsListW
void on_groupsCombo_editTextChanged( const QString &text );

void openStyleManager();
void clipFeaturesToggled( bool checked );

signals:
void changed();

protected:
QgsSymbolV2* mSymbol;
QgsStyleV2* mStyle;
QMenu* mAdvancedMenu;
QAction* mClipFeaturesAction;

void populateSymbolView();
void populateSymbols( QStringList symbols );

0 comments on commit 8b37ea2

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