Skip to content

Commit

Permalink
#8725-R: OgrProvider simplifies on provider side
Browse files Browse the repository at this point in the history
OgrFeatureIterator implements simplification on provider side
  • Loading branch information
ahuarte47 authored and m-kuhn committed Jan 15, 2014
1 parent 23b6523 commit dcaf0b5
Show file tree
Hide file tree
Showing 6 changed files with 382 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/providers/ogr/CMakeLists.txt
@@ -1,5 +1,5 @@

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

SET(OGR_MOC_HDRS qgsogrprovider.h qgsogrdataitems.h)

Expand Down
47 changes: 47 additions & 0 deletions src/providers/ogr/qgsogrfeatureiterator.cpp
Expand Up @@ -15,6 +15,7 @@
#include "qgsogrfeatureiterator.h"

#include "qgsogrprovider.h"
#include "qgsogrgeometrysimplifier.h"

#include "qgsapplication.h"
#include "qgsgeometry.h"
Expand All @@ -38,6 +39,7 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrProvider* p, const QgsFeatur
, ogrDataSource( 0 )
, ogrLayer( 0 )
, mSubsetStringSet( false )
, mGeometrySimplifier( NULL )
{
mFeatureFetched = false;

Expand Down Expand Up @@ -78,12 +80,20 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrProvider* p, const QgsFeatur
OGR_L_SetSpatialFilter( ogrLayer, 0 );
}

//setup if required the simplification of OGR-geometries fetched
prepareProviderSimplification();

//start with first feature
rewind();
}

QgsOgrFeatureIterator::~QgsOgrFeatureIterator()
{
if ( mGeometrySimplifier )
{
delete mGeometrySimplifier;
mGeometrySimplifier = NULL;
}
close();
}

Expand All @@ -95,6 +105,40 @@ void QgsOgrFeatureIterator::ensureRelevantFields()
P->mRelevantFieldsForNextFeature = true;
}

bool QgsOgrFeatureIterator::prepareProviderSimplification()
{
const QgsSimplifyMethod& simplifyMethod = mRequest.simplifyMethod();

if ( mGeometrySimplifier )
{
delete mGeometrySimplifier;
mGeometrySimplifier = NULL;
}

// setup if required the simplification of OGR-geometries fetched, it uses the settings of current FeatureRequest
if ( simplifyMethod.methodType() != QgsSimplifyMethod::NoSimplification && !simplifyMethod.forceLocalOptimization() && !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) )
{
QgsSimplifyMethod::MethodType methodType = simplifyMethod.methodType();

if ( methodType == QgsSimplifyMethod::OptimizeForRendering )
{
int simplifyFlags = QgsMapToPixelSimplifier::SimplifyGeometry | QgsMapToPixelSimplifier::SimplifyEnvelope;
mGeometrySimplifier = new QgsOgrMapToPixelSimplifier( simplifyFlags, simplifyMethod.tolerance() );
return true;
}
else
if ( methodType == QgsSimplifyMethod::PreserveTopology )
{
mGeometrySimplifier = new QgsOgrTopologyPreservingSimplifier( simplifyMethod.tolerance() );
return true;
}
else
{
QgsDebugMsg( QString( "Simplification method type (%1) is not recognised by OgrFeatureIterator class" ).arg( methodType ) );
}
}
return false;
}

bool QgsOgrFeatureIterator::fetchFeature( QgsFeature& feature )
{
Expand Down Expand Up @@ -233,6 +277,9 @@ bool QgsOgrFeatureIterator::readFeature( OGRFeatureH fet, QgsFeature& feature )

if ( geom )
{
OGRGeometry* ogrGeometry = (OGRGeometry*)geom;
if ( mGeometrySimplifier ) mGeometrySimplifier->simplifyGeometry( ogrGeometry );

// get the wkb representation
int memorySize = OGR_G_WkbSize( geom );
unsigned char *wkb = new unsigned char[memorySize];
Expand Down
8 changes: 8 additions & 0 deletions src/providers/ogr/qgsogrfeatureiterator.h
Expand Up @@ -20,6 +20,7 @@
#include <ogr_api.h>

class QgsOgrProvider;
class QgsOgrAbstractGeometrySimplifier;

class QgsOgrFeatureIterator : public QgsAbstractFeatureIterator
{
Expand All @@ -38,6 +39,9 @@ class QgsOgrFeatureIterator : public QgsAbstractFeatureIterator
//! fetch next feature, return true on success
virtual bool fetchFeature( QgsFeature& feature );

//! setup if required the simplification of OGR-geometries fetched, it uses the settings of current FeatureRequest
virtual bool prepareProviderSimplification();

QgsOgrProvider* P;

void ensureRelevantFields();
Expand All @@ -56,6 +60,10 @@ class QgsOgrFeatureIterator : public QgsAbstractFeatureIterator

//! Set to true, if geometry is in the requested columns
bool mFetchGeometry;

private:
//! optional object to simplify OGR-geometries fecthed by this feature iterator
QgsOgrAbstractGeometrySimplifier* mGeometrySimplifier;
};

#endif // QGSOGRFEATUREITERATOR_H
241 changes: 241 additions & 0 deletions src/providers/ogr/qgsogrgeometrysimplifier.cpp
@@ -0,0 +1,241 @@
/***************************************************************************
qgsogrgeometrysimplifier.cpp
---------------------
begin : December 2013
copyright : (C) 2013 by Alvaro Huarte
email : http://wiki.osgeo.org/wiki/Alvaro_Huarte
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsogrgeometrysimplifier.h"
#include "qgsogrprovider.h"
#include "qgsapplication.h"

QgsOgrAbstractGeometrySimplifier::~QgsOgrAbstractGeometrySimplifier()
{
}

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

QgsOgrTopologyPreservingSimplifier::QgsOgrTopologyPreservingSimplifier( double tolerance ) : QgsTopologyPreservingSimplifier( tolerance )
{
}

QgsOgrTopologyPreservingSimplifier::~QgsOgrTopologyPreservingSimplifier()
{
}

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

if ( wkbGeometryType == wkbLineString || wkbGeometryType == wkbPolygon )
{
OGRGeometry* g = geometry->SimplifyPreserveTopology( mTolerance );

if ( g )
{
size_t wkbSize = g->WkbSize();
unsigned char * wkb = (unsigned char *)OGRMalloc( wkbSize );
g->exportToWkb( ( OGRwkbByteOrder ) QgsApplication::endian(), wkb );
geometry->importFromWkb( wkb, wkbSize );
delete g;

return true;
}
}
return false;
}

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

QgsOgrMapToPixelSimplifier::QgsOgrMapToPixelSimplifier( int simplifyFlags, double map2pixelTol ) : QgsMapToPixelSimplifier( simplifyFlags, map2pixelTol )
{
mPointBufferCount = 64;
mPointBufferPtr = ( OGRRawPoint* )OGRMalloc( mPointBufferCount * sizeof( OGRRawPoint ) );
}

QgsOgrMapToPixelSimplifier::~QgsOgrMapToPixelSimplifier()
{
if ( mPointBufferPtr )
{
OGRFree( mPointBufferPtr );
mPointBufferPtr = NULL;
}
}

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

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

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

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

double map2pixelTol = mMapToPixelTol * mMapToPixelTol; //-> Use mappixelTol for 'LengthSquare' calculations.
double x, y, lastX = 0, lastY = 0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

if ( wkbGeometryType == wkbLineString || wkbGeometryType == wkbPolygon )
{
return simplifyOgrGeometry( geometry, wkbGeometryType == wkbPolygon );
}
return false;
}

0 comments on commit dcaf0b5

Please sign in to comment.