Skip to content

Commit 942d5da

Browse files
committed
Add option to ignore interior polygon rings when calculating buffered shading for shapeburst fill mode (can be useful for eg ignoring islands when shading water bodies)
1 parent a0c2c7a commit 942d5da

File tree

8 files changed

+101
-12
lines changed

8 files changed

+101
-12
lines changed

python/core/symbology-ng/qgsfillsymbollayerv2.sip

+14
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,20 @@ class QgsShapeburstFillSymbolLayerV2 : QgsFillSymbolLayerV2
351351
* @see colorType
352352
*/
353353
QColor color2() const;
354+
355+
/**Sets whether the shapeburst fill should ignore polygon rings when calculating
356+
* the buffered shading.
357+
* @param ignoreRings Set to true if buffers should ignore interior rings for polygons.
358+
* @note added in 2.3
359+
* @see ignoreRings
360+
*/
361+
void setIgnoreRings( double ignoreRings );
362+
/**Returns whether the shapeburst fill is set to ignore polygon interior rings.
363+
* @returns True if the shapeburst fill will ignore interior rings when calculating buffered shading.
364+
* @note added in 2.3
365+
* @see setIgnoreRings
366+
*/
367+
double ignoreRings() const;
354368

355369
/**Sets the offset for the shapeburst fill.
356370
* @param offset QPointF indicating the horizontal/vertical offset amount

src/core/symbology-ng/qgsfillsymbollayerv2.cpp

+33-3
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,7 @@ QgsShapeburstFillSymbolLayerV2::QgsShapeburstFillSymbolLayerV2( QColor color, QC
795795
mColor2( color2 ),
796796
mGradientRamp( NULL ),
797797
mTwoColorGradientRamp( 0 ),
798+
mIgnoreRings( false ),
798799
mOffsetUnit( QgsSymbolV2::MM )
799800
{
800801
mColor = color;
@@ -859,6 +860,10 @@ QgsSymbolLayerV2* QgsShapeburstFillSymbolLayerV2::create( const QgsStringMap& pr
859860
{
860861
sl->setDistanceUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["distance_unit"] ) );
861862
}
863+
if ( props.contains( "ignore_rings" ) )
864+
{
865+
sl->setIgnoreRings( props["ignore_rings"].toInt() );
866+
}
862867
if ( gradientRamp )
863868
{
864869
sl->setColorRamp( gradientRamp );
@@ -874,6 +879,8 @@ QgsSymbolLayerV2* QgsShapeburstFillSymbolLayerV2::create( const QgsStringMap& pr
874879
sl->setDataDefinedProperty( "use_whole_shape", props["use_whole_shape_expression"] );
875880
if ( props.contains( "max_distance_expression" ) )
876881
sl->setDataDefinedProperty( "max_distance", props["max_distance_expression"] );
882+
if ( props.contains( "ignore_rings_expression" ) )
883+
sl->setDataDefinedProperty( "ignore_rings", props["ignore_rings_expression"] );
877884

878885
return sl;
879886
}
@@ -890,7 +897,7 @@ void QgsShapeburstFillSymbolLayerV2::setColorRamp( QgsVectorColorRampV2* ramp )
890897
}
891898

892899
void QgsShapeburstFillSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QColor& color, QColor& color2, int& blurRadius, bool& useWholeShape,
893-
double& maxDistance )
900+
double& maxDistance, bool& ignoreRings )
894901
{
895902
//first gradient color
896903
QgsExpression* colorExpression = expression( "color" );
@@ -922,6 +929,12 @@ void QgsShapeburstFillSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2Rende
922929
if ( maxDistanceExpression )
923930
maxDistance = maxDistanceExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
924931

932+
//ignore rings
933+
QgsExpression* ignoreRingsExpression = expression( "ignore_rings" );
934+
ignoreRings = mIgnoreRings;
935+
if ( ignoreRingsExpression )
936+
ignoreRings = ignoreRingsExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toBool();
937+
925938
}
926939

927940
void QgsShapeburstFillSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context )
@@ -970,8 +983,9 @@ void QgsShapeburstFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QLi
970983
int blurRadius;
971984
bool useWholeShape;
972985
double maxDistance;
986+
bool ignoreRings;
973987
//calculate data defined symbology
974-
applyDataDefinedSymbology( context, color1, color2, blurRadius, useWholeShape, maxDistance );
988+
applyDataDefinedSymbology( context, color1, color2, blurRadius, useWholeShape, maxDistance, ignoreRings );
975989

976990
//calculate max distance for shapeburst fill to extend from polygon boundary, in pixels
977991
int outputPixelMaxDist = 0;
@@ -1022,7 +1036,21 @@ void QgsShapeburstFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QLi
10221036
//now that we have a render of the polygon in white, draw this onto the shapeburst fill image too
10231037
//(this avoids calling _renderPolygon twice, since that can be slow)
10241038
imgPainter.begin( fillImage );
1025-
imgPainter.drawImage( 0, 0, *alphaImage );
1039+
if ( !ignoreRings )
1040+
{
1041+
imgPainter.drawImage( 0, 0, *alphaImage );
1042+
}
1043+
else
1044+
{
1045+
//using ignore rings mode, so the alpha image can't be used
1046+
//directly as the alpha channel contains polygon rings and we need
1047+
//to draw now without any rings
1048+
imgPainter.setBrush( QBrush( Qt::white ) );
1049+
imgPainter.setPen( QPen( Qt::black ) );
1050+
imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1051+
imgPainter.scale( context.renderContext().rasterScaleFactor(), context.renderContext().rasterScaleFactor() );
1052+
_renderPolygon( &imgPainter, points, NULL, context );
1053+
}
10261054
imgPainter.end();
10271055

10281056
//apply distance transform to image, uses the current color ramp to calculate final pixel colours
@@ -1252,6 +1280,7 @@ QgsStringMap QgsShapeburstFillSymbolLayerV2::properties() const
12521280
map["use_whole_shape"] = QString::number( mUseWholeShape );
12531281
map["max_distance"] = QString::number( mMaxDistance );
12541282
map["distance_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceUnit );
1283+
map["ignore_rings"] = QString::number( mIgnoreRings );
12551284
map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
12561285
map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
12571286

@@ -1273,6 +1302,7 @@ QgsSymbolLayerV2* QgsShapeburstFillSymbolLayerV2::clone() const
12731302
sl->setColorRamp( mGradientRamp->clone() );
12741303
}
12751304
sl->setDistanceUnit( mDistanceUnit );
1305+
sl->setIgnoreRings( mIgnoreRings );
12761306
sl->setOffset( mOffset );
12771307
sl->setOffsetUnit( mOffsetUnit );
12781308
copyDataDefinedProperties( sl );

src/core/symbology-ng/qgsfillsymbollayerv2.h

+17-1
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,20 @@ class CORE_EXPORT QgsShapeburstFillSymbolLayerV2 : public QgsFillSymbolLayerV2
421421
*/
422422
QColor color2() const { return mColor2; };
423423

