Skip to content

Commit

Permalink
Merge pull request #2900 from ahuarte47/Issue_QPainterSimplification
Browse files Browse the repository at this point in the history
[Bugfix] QPainter simplification (fixes #14301)
  • Loading branch information
nyalldawson committed May 27, 2016
2 parents cbbc535 + 6969a1a commit d4a482b
Show file tree
Hide file tree
Showing 50 changed files with 1,088 additions and 668 deletions.
2 changes: 1 addition & 1 deletion python/core/qgsclipper.sip
Expand Up @@ -56,5 +56,5 @@ class QgsClipper
@param wkb pointer to the start of the line wkb
@param clipExtent clipping bounds
@param line out: clipped line coordinates*/
static QgsConstWkbPtr clippedLineWKB( QgsConstWkbPtr wkb, const QgsRectangle& clipExtent, QPolygonF& line );
static QgsConstWkbPtr clippedLineWKB( QgsConstWkbPtr& wkb, const QgsRectangle& clipExtent, QPolygonF& line );
};
29 changes: 27 additions & 2 deletions python/core/qgsmaptopixelgeometrysimplifier.sip
Expand Up @@ -4,7 +4,16 @@ class QgsMapToPixelSimplifier : QgsAbstractGeometrySimplifier
#include "qgsmaptopixelgeometrysimplifier.h"
%End
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
Visvalingam = 2, //!< The simplification gives each point in a line an importance weighting, so that least important points are removed first
};

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

//! Applicable simplification flags
Expand All @@ -20,15 +29,28 @@ 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:
//! Gets the simplification hints of the vector layer managed
int simplifyFlags() const;
//! Sets the simplification hints of the vector layer managed
void setSimplifyFlags( int simplifyFlags );

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

//! Returns a simplified version the specified geometry
virtual QgsGeometry* simplify( QgsGeometry* geometry ) const;
//! Simplifies the specified geometry
virtual bool simplifyGeometry( QgsGeometry* geometry ) const;

//! Simplifies the specified WKB-point array
virtual bool simplifyPoints( QgsWKBTypes::Type wkbType, QgsConstWkbPtr& sourceWkbPtr, QPolygonF& targetPoints ) const;

// MapToPixel simplification helper methods
public:

Expand All @@ -39,6 +61,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, SimplifyAlgorithm simplifyAlgorithm = Distance );

};
2 changes: 1 addition & 1 deletion python/core/qgssimplifymethod.sip
Expand Up @@ -26,7 +26,7 @@ class QgsSimplifyMethod

//! 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.
//! Gets the tolerance of simplification in map units. Represents the maximum distance in map units between two coordinates which can be considered equal.
double tolerance() const;

//! Sets the simplification threshold in pixels. Represents the maximum distance in pixels between two coordinates which can be considered equal.
Expand Down
18 changes: 18 additions & 0 deletions python/core/qgsvectorsimplifymethod.sip
Expand Up @@ -29,6 +29,24 @@ 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
Visvalingam = 2, //!< The simplification gives each point in a line an importance weighting, so that least important points are removed first
};

/** 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 */
double tolerance() const;

/** Sets the simplification threshold of the vector layer managed */
void setThreshold( float threshold );
/** Gets the simplification threshold of the vector layer managed */
Expand Down
20 changes: 17 additions & 3 deletions python/core/symbology-ng/qgsrendererv2.sip
Expand Up @@ -403,9 +403,23 @@ class QgsFeatureRendererV2
//! render editing vertex marker for a polygon
void renderVertexMarkerPolygon( QPolygonF& pts, QList<QPolygonF>* rings, QgsRenderContext& context );

static QgsConstWkbPtr _getPoint( QPointF& pt, QgsRenderContext& context, QgsConstWkbPtr wkb );
static QgsConstWkbPtr _getLineString( QPolygonF& pts, QgsRenderContext& context, QgsConstWkbPtr wkb, bool clipToExtent = true );
static QgsConstWkbPtr _getPolygon( QPolygonF& pts, QList<QPolygonF>& holes, QgsRenderContext& context, QgsConstWkbPtr wkb, bool clipToExtent = true );
/**
* Creates a point in screen coordinates from a wkb string in map
* coordinates
*/
static QgsConstWkbPtr _getPoint( QPointF& pt, QgsRenderContext& context, QgsConstWkbPtr& wkb );

