Skip to content

Commit 5499457

Browse files
committed
Feature #8725: FastRendering of geometries
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'
1 parent 440ede9 commit 5499457

5 files changed

+212
-16
lines changed

src/core/qgsmaprequest.cpp

+53-3
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ inline static bool simplifyWkbGeometry( QGis::WkbType wkbType, unsigned char* so
270270
x = *((double*)sourceWkb); sourceWkb += sizeOfDoubleX;
271271
y = *((double*)sourceWkb); sourceWkb += sizeOfDoubleY;
272272

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

370370
//////////////////////////////////////////////////////////////////////////////////////////////
371371

372-
//! Returns whether the devide-geometry can be replaced by its BBOX when is applied the specified the map2pixel context
372+
//! Returns whether the device-geometry can be replaced by its BBOX when is applied the specified map2pixel tolerance
373373
bool QgsMapRequest::canbeGeneralizedByWndBoundingBox( const QgsRectangle& envelope, float mapToPixelTol )
374374
{
375375
return (envelope.xMaximum()-envelope.xMinimum()) < mapToPixelTol && (envelope.yMaximum()-envelope.yMinimum()) < mapToPixelTol;
376376
}
377-
//! Returns whether the devide-geometry can be replaced by its BBOX when is applied the specified the map2pixel context
377+
//! Returns whether the device-geometry can be replaced by its BBOX when is applied the specified map2pixel tolerance
378378
bool QgsMapRequest::canbeGeneralizedByWndBoundingBox( const QVector<QPointF>& points, float mapToPixelTol )
379379
{
380380
QgsRectangle env = calculateBoundingBox( points );
381381
return canbeGeneralizedByWndBoundingBox( env, mapToPixelTol);
382382
}
383383

384+
//! Returns whether the envelope can be replaced by its BBOX when is applied the map2pixel context
385+
bool QgsMapRequest::canbeGeneralizedByMapBoundingBox( const QgsRectangle& envelope, const QgsCoordinateTransform* coordinateTransform, const QgsMapToPixel* mtp, float mapToPixelTol )
386+
{
387+
double map2pixelTol = mapToPixelTol * calculateViewPixelTolerance( envelope, coordinateTransform, mtp );
388+
389+
// Can replace the geometry by its BBOX ?
390+
if ( (envelope.xMaximum()-envelope.xMinimum()) < map2pixelTol && (envelope.yMaximum()-envelope.yMinimum()) < map2pixelTol )
391+
{
392+
return true;
393+
}
394+
return false;
395+
}
396+
384397
//! Simplify the specified geometry (Removing duplicated points) when is applied the map2pixel context
385398
bool QgsMapRequest::simplifyGeometry( QgsGeometry* geometry, const QgsCoordinateTransform* coordinateTransform, const QgsMapToPixel* mtp, float mapToPixelTol )
386399
{
@@ -407,3 +420,40 @@ bool QgsMapRequest::simplifyGeometry( QgsGeometry* geometry, const QgsCoordinate
407420
}
408421
return false;
409422
}
423+
424+
//! Simplify the specified point stream (Removing duplicated points) when is applied the map2pixel context
425+
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 )
426+
{
427+
pointSimplifiedCount = pointCount;
428+
if (geometryType==QGis::Point || geometryType==QGis::UnknownGeometry) return false;
429+
pointSimplifiedCount = 0;
430+
431+
double map2pixelTol = mapToPixelTol * calculateViewPixelTolerance( envelope, coordinateTransform, mtp );
432+
map2pixelTol *= map2pixelTol; //-> Use mappixelTol for 'LengthSquare' calculations.
433+
double x,y, lastX=0, lastY=0;
434+
435+
char* xsourcePtr = (char*)xptr;
436+
char* ysourcePtr = (char*)yptr;
437+
char* xtargetPtr = (char*)xptr;
438+
char* ytargetPtr = (char*)yptr;
439+
440+
for ( int i = 0, numPoints = geometryType==QGis::Polygon ? pointCount-1 : pointCount; i < numPoints; ++i )
441+
{
442+
x = *((double*)xsourcePtr); xsourcePtr += xStride;
443+
y = *((double*)ysourcePtr); ysourcePtr += yStride;
444+
445+
if ( i==0 || calculateLengthSquared2D(x,y,lastX,lastY)>map2pixelTol )
446+
{
447+
*((double*)xtargetPtr) = lastX = x; xtargetPtr += xStride;
448+
*((double*)ytargetPtr) = lastY = y; ytargetPtr += yStride;
449+
pointSimplifiedCount++;
450+
}
451+
}
452+
if ( geometryType==QGis::Polygon )
453+
{
454+
*((double*)xtargetPtr) = *xptr;
455+
*((double*)ytargetPtr) = *yptr;
456+
pointSimplifiedCount++;
457+
}
458+
return pointSimplifiedCount!=pointCount;
459+
}

