Use new geometry classes in QgsDistanceArea instead wkb parsing. That way, also the new zm types can be measured

This commit is contained in:
Marco Hugentobler 2015-07-27 11:44:01 +02:00
parent fc372c9829
commit 8680259818
3 changed files with 198 additions and 178 deletions

View File

@ -42,19 +42,19 @@ class QgsMapToPixel;
class QPainter;
class QgsPolygonV2;
/** polyline is represented as a vector of points */
/** Polyline is represented as a vector of points */
typedef QVector<QgsPoint> QgsPolyline;
/** polygon: first item of the list is outer ring, inner rings (if any) start from second item */
/** Polygon: first item of the list is outer ring, inner rings (if any) start from second item */
typedef QVector<QgsPolyline> QgsPolygon;
/** a collection of QgsPoints that share a common collection of attributes */
/** A collection of QgsPoints that share a common collection of attributes */
typedef QVector<QgsPoint> QgsMultiPoint;
/** a collection of QgsPolylines that share a common collection of attributes */
/** A collection of QgsPolylines that share a common collection of attributes */
typedef QVector<QgsPolyline> QgsMultiPolyline;
/** a collection of QgsPolygons that share a common collection of attributes */
/** A collection of QgsPolygons that share a common collection of attributes */
typedef QVector<QgsPolygon> QgsMultiPolygon;
class QgsRectangle;
@ -78,10 +78,10 @@ class CORE_EXPORT QgsGeometry
//! Constructor
QgsGeometry();
/** copy constructor will prompt a deep copy of the object */
/** Copy constructor will prompt a deep copy of the object */
QgsGeometry( const QgsGeometry & );
/** assignments will prompt a deep copy of the object
/** Assignments will prompt a deep copy of the object
@note not available in python bindings
*/
QgsGeometry & operator=( QgsGeometry const & rhs );
@ -300,61 +300,61 @@ class CORE_EXPORT QgsGeometry
*/
double closestSegmentWithContext( const QgsPoint& point, QgsPoint& minDistPoint, int& afterVertex, double* leftOf = 0, double epsilon = DEFAULT_SEGMENT_EPSILON ) const;
/**Adds a new ring to this geometry. This makes only sense for polygon and multipolygons.
/** Adds a new ring to this geometry. This makes only sense for polygon and multipolygons.
@return 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed,
3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring*/
int addRing( const QList<QgsPoint>& ring );
/**Adds a new ring to this geometry. This makes only sense for polygon and multipolygons.
/** Adds a new ring to this geometry. This makes only sense for polygon and multipolygons.
@return 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed,
3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring*/
int addRing( QgsCurveV2* ring );
/**Adds a new island polygon to a multipolygon feature
/** Adds a new island polygon to a multipolygon feature
@return 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
not disjoint with existing polygons of the feature*/
int addPart( const QList<QgsPoint> &points, QGis::GeometryType geomType = QGis::UnknownGeometry );
/**Adds a new part to this geometry (takes ownership)
/** Adds a new part to this geometry (takes ownership)
@return 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
not disjoint with existing polygons of the feature*/
int addPart( QgsAbstractGeometryV2* part );
/**Adds a new island polygon to a multipolygon feature
/** Adds a new island polygon to a multipolygon feature
@return 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
not disjoint with existing polygons of the feature
@note not available in python bindings
*/
int addPart( GEOSGeometry *newPart );
/**Adds a new island polygon to a multipolygon feature
/** Adds a new island polygon to a multipolygon feature
@return 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
not disjoint with existing polygons of the feature
@note available in python bindings as addPartGeometry (added in 2.2)
*/
int addPart( const QgsGeometry *newPart );
/**Translate this geometry by dx, dy
/** Translate this geometry by dx, dy
@return 0 in case of success*/
int translate( double dx, double dy );
/**Transform this geometry as described by CoordinateTransform ct
/** Transform this geometry as described by CoordinateTransform ct
@return 0 in case of success*/
int transform( const QgsCoordinateTransform& ct );
/**Transform this geometry as described by QTransform ct
/** Transform this geometry as described by QTransform ct
@note added in 2.8
@return 0 in case of success*/
int transform( const QTransform& ct );
/**Rotate this geometry around the Z axis
/** Rotate this geometry around the Z axis
@note added in 2.8
@param rotation clockwise rotation in degrees
@param center rotation center
@return 0 in case of success*/
int rotate( double rotation, const QgsPoint& center );
/**Splits this geometry according to a given line.
/** Splits this geometry according to a given line.
@param splitLine the line that splits the geometry
@param[out] newGeometries list of new geometries that have been created with the split
@param topological true if topological editing is enabled
@ -365,17 +365,17 @@ class CORE_EXPORT QgsGeometry
bool topological,
QList<QgsPoint> &topologyTestPoints );
/**Replaces a part of this geometry with another line
/** Replaces a part of this geometry with another line
@return 0 in case of success
@note: this function was added in version 1.3*/
int reshapeGeometry( const QList<QgsPoint>& reshapeWithLine );
/**Changes this geometry such that it does not intersect the other geometry
/** Changes this geometry such that it does not intersect the other geometry
@param other geometry that should not be intersect
@return 0 in case of success*/
int makeDifference( const QgsGeometry* other );
/**Returns the bounding box of this feature*/
/** Returns the bounding box of this feature*/
QgsRectangle boundingBox() const;
/** Test for intersection with a rectangle (uses GEOS) */
@ -522,13 +522,13 @@ class CORE_EXPORT QgsGeometry
@note added in version 1.1 */
QList<QgsGeometry*> asGeometryCollection() const;
/**Return contents of the geometry as a QPointF if wkbType is WKBPoint,
/** Return contents of the geometry as a QPointF if wkbType is WKBPoint,
* otherwise returns a null QPointF.
* @note added in QGIS 2.7
*/
QPointF asQPointF() const;
/**Return contents of the geometry as a QPolygonF. If geometry is a linestring,
/** Return contents of the geometry as a QPolygonF. If geometry is a linestring,
* then the result will be an open QPolygonF. If the geometry is a polygon,
* then the result will be a closed QPolygonF of the geometry's exterior ring.
* @note added in QGIS 2.7
@ -587,7 +587,7 @@ class CORE_EXPORT QgsGeometry
**/
void validateGeometry( QList<Error> &errors );
/** compute the unary union on a list of geometries. May be faster than an iterative union on a set of geometries.
/** Compute the unary union on a list of geometries. May be faster than an iterative union on a set of geometries.
@param geometryList a list of QgsGeometry* as input
@returns the new computed QgsGeometry, or null
*/
@ -642,19 +642,19 @@ class CORE_EXPORT QgsGeometry
*/
int vertexNrFromVertexId( const QgsVertexId& i ) const;
/** return GEOS context handle
/** Return GEOS context handle
* @note added in 2.6
* @note not available in Python
*/
static GEOSContextHandle_t getGEOSHandler();
/**Construct geometry from a QPointF
/** Construct geometry from a QPointF
* @param point source QPointF
* @note added in QGIS 2.7
*/
static QgsGeometry* fromQPointF( const QPointF& point );
/**Construct geometry from a QPolygonF. If the polygon is closed than
/** Construct geometry from a QPolygonF. If the polygon is closed than
* the resultant geometry will be a polygon, if it is open than the
* geometry will be a polyline.
* @param polygon source QPolygonF
@ -707,7 +707,7 @@ class CORE_EXPORT QgsGeometry
*/
static bool compare( const QgsMultiPolygon& p1, const QgsMultiPolygon& p2, double epsilon = 4 * DBL_EPSILON );
/**Smooths a geometry by rounding off corners using the Chaikin algorithm. This operation
/** Smooths a geometry by rounding off corners using the Chaikin algorithm. This operation
* roughly doubles the number of vertices in a geometry.
* @param iterations number of smoothing iterations to run. More iterations results
* in a smoother geometry
@ -718,15 +718,19 @@ class CORE_EXPORT QgsGeometry
*/
QgsGeometry* smooth( const unsigned int iterations = 1, const double offset = 0.25 ) const;
/**Smooths a polygon using the Chaikin algorithm*/
/** Smooths a polygon using the Chaikin algorithm*/
QgsPolygon smoothPolygon( const QgsPolygon &polygon, const unsigned int iterations = 1, const double offset = 0.25 ) const;
/**Smooths a polyline using the Chaikin algorithm*/
/** Smooths a polyline using the Chaikin algorithm*/
QgsPolyline smoothLine( const QgsPolyline &polyline, const unsigned int iterations = 1, const double offset = 0.25 ) const;
/** Creates and returns a new geometry engine
*/
static QgsGeometryEngine* createGeometryEngine( const QgsAbstractGeometryV2* geometry );
//convert point list from v1 to v2
static void convertPointList( const QList<QgsPoint>& input, QList<QgsPointV2>& output );
static void convertPointList( const QList<QgsPointV2>& input, QList<QgsPoint>& output );
private:
QgsGeometryPrivate* d; //implicitely shared data pointer
@ -734,17 +738,14 @@ class CORE_EXPORT QgsGeometry
void detach( bool cloneGeom = true ); //make sure mGeometry only referenced from this instance
void removeWkbGeos();
//convert point list from v1 to v2
static void convertPointList( const QList<QgsPoint>& input, QList<QgsPointV2>& output );
static void convertPointList( const QList<QgsPointV2>& input, QList<QgsPoint>& output );
static void convertToPolyline( const QList<QgsPointV2>& input, QgsPolyline& output );
static void convertPolygon( const QgsPolygonV2& input, QgsPolygon& output );
/** try to convert the geometry to a point */
/** Try to convert the geometry to a point */
QgsGeometry* convertToPoint( bool destMultipart ) const;
/** try to convert the geometry to a line */
/** Try to convert the geometry to a line */
QgsGeometry* convertToLine( bool destMultipart ) const;
/** try to convert the geometry to a polygon */
/** Try to convert the geometry to a polygon */
QgsGeometry* convertToPolygon( bool destMultipart ) const;
}; // class QgsGeometry

