Skip to content

Commit 3fe12df

Browse files
committedJun 10, 2014
Fix #10355 (crash) and #10338 (overlapping polygons) in inverted polygon renderer
Conflicts: python/core/symbology-ng/qgsinvertedpolygonrenderer.sip src/core/symbology-ng/qgsinvertedpolygonrenderer.cpp src/core/symbology-ng/qgsinvertedpolygonrenderer.h src/gui/symbology-ng/qgsinvertedpolygonrendererwidget.cpp src/gui/symbology-ng/qgsinvertedpolygonrendererwidget.h
2 parents ae9048a + 25346fe commit 3fe12df

18 files changed

+396
-76
lines changed
 

‎python/core/symbology-ng/qgsinvertedpolygonrenderer.sip

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,14 @@ class QgsInvertedPolygonRenderer : QgsFeatureRendererV2
7171
/** @returns the current embedded renderer
7272
*/
7373
const QgsFeatureRendererV2* embeddedRenderer() const;
74+
75+
/** @returns true if the geometries are to be preprocessed (merged with an union) before rendering.*/
76+
bool preprocessingEnabled() const;
77+
/**
78+
@param enabled enables or disables the preprocessing.
79+
When enabled, geometries will be merged with an union before being rendered.
80+
It allows to fix some rendering artefacts (when rendering overlapping polygons for instance).
81+
This will involve some CPU-demanding computations and is thus disabled by default.
82+
*/
83+
void setPreprocessingEnabled( bool enabled );
7484
};

‎src/core/symbology-ng/qgsinvertedpolygonrenderer.cpp