424+
/**Sets whether the shapeburst fill should ignore polygon rings when calculating
425+
* the buffered shading.
426+
* @param ignoreRings Set to true if buffers should ignore interior rings for polygons.
427+
* @note added in 2.3
428+
* @see ignoreRings
429+
*/
430+
void setIgnoreRings( double ignoreRings ) { mIgnoreRings = ignoreRings; };
431+
/**Returns whether the shapeburst fill is set to ignore polygon interior rings.
432+
* @returns True if the shapeburst fill will ignore interior rings when calculating buffered shading.
433+
* @note added in 2.3
434+
* @see setIgnoreRings
435+
*/
436+
double ignoreRings() const { return mIgnoreRings; };
437+
424438
/**Sets the offset for the shapeburst fill.
425439
* @param offset QPointF indicating the horizontal/vertical offset amount
426440
* @note added in 2.3
@@ -466,14 +480,16 @@ class CORE_EXPORT QgsShapeburstFillSymbolLayerV2 : public QgsFillSymbolLayerV2
466480
QgsVectorColorRampV2* mGradientRamp;
467481
QgsVectorColorRampV2* mTwoColorGradientRamp;
468482

483+
bool mIgnoreRings;
484+
469485
QPointF mOffset;
470486
QgsSymbolV2::OutputUnit mOffsetUnit;
471487

472488
private:
473489

474490
//helper functions for data defined symbology
475491
void applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QColor& color, QColor& color2, int& blurRadius, bool& useWholeShape,
476-
double& maxDistance );
492+
double& maxDistance , bool &ignoreRings );
477493

478494
/* distance transform of a 1d function using squared distance */
479495
void distanceTransform1d( double *f, int n, int *v, double *z, double *d );

