Skip to content

Commit 83d070e

Browse files
committedOct 27, 2014
Fix #11489 (crash with force point inside polygon in centroid fill style)
This isn't actually a very good fix. The issue in the "maptopixel" simplification is still there, it is just less obvious, while sacrificing a bit of QgsGeometry correctness (like the fact that linear ring should have >= 4 points) Along the way I have added some comments that may help others decode why the code does things it does.
1 parent bb85761 commit 83d070e

File tree

2 files changed

+46
-24
lines changed

2 files changed

+46
-24
lines changed
 

‎src/core/qgsgeometry.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,8 @@ static GEOSGeometry *createGeosLinearRing( const QgsPolyline& polyline )
284284
}
285285
else
286286
{
287+
// XXX [MD] this exception should not be silenced!
288+
// this is here just because maptopixel simplification can return invalid linear rings
287289
if ( polyline.count() == 3 ) //-> Avoid 'GEOS::IllegalArgumentException: Invalid number of points in LinearRing found 3 - must be 0 or >= 4'
288290
return 0;
289291

@@ -359,7 +361,16 @@ static GEOSGeometry *createGeosPolygon( const QgsPolygon& polygon )
359361
for ( int i = 0; i < polygon.count(); i++ )
360362
{
361363
GEOSGeometry *ring = createGeosLinearRing( polygon[i] );
362-
if ( ring ) geoms << ring;
364+
if ( !ring )
365+
{
366+
// something went really wrong - exit
367+
for ( int j = 0; j < geoms.count(); j++ )
368+
GEOSGeom_destroy_r( geosinit.ctxt, geoms[j] );
369+
// XXX [MD] we just silently return here - but we shouldn't
370+
// this is just because maptopixel simplification can return invalid linear rings
371+
return 0;
372+
}
373+
geoms << ring;
363374
}
364375

365376
return createGeosPolygon( geoms );

‎src/core/qgsmaptopixelgeometrysimplifier.cpp

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ inline static QgsRectangle calculateBoundingBox( QGis::WkbType wkbType, unsigned
6161
}
6262

6363
//! Generalize the WKB-geometry using the BBOX of the original geometry
64-
inline static bool generalizeWkbGeometry(
64+
static bool generalizeWkbGeometryByBoundingBox(
6565
QGis::WkbType wkbType,
6666
unsigned char* sourceWkb, size_t sourceWkbSize,
6767
unsigned char* targetWkb, size_t& targetWkbSize,
@@ -169,7 +169,7 @@ bool QgsMapToPixelSimplifier::simplifyWkbGeometry(
169169
if (( simplifyFlags & QgsMapToPixelSimplifier::SimplifyEnvelope ) &&
170170
isGeneralizableByMapBoundingBox( envelope, map2pixelTol ) )
171171
{
172-
isGeneralizable = generalizeWkbGeometry( wkbType, sourceWkb, sourceWkbSize, targetWkb, targetWkbSize, envelope, writeHeader );
172+
isGeneralizable = generalizeWkbGeometryByBoundingBox( wkbType, sourceWkb, sourceWkbSize, targetWkb, targetWkbSize, envelope, writeHeader );
173173
if ( isGeneralizable )
174174
return true;
175175
}
@@ -222,8 +222,8 @@ bool QgsMapToPixelSimplifier::simplifyWkbGeometry(
222222
double* ptr = ( double* )targetWkb;
223223
map2pixelTol *= map2pixelTol; //-> Use mappixelTol for 'LengthSquare' calculations.
224224

225-
bool isaUngenerizableSegment;
226-
bool hasUngenerizableSegments = false; //-> To avoid replace the simplified geometry by its BBOX when there are 'long' segments.
225+
bool isLongSegment;
226+
bool hasLongSegments = false; //-> To avoid replace the simplified geometry by its BBOX when there are 'long' segments.
227227

228228
// Check whether the LinearRing is really closed.
229229
if ( isaLinearRing )
@@ -249,44 +249,55 @@ bool QgsMapToPixelSimplifier::simplifyWkbGeometry(
249249
memcpy( &x, sourceWkb, sizeof( double ) ); sourceWkb += sizeOfDoubleX;
250250
memcpy( &y, sourceWkb, sizeof( double ) ); sourceWkb += sizeOfDoubleY;
251251

252-
isaUngenerizableSegment = false;
252+
isLongSegment = false;
253253

254254
if ( i == 0 ||
255255
!isGeneralizable ||
256-
( isaUngenerizableSegment = ( calculateLengthSquared2D( x, y, lastX, lastY ) > map2pixelTol ) ) ||
256+
( isLongSegment = ( calculateLengthSquared2D( x, y, lastX, lastY ) > map2pixelTol ) ) ||
257257
( !isaLinearRing && ( i == 1 || i >= numPoints - 2 ) ) )
258258
{
259259
memcpy( ptr, &x, sizeof( double ) ); lastX = x; ptr++;
260260
memcpy( ptr, &y, sizeof( double ) ); lastY = y; ptr++;
261261
numTargetPoints++;
262262

263-
if ( isaUngenerizableSegment && !hasUngenerizableSegments )
264-
{
265-
hasUngenerizableSegments = true;
266-
}
263+
hasLongSegments |= isLongSegment;
267264
}
268265

269266
r.combineExtentWith( x, y );
270267
}
271268
targetWkb = wkb2 + 4;
272269

273-
// Fix the topology of the geometry
274-
if ( numTargetPoints <= ( isaLinearRing ? 2 : 1 ) && !hasUngenerizableSegments )
270+
if ( numTargetPoints < ( isaLinearRing ? 4 : 2 ) )
275271
{
276-
unsigned char* targetTempWkb = targetWkb;
277-
int targetWkbTempSize = targetWkbSize;
278-
279-
sourceWkb = sourcePrevWkb;
280-
targetWkb = targetPrevWkb;
281-
targetWkbSize = targetWkbPrevSize;
282-
if ( generalizeWkbGeometry( wkbType, sourceWkb, sourceWkbSize, targetWkb, targetWkbSize, r, writeHeader ) )
283-
return true;
284-
285-
targetWkb = targetTempWkb;
286-
targetWkbSize = targetWkbTempSize;
272+
// we simplified the geometry too much!
273+
if ( !hasLongSegments )
274+
{
275+
// approximate the geometry's shape by its bounding box
276+
// (rect for linear ring / one segment for line string)
277+
unsigned char* targetTempWkb = targetWkb;
278+
int targetWkbTempSize = targetWkbSize;
279+
280+
sourceWkb = sourcePrevWkb;
281+
targetWkb = targetPrevWkb;
282+
targetWkbSize = targetWkbPrevSize;
283+
if ( generalizeWkbGeometryByBoundingBox( wkbType, sourceWkb, sourceWkbSize, targetWkb, targetWkbSize, r, writeHeader ) )
284+
return true;
285+
286+
targetWkb = targetTempWkb;
287+
targetWkbSize = targetWkbTempSize;
288+
}
289+
else
290+
{
291+
// Bad luck! The simplified geometry is invalid and approximation by bounding box
292+
// would create artifacts due to long segments. Worst of all, we may have overwritten
293+
// the original coordinates by the simplified ones (source and target WKB ptr can be the same)
294+
// so we cannot even undo the changes here. We will return invalid geometry and hope that
295+
// other pieces of QGIS will survive that :-/
296+
}
287297
}
288298
if ( isaLinearRing )
289299
{
300+
// make sure we keep the linear ring closed
290301
memcpy( &x, targetWkb + 0, sizeof( double ) );
291302
memcpy( &y, targetWkb + sizeof( double ), sizeof( double ) );
292303
if ( lastX != x || lastY != y )

0 commit comments

Comments
 (0)