Lines changed: 105 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
QgsInvertedPolygonRenderer::QgsInvertedPolygonRenderer( const QgsFeatureRendererV2* subRenderer )
3131
: QgsFeatureRendererV2( "invertedPolygonRenderer" )
32+
, mPreprocessingEnabled( false )
3233
{
3334
if ( subRenderer )
3435
{
@@ -68,7 +69,6 @@ void QgsInvertedPolygonRenderer::startRender( QgsRenderContext& context, const Q
6869
return;
6970
}
7071

71-
mSubRenderer->startRender( context, fields );
7272
mFeaturesCategoryMap.clear();
7373
mFeatureDecorations.clear();
7474
mFields = fields;
@@ -78,36 +78,52 @@ void QgsInvertedPolygonRenderer::startRender( QgsRenderContext& context, const Q
7878
// It must be computed in the destination CRS if reprojection is enabled.
7979
const QgsMapToPixel& mtp( context.mapToPixel() );
8080

81+
if ( !context.painter() )
82+
{
83+
return;
84+
}
85+
8186
// convert viewport to dest CRS
8287
QRect e( context.painter()->viewport() );
8388
// add some space to hide borders and tend to infinity
84-
e.adjust( -e.width()*10, -e.height()*10, e.width()*10, e.height()*10 );
89+
e.adjust( -e.width()*5, -e.height()*5, e.width()*5, e.height()*5 );
8590
QgsPolyline exteriorRing;
8691
exteriorRing << mtp.toMapCoordinates( e.topLeft() );
8792
exteriorRing << mtp.toMapCoordinates( e.topRight() );
8893
exteriorRing << mtp.toMapCoordinates( e.bottomRight() );
8994
exteriorRing << mtp.toMapCoordinates( e.bottomLeft() );
9095
exteriorRing << mtp.toMapCoordinates( e.topLeft() );
9196

92-
mTransform = context.coordinateTransform();
97+
// copy the rendering context
98+
mContext = context;
99+
93100
// If reprojection is enabled, we must reproject during renderFeature
94101
// and act as if there is no reprojection
95102
// If we don't do that, there is no need to have a simple rectangular extent
96103
// that covers the whole screen
97104
// (a rectangle in the destCRS cannot be expressed as valid coordinates in the sourceCRS in general)
98-
if ( mTransform )
105+
if ( context.coordinateTransform() )
99106
{
100107
// disable projection
101-
context.setCoordinateTransform( 0 );
108+
mContext.setCoordinateTransform( 0 );
109+
// recompute extent so that polygon clipping is correct
110+
QRect v( context.painter()->viewport() );
111+
mContext.setExtent( QgsRectangle( mtp.toMapCoordinates( v.topLeft() ), mtp.toMapCoordinates( v.bottomRight() ) ) );
112+
// do we have to recompute the MapToPixel ?
102113
}
103114

104115
mExtentPolygon.clear();
105116
mExtentPolygon.append( exteriorRing );
117+
118+
mSubRenderer->startRender( mContext, fields );
106119
}
107120

108121
bool QgsInvertedPolygonRenderer::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker )
109122
{
110-
Q_UNUSED( context );
123+
if ( !context.painter() )
124+
{
125+
return false;
126+
}
111127

112128
// store this feature as a feature to render with decoration if needed
113129
if ( selected || drawVertexMarker )
@@ -153,33 +169,32 @@ bool QgsInvertedPolygonRenderer::renderFeature( QgsFeature& feature, QgsRenderCo
153169
return false;
154170
}
155171

156-
// We build here a "reversed" geometry of all the polygons
157-
//
158-
// The final geometry is a multipolygon F, with :
159-
// * the first polygon of F having the current extent as its exterior ring
160-
// * each polygon's exterior ring is added as interior ring of the first polygon of F
161-
// * each polygon's interior ring is added as new polygons in F
162-
//
163-
// No validity check is done, on purpose, it will be very slow and painting
164-
// operations do not need geometries to be valid
165-
if ( ! mFeaturesCategoryMap.contains( catId ) )
172+
if ( ! mSymbolCategories.contains( catId ) )
166173
{
167174
// the exterior ring must be a square in the destination CRS
168175
CombinedFeature cFeat;
169176
cFeat.multiPolygon.append( mExtentPolygon );
170177
// store the first feature
171178
cFeat.feature = feature;
172-
mFeaturesCategoryMap.insert( catId, cFeat );
179+
mSymbolCategories.insert( catId, mSymbolCategories.count() );
180+
mFeaturesCategoryMap.append( cFeat );
173181
}
174182

175-
// update the gometry
176-
CombinedFeature& cFeat = mFeaturesCategoryMap[catId];
183+
// update the geometry
184+
CombinedFeature& cFeat = mFeaturesCategoryMap[ mSymbolCategories[catId] ];
177185
QgsMultiPolygon multi;
178186
QgsGeometry* geom = feature.geometry();
179187
if ( !geom )
180188
{
181189
return false;
182190
}
191+
192+
const QgsCoordinateTransform* xform = context.coordinateTransform();
193+
if ( xform )
194+
{
195+
geom->transform( *xform );
196+
}
197+
183198
if (( geom->wkbType() == QGis::WKBPolygon ) ||
184199
( geom->wkbType() == QGis::WKBPolygon25D ) )
185200
{
@@ -191,43 +206,49 @@ bool QgsInvertedPolygonRenderer::renderFeature( QgsFeature& feature, QgsRenderCo
191206
multi = geom->asMultiPolygon();
192207
}
193208

194-
for ( int i = 0; i < multi.size(); i++ )
209+
if ( mPreprocessingEnabled )
195210
{
196-
// add the exterior ring as interior ring to the first polygon
197-
if ( mTransform )
211+
// preprocessing
212+
if ( ! cFeat.feature.geometry() )
198213
{
199-
QgsPolyline new_ls;
200-
QgsPolyline& old_ls = multi[i][0];
201-
for ( int k = 0; k < old_ls.size(); k++ )
202-
{
203-
new_ls.append( mTransform->transform( old_ls[k] ) );
204-
}
205-
cFeat.multiPolygon[0].append( new_ls );
214+
// first feature: add the current geometry
215+
cFeat.feature.setGeometry( new QgsGeometry( *geom ) );
206216
}
207217
else
208218
{
209-
cFeat.multiPolygon[0].append( multi[i][0] );
210-
}
211-
// add interior rings as new polygons
212-
for ( int j = 1; j < multi[i].size(); j++ )
213-
{
214-
QgsPolygon new_poly;
215-
if ( mTransform )
219+
// other features: combine them (union)
220+
QgsGeometry* combined = cFeat.feature.geometry()->combine( geom );
221+
if ( combined && combined->isGeosValid() )
216222
{
217-
QgsPolyline new_ls;
218-
QgsPolyline& old_ls = multi[i][j];
219-
for ( int k = 0; k < old_ls.size(); k++ )
220-
{
221-
new_ls.append( mTransform->transform( old_ls[k] ) );
222-
}
223-
new_poly.append( new_ls );
223+
cFeat.feature.setGeometry( combined );
224224
}
225-
else
225+
}
226+
}
227+
else
228+
{
229+
// No preprocessing involved.
230+
// We build here a "reversed" geometry of all the polygons
231+
//
232+
// The final geometry is a multipolygon F, with :
233+
// * the first polygon of F having the current extent as its exterior ring
234+
// * each polygon's exterior ring is added as interior ring of the first polygon of F
235+
// * each polygon's interior ring is added as new polygons in F
236+
//
237+
// No validity check is done, on purpose, it will be very slow and painting
238+
// operations do not need geometries to be valid
239+
240+
for ( int i = 0; i < multi.size(); i++ )
241+
{
242+
// add the exterior ring as interior ring to the first polygon
243+
cFeat.multiPolygon[0].append( multi[i][0] );
244+
245+
// add interior rings as new polygons
246+
for ( int j = 1; j < multi[i].size(); j++ )
226247
{
248+
QgsPolygon new_poly;
227249
new_poly.append( multi[i][j] );
250+
cFeat.multiPolygon.append( new_poly );
228251
}
229-
230-
cFeat.multiPolygon.append( new_poly );
231252
}
232253
}
233254
return true;
@@ -239,12 +260,33 @@ void QgsInvertedPolygonRenderer::stopRender( QgsRenderContext& context )
239260
{
240261
return;
241262
}
263+
if ( !context.painter() )
264+
{
265+
return;
266+
}
242267

243268
for ( FeatureCategoryMap::iterator cit = mFeaturesCategoryMap.begin(); cit != mFeaturesCategoryMap.end(); ++cit )
244269
{
245-
QgsFeature feat( cit.value().feature );
246-
feat.setGeometry( QgsGeometry::fromMultiPolygon( cit.value().multiPolygon ) );
247-
mSubRenderer->renderFeature( feat, context );
270+
QgsFeature feat( cit->feature );
271+
if ( !mPreprocessingEnabled )
272+
{
273+
// no preprocessing - the final polygon has already been prepared
274+
feat.setGeometry( QgsGeometry::fromMultiPolygon( cit->multiPolygon ) );
275+
}
276+
else
277+
{
278+
// preprocessing mode - we still have to invert (using difference)
279+
if ( feat.geometry() )
280+
{
281+
QScopedPointer<QgsGeometry> rect( QgsGeometry::fromPolygon( mExtentPolygon ) );
282+
QgsGeometry *final = rect->difference( feat.geometry() );
283+
if ( final )
284+
{
285+
feat.setGeometry( final );
286+
}
287+
}
288+
}
289+
mSubRenderer->renderFeature( feat, mContext );
248290
}
249291

250292
// when no features are visible, we still have to draw the exterior rectangle
@@ -256,22 +298,16 @@ void QgsInvertedPolygonRenderer::stopRender( QgsRenderContext& context )
256298
// empty feature with default attributes
257299
QgsFeature feat( mFields );
258300
feat.setGeometry( QgsGeometry::fromPolygon( mExtentPolygon ) );
259-
mSubRenderer->renderFeature( feat, context );
301+
mSubRenderer->renderFeature( feat, mContext );
260302
}
261303

262304
// draw feature decorations
263305
foreach ( FeatureDecoration deco, mFeatureDecorations )
264306
{
265-
mSubRenderer->renderFeature( deco.feature, context, deco.layer, deco.selected, deco.drawMarkers );
307+
mSubRenderer->renderFeature( deco.feature, mContext, deco.layer, deco.selected, deco.drawMarkers );
266308
}
267309

268-
mSubRenderer->stopRender( context );
269-
270-
if ( mTransform )
271-
{
272-
// restore the coordinate transform if needed
273-
context.setCoordinateTransform( mTransform );
274-
}
310+
mSubRenderer->stopRender( mContext );
275311
}
276312

277313
QString QgsInvertedPolygonRenderer::dump() const
@@ -285,12 +321,17 @@ QString QgsInvertedPolygonRenderer::dump() const
285321

286322
QgsFeatureRendererV2* QgsInvertedPolygonRenderer::clone()
287323
{
324+
QgsInvertedPolygonRenderer* newRenderer;
288325
if ( mSubRenderer.isNull() )
289326
{
290-
return new QgsInvertedPolygonRenderer( 0 );
327+
newRenderer = new QgsInvertedPolygonRenderer( 0 );
328+
}
329+
else
330+
{
331+
newRenderer = new QgsInvertedPolygonRenderer( mSubRenderer->clone() );
291332
}
292-
// else
293-
return new QgsInvertedPolygonRenderer( mSubRenderer->clone() );
333+
newRenderer->setPreprocessingEnabled( preprocessingEnabled() );
334+
return newRenderer;
294335
}
295336

296337
QgsFeatureRendererV2* QgsInvertedPolygonRenderer::create( QDomElement& element )
@@ -302,13 +343,15 @@ QgsFeatureRendererV2* QgsInvertedPolygonRenderer::create( QDomElement& element )
302343
{
303344
r->setEmbeddedRenderer( QgsFeatureRendererV2::load( embeddedRendererElem ) );
304345
}
346+
r->setPreprocessingEnabled( element.attribute( "preprocessing", "0" ).toInt() == 1 );
305347
return r;
306348
}
307349

