Skip to content
Permalink
Browse files

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)
  • Loading branch information
nyalldawson committed Mar 21, 2014
1 parent a0c2c7a commit 942d5da59b665aec201e69a9f407783e02dce7cf
@@ -351,6 +351,20 @@ class QgsShapeburstFillSymbolLayerV2 : QgsFillSymbolLayerV2
* @see colorType
*/
QColor color2() const;

/**Sets whether the shapeburst fill should ignore polygon rings when calculating
* the buffered shading.
* @param ignoreRings Set to true if buffers should ignore interior rings for polygons.
* @note added in 2.3
* @see ignoreRings
*/
void setIgnoreRings( double ignoreRings );
/**Returns whether the shapeburst fill is set to ignore polygon interior rings.
* @returns True if the shapeburst fill will ignore interior rings when calculating buffered shading.
* @note added in 2.3
* @see setIgnoreRings
*/
double ignoreRings() const;

/**Sets the offset for the shapeburst fill.
* @param offset QPointF indicating the horizontal/vertical offset amount
@@ -795,6 +795,7 @@ QgsShapeburstFillSymbolLayerV2::QgsShapeburstFillSymbolLayerV2( QColor color, QC
mColor2( color2 ),
mGradientRamp( NULL ),
mTwoColorGradientRamp( 0 ),
mIgnoreRings( false ),
mOffsetUnit( QgsSymbolV2::MM )
{
mColor = color;
@@ -859,6 +860,10 @@ QgsSymbolLayerV2* QgsShapeburstFillSymbolLayerV2::create( const QgsStringMap& pr
{
sl->setDistanceUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["distance_unit"] ) );
}
if ( props.contains( "ignore_rings" ) )
{
sl->setIgnoreRings( props["ignore_rings"].toInt() );
}
if ( gradientRamp )
{
sl->setColorRamp( gradientRamp );
@@ -874,6 +879,8 @@ QgsSymbolLayerV2* QgsShapeburstFillSymbolLayerV2::create( const QgsStringMap& pr
sl->setDataDefinedProperty( "use_whole_shape", props["use_whole_shape_expression"] );
if ( props.contains( "max_distance_expression" ) )
sl->setDataDefinedProperty( "max_distance", props["max_distance_expression"] );
if ( props.contains( "ignore_rings_expression" ) )
sl->setDataDefinedProperty( "ignore_rings", props["ignore_rings_expression"] );

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

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

//ignore rings
QgsExpression* ignoreRingsExpression = expression( "ignore_rings" );
ignoreRings = mIgnoreRings;
if ( ignoreRingsExpression )
ignoreRings = ignoreRingsExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toBool();

}

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

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

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

@@ -1273,6 +1302,7 @@ QgsSymbolLayerV2* QgsShapeburstFillSymbolLayerV2::clone() const
sl->setColorRamp( mGradientRamp->clone() );
}
sl->setDistanceUnit( mDistanceUnit );
sl->setIgnoreRings( mIgnoreRings );
sl->setOffset( mOffset );
sl->setOffsetUnit( mOffsetUnit );
copyDataDefinedProperties( sl );
@@ -421,6 +421,20 @@ class CORE_EXPORT QgsShapeburstFillSymbolLayerV2 : public QgsFillSymbolLayerV2
*/
QColor color2() const { return mColor2; };

/**Sets whether the shapeburst fill should ignore polygon rings when calculating
* the buffered shading.
* @param ignoreRings Set to true if buffers should ignore interior rings for polygons.
* @note added in 2.3
* @see ignoreRings
*/
void setIgnoreRings( double ignoreRings ) { mIgnoreRings = ignoreRings; };
/**Returns whether the shapeburst fill is set to ignore polygon interior rings.
* @returns True if the shapeburst fill will ignore interior rings when calculating buffered shading.
* @note added in 2.3
* @see setIgnoreRings
*/
double ignoreRings() const { return mIgnoreRings; };