src/gui/symbology-ng/qgssymbollayerv2widget.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,10 @@ void QgsShapeburstFillSymbolLayerV2Widget::setSymbolLayer( QgsSymbolLayerV2* lay
10901090
mDistanceUnitComboBox->setCurrentIndex( mLayer->distanceUnit() );
10911091
mDistanceUnitComboBox->blockSignals( false );
10921092

1093+
mIgnoreRingsCheckBox->blockSignals( true );
1094+
mIgnoreRingsCheckBox->setCheckState( mLayer->ignoreRings() ? Qt::Checked : Qt::Unchecked );
1095+
mIgnoreRingsCheckBox->blockSignals( false );
1096+
10931097
// set source color ramp
10941098
if ( mLayer->colorRamp() )
10951099
{
@@ -1228,6 +1232,7 @@ void QgsShapeburstFillSymbolLayerV2Widget::on_mDataDefinedPropertiesButton_click
12281232
tr( "Integer between 0 and 18" ) );
12291233
dataDefinedProperties << QgsDataDefinedSymbolDialog::DataDefinedSymbolEntry( "use_whole_shape", tr( "Use whole shape" ), mLayer->dataDefinedPropertyString( "use_whole_shape" ), QgsDataDefinedSymbolDialog::boolHelpText() );
12301234
dataDefinedProperties << QgsDataDefinedSymbolDialog::DataDefinedSymbolEntry( "max_distance", tr( "Maximum distance" ), mLayer->dataDefinedPropertyString( "max_distance" ), QgsDataDefinedSymbolDialog::doubleHelpText() );
1235+
dataDefinedProperties << QgsDataDefinedSymbolDialog::DataDefinedSymbolEntry( "ignore_rings", tr( "Ignore rings" ), mLayer->dataDefinedPropertyString( "ignore_rings" ), QgsDataDefinedSymbolDialog::boolHelpText() );
12311236

12321237
QgsDataDefinedSymbolDialog d( dataDefinedProperties, mVectorLayer );
12331238
if ( d.exec() == QDialog::Accepted )
@@ -1248,6 +1253,13 @@ void QgsShapeburstFillSymbolLayerV2Widget::on_mDataDefinedPropertiesButton_click
12481253
}
12491254
}
12501255

1256+
void QgsShapeburstFillSymbolLayerV2Widget::on_mIgnoreRingsCheckBox_stateChanged( int state )
1257+
{
1258+
bool checked = ( state == Qt::Checked );
1259+
mLayer->setIgnoreRings( checked );
1260+
emit changed();
1261+
}
1262+
12511263
///////////
12521264

12531265
QgsMarkerLineSymbolLayerV2Widget::QgsMarkerLineSymbolLayerV2Widget( const QgsVectorLayer* vl, QWidget* parent )

src/gui/symbology-ng/qgssymbollayerv2widget.h

+1
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ class GUI_EXPORT QgsShapeburstFillSymbolLayerV2Widget : public QgsSymbolLayerV2W
226226
void offsetChanged();
227227
void on_mOffsetUnitComboBox_currentIndexChanged( int index );
228228
void on_mDataDefinedPropertiesButton_clicked();
229+
void on_mIgnoreRingsCheckBox_stateChanged( int state );
229230

230231
protected:
231232
QgsShapeburstFillSymbolLayerV2* mLayer;

src/ui/symbollayer/widget_shapeburstfill.ui

