Skip to content

Commit 03dfef8

Browse files
committedJun 13, 2014
Merge pull request #1439 from Oslandia/fix_inverted
Inverted polygons: bug fixes
2 parents 95687a2 + b2782ee commit 03dfef8

File tree

5 files changed

+99
-69
lines changed

5 files changed

+99
-69
lines changed
 

‎python/core/qgsgeometry.sip

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,3 +442,12 @@ class QgsGeometry
442442
void validateGeometry( QList<QgsGeometry::Error> &errors /Out/ );
443443
}; // class QgsGeometry
444444

445+
/** namespace where QgsGeometry-based algorithms lie */
446+
namespace QgsGeometryAlgorithms
447+
{
448+
/** compute the unary union on a list of geometries. May be faster than an iterative union on a set of geometries.
449+
@param geometryList a list of QgsGeometry* as input
450+
@returns the new computed QgsGeometry, or null
451+
*/
452+
QgsGeometry* unaryUnion( const QList<QgsGeometry*>& geometryList );
453+
};

‎src/core/qgsgeometry.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6396,3 +6396,23 @@ QgsGeometry* QgsGeometry::convertToPolygon( bool destMultipart )
63966396
return 0;
63976397
}
63986398
}
6399+
6400+
namespace QgsGeometryAlgorithms
6401+
{
6402+
6403+
QgsGeometry* unaryUnion( const QList<QgsGeometry*>& geometryList )
6404+
{
6405+
QList<GEOSGeometry*> geoms;
6406+
foreach( QgsGeometry* g, geometryList )
6407+
{
6408+
// const cast: it is ok here, since the pointers will only be used to be stored
6409+
// in a list for a call to union
6410+
geoms.append( const_cast<GEOSGeometry*>(g->asGeos()) );
6411+
}
6412+
GEOSGeometry* unioned = _makeUnion( geoms );
6413+
QgsGeometry *ret = new QgsGeometry();
6414+
ret->fromGeos( unioned );
6415+
return ret;
6416+
}
6417+
6418+
}// QgsGeometryAlgorithms

‎src/core/qgsgeometry.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,4 +679,14 @@ class CORE_EXPORT QgsConstWkbPtr
679679
inline operator const unsigned char *() const { return mP; }
680680
};
681681

682+
/** namespace where QgsGeometry-based algorithms lie */
683+
namespace QgsGeometryAlgorithms
684+
{
685+
/** compute the unary union on a list of geometries. May be faster than an iterative union on a set of geometries.
686+
@param geometryList a list of QgsGeometry* as input
687+
@returns the new computed QgsGeometry, or null
688+
*/
689+
QgsGeometry* unaryUnion( const QList<QgsGeometry*>& geometryList );
690+
}
691+
682692
#endif

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

Lines changed: 58 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ void QgsInvertedPolygonRenderer::startRender( QgsRenderContext& context, const Q
6969
return;
7070
}
7171

72+
// first call start render on the sub renderer
73+
mSubRenderer->startRender( context, fields );
74+
7275
mFeaturesCategories.clear();
7376
mFeatureDecorations.clear();
7477
mFields = fields;
@@ -114,8 +117,6 @@ void QgsInvertedPolygonRenderer::startRender( QgsRenderContext& context, const Q
114117

115118
mExtentPolygon.clear();
116119
mExtentPolygon.append( exteriorRing );
117-
118-
mSubRenderer->startRender( mContext, fields );
119120
}
120121

