Skip to content

Commit

Permalink
Add SimplifyAlgorithm enum for local simplification
Browse files Browse the repository at this point in the history
It implements the SimplifyAlgorithm enum for local simplification of
geometries.
Also the SnapToGrid algorithm is added.
  • Loading branch information
ahuarte47 committed May 25, 2016
1 parent 01ece90 commit 734b8b4
Show file tree
Hide file tree
Showing 14 changed files with 200 additions and 44 deletions.
17 changes: 15 additions & 2 deletions python/core/qgsmaptopixelgeometrysimplifier.sip
Expand Up @@ -4,6 +4,13 @@ class QgsMapToPixelSimplifier : QgsAbstractGeometrySimplifier
#include "qgsmaptopixelgeometrysimplifier.h"
%End
public:
//! Types of simplification algorithms that can be used
enum SimplifyAlgorithm
{
Distance = 0, //!< The simplification uses the distance between points to remove duplicate points
SnapToGrid = 1, //!< The simplification uses a grid (similar to ST_SnapToGrid) to remove duplicate points
};

QgsMapToPixelSimplifier( int simplifyFlags, double tolerance );
virtual ~QgsMapToPixelSimplifier();

Expand All @@ -20,10 +27,16 @@ class QgsMapToPixelSimplifier : QgsAbstractGeometrySimplifier
//! Returns the squared 2D-distance of the vector defined by the two points specified
static float calculateLengthSquared2D( double x1, double y1, double x2, double y2 );

//! Returns whether the points belong to the same grid
static bool equalSnapToGrid( double x1, double y1, double x2, double y2, double gridOriginX, double gridOriginY, float gridInverseSizeXY );

public:
int simplifyFlags() const;
void setSimplifyFlags( int simplifyFlags );

SimplifyAlgorithm simplifyAlgorithm() const;
void setSimplifyAlgorithm( SimplifyAlgorithm simplifyAlgorithm );

//! Returns a simplified version the specified geometry
virtual QgsGeometry* simplify( QgsGeometry* geometry ) const;
//! Simplifies the specified geometry
Expand All @@ -42,9 +55,9 @@ class QgsMapToPixelSimplifier : QgsAbstractGeometrySimplifier
bool isGeneralizableByMapBoundingBox( const QgsRectangle& envelope ) const;

//! Simplifies the geometry when is applied the specified map2pixel context
static bool simplifyGeometry( QgsGeometry* geometry, int simplifyFlags, double tolerance );
static bool simplifyGeometry( QgsGeometry* geometry, int simplifyFlags, double tolerance, SimplifyAlgorithm simplifyAlgorithm = Distance );

//! Simplifies the WKB-point array when is applied the specified map2pixel context
static bool simplifyPoints( QgsWKBTypes::Type wkbType, QgsConstWkbPtr& sourceWkbPtr, QPolygonF& targetPoints, int simplifyFlags, double tolerance );
static bool simplifyPoints( QgsWKBTypes::Type wkbType, QgsConstWkbPtr& sourceWkbPtr, QPolygonF& targetPoints, int simplifyFlags, double tolerance, SimplifyAlgorithm simplifyAlgorithm = Distance );

};
12 changes: 12 additions & 0 deletions python/core/qgsvectorsimplifymethod.sip
Expand Up @@ -29,6 +29,18 @@ class QgsVectorSimplifyMethod
/** Gets the simplification hints of the vector layer managed */
QFlags<QgsVectorSimplifyMethod::SimplifyHint> simplifyHints() const;

/** Types of local simplification algorithms that can be used */
enum SimplifyAlgorithm
{
Distance = 0, //!< The simplification uses the distance between points to remove duplicate points
SnapToGrid = 1, //!< The simplification uses a grid (similar to ST_SnapToGrid) to remove duplicate points
};

/** Sets the local simplification algorithm of the vector layer managed */
void setSimplifyAlgorithm( const SimplifyAlgorithm& simplifyAlgorithm );
/** Gets the local simplification algorithm of the vector layer managed */
SimplifyAlgorithm simplifyAlgorithm() const;