308350
QDomElement QgsInvertedPolygonRenderer::save( QDomDocument& doc )
309351
{
310352
QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
311353
rendererElem.setAttribute( "type", "invertedPolygonRenderer" );
354+
rendererElem.setAttribute( "preprocessing", preprocessingEnabled() ? "1" : "0" );
312355

313356
if ( mSubRenderer )
314357
{
@@ -390,3 +433,4 @@ bool QgsInvertedPolygonRenderer::willRenderFeature( QgsFeature& feat )
390433
}
391434
return mSubRenderer->willRenderFeature( feat );
392435
}
436+

‎src/core/symbology-ng/qgsinvertedpolygonrenderer.h

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,16 @@ class CORE_EXPORT QgsInvertedPolygonRenderer : public QgsFeatureRendererV2
108108
*/
109109
const QgsFeatureRendererV2* embeddedRenderer() const;
110110

111+
/** @returns true if the geometries are to be preprocessed (merged with an union) before rendering.*/
112+
bool preprocessingEnabled() const { return mPreprocessingEnabled; }
113+
/**
114+
@param enabled enables or disables the preprocessing.
115+
When enabled, geometries will be merged with an union before being rendered.
116+
It allows to fix some rendering artefacts (when rendering overlapping polygons for instance).
117+
This will involve some CPU-demanding computations and is thus disabled by default.
118+
*/
119+
void setPreprocessingEnabled( bool enabled ) { mPreprocessingEnabled = enabled; }
120+
111121
private:
112122
/** Private copy constructor. @see clone() */
113123
QgsInvertedPolygonRenderer( const QgsInvertedPolygonRenderer& );
@@ -123,15 +133,18 @@ class CORE_EXPORT QgsInvertedPolygonRenderer : public QgsFeatureRendererV2
123133
QgsMultiPolygon multiPolygon; //< the final combined geometry
124134
QgsFeature feature; //< one feature (for attriute-based rendering)
125135
};
126-
typedef QMap< QByteArray, CombinedFeature > FeatureCategoryMap;
127-
/** where features are stored, based on their symbol category */
136+
typedef QVector<CombinedFeature> FeatureCategoryMap;
137+
/** where features are stored, based on the index of their symbol category @see mSymbolCategories */
128138
FeatureCategoryMap mFeaturesCategoryMap;
129139