121122
bool QgsInvertedPolygonRenderer::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker )
@@ -171,9 +172,7 @@ bool QgsInvertedPolygonRenderer::renderFeature( QgsFeature& feature, QgsRenderCo
171172

172173
if ( ! mSymbolCategories.contains( catId ) )
173174
{
174-
// the exterior ring must be a square in the destination CRS
175175
CombinedFeature cFeat;
176-
cFeat.multiPolygon.append( mExtentPolygon );
177176
// store the first feature
178177
cFeat.feature = feature;
179178
mSymbolCategories.insert( catId, mSymbolCategories.count() );
@@ -182,11 +181,11 @@ bool QgsInvertedPolygonRenderer::renderFeature( QgsFeature& feature, QgsRenderCo
182181

183182
// update the geometry
184183
CombinedFeature& cFeat = mFeaturesCategories[ mSymbolCategories[catId] ];
185-
QgsGeometry* geom = feature.geometry();
186-
if ( !geom )
184+
if ( !feature.geometry() )
187185
{
188186
return false;
189187
}
188+
QScopedPointer<QgsGeometry> geom( new QgsGeometry( *feature.geometry() ) );
190189

191190
const QgsCoordinateTransform* xform = context.coordinateTransform();
192191
if ( xform )
@@ -196,61 +195,15 @@ bool QgsInvertedPolygonRenderer::renderFeature( QgsFeature& feature, QgsRenderCo
196195

197196
if ( mPreprocessingEnabled )
198197
{
199-
// preprocessing
200-
if ( ! cFeat.feature.geometry() )
198+
// fix the polygon if it is not valid
199+
if ( ! geom->isGeosValid() )
201200
{
202-
// first feature: add the current geometry
203-
cFeat.feature.setGeometry( new QgsGeometry( *geom ) );
204-
}
205-
else
206-
{
207-
// other features: combine them (union)
208-
QgsGeometry* combined = cFeat.feature.geometry()->combine( geom );
209-
if ( combined && combined->isGeosValid() )
210-
{
211-
cFeat.feature.setGeometry( combined );
212-
}
201+
geom.reset( geom->buffer( 0, 0 ) );
213202
}
214203
}
215-
else
216-
{
217-
// No preprocessing involved.
218-
// We build here a "reversed" geometry of all the polygons
219-
//
220-
// The final geometry is a multipolygon F, with :
221-
// * the first polygon of F having the current extent as its exterior ring
222-
// * each polygon's exterior ring is added as interior ring of the first polygon of F
223-
// * each polygon's interior ring is added as new polygons in F
224-
//
225-
// No validity check is done, on purpose, it will be very slow and painting
226-
// operations do not need geometries to be valid
227-
228-
QgsMultiPolygon multi;
229-
if (( geom->wkbType() == QGis::WKBPolygon ) ||
230-
( geom->wkbType() == QGis::WKBPolygon25D ) )
231-
{
232-
multi.append( geom->asPolygon() );
233-
}
234-
else if (( geom->wkbType() == QGis::WKBMultiPolygon ) ||
235-
( geom->wkbType() == QGis::WKBMultiPolygon25D ) )
236-
{
237-
multi = geom->asMultiPolygon();
238-
}
204+
// add the geometry to the list of geometries for this feature
205+
cFeat.geometries.append( geom.take() );
239206

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++ )
247-
{
248-
QgsPolygon new_poly;
249-
new_poly.append( multi[i][j] );
250-
cFeat.multiPolygon.append( new_poly );
251-
}
252-
}
253-
}
254207
return true;
255208
}
256209

@@ -267,23 +220,61 @@ void QgsInvertedPolygonRenderer::stopRender( QgsRenderContext& context )
267220

268221
for ( FeatureCategoryVector::iterator cit = mFeaturesCategories.begin(); cit != mFeaturesCategories.end(); ++cit )
269222
{
270-
QgsFeature feat( cit->feature );
271-
if ( !mPreprocessingEnabled )
223+
QgsFeature& feat = cit->feature;
224+
if ( mPreprocessingEnabled )
272225
{
273-
// no preprocessing - the final polygon has already been prepared
274-
feat.setGeometry( QgsGeometry::fromMultiPolygon( cit->multiPolygon ) );
226+
// compute the unary union on the polygons
227+
QScopedPointer<QgsGeometry> unioned( QgsGeometryAlgorithms::unaryUnion( cit->geometries ) );
228+
// compute the difference with the extent
229+
QScopedPointer<QgsGeometry> rect( QgsGeometry::fromPolygon( mExtentPolygon ) );
230+
QgsGeometry *final = rect->difference( const_cast<QgsGeometry*>(unioned.data()) );
231+
if ( final )
232+
{
233+
feat.setGeometry( final );
234+
}
275235
}
276236
else
277237
{
278-
// preprocessing mode - we still have to invert (using difference)
279-
if ( feat.geometry() )
238+
// No preprocessing involved.
239+
// We build here a "reversed" geometry of all the polygons
240+
//
241+
// The final geometry is a multipolygon F, with :
242+
// * the first polygon of F having the current extent as its exterior ring
243+
// * each polygon's exterior ring is added as interior ring of the first polygon of F
244+
// * each polygon's interior ring is added as new polygons in F
245+
//
246+
// No validity check is done, on purpose, it will be very slow and painting
247+
// operations do not need geometries to be valid
248+
QgsMultiPolygon finalMulti;
249+
finalMulti.append( mExtentPolygon );
250+
foreach( QgsGeometry* geom, cit->geometries )
280251
{
281-
QScopedPointer<QgsGeometry> rect( QgsGeometry::fromPolygon( mExtentPolygon ) );
282-
QgsGeometry *final = rect->difference( feat.geometry() );
283-
if ( final )
252+
QgsMultiPolygon multi;
253+
if (( geom->wkbType() == QGis::WKBPolygon ) ||
254+
( geom->wkbType() == QGis::WKBPolygon25D ) )
255+
{
256+
multi.append( geom->asPolygon() );
257+
}
258+
else if (( geom->wkbType() == QGis::WKBMultiPolygon ) ||
259+
( geom->wkbType() == QGis::WKBMultiPolygon25D ) )
260+
{
261+
multi = geom->asMultiPolygon();
262+
}
263+
264+
for ( int i = 0; i < multi.size(); i++ )
284265
{
285-
feat.setGeometry( final );
266+
// add the exterior ring as interior ring to the first polygon
267+
finalMulti[0].append( multi[i][0] );
268+
269+
// add interior rings as new polygons
270+
for ( int j = 1; j < multi[i].size(); j++ )
271+
{
272+
QgsPolygon new_poly;
273+
new_poly.append( multi[i][j] );
274+
finalMulti.append( new_poly );
275+
}
286276
}
277+
feat.setGeometry( QgsGeometry::fromMultiPolygon( finalMulti ) );
287278
}
288279
}
289280
mSubRenderer->renderFeature( feat, mContext );

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ class CORE_EXPORT QgsInvertedPolygonRenderer : public QgsFeatureRendererV2
130130
/** Structure where the reversed geometry is built during renderFeature */
131131
struct CombinedFeature
132132
{
133-
QgsMultiPolygon multiPolygon; //< the final combined geometry
134-
QgsFeature feature; //< one feature (for attriute-based rendering)
133+
QList<QgsGeometry*> geometries; //< list of geometries
134+
QgsFeature feature; //< one feature (for attriute-based rendering)
135135
};
136136
typedef QVector<CombinedFeature> FeatureCategoryVector;
137137
/** where features are stored, based on the index of their symbol category @see mSymbolCategories */

0 commit comments

Comments
 (0)
Please sign in to comment.