Feature #8725: Fast rendering of geom (v-OGR)

Implements fast rendering of LineStrings and Polygons pre-applying a
view threshold filter to the geometries to render in qgis. Also disable
'Antialiasing' when it is possible.

View Table of test results in 'http://hub.qgis.org/issues/8725'

(This version of branch implements the improvement in vector-providers)
This commit is contained in:
Alvaro Huarte 2013-10-18 12:00:23 +02:00
parent cecfff0408
commit db8eaf0c20
23 changed files with 746 additions and 44 deletions

View File

@ -202,5 +202,5 @@ class QgsFillSymbolLayerV2 : 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 );
};

View File

@ -81,6 +81,7 @@ SET(QGIS_CORE_SRCS
qgslabelattributes.cpp
qgslabelsearchtree.cpp
qgslogger.cpp
qgsmaprequest.cpp
qgsmaplayer.cpp
qgsmaplayerregistry.cpp
qgsmaprenderer.cpp

View File

@ -45,6 +45,9 @@ const unsigned char* QgsClipper::clippedLineWKB( const unsigned char* wkb, const
bool hasZValue = ( wkbType == QGis::WKBLineString25D );
int sizeOfDoubleX = sizeof(double);
int sizeOfDoubleY = hasZValue ? 2*sizeof(double) : sizeof(double);
double p0x, p0y, p1x = 0.0, p1y = 0.0; //original coordinates
double p1x_c, p1y_c; //clipped end coordinates
double lastClipX = 0.0, lastClipY = 0.0; //last successfully clipped coords
@ -56,14 +59,9 @@ const unsigned char* QgsClipper::clippedLineWKB( const unsigned char* wkb, const
{
if ( i == 0 )
{
memcpy( &p1x, wkb, sizeof( double ) );
wkb += sizeof( double );
memcpy( &p1y, wkb, sizeof( double ) );
wkb += sizeof( double );
if ( hasZValue ) // ignore Z value
{
wkb += sizeof( double );
}
memcpy( &p1x, wkb, sizeof( double ) ); wkb += sizeOfDoubleX;
memcpy( &p1y, wkb, sizeof( double ) ); wkb += sizeOfDoubleY;
continue;
}
else
@ -71,14 +69,8 @@ const unsigned char* QgsClipper::clippedLineWKB( const unsigned char* wkb, const
p0x = p1x;
p0y = p1y;
memcpy( &p1x, wkb, sizeof( double ) );
wkb += sizeof( double );
memcpy( &p1y, wkb, sizeof( double ) );
wkb += sizeof( double );
if ( hasZValue ) // ignore Z value
{
wkb += sizeof( double );
}
memcpy( &p1x, wkb, sizeof( double ) ); wkb += sizeOfDoubleX;
memcpy( &p1y, wkb, sizeof( double ) ); wkb += sizeOfDoubleY;
p1x_c = p1x; p1y_c = p1y;
if ( clipLineSegment( clipExtent.xMinimum(), clipExtent.xMaximum(), clipExtent.yMinimum(), clipExtent.yMaximum(),

View File

@ -49,6 +49,8 @@ QgsFeatureRequest::QgsFeatureRequest( const QgsFeatureRequest &rh )
QgsFeatureRequest& QgsFeatureRequest::operator=( const QgsFeatureRequest & rh )
{
QgsMapRequest::operator=( rh );
mFlags = rh.mFlags;
mFilter = rh.mFilter;
mFilterRect = rh.mFilterRect;

View File

@ -17,6 +17,7 @@
#include <QFlags>
#include "qgsmaprequest.h"
#include "qgsfeature.h"
#include "qgsrectangle.h"
#include "qgsexpression.h"
@ -53,7 +54,7 @@ typedef QList<int> QgsAttributeList;
* QgsFeatureRequest().setFilterFid(45)
*
*/
class CORE_EXPORT QgsFeatureRequest
class CORE_EXPORT QgsFeatureRequest : public QgsMapRequest
{
public:
enum Flag
@ -61,7 +62,8 @@ class CORE_EXPORT QgsFeatureRequest
NoFlags = 0,
NoGeometry = 1, //!< Geometry is not required. It may still be returned if e.g. required for a filter condition.
SubsetOfAttributes = 2, //!< Fetch only a subset of attributes (setSubsetOfAttributes sets this flag)
ExactIntersect = 4 //!< Use exact geometry intersection (slower) instead of bounding boxes
ExactIntersect = 4, //!< Use exact geometry intersection (slower) instead of bounding boxes
SimplifyGeometries = 8 //!< Simplify the geometry using the current map2pixel context
};
Q_DECLARE_FLAGS( Flags, Flag )

View File

@ -278,6 +278,10 @@ void QgsMapRenderer::render( QPainter* painter, double* forceWidthScale )
//so must be false at every new render operation
mRenderContext.setRenderingStopped( false );
// Gets the configured Tolerance for simplify transformations between map coordinates and device coordinates
QSettings mySettings2;
mRenderContext.setMapToPixelTol( mySettings2.value( "Map/map2pixelTol", 1.0f ).toFloat() );
// set selection color
QgsProject* prj = QgsProject::instance();
int myRed = prj->readNumEntry( "Gui", "/SelectionColorRedPart", 255 );

409
src/core/qgsmaprequest.cpp Normal file
View File

@ -0,0 +1,409 @@
/***************************************************************************
qgsmaprequest.cpp
----------------------
begin : October 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 "qgsmaprequest.h"
#include "qgsrectangle.h"
#include "qgsgeometry.h"
QgsMapRequest::QgsMapRequest( ) : mMapCoordTransform( NULL ), mMapToPixel( NULL ), mMapToPixelTol( 1.0f )
{
}
QgsMapRequest::QgsMapRequest( const QgsMapRequest &rh )
{
operator=( rh );
}
QgsMapRequest& QgsMapRequest::operator=( const QgsMapRequest & rh )
{
mMapCoordTransform = rh.mMapCoordTransform;
mMapToPixel = rh.mMapToPixel;
mMapToPixelTol = rh.mMapToPixelTol;
return *this;
}
QgsMapRequest::~QgsMapRequest()
{
}
QgsMapRequest& QgsMapRequest::setCoordinateTransform( const QgsCoordinateTransform* ct )
{
mMapCoordTransform = ct;
return *this;
}
QgsMapRequest& QgsMapRequest::setMapToPixel( const QgsMapToPixel* mtp )
{
mMapToPixel = mtp;
return *this;
}
QgsMapRequest& QgsMapRequest::setMapToPixelTol( float map2pixelTol )
{
mMapToPixelTol = map2pixelTol;
return *this;
}
//////////////////////////////////////////////////////////////////////////////////////////////
// Helper simplification methods
#include "qgsrendercontext.h"
#include "qgsgeometry.h"
#include "qgsapplication.h"
//! Returns the squared 2D-distance of the vector defined by the two points specified
inline static float calculateLengthSquared2D( double x1, double y1, double x2, double y2 )
{
float vx = (float)( x2 - x1 );
float vy = (float)( y2 - y1 );
return vx*vx + vy*vy;
}
//! Returns the MapTolerance for transform between map coordinates and device coordinates
inline static float calculateViewPixelTolerance( const QgsRectangle& boundingRect, const QgsCoordinateTransform* ct, const QgsMapToPixel* mtp )
{
double mapUnitsPerPixel = mtp ? mtp->mapUnitsPerPixel() : 1.0;
double mapUnitsFactor = 1;
// Calculate one aprox factor of the size of the BBOX from the source CoordinateSystem to the target CoordinateSystem.
if (ct && !((QgsCoordinateTransform*)ct)->isShortCircuited())
{
QgsRectangle sourceRect = boundingRect;
QgsRectangle targetRect = ct->transform(sourceRect);
QgsPoint minimumSrcPoint( sourceRect.xMinimum(), sourceRect.yMinimum() );
QgsPoint maximumSrcPoint( sourceRect.xMaximum(), sourceRect.yMaximum() );
QgsPoint minimumDstPoint( targetRect.xMinimum(), targetRect.yMinimum() );
QgsPoint maximumDstPoint( targetRect.xMaximum(), targetRect.yMaximum() );
double sourceHypothenuse = sqrt( calculateLengthSquared2D( minimumSrcPoint.x(), minimumSrcPoint.y(), maximumSrcPoint.x(), maximumSrcPoint.y() ) );
double targetHypothenuse = sqrt( calculateLengthSquared2D( minimumDstPoint.x(), minimumDstPoint.y(), maximumDstPoint.x(), maximumDstPoint.y() ) );
if (targetHypothenuse!=0)
mapUnitsFactor = sourceHypothenuse/targetHypothenuse;
}
return (float)( mapUnitsPerPixel * mapUnitsFactor );
}
//! Returns the BBOX of the specified Q-point stream
inline static QgsRectangle calculateBoundingBox( const QVector<QPointF>& points )
{
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 QgsRectangle( xmin, ymin, xmax, ymax );
}
//! Returns the BBOX of the specified WKB-point stream
inline static QgsRectangle calculateBoundingBox( QGis::WkbType wkbType, unsigned char* wkb, size_t numPoints )
{
unsigned char* wkb2 = wkb;
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();
int sizeOfDoubleX = sizeof(double);
int sizeOfDoubleY = QGis::wkbDimensions(wkbType)==3 /*hasZValue*/ ? 2*sizeof(double) : sizeof(double);
for (size_t i = 0; i < numPoints; ++i)
{
x = *(( double * ) wkb ); wkb += sizeOfDoubleX;
y = *(( double * ) wkb ); wkb += sizeOfDoubleY;
if (xmin>x) xmin = x;
if (ymin>y) ymin = y;
if (xmax<x) xmax = x;
if (ymax<y) ymax = y;
}
wkb = wkb2;
return QgsRectangle( xmin, ymin, xmax, ymax );
}
//! Generalize the WKB-geometry using the BBOX of the original geometry
inline static bool generalizeGeometry( QGis::WkbType wkbType, unsigned char* sourceWkb, size_t sourceWkbSize, unsigned char* targetWkb, size_t& targetWkbSize, const QgsRectangle& envelope, bool writeHeader )
{
unsigned char* wkb2 = targetWkb;
unsigned int geometryType = QGis::singleType( QGis::flatType( wkbType ) );
int sizeOfDoubleX = sizeof(double);
int sizeOfDoubleY = QGis::wkbDimensions(wkbType)==3 /*hasZValue*/ ? 2*sizeof(double) : sizeof(double);
// Skip the unnecesary generalization because of is a very single geometry
size_t minimumSize = (geometryType==QGis::WKBLineString ? 4 + 2*(sizeOfDoubleX+sizeOfDoubleY) : 8 + 5*(sizeOfDoubleX+sizeOfDoubleY) );
if ( writeHeader ) minimumSize += 5;
if ( sourceWkbSize <= minimumSize )
{
targetWkbSize = 0;
return false;
}
double x1 = envelope.xMinimum();
double y1 = envelope.yMinimum();
double x2 = envelope.xMaximum();
double y2 = envelope.yMaximum();
// Write the main header of the geometry
if ( writeHeader )
{
char byteOrder = QgsApplication::endian(); // byteOrder
*targetWkb = byteOrder;
targetWkb += 1;
*((int*)targetWkb) = geometryType; // type
targetWkb += 4;
if (geometryType==QGis::WKBPolygon) { *((int*)targetWkb) = 1; targetWkb += 4; } // numRings
}
// Write the generalized geometry
if (geometryType==QGis::WKBLineString)
{
*((int*)targetWkb) = 2; // numPoints;
targetWkb += 4;
double* ptr = (double*)targetWkb;
targetWkb += 32;
*ptr = x1; ptr++; *ptr = y1; ptr++;
*ptr = x2; ptr++; *ptr = y2; ptr++;
}
else
{
*((int*)targetWkb) = 5; // numPoints;
targetWkb += 4;
double* ptr = (double*)targetWkb;
targetWkb += 80;
*ptr = x1; ptr++; *ptr = y1; ptr++;
*ptr = x2; ptr++; *ptr = y1; ptr++;
*ptr = x2; ptr++; *ptr = y2; ptr++;
*ptr = x1; ptr++; *ptr = y2; ptr++;
*ptr = x1; ptr++; *ptr = y1; ptr++;
}
targetWkbSize += targetWkb - wkb2;
targetWkb = wkb2;
return true;
}
//! Simplify the WKB-geometry using the specified tolerance
inline static bool simplifyWkbGeometry( 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 )
{
bool canbeGeneralizable = true;
bool hasZValue = QGis::wkbDimensions(wkbType)==3;
bool result = false;
// Can replace the geometry by its BBOX ?
if ( (envelope.xMaximum()-envelope.xMinimum()) < map2pixelTol && (envelope.yMaximum()-envelope.yMinimum()) < map2pixelTol )
{
canbeGeneralizable = generalizeGeometry( wkbType, sourceWkb, sourceWkbSize, targetWkb, targetWkbSize, envelope, writeHeader );
if (canbeGeneralizable) return true;
}
// Write the main header of the geometry
if ( writeHeader )
{
*targetWkb = *sourceWkb; // byteOrder
sourceWkb += 1;
targetWkb += 1;
*((int*)targetWkb) = QGis::flatType( (QGis::WkbType) *((int*)sourceWkb) ); // type
sourceWkb += 4;
targetWkb += 4;
targetWkbSize += 5;
}
unsigned char* wkb1 = sourceWkb;
unsigned char* wkb2 = targetWkb;
unsigned int flatType = QGis::flatType( wkbType );
// Write the geometry
if (flatType==QGis::WKBLineString || isaLinearRing)
{
double x,y, lastX=0,lastY=0;
int sizeOfDoubleX = sizeof(double);
int sizeOfDoubleY = QGis::wkbDimensions(wkbType)==3 /*hasZValue*/ ? 2*sizeof(double) : sizeof(double);
int numPoints = *((int*)sourceWkb);
sourceWkb += 4;
if (numPoints <= (isaLinearRing ? 5 : 2)) canbeGeneralizable = false;
int numTargetPoints = 0;
*((int*)targetWkb) = numTargetPoints;
targetWkb += 4;
targetWkbSize += 4;
double* ptr = (double*)targetWkb;
map2pixelTol *= map2pixelTol; //-> Use mappixelTol for 'LengthSquare' calculations.
// Process each vertex...
for (int i = 0, numPoints_i = (isaLinearRing ? numPoints-1 : numPoints); i < numPoints_i; ++i)
{
x = *((double*)sourceWkb); sourceWkb += sizeOfDoubleX;
y = *((double*)sourceWkb); sourceWkb += sizeOfDoubleY;
if ( i==0 || !canbeGeneralizable || calculateLengthSquared2D(x,y,lastX,lastY)>map2pixelTol)
{
*ptr = lastX = x; ptr++;
*ptr = lastY = y; ptr++;
numTargetPoints++;
}
}
targetWkb = wkb2+4;
// Fix the topology of the geometry
if ( isaLinearRing )
{
*ptr = x = *((double*)(targetWkb+0)); ptr++;
*ptr = y = *((double*)(targetWkb+8)); ptr++;
numTargetPoints++;
}
targetWkbSize += numTargetPoints * 16;
targetWkb = wkb2;
*((int*)targetWkb) = numTargetPoints;
result = numPoints!=numTargetPoints;
}
else
if (flatType==QGis::WKBPolygon)
{
int numRings = *((int*)sourceWkb);
sourceWkb += 4;
*((int*)targetWkb) = numRings;
targetWkb += 4;
targetWkbSize += 4;
for (int i = 0; i < numRings; ++i)
{
int numPoints_i = *((int*)sourceWkb);
QgsRectangle envelope_i = numRings==1 ? envelope : calculateBoundingBox( wkbType, sourceWkb+4, numPoints_i );
size_t sourceWkbSize_i = 4 + numPoints_i * (hasZValue ? 3 : 2) * sizeof(double);
size_t targetWkbSize_i = 0;
result |= simplifyWkbGeometry( wkbType, sourceWkb, sourceWkbSize_i, targetWkb, targetWkbSize_i, envelope_i, map2pixelTol, false, true );
sourceWkb += sourceWkbSize_i;
targetWkb += targetWkbSize_i;
targetWkbSize += targetWkbSize_i;
}
}
else
if (flatType==QGis::WKBMultiLineString || flatType==QGis::WKBMultiPolygon)
{
int numGeoms = *((int*)sourceWkb);
sourceWkb += 4;
wkb1 += 4;
*((int*)targetWkb) = numGeoms;
targetWkb += 4;
targetWkbSize += 4;
for (int i = 0; i < numGeoms; ++i)
{
size_t sourceWkbSize_i = 0;
size_t targetWkbSize_i = 0;
// ... calculate the wkb-size of the current child complex geometry
if (flatType==QGis::WKBMultiLineString)
{
int numPoints_i = *((int*)(wkb1+5));
int wkbSize_i = 4 + numPoints_i * (hasZValue ? 3 : 2) * sizeof(double);
sourceWkbSize_i += 5 + wkbSize_i;
wkb1 += 5 + wkbSize_i;
}
else
{
int numPrings_i = *((int*)(wkb1+5));
sourceWkbSize_i = 9;
wkb1 += 9;
for (int j = 0; j < numPrings_i; ++j)
{
int numPoints_i = *((int*)(wkb1));
int wkbSize_i = 4 + numPoints_i * (hasZValue ? 3 : 2) * sizeof(double);
sourceWkbSize_i += wkbSize_i;
wkb1 += wkbSize_i;
}
}
result |= simplifyWkbGeometry( QGis::singleType(wkbType), sourceWkb, sourceWkbSize_i, targetWkb, targetWkbSize_i, envelope, map2pixelTol, true, false );
sourceWkb += sourceWkbSize_i;
targetWkb += targetWkbSize_i;
targetWkbSize += targetWkbSize_i;
}
}
return result;
}
//////////////////////////////////////////////////////////////////////////////////////////////
//! Returns whether the devide-geometry can be replaced by its BBOX when is applied the specified the map2pixel context
bool QgsMapRequest::canbeGeneralizedByWndBoundingBox( const QgsRectangle& envelope, float mapToPixelTol )
{
return (envelope.xMaximum()-envelope.xMinimum()) < mapToPixelTol && (envelope.yMaximum()-envelope.yMinimum()) < mapToPixelTol;
}
//! Returns whether the devide-geometry can be replaced by its BBOX when is applied the specified the map2pixel context
bool QgsMapRequest::canbeGeneralizedByWndBoundingBox( const QVector<QPointF>& points, float mapToPixelTol )
{
QgsRectangle env = calculateBoundingBox( points );
return canbeGeneralizedByWndBoundingBox( env, mapToPixelTol);
}
//! Simplify the specified geometry (Removing duplicated points) when is applied the map2pixel context
bool QgsMapRequest::simplifyGeometry( QgsGeometry* geometry, const QgsCoordinateTransform* coordinateTransform, const QgsMapToPixel* mtp, float mapToPixelTol )
{
size_t targetWkbSize = 0;
// Check whether the geometry can be simplified using the map2pixel context
QGis::GeometryType geometryType = geometry->type();
if (!(geometryType==QGis::Line || geometryType==QGis::Polygon)) return false;
QgsRectangle envelope = geometry->boundingBox();
QGis::WkbType wkbType = geometry->wkbType();
double map2pixelTol = mapToPixelTol * calculateViewPixelTolerance( envelope, coordinateTransform, mtp );
unsigned char* wkb = (unsigned char*)geometry->asWkb( );
size_t wkbSize = geometry->wkbSize( );
// Simplify the geometry rewriting temporally its WKB-stream for saving calloc's.
if ( simplifyWkbGeometry( wkbType, wkb, wkbSize, wkb, targetWkbSize, envelope, map2pixelTol ) )
{
unsigned char* targetWkb = (unsigned char*)malloc( targetWkbSize );
memcpy( targetWkb, wkb, targetWkbSize );
geometry->fromWkb( targetWkb, targetWkbSize );
return true;
}
return false;
}

75
src/core/qgsmaprequest.h Normal file
View File

@ -0,0 +1,75 @@
/***************************************************************************
qgsmaprequest.h
----------------------
begin : October 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 QGSMAPREQUEST_H
#define QGSMAPREQUEST_H
#include "qgscoordinatetransform.h"
#include "qgsmaptopixel.h"
class QgsRectangle;
class QgsGeometry;
/**
* This class wraps a generic request for a map layer (or directly its data provider).
* The request may apply a simplification using the map2pixel render state to fetch
* only a particular subset of information.
*/
class CORE_EXPORT QgsMapRequest
{
public:
//! construct a default request
QgsMapRequest();
//! copy constructor
QgsMapRequest( const QgsMapRequest& rh );
QgsMapRequest& operator=( const QgsMapRequest& rh );
~QgsMapRequest();
public:
const QgsCoordinateTransform* coordinateTransform() const { return mMapCoordTransform; }
QgsMapRequest& setCoordinateTransform( const QgsCoordinateTransform* ct );
const QgsMapToPixel* mapToPixel() const { return mMapToPixel; }
QgsMapRequest& setMapToPixel( const QgsMapToPixel* mtp );
float mapToPixelTol() const { return mMapToPixelTol; }
QgsMapRequest& setMapToPixelTol( float map2pixelTol );
protected:
//! 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;
public:
//! Returns whether the devided-geometry can be replaced by its BBOX when is applied the specified the map2pixel context
static bool canbeGeneralizedByWndBoundingBox( const QgsRectangle& envelope, float mapToPixelTol = 1.0f );
//! Returns whether the devided-geometry can be replaced by its BBOX when is applied the specified the map2pixel context
static bool canbeGeneralizedByWndBoundingBox( const QVector<QPointF>& points, float mapToPixelTol = 1.0f );
//! Simplify the specified geometry (Removing duplicated points) when is applied the map2pixel context
static bool simplifyGeometry( QgsGeometry* geometry,
const QgsCoordinateTransform* coordinateTransform, const QgsMapToPixel* mtp, float mapToPixelTol = 1.0f );
//! Simplify the specified geometry (Removing duplicated points) when is applied the map2pixel context
inline bool simplifyGeometry( QgsGeometry* geometry ) { return simplifyGeometry( geometry, mMapCoordTransform, mMapToPixel, mMapToPixelTol ); }
};
#endif // QGSMAPREQUEST_H

View File

@ -28,7 +28,8 @@ QgsRenderContext::QgsRenderContext()
mScaleFactor( 1.0 ),
mRasterScaleFactor( 1.0 ),
mRendererScale( 1.0 ),
mLabelingEngine( NULL )
mLabelingEngine( NULL ),
mMapToPixelTol( 1.0f )
{
}
@ -42,3 +43,7 @@ void QgsRenderContext::setCoordinateTransform( const QgsCoordinateTransform* t )
mCoordTransform = t;
}
void QgsRenderContext::setMapToPixelTol( float map2pixelTol )
{
mMapToPixelTol = map2pixelTol;
}

View File

@ -95,6 +95,9 @@ class CORE_EXPORT QgsRenderContext
//! Added in QGIS v2.0
void setSelectionColor( const QColor& color ) { mSelectionColor = color; }
float mapToPixelTol() const { return mMapToPixelTol; }
void setMapToPixelTol( float map2pixelTol );
private:
/**Painter for rendering operations*/
@ -115,6 +118,8 @@ class CORE_EXPORT QgsRenderContext
bool mUseAdvancedEffects;
QgsMapToPixel mMapToPixel;
/** Tolerance for simplify transformations between map coordinates and device coordinates*/
float mMapToPixelTol;
/**True if the rendering has been canceled*/
bool mRenderingStopped;

View File

@ -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( ", " );
}

View File

@ -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 */

View File

@ -687,12 +687,19 @@ 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 );
if (( mRendererV2->capabilities() & QgsFeatureRendererV2::SymbolLevels )
&& mRendererV2->usingSymbolLevels() )
// Enable the simplification of the geometries before fetch the features using the current map2pixel context.
featureRequest.setFlags( featureRequest.flags() | QgsFeatureRequest::SimplifyGeometries );
featureRequest.setCoordinateTransform( rendererContext.coordinateTransform() );
featureRequest.setMapToPixel( &rendererContext.mapToPixel() );
featureRequest.setMapToPixelTol( rendererContext.mapToPixelTol() );
QgsFeatureIterator fit = getFeatures( featureRequest );
if (( mRendererV2->capabilities() & QgsFeatureRendererV2::SymbolLevels ) && mRendererV2->usingSymbolLevels() )
drawRendererV2Levels( fit, rendererContext, labeling );
else
drawRendererV2( fit, rendererContext, labeling );
@ -1209,6 +1216,9 @@ QgsFeatureIterator QgsVectorLayer::getFeatures( const QgsFeatureRequest& request
if ( !mDataProvider )
return QgsFeatureIterator();
if ( request.flags() & QgsFeatureRequest::SimplifyGeometries )
return QgsFeatureIterator( new QgsSimplifiedVectorLayerFeatureIterator( this, request ) );
return QgsFeatureIterator( new QgsVectorLayerFeatureIterator( this, request ) );
}

View File

@ -585,3 +585,42 @@ void QgsVectorLayerFeatureIterator::updateFeatureGeometry( QgsFeature &f )
if ( mChangedGeometries.contains( f.id() ) )
f.setGeometry( mChangedGeometries[f.id()] );
}
/***************************************************************************
MapToPixel simplification classes
----------------------
begin : October 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 )
: QgsVectorLayerFeatureIterator( layer, request )
{
mSupportsPresimplify = layer->dataProvider()->capabilities() & QgsVectorDataProvider::SimplifyGeometries;
}
QgsSimplifiedVectorLayerFeatureIterator::~QgsSimplifiedVectorLayerFeatureIterator()
{
}
//! fetch next feature, return true on success
bool QgsSimplifiedVectorLayerFeatureIterator::fetchFeature( QgsFeature& feature )
{
if (QgsVectorLayerFeatureIterator::fetchFeature( feature ))
{
const QgsMapToPixel* mtp = mRequest.mapToPixel();
if ( mtp && !mSupportsPresimplify ) mRequest.simplifyGeometry( feature.geometry() );
return true;
}
return false;
}
/***************************************************************************/

View File

@ -116,4 +116,38 @@ class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureItera
QMap<QgsVectorLayer*, FetchJoinInfo> mFetchJoinInfo;
};
/***************************************************************************
MapToPixel simplification classes
----------------------
begin : October 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 VectorLayerFeatureIterator for enable map2pixel simplification of the geometries
class CORE_EXPORT QgsSimplifiedVectorLayerFeatureIterator : public QgsVectorLayerFeatureIterator
{
public:
QgsSimplifiedVectorLayerFeatureIterator( QgsVectorLayer* layer, const QgsFeatureRequest& request );
~QgsSimplifiedVectorLayerFeatureIterator( );
protected:
//! fetch next feature, return true on success
virtual bool fetchFeature( QgsFeature& feature );
private:
//! Indicates the related vector provider supports simplify the geometries before fecth the feature
bool mSupportsPresimplify;
};
/***************************************************************************/
#endif // QGSVECTORLAYERFEATUREITERATOR_H

View File

@ -182,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() )
{
@ -313,7 +313,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 ) )
@ -328,7 +328,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() );

