80 changes: 80 additions & 0 deletions src/core/qgsgeometrysimplifier.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/***************************************************************************
qgsgeometrysimplifier.cpp
---------------------
begin : December 2013
copyright : (C) 2013 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 <limits>
#include "qgsgeometrysimplifier.h"

//! Returns whether the device-envelope can be replaced by its BBOX when is applied the specified tolerance
bool QgsAbstractGeometrySimplifier::canbeGeneralizedByDeviceBoundingBox( const QgsRectangle& envelope, float mapToPixelTol )
{
return (envelope.xMaximum()-envelope.xMinimum()) < mapToPixelTol && (envelope.yMaximum()-envelope.yMinimum()) < mapToPixelTol;
}

//! Returns whether the device-geometry can be replaced by its BBOX when is applied the specified tolerance
bool QgsAbstractGeometrySimplifier::canbeGeneralizedByDeviceBoundingBox( const QVector<QPointF>& points, float mapToPixelTol )
{
double xmin = std::numeric_limits<double>::max(), x,y;
double ymin = std::numeric_limits<double>::max();
double xmax = -std::numeric_limits<double>::max();
double ymax = -std::numeric_limits<double>::max();

for ( int i = 0, numPoints = points.size(); i < numPoints; ++i )
{
x = points[i].x();
y = points[i].y();

if (xmin>x) xmin = x;
if (ymin>y) ymin = y;
if (xmax<x) xmax = x;
if (ymax<y) ymax = y;
}
return canbeGeneralizedByDeviceBoundingBox( QgsRectangle( xmin, ymin, xmax, ymax ), mapToPixelTol );
}

/***************************************************************************/
/**
* Implementation of GeometrySimplifier using the Douglas-Peucker algorithm
*/
QgsTopologyPreservingSimplifier::QgsTopologyPreservingSimplifier( double tolerance ) : mTolerance( tolerance )
{
}
QgsTopologyPreservingSimplifier::~QgsTopologyPreservingSimplifier()
{
}

//! Returns a simplified version the specified geometry
QgsGeometry* QgsTopologyPreservingSimplifier::simplify( QgsGeometry* geometry )
{
return geometry->simplify( mTolerance );
}

//! Simplifies the specified geometry
bool QgsTopologyPreservingSimplifier::simplifyGeometry( QgsGeometry* geometry )
{
QgsGeometry* g = geometry->simplify( mTolerance );

if ( g )
{
size_t wkbSize = g->wkbSize();
unsigned char* wkb = (unsigned char*)malloc( wkbSize );
memcpy( wkb, g->asWkb(), wkbSize );
geometry->fromWkb( wkb, wkbSize );
delete g;

return true;
}
return false;
}
65 changes: 65 additions & 0 deletions src/core/qgsgeometrysimplifier.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/***************************************************************************
qgsgeometrysimplifier.h
---------------------
begin : December 2013
copyright : (C) 2013 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. *
* *
***************************************************************************/

#ifndef QGSGEOMETRYSIMPLIFIER_H
#define QGSGEOMETRYSIMPLIFIER_H

#include "qgsgeometry.h"

/**
* Abstract base class for simplify geometries using a specific algorithm
*/
class CORE_EXPORT QgsAbstractGeometrySimplifier
{
public:
//! Returns a simplified version the specified geometry
virtual QgsGeometry* simplify( QgsGeometry* geometry ) = 0;
//! Simplifies the specified geometry
virtual bool simplifyGeometry( QgsGeometry* geometry ) = 0;

// MapToPixel simplification helper methods
public:
//! Returns whether the device-envelope can be replaced by its BBOX when is applied the specified tolerance
static bool canbeGeneralizedByDeviceBoundingBox( const QgsRectangle& envelope, float mapToPixelTol = 1.0f );
//! Returns whether the device-geometry can be replaced by its BBOX when is applied the specified tolerance
static bool canbeGeneralizedByDeviceBoundingBox( const QVector<QPointF>& points, float mapToPixelTol = 1.0f );
};

/***************************************************************************/
/**
* Implementation of GeometrySimplifier using the Douglas-Peucker algorithm
*
* Simplifies a geometry, ensuring that the result is a valid geometry having the same dimension and number of components as the input.
* The simplification uses a maximum distance difference algorithm similar to the one used in the Douglas-Peucker algorithm.
*/
class CORE_EXPORT QgsTopologyPreservingSimplifier : public QgsAbstractGeometrySimplifier
{
public:
QgsTopologyPreservingSimplifier( double tolerance );
virtual ~QgsTopologyPreservingSimplifier();

private:
//! Distance tolerance for the simplification
double mTolerance;

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

#endif // QGSGEOMETRYSIMPLIFIER_H
401 changes: 401 additions & 0 deletions src/core/qgsmaptopixelgeometrysimplifier.cpp

Large diffs are not rendered by default.

100 changes: 100 additions & 0 deletions src/core/qgsmaptopixelgeometrysimplifier.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/***************************************************************************
qgsmaptopixelgeometrysimplifier.h
---------------------
begin : December 2013
copyright : (C) 2013 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. *
* *
***************************************************************************/

#ifndef QGSMAPTOPIXELGEOMETRYSIMPLIFIER_H
#define QGSMAPTOPIXELGEOMETRYSIMPLIFIER_H

#include "qgsgeometry.h"
#include "qgscoordinatetransform.h"
#include "qgsmaptopixel.h"

#include "qgsgeometrysimplifier.h"

/**
* Implementation of GeometrySimplifier using the "MapToPixel" algorithm
*
* Simplifies a geometry removing points within of the maximum distance difference that defines the MapToPixel info of a RenderContext request.
* This class enables simplify the geometries to be rendered in a MapCanvas target to speed up the vector drawing.
*/
class CORE_EXPORT QgsMapToPixelSimplifier : public QgsAbstractGeometrySimplifier
{
public:
QgsMapToPixelSimplifier( int simplifyFlags, const QgsCoordinateTransform* coordinateTransform, const QgsMapToPixel* mapToPixel, float mapToPixelTol );
virtual ~QgsMapToPixelSimplifier();

//! Applicable simplification flags
enum SimplifyFlag
{
NoFlags = 0, //!< No simplification can be applied
SimplifyGeometry = 1, //!< The geometries can be simplified using the current map2pixel context state
SimplifyEnvelope = 2, //!< The geometries can be fully simplified by its BoundingBox
};

private:
//! Simplify the WKB-geometry using the specified tolerance
static bool simplifyWkbGeometry( int simplifyFlags, QGis::WkbType wkbType, unsigned char* sourceWkb, size_t sourceWkbSize, unsigned char* targetWkb, size_t& targetWkbSize, const QgsRectangle& envelope, float map2pixelTol, bool writeHeader = true, bool isaLinearRing = false );

protected:
//! Current simplification flags
int mSimplifyFlags;

//! For transformation between coordinate systems from current layer to map target. Can be 0 if on-the-fly reprojection is not used
const QgsCoordinateTransform* mMapCoordTransform;
//! For transformation between map coordinates and device coordinates
const QgsMapToPixel* mMapToPixel;
//! Factor tolterance to apply in transformation between map coordinates and device coordinates
float mMapToPixelTol;

//! 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 the MapTolerance for transform between map coordinates and device coordinates
static float calculateViewPixelTolerance( const QgsRectangle& boundingRect, const QgsCoordinateTransform* ct, const QgsMapToPixel* mapToPixel );

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

const QgsCoordinateTransform* coordinateTransform() const { return mMapCoordTransform; }
void setCoordinateTransform( const QgsCoordinateTransform* ct ) { mMapCoordTransform = ct; }

const QgsMapToPixel* mapToPixel() const { return mMapToPixel; }
void setMapToPixel( const QgsMapToPixel* mtp ) { mMapToPixel = mtp; }

float mapToPixelTol() const { return mMapToPixelTol; }
void setMapToPixelTol( float map2pixelTol ) { mMapToPixelTol = map2pixelTol; }

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

// MapToPixel simplification helper methods
public:

//! Returns whether the envelope can be replaced by its BBOX when is applied the specified map2pixel context
static bool canbeGeneralizedByMapBoundingBox( const QgsRectangle& envelope,
const QgsCoordinateTransform* coordinateTransform, const QgsMapToPixel* mapToPixel, float mapToPixelTol = 1.0f );

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

//! Simplifies the geometry when is applied the specified map2pixel context
static bool simplifyGeometry( QgsGeometry* geometry,
int simplifyFlags, const QgsCoordinateTransform* coordinateTransform, const QgsMapToPixel* mapToPixel, float mapToPixelTol = 1.0f );

};

#endif // QGSMAPTOPIXELGEOMETRYSIMPLIFIER_H
4 changes: 2 additions & 2 deletions src/core/qgsrendercontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ QgsRenderContext::QgsRenderContext()
mScaleFactor( 1.0 ),
mRasterScaleFactor( 1.0 ),
mRendererScale( 1.0 ),
mLabelingEngine( NULL )
mLabelingEngine( NULL ),
mRenderingPrintComposition( false )
{

}
Expand All @@ -41,4 +42,3 @@ void QgsRenderContext::setCoordinateTransform( const QgsCoordinateTransform* t )
{
mCoordTransform = t;
}

7 changes: 7 additions & 0 deletions src/core/qgsrendercontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ class CORE_EXPORT QgsRenderContext
//! Added in QGIS v2.0
void setSelectionColor( const QColor& color ) { mSelectionColor = color; }

/**True if the rendering request comes from a print composition, false it comes from a normal window request */
void setRenderingPrintComposition( bool renderingPrintComposition ) { mRenderingPrintComposition = renderingPrintComposition; }
bool renderingPrintComposition( ) const { return mRenderingPrintComposition; }

private:

/**Painter for rendering operations*/
Expand Down Expand Up @@ -133,6 +137,9 @@ class CORE_EXPORT QgsRenderContext

/** Color used for features that are marked as selected */
QColor mSelectionColor;

/**True if the rendering request comes from a print composition, false it comes from a normal window request */
bool mRenderingPrintComposition;
};