/** Sets the tolerance of simplification in map units. Represents the maximum distance in map units between two coordinates which can be considered equal */
void setTolerance( double tolerance );
/** Gets the tolerance of simplification in map units. Represents the maximum distance in map units between two coordinates which can be considered equal */
Expand Down
6 changes: 6 additions & 0 deletions src/app/qgsoptions.cpp
Expand Up @@ -613,6 +613,11 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl )
doubleSpinBoxMagnifierDefault->setSuffix( "%" );
doubleSpinBoxMagnifierDefault->setValue( mSettings->value( "/qgis/magnifier_level", 100 ).toInt() );

// Default local simplification algorithm
mSimplifyAlgorithmComboBox->addItem( tr( "Distance" ), ( int )QgsVectorSimplifyMethod::Distance );
mSimplifyAlgorithmComboBox->addItem( tr( "SnapToGrid" ), ( int )QgsVectorSimplifyMethod::SnapToGrid );
mSimplifyAlgorithmComboBox->setCurrentIndex( mSimplifyAlgorithmComboBox->findData( mSettings->value( "/qgis/simplifyAlgorithm", 0 ).toInt() ) );

// Slightly awkard here at the settings value is true to use QImage,
// but the checkbox is true to use QPixmap
chkAddedVisibility->setChecked( mSettings->value( "/qgis/new_layers_visible", true ).toBool() );
Expand Down Expand Up @@ -1212,6 +1217,7 @@ void QgsOptions::saveOptions()
if ( mSimplifyDrawingSpinBox->value() > 1 ) simplifyHints |= QgsVectorSimplifyMethod::AntialiasingSimplification;
}
mSettings->setValue( "/qgis/simplifyDrawingHints", ( int ) simplifyHints );
mSettings->setValue( "/qgis/simplifyAlgorithm", mSimplifyAlgorithmComboBox->itemData( mSimplifyAlgorithmComboBox->currentIndex() ).toInt() );
mSettings->setValue( "/qgis/simplifyDrawingTol", mSimplifyDrawingSpinBox->value() );
mSettings->setValue( "/qgis/simplifyLocal", !mSimplifyDrawingAtProvider->isChecked() );
mSettings->setValue( "/qgis/simplifyMaxScale", 1.0 / mSimplifyMaximumScaleComboBox->scale() );
Expand Down
6 changes: 6 additions & 0 deletions src/app/qgsvectorlayerproperties.cpp
Expand Up @@ -439,6 +439,11 @@ void QgsVectorLayerProperties::syncToLayer()
mSimplifyDrawingGroupBox->setEnabled( false );
}

// Default local simplification algorithm
mSimplifyAlgorithmComboBox->addItem( tr( "Distance" ), ( int )QgsVectorSimplifyMethod::Distance );
mSimplifyAlgorithmComboBox->addItem( tr( "SnapToGrid" ), ( int )QgsVectorSimplifyMethod::SnapToGrid );
mSimplifyAlgorithmComboBox->setCurrentIndex( mSimplifyAlgorithmComboBox->findData(( int )simplifyMethod.simplifyAlgorithm() ) );