src/core/qgsmaprequest.h

+18-4
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,31 @@ class CORE_EXPORT QgsMapRequest
5959
float mMapToPixelTol;
6060

6161
public:
62-
//! Returns whether the devided-geometry can be replaced by its BBOX when is applied the specified the map2pixel context
62+
//! Returns whether the device-geometry can be replaced by its BBOX when is applied the specified map2pixel tolerance
6363
static bool canbeGeneralizedByWndBoundingBox( const QgsRectangle& envelope, float mapToPixelTol = 1.0f );
64-
//! Returns whether the devided-geometry can be replaced by its BBOX when is applied the specified the map2pixel context
64+
//! Returns whether the device-geometry can be replaced by its BBOX when is applied the specified map2pixel tolerance
6565
static bool canbeGeneralizedByWndBoundingBox( const QVector<QPointF>& points, float mapToPixelTol = 1.0f );
6666

67+
//! Returns whether the envelope can be replaced by its BBOX when is applied the map2pixel context
68+
static bool canbeGeneralizedByMapBoundingBox( const QgsRectangle& envelope,
69+
const QgsCoordinateTransform* coordinateTransform, const QgsMapToPixel* mtp, float mapToPixelTol = 1.0f );
70+
71+
//! Returns whether the envelope can be replaced by its BBOX when is applied the map2pixel context
72+
inline bool canbeGeneralizedByMapBoundingBox( const QgsRectangle& envelope ) const { return canbeGeneralizedByMapBoundingBox( envelope, mMapCoordTransform, mMapToPixel, mMapToPixelTol ); }
73+
6774
//! Simplify the specified geometry (Removing duplicated points) when is applied the map2pixel context
6875
static bool simplifyGeometry( QgsGeometry* geometry,
6976
const QgsCoordinateTransform* coordinateTransform, const QgsMapToPixel* mtp, float mapToPixelTol = 1.0f );
7077

71-
//! Simplify the specified geometry (Removing duplicated points) when is applied the map2pixel context
72-
inline bool simplifyGeometry( QgsGeometry* geometry ) { return simplifyGeometry( geometry, mMapCoordTransform, mMapToPixel, mMapToPixelTol ); }
78+
//! Simplify the specified geometry (Removing duplicated points) when is applied the map2pixel context
79+
inline bool simplifyGeometry( QgsGeometry* geometry ) const { return simplifyGeometry( geometry, mMapCoordTransform, mMapToPixel, mMapToPixelTol ); }
80+
81+
//! Simplify the specified point stream (Removing duplicated points) when is applied a map2pixel context
82+
static bool simplifyGeometry( QGis::GeometryType geometryType, const QgsRectangle& envelope, double* xptr, int xStride, double* yptr, int yStride, int pointCount, int& pointSimplifiedCount,
83+
const QgsCoordinateTransform* coordinateTransform, const QgsMapToPixel* mtp, float mapToPixelTol = 1.0f );
84+
85+
//! Simplify the specified point stream (Removing duplicated points) when is applied the map2pixel context
86+
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 ); }
7387
};
7488

7589
#endif // QGSMAPREQUEST_H

src/providers/ogr/qgsogrfeatureiterator.cpp

