29
29
30
30
QgsInvertedPolygonRenderer::QgsInvertedPolygonRenderer ( const QgsFeatureRendererV2* subRenderer )
31
31
: QgsFeatureRendererV2( " invertedPolygonRenderer" )
32
+ , mPreprocessingEnabled( false )
32
33
{
33
34
if ( subRenderer )
34
35
{
@@ -68,7 +69,6 @@ void QgsInvertedPolygonRenderer::startRender( QgsRenderContext& context, const Q
68
69
return ;
69
70
}
70
71
71
- mSubRenderer ->startRender ( context, fields );
72
72
mFeaturesCategoryMap .clear ();
73
73
mFeatureDecorations .clear ();
74
74
mFields = fields;
@@ -78,36 +78,52 @@ void QgsInvertedPolygonRenderer::startRender( QgsRenderContext& context, const Q
78
78
// It must be computed in the destination CRS if reprojection is enabled.
79
79
const QgsMapToPixel& mtp ( context.mapToPixel () );
80
80
81
+ if ( !context.painter () )
82
+ {
83
+ return ;
84
+ }
85
+
81
86
// convert viewport to dest CRS
82
87
QRect e ( context.painter ()->viewport () );
83
88
// 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 );
85
90
QgsPolyline exteriorRing;
86
91
exteriorRing << mtp.toMapCoordinates ( e.topLeft () );
87
92
exteriorRing << mtp.toMapCoordinates ( e.topRight () );
88
93
exteriorRing << mtp.toMapCoordinates ( e.bottomRight () );
89
94
exteriorRing << mtp.toMapCoordinates ( e.bottomLeft () );
90
95
exteriorRing << mtp.toMapCoordinates ( e.topLeft () );
91
96
92
- mTransform = context.coordinateTransform ();
97
+ // copy the rendering context
98
+ mContext = context;
99
+
93
100
// If reprojection is enabled, we must reproject during renderFeature
94
101
// and act as if there is no reprojection
95
102
// If we don't do that, there is no need to have a simple rectangular extent
96
103
// that covers the whole screen
97
104
// (a rectangle in the destCRS cannot be expressed as valid coordinates in the sourceCRS in general)
98
- if ( mTransform )
105
+ if ( context. coordinateTransform () )
99
106
{
100
107
// 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 ?
102
113
}
103
114
104
115
mExtentPolygon .clear ();
105
116
mExtentPolygon .append ( exteriorRing );
117
+
118
+ mSubRenderer ->startRender ( mContext , fields );
106
119
}
107
120
108
121
bool QgsInvertedPolygonRenderer::renderFeature ( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker )
109
122
{
110
- Q_UNUSED ( context );
123
+ if ( !context.painter () )
124
+ {
125
+ return false ;
126
+ }
111
127
112
128
// store this feature as a feature to render with decoration if needed
113
129
if ( selected || drawVertexMarker )
@@ -153,33 +169,32 @@ bool QgsInvertedPolygonRenderer::renderFeature( QgsFeature& feature, QgsRenderCo
153
169
return false ;
154
170
}
155
171
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 ) )
166
173
{
167
174
// the exterior ring must be a square in the destination CRS
168
175
CombinedFeature cFeat;
169
176
cFeat.multiPolygon .append ( mExtentPolygon );
170
177
// store the first feature
171
178
cFeat.feature = feature;
172
- mFeaturesCategoryMap .insert ( catId, cFeat );
179
+ mSymbolCategories .insert ( catId, mSymbolCategories .count () );
180
+ mFeaturesCategoryMap .append ( cFeat );
173
181
}
174
182
175
- // update the gometry
176
- CombinedFeature& cFeat = mFeaturesCategoryMap [catId];
183
+ // update the geometry
184
+ CombinedFeature& cFeat = mFeaturesCategoryMap [ mSymbolCategories [ catId] ];
177
185
QgsMultiPolygon multi;
178
186
QgsGeometry* geom = feature.geometry ();
179
187
if ( !geom )
180
188
{
181
189
return false ;
182
190
}
191
+
192
+ const QgsCoordinateTransform* xform = context.coordinateTransform ();
193
+ if ( xform )
194
+ {
195
+ geom->transform ( *xform );
196
+ }
197
+
183
198
if (( geom->wkbType () == QGis::WKBPolygon ) ||
184
199
( geom->wkbType () == QGis::WKBPolygon25D ) )
185
200
{
@@ -191,43 +206,49 @@ bool QgsInvertedPolygonRenderer::renderFeature( QgsFeature& feature, QgsRenderCo
191
206
multi = geom->asMultiPolygon ();
192
207
}
193
208
194
- for ( int i = 0 ; i < multi. size (); i++ )
209
+ if ( mPreprocessingEnabled )
195
210
{
196
- // add the exterior ring as interior ring to the first polygon
197
- if ( mTransform )
211
+ // preprocessing
212
+ if ( ! cFeat. feature . geometry () )
198
213
{
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 ) );
206
216
}
207
217
else
208
218
{
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 () )
216
222
{
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 );
224
224
}
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++ )
226
247
{
248
+ QgsPolygon new_poly;
227
249
new_poly.append ( multi[i][j] );
250
+ cFeat.multiPolygon .append ( new_poly );
228
251
}
229
-
230
- cFeat.multiPolygon .append ( new_poly );
231
252
}
232
253
}
233
254
return true ;
@@ -239,12 +260,33 @@ void QgsInvertedPolygonRenderer::stopRender( QgsRenderContext& context )
239
260
{
240
261
return ;
241
262
}
263
+ if ( !context.painter () )
264
+ {
265
+ return ;
266
+ }
242
267
243
268
for ( FeatureCategoryMap::iterator cit = mFeaturesCategoryMap .begin (); cit != mFeaturesCategoryMap .end (); ++cit )
244
269
{
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 );
248
290
}
249
291
250
292
// when no features are visible, we still have to draw the exterior rectangle
@@ -256,22 +298,16 @@ void QgsInvertedPolygonRenderer::stopRender( QgsRenderContext& context )
256
298
// empty feature with default attributes
257
299
QgsFeature feat ( mFields );
258
300
feat.setGeometry ( QgsGeometry::fromPolygon ( mExtentPolygon ) );
259
- mSubRenderer ->renderFeature ( feat, context );
301
+ mSubRenderer ->renderFeature ( feat, mContext );
260
302
}
261
303
262
304
// draw feature decorations
263
305
foreach ( FeatureDecoration deco, mFeatureDecorations )
264
306
{
265
- mSubRenderer ->renderFeature ( deco.feature , context , deco.layer , deco.selected , deco.drawMarkers );
307
+ mSubRenderer ->renderFeature ( deco.feature , mContext , deco.layer , deco.selected , deco.drawMarkers );
266
308
}
267
309
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 );
275
311
}
276
312
277
313
QString QgsInvertedPolygonRenderer::dump () const
@@ -285,12 +321,17 @@ QString QgsInvertedPolygonRenderer::dump() const
285
321
286
322
QgsFeatureRendererV2* QgsInvertedPolygonRenderer::clone ()
287
323
{
324
+ QgsInvertedPolygonRenderer* newRenderer;
288
325
if ( mSubRenderer .isNull () )
289
326
{
290
- return new QgsInvertedPolygonRenderer ( 0 );
327
+ newRenderer = new QgsInvertedPolygonRenderer ( 0 );
328
+ }
329
+ else
330
+ {
331
+ newRenderer = new QgsInvertedPolygonRenderer ( mSubRenderer ->clone () );
291
332
}
292
- // else
293
- return new QgsInvertedPolygonRenderer ( mSubRenderer -> clone () ) ;
333
+ newRenderer-> setPreprocessingEnabled ( preprocessingEnabled () );
334
+ return newRenderer ;
294
335
}
295
336
296
337
QgsFeatureRendererV2* QgsInvertedPolygonRenderer::create ( QDomElement& element )
@@ -302,13 +343,15 @@ QgsFeatureRendererV2* QgsInvertedPolygonRenderer::create( QDomElement& element )
302
343
{
303
344
r->setEmbeddedRenderer ( QgsFeatureRendererV2::load ( embeddedRendererElem ) );
304
345
}
346
+ r->setPreprocessingEnabled ( element.attribute ( " preprocessing" , " 0" ).toInt () == 1 );
305
347
return r;
306
348
}
307
349
308
350
QDomElement QgsInvertedPolygonRenderer::save ( QDomDocument& doc )
309
351
{
310
352
QDomElement rendererElem = doc.createElement ( RENDERER_TAG_NAME );
311
353
rendererElem.setAttribute ( " type" , " invertedPolygonRenderer" );
354
+ rendererElem.setAttribute ( " preprocessing" , preprocessingEnabled () ? " 1" : " 0" );
312
355
313
356
if ( mSubRenderer )
314
357
{
@@ -390,3 +433,4 @@ bool QgsInvertedPolygonRenderer::willRenderFeature( QgsFeature& feat )
390
433
}
391
434
return mSubRenderer ->willRenderFeature ( feat );
392
435
}
436
+
0 commit comments