/**
* Creates a line string in screen coordinates from a wkb string in map
* coordinates
*/
static QgsConstWkbPtr _getLineString( QPolygonF& pts, QgsRenderContext& context, QgsConstWkbPtr& wkb, bool clipToExtent = true );

/**
* Creates a polygon in screen coordinates from a wkb string in map
* coordinates
*/
static QgsConstWkbPtr _getPolygon( QPolygonF& pts, QList<QPolygonF>& holes, QgsRenderContext& context, QgsConstWkbPtr& wkb, bool clipToExtent = true );

void setScaleMethodToSymbol( QgsSymbolV2* symbol, int scaleMethod );

Expand Down
6 changes: 3 additions & 3 deletions python/core/symbology-ng/qgssymbolv2.sip
Expand Up @@ -228,19 +228,19 @@ class QgsSymbolV2
* Creates a point in screen coordinates from a wkb string in map
* coordinates
*/
static QgsConstWkbPtr _getPoint( QPointF& pt, QgsRenderContext& context, QgsConstWkbPtr wkb );
static QgsConstWkbPtr _getPoint( QPointF& pt, QgsRenderContext& context, QgsConstWkbPtr& wkb );

/**
* Creates a line string in screen coordinates from a wkb string in map
* coordinates
*/
static QgsConstWkbPtr _getLineString( QPolygonF& pts, QgsRenderContext& context, QgsConstWkbPtr wkb, bool clipToExtent = true );
static QgsConstWkbPtr _getLineString( QPolygonF& pts, QgsRenderContext& context, QgsConstWkbPtr& wkb, bool clipToExtent = true );

/**
* Creates a polygon in screen coordinates from a wkb string in map
* coordinates
*/
static QgsConstWkbPtr _getPolygon( QPolygonF& pts, QList<QPolygonF>& holes, QgsRenderContext& context, QgsConstWkbPtr wkb, bool clipToExtent = true );
static QgsConstWkbPtr _getPolygon( QPolygonF& pts, QList<QPolygonF>& holes, QgsRenderContext& context, QgsConstWkbPtr& wkb, bool clipToExtent = true );