+124-5
Original file line numberDiff line numberDiff line change
@@ -302,27 +302,146 @@ void QgsOgrFeatureIterator::notifyLoadedFeature( OGRFeatureH fet, QgsFeature& fe
302302
* *
303303
***************************************************************************/
304304

305+
#include <ogr_geometry.h>
306+
305307
//! Provides a specialized FeatureIterator for enable map2pixel simplification of the geometries
306308
QgsOgrSimplifiedFeatureIterator::QgsOgrSimplifiedFeatureIterator( QgsOgrProvider* p, const QgsFeatureRequest& request ) : QgsOgrFeatureIterator( p, request )
307309
{
310+
mPointBufferCount = 512;
311+
mPointBufferPtr = (OGRRawPoint*)malloc( mPointBufferCount * sizeof(OGRRawPoint) );
308312
}
309313
QgsOgrSimplifiedFeatureIterator::~QgsOgrSimplifiedFeatureIterator( )
310314
{
315+
if ( mPointBufferPtr )
316+
{
317+
OGRFree( mPointBufferPtr );
318+
mPointBufferPtr = NULL;
319+
}
320+
}
321+
322+
//! Returns a point buffer of the specified size
323+
OGRRawPoint* QgsOgrSimplifiedFeatureIterator::mallocPoints( int numPoints )
324+
{
325+
if ( mPointBufferPtr && mPointBufferCount < numPoints )
326+
{
327+
OGRFree( mPointBufferPtr );
328+
mPointBufferPtr = NULL;
329+
}
330+
if ( mPointBufferPtr==NULL )
331+
{
332+
mPointBufferCount = numPoints;
333+
mPointBufferPtr = (OGRRawPoint*)malloc( mPointBufferCount * sizeof(OGRRawPoint) );
334+
}
335+
return mPointBufferPtr;
336+
}
337+
338+
//! Simplify the OGR-geometry using the specified tolerance
339+
bool QgsOgrSimplifiedFeatureIterator::simplifyOgrGeometry( const QgsFeatureRequest& request, OGRGeometry* geometry, bool isaLinearRing )
340+
{
341+
OGRwkbGeometryType wkbGeometryType = wkbFlatten( geometry->getGeometryType() );
342+
343+
// Simplify the geometry rewriting temporally its WKB-stream for saving calloc's.
344+
if (wkbGeometryType==wkbLineString)
345+
{
346+
OGRLineString* lineString = (OGRLineString*)geometry;
347+
348+
OGREnvelope env;
349+
geometry->getEnvelope(&env );
350+
QgsRectangle envelope( env.MinX, env.MinY, env.MaxX, env.MaxY );
351+
352+
// Can replace the geometry by its BBOX ?
353+
if ( request.canbeGeneralizedByMapBoundingBox( envelope ) )
354+
{
355+
OGRRawPoint* points = NULL;
356+
int numPoints = 0;
357+
358+
double x1 = envelope.xMinimum();
359+
double y1 = envelope.yMinimum();
360+
double x2 = envelope.xMaximum();
361+
double y2 = envelope.yMaximum();
362+
363+
if ( isaLinearRing )
364+
{
365+
numPoints = 5;
366+
points = mallocPoints( numPoints );
367+
points[0].x = x1; points[0].y = y1;
368+
points[1].x = x2; points[1].y = y1;
369+
points[2].x = x2; points[2].y = y2;
370+
points[3].x = x1; points[3].y = y2;
371+
points[4].x = x1; points[4].y = y1;
372+
}
373+
else
374+
{
375+
numPoints = 2;
376+
points = mallocPoints( numPoints );
377+
points[0].x = x1; points[0].y = y1;
378+
points[1].x = x2; points[1].y = y2;
379+
}
380+
lineString->setPoints( numPoints, points );
381+
lineString->flattenTo2D();
382+
return true;
383+
}
384+
else
385+
{
386+
QGis::GeometryType geometryType = isaLinearRing ? QGis::Polygon : QGis::Line;
387+
388+
int numPoints = lineString->getNumPoints();
389+
int numSimplifiedPoints = 0;
390+
391+
OGRRawPoint* points = mallocPoints( numPoints );
392+
double* xptr = (double*)points;
393+
double* yptr = xptr+1;
394+
lineString->getPoints( points );
395+
396+
if ( request.simplifyGeometry( geometryType, envelope, xptr, 16, yptr, 16, numPoints, numSimplifiedPoints ) )
397+
{
398+
lineString->setPoints(numSimplifiedPoints, points);
399+
lineString->flattenTo2D();
400+
}
401+
return numSimplifiedPoints!=numPoints;
402+
}
403+
}
404+
else
405+
if (wkbGeometryType==wkbPolygon)
406+
{
407+
OGRPolygon* polygon = (OGRPolygon*)geometry;
408+
bool result = simplifyOgrGeometry( request, polygon->getExteriorRing(), true );
409+
410+
for (int i = 0, numInteriorRings = polygon->getNumInteriorRings(); i < numInteriorRings; ++i)
411+
{
412+
result |= simplifyOgrGeometry( request, polygon->getInteriorRing(i), true );
413+
}
414+
if ( result ) polygon->flattenTo2D();
415+
return result;
416+
}
417+
else
418+
if (wkbGeometryType==wkbMultiLineString || wkbGeometryType==wkbMultiPolygon)
419+
{
420+
OGRGeometryCollection* collection = (OGRGeometryCollection*)geometry;
421+
bool result = false;
422+
423+
for (int i = 0, numGeometries = collection->getNumGeometries(); i < numGeometries; ++i)
424+
{
425+
result |= simplifyOgrGeometry( request, collection->getGeometryRef(i), wkbGeometryType==wkbMultiPolygon );
426+
}
427+
if ( result ) collection->flattenTo2D();
428+
return result;
429+
}
430+
return false;
311431
}
312432

313433
//! notify the OGRFeatureH was readed of the data provider
314434
void QgsOgrSimplifiedFeatureIterator::notifyReadedFeature( OGRFeatureH fet, OGRGeometryH geom, QgsFeature& feature )
315435
{
316-
/* TODO: ### ahuarte47!
317436
if ( mRequest.flags() & QgsFeatureRequest::SimplifyGeometries )
318437
{
319-
OGRwkbGeometryType wkbType = OGR_G_GetGeometryType( geom );
320-
OGRwkbGeometryType wkbGeometryType = QgsOgrProvider::ogrWkbSingleFlatten( wkbType );
438+
OGRwkbGeometryType wkbType = QgsOgrProvider::ogrWkbSingleFlatten( OGR_G_GetGeometryType(geom) );
321439

322-
if (wkbGeometryType==wkbLineString || wkbGeometryType==wkbPolygon)
440+
if (wkbType==wkbLineString || wkbType==wkbPolygon)
323441
{
442+
simplifyOgrGeometry( mRequest, (OGRGeometry*)geom, wkbType==wkbPolygon );
324443
}
325-
}*/
444+
}
326445
QgsOgrFeatureIterator::notifyReadedFeature( fet, geom, feature );
327446
}
328447