/**Sets the offset for the shapeburst fill.
* @param offset QPointF indicating the horizontal/vertical offset amount
* @note added in 2.3
@@ -466,14 +480,16 @@ class CORE_EXPORT QgsShapeburstFillSymbolLayerV2 : public QgsFillSymbolLayerV2
QgsVectorColorRampV2* mGradientRamp;
QgsVectorColorRampV2* mTwoColorGradientRamp;

bool mIgnoreRings;

QPointF mOffset;
QgsSymbolV2::OutputUnit mOffsetUnit;

private:

//helper functions for data defined symbology
void applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QColor& color, QColor& color2, int& blurRadius, bool& useWholeShape,
double& maxDistance );
double& maxDistance , bool &ignoreRings );

/* distance transform of a 1d function using squared distance */
void distanceTransform1d( double *f, int n, int *v, double *z, double *d );
@@ -1090,6 +1090,10 @@ void QgsShapeburstFillSymbolLayerV2Widget::setSymbolLayer( QgsSymbolLayerV2* lay
mDistanceUnitComboBox->setCurrentIndex( mLayer->distanceUnit() );
mDistanceUnitComboBox->blockSignals( false );

mIgnoreRingsCheckBox->blockSignals( true );
mIgnoreRingsCheckBox->setCheckState( mLayer->ignoreRings() ? Qt::Checked : Qt::Unchecked );
mIgnoreRingsCheckBox->blockSignals( false );

// set source color ramp
if ( mLayer->colorRamp() )
{
@@ -1228,6 +1232,7 @@ void QgsShapeburstFillSymbolLayerV2Widget::on_mDataDefinedPropertiesButton_click
tr( "Integer between 0 and 18" ) );
dataDefinedProperties << QgsDataDefinedSymbolDialog::DataDefinedSymbolEntry( "use_whole_shape", tr( "Use whole shape" ), mLayer->dataDefinedPropertyString( "use_whole_shape" ), QgsDataDefinedSymbolDialog::boolHelpText() );
dataDefinedProperties << QgsDataDefinedSymbolDialog::DataDefinedSymbolEntry( "max_distance", tr( "Maximum distance" ), mLayer->dataDefinedPropertyString( "max_distance" ), QgsDataDefinedSymbolDialog::doubleHelpText() );
dataDefinedProperties << QgsDataDefinedSymbolDialog::DataDefinedSymbolEntry( "ignore_rings", tr( "Ignore rings" ), mLayer->dataDefinedPropertyString( "ignore_rings" ), QgsDataDefinedSymbolDialog::boolHelpText() );

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

void QgsShapeburstFillSymbolLayerV2Widget::on_mIgnoreRingsCheckBox_stateChanged( int state )
{
bool checked = ( state == Qt::Checked );
mLayer->setIgnoreRings( checked );
emit changed();
}

///////////

QgsMarkerLineSymbolLayerV2Widget::QgsMarkerLineSymbolLayerV2Widget( const QgsVectorLayer* vl, QWidget* parent )
@@ -226,6 +226,7 @@ class GUI_EXPORT QgsShapeburstFillSymbolLayerV2Widget : public QgsSymbolLayerV2W
void offsetChanged();
void on_mOffsetUnitComboBox_currentIndexChanged( int index );
void on_mDataDefinedPropertiesButton_clicked();
void on_mIgnoreRingsCheckBox_stateChanged( int state );

protected:
QgsShapeburstFillSymbolLayerV2* mLayer;
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>497</width>
<height>358</height>
<height>365</height>
</rect>
</property>
<property name="windowTitle">
@@ -105,6 +105,13 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="mIgnoreRingsCheckBox">
<property name="text">
<string>Ignore rings in polygons while shading</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
@@ -152,14 +159,14 @@
</layout>
</widget>
</item>
<item row="2" column="0">
<item row="4" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Blur strength</string>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QSlider" name="mBlurSlider">
@@ -195,14 +202,14 @@
</item>
</layout>
</item>
<item row="3" column="0">
<item row="5" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Offset X,Y</string>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QDoubleSpinBox" name="spinOffsetX">
@@ -264,7 +271,7 @@
</item>
</layout>
</item>
<item row="4" column="0" colspan="2">
<item row="6" column="0" colspan="2">
<widget class="QPushButton" name="mDataDefinedPropertiesButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
@@ -55,6 +55,7 @@ class TestQgsShapeburst: public QObject
void shapeburstBlur();
void shapeburstMaxDistanceMm();
void shapeburstMaxDistanceMapUnits();
void shapeburstIgnoreRings();
void shapeburstSymbolFromQml();

private:
@@ -179,7 +180,7 @@ void TestQgsShapeburst::shapeburstMaxDistanceMm()
mShapeburstFill->setMaxDistance( 3 );
mShapeburstFill->setDistanceUnit( QgsSymbolV2::MM );
QVERIFY( imageCheck( "shapeburst_maxdistance_mm" ) );
mShapeburstFill->setUseWholeShape( true);
mShapeburstFill->setUseWholeShape( true );
}

void TestQgsShapeburst::shapeburstMaxDistanceMapUnits()
@@ -189,10 +190,18 @@ void TestQgsShapeburst::shapeburstMaxDistanceMapUnits()
mShapeburstFill->setMaxDistance( 10 );
mShapeburstFill->setDistanceUnit( QgsSymbolV2::MapUnit );
QVERIFY( imageCheck( "shapeburst_maxdistance_mapunit" ) );
mShapeburstFill->setUseWholeShape( true);
mShapeburstFill->setUseWholeShape( true );
mShapeburstFill->setDistanceUnit( QgsSymbolV2::MM );
}

void TestQgsShapeburst::shapeburstIgnoreRings()
{
mReport += "<h2>Shapeburst symbol renderer ignore rings</h2>\n";
mShapeburstFill->setIgnoreRings( true );
QVERIFY( imageCheck( "shapeburst_ignorerings" ) );
mShapeburstFill->setIgnoreRings( false );
}

void TestQgsShapeburst::shapeburstSymbolFromQml()
{
mReport += "<h2>Shapeburst symbol from QML test</h2>\n";
Binary file not shown.

0 comments on commit 942d5da

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