Skip to content

Commit 8b53001

Browse files
committed
Merge pull request #1099 from ahuarte47/Issue_8725R-ST_simplify
Fix bug #8725R: fix collapsed geometries by ST_simplify function of postgis
2 parents 49a807a + 2baf403 commit 8b53001

9 files changed

+72
-32
lines changed

python/core/qgssimplifymethod.sip

-3
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ class QgsSimplifyMethod
2929
/** Gets the tolerance of simplification */
3030
double tolerance() const;
3131

32-
/** Returns the optimal tolerance for Douglas-Peucker simplification algorithms */
33-
double toleranceForDouglasPeuckerAlgorithms() const;
34-
3532
/** Sets whether the simplification executes after fetch the geometries from provider, otherwise it executes, when supported, in provider before fetch the geometries */
3633
void setForceLocalOptimization( bool localOptimization );
3734
/** Gets whether the simplification executes after fetch the geometries from provider, otherwise it executes, when supported, in provider before fetch the geometries */

src/core/qgsgeometry.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -4697,7 +4697,10 @@ bool QgsGeometry::exportWkbToGeos() const
46974697
}
46984698
sequence << QgsPoint( *x, *y );
46994699
}
4700-
lines << createGeosLineString( sequence );
4700+
4701+
// ignore invalid parts, it can come from ST_Simplify operations
4702+
if ( sequence.count() > 1 )
4703+
lines << createGeosLineString( sequence );
47014704
}
47024705
mGeos = createGeosCollection( GEOS_MULTILINESTRING, lines );
47034706
mDirtyGeos = false;

src/core/qgsmaptopixelgeometrysimplifier.cpp

+6-6
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
#include "qgsmaptopixelgeometrysimplifier.h"
1919
#include "qgsapplication.h"
2020

21-
QgsMapToPixelSimplifier::QgsMapToPixelSimplifier( int simplifyFlags, double map2pixelTol )
21+
QgsMapToPixelSimplifier::QgsMapToPixelSimplifier( int simplifyFlags, double tolerance )
2222
: mSimplifyFlags( simplifyFlags )
23-
, mMapToPixelTol( map2pixelTol )
23+
, mTolerance( tolerance )
2424
{
2525
}
2626
QgsMapToPixelSimplifier::~QgsMapToPixelSimplifier()
@@ -330,13 +330,13 @@ QgsGeometry* QgsMapToPixelSimplifier::simplify( QgsGeometry* geometry ) const
330330
unsigned char* wkb = ( unsigned char* )malloc( wkbSize );
331331
memcpy( wkb, geometry->asWkb(), wkbSize );
332332
g->fromWkb( wkb, wkbSize );
333-
simplifyGeometry( g, mSimplifyFlags, mMapToPixelTol );
333+
simplifyGeometry( g, mSimplifyFlags, mTolerance );
334334

335335
return g;
336336
}
337337