src/providers/ogr/qgsogrfeatureiterator.h

+15
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ class QgsOgrFeatureIterator : public QgsAbstractFeatureIterator
7979
* *
8080
***************************************************************************/
8181

82+
class OGRRawPoint;
83+
class OGRGeometry;
84+
8285
//! Provides a specialized FeatureIterator for enable map2pixel simplification of the geometries
8386
class QgsOgrSimplifiedFeatureIterator : public QgsOgrFeatureIterator
8487
{
@@ -89,6 +92,18 @@ class QgsOgrSimplifiedFeatureIterator : public QgsOgrFeatureIterator
8992
protected:
9093
//! notify the OGRFeatureH was readed of the data provider
9194
virtual void notifyReadedFeature( OGRFeatureH fet, OGRGeometryH geom, QgsFeature& feature );
95+
96+
private:
97+
//! Point memory buffer for optimize the simplification process
98+
OGRRawPoint* mPointBufferPtr;
99+
//! Current Point memory buffer size
100+
int mPointBufferCount;
101+
102+
//! Simplify the OGR-geometry using the specified tolerance
103+
bool simplifyOgrGeometry ( const QgsFeatureRequest& request, OGRGeometry* geometry, bool isaLinearRing );
104+
105+
//! Returns a point buffer of the specified size
106+
OGRRawPoint* mallocPoints( int numPoints );
92107
};
93108

94109
/***************************************************************************/

src/providers/ogr/qgsogrprovider.cpp

+2-4
Original file line numberDiff line numberDiff line change
@@ -808,10 +808,9 @@ void QgsOgrProvider::setRelevantFields( OGRLayerH ogrLayer, bool fetchGeometry,
808808

809809
QgsFeatureIterator QgsOgrProvider::getFeatures( const QgsFeatureRequest& request )
810810
{
811-
/* TODO: ### ahuarte47!
812811
if ( request.flags() & QgsFeatureRequest::SimplifyGeometries )
813812
return QgsFeatureIterator( new QgsOgrSimplifiedFeatureIterator( this, request ) );
814-
*/
813+
815814
return QgsFeatureIterator( new QgsOgrFeatureIterator( this, request ) );
816815
}
817816

@@ -1498,9 +1497,8 @@ int QgsOgrProvider::capabilities() const
14981497
}
14991498
}
15001499

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

15061504
return ability;

0 commit comments

Comments
 (0)