140+
/** maps a category to an index */
141+
QMap<QByteArray, int> mSymbolCategories;
142+
130143
/** the polygon used as exterior ring that covers the current extent */
131144
QgsPolygon mExtentPolygon;
132145

133-
/** the current coordinate transform (or null) */
134-
const QgsCoordinateTransform* mTransform;
146+
/** the context used for rendering */
147+
QgsRenderContext mContext;
135148

136149
/** fields of each feature*/
137150
QgsFields mFields;
@@ -149,6 +162,9 @@ class CORE_EXPORT QgsInvertedPolygonRenderer : public QgsFeatureRendererV2
149162
feature( a_feature ), selected( a_selected ), drawMarkers( a_drawMarkers ), layer( a_layer ) {}
150163
};
151164
QList<FeatureDecoration> mFeatureDecorations;
165+
166+
/** whether to preprocess (merge) geometries before rendering*/
167+
bool mPreprocessingEnabled;
152168
};
153169

154170

‎src/gui/symbology-ng/qgsinvertedpolygonrendererwidget.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ QgsInvertedPolygonRendererWidget::QgsInvertedPolygonRendererWidget( QgsVectorLay
6868
{
6969
// an existing inverted renderer
7070
mRenderer.reset( static_cast<QgsInvertedPolygonRenderer*>( renderer ) );
71+
mMergePolygonsCheckBox->blockSignals( true );
72+
mMergePolygonsCheckBox->setCheckState( mRenderer->preprocessingEnabled() ? Qt::Checked : Qt::Unchecked );
73+
mMergePolygonsCheckBox->blockSignals( false );
7174
}
7275

7376
int currentEmbeddedIdx = 0;
@@ -123,12 +126,16 @@ void QgsInvertedPolygonRendererWidget::on_mRendererComboBox_currentIndexChanged(
123126
{
124127
mEmbeddedRendererWidget.reset( m->createRendererWidget( mLayer, mStyle, const_cast<QgsFeatureRendererV2*>( mRenderer->embeddedRenderer() )->clone() ) );
125128

126-
if ( mLayout->count() > 1 )
129+
if ( mLayout->count() > 2 )
127130
{
128131
// remove the current renderer widget
129-
mLayout->takeAt( 1 );
132+
mLayout->takeAt( 2 );
130133
}
131134
mLayout->addWidget( mEmbeddedRendererWidget.data() );
132135
}
133136
}
134137

138+
void QgsInvertedPolygonRendererWidget::on_mMergePolygonsCheckBox_stateChanged( int state )
139+
{
140+
mRenderer->setPreprocessingEnabled( state == Qt::Checked );
141+
}

‎src/gui/symbology-ng/qgsinvertedpolygonrendererwidget.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class GUI_EXPORT QgsInvertedPolygonRendererWidget : public QgsRendererV2Widget,
5656

5757
private slots:
5858
void on_mRendererComboBox_currentIndexChanged( int index );
59+
void on_mMergePolygonsCheckBox_stateChanged( int state );
5960
};
6061