View File

@ -180,6 +180,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 && QgsMapRequest::canbeGeneralizedByWndBoundingBox( points, context.renderContext().mapToPixelTol() ) && p->renderHints() & QPainter::Antialiasing )
{
p->setRenderHint( QPainter::Antialiasing, false );
p->drawPolyline ( points );
p->setRenderHint( QPainter::Antialiasing, true );
return;
}
if ( offset == 0 )
{
p->drawPolyline( points );

View File

@ -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();
@ -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 );
x = *(( double * ) wkb ); wkb += sizeOfDoubleX;
y = *(( double * ) wkb ); wkb += sizeOfDoubleY;
*ptr = QPointF( x, y );
}
@ -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();
@ -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 );
x = *(( double * ) wkb ); wkb += sizeOfDoubleX;
y = *(( double * ) wkb ); 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 )

View File

@ -324,13 +324,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 && QgsMapRequest::canbeGeneralizedByWndBoundingBox( points, context.renderContext().mapToPixelTol() ) && p->renderHints() & QPainter::Antialiasing )
{
p->setRenderHint( QPainter::Antialiasing, false );
p->drawPolygon( points );
p->setRenderHint( QPainter::Antialiasing, true );
return;
}
if ( rings == NULL )
{
// simple polygon without holes

View File

@ -231,7 +231,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;
};

