Skip to content

Commit

Permalink
[FEATURE] Add option to symbology to prevent clipping of features
Browse files Browse the repository at this point in the history
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 28, 2015
1 parent 742f323 commit 8b37ea2
Show file tree
Hide file tree
Showing 18 changed files with 230 additions and 19 deletions.
4 changes: 2 additions & 2 deletions python/core/symbology-ng/qgsrendererv2.sip
Expand Up @@ -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 );

Expand Down
20 changes: 20 additions & 0 deletions python/core/symbology-ng/qgssymbolv2.sip
Expand Up @@ -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 );
Expand Down
27 changes: 14 additions & 13 deletions src/core/symbology-ng/qgsrendererv2.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -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 );

Expand Down Expand Up @@ -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 )
Expand Down Expand Up @@ -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 )
Expand All @@ -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 )
Expand All @@ -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;
Expand All @@ -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 )
Expand All @@ -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;
Expand All @@ -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 )
Expand Down
4 changes: 2 additions & 2 deletions src/core/symbology-ng/qgsrendererv2.h
Expand Up @@ -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 );

Expand Down
2 changes: 2 additions & 0 deletions src/core/symbology-ng/qgssymbollayerv2utils.cpp
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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++ )
Expand Down
4 changes: 4 additions & 0 deletions src/core/symbology-ng/qgssymbolv2.cpp
Expand Up @@ -40,6 +40,7 @@ QgsSymbolV2::QgsSymbolV2( SymbolType type, QgsSymbolLayerV2List layers )
, mLayers( layers )
, mAlpha( 1.0 )
, mRenderHints( 0 )
, mClipFeaturesToExtent( true )
, mLayer( 0 )
{

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

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

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

Expand Down
21 changes: 21 additions & 0 deletions src/core/symbology-ng/qgssymbolv2.h
Expand Up @@ -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; }
Expand All @@ -182,6 +202,7 @@ class CORE_EXPORT QgsSymbolV2
qreal mAlpha;

int mRenderHints;
bool mClipFeaturesToExtent;

const QgsVectorLayer* mLayer; //current vectorlayer

Expand Down
39 changes: 37 additions & 2 deletions src/gui/symbology-ng/qgssymbolslistwidget.cpp
Expand Up @@ -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;
Expand All @@ -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( "" );
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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 )
Expand Down
3 changes: 3 additions & 0 deletions src/gui/symbology-ng/qgssymbolslistwidget.h
Expand Up @@ -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 );
Expand Down

0 comments on commit 8b37ea2

Please sign in to comment.