6162

‎src/ui/qgsinvertedpolygonrendererwidgetbase.ui

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
<rect>
77
<x>0</x>
88
<y>0</y>
9-
<width>390</width>
10-
<height>79</height>
9+
<width>316</width>
10+
<height>75</height>
1111
</rect>
1212
</property>
1313
<property name="windowTitle">
@@ -28,6 +28,16 @@
2828
</item>
2929
</layout>
3030
</item>
31+
<item>
32+
<widget class="QCheckBox" name="mMergePolygonsCheckBox">
33+
<property name="toolTip">
34+
<string/>
35+
</property>
36+
<property name="text">
37+
<string>Merge polygons before rendering (slow)</string>
38+
</property>
39+
</widget>
40+
</item>
3141
</layout>
3242
</widget>
3343
<resources/>

‎tests/src/core/testqgsinvertedpolygonrenderer.cpp

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,13 @@ class TestQgsInvertedPolygon: public QObject
4747

4848
void singleSubRenderer();
4949
void graduatedSubRenderer();
50+
void preprocess();
51+
void projectionTest();
5052

5153
private:
5254
bool mTestHasError;
5355
bool setQml( QString qmlFile );
54-
bool imageCheck( QString theType );
56+
bool imageCheck( QString theType, const QgsRectangle* = 0 );
5557
QgsMapSettings mMapSettings;
5658
QgsVectorLayer * mpPolysLayer;
5759
QString mTestDataDir;
@@ -73,7 +75,7 @@ void TestQgsInvertedPolygon::initTestCase()
7375
//
7476
//create a poly layer that will be used in all tests...
7577
//
76-
QString myPolysFileName = mTestDataDir + "polys.shp";
78+
QString myPolysFileName = mTestDataDir + "polys_overlapping.shp";
7779
QFileInfo myPolyFileInfo( myPolysFileName );
7880
mpPolysLayer = new QgsVectorLayer( myPolyFileInfo.filePath(),
7981
myPolyFileInfo.completeBaseName(), "ogr" );
@@ -115,6 +117,27 @@ void TestQgsInvertedPolygon::graduatedSubRenderer()
115117
QVERIFY( imageCheck( "inverted_polys_graduated" ) );
116118
}
117119

120+
void TestQgsInvertedPolygon::preprocess()
121+
{
122+
// FIXME will have to find some overlapping polygons
123+
mReport += "<h2>Inverted polygon renderer, preprocessing test</h2>\n";
124+
QVERIFY( setQml( "inverted_polys_preprocess.qml" ) );
125+
QVERIFY( imageCheck( "inverted_polys_preprocess" ) );
126+
}
127+
128+
void TestQgsInvertedPolygon::projectionTest()
129+
{
130+
mReport += "<h2>Inverted polygon renderer, projection test</h2>\n";
131+
mMapSettings.setDestinationCrs( QgsCoordinateReferenceSystem("EPSG:2154") );
132+
mMapSettings.setCrsTransformEnabled( true );
133+
QgsRectangle extent( QgsPoint(-8639421,8382691), QgsPoint(-3969110,12570905) );
134+
QVERIFY( setQml( "inverted_polys_single.qml" ) );
135+
QVERIFY( imageCheck( "inverted_polys_projection", &extent ) );
136+
QVERIFY( setQml( "inverted_polys_preprocess.qml" ) );
137+
QVERIFY( imageCheck( "inverted_polys_projection2", &extent ) );
138+
mMapSettings.setCrsTransformEnabled( false );
139+
}
140+
118141
//
119142
// Private helper functions not called directly by CTest
120143
//
@@ -135,11 +158,18 @@ bool TestQgsInvertedPolygon::setQml( QString qmlFile )
135158
return myStyleFlag;
136159
}
137160