#endif
6 changes: 6 additions & 0 deletions src/core/qgsvectordataprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,12 @@ QString QgsVectorDataProvider::capabilitiesString() const
QgsDebugMsg( "Capability: Change Geometries" );
}

if ( abilities & QgsVectorDataProvider::SimplifyGeometries )
{
abilitiesList += tr( "Simplify Geometries" );
QgsDebugMsg( "Capability: Simplify Geometries before fetch the feature" );
}

return abilitiesList.join( ", " );

}
Expand Down
2 changes: 2 additions & 0 deletions src/core/qgsvectordataprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
CreateAttributeIndex = 1 << 12,
/** allows user to select encoding */
SelectEncoding = 1 << 13,
/** supports simplification of geometries before fetch the feature */
SimplifyGeometries = 1 << 14,
};

/** bitmask of all provider's editing capabilities */
Expand Down
63 changes: 58 additions & 5 deletions src/core/qgsvectorlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
#include "qgssymbologyv2conversion.h"
#include "qgspallabeling.h"

#include "qgsmaptopixelgeometrysimplifier.h"

#ifdef TESTPROVIDERLIB
#include <dlfcn.h>
#endif
Expand Down Expand Up @@ -135,6 +137,8 @@ QgsVectorLayer::QgsVectorLayer( QString vectorLayerPath,
, mValidExtent( false )
, mSymbolFeatureCounted( false )
, mCurrentRendererContext( 0 )
, mSimplifyDrawingHints( QGis::DEFAULT_MAPTOPIXEL_THRESHOLD > 1 ? QgsVectorLayer::FullSimplification : QgsVectorLayer::DefaultSimplification )
, mSimplifyDrawingTol( QGis::DEFAULT_MAPTOPIXEL_THRESHOLD )

{
mActions = new QgsAttributeAction( this );
Expand Down Expand Up @@ -179,6 +183,12 @@ QgsVectorLayer::QgsVectorLayer( QString vectorLayerPath,
connect( this, SIGNAL( selectionChanged( QgsFeatureIds, QgsFeatureIds, bool ) ), this, SIGNAL( selectionChanged() ) );

connect( QgsProject::instance()->relationManager(), SIGNAL( relationsLoaded() ), this, SLOT( onRelationsLoaded() ) );

// Default simplify drawing configuration
QSettings settings;
setSimplifyDrawingHints( settings.value( "/qgis/simplifyDrawingHints", (int)mSimplifyDrawingHints ).toInt() );
setSimplifyDrawingTol( settings.value( "/qgis/simplifyDrawingTol", mSimplifyDrawingTol ).toFloat() );

} // QgsVectorLayer ctor


Expand Down Expand Up @@ -687,12 +697,43 @@ bool QgsVectorLayer::draw( QgsRenderContext& rendererContext )
//do startRender before getFeatures to give renderers the possibility of querying features in the startRender method
mRendererV2->startRender( rendererContext, this );

QgsFeatureIterator fit = getFeatures( QgsFeatureRequest()
.setFilterRect( rendererContext.extent() )
.setSubsetOfAttributes( attributes ) );
QgsFeatureRequest& featureRequest = QgsFeatureRequest()
.setFilterRect( rendererContext.extent() )
.setSubsetOfAttributes( attributes );

QgsFeatureIterator fit = QgsFeatureIterator();

// Enable the simplification of the geometries (Using the current map2pixel context) before fetch the features.
if ( simplifyDrawingCanbeApplied( QgsVectorLayer::GeometrySimplification | QgsVectorLayer::EnvelopeSimplification ) && !(featureRequest.flags() & QgsFeatureRequest::NoGeometry) && !rendererContext.renderingPrintComposition() )
{
QPainter* p = rendererContext.painter();
float dpi = ( p->device()->logicalDpiX() + p->device()->logicalDpiY() ) / 2;
float map2pixelTol = mSimplifyDrawingTol * 96.0f/dpi;

int simplifyFlags = QgsMapToPixelSimplifier::NoFlags;
if ( mSimplifyDrawingHints & QgsVectorLayer::GeometrySimplification ) simplifyFlags |= QgsMapToPixelSimplifier::SimplifyGeometry;
if ( mSimplifyDrawingHints & QgsVectorLayer::EnvelopeSimplification ) simplifyFlags |= QgsMapToPixelSimplifier::SimplifyEnvelope;

QgsFeatureRequest::Flags requestFlags = QgsFeatureRequest::NoFlags;
if ( mSimplifyDrawingHints & QgsVectorLayer::GeometrySimplification ) requestFlags |= QgsFeatureRequest::SimplifyGeometry;
if ( mSimplifyDrawingHints & QgsVectorLayer::EnvelopeSimplification ) requestFlags |= QgsFeatureRequest::SimplifyEnvelope;

featureRequest.setFlags( featureRequest.flags() | requestFlags );
featureRequest.setCoordinateTransform( rendererContext.coordinateTransform() );
featureRequest.setMapToPixel( &rendererContext.mapToPixel() );
featureRequest.setMapToPixelTol( map2pixelTol );

if (( mRendererV2->capabilities() & QgsFeatureRendererV2::SymbolLevels )
&& mRendererV2->usingSymbolLevels() )
QgsMapToPixelSimplifier* simplifier =
new QgsMapToPixelSimplifier( simplifyFlags, rendererContext.coordinateTransform(), &rendererContext.mapToPixel(), map2pixelTol );

fit = QgsFeatureIterator( new QgsSimplifiedVectorLayerFeatureIterator( this, featureRequest, simplifier ) );
}
else
{
fit = getFeatures( featureRequest );
}

if (( mRendererV2->capabilities() & QgsFeatureRendererV2::SymbolLevels ) && mRendererV2->usingSymbolLevels() )
drawRendererV2Levels( fit, rendererContext, labeling );
else
drawRendererV2( fit, rendererContext, labeling );
Expand Down Expand Up @@ -1206,6 +1247,10 @@ bool QgsVectorLayer::setSubsetString( QString subset )
return res;
}

bool QgsVectorLayer::simplifyDrawingCanbeApplied( int simplifyHint ) const
{
return mDataProvider && ( mSimplifyDrawingHints & simplifyHint ) && !mEditBuffer && ( !mCurrentRendererContext || !mCurrentRendererContext->renderingPrintComposition() );
}

QgsFeatureIterator QgsVectorLayer::getFeatures( const QgsFeatureRequest& request )
{
Expand Down Expand Up @@ -1815,6 +1860,10 @@ bool QgsVectorLayer::readSymbology( const QDomNode& node, QString& errorMessage
mLabel->setMinScale( e.attribute( "minLabelScale", "1" ).toFloat() );
mLabel->setMaxScale( e.attribute( "maxLabelScale", "100000000" ).toFloat() );

// get the simplification drawing configuration
setSimplifyDrawingHints( e.attribute( "simplifyDrawingHints", "7" ).toInt() );
setSimplifyDrawingTol( e.attribute( "simplifyDrawingTol", "1" ).toFloat() );

//also restore custom properties (for labeling-ng)
readCustomProperties( node, "labeling" );

Expand Down Expand Up @@ -2148,6 +2197,10 @@ bool QgsVectorLayer::writeSymbology( QDomNode& node, QDomDocument& doc, QString&
mapLayerNode.setAttribute( "minLabelScale", QString::number( mLabel->minScale() ) );
mapLayerNode.setAttribute( "maxLabelScale", QString::number( mLabel->maxScale() ) );

// save the simplification drawing configuration
mapLayerNode.setAttribute( "simplifyDrawingHints", QString::number( mSimplifyDrawingHints ) );
mapLayerNode.setAttribute( "simplifyDrawingTol", QString::number( mSimplifyDrawingTol ) );

//save customproperties (for labeling ng)
writeCustomProperties( node, doc );

Expand Down
27 changes: 27 additions & 0 deletions src/core/qgsvectorlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,28 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
/** @note not available in python bindings */
inline QgsGeometryCache* cache() { return mCache; }

/** Set the Map2pixel simplification threshold for fast rendering of features */
void setSimplifyDrawingTol( float simplifyDrawingTol ) { mSimplifyDrawingTol = simplifyDrawingTol; }
/** Returns the Map2pixel simplification threshold for fast rendering of features */
float simplifyDrawingTol() const { return mSimplifyDrawingTol; }

/** Simplification flags for fast rendering of features */
enum SimplifyHint
{
NoSimplification = 0, //!< No simplification can be applied
GeometrySimplification = 1, //!< The geometries can be simplified using the current map2pixel context state
EnvelopeSimplification = 2, //!< The geometries can be fully simplified by its BoundingBox using the current map2pixel context state
AntialiasingSimplification = 4, //!< The geometries can be rendered with 'AntiAliasing' disabled because of it is '1-pixel size'
DefaultSimplification = 3, //!< Default simplification hints can be applied ( Geometry + Envelope )
FullSimplification = 7, //!< All simplification hints can be applied ( Geometry + Envelope + AA-disabling )
};
/** Set the Map2pixel simplification hints for fast rendering of features */
void setSimplifyDrawingHints( int simplifyDrawingHints ) { mSimplifyDrawingHints = simplifyDrawingHints; }
/** Returns the Map2pixel simplification hints for fast rendering of features */
int simplifyDrawingHints() const { return mSimplifyDrawingHints; }

/** Returns whether the VectorLayer can apply the specified simplification hint */
bool simplifyDrawingCanbeApplied( int simplifyHint ) const;

public slots:
/**
Expand Down Expand Up @@ -1634,6 +1656,11 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
/** Renderer object which holds the information about how to display the features */
QgsFeatureRendererV2 *mRendererV2;

/** Map2pixel geometry simplification threshold for fast rendering of features */
float mSimplifyDrawingTol;
/** Map2pixel geometry simplification hints for fast rendering of features */
int mSimplifyDrawingHints;

/** Label */
QgsLabel *mLabel;

Expand Down
43 changes: 43 additions & 0 deletions src/core/qgsvectorlayerfeatureiterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -589,3 +589,46 @@ void QgsVectorLayerFeatureIterator::updateFeatureGeometry( QgsFeature &f )
if ( mChangedGeometries.contains( f.id() ) )
f.setGeometry( mChangedGeometries[f.id()] );
}

/***************************************************************************
QgsSimplifiedVectorLayerFeatureIterator class
----------------------
begin : December 2013
copyright : (C) 2013 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. *
* *
***************************************************************************/

QgsSimplifiedVectorLayerFeatureIterator::QgsSimplifiedVectorLayerFeatureIterator( QgsVectorLayer* layer, const QgsFeatureRequest& request, QgsAbstractGeometrySimplifier* simplifier )
: QgsVectorLayerFeatureIterator( layer, request )
, mSimplifier( simplifier )
{
mSupportsPresimplify = layer->dataProvider()->capabilities() & QgsVectorDataProvider::SimplifyGeometries;
}
QgsSimplifiedVectorLayerFeatureIterator::~QgsSimplifiedVectorLayerFeatureIterator()
{
if ( mSimplifier )
{
delete mSimplifier;
mSimplifier = NULL;
}
}

//! fetch next feature, return true on success
bool QgsSimplifiedVectorLayerFeatureIterator::fetchFeature( QgsFeature& feature )
{
if (QgsVectorLayerFeatureIterator::fetchFeature( feature ))
{
const QgsMapToPixel* mtp = mRequest.mapToPixel();
if ( mtp && !mSupportsPresimplify && mSimplifier ) mSimplifier->simplifyGeometry( feature.geometry() );
return true;
}
return false;
}
38 changes: 38 additions & 0 deletions src/core/qgsvectorlayerfeatureiterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,42 @@ class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureItera
QMap<QgsVectorLayer*, FetchJoinInfo> mFetchJoinInfo;
};

/***************************************************************************
QgsSimplifiedVectorLayerFeatureIterator class
----------------------
begin : December 2013
copyright : (C) 2013 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 "qgsgeometrysimplifier.h"

//! Provides a specialized VectorLayerFeatureIterator for enable simplification of the geometries fetched
class CORE_EXPORT QgsSimplifiedVectorLayerFeatureIterator : public QgsVectorLayerFeatureIterator
{
public:
QgsSimplifiedVectorLayerFeatureIterator( QgsVectorLayer* layer, const QgsFeatureRequest& request, QgsAbstractGeometrySimplifier* simplifier );
~QgsSimplifiedVectorLayerFeatureIterator( );

protected:
//! fetch next feature, return true on success
virtual bool fetchFeature( QgsFeature& feature );

private:
//! Related geometry simplifier
QgsAbstractGeometrySimplifier* mSimplifier;
//! Indicates the related vector provider supports simplify the geometries before fecth the feature
bool mSupportsPresimplify;
};

/***************************************************************************/

#endif // QGSVECTORLAYERFEATUREITERATOR_H
9 changes: 4 additions & 5 deletions src/core/symbology-ng/qgsfillsymbollayerv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ void QgsSimpleFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList<Q
applyDataDefinedSymbology( context, mBrush, mPen, mSelPen );

p->setBrush( context.selected() ? mSelBrush : mBrush );
p->setPen( mPen );
p->setPen( context.selected() ? mSelPen : mPen );

QPointF offset;
Expand All @@ -183,7 +182,7 @@ void QgsSimpleFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList<Q
p->translate( offset );
}

_renderPolygon( p, points, rings );
_renderPolygon( p, points, rings, context );

if ( !mOffset.isNull() )
{
Expand Down Expand Up @@ -682,7 +681,7 @@ void QgsGradientFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList
p->translate( offset );
}

_renderPolygon( p, points, rings );
_renderPolygon( p, points, rings, context );

if ( !mOffset.isNull() )
{
Expand Down Expand Up @@ -760,7 +759,7 @@ void QgsImageFillSymbolLayer::renderPolygon( const QPolygonF& points, QList<QPol
//if ( ! selectionIsOpaque )
// selColor.setAlphaF( context.alpha() );
p->setBrush( QBrush( selColor ) );
_renderPolygon( p, points, rings );
_renderPolygon( p, points, rings, context );
}

if ( qgsDoubleNear( mNextAngle, 0.0 ) )
Expand All @@ -775,7 +774,7 @@ void QgsImageFillSymbolLayer::renderPolygon( const QPolygonF& points, QList<QPol
rotatedBrush.setTransform( t );
p->setBrush( rotatedBrush );
}
_renderPolygon( p, points, rings );
_renderPolygon( p, points, rings, context );
if ( mOutline )
{
mOutline->renderPolyline( points, context.feature(), context.renderContext(), -1, selectFillBorder && context.selected() );
Expand Down
10 changes: 10 additions & 0 deletions src/core/symbology-ng/qgslinesymbollayerv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "qgsrendercontext.h"
#include "qgslogger.h"
#include "qgsvectorlayer.h"
#include "qgsgeometrysimplifier.h"

#include <QPainter>
#include <QDomDocument>
Expand Down Expand Up @@ -180,6 +181,15 @@ void QgsSimpleLineSymbolLayerV2::renderPolyline( const QPolygonF& points, QgsSym

p->setPen( context.selected() ? mSelPen : mPen );

// Disable 'Antialiasing' if the geometry was generalized in the current RenderContext (We known that it must have least #2 points).
if ( points.size()<=2 && context.layer() && context.layer()->simplifyDrawingCanbeApplied( QgsVectorLayer::AntialiasingSimplification ) && QgsAbstractGeometrySimplifier::canbeGeneralizedByDeviceBoundingBox( points, context.layer()->simplifyDrawingTol() ) && (p->renderHints() & QPainter::Antialiasing) )
{
p->setRenderHint( QPainter::Antialiasing, false );
p->drawPolyline( points );
p->setRenderHint( QPainter::Antialiasing, true );
return;
}

if ( offset == 0 )
{
p->drawPolyline( points );
Expand Down
29 changes: 15 additions & 14 deletions src/core/symbology-ng/qgsrendererv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ const unsigned char* QgsFeatureRendererV2::_getLineString( QPolygonF& pts, QgsRe
wkb += sizeof( unsigned int );

bool hasZValue = ( wkbType == QGis::WKBLineString25D );

int sizeOfDoubleX = sizeof(double);
int sizeOfDoubleY = hasZValue ? 2*sizeof(double) : sizeof(double);

double x, y;
const QgsCoordinateTransform* ct = context.coordinateTransform();
const QgsMapToPixel& mtp = context.mapToPixel();
Expand All @@ -86,13 +90,8 @@ const unsigned char* QgsFeatureRendererV2::_getLineString( QPolygonF& pts, QgsRe
QPointF* ptr = pts.data();
for ( unsigned int i = 0; i < nPoints; ++i, ++ptr )
{
x = *(( double * ) wkb );
wkb += sizeof( double );
y = *(( double * ) wkb );
wkb += sizeof( double );

if ( hasZValue ) // ignore Z value
wkb += sizeof( double );
memcpy( &x, wkb, sizeof( double ) ); wkb += sizeOfDoubleX;
memcpy( &y, wkb, sizeof( double ) ); wkb += sizeOfDoubleY;

*ptr = QPointF( x, y );
}
Expand Down Expand Up @@ -126,6 +125,10 @@ const unsigned char* QgsFeatureRendererV2::_getPolygon( QPolygonF& pts, QList<QP
return wkb;

bool hasZValue = ( wkbType == QGis::WKBPolygon25D );

int sizeOfDoubleX = sizeof(double);
int sizeOfDoubleY = hasZValue ? 2*sizeof(double) : sizeof(double);

double x, y;
holes.clear();

Expand All @@ -146,20 +149,18 @@ const unsigned char* QgsFeatureRendererV2::_getPolygon( QPolygonF& pts, QList<QP
QPointF* ptr = poly.data();
for ( unsigned int jdx = 0; jdx < nPoints; ++jdx, ++ptr )
{
x = *(( double * ) wkb ); wkb += sizeof( double );
y = *(( double * ) wkb ); wkb += sizeof( double );
memcpy( &x, wkb, sizeof( double ) ); wkb += sizeOfDoubleX;
memcpy( &y, wkb, sizeof( double ) ); wkb += sizeOfDoubleY;

*ptr = QPointF( x, y );

if ( hasZValue )
wkb += sizeof( double );
}

if ( nPoints < 1 )
continue;

//clip close to view extent
QgsClipper::trimPolygon( poly, clipRect );
//clip close to view extent, if needed
QRectF ptsRect = poly.boundingRect();
if (!context.extent().contains( ptsRect )) QgsClipper::trimPolygon( poly, clipRect );

//transform the QPolygonF to screen coordinates
if ( ct )
Expand Down
12 changes: 11 additions & 1 deletion src/core/symbology-ng/qgssymbollayerv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "qgsrendercontext.h"
#include "qgsvectorlayer.h"
#include "qgsdxfexport.h"
#include "qgsgeometrysimplifier.h"

#include <QSize>
#include <QPainter>
Expand Down Expand Up @@ -350,13 +351,22 @@ void QgsFillSymbolLayerV2::drawPreviewIcon( QgsSymbolV2RenderContext& context, Q
stopRender( context );
}

void QgsFillSymbolLayerV2::_renderPolygon( QPainter* p, const QPolygonF& points, const QList<QPolygonF>* rings )
void QgsFillSymbolLayerV2::_renderPolygon( QPainter* p, const QPolygonF& points, const QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
{
if ( !p )
{
return;
}

// Disable 'Antialiasing' if the geometry was generalized in the current RenderContext (We known that it must have least #5 points).
if ( points.size()<=5 && context.layer() && context.layer()->simplifyDrawingCanbeApplied( QgsVectorLayer::AntialiasingSimplification ) && QgsAbstractGeometrySimplifier::canbeGeneralizedByDeviceBoundingBox( points, context.layer()->simplifyDrawingTol() ) && (p->renderHints() & QPainter::Antialiasing) )
{
p->setRenderHint( QPainter::Antialiasing, false );
p->drawRect( points.boundingRect() );
p->setRenderHint( QPainter::Antialiasing, true );
return;
}

if ( rings == NULL )
{
// simple polygon without holes
Expand Down
2 changes: 1 addition & 1 deletion src/core/symbology-ng/qgssymbollayerv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ class CORE_EXPORT QgsFillSymbolLayerV2 : public QgsSymbolLayerV2
protected:
QgsFillSymbolLayerV2( bool locked = false );
/**Default method to render polygon*/
void _renderPolygon( QPainter* p, const QPolygonF& points, const QList<QPolygonF>* rings );
void _renderPolygon( QPainter* p, const QPolygonF& points, const QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context );

double mAngle;
};
Expand Down
19 changes: 17 additions & 2 deletions src/core/symbology-ng/qgssymbolv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
#include <cmath>

QgsSymbolV2::QgsSymbolV2( SymbolType type, QgsSymbolLayerV2List layers )
: mType( type ), mLayers( layers ), mAlpha( 1.0 ), mRenderHints( 0 )
: mType( type ), mLayers( layers ), mAlpha( 1.0 ), mRenderHints( 0 ), mLayer( NULL )
{

// check they're all correct symbol layers
Expand Down Expand Up @@ -215,17 +215,24 @@ bool QgsSymbolV2::changeSymbolLayer( int index, QgsSymbolLayerV2* layer )

void QgsSymbolV2::startRender( QgsRenderContext& context, const QgsVectorLayer* layer )
{
mLayer = layer;

QgsSymbolV2RenderContext symbolContext( context, outputUnit(), mAlpha, false, mRenderHints );
symbolContext.setLayer( layer );
symbolContext.setLayer( mLayer );

for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
( *it )->startRender( symbolContext );
}

void QgsSymbolV2::stopRender( QgsRenderContext& context )
{
QgsSymbolV2RenderContext symbolContext( context, outputUnit(), mAlpha, false, mRenderHints );
symbolContext.setLayer( mLayer );

for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
( *it )->stopRender( symbolContext );

mLayer = NULL;
}

void QgsSymbolV2::setColor( const QColor& color )
Expand All @@ -252,6 +259,8 @@ void QgsSymbolV2::drawPreviewIcon( QPainter* painter, QSize size )
{
QgsRenderContext context = QgsSymbolLayerV2Utils::createRenderContext( painter );
QgsSymbolV2RenderContext symbolContext( context, outputUnit(), mAlpha, false, mRenderHints );
symbolContext.setLayer( mLayer );

for ( QgsSymbolLayerV2List::iterator it = mLayers.begin(); it != mLayers.end(); ++it )
{
if ( mType == Fill && ( *it )->type() == Line )
Expand Down Expand Up @@ -532,6 +541,8 @@ QgsSymbolV2::ScaleMethod QgsMarkerSymbolV2::scaleMethod()
void QgsMarkerSymbolV2::renderPoint( const QPointF& point, const QgsFeature* f, QgsRenderContext& context, int layer, bool selected )
{
QgsSymbolV2RenderContext symbolContext( context, outputUnit(), mAlpha, selected, mRenderHints, f );
symbolContext.setLayer( mLayer );

if ( layer != -1 )
{
if ( layer >= 0 && layer < mLayers.count() )
Expand Down Expand Up @@ -600,6 +611,8 @@ double QgsLineSymbolV2::width()
void QgsLineSymbolV2::renderPolyline( const QPolygonF& points, const QgsFeature* f, QgsRenderContext& context, int layer, bool selected )
{
QgsSymbolV2RenderContext symbolContext( context, outputUnit(), mAlpha, selected, mRenderHints, f );
symbolContext.setLayer( mLayer );

if ( layer != -1 )
{
if ( layer >= 0 && layer < mLayers.count() )
Expand Down Expand Up @@ -635,6 +648,8 @@ QgsFillSymbolV2::QgsFillSymbolV2( QgsSymbolLayerV2List layers )
void QgsFillSymbolV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, const QgsFeature* f, QgsRenderContext& context, int layer, bool selected )
{
QgsSymbolV2RenderContext symbolContext( context, outputUnit(), mAlpha, selected, mRenderHints, f );
symbolContext.setLayer( mLayer );

if ( layer != -1 )
{
if ( layer >= 0 && layer < mLayers.count() )
Expand Down
5 changes: 5 additions & 0 deletions src/core/symbology-ng/qgssymbolv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ class CORE_EXPORT QgsSymbolV2

QSet<QString> usedAttributes() const;

void setLayer( const QgsVectorLayer* layer ) { mLayer = layer; }
const QgsVectorLayer* layer() const { return mLayer; }

protected:
QgsSymbolV2( SymbolType type, QgsSymbolLayerV2List layers ); // can't be instantiated

Expand All @@ -145,6 +148,8 @@ class CORE_EXPORT QgsSymbolV2
qreal mAlpha;

int mRenderHints;

const QgsVectorLayer* mLayer; //current vectorlayer
};

///////////////////////
Expand Down
2 changes: 1 addition & 1 deletion src/providers/ogr/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

SET (OGR_SRCS qgsogrprovider.cpp qgsogrdataitems.cpp qgsogrfeatureiterator.cpp)
SET (OGR_SRCS qgsogrprovider.cpp qgsogrdataitems.cpp qgsogrfeatureiterator.cpp qgsogrmaptopixelgeometrysimplifier.cpp)

SET(OGR_MOC_HDRS qgsogrprovider.h qgsogrdataitems.h)

Expand Down
64 changes: 62 additions & 2 deletions src/providers/ogr/qgsogrfeatureiterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,11 +233,15 @@ bool QgsOgrFeatureIterator::readFeature( OGRFeatureH fet, QgsFeature& feature )

if ( geom )
{
fetchedFeature( fet, geom );

// get the wkb representation
unsigned char *wkb = new unsigned char[OGR_G_WkbSize( geom )];
int memorySize = OGR_G_WkbSize( geom );
unsigned char *wkb = new unsigned char[memorySize];
OGR_G_ExportToWkb( geom, ( OGRwkbByteOrder ) QgsApplication::endian(), wkb );

feature.setGeometryAndOwnership( wkb, OGR_G_WkbSize( geom ) );
QgsGeometry* geometry = feature.geometry();
if ( !geometry ) feature.setGeometryAndOwnership( wkb, memorySize ); else geometry->fromWkb( wkb, memorySize );
}
if (( useIntersect && ( !feature.geometry() || !feature.geometry()->intersects( mRequest.filterRect() ) ) )
|| ( geometryTypeFilter && ( !feature.geometry() || QgsOgrProvider::ogrWkbSingleFlatten(( OGRwkbGeometryType )feature.geometry()->wkbType() ) != P->mOgrGeometryTypeFilter ) ) )
Expand Down Expand Up @@ -272,3 +276,59 @@ bool QgsOgrFeatureIterator::readFeature( OGRFeatureH fet, QgsFeature& feature )

return true;
}

//! Notified a new OGRFeatureH fecthed from data provider
void QgsOgrFeatureIterator::fetchedFeature( OGRFeatureH feature, OGRGeometryH geometry )
{
}

/***************************************************************************
QgsOgrSimplifiedFeatureIterator class
----------------------
begin : December 2013
copyright : (C) 2013 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. *
* *
***************************************************************************/

//! Provides a specialized FeatureIterator for enable simplification of the geometries
QgsOgrSimplifiedFeatureIterator::QgsOgrSimplifiedFeatureIterator( QgsOgrProvider* p, const QgsFeatureRequest& request )
: QgsOgrFeatureIterator( p, request )
, mSimplifier( NULL )
{
QgsFeatureRequest::Flags requestFlags = request.flags();

int simplifyFlags = QgsMapToPixelSimplifier::NoFlags;
if ( requestFlags & QgsFeatureRequest::SimplifyGeometry ) simplifyFlags |= QgsMapToPixelSimplifier::SimplifyGeometry;
if ( requestFlags & QgsFeatureRequest::SimplifyEnvelope ) simplifyFlags |= QgsMapToPixelSimplifier::SimplifyEnvelope;

if ( simplifyFlags != QgsMapToPixelSimplifier::NoFlags )
{
mSimplifier = new QgsOgrMapToPixelSimplifier( simplifyFlags, request.coordinateTransform(), request.mapToPixel(), request.mapToPixelTol() );
}
}
QgsOgrSimplifiedFeatureIterator::~QgsOgrSimplifiedFeatureIterator( )
{
if ( mSimplifier )
{
delete mSimplifier;
mSimplifier = NULL;
}
}

//! Notified a new OGRFeatureH fecthed from data provider
void QgsOgrSimplifiedFeatureIterator::fetchedFeature( OGRFeatureH feature, OGRGeometryH geometry )
{
if ( mSimplifier && (mSimplifier->simplifyFlags() & (QgsMapToPixelSimplifier::SimplifyGeometry | QgsMapToPixelSimplifier::SimplifyEnvelope) ) )
{
mSimplifier->simplifyGeometry( (OGRGeometry*) geometry );
}
QgsOgrFeatureIterator::fetchedFeature( feature, geometry );
}
39 changes: 39 additions & 0 deletions src/providers/ogr/qgsogrfeatureiterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ class QgsOgrFeatureIterator : public QgsAbstractFeatureIterator
//! Get an attribute associated with a feature
void getFeatureAttribute( OGRFeatureH ogrFet, QgsFeature & f, int attindex );

//! Notified a new OGRFeatureH fecthed from data provider
virtual void fetchedFeature( OGRFeatureH feature, OGRGeometryH geometry );

bool mFeatureFetched;

OGRDataSourceH ogrDataSource;
Expand All @@ -58,5 +61,41 @@ class QgsOgrFeatureIterator : public QgsAbstractFeatureIterator
bool mFetchGeometry;
};

/***************************************************************************
QgsOgrSimplifiedFeatureIterator class
----------------------
begin : December 2013
copyright : (C) 2013 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 "qgsogrmaptopixelgeometrysimplifier.h"

class OGRRawPoint;
class OGRGeometry;

//! Provides a specialized FeatureIterator for enable simplification of the geometries fetched
class QgsOgrSimplifiedFeatureIterator : public QgsOgrFeatureIterator
{
public:
QgsOgrSimplifiedFeatureIterator( QgsOgrProvider* p, const QgsFeatureRequest& request );
~QgsOgrSimplifiedFeatureIterator( );

protected:
//! Notified a new OGRFeatureH fecthed from data provider
virtual void fetchedFeature( OGRFeatureH feature, OGRGeometryH geometry );

private:
//! Related geometry simplifier
QgsOgrMapToPixelSimplifier* mSimplifier;
};

#endif // QGSOGRFEATUREITERATOR_H
201 changes: 201 additions & 0 deletions src/providers/ogr/qgsogrmaptopixelgeometrysimplifier.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/***************************************************************************
qgsogrmaptopixelgeometrysimplifier.cpp
---------------------
begin : December 2013
copyright : (C) 2013 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 "qgsogrmaptopixelgeometrysimplifier.h"
#include "qgsogrprovider.h"

QgsOgrMapToPixelSimplifier::QgsOgrMapToPixelSimplifier( int simplifyFlags, const QgsCoordinateTransform* coordinateTransform, const QgsMapToPixel* mapTolPixel, float mapToPixelTol ) : QgsMapToPixelSimplifier( simplifyFlags, coordinateTransform, mapTolPixel, mapToPixelTol )
{
mPointBufferCount = 512;
mPointBufferPtr = (OGRRawPoint*)OGRMalloc( mPointBufferCount * sizeof(OGRRawPoint) );
}
QgsOgrMapToPixelSimplifier::~QgsOgrMapToPixelSimplifier()
{
if ( mPointBufferPtr )
{
OGRFree( mPointBufferPtr );
mPointBufferPtr = NULL;
}
}

//! Returns a point buffer of the specified size
OGRRawPoint* QgsOgrMapToPixelSimplifier::mallocPoints( int numPoints )
{
if ( mPointBufferPtr && mPointBufferCount < numPoints )
{
OGRFree( mPointBufferPtr );
mPointBufferPtr = NULL;
}
if ( mPointBufferPtr==NULL )
{
mPointBufferCount = numPoints;
mPointBufferPtr = (OGRRawPoint*)OGRMalloc( mPointBufferCount * sizeof(OGRRawPoint) );
}
return mPointBufferPtr;
}

//////////////////////////////////////////////////////////////////////////////////////////////
// Helper simplification methods

//! Simplifies the OGR-geometry (Removing duplicated points) when is applied the specified map2pixel context
bool QgsOgrMapToPixelSimplifier::simplifyOgrGeometry( QGis::GeometryType geometryType, const QgsRectangle& envelope, double* xptr, int xStride, double* yptr, int yStride, int pointCount, int& pointSimplifiedCount)
{
bool canbeGeneralizable = ( mSimplifyFlags & QgsMapToPixelSimplifier::SimplifyGeometry );

pointSimplifiedCount = pointCount;
if ( geometryType == QGis::Point || geometryType == QGis::UnknownGeometry ) return false;
pointSimplifiedCount = 0;

double map2pixelTol = mMapToPixelTol * QgsMapToPixelSimplifier::calculateViewPixelTolerance( envelope, mMapCoordTransform, mMapToPixel );
map2pixelTol *= map2pixelTol; //-> Use mappixelTol for 'LengthSquare' calculations.
double x,y, lastX=0, lastY=0;

char* xsourcePtr = (char*)xptr;
char* ysourcePtr = (char*)yptr;
char* xtargetPtr = (char*)xptr;
char* ytargetPtr = (char*)yptr;

for ( int i = 0, numPoints = geometryType==QGis::Polygon ? pointCount-1 : pointCount; i < numPoints; ++i )
{
memcpy( &x, xsourcePtr, sizeof( double ) ); xsourcePtr += xStride;
memcpy( &y, ysourcePtr, sizeof( double ) ); ysourcePtr += yStride;

if ( i==0 || !canbeGeneralizable || QgsMapToPixelSimplifier::calculateLengthSquared2D(x,y,lastX,lastY)>map2pixelTol )
{
memcpy( xtargetPtr, &x, sizeof( double ) ); lastX = x; xtargetPtr += xStride;
memcpy( ytargetPtr, &y, sizeof( double ) ); lastY = y; ytargetPtr += yStride;
pointSimplifiedCount++;
}
}
if ( geometryType == QGis::Polygon )
{
memcpy( xtargetPtr, xptr, sizeof( double ) );
memcpy( ytargetPtr, yptr, sizeof( double ) );
pointSimplifiedCount++;
}
return pointSimplifiedCount!=pointCount;
}

//! Simplifies the OGR-geometry (Removing duplicated points) when is applied the specified map2pixel context
bool QgsOgrMapToPixelSimplifier::simplifyOgrGeometry( OGRGeometry* geometry, bool isaLinearRing )
{
OGRwkbGeometryType wkbGeometryType = wkbFlatten( geometry->getGeometryType() );

// Simplify the geometry rewriting temporally its WKB-stream for saving calloc's.
if ( wkbGeometryType == wkbLineString )
{
OGRLineString* lineString = (OGRLineString*)geometry;

int numPoints = lineString->getNumPoints();
if ( (isaLinearRing && numPoints<=5) || (!isaLinearRing && numPoints<=2) ) return false;

OGREnvelope env;
geometry->getEnvelope( &env );
QgsRectangle envelope( env.MinX, env.MinY, env.MaxX, env.MaxY );

// Can replace the geometry by its BBOX ?
if ( (mSimplifyFlags & QgsMapToPixelSimplifier::SimplifyEnvelope) && canbeGeneralizedByMapBoundingBox( envelope ) )
{
OGRRawPoint* points = NULL;
int numPoints = 0;

double x1 = envelope.xMinimum();
double y1 = envelope.yMinimum();
double x2 = envelope.xMaximum();
double y2 = envelope.yMaximum();

if ( isaLinearRing )
{
numPoints = 5;
points = mallocPoints( numPoints );
points[0].x = x1; points[0].y = y1;
points[1].x = x2; points[1].y = y1;
points[2].x = x2; points[2].y = y2;
points[3].x = x1; points[3].y = y2;
points[4].x = x1; points[4].y = y1;
}
else
{
numPoints = 2;
points = mallocPoints( numPoints );
points[0].x = x1; points[0].y = y1;
points[1].x = x2; points[1].y = y2;
}
lineString->setPoints( numPoints, points );
lineString->flattenTo2D();
return true;
}
else
if ( mSimplifyFlags & QgsMapToPixelSimplifier::SimplifyGeometry )
{
QGis::GeometryType geometryType = isaLinearRing ? QGis::Polygon : QGis::Line;
int numSimplifiedPoints = 0;

OGRRawPoint* points = mallocPoints( numPoints );
double* xptr = (double*)points;
double* yptr = xptr+1;
lineString->getPoints( points );

if ( simplifyOgrGeometry( geometryType, envelope, xptr, 16, yptr, 16, numPoints, numSimplifiedPoints ) )
{
lineString->setPoints( numSimplifiedPoints, points );
lineString->flattenTo2D();
}
return numSimplifiedPoints!=numPoints;
}
}
else
if ( wkbGeometryType == wkbPolygon )
{
OGRPolygon* polygon = (OGRPolygon*)geometry;
bool result = simplifyOgrGeometry( polygon->getExteriorRing(), true );

for ( int i = 0, numInteriorRings = polygon->getNumInteriorRings(); i < numInteriorRings; ++i )
{
result |= simplifyOgrGeometry( polygon->getInteriorRing(i), true );
}
if ( result ) polygon->flattenTo2D();
return result;
}
else
if ( wkbGeometryType == wkbMultiLineString || wkbGeometryType == wkbMultiPolygon )
{
OGRGeometryCollection* collection = (OGRGeometryCollection*)geometry;
bool result = false;

for ( int i = 0, numGeometries = collection->getNumGeometries(); i < numGeometries; ++i )
{
result |= simplifyOgrGeometry( collection->getGeometryRef(i), wkbGeometryType==wkbMultiPolygon );
}
if ( result ) collection->flattenTo2D();
return result;
}
return false;
}

//////////////////////////////////////////////////////////////////////////////////////////////

//! Simplifies the specified geometry
bool QgsOgrMapToPixelSimplifier::simplifyGeometry( OGRGeometry* geometry )
{
OGRwkbGeometryType wkbGeometryType = QgsOgrProvider::ogrWkbSingleFlatten( geometry->getGeometryType() );

if ( wkbGeometryType == wkbLineString || wkbGeometryType == wkbPolygon )
{
return simplifyOgrGeometry( geometry, wkbGeometryType==wkbPolygon );
}
return false;
}
54 changes: 54 additions & 0 deletions src/providers/ogr/qgsogrmaptopixelgeometrysimplifier.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/***************************************************************************
qgsogrmaptopixelgeometrysimplifier.h
---------------------
begin : December 2013
copyright : (C) 2013 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. *
* *
***************************************************************************/

#ifndef QGSOGRMAPTOPIXELGEOMETRYSIMPLIFIER_H
#define QGSOGRMAPTOPIXELGEOMETRYSIMPLIFIER_H

#include "qgsmaptopixelgeometrysimplifier.h"
#include <ogr_geometry.h>

/**
* OGR implementation of GeometrySimplifier using the "MapToPixel" algorithm
*
* Simplifies a geometry removing points within of the maximum distance difference that defines the MapToPixel info of a RenderContext request.
* This class enables simplify the geometries to be rendered in a MapCanvas target to speed up the vector drawing.
*/
class QgsOgrMapToPixelSimplifier : public QgsMapToPixelSimplifier
{
public:
QgsOgrMapToPixelSimplifier( int simplifyFlags, const QgsCoordinateTransform* coordinateTransform, const QgsMapToPixel* mapToPixel, float mapToPixelTol );
virtual ~QgsOgrMapToPixelSimplifier();

private:
//! Point memory buffer for optimize the simplification process
OGRRawPoint* mPointBufferPtr;
//! Current Point memory buffer size
int mPointBufferCount;

//! Simplifies the OGR-geometry (Removing duplicated points) when is applied the specified map2pixel context
bool simplifyOgrGeometry( QGis::GeometryType geometryType, const QgsRectangle& envelope, double* xptr, int xStride, double* yptr, int yStride, int pointCount, int& pointSimplifiedCount);
//! Simplifies the OGR-geometry (Removing duplicated points) when is applied the specified map2pixel context
bool simplifyOgrGeometry( OGRGeometry* geometry, bool isaLinearRing );

//! Returns a point buffer of the specified size
OGRRawPoint* mallocPoints( int numPoints );

public:
//! Simplifies the specified geometry
bool simplifyGeometry( OGRGeometry* geometry );
};

#endif // QGSOGRMAPTOPIXELGEOMETRYSIMPLIFIER_H
6 changes: 6 additions & 0 deletions src/providers/ogr/qgsogrprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,9 @@ void QgsOgrProvider::setRelevantFields( OGRLayerH ogrLayer, bool fetchGeometry,

QgsFeatureIterator QgsOgrProvider::getFeatures( const QgsFeatureRequest& request )
{
if ( request.flags() & ( QgsFeatureRequest::SimplifyGeometry | QgsFeatureRequest::SimplifyEnvelope ) )
return QgsFeatureIterator( new QgsOgrSimplifiedFeatureIterator( this, request ) );

return QgsFeatureIterator( new QgsOgrFeatureIterator( this, request ) );
}

Expand Down Expand Up @@ -1493,6 +1496,9 @@ int QgsOgrProvider::capabilities() const
ability &= ~( AddAttributes | DeleteFeatures );
}
}

// By default, supports simplification of geometries before fetch the OGR-feature.
ability |= SimplifyGeometries;
}

return ability;
Expand Down
176 changes: 176 additions & 0 deletions src/ui/qgsoptionsbase.ui
Original file line number Diff line number Diff line change
Expand Up @@ -1661,6 +1661,182 @@
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QGroupBox" name="mSimplifyDrawingGroupBox">
<property name="title">
<string>Simplify geometries by default</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="_14">
<item row="0" column="2" colspan="3">
<widget class="QLabel" name="label_59">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;If checked, new vector layers added to the map will automatically use geometry simplification to speed up rendering. The simplification applies only during rendering of the layer and does not modify the layer geometry.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="margin">
<number>2</number>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLabel" name="label_56">
<property name="text">
<string>Simplification factor (higher values result in more simplification): </string>
</property>
<property name="margin">
<number>2</number>
</property>
</widget>
</item>
<item row="5" column="3">
<widget class="QSlider" name="mSimplifyDrawingSlider">
<property name="maximumSize">
<size>
<width>130</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Higher values result in more simplification</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>20</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="tracking">
<bool>true</bool>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="invertedControls">
<bool>false</bool>
</property>
</widget>
</item>
<item row="8" column="2" colspan="2">
<widget class="QFrame" name="mSimplifyDrawingPanel">
<property name="minimumSize">
<size>
<width>0</width>
<height>70</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<widget class="QLabel" name="mSimplifyDrawingIcon">
<property name="geometry">
<rect>
<x>10</x>
<y>0</y>
<width>60</width>
<height>67</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>67</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../images/images.qrc">:/images/themes/default/mIconWarn.png</pixmap>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
</widget>
<widget class="QLabel" name="mSimplifyDrawingLabel">
<property name="geometry">
<rect>
<x>45</x>
<y>8</y>
<width>411</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; &lt;/span&gt;&lt;span style=&quot; font-size:8pt; font-weight:600;&quot;&gt;Warning:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; Increasing this factor will further speed up rendering, but may result in gaps &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; or topological errors in the layer display.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</widget>
</item>
<item row="5" column="4">
<spacer name="horizontalSpacer_40">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
Expand Down
241 changes: 241 additions & 0 deletions src/ui/qgsvectorlayerpropertiesbase.ui
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,18 @@
<normaloff>:/images/themes/default/propertyicons/attributes.png</normaloff>:/images/themes/default/propertyicons/attributes.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>Rendering</string>
</property>
<property name="toolTip">
<string>Rendering</string>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/propertyicons/rendering.png</normaloff>:/images/themes/default/propertyicons/rendering.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>Display</string>
Expand Down Expand Up @@ -895,6 +907,235 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="mOptsPage_Rendering">
<layout class="QVBoxLayout" name="verticalLayout_19">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_15">
<property name="styleSheet">
<string notr="true">font-weight:bold;</string>
</property>
<property name="text">
<string>Rendering</string>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea_19">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_19">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>794</width>
<height>780</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_32">
<item>
<widget class="QGroupBox" name="mSimplifyDrawingGroupBox">
<property name="title">
<string>Simplify geometry</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="_12">
<item row="0" column="2" colspan="3">
<widget class="QLabel" name="label_16">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Enabling this option simplifies geometries in this layer to improve rendering speed. The simplification applies only during rendering of the layer and does not modify the layer geometry.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="margin">
<number>2</number>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Simplification factor (higher values result in more simplification): </string>
</property>
<property name="margin">
<number>2</number>
</property>
</widget>
</item>
<item row="5" column="3">
<widget class="QSlider" name="mSimplifyDrawingSlider">
<property name="maximumSize">
<size>
<width>130</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Higher values result in more simplification</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>20</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="tracking">
<bool>true</bool>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="invertedControls">
<bool>false</bool>
</property>
</widget>
</item>
<item row="8" column="2" colspan="2">
<widget class="QFrame" name="mSimplifyDrawingPanel">
<property name="minimumSize">
<size>
<width>0</width>
<height>70</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<widget class="QLabel" name="mSimplifyDrawingIcon">
<property name="geometry">
<rect>
<x>10</x>
<y>0</y>
<width>60</width>
<height>67</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>67</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../images/images.qrc">:/images/themes/default/mIconWarn.png</pixmap>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
</widget>
<widget class="QLabel" name="mSimplifyDrawingLabel">
<property name="geometry">
<rect>
<x>45</x>
<y>8</y>
<width>411</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; &lt;/span&gt;&lt;span style=&quot; font-size:8pt; font-weight:600;&quot;&gt;Warning:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; Increasing this factor will further speed up rendering, but may result in gaps &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; or topological errors in the layer display.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</widget>
</item>
<item row="5" column="4">
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_6">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="mOptsPage_Display">
<layout class="QVBoxLayout" name="verticalLayout_25">
<property name="margin">
Expand Down