QStringList myScalesList = PROJECT_SCALES.split( ',' );
myScalesList.append( "1:1" );
mSimplifyMaximumScaleComboBox->updateScales( myScalesList );
Expand Down Expand Up @@ -625,6 +630,7 @@ void QgsVectorLayerProperties::apply()
}
QgsVectorSimplifyMethod simplifyMethod = mLayer->simplifyMethod();
simplifyMethod.setSimplifyHints( simplifyHints );
simplifyMethod.setSimplifyAlgorithm( static_cast< QgsVectorSimplifyMethod::SimplifyAlgorithm >( mSimplifyAlgorithmComboBox->itemData( mSimplifyAlgorithmComboBox->currentIndex() ).toInt() ) );
simplifyMethod.setThreshold( mSimplifyDrawingSpinBox->value() );
simplifyMethod.setForceLocalOptimization( !mSimplifyDrawingAtProvider->isChecked() );
simplifyMethod.setMaximumScale( 1.0 / mSimplifyMaximumScaleComboBox->scale() );
Expand Down
3 changes: 2 additions & 1 deletion src/core/geometry/qgswkbsimplifierptr.cpp
Expand Up @@ -33,10 +33,11 @@ const QgsConstWkbPtr &QgsConstWkbSimplifierPtr::operator>>( QPolygonF &points )
if ( mSimplifyMethod.simplifyHints() != QgsVectorSimplifyMethod::NoSimplification && mSimplifyMethod.forceLocalOptimization() )
{
int simplifyHints = mSimplifyMethod.simplifyHints() | QgsMapToPixelSimplifier::SimplifyEnvelope;
QgsMapToPixelSimplifier::SimplifyAlgorithm simplifyAlgorithm = static_cast< QgsMapToPixelSimplifier::SimplifyAlgorithm >( mSimplifyMethod.simplifyAlgorithm() );

QgsConstWkbPtr wkbPtr = *this;

if ( QgsMapToPixelSimplifier::simplifyPoints( mWkbType, wkbPtr, points, simplifyHints, mSimplifyMethod.tolerance() ) )
if ( QgsMapToPixelSimplifier::simplifyPoints( mWkbType, wkbPtr, points, simplifyHints, mSimplifyMethod.tolerance(), simplifyAlgorithm ) )
{
mP = const_cast< unsigned char * >(( const unsigned char * ) wkbPtr );
return *this;
Expand Down
104 changes: 76 additions & 28 deletions src/core/qgsmaptopixelgeometrysimplifier.cpp
Expand Up @@ -21,9 +21,10 @@
#include "qgslogger.h"


QgsMapToPixelSimplifier::QgsMapToPixelSimplifier( int simplifyFlags, double tolerance )
QgsMapToPixelSimplifier::QgsMapToPixelSimplifier( int simplifyFlags, double tolerance, SimplifyAlgorithm simplifyAlgorithm )
: mSimplifyFlags( simplifyFlags )
, mTolerance( tolerance )
, mSimplifyAlgorithm( simplifyAlgorithm )
{
}

Expand All @@ -40,7 +41,22 @@ float QgsMapToPixelSimplifier::calculateLengthSquared2D( double x1, double y1, d
float vx = static_cast< float >( x2 - x1 );
float vy = static_cast< float >( y2 - y1 );

return vx*vx + vy*vy;
return ( vx * vx ) + ( vy * vy );
}


//! Returns whether the points belong to the same grid
bool QgsMapToPixelSimplifier::equalSnapToGrid( double x1, double y1, double x2, double y2, double gridOriginX, double gridOriginY, float gridInverseSizeXY )
{
int grid_x1 = qRound(( x1 - gridOriginX ) * gridInverseSizeXY );
int grid_x2 = qRound(( x2 - gridOriginX ) * gridInverseSizeXY );
if ( grid_x1 != grid_x2 ) return false;

int grid_y1 = qRound(( y1 - gridOriginY ) * gridInverseSizeXY );
int grid_y2 = qRound(( y2 - gridOriginY ) * gridInverseSizeXY );
if ( grid_y1 != grid_y2 ) return false;

return true;
}

//! Returns the BBOX of the specified WKB-point stream
Expand Down Expand Up @@ -122,7 +138,9 @@ static bool generalizeWkbGeometryByBoundingBox(

//! Simplify the WKB-geometry using the specified tolerance
bool QgsMapToPixelSimplifier::simplifyWkbGeometry(
int simplifyFlags, QGis::WkbType wkbType,
int simplifyFlags,
SimplifyAlgorithm simplifyAlgorithm,
QGis::WkbType wkbType,
QgsConstWkbPtr sourceWkbPtr,
QgsWkbPtr targetWkbPtr,
int &targetWkbSize,
Expand Down Expand Up @@ -184,8 +202,6 @@ bool QgsMapToPixelSimplifier::simplifyWkbGeometry(
targetWkbPtr << numTargetPoints;
targetWkbSize += 4;

map2pixelTol *= map2pixelTol; //-> Use mappixelTol for 'LengthSquare' calculations.

bool isLongSegment;
bool hasLongSegments = false; //-> To avoid replace the simplified geometry by its BBOX when there are 'long' segments.
bool badLuck = false;
Expand All @@ -205,27 +221,59 @@ bool QgsMapToPixelSimplifier::simplifyWkbGeometry(
}

// Process each vertex...
for ( int i = 0; i < numPoints; ++i )
if ( simplifyAlgorithm == SnapToGrid )
{
sourceWkbPtr >> x >> y;
sourceWkbPtr += skipZM;
double gridOriginX = envelope.xMinimum();
double gridOriginY = envelope.yMinimum();

isLongSegment = false;
// Use a factor for the maximum displacement distance for simplification, similar as GeoServer does
float gridInverseSizeXY = map2pixelTol != 0 ? ( float )( 1.0f / ( 0.8 * map2pixelTol ) ) : 0.0f;

if ( i == 0 ||
!isGeneralizable ||
( isLongSegment = ( calculateLengthSquared2D( x, y, lastX, lastY ) > map2pixelTol ) ) ||
( !isaLinearRing && ( i == 1 || i >= numPoints - 2 ) ) )
for ( int i = 0; i < numPoints; ++i )
{
targetWkbPtr << x << y;
lastX = x;
lastY = y;
numTargetPoints++;
sourceWkbPtr >> x >> y;
sourceWkbPtr += skipZM;

if ( i == 0 ||
!isGeneralizable ||
!equalSnapToGrid( x, y, lastX, lastY, gridOriginX, gridOriginY, gridInverseSizeXY ) ||
( !isaLinearRing && ( i == 1 || i >= numPoints - 2 ) ) )
{
targetWkbPtr << x << y;
lastX = x;
lastY = y;
numTargetPoints++;
}

hasLongSegments |= isLongSegment;
r.combineExtentWith( x, y );
}
}
else
{
map2pixelTol *= map2pixelTol; //-> Use mappixelTol for 'LengthSquare' calculations.

for ( int i = 0; i < numPoints; ++i )
{
sourceWkbPtr >> x >> y;
sourceWkbPtr += skipZM;

isLongSegment = false;

r.combineExtentWith( x, y );
if ( i == 0 ||
!isGeneralizable ||
( isLongSegment = ( calculateLengthSquared2D( x, y, lastX, lastY ) > map2pixelTol ) ) ||
( !isaLinearRing && ( i == 1 || i >= numPoints - 2 ) ) )
{
targetWkbPtr << x << y;
lastX = x;
lastY = y;
numTargetPoints++;

hasLongSegments |= isLongSegment;
}

r.combineExtentWith( x, y );
}
}

QgsWkbPtr nextPointPtr( targetWkbPtr );
Expand Down Expand Up @@ -296,7 +344,7 @@ bool QgsMapToPixelSimplifier::simplifyWkbGeometry(
int sourceWkbSize_i = sizeof( int ) + numPoints_i * QGis::wkbDimensions( wkbType ) * sizeof( double );
int targetWkbSize_i = 0;

result |= simplifyWkbGeometry( simplifyFlags, wkbType, sourceWkbPtr, targetWkbPtr, targetWkbSize_i, envelope_i, map2pixelTol, false, true );
result |= simplifyWkbGeometry( simplifyFlags, simplifyAlgorithm, wkbType, sourceWkbPtr, targetWkbPtr, targetWkbSize_i, envelope_i, map2pixelTol, false, true );
sourceWkbPtr += sourceWkbSize_i;
targetWkbPtr += targetWkbSize_i;

Expand Down Expand Up @@ -347,7 +395,7 @@ bool QgsMapToPixelSimplifier::simplifyWkbGeometry(
sourceWkbPtr2 += wkbSize_i;
}
}
result |= simplifyWkbGeometry( simplifyFlags, QGis::singleType( wkbType ), sourceWkbPtr, targetWkbPtr, targetWkbSize_i, envelope, map2pixelTol, true, false );
result |= simplifyWkbGeometry( simplifyFlags, simplifyAlgorithm, QGis::singleType( wkbType ), sourceWkbPtr, targetWkbPtr, targetWkbSize_i, envelope, map2pixelTol, true, false );
sourceWkbPtr += sourceWkbSize_i;
targetWkbPtr += targetWkbSize_i;

Expand Down Expand Up @@ -376,13 +424,13 @@ QgsGeometry* QgsMapToPixelSimplifier::simplify( QgsGeometry* geometry ) const
unsigned char* wkb = new unsigned char[ wkbSize ];
memcpy( wkb, geometry->asWkb(), wkbSize );
g->fromWkb( wkb, wkbSize );
simplifyGeometry( g, mSimplifyFlags, mTolerance );
simplifyGeometry( g, mSimplifyFlags, mTolerance, mSimplifyAlgorithm );

return g;
}

//! Simplifies the geometry (Removing duplicated points) when is applied the specified map2pixel context
bool QgsMapToPixelSimplifier::simplifyGeometry( QgsGeometry *geometry, int simplifyFlags, double tolerance )
bool QgsMapToPixelSimplifier::simplifyGeometry( QgsGeometry *geometry, int simplifyFlags, double tolerance, SimplifyAlgorithm simplifyAlgorithm )
{
int finalWkbSize = 0;

Expand All @@ -402,7 +450,7 @@ bool QgsMapToPixelSimplifier::simplifyGeometry( QgsGeometry *geometry, int simpl

try
{
if ( simplifyWkbGeometry( simplifyFlags, wkbType, wkbPtr, targetWkbPtr, finalWkbSize, envelope, tolerance ) )
if ( simplifyWkbGeometry( simplifyFlags, simplifyAlgorithm, wkbType, wkbPtr, targetWkbPtr, finalWkbSize, envelope, tolerance ) )
{
unsigned char *finalWkb = new unsigned char[finalWkbSize];
memcpy( finalWkb, targetWkb, finalWkbSize );
Expand All @@ -423,11 +471,11 @@ bool QgsMapToPixelSimplifier::simplifyGeometry( QgsGeometry *geometry, int simpl
//! Simplifies the geometry (Removing duplicated points) when is applied the specified map2pixel context
bool QgsMapToPixelSimplifier::simplifyGeometry( QgsGeometry* geometry ) const
{
return simplifyGeometry( geometry, mSimplifyFlags, mTolerance );
return simplifyGeometry( geometry, mSimplifyFlags, mTolerance, mSimplifyAlgorithm );
}

//! Simplifies the WKB-point array (Removing duplicated points) when is applied the specified map2pixel context
bool QgsMapToPixelSimplifier::simplifyPoints( QgsWKBTypes::Type wkbType, QgsConstWkbPtr& sourceWkbPtr, QPolygonF& targetPoints, int simplifyFlags, double tolerance )
bool QgsMapToPixelSimplifier::simplifyPoints( QgsWKBTypes::Type wkbType, QgsConstWkbPtr& sourceWkbPtr, QPolygonF& targetPoints, int simplifyFlags, double tolerance, SimplifyAlgorithm simplifyAlgorithm )
{
QgsWKBTypes::Type singleType = QgsWKBTypes::singleType( wkbType );
QgsWKBTypes::Type flatType = QgsWKBTypes::flatType( singleType );
Expand Down Expand Up @@ -457,7 +505,7 @@ bool QgsMapToPixelSimplifier::simplifyPoints( QgsWKBTypes::Type wkbType, QgsCons
targetWkbPtr << ( char ) QgsApplication::endian() << flatType;
targetWkbSize = 5;

if ( simplifyWkbGeometry( simplifyFlags, QGis::fromNewWkbType( singleType ), sourceWkbPtr, targetWkbPtr, targetWkbSize, envelope, tolerance, false, isaLinearRing ) )
if ( simplifyWkbGeometry( simplifyFlags, simplifyAlgorithm, QGis::fromNewWkbType( singleType ), sourceWkbPtr, targetWkbPtr, targetWkbSize, envelope, tolerance, false, isaLinearRing ) )
{
QgsConstWkbPtr finalWkbPtr( targetWkb, targetWkbSize );
finalWkbPtr.readHeader();
Expand All @@ -481,5 +529,5 @@ bool QgsMapToPixelSimplifier::simplifyPoints( QgsWKBTypes::Type wkbType, QgsCons
//! Simplifies the specified WKB-point array
bool QgsMapToPixelSimplifier::simplifyPoints( QgsWKBTypes::Type wkbType, QgsConstWkbPtr& sourceWkbPtr, QPolygonF& targetPoints ) const
{
return simplifyPoints( wkbType, sourceWkbPtr, targetPoints, mSimplifyFlags, mTolerance );
return simplifyPoints( wkbType, sourceWkbPtr, targetPoints, mSimplifyFlags, mTolerance, mSimplifyAlgorithm );
}
24 changes: 20 additions & 4 deletions src/core/qgsmaptopixelgeometrysimplifier.h
Expand Up @@ -32,7 +32,14 @@
class CORE_EXPORT QgsMapToPixelSimplifier : public QgsAbstractGeometrySimplifier
{
public:
QgsMapToPixelSimplifier( int simplifyFlags, double tolerance );
//! Types of simplification algorithms that can be used
enum SimplifyAlgorithm
{
Distance = 0, //!< The simplification uses the distance between points to remove duplicate points
SnapToGrid = 1, //!< The simplification uses a grid (similar to ST_SnapToGrid) to remove duplicate points
};

QgsMapToPixelSimplifier( int simplifyFlags, double tolerance, SimplifyAlgorithm simplifyAlgorithm = Distance );
virtual ~QgsMapToPixelSimplifier();

//! Applicable simplification flags
Expand All @@ -45,22 +52,31 @@ class CORE_EXPORT QgsMapToPixelSimplifier : public QgsAbstractGeometrySimplifier

private:
//! Simplify the WKB-geometry using the specified tolerance
static bool simplifyWkbGeometry( int simplifyFlags, QGis::WkbType wkbType, QgsConstWkbPtr sourceWkbPtr, QgsWkbPtr targetWkbPtr, int &targetWkbSize, const QgsRectangle& envelope, double map2pixelTol, bool writeHeader = true, bool isaLinearRing = false );
static bool simplifyWkbGeometry( int simplifyFlags, SimplifyAlgorithm simplifyAlgorithm, QGis::WkbType wkbType, QgsConstWkbPtr sourceWkbPtr, QgsWkbPtr targetWkbPtr, int &targetWkbSize, const QgsRectangle& envelope, double map2pixelTol, bool writeHeader = true, bool isaLinearRing = false );

protected:
//! Current simplification flags
int mSimplifyFlags;

//! Current algorithm
SimplifyAlgorithm mSimplifyAlgorithm;

//! Distance tolerance for the simplification
double mTolerance;

//! Returns the squared 2D-distance of the vector defined by the two points specified
static float calculateLengthSquared2D( double x1, double y1, double x2, double y2 );

//! Returns whether the points belong to the same grid
static bool equalSnapToGrid( double x1, double y1, double x2, double y2, double gridOriginX, double gridOriginY, float gridInverseSizeXY );

public:
int simplifyFlags() const { return mSimplifyFlags; }
void setSimplifyFlags( int simplifyFlags ) { mSimplifyFlags = simplifyFlags; }

SimplifyAlgorithm simplifyAlgorithm() const { return mSimplifyAlgorithm; }
void setSimplifyAlgorithm( SimplifyAlgorithm simplifyAlgorithm ) { mSimplifyAlgorithm = simplifyAlgorithm; }

//! Returns a simplified version the specified geometry
virtual QgsGeometry* simplify( QgsGeometry* geometry ) const override;
//! Simplifies the specified geometry
Expand All @@ -82,10 +98,10 @@ class CORE_EXPORT QgsMapToPixelSimplifier : public QgsAbstractGeometrySimplifier
}

//! Simplifies the geometry when is applied the specified map2pixel context
static bool simplifyGeometry( QgsGeometry* geometry, int simplifyFlags, double tolerance );
static bool simplifyGeometry( QgsGeometry* geometry, int simplifyFlags, double tolerance, SimplifyAlgorithm simplifyAlgorithm = Distance );

//! Simplifies the WKB-point array when is applied the specified map2pixel context
static bool simplifyPoints( QgsWKBTypes::Type wkbType, QgsConstWkbPtr& sourceWkbPtr, QPolygonF& targetPoints, int simplifyFlags, double tolerance );
static bool simplifyPoints( QgsWKBTypes::Type wkbType, QgsConstWkbPtr& sourceWkbPtr, QPolygonF& targetPoints, int simplifyFlags, double tolerance, SimplifyAlgorithm simplifyAlgorithm = Distance );
};

#endif // QGSMAPTOPIXELGEOMETRYSIMPLIFIER_H

0 comments on commit 734b8b4

Please sign in to comment.