View File

@ -25,11 +25,16 @@
#include "qgscoordinatetransform.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsgeometry.h"
#include "qgsgeometrycollectionv2.h"
#include "qgsdistancearea.h"
#include "qgsapplication.h"
#include "qgslogger.h"
#include "qgsmessagelog.h"
#include "qgsmultisurfacev2.h"
#include "qgswkbptr.h"
#include "qgslinestringv2.h"
#include "qgspolygonv2.h"
#include "qgssurfacev2.h"
// MSVC compiler doesn't have defined M_PI in math.h
#ifndef M_PI
@ -256,79 +261,85 @@ bool QgsDistanceArea::setEllipsoid( double semiMajor, double semiMinor )
return true;
}
double QgsDistanceArea::measure( const QgsAbstractGeometryV2* geomV2 ) const
{
if ( !geomV2 )
{
return 0.0;
}
int geomDimension = geomV2->dimension();
if ( geomDimension <= 0 )
{
return 0.0;
}
if ( !mEllipsoidalMode )
{
if ( geomDimension == 1 )
{
return geomV2->length();
}
else
{
return geomV2->area();
}
}
else
{
//multigeom is sum of measured parts
const QgsGeometryCollectionV2* collection = dynamic_cast<const QgsGeometryCollectionV2*>( geomV2 );
if ( collection )
{
double sum = 0;
for ( int i = 0; i < collection->numGeometries(); ++i )
{
sum += measure( collection->geometryN( i ) );
}
return sum;
}
if ( geomDimension == 1 )
{
const QgsCurveV2* curve = dynamic_cast<const QgsCurveV2*>( geomV2 );
if ( !curve )
{
return 0.0;
}
QgsLineStringV2* lineString = curve->curveToLine();
double length = measureLine( lineString );
delete lineString;
return length;
}
else
{
const QgsSurfaceV2* surface = dynamic_cast<const QgsSurfaceV2*>( geomV2 );
QgsPolygonV2* polygon = surface->surfaceToPolygon();
double area = 0;
const QgsCurveV2* outerRing = polygon->exteriorRing();
area += measurePolygon( outerRing );
for ( int i = 0; i < polygon->numInteriorRings(); ++i )
{
const QgsCurveV2* innerRing = polygon->interiorRing( i );
area -= measurePolygon( innerRing );
}
delete polygon;
return area;
}
}
return 0.0;
}
double QgsDistanceArea::measure( const QgsGeometry *geometry ) const
{
if ( !geometry )
return 0.0;
const unsigned char* wkb = geometry->asWkb();
if ( !wkb )
return 0.0;
QgsConstWkbPtr wkbPtr( wkb + 1 );
QGis::WkbType wkbType;
wkbPtr >> wkbType;
double res, resTotal = 0;
int count, i;
// measure distance or area based on what is the type of geometry
bool hasZptr = false;
switch ( wkbType )
{
case QGis::WKBLineString25D:
hasZptr = true;
//intentional fall-through
case QGis::WKBLineString:
measureLine( wkb, &res, hasZptr );
QgsDebugMsg( "returning " + QString::number( res ) );
return res;
case QGis::WKBMultiLineString25D:
hasZptr = true;
//intentional fall-through
case QGis::WKBMultiLineString:
wkbPtr >> count;
for ( i = 0; i < count; i++ )
{
wkbPtr = measureLine( wkbPtr, &res, hasZptr );
resTotal += res;
}
QgsDebugMsg( "returning " + QString::number( resTotal ) );
return resTotal;
case QGis::WKBPolygon25D:
hasZptr = true;
//intentional fall-through
case QGis::WKBPolygon:
measurePolygon( wkb, &res, 0, hasZptr );
QgsDebugMsg( "returning " + QString::number( res ) );
return res;
case QGis::WKBMultiPolygon25D:
hasZptr = true;
//intentional fall-through
case QGis::WKBMultiPolygon:
wkbPtr >> count;
for ( i = 0; i < count; i++ )
{
wkbPtr = measurePolygon( wkbPtr, &res, 0, hasZptr );
if ( !wkbPtr )
{
QgsDebugMsg( "measurePolygon returned 0" );
break;
}
resTotal += res;
}
QgsDebugMsg( "returning " + QString::number( resTotal ) );
return resTotal;
default:
QgsDebugMsg( QString( "measure: unexpected geometry type: %1" ).arg( wkbType ) );
return 0;
}
const QgsAbstractGeometryV2* geomV2 = geometry->geometry();
return measure( geomV2 );
}
double QgsDistanceArea::measurePerimeter( const QgsGeometry* geometry ) const
@ -336,86 +347,71 @@ double QgsDistanceArea::measurePerimeter( const QgsGeometry* geometry ) const
if ( !geometry )
return 0.0;
const unsigned char* wkb = geometry->asWkb();
if ( !wkb )
const QgsAbstractGeometryV2* geomV2 = geometry->geometry();
if ( !geomV2 || geomV2->dimension() < 2 )
{
return 0.0;
QgsConstWkbPtr wkbPtr( wkb + 1 );
QGis::WkbType wkbType;
wkbPtr >> wkbType;
double res = 0.0, resTotal = 0.0;
int count, i;
// measure distance or area based on what is the type of geometry
bool hasZptr = false;
switch ( wkbType )
{
case QGis::WKBLineString25D:
case QGis::WKBLineString:
case QGis::WKBMultiLineString25D:
case QGis::WKBMultiLineString:
return 0.0;
case QGis::WKBPolygon25D:
hasZptr = true;
//intentional fall-through
case QGis::WKBPolygon:
measurePolygon( wkb, 0, &res, hasZptr );
QgsDebugMsg( "returning " + QString::number( res ) );
return res;
case QGis::WKBMultiPolygon25D:
hasZptr = true;
//intentional fall-through
case QGis::WKBMultiPolygon:
wkbPtr >> count;
for ( i = 0; i < count; i++ )
{
wkbPtr = measurePolygon( wkbPtr, 0, &res, hasZptr );
if ( !wkbPtr )
{
QgsDebugMsg( "measurePolygon returned 0" );
break;
}
resTotal += res;
}
QgsDebugMsg( "returning " + QString::number( resTotal ) );
return resTotal;
default:
QgsDebugMsg( QString( "measure: unexpected geometry type: %1" ).arg( wkbType ) );
return 0;
}
}
const unsigned char* QgsDistanceArea::measureLine( const unsigned char* feature, double* area, bool hasZptr ) const
{
QgsConstWkbPtr wkbPtr( feature + 1 + sizeof( int ) );
int nPoints;
wkbPtr >> nPoints;
QList<QgsPoint> points;
double x, y;
QgsDebugMsg( "This feature WKB has " + QString::number( nPoints ) + " points" );
// Extract the points from the WKB format into the vector
for ( int i = 0; i < nPoints; ++i )
if ( !mEllipsoidalMode )
{
wkbPtr >> x >> y;
if ( hasZptr )
return geomV2->length();
}
//create list with (single) surfaces
QList< const QgsSurfaceV2* > surfaces;
const QgsSurfaceV2* surf = dynamic_cast<const QgsSurfaceV2*>( geomV2 );
if ( surf )
{
surfaces.append( surf );
}
const QgsMultiSurfaceV2* multiSurf = dynamic_cast<const QgsMultiSurfaceV2*>( geomV2 );
if ( multiSurf )
{
for ( int i = 0; i < multiSurf->numGeometries(); ++i )
{
// totally ignore Z value
wkbPtr += sizeof( double );
surfaces.append( static_cast<const QgsSurfaceV2*>( multiSurf->geometryN( i ) ) );
}
}
double length = 0;
QList<QgsPointV2> pointList;
QList<const QgsSurfaceV2*>::const_iterator surfaceIt = surfaces.constBegin();
for ( ; surfaceIt != surfaces.constEnd(); ++surfaceIt )
{
if ( !*surfaceIt )
{
continue;
}
points.append( QgsPoint( x, y ) );
QgsPolygonV2* poly = ( *surfaceIt )->surfaceToPolygon();
const QgsCurveV2* outerRing = poly->exteriorRing();
if ( outerRing )
{
length += measure( outerRing );
}
int nInnerRings = poly->numInteriorRings();
for ( int i = 0; i < nInnerRings; ++i )
{
length += measure( poly->interiorRing( i ) );
}
delete poly;
}
return length;
}
double QgsDistanceArea::measureLine( const QgsCurveV2* curve ) const
{
if ( !curve )
{
return 0.0;
}
*area = measureLine( points );
return wkbPtr;
QList<QgsPointV2> linePointsV2;
QList<QgsPoint> linePoints;
curve->points( linePointsV2 );
QgsGeometry::convertPointList( linePointsV2, linePoints );
return measureLine( linePoints );
}
double QgsDistanceArea::measureLine( const QList<QgsPoint> &points ) const
@ -590,6 +586,26 @@ const unsigned char *QgsDistanceArea::measurePolygon( const unsigned char* featu
return wkbPtr;
}
double QgsDistanceArea::measurePolygon( const QgsCurveV2* curve ) const
{
if ( !curve )
{
return 0.0;
}
QList<QgsPointV2> linePointsV2;
curve->points( linePointsV2 );
QList<QgsPoint> linePoints;
QList<QgsPointV2>::const_iterator ptIt = linePointsV2.constBegin();
for ( ; ptIt != linePointsV2.constEnd(); ++ptIt )
{
linePoints.append( mCoordTransform->transform( QPoint( ptIt->x(), ptIt->y() ) ) );
}
return computePolygonArea( linePoints );
}
double QgsDistanceArea::measurePolygon( const QList<QgsPoint>& points ) const
{

View File

@ -20,6 +20,8 @@
#include "qgscoordinatetransform.h"
class QgsGeometry;
class QgsAbstractGeometryV2;
class QgsCurveV2;
/** \ingroup core
General purpose distance and area calculator.
@ -110,9 +112,6 @@ class CORE_EXPORT QgsDistanceArea
void convertMeasurement( double &measure, QGis::UnitType &measureUnits, QGis::UnitType displayUnits, bool isArea ) const;
protected:
//! measures line distance, line points are extracted from WKB
// @note available in python bindings
const unsigned char* measureLine( const unsigned char* feature, double* area, bool hasZptr = false ) const;
//! measures polygon area and perimeter, vertices are extracted from WKB
// @note available in python bindings
const unsigned char* measurePolygon( const unsigned char* feature, double* area, double* perimeter, bool hasZptr = false ) const;
@ -172,6 +171,10 @@ class CORE_EXPORT QgsDistanceArea
double getQ( double x ) const;
double getQbar( double x ) const;
double measure( const QgsAbstractGeometryV2* geomV2 ) const;
double measureLine( const QgsCurveV2* curve ) const;
double measurePolygon( const QgsCurveV2* curve ) const;
// temporary area measurement stuff
double m_QA, m_QB, m_QC;