+13-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<x>0</x>
88
<y>0</y>
99
<width>497</width>
10-
<height>358</height>
10+
<height>365</height>
1111
</rect>
1212
</property>
1313
<property name="windowTitle">
@@ -105,6 +105,13 @@
105105
</property>
106106
</widget>
107107
</item>
108+
<item row="2" column="0">
109+
<widget class="QCheckBox" name="mIgnoreRingsCheckBox">
110+
<property name="text">
111+
<string>Ignore rings in polygons while shading</string>
112+
</property>
113+
</widget>
114+
</item>
108115
<item row="1" column="0" colspan="2">
109116
<layout class="QHBoxLayout" name="horizontalLayout_3">
110117
<property name="spacing">
@@ -152,14 +159,14 @@
152159
</layout>
153160
</widget>
154161
</item>
155-
<item row="2" column="0">
162+
<item row="4" column="0">
156163
<widget class="QLabel" name="label">
157164
<property name="text">
158165
<string>Blur strength</string>
159166
</property>
160167
</widget>
161168
</item>
162-
<item row="2" column="1">
169+
<item row="4" column="1">
163170
<layout class="QHBoxLayout" name="horizontalLayout_4">
164171
<item>
165172
<widget class="QSlider" name="mBlurSlider">
@@ -195,14 +202,14 @@
195202
</item>
196203
</layout>
197204
</item>
198-
<item row="3" column="0">
205+
<item row="5" column="0">
199206
<widget class="QLabel" name="label_6">
200207
<property name="text">
201208
<string>Offset X,Y</string>
202209
</property>
203210
</widget>
204211
</item>
205-
<item row="3" column="1">
212+
<item row="5" column="1">
206213
<layout class="QHBoxLayout" name="horizontalLayout">
207214
<item>
208215
<widget class="QDoubleSpinBox" name="spinOffsetX">
@@ -264,7 +271,7 @@
264271
</item>
265272
</layout>
266273
</item>
267-
<item row="4" column="0" colspan="2">
274+
<item row="6" column="0" colspan="2">
268275
<widget class="QPushButton" name="mDataDefinedPropertiesButton">
269276
<property name="sizePolicy">
270277
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">

tests/src/core/testqgsshapeburst.cpp

+11-2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class TestQgsShapeburst: public QObject
5555
void shapeburstBlur();
5656
void shapeburstMaxDistanceMm();
5757
void shapeburstMaxDistanceMapUnits();
58+
void shapeburstIgnoreRings();
5859
void shapeburstSymbolFromQml();
5960

6061
private:
@@ -179,7 +180,7 @@ void TestQgsShapeburst::shapeburstMaxDistanceMm()
179180
mShapeburstFill->setMaxDistance( 3 );
180181
mShapeburstFill->setDistanceUnit( QgsSymbolV2::MM );
181182
QVERIFY( imageCheck( "shapeburst_maxdistance_mm" ) );
182-
mShapeburstFill->setUseWholeShape( true);
183+
mShapeburstFill->setUseWholeShape( true );
183184
}
184185

185186
void TestQgsShapeburst::shapeburstMaxDistanceMapUnits()
@@ -189,10 +190,18 @@ void TestQgsShapeburst::shapeburstMaxDistanceMapUnits()
189190
mShapeburstFill->setMaxDistance( 10 );
190191
mShapeburstFill->setDistanceUnit( QgsSymbolV2::MapUnit );
191192
QVERIFY( imageCheck( "shapeburst_maxdistance_mapunit" ) );
192-
mShapeburstFill->setUseWholeShape( true);
193+
mShapeburstFill->setUseWholeShape( true );
193194
mShapeburstFill->setDistanceUnit( QgsSymbolV2::MM );
194195
}
195196

197+
void TestQgsShapeburst::shapeburstIgnoreRings()
198+
{
199+
mReport += "<h2>Shapeburst symbol renderer ignore rings</h2>\n";
200+
mShapeburstFill->setIgnoreRings( true );
201+
QVERIFY( imageCheck( "shapeburst_ignorerings" ) );
202+
mShapeburstFill->setIgnoreRings( false );
203+
}
204+
196205
void TestQgsShapeburst::shapeburstSymbolFromQml()
197206
{
198207
mReport += "<h2>Shapeburst symbol from QML test</h2>\n";

0 commit comments

Comments
 (0)