/**
* Retrieve a cloned list of all layers that make up this symbol.
Expand Down
7 changes: 7 additions & 0 deletions src/app/qgsoptions.cpp
Expand Up @@ -613,6 +613,12 @@ 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->addItem( tr( "Visvalingam" ), ( int )QgsVectorSimplifyMethod::Visvalingam );
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 +1218,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
7 changes: 7 additions & 0 deletions src/app/qgsvectorlayerproperties.cpp
Expand Up @@ -439,6 +439,12 @@ 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->addItem( tr( "Visvalingam" ), ( int )QgsVectorSimplifyMethod::Visvalingam );
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 +631,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
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -352,6 +352,7 @@ SET(QGIS_CORE_SRCS
geometry/qgspolygonv2.cpp
geometry/qgswkbptr.cpp
geometry/qgswkbtypes.cpp
geometry/qgswkbsimplifierptr.cpp

${CMAKE_CURRENT_BINARY_DIR}/qgscontexthelp_texts.cpp
${CMAKE_CURRENT_BINARY_DIR}/qgsexpression_texts.cpp
Expand Down Expand Up @@ -836,6 +837,7 @@ SET(QGIS_CORE_HDRS
geometry/qgspointv2.h
geometry/qgswkbptr.h
geometry/qgswkbtypes.h
geometry/qgswkbsimplifierptr.h
)

IF (QT_MOBILITY_LOCATION_FOUND OR Qt5Positioning_FOUND)
Expand Down
31 changes: 30 additions & 1 deletion src/core/geometry/qgswkbptr.cpp
Expand Up @@ -32,6 +32,7 @@ QgsConstWkbPtr::QgsConstWkbPtr( const unsigned char *p, int size )
mP = const_cast< unsigned char * >( p );
mEnd = mP + size;
mEndianSwap = false;
mWkbType = QgsWKBTypes::Unknown;
}

QgsWKBTypes::Type QgsConstWkbPtr::readHeader() const
Expand All @@ -45,12 +46,40 @@ QgsWKBTypes::Type QgsConstWkbPtr::readHeader() const

int wkbType;
*this >> wkbType;
mWkbType = static_cast<QgsWKBTypes::Type>( wkbType );

return static_cast<QgsWKBTypes::Type>( wkbType );
return mWkbType;
}

void QgsConstWkbPtr::verifyBound( int size ) const
{
if ( !mP || mP + size > mEnd )
throw QgsWkbException( "wkb access out of bounds" );
}

const QgsConstWkbPtr &QgsConstWkbPtr::operator>>( QPointF &point ) const
{
read( point.rx() );
read( point.ry() );
return *this;
}

const QgsConstWkbPtr &QgsConstWkbPtr::operator>>( QPolygonF &points ) const
{
int skipZM = ( QgsWKBTypes::coordDimensions( mWkbType ) - 2 ) * sizeof( double );
Q_ASSERT( skipZM >= 0 );

unsigned int nPoints;
read( nPoints );

points.resize( nPoints );
QPointF* ptr = points.data();

for ( unsigned int i = 0; i < nPoints; ++i, ++ptr )
{
read( ptr->rx() );
read( ptr->ry() );
mP += skipZM;
}
return *this;
}
10 changes: 10 additions & 0 deletions src/core/geometry/qgswkbptr.h
Expand Up @@ -19,6 +19,7 @@
#include "qgsapplication.h"
#include "qgis.h"
#include "qgsexception.h"
#include "qpolygon.h"

/** \ingroup core
* * Custom exception class for Wkb related exceptions.
Expand Down Expand Up @@ -89,12 +90,16 @@ class CORE_EXPORT QgsWkbPtr

class CORE_EXPORT QgsConstWkbPtr
{
protected:
mutable unsigned char *mP;
unsigned char *mEnd;
mutable bool mEndianSwap;
mutable QgsWKBTypes::Type mWkbType;

//! Verify bounds
void verifyBound( int size ) const;

//! Read a value
template<typename T> void read( T& v ) const
{
verifyBound( sizeof v );
Expand All @@ -114,6 +119,11 @@ class CORE_EXPORT QgsConstWkbPtr
inline const QgsConstWkbPtr &operator>>( unsigned int &v ) const { read( v ); return *this; }
inline const QgsConstWkbPtr &operator>>( char &v ) const { read( v ); return *this; }

//! Read a point
virtual const QgsConstWkbPtr &operator>>( QPointF &point ) const;
//! Read a point array
virtual const QgsConstWkbPtr &operator>>( QPolygonF &points ) const;

inline void operator+=( int n ) { verifyBound( n ); mP += n; }
inline void operator-=( int n ) { mP -= n; }

Expand Down
48 changes: 48 additions & 0 deletions src/core/geometry/qgswkbsimplifierptr.cpp
@@ -0,0 +1,48 @@
/***************************************************************************
qgswkbsimplifierptr.cpp
---------------------
begin : February 2016
copyright : (C) 2016 by Alvaro Huarte
email : http://wiki.osgeo.org/wiki/Alvaro_Huarte
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgswkbsimplifierptr.h"
#include "qgsgeometry.h"
#include "qgssimplifymethod.h"
#include "qgsmaptopixelgeometrysimplifier.h"

QgsConstWkbSimplifierPtr::QgsConstWkbSimplifierPtr( const unsigned char *p, int size, const QgsVectorSimplifyMethod &simplifyMethod )
: QgsConstWkbPtr( p, size )
, mSimplifyMethod( simplifyMethod )
{
}

const QgsConstWkbPtr &QgsConstWkbSimplifierPtr::operator>>( QPointF &point ) const
{
return QgsConstWkbPtr::operator>>( point );
}

const QgsConstWkbPtr &QgsConstWkbSimplifierPtr::operator>>( QPolygonF &points ) const
{
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(), simplifyAlgorithm ) )
{
mP = const_cast< unsigned char * >(( const unsigned char * ) wkbPtr );
return *this;
}
}
QgsConstWkbPtr::operator>>( points );
return *this;
}

0 comments on commit d4a482b

Please sign in to comment.