Skip to content

Commit

Permalink
Feature #8725: FastRendering of geometries
Browse files Browse the repository at this point in the history
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'
  • Loading branch information
ahuarte47 committed Dec 17, 2013
1 parent 440ede9 commit 5499457
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 16 deletions.
56 changes: 53 additions & 3 deletions src/core/qgsmaprequest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ inline static bool simplifyWkbGeometry( QGis::WkbType wkbType, unsigned char* so
x = *((double*)sourceWkb); sourceWkb += sizeOfDoubleX;
y = *((double*)sourceWkb); sourceWkb += sizeOfDoubleY;

if ( i==0 || !canbeGeneralizable || calculateLengthSquared2D(x,y,lastX,lastY)>map2pixelTol)
if ( i==0 || !canbeGeneralizable || calculateLengthSquared2D(x,y,lastX,lastY)>map2pixelTol )
{
*ptr = lastX = x; ptr++;
*ptr = lastY = y; ptr++;
Expand Down Expand Up @@ -369,18 +369,31 @@ inline static bool simplifyWkbGeometry( QGis::WkbType wkbType, unsigned char* so

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

//! Returns whether the devide-geometry can be replaced by its BBOX when is applied the specified the map2pixel context
//! Returns whether the device-geometry can be replaced by its BBOX when is applied the specified map2pixel tolerance
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
//! Returns whether the device-geometry can be replaced by its BBOX when is applied the specified map2pixel tolerance
bool QgsMapRequest::canbeGeneralizedByWndBoundingBox( const QVector<QPointF>& points, float mapToPixelTol )
{
QgsRectangle env = calculateBoundingBox( points );
return canbeGeneralizedByWndBoundingBox( env, mapToPixelTol);
}

//! Returns whether the envelope can be replaced by its BBOX when is applied the map2pixel context
bool QgsMapRequest::canbeGeneralizedByMapBoundingBox( const QgsRectangle& envelope, const QgsCoordinateTransform* coordinateTransform, const QgsMapToPixel* mtp, float mapToPixelTol )
{
double map2pixelTol = mapToPixelTol * calculateViewPixelTolerance( envelope, coordinateTransform, mtp );

// Can replace the geometry by its BBOX ?
if ( (envelope.xMaximum()-envelope.xMinimum()) < map2pixelTol && (envelope.yMaximum()-envelope.yMinimum()) < map2pixelTol )
{
return true;
}
return false;
}

//! 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 )
{
Expand All @@ -407,3 +420,40 @@ bool QgsMapRequest::simplifyGeometry( QgsGeometry* geometry, const QgsCoordinate
}
return false;
}

//! Simplify the specified point stream (Removing duplicated points) when is applied the map2pixel context
bool QgsMapRequest::simplifyGeometry( QGis::GeometryType geometryType, const QgsRectangle& envelope, double* xptr, int xStride, double* yptr, int yStride, int pointCount, int& pointSimplifiedCount, const QgsCoordinateTransform* coordinateTransform, const QgsMapToPixel* mtp, float mapToPixelTol )
{
pointSimplifiedCount = pointCount;
if (geometryType==QGis::Point || geometryType==QGis::UnknownGeometry) return false;
pointSimplifiedCount = 0;

double map2pixelTol = mapToPixelTol * calculateViewPixelTolerance( envelope, coordinateTransform, mtp );
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 )
{
x = *((double*)xsourcePtr); xsourcePtr += xStride;
y = *((double*)ysourcePtr); ysourcePtr += yStride;

if ( i==0 || calculateLengthSquared2D(x,y,lastX,lastY)>map2pixelTol )
{
*((double*)xtargetPtr) = lastX = x; xtargetPtr += xStride;
*((double*)ytargetPtr) = lastY = y; ytargetPtr += yStride;
pointSimplifiedCount++;
}
}
if ( geometryType==QGis::Polygon )
{
*((double*)xtargetPtr) = *xptr;
*((double*)ytargetPtr) = *yptr;
pointSimplifiedCount++;
}
return pointSimplifiedCount!=pointCount;
}
22 changes: 18 additions & 4 deletions src/core/qgsmaprequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,31 @@ class CORE_EXPORT QgsMapRequest
float mMapToPixelTol;

public:
//! Returns whether the devided-geometry can be replaced by its BBOX when is applied the specified the map2pixel context
//! Returns whether the device-geometry can be replaced by its BBOX when is applied the specified map2pixel tolerance
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
//! Returns whether the device-geometry can be replaced by its BBOX when is applied the specified map2pixel tolerance
static bool canbeGeneralizedByWndBoundingBox( const QVector<QPointF>& points, float mapToPixelTol = 1.0f );

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

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

//! 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 ); }
//! Simplify the specified geometry (Removing duplicated points) when is applied the map2pixel context
inline bool simplifyGeometry( QgsGeometry* geometry ) const { return simplifyGeometry( geometry, mMapCoordTransform, mMapToPixel, mMapToPixelTol ); }

//! Simplify the specified point stream (Removing duplicated points) when is applied a map2pixel context
static bool simplifyGeometry( QGis::GeometryType geometryType, const QgsRectangle& envelope, double* xptr, int xStride, double* yptr, int yStride, int pointCount, int& pointSimplifiedCount,
const QgsCoordinateTransform* coordinateTransform, const QgsMapToPixel* mtp, float mapToPixelTol = 1.0f );

//! Simplify the specified point stream (Removing duplicated points) when is applied the map2pixel context
inline bool simplifyGeometry( QGis::GeometryType geometryType, const QgsRectangle& envelope, double* xptr, int xStride, double* yptr, int yStride, int pointCount, int& pointSimplifiedCount ) const { return simplifyGeometry( geometryType, envelope, xptr, xStride, yptr, yStride, pointCount, pointSimplifiedCount, mMapCoordTransform, mMapToPixel, mMapToPixelTol ); }
};

#endif // QGSMAPREQUEST_H
129 changes: 124 additions & 5 deletions src/providers/ogr/qgsogrfeatureiterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,27 +302,146 @@ void QgsOgrFeatureIterator::notifyLoadedFeature( OGRFeatureH fet, QgsFeature& fe
* *
***************************************************************************/

#include <ogr_geometry.h>

//! Provides a specialized FeatureIterator for enable map2pixel simplification of the geometries
QgsOgrSimplifiedFeatureIterator::QgsOgrSimplifiedFeatureIterator( QgsOgrProvider* p, const QgsFeatureRequest& request ) : QgsOgrFeatureIterator( p, request )
{
mPointBufferCount = 512;
mPointBufferPtr = (OGRRawPoint*)malloc( mPointBufferCount * sizeof(OGRRawPoint) );
}
QgsOgrSimplifiedFeatureIterator::~QgsOgrSimplifiedFeatureIterator( )
{
if ( mPointBufferPtr )
{
OGRFree( mPointBufferPtr );
mPointBufferPtr = NULL;
}
}

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

//! Simplify the OGR-geometry using the specified tolerance
bool QgsOgrSimplifiedFeatureIterator::simplifyOgrGeometry( const QgsFeatureRequest& request, 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;

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

// Can replace the geometry by its BBOX ?
if ( request.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
{
QGis::GeometryType geometryType = isaLinearRing ? QGis::Polygon : QGis::Line;

int numPoints = lineString->getNumPoints();
int numSimplifiedPoints = 0;

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

if ( request.simplifyGeometry( 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( request, polygon->getExteriorRing(), true );

for (int i = 0, numInteriorRings = polygon->getNumInteriorRings(); i < numInteriorRings; ++i)
{
result |= simplifyOgrGeometry( request, 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( request, collection->getGeometryRef(i), wkbGeometryType==wkbMultiPolygon );
}
if ( result ) collection->flattenTo2D();
return result;
}
return false;
}

//! 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 );
OGRwkbGeometryType wkbType = QgsOgrProvider::ogrWkbSingleFlatten( OGR_G_GetGeometryType(geom) );

if (wkbGeometryType==wkbLineString || wkbGeometryType==wkbPolygon)
if (wkbType==wkbLineString || wkbType==wkbPolygon)
{
simplifyOgrGeometry( mRequest, (OGRGeometry*)geom, wkbType==wkbPolygon );
}
}*/
}
QgsOgrFeatureIterator::notifyReadedFeature( fet, geom, feature );
}

Expand Down
15 changes: 15 additions & 0 deletions src/providers/ogr/qgsogrfeatureiterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ class QgsOgrFeatureIterator : public QgsAbstractFeatureIterator
* *
***************************************************************************/

class OGRRawPoint;
class OGRGeometry;

//! Provides a specialized FeatureIterator for enable map2pixel simplification of the geometries
class QgsOgrSimplifiedFeatureIterator : public QgsOgrFeatureIterator
{
Expand All @@ -89,6 +92,18 @@ class QgsOgrSimplifiedFeatureIterator : public QgsOgrFeatureIterator
protected:
//! notify the OGRFeatureH was readed of the data provider
virtual void notifyReadedFeature( OGRFeatureH fet, OGRGeometryH geom, QgsFeature& feature );

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

//! Simplify the OGR-geometry using the specified tolerance
bool simplifyOgrGeometry ( const QgsFeatureRequest& request, OGRGeometry* geometry, bool isaLinearRing );

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

/***************************************************************************/
Expand Down
6 changes: 2 additions & 4 deletions src/providers/ogr/qgsogrprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -808,10 +808,9 @@ void QgsOgrProvider::setRelevantFields( OGRLayerH ogrLayer, bool fetchGeometry,

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 ) );
}

Expand Down Expand Up @@ -1498,9 +1497,8 @@ int QgsOgrProvider::capabilities() const
}
}

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

return ability;
Expand Down

0 comments on commit 5499457

Please sign in to comment.