View File

@ -233,11 +233,15 @@ bool QgsOgrFeatureIterator::readFeature( OGRFeatureH fet, QgsFeature& feature )
if ( geom )
{
notifyReadedFeature( fet, geom, feature );
// get the wkb representation
unsigned char *wkb = new unsigned char[OGR_G_WkbSize( geom )];
OGR_G_ExportToWkb( geom, ( OGRwkbByteOrder ) QgsApplication::endian(), wkb );
feature.setGeometryAndOwnership( wkb, OGR_G_WkbSize( geom ) );
notifyLoadedFeature( fet, feature );
}
if (( useIntersect && ( !feature.geometry() || !feature.geometry()->intersects( mRequest.filterRect() ) ) )
|| ( geometryTypeFilter && ( !feature.geometry() || QgsOgrProvider::ogrWkbSingleFlatten(( OGRwkbGeometryType )feature.geometry()->wkbType() ) != P->mOgrGeometryTypeFilter ) ) )
@ -272,3 +276,54 @@ bool QgsOgrFeatureIterator::readFeature( OGRFeatureH fet, QgsFeature& feature )
return true;
}
//! notify the OGRFeatureH was readed of the data provider
void QgsOgrFeatureIterator::notifyReadedFeature( OGRFeatureH fet, OGRGeometryH geom, QgsFeature& feature )
{
}
//! notify the OGRFeatureH was loaded to the QgsFeature object
void QgsOgrFeatureIterator::notifyLoadedFeature( OGRFeatureH fet, QgsFeature& feature )
{
}
/***************************************************************************
MapToPixel simplification classes
----------------------
begin : October 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 map2pixel simplification of the geometries
QgsOgrSimplifiedFeatureIterator::QgsOgrSimplifiedFeatureIterator( QgsOgrProvider* p, const QgsFeatureRequest& request ) : QgsOgrFeatureIterator( p, request )
{
}
QgsOgrSimplifiedFeatureIterator::~QgsOgrSimplifiedFeatureIterator( )
{
}
//! notify the OGRFeatureH was readed of the data provider
void QgsOgrSimplifiedFeatureIterator::notifyReadedFeature( OGRFeatureH fet, OGRGeometryH geom, QgsFeature& feature )
{
/* TODO: ### ahuarte47!
if ( mRequest.flags() & QgsFeatureRequest::SimplifyGeometries )
{
OGRwkbGeometryType wkbType = OGR_G_GetGeometryType( geom );
OGRwkbGeometryType wkbGeometryType = QgsOgrProvider::ogrWkbSingleFlatten( wkbType );
if (wkbGeometryType==wkbLineString || wkbGeometryType==wkbPolygon)
{
}
}*/
QgsOgrFeatureIterator::notifyReadedFeature( fet, geom, feature );
}
/***************************************************************************/