338338
//! Simplifies the geometry (Removing duplicated points) when is applied the specified map2pixel context
339-
bool QgsMapToPixelSimplifier::simplifyGeometry( QgsGeometry* geometry, int simplifyFlags, double map2pixelTol )
339+
bool QgsMapToPixelSimplifier::simplifyGeometry( QgsGeometry* geometry, int simplifyFlags, double tolerance )
340340
{
341341
size_t targetWkbSize = 0;
342342

@@ -351,7 +351,7 @@ bool QgsMapToPixelSimplifier::simplifyGeometry( QgsGeometry* geometry, int simpl
351351
size_t wkbSize = geometry->wkbSize( );
352352

353353
// Simplify the geometry rewriting temporally its WKB-stream for saving calloc's.
354-
if ( simplifyWkbGeometry( simplifyFlags, wkbType, wkb, wkbSize, wkb, targetWkbSize, envelope, map2pixelTol ) )
354+
if ( simplifyWkbGeometry( simplifyFlags, wkbType, wkb, wkbSize, wkb, targetWkbSize, envelope, tolerance ) )
355355
{
356356
unsigned char* targetWkb = ( unsigned char* )malloc( targetWkbSize );
357357
memcpy( targetWkb, wkb, targetWkbSize );
@@ -364,5 +364,5 @@ bool QgsMapToPixelSimplifier::simplifyGeometry( QgsGeometry* geometry, int simpl
364364
//! Simplifies the geometry (Removing duplicated points) when is applied the specified map2pixel context
365365
bool QgsMapToPixelSimplifier::simplifyGeometry( QgsGeometry* geometry ) const
366366
{
367-
return simplifyGeometry( geometry, mSimplifyFlags, mMapToPixelTol );
367+
return simplifyGeometry( geometry, mSimplifyFlags, mTolerance );
368368
}

src/core/qgsmaptopixelgeometrysimplifier.h

+5-5
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
class CORE_EXPORT QgsMapToPixelSimplifier : public QgsAbstractGeometrySimplifier
3333
{
3434
public:
35-
QgsMapToPixelSimplifier( int simplifyFlags, double map2pixelTol );
35+
QgsMapToPixelSimplifier( int simplifyFlags, double tolerance );
3636
virtual ~QgsMapToPixelSimplifier();
3737

3838
//! Applicable simplification flags
@@ -51,8 +51,8 @@ class CORE_EXPORT QgsMapToPixelSimplifier : public QgsAbstractGeometrySimplifier
5151
//! Current simplification flags
5252
int mSimplifyFlags;
5353

54-
//! Map2Pixel tolerance for the simplification
55-
double mMapToPixelTol;
54+
//! Distance tolerance for the simplification
55+
double mTolerance;
5656

5757
//! Returns the squared 2D-distance of the vector defined by the two points specified
5858
static float calculateLengthSquared2D( double x1, double y1, double x2, double y2 );
@@ -73,10 +73,10 @@ class CORE_EXPORT QgsMapToPixelSimplifier : public QgsAbstractGeometrySimplifier
7373
static bool canbeGeneralizedByMapBoundingBox( const QgsRectangle& envelope, double map2pixelTol );
7474

7575
//! Returns whether the envelope can be replaced by its BBOX when is applied the specified map2pixel context
76-
inline bool canbeGeneralizedByMapBoundingBox( const QgsRectangle& envelope ) const { return canbeGeneralizedByMapBoundingBox( envelope, mMapToPixelTol ); }
76+
inline bool canbeGeneralizedByMapBoundingBox( const QgsRectangle& envelope ) const { return canbeGeneralizedByMapBoundingBox( envelope, mTolerance ); }
7777

7878
//! Simplifies the geometry when is applied the specified map2pixel context
79-
static bool simplifyGeometry( QgsGeometry* geometry, int simplifyFlags, double map2pixelTol );
79+
static bool simplifyGeometry( QgsGeometry* geometry, int simplifyFlags, double tolerance );
8080

8181
};
8282

src/core/qgssimplifymethod.cpp

-6
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,6 @@ void QgsSimplifyMethod::setForceLocalOptimization( bool localOptimization )
5454
mForceLocalOptimization = localOptimization;
5555
}
5656

57-
double QgsSimplifyMethod::toleranceForDouglasPeuckerAlgorithms() const
58-
{
59-
//TODO: define more precise value, now, it is experimental but conservative
60-
return mTolerance / 5.0;
61-
}
62-
6357
QgsAbstractGeometrySimplifier* QgsSimplifyMethod::createGeometrySimplifier( const QgsSimplifyMethod& simplifyMethod )
6458
{
6559
QgsSimplifyMethod::MethodType methodType = simplifyMethod.methodType();

src/core/qgssimplifymethod.h

-3
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,6 @@ class CORE_EXPORT QgsSimplifyMethod
4848
//! Gets the tolerance of simplification
4949
inline double tolerance() const { return mTolerance; }
5050

51-
//! Returns the optimal tolerance for Douglas-Peucker simplification algorithms
52-
double toleranceForDouglasPeuckerAlgorithms() const;
53-
5451
//! Sets whether the simplification executes after fetch the geometries from provider, otherwise it executes, when supported, in provider before fetch the geometries
5552
void setForceLocalOptimization( bool localOptimization );
5653
//! Gets whether the simplification executes after fetch the geometries from provider, otherwise it executes, when supported, in provider before fetch the geometries

src/providers/ogr/qgsogrgeometrysimplifier.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ bool QgsOgrTopologyPreservingSimplifier::simplifyGeometry( OGRGeometryH geometry
6363
#if defined(GDAL_VERSION_NUM) && defined(GDAL_COMPUTE_VERSION)
6464
#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(1,11,0)
6565

66-
QgsOgrMapToPixelSimplifier::QgsOgrMapToPixelSimplifier( int simplifyFlags, double map2pixelTol )
67-
: QgsMapToPixelSimplifier( simplifyFlags, map2pixelTol )
66+
QgsOgrMapToPixelSimplifier::QgsOgrMapToPixelSimplifier( int simplifyFlags, double tolerance )
67+
: QgsMapToPixelSimplifier( simplifyFlags, tolerance )
6868
, mPointBufferPtr( NULL )
6969
, mPointBufferCount( 0 )
7070
{
@@ -137,7 +137,7 @@ bool QgsOgrMapToPixelSimplifier::simplifyOgrGeometry( QGis::GeometryType geometr
137137
if ( geometryType == QGis::Point || geometryType == QGis::UnknownGeometry ) return false;
138138
pointSimplifiedCount = 0;
139139

140-
double map2pixelTol = mMapToPixelTol * mMapToPixelTol; //-> Use mappixelTol for 'LengthSquare' calculations.
140+
double map2pixelTol = mTolerance * mTolerance; //-> Use mappixelTol for 'LengthSquare' calculations.
141141
double x, y, lastX = 0, lastY = 0;
142142

143143
char* xsourcePtr = ( char* )xptr;

src/providers/ogr/qgsogrgeometrysimplifier.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class QgsOgrTopologyPreservingSimplifier : public QgsOgrAbstractGeometrySimplifi
6363
class QgsOgrMapToPixelSimplifier : public QgsOgrAbstractGeometrySimplifier, QgsMapToPixelSimplifier
6464
{
6565
public:
66-
QgsOgrMapToPixelSimplifier( int simplifyFlags, double map2pixelTol );
66+
QgsOgrMapToPixelSimplifier( int simplifyFlags, double tolerance );
6767
virtual ~QgsOgrMapToPixelSimplifier();
6868

6969
private:

src/providers/postgres/qgspostgresfeatureiterator.cpp

+53-4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
***************************************************************************/
1515
#include "qgspostgresfeatureiterator.h"
1616
#include "qgspostgresprovider.h"
17+
#include "qgsgeometry.h"
1718

1819
#include "qgslogger.h"
1920
#include "qgsmessagelog.h"
@@ -292,6 +293,7 @@ QString QgsPostgresFeatureIterator::whereClauseRect()
292293
bool QgsPostgresFeatureIterator::declareCursor( const QString& whereClause )
293294
{
294295
mFetchGeometry = !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) && !P->mGeometryColumn.isNull();
296+
bool simplifyGeometry = false;
295297

296298
try
297299
{
@@ -303,11 +305,10 @@ bool QgsPostgresFeatureIterator::declareCursor( const QString& whereClause )
303305
{
304306
QString simplifyFunctionName = simplifyMethod.methodType() == QgsSimplifyMethod::OptimizeForRendering
305307
? ( P->mConnectionRO->majorVersion() < 2 ? "simplify" : "st_simplify" )
306-
: ( P->mConnectionRO->majorVersion() < 2 ? "simplifypreservetopology" : "st_simplifypreservetopology" );
308+
: ( P->mConnectionRO->majorVersion() < 2 ? "simplifypreservetopology" : "st_simplifypreservetopology" );
307309

308-
double tolerance = simplifyMethod.methodType() == QgsSimplifyMethod::OptimizeForRendering
309-
? simplifyMethod.toleranceForDouglasPeuckerAlgorithms()
310-
: simplifyMethod.tolerance();
310+
double tolerance = simplifyMethod.tolerance() * 0.8; //-> Default factor for the maximum displacement distance for simplification, similar as GeoServer does
311+
simplifyGeometry = simplifyMethod.methodType() == QgsSimplifyMethod::OptimizeForRendering;
311312

312313
query += QString( "%1(%5(%2%3,%6),'%4')" )
313314
.arg( P->mConnectionRO->majorVersion() < 2 ? "asbinary" : "st_asbinary" )
@@ -368,6 +369,17 @@ bool QgsPostgresFeatureIterator::declareCursor( const QString& whereClause )
368369
query += delim + P->mConnectionRO->fieldExpression( P->field( idx ) );
369370
}
370371

372+
// query BBOX of geometries to redefine the geometries collapsed by ST_Simplify()
373+
if ( simplifyGeometry && !( P->mConnectionRO->majorVersion() >= 2 && P->mConnectionRO->minorVersion() >= 1 ) )
374+
{
375+
query += QString( ",%1(%5(%2)%3,'%4')" )
376+
.arg( P->mConnectionRO->majorVersion() < 2 ? "asbinary" : "st_asbinary" )
377+
.arg( P->quotedIdentifier( P->mGeometryColumn ) )
378+
.arg( P->mSpatialColType == sctGeography ? "::geometry" : "" )
379+
.arg( P->endianString() )
380+
.arg( P->mConnectionRO->majorVersion() < 2 ? "envelope" : "st_envelope" );
381+
}
382+
371383
query += " FROM " + P->mQuery;
372384

373385
if ( !whereClause.isEmpty() )
@@ -591,6 +603,43 @@ bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int
591603
getFeatureAttribute( idx, queryResult, row, col, feature );
592604
}
593605

606+
// fix collapsed geometries by ST_Simplify() using the BBOX fetched from the current query
607+
const QgsSimplifyMethod& simplifyMethod = mRequest.simplifyMethod();
608+
if ( mFetchGeometry && !simplifyMethod.forceLocalOptimization() && simplifyMethod.methodType() == QgsSimplifyMethod::OptimizeForRendering )
609+
{
610+
QgsGeometry* geometry = feature.geometry();
611+
612+
if ( !( P->mConnectionRO->majorVersion() >= 2 && P->mConnectionRO->minorVersion() >= 1 ) && ( !geometry || geometry->length() == 0 ) )
613+
{
614+
int returnedLength = ::PQgetlength( queryResult.result(), row, col );
615+
616+
if ( returnedLength > 0 )
617+
{
618+
unsigned char *featureGeom = new unsigned char[returnedLength + 1];
619+
memcpy( featureGeom, PQgetvalue( queryResult.result(), row, col ), returnedLength );
620+
memset( featureGeom + returnedLength, 0, 1 );
621+
622+
QgsGeometry *envelope = new QgsGeometry();
623+
envelope->fromWkb( featureGeom, returnedLength + 1 );
624+
625+
if ( QGis::flatType( QGis::singleType( P->geometryType() ) ) == QGis::WKBPolygon )
626+
{
627+
feature.setGeometry( envelope );
628+
}
629+
else
630+
{
631+
QgsPolyline polyline;
632+
polyline.append( envelope->vertexAt( 0 ) );
633+
polyline.append( envelope->vertexAt( 2 ) );
634+
delete envelope;
635+
636+
geometry = QgsGeometry::fromPolyline( polyline );
637+
feature.setGeometry( geometry );
638+
}
639+
}
640+
}
641+
}
642+
594643
return true;
595644
}
596645
catch ( QgsPostgresProvider::PGFieldNotFound )

0 commit comments

Comments
 (0)