From db8eaf0c20090958bbf255b0399ee51b7064078c Mon Sep 17 00:00:00 2001 From: Alvaro Huarte Date: Fri, 18 Oct 2013 12:00:23 +0200 Subject: [PATCH] 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) --- python/core/symbology-ng/qgssymbollayerv2.sip | 2 +- src/core/CMakeLists.txt | 1 + src/core/qgsclipper.cpp | 24 +- src/core/qgsfeaturerequest.cpp | 2 + src/core/qgsfeaturerequest.h | 6 +- src/core/qgsmaprenderer.cpp | 4 + src/core/qgsmaprequest.cpp | 409 ++++++++++++++++++ src/core/qgsmaprequest.h | 75 ++++ src/core/qgsrendercontext.cpp | 7 +- src/core/qgsrendercontext.h | 5 + src/core/qgsvectordataprovider.cpp | 6 + src/core/qgsvectordataprovider.h | 2 + src/core/qgsvectorlayer.cpp | 20 +- src/core/qgsvectorlayerfeatureiterator.cpp | 39 ++ src/core/qgsvectorlayerfeatureiterator.h | 34 ++ .../symbology-ng/qgsfillsymbollayerv2.cpp | 6 +- .../symbology-ng/qgslinesymbollayerv2.cpp | 9 + src/core/symbology-ng/qgsrendererv2.cpp | 29 +- src/core/symbology-ng/qgssymbollayerv2.cpp | 11 +- src/core/symbology-ng/qgssymbollayerv2.h | 2 +- src/providers/ogr/qgsogrfeatureiterator.cpp | 55 +++ src/providers/ogr/qgsogrfeatureiterator.h | 34 ++ src/providers/ogr/qgsogrprovider.cpp | 8 + 23 files changed, 746 insertions(+), 44 deletions(-) create mode 100644 src/core/qgsmaprequest.cpp create mode 100644 src/core/qgsmaprequest.h diff --git a/python/core/symbology-ng/qgssymbollayerv2.sip b/python/core/symbology-ng/qgssymbollayerv2.sip index 4184d782e210..8e95577f3c2a 100644 --- a/python/core/symbology-ng/qgssymbollayerv2.sip +++ b/python/core/symbology-ng/qgssymbollayerv2.sip @@ -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* rings ); + void _renderPolygon( QPainter* p, const QPolygonF& points, const QList* rings, QgsSymbolV2RenderContext& context ); }; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index be83245dba24..c20d76d1962d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -81,6 +81,7 @@ SET(QGIS_CORE_SRCS qgslabelattributes.cpp qgslabelsearchtree.cpp qgslogger.cpp + qgsmaprequest.cpp qgsmaplayer.cpp qgsmaplayerregistry.cpp qgsmaprenderer.cpp diff --git a/src/core/qgsclipper.cpp b/src/core/qgsclipper.cpp index b6fe2aeac714..a6befb83d294 100644 --- a/src/core/qgsclipper.cpp +++ b/src/core/qgsclipper.cpp @@ -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(), diff --git a/src/core/qgsfeaturerequest.cpp b/src/core/qgsfeaturerequest.cpp index 8088bac84a6f..f6dac3e66db9 100644 --- a/src/core/qgsfeaturerequest.cpp +++ b/src/core/qgsfeaturerequest.cpp @@ -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; diff --git a/src/core/qgsfeaturerequest.h b/src/core/qgsfeaturerequest.h index b99dfdbbb374..fa7d8f8f79d5 100644 --- a/src/core/qgsfeaturerequest.h +++ b/src/core/qgsfeaturerequest.h @@ -17,6 +17,7 @@ #include +#include "qgsmaprequest.h" #include "qgsfeature.h" #include "qgsrectangle.h" #include "qgsexpression.h" @@ -53,7 +54,7 @@ typedef QList 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 ) diff --git a/src/core/qgsmaprenderer.cpp b/src/core/qgsmaprenderer.cpp index 3a748dd1c036..adcb1dc596ce 100644 --- a/src/core/qgsmaprenderer.cpp +++ b/src/core/qgsmaprenderer.cpp @@ -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 ); diff --git a/src/core/qgsmaprequest.cpp b/src/core/qgsmaprequest.cpp new file mode 100644 index 000000000000..11fcbf30476b --- /dev/null +++ b/src/core/qgsmaprequest.cpp @@ -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& points ) +{ + double xmin = std::numeric_limits::max(), x,y; + double ymin = std::numeric_limits::max(); + double xmax = -std::numeric_limits::max(); + double ymax = -std::numeric_limits::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::max(), x,y; + double ymin = std::numeric_limits::max(); + double xmax = -std::numeric_limits::max(); + double ymax = -std::numeric_limits::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 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& 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; +} diff --git a/src/core/qgsmaprequest.h b/src/core/qgsmaprequest.h new file mode 100644 index 000000000000..2c3bf2d6d2b9 --- /dev/null +++ b/src/core/qgsmaprequest.h @@ -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& 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 diff --git a/src/core/qgsrendercontext.cpp b/src/core/qgsrendercontext.cpp index 288481497340..367795933cdf 100644 --- a/src/core/qgsrendercontext.cpp +++ b/src/core/qgsrendercontext.cpp @@ -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; +} diff --git a/src/core/qgsrendercontext.h b/src/core/qgsrendercontext.h index f047d9bed990..884774c6b9c8 100644 --- a/src/core/qgsrendercontext.h +++ b/src/core/qgsrendercontext.h @@ -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; diff --git a/src/core/qgsvectordataprovider.cpp b/src/core/qgsvectordataprovider.cpp index 3ec4cba39e9d..614daa30fe8e 100644 --- a/src/core/qgsvectordataprovider.cpp +++ b/src/core/qgsvectordataprovider.cpp @@ -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( ", " ); } diff --git a/src/core/qgsvectordataprovider.h b/src/core/qgsvectordataprovider.h index 00ec22287ca4..5758eb600d77 100644 --- a/src/core/qgsvectordataprovider.h +++ b/src/core/qgsvectordataprovider.h @@ -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 */ diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 7737cf56b6b9..54cd96d2f298 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -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 ); + + // 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() ); - if (( mRendererV2->capabilities() & QgsFeatureRendererV2::SymbolLevels ) - && mRendererV2->usingSymbolLevels() ) + 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 ) ); } diff --git a/src/core/qgsvectorlayerfeatureiterator.cpp b/src/core/qgsvectorlayerfeatureiterator.cpp index c759a8482a2a..ac609e7b6858 100644 --- a/src/core/qgsvectorlayerfeatureiterator.cpp +++ b/src/core/qgsvectorlayerfeatureiterator.cpp @@ -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; +} + +/***************************************************************************/ diff --git a/src/core/qgsvectorlayerfeatureiterator.h b/src/core/qgsvectorlayerfeatureiterator.h index 077dc6f31659..7ae98e5eab92 100644 --- a/src/core/qgsvectorlayerfeatureiterator.h +++ b/src/core/qgsvectorlayerfeatureiterator.h @@ -116,4 +116,38 @@ class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureItera QMap 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 diff --git a/src/core/symbology-ng/qgsfillsymbollayerv2.cpp b/src/core/symbology-ng/qgsfillsymbollayerv2.cpp index 927dfaf687f1..ff32908b97ea 100644 --- a/src/core/symbology-ng/qgsfillsymbollayerv2.cpp +++ b/src/core/symbology-ng/qgsfillsymbollayerv2.cpp @@ -182,7 +182,7 @@ void QgsSimpleFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QListtranslate( offset ); } - _renderPolygon( p, points, rings ); + _renderPolygon( p, points, rings, context ); if ( !mOffset.isNull() ) { @@ -313,7 +313,7 @@ void QgsImageFillSymbolLayer::renderPolygon( const QPolygonF& points, QListsetBrush( 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, QListsetBrush( rotatedBrush ); } - _renderPolygon( p, points, rings ); + _renderPolygon( p, points, rings, context ); if ( mOutline ) { mOutline->renderPolyline( points, context.feature(), context.renderContext(), -1, selectFillBorder && context.selected() ); diff --git a/src/core/symbology-ng/qgslinesymbollayerv2.cpp b/src/core/symbology-ng/qgslinesymbollayerv2.cpp index 009351104473..1f61306c9f2b 100644 --- a/src/core/symbology-ng/qgslinesymbollayerv2.cpp +++ b/src/core/symbology-ng/qgslinesymbollayerv2.cpp @@ -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 ); diff --git a/src/core/symbology-ng/qgsrendererv2.cpp b/src/core/symbology-ng/qgsrendererv2.cpp index f30a9a1e959e..160d2c7aad13 100644 --- a/src/core/symbology-ng/qgsrendererv2.cpp +++ b/src/core/symbology-ng/qgsrendererv2.cpp @@ -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* rings ) +void QgsFillSymbolLayerV2::_renderPolygon( QPainter* p, const QPolygonF& points, const QList* 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 diff --git a/src/core/symbology-ng/qgssymbollayerv2.h b/src/core/symbology-ng/qgssymbollayerv2.h index 37ed05396abf..5b8e262ca307 100644 --- a/src/core/symbology-ng/qgssymbollayerv2.h +++ b/src/core/symbology-ng/qgssymbollayerv2.h @@ -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* rings ); + void _renderPolygon( QPainter* p, const QPolygonF& points, const QList* rings, QgsSymbolV2RenderContext& context ); double mAngle; }; diff --git a/src/providers/ogr/qgsogrfeatureiterator.cpp b/src/providers/ogr/qgsogrfeatureiterator.cpp index 29e679aeb94a..fdc2184ac9d7 100644 --- a/src/providers/ogr/qgsogrfeatureiterator.cpp +++ b/src/providers/ogr/qgsogrfeatureiterator.cpp @@ -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 ); +} + +/***************************************************************************/ diff --git a/src/providers/ogr/qgsogrfeatureiterator.h b/src/providers/ogr/qgsogrfeatureiterator.h index a8aa2ffde641..613520d52815 100644 --- a/src/providers/ogr/qgsogrfeatureiterator.h +++ b/src/providers/ogr/qgsogrfeatureiterator.h @@ -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 diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index 61e1bd02fba6..072eece89367 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -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;