View File

@ -47,6 +47,11 @@ class QgsOgrFeatureIterator : public QgsAbstractFeatureIterator
//! Get an attribute associated with a feature
void getFeatureAttribute( OGRFeatureH ogrFet, QgsFeature & f, int attindex );
//! notify the OGRFeatureH was readed of the data provider
virtual void notifyReadedFeature( OGRFeatureH fet, OGRGeometryH geom, QgsFeature& feature );
//! notify the OGRFeatureH was loaded to the QgsFeature object
virtual void notifyLoadedFeature( OGRFeatureH fet, QgsFeature& feature );
bool mFeatureFetched;
OGRDataSourceH ogrDataSource;
@ -58,5 +63,34 @@ class QgsOgrFeatureIterator : public QgsAbstractFeatureIterator
bool mFetchGeometry;
};
/***************************************************************************
MapToPixel simplification classes
----------------------
begin : October 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 map2pixel simplification of the geometries
class QgsOgrSimplifiedFeatureIterator : public QgsOgrFeatureIterator
{
public:
QgsOgrSimplifiedFeatureIterator( QgsOgrProvider* p, const QgsFeatureRequest& request );
~QgsOgrSimplifiedFeatureIterator( );
protected:
//! notify the OGRFeatureH was readed of the data provider
virtual void notifyReadedFeature( OGRFeatureH fet, OGRGeometryH geom, QgsFeature& feature );
};
/***************************************************************************/
#endif // QGSOGRFEATUREITERATOR_H

View File

@ -749,6 +749,10 @@ void QgsOgrProvider::setRelevantFields( bool fetchGeometry, const QgsAttributeLi
QgsFeatureIterator QgsOgrProvider::getFeatures( const QgsFeatureRequest& request )
{
/* TODO: ### ahuarte47!
if ( request.flags() & QgsFeatureRequest::SimplifyGeometries )
return QgsFeatureIterator( new QgsOgrSimplifiedFeatureIterator( this, request ) );
*/
return QgsFeatureIterator( new QgsOgrFeatureIterator( this, request ) );
}
@ -1484,6 +1488,10 @@ int QgsOgrProvider::capabilities() const
ability &= ~( AddAttributes | DeleteFeatures );
}
}
// TODO: ### ahuarte47!
// By default, supports simplification of geometries before fetch the OGR-feature.
// ability |= SimplifyGeometries;
}
return ability;