138-
bool TestQgsInvertedPolygon::imageCheck( QString theTestType )
161+
bool TestQgsInvertedPolygon::imageCheck( QString theTestType, const QgsRectangle* extent )
139162
{
140163
//use the QgsRenderChecker test utility class to
141164
//ensure the rendered output matches our control image
142-
mMapSettings.setExtent( mpPolysLayer->extent() );
165+
if ( !extent )
166+
{
167+
mMapSettings.setExtent( mpPolysLayer->extent() );
168+
}
169+
else
170+
{
171+
mMapSettings.setExtent( *extent );
172+
}
143173
QgsRenderChecker myChecker;
144174
myChecker.setControlName( "expected_" + theTestType );
145175
myChecker.setMapSettings( mMapSettings );

‎tests/testdata/inverted_polys_graduated.qml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
22
<qgis version="2.3.0-Master" minimumScale="1" maximumScale="1e+08" simplifyDrawingHints="1" minLabelScale="1" maxLabelScale="1e+08" simplifyDrawingTol="1" simplifyMaxScale="1" hasScaleBasedVisibilityFlag="0" simplifyLocal="1" scaleBasedLabelVisibilityFlag="0">
3-
<renderer-v2 type="invertedPolygonRenderer">
4-
<renderer-v2 attr="Name" symbollevels="0" type="categorizedSymbol">
3+
<renderer-v2 preprocessing="0" type="invertedPolygonRenderer">
4+
<renderer-v2 attr="Name" symbollevels="1" type="categorizedSymbol">
55
<categories>
66
<category symbol="0" value="Dam" label="Dam"/>
77
<category symbol="1" value="Lake" label="Lake"/>
@@ -27,7 +27,7 @@
2727
</layer>
2828
</symbol>
2929
<symbol alpha="1" type="fill" name="1">
30-
<layer pass="0" class="ShapeburstFill" locked="0">
30+
<layer pass="1" class="ShapeburstFill" locked="0">
3131
<prop k="blur_radius" v="0"/>
3232
<prop k="color1" v="0,0,255,255"/>
3333
<prop k="color2" v="0,255,0,255"/>
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
2+
<qgis version="2.3.0-Master" minimumScale="1" maximumScale="1e+08" simplifyDrawingHints="1" minLabelScale="1" maxLabelScale="1e+08" simplifyDrawingTol="1" simplifyMaxScale="1" hasScaleBasedVisibilityFlag="0" simplifyLocal="1" scaleBasedLabelVisibilityFlag="0">
3+
<renderer-v2 type="invertedPolygonRenderer" preprocessing="1">
4+
<renderer-v2 symbollevels="0" type="singleSymbol">
5+
<symbols>
6+
<symbol alpha="1" type="fill" name="0">
7+
<layer pass="0" class="SimpleFill" locked="0">
8+
<prop k="border_width_map_unit_scale" v="0,0"/>
9+
<prop k="border_width_unit" v="MM"/>
10+
<prop k="color" v="76,143,160,255"/>
11+
<prop k="color_border" v="0,0,0,255"/>
12+
<prop k="joinstyle" v="bevel"/>
13+
<prop k="offset" v="0,0"/>
14+
<prop k="offset_map_unit_scale" v="0,0"/>
15+
<prop k="offset_unit" v="MM"/>
16+
<prop k="style" v="solid"/>
17+
<prop k="style_border" v="solid"/>
18+
<prop k="width_border" v="0.26"/>
19+
</layer>
20+
</symbol>
21+
</symbols>
22+
<rotation/>
23+
<sizescale scalemethod="area"/>
24+
</renderer-v2>
25+
</renderer-v2>
26+
<customproperties>
27+
<property key="labeling" value="pal"/>
28+
<property key="labeling/addDirectionSymbol" value="false"/>
29+
<property key="labeling/angleOffset" value="0"/>
30+
<property key="labeling/blendMode" value="0"/>
31+
<property key="labeling/bufferBlendMode" value="0"/>
32+
<property key="labeling/bufferColorA" value="255"/>
33+
<property key="labeling/bufferColorB" value="255"/>
34+
<property key="labeling/bufferColorG" value="255"/>
35+
<property key="labeling/bufferColorR" value="255"/>
36+
<property key="labeling/bufferDraw" value="false"/>
37+
<property key="labeling/bufferJoinStyle" value="64"/>
38+
<property key="labeling/bufferNoFill" value="false"/>
39+
<property key="labeling/bufferSize" value="1"/>
40+
<property key="labeling/bufferSizeInMapUnits" value="false"/>
41+
<property key="labeling/bufferSizeMapUnitMaxScale" value="0"/>
42+
<property key="labeling/bufferSizeMapUnitMinScale" value="0"/>
43+
<property key="labeling/bufferTransp" value="0"/>
44+
<property key="labeling/centroidWhole" value="false"/>
45+
<property key="labeling/decimals" value="3"/>
46+
<property key="labeling/displayAll" value="false"/>
47+
<property key="labeling/dist" value="0"/>
48+
<property key="labeling/distInMapUnits" value="false"/>
49+
<property key="labeling/distMapUnitMaxScale" value="0"/>
50+
<property key="labeling/distMapUnitMinScale" value="0"/>
51+
<property key="labeling/enabled" value="false"/>
52+
<property key="labeling/fieldName" value=""/>
53+
<property key="labeling/fontBold" value="true"/>
54+
<property key="labeling/fontCapitals" value="0"/>
55+
<property key="labeling/fontFamily" value="Ubuntu"/>
56+
<property key="labeling/fontItalic" value="true"/>
57+
<property key="labeling/fontLetterSpacing" value="0"/>
58+
<property key="labeling/fontLimitPixelSize" value="false"/>
59+
<property key="labeling/fontMaxPixelSize" value="10000"/>
60+
<property key="labeling/fontMinPixelSize" value="3"/>
61+
<property key="labeling/fontSize" value="11"/>
62+
<property key="labeling/fontSizeInMapUnits" value="false"/>
63+
<property key="labeling/fontSizeMapUnitMaxScale" value="0"/>
64+
<property key="labeling/fontSizeMapUnitMinScale" value="0"/>
65+
<property key="labeling/fontStrikeout" value="false"/>
66+
<property key="labeling/fontUnderline" value="false"/>
67+
<property key="labeling/fontWeight" value="75"/>
68+
<property key="labeling/fontWordSpacing" value="0"/>
69+
<property key="labeling/formatNumbers" value="false"/>
70+
<property key="labeling/isExpression" value="false"/>
71+
<property key="labeling/labelOffsetInMapUnits" value="true"/>
72+
<property key="labeling/labelOffsetMapUnitMaxScale" value="0"/>
73+
<property key="labeling/labelOffsetMapUnitMinScale" value="0"/>
74+
<property key="labeling/labelPerPart" value="false"/>
75+
<property key="labeling/leftDirectionSymbol" value="&lt;"/>
76+
<property key="labeling/limitNumLabels" value="false"/>
77+
<property key="labeling/maxCurvedCharAngleIn" value="20"/>
78+
<property key="labeling/maxCurvedCharAngleOut" value="-20"/>
79+
<property key="labeling/maxNumLabels" value="2000"/>
80+
<property key="labeling/mergeLines" value="false"/>
81+
<property key="labeling/minFeatureSize" value="0"/>
82+
<property key="labeling/multilineAlign" value="0"/>
83+
<property key="labeling/multilineHeight" value="1"/>
84+
<property key="labeling/namedStyle" value="Bold Italic"/>
85+
<property key="labeling/obstacle" value="true"/>
86+
<property key="labeling/placeDirectionSymbol" value="0"/>
87+
<property key="labeling/placement" value="0"/>
88+
<property key="labeling/placementFlags" value="0"/>
89+
<property key="labeling/plussign" value="false"/>
90+
<property key="labeling/preserveRotation" value="true"/>
91+
<property key="labeling/previewBkgrdColor" value="#ffffff"/>
92+
<property key="labeling/priority" value="5"/>
93+
<property key="labeling/quadOffset" value="4"/>
94+
<property key="labeling/reverseDirectionSymbol" value="false"/>
95+
<property key="labeling/rightDirectionSymbol" value=">"/>
96+
<property key="labeling/scaleMax" value="10000000"/>
97+
<property key="labeling/scaleMin" value="1"/>
98+
<property key="labeling/scaleVisibility" value="false"/>
99+
<property key="labeling/shadowBlendMode" value="6"/>
100+
<property key="labeling/shadowColorB" value="0"/>
101+
<property key="labeling/shadowColorG" value="0"/>
102+
<property key="labeling/shadowColorR" value="0"/>
103+
<property key="labeling/shadowDraw" value="false"/>
104+
<property key="labeling/shadowOffsetAngle" value="135"/>
105+
<property key="labeling/shadowOffsetDist" value="1"/>
106+
<property key="labeling/shadowOffsetGlobal" value="true"/>
107+
<property key="labeling/shadowOffsetMapUnitMaxScale" value="0"/>
108+
<property key="labeling/shadowOffsetMapUnitMinScale" value="0"/>
109+
<property key="labeling/shadowOffsetUnits" value="1"/>
110+
<property key="labeling/shadowRadius" value="1.5"/>
111+
<property key="labeling/shadowRadiusAlphaOnly" value="false"/>
112+
<property key="labeling/shadowRadiusMapUnitMaxScale" value="0"/>
113+
<property key="labeling/shadowRadiusMapUnitMinScale" value="0"/>
114+
<property key="labeling/shadowRadiusUnits" value="1"/>
115+
<property key="labeling/shadowScale" value="100"/>
116+
<property key="labeling/shadowTransparency" value="30"/>
117+
<property key="labeling/shadowUnder" value="0"/>
118+
<property key="labeling/shapeBlendMode" value="0"/>
119+
<property key="labeling/shapeBorderColorA" value="255"/>
120+
<property key="labeling/shapeBorderColorB" value="128"/>
121+
<property key="labeling/shapeBorderColorG" value="128"/>
122+
<property key="labeling/shapeBorderColorR" value="128"/>
123+
<property key="labeling/shapeBorderWidth" value="0"/>
124+
<property key="labeling/shapeBorderWidthMapUnitMaxScale" value="0"/>
125+
<property key="labeling/shapeBorderWidthMapUnitMinScale" value="0"/>
126+
<property key="labeling/shapeBorderWidthUnits" value="1"/>
127+
<property key="labeling/shapeDraw" value="false"/>
128+
<property key="labeling/shapeFillColorA" value="255"/>
129+
<property key="labeling/shapeFillColorB" value="255"/>
130+
<property key="labeling/shapeFillColorG" value="255"/>
131+
<property key="labeling/shapeFillColorR" value="255"/>
132+
<property key="labeling/shapeJoinStyle" value="64"/>
133+
<property key="labeling/shapeOffsetMapUnitMaxScale" value="0"/>
134+
<property key="labeling/shapeOffsetMapUnitMinScale" value="0"/>
135+
<property key="labeling/shapeOffsetUnits" value="1"/>
136+
<property key="labeling/shapeOffsetX" value="0"/>
137+
<property key="labeling/shapeOffsetY" value="0"/>
138+
<property key="labeling/shapeRadiiMapUnitMaxScale" value="0"/>
139+
<property key="labeling/shapeRadiiMapUnitMinScale" value="0"/>
140+
<property key="labeling/shapeRadiiUnits" value="1"/>
141+
<property key="labeling/shapeRadiiX" value="0"/>
142+
<property key="labeling/shapeRadiiY" value="0"/>
143+
<property key="labeling/shapeRotation" value="0"/>
144+
<property key="labeling/shapeRotationType" value="0"/>
145+
<property key="labeling/shapeSVGFile" value=""/>
146+
<property key="labeling/shapeSizeMapUnitMaxScale" value="0"/>
147+
<property key="labeling/shapeSizeMapUnitMinScale" value="0"/>
148+
<property key="labeling/shapeSizeType" value="0"/>
149+
<property key="labeling/shapeSizeUnits" value="1"/>
150+
<property key="labeling/shapeSizeX" value="0"/>
151+
<property key="labeling/shapeSizeY" value="0"/>
152+
<property key="labeling/shapeTransparency" value="0"/>
153+
<property key="labeling/shapeType" value="0"/>
154+
<property key="labeling/textColorA" value="255"/>
155+
<property key="labeling/textColorB" value="0"/>
156+
<property key="labeling/textColorG" value="0"/>
157+
<property key="labeling/textColorR" value="0"/>
158+
<property key="labeling/textTransp" value="0"/>
159+
<property key="labeling/upsidedownLabels" value="0"/>
160+
<property key="labeling/wrapChar" value=""/>
161+
<property key="labeling/xOffset" value="0"/>
162+
<property key="labeling/yOffset" value="0"/>
163+
</customproperties>
164+
<blendMode>0</blendMode>
165+
<featureBlendMode>0</featureBlendMode>
166+
<layerTransparency>0</layerTransparency>
167+
<displayfield>Name</displayfield>
168+
<label>0</label>
169+
<labelattributes>
170+
<label fieldname="" text="Label"/>
171+
<family fieldname="" name="Lucida Grande"/>
172+
<size fieldname="" units="pt" value="12"/>
173+
<bold fieldname="" on="0"/>
174+
<italic fieldname="" on="0"/>
175+
<underline fieldname="" on="0"/>
176+
<strikeout fieldname="" on="0"/>
177+
<color fieldname="" red="0" blue="0" green="0"/>
178+
<x fieldname=""/>
179+
<y fieldname=""/>
180+
<offset x="0" y="0" units="pt" yfieldname="" xfieldname=""/>
181+
<angle fieldname="" value="0" auto="0"/>
182+
<alignment fieldname="" value="center"/>
183+
<buffercolor fieldname="" red="255" blue="255" green="255"/>
184+
<buffersize fieldname="" units="pt" value="1"/>
185+
<bufferenabled fieldname="" on=""/>
186+
<multilineenabled fieldname="" on=""/>
187+
<selectedonly on=""/>
188+
</labelattributes>
189+
<edittypes>
190+
<edittype labelontop="0" editable="1" type="0" name="Name"/>
191+
<edittype labelontop="0" editable="1" type="0" name="Value"/>
192+
</edittypes>
193+
<editform></editform>
194+
<editforminit></editforminit>
195+
<featformsuppress>0</featformsuppress>
196+
<annotationform></annotationform>
197+
<editorlayout>generatedlayout</editorlayout>
198+
<excludeAttributesWMS/>
199+
<excludeAttributesWFS/>
200+
<attributeactions/>
201+
</qgis>

‎tests/testdata/polys_overlapping.dbf

1.22 KB
Binary file not shown.

‎tests/testdata/polys_overlapping.prj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
GEOGCS["WGS 84",DATUM["unknown",SPHEROID["WGS84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]]

‎tests/testdata/polys_overlapping.shp

10.7 KB
Binary file not shown.

‎tests/testdata/polys_overlapping.shx

188 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)
Please sign in to comment.