[FEATURE] New class for triangle

Adds a new geometry class for Triangle geometries

Methods include orthocenter, bisectors, medians, medial, circumscribed (center,
radius), inscribed (center, radius)

Also adds make_triangle expression function for creating triangles
This commit is contained in:
lbartoletti 2017-03-21 09:50:47 +01:00 committed by Nyall Dawson
parent 0710fb5da8
commit fb3d07f3f5
15 changed files with 1624 additions and 0 deletions

View File

@ -370,5 +370,6 @@
%Include geometry/qgspointv2.sip
%Include geometry/qgspolygon.sip
%Include geometry/qgssurface.sip
%Include geometry/qgstriangle.sip
%Include geometry/qgswkbtypes.sip
%Include geometry/qgswkbptr.sip

View File

@ -67,4 +67,10 @@ class QgsGeometryUtils
static QgsPointV2 midpoint (const QgsPointV2& pt1, const QgsPointV2& pt2);
static double gradient( const QgsPointV2& pt1, const QgsPointV2& pt2 );
static void coefficients(const QgsPointV2 &pt1, const QgsPointV2 &pt2, double& a /Out/, double& b /Out/, double& c /Out/);
static QgsLineString perpendicularSegment( const QgsPointV2& p, const QgsPointV2& s1, const QgsPointV2& s2 );
};

View File

@ -0,0 +1,63 @@
class QgsTriangle : public QgsPolygonV2
{
%TypeHeaderCode
#include <qgstriangle.h>
%End
public:
QgsTriangle();
QgsTriangle( const QgsPointV2 &p1, const QgsPointV2 &p2, const QgsPointV2 &p3 );
explicit QgsTriangle( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3 );
explicit QgsTriangle( const QPointF p1, const QPointF p2, const QPointF p3 );
// inherited: bool operator==( const QgsTriangle& other ) const;
// inherited: bool operator!=( const QgsTriangle& other ) const;
virtual QString geometryType() const;
virtual QgsTriangle* clone() const /Factory/;
void clear();
virtual bool fromWkb( QgsConstWkbPtr& wkbPtr );
bool fromWkt( const QString &wkt );
// inherited: QString asWkt( int precision = 17 ) const;
// inherited: QDomElement asGML2( QDomDocument& doc, int precision = 17, const QString& ns = "gml" ) const;
// inherited: QDomElement asGML3( QDomDocument& doc, int precision = 17, const QString& ns = "gml" ) const;
// inherited: QString asJSON( int precision = 17 ) const;
QgsPolygonV2* surfaceToPolygon() const /Factory/;
QgsAbstractGeometry* toCurveType() const /Factory/;
//overridden to handle LineString25D rings
virtual void setExteriorRing( QgsCurve* ring /Transfer/ );
virtual QgsAbstractGeometry* boundary() const /Factory/;
// inherited: double pointDistanceToBoundary( double x, double y ) const;
QgsPointV2 vertexAt( int atVertex ) const;
void addInteriorRing( QgsCurve* ring /Transfer/ ); // NOTE: no interior ring for triangle.
bool deleteVertex( QgsVertexId position );
bool insertVertex( QgsVertexId position, const QgsPointV2 &vertex );
bool moveVertex( QgsVertexId vId, const QgsPointV2& newPos );
QVector<double> lengths() const;
QVector<double> angles() const;
bool isIsocele( double lengthTolerance = 0.0001 ) const;
bool isEquilateral( double lengthTolerance = 0.0001 ) const;
bool isRight( double angleTolerance = 0.0001 ) const;
bool isScalene( double lengthTolerance = 0.0001 ) const;
QVector<QgsLineString> altitudes( ) const;
QVector<QgsLineString> medians( ) const;
QVector<QgsLineString> bisectors( double lengthTolerance = 0.0001 ) const;
QgsTriangle medial( ) const;
QgsPointV2 orthocenter( double lengthTolerance = 0.0001 ) const;
QgsPointV2 circumscribedCenter( ) const;
double circumscribedRadius( ) const;
// TODO:
// QgsCircle circumscribedCircle ( ) const; // need QgsCircle (from CADDigitize.CADCircle)
QgsPointV2 inscribedCenter( ) const;
double inscribedRadius( ) const;
// TODO:
// QgsCircle inscribedCircle ( ) const; // need QgsCircle (from CADDigitize.CADCircle)
};

View File

@ -383,6 +383,7 @@ SET(QGIS_CORE_SRCS
geometry/qgsmultisurface.cpp
geometry/qgspointv2.cpp
geometry/qgspolygon.cpp
geometry/qgstriangle.cpp
geometry/qgswkbptr.cpp
geometry/qgswkbtypes.cpp
@ -922,6 +923,7 @@ SET(QGIS_CORE_HDRS
geometry/qgsmultisurface.h
geometry/qgspointv2.h
geometry/qgspolygon.h
geometry/qgstriangle.h
geometry/qgssurface.h
geometry/qgswkbptr.h
geometry/qgswkbtypes.h

View File

@ -21,6 +21,7 @@ email : marco.hugentobler at sourcepole dot com
#include "qgslinestring.h"
#include "qgswkbptr.h"
#include <memory>
#include <QStringList>
#include <QVector>
#include <QRegularExpression>
@ -834,6 +835,79 @@ QgsPointV2 QgsGeometryUtils::midpoint( const QgsPointV2 &pt1, const QgsPointV2 &
return QgsPointV2( pType, x, y, z, m );
}
double QgsGeometryUtils::gradient( const QgsPointV2 &pt1, const QgsPointV2 &pt2 )
{
double delta_x = pt2.x() - pt1.x();
double delta_y = pt2.y() - pt1.y();
if ( qgsDoubleNear( delta_x, 0.0 ) )
{
return INFINITY;
}
return delta_y / delta_x;
}
void QgsGeometryUtils::coefficients( const QgsPointV2 &pt1, const QgsPointV2 &pt2, double &a, double &b, double &c )
{
if ( qgsDoubleNear( pt1.x(), pt2.x() ) )
{
a = 1;
b = 0;
c = -pt1.x();
}
else if ( qgsDoubleNear( pt1.y(), pt2.y() ) )
{
a = 0;
b = 1;
c = -pt1.y();
}
else
{
a = pt1.y() - pt2.y();
b = pt2.x() - pt1.x();
c = pt1.x() * pt2.y() - pt1.y() * pt2.x();
}
}
QgsLineString QgsGeometryUtils::perpendicularSegment( const QgsPointV2 &p, const QgsPointV2 &s1, const QgsPointV2 &s2 )
{
QgsLineString line;
QgsPointV2 p2;
if ( ( p == s1 ) || ( p == s2 ) )
{
return line;
}
double a, b, c;
coefficients( s1, s2, a, b, c );
if ( qgsDoubleNear( a, 0 ) )
{
p2 = QgsPointV2( p.x(), s1.y() );
}
else if ( qgsDoubleNear( b, 0 ) )
{
p2 = QgsPointV2( s1.x(), p.y() );
}
else
{
double y = ( -c - a * p.x() ) / b;
double m = gradient( s1, s2 );
double d2 = 1 + m * m;
double H = p.y() - y;
double dx = m * H / d2;
double dy = m * dx;
p2 = QgsPointV2( p.x() + dx, y + dy );
}
line.addVertex( p );
line.addVertex( p2 );
return line;
}
double QgsGeometryUtils::lineAngle( double x1, double y1, double x2, double y2 )
{
double at = atan2( y2 - y1, x2 - x1 );

View File

@ -291,6 +291,33 @@ class CORE_EXPORT QgsGeometryUtils
*/
static QgsPointV2 midpoint( const QgsPointV2 &pt1, const QgsPointV2 &pt2 );
/** Return the gradient of a line defined by points \a pt1 and \a pt2.
* @param pt1 first point.
* @param pt2 second point.
* @return The gradient of this linear entity, or infinity if vertical
* @note added in QGIS 3.0
*/
static double gradient( const QgsPointV2 &pt1, const QgsPointV2 &pt2 );
/** Return the coefficients (a, b, c for equation "ax + by + c = 0") of a line defined by points \a pt1 and \a pt2.
* @param pt1 first point.
* @param pt2 second point.
* @param a Output parameter, a coefficient of the equation.
* @param b Output parameter, b coefficient of the equation.
* @param c Output parameter, c coefficient of the equation.
* @note added in QGIS 3.0
*/
static void coefficients( const QgsPointV2 &pt1, const QgsPointV2 &pt2, double &a, double &b, double &c );
/**
* @brief Create a perpendicular line segment from p to segment [s1, s2]
* @param p The point
* @param s1 The segment start point
* @param s2 The segment end point
* @return A line (segment) from p to perpendicular point on segment [s1, s2]
*/
static QgsLineString perpendicularSegment( const QgsPointV2 &p, const QgsPointV2 &s1, const QgsPointV2 &s2 );
//! @note not available in Python bindings
enum ComponentType
{

View File

@ -210,6 +210,7 @@ class CORE_EXPORT QgsLineString: public QgsCurve
void fromWkbPoints( QgsWkbTypes::Type type, const QgsConstWkbPtr &wkb );
friend class QgsPolygonV2;
friend class QgsTriangle;
};

View File

@ -0,0 +1,529 @@
/***************************************************************************
qgstriangle.cpp
-------------------
begin : January 2017
copyright : (C) 2017 by Loïc Bartoletti
email : lbartoletti at tuxfamily dot org
***************************************************************************/
/***************************************************************************
* *
* 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 "qgstriangle.h"
#include "qgsgeometryutils.h"
#include "qgslinestring.h"
#include "qgswkbptr.h"
#include <memory>
QgsTriangle::QgsTriangle()
: QgsPolygonV2()
{
mWkbType = QgsWkbTypes::Triangle;
}
QgsTriangle::QgsTriangle( const QgsPointV2 &p1, const QgsPointV2 &p2, const QgsPointV2 &p3 )
{
mWkbType = QgsWkbTypes::Triangle;
if ( !validateGeom( p1, p2, p3 ) )
{
return;
}
QgsLineString *ext = new QgsLineString();
ext->setPoints( QgsPointSequence() << p1 << p2 << p3 << p1 );
setExteriorRing( ext );
}
QgsTriangle::QgsTriangle( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3 )
: QgsPolygonV2()
{
mWkbType = QgsWkbTypes::Triangle;
QgsPointV2 pt1( p1 );
QgsPointV2 pt2( p2 );
QgsPointV2 pt3( p3 );
if ( !validateGeom( pt1, pt2, pt3 ) )
{
return;
}
QgsLineString *ext = new QgsLineString();
ext->setPoints( QgsPointSequence() << pt1 << pt2 << pt3 << pt1 );
setExteriorRing( ext );
}
QgsTriangle::QgsTriangle( const QPointF p1, const QPointF p2, const QPointF p3 )
: QgsPolygonV2()
{
mWkbType = QgsWkbTypes::Triangle;
QgsPointV2 pt1( p1 );
QgsPointV2 pt2( p2 );
QgsPointV2 pt3( p3 );
if ( !validateGeom( pt1, pt2, pt3 ) )
{
return;
}
QgsLineString *ext = new QgsLineString();
ext->setPoints( QgsPointSequence() << pt1 << pt2 << pt3 << pt1 );
setExteriorRing( ext );
}
void QgsTriangle::clear()
{
QgsCurvePolygon::clear();
mWkbType = QgsWkbTypes::Triangle;
}
QgsTriangle *QgsTriangle::clone() const
{
return new QgsTriangle( *this );
}
bool QgsTriangle::fromWkb( QgsConstWkbPtr &wkbPtr )
{
clear();
if ( !wkbPtr )
{
return false;
}
QgsWkbTypes::Type type = wkbPtr.readHeader();
if ( QgsWkbTypes::flatType( type ) != QgsWkbTypes::Triangle )
{
return false;
}
mWkbType = type;
QgsWkbTypes::Type ringType;
switch ( mWkbType )
{
case QgsWkbTypes::TriangleZ:
ringType = QgsWkbTypes::LineStringZ;
break;
case QgsWkbTypes::TriangleM:
ringType = QgsWkbTypes::LineStringM;
break;
case QgsWkbTypes::TriangleZM:
ringType = QgsWkbTypes::LineStringZM;
break;
default:
ringType = QgsWkbTypes::LineString;
break;
}
int nRings;
wkbPtr >> nRings;
if ( nRings > 1 )
{
return false;
}
QgsLineString *line = new QgsLineString();
line->fromWkbPoints( ringType, wkbPtr );
if ( !mExteriorRing )
{
mExteriorRing = line;
}
return true;
}
bool QgsTriangle::fromWkt( const QString &wkt )
{
clear();
QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
if ( QgsWkbTypes::geometryType( parts.first ) != QgsWkbTypes::PolygonGeometry )
return false;
mWkbType = parts.first;
QString defaultChildWkbType = QStringLiteral( "LineString%1%2" ).arg( is3D() ? "Z" : QString(), isMeasure() ? "M" : QString() );
Q_FOREACH ( const QString &childWkt, QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType ) )
{
QPair<QgsWkbTypes::Type, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
QgsWkbTypes::Type flatCurveType = QgsWkbTypes::flatType( childParts.first );
if ( flatCurveType == QgsWkbTypes::LineString )
mInteriorRings.append( new QgsLineString() );
else
{
clear();
return false;
}
if ( !mInteriorRings.back()->fromWkt( childWkt ) )
{
clear();
return false;
}
}
if ( mInteriorRings.isEmpty() )
{
clear();
return false;
}
mExteriorRing = mInteriorRings.at( 0 );
mInteriorRings.removeFirst();
//scan through rings and check if dimensionality of rings is different to CurvePolygon.
//if so, update the type dimensionality of the CurvePolygon to match
bool hasZ = false;
bool hasM = false;
if ( mExteriorRing )
{
hasZ = hasZ || mExteriorRing->is3D();
hasM = hasM || mExteriorRing->isMeasure();
}
if ( hasZ )
addZValue( 0 );
if ( hasM )
addMValue( 0 );
return true;
}
QgsPolygonV2 *QgsTriangle::surfaceToPolygon() const
{
return toPolygon();
}
QgsAbstractGeometry *QgsTriangle::toCurveType() const
{
std::unique_ptr<QgsCurvePolygon> curvePolygon( new QgsCurvePolygon() );
curvePolygon->setExteriorRing( mExteriorRing->clone() );
return curvePolygon.release();
}
void QgsTriangle::addInteriorRing( QgsCurve *ring )
{
Q_UNUSED( ring );
return;
}
bool QgsTriangle::deleteVertex( QgsVertexId position )
{
Q_UNUSED( position );
return false;
}
bool QgsTriangle::insertVertex( QgsVertexId position, const QgsPointV2 &vertex )
{
Q_UNUSED( position );
Q_UNUSED( vertex );
return false;
}
#include <iostream>
bool QgsTriangle::moveVertex( QgsVertexId vId, const QgsPointV2 &newPos )
{
if ( !mExteriorRing || vId.part != 0 || vId.ring != 0 || vId.vertex < 0 || vId.vertex > 4 )
{
return false;
}
if ( vId.vertex == 4 )
{
vId.vertex = 0;
}
QgsPointV2 p1( vId.vertex == 0 ? newPos : vertexAt( 0 ) );
QgsPointV2 p2( vId.vertex == 1 ? newPos : vertexAt( 1 ) );
QgsPointV2 p3( vId.vertex == 2 ? newPos : vertexAt( 2 ) );
if ( !validateGeom( p1, p2, p3 ) )
{
return false;
}
QgsCurve *ring = mExteriorRing;
int n = ring->numPoints();
bool success = ring->moveVertex( vId, newPos );
if ( success )
{
// If first or last vertex is moved, also move the last/first vertex
if ( vId.vertex == 0 )
ring->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos );
clearCache();
}
return success;
}
void QgsTriangle::setExteriorRing( QgsCurve *ring )
{
if ( !ring )
{
return;
}
if ( ring->hasCurvedSegments() )
{
//need to segmentize ring as polygon does not support curves
QgsCurve *line = ring->segmentize();
delete ring;
ring = line;
}
if ( ( ring->numPoints() > 4 ) || ( ring->numPoints() < 3 ) )
{
return;
}
else if ( ring->numPoints() == 4 )
{
if ( !ring->isClosed() )
{
return;
}
}
else if ( ring->numPoints() == 3 )
{
if ( ring->isClosed() )
{
return;
}
QgsLineString *lineString = dynamic_cast< QgsLineString *>( ring );
if ( lineString && !lineString->isClosed() )
{
lineString->close();
}
ring = lineString;
}
if ( !validateGeom( ring->vertexAt( QgsVertexId( 0, 0, 0 ) ), ring->vertexAt( QgsVertexId( 0, 0, 1 ) ), ring->vertexAt( QgsVertexId( 0, 0, 2 ) ) ) )
{
return;
}
delete mExteriorRing;
mExteriorRing = ring;
//set proper wkb type
setZMTypeFromSubGeometry( ring, QgsWkbTypes::Triangle );
clearCache();
}
QgsAbstractGeometry *QgsTriangle::boundary() const
{
if ( !mExteriorRing )
return nullptr;
return mExteriorRing->clone();
}
QgsPointV2 QgsTriangle::vertexAt( int atVertex ) const
{
QgsVertexId id( 0, 0, atVertex );
return mExteriorRing->vertexAt( id );
}
QVector<double> QgsTriangle::lengths() const
{
QVector<double> lengths;
lengths.append( vertexAt( 0 ).distance( vertexAt( 1 ) ) );
lengths.append( vertexAt( 1 ).distance( vertexAt( 2 ) ) );
lengths.append( vertexAt( 2 ).distance( vertexAt( 0 ) ) );
return lengths;
}
QVector<double> QgsTriangle::angles() const
{
QVector<double> angles;
double ax, ay, bx, by, cx, cy;
ax = vertexAt( 0 ).x();
ay = vertexAt( 0 ).y();
bx = vertexAt( 1 ).x();
by = vertexAt( 1 ).y();
cx = vertexAt( 2 ).x();
cy = vertexAt( 2 ).y();
double a1 = fmod( QgsGeometryUtils::angleBetweenThreePoints( cx, cy, ax, ay, bx, by ), M_PI );
double a2 = fmod( QgsGeometryUtils::angleBetweenThreePoints( ax, ay, bx, by, cx, cy ), M_PI );
double a3 = fmod( QgsGeometryUtils::angleBetweenThreePoints( bx, by, cx, cy, ax, ay ), M_PI );
angles.append( ( a1 > M_PI / 2 ? a1 - M_PI / 2 : a1 ) );
angles.append( ( a2 > M_PI / 2 ? a2 - M_PI / 2 : a2 ) );
angles.append( ( a3 > M_PI / 2 ? a3 - M_PI / 2 : a3 ) );
return angles;
}
bool QgsTriangle::isIsocele( double lengthTolerance ) const
{
QVector<double> sides = lengths();
bool ab_bc = qgsDoubleNear( sides.at( 0 ), sides.at( 1 ), lengthTolerance );
bool bc_ca = qgsDoubleNear( sides.at( 1 ), sides.at( 2 ), lengthTolerance );
bool ca_ab = qgsDoubleNear( sides.at( 2 ), sides.at( 0 ), lengthTolerance );
return ( ab_bc || bc_ca || ca_ab );
}
bool QgsTriangle::isEquilateral( double lengthTolerance ) const
{
QVector<double> sides = lengths();
bool ab_bc = qgsDoubleNear( sides.at( 0 ), sides.at( 1 ), lengthTolerance );
bool bc_ca = qgsDoubleNear( sides.at( 1 ), sides.at( 2 ), lengthTolerance );
bool ca_ab = qgsDoubleNear( sides.at( 2 ), sides.at( 0 ), lengthTolerance );
return ( ab_bc && bc_ca && ca_ab );
}
bool QgsTriangle::isRight( double angleTolerance ) const
{
QVector<double> a = angles();
QVector<double>::iterator ita = a.begin();
while ( ita != a.end() )
{
if ( qgsDoubleNear( *ita, M_PI / 2.0, angleTolerance ) )
return true;
ita++;
}
return false;
}
bool QgsTriangle::isScalene( double lengthTolerance ) const
{
return !isIsocele( lengthTolerance );
}
QVector<QgsLineString> QgsTriangle::altitudes() const
{
QVector<QgsLineString> alt;
alt.append( QgsGeometryUtils::perpendicularSegment( vertexAt( 0 ), vertexAt( 2 ), vertexAt( 1 ) ) );
alt.append( QgsGeometryUtils::perpendicularSegment( vertexAt( 1 ), vertexAt( 0 ), vertexAt( 2 ) ) );
alt.append( QgsGeometryUtils::perpendicularSegment( vertexAt( 2 ), vertexAt( 0 ), vertexAt( 1 ) ) );
return alt;
}
QVector<QgsLineString> QgsTriangle::medians() const
{
QVector<QgsLineString> med;
QgsLineString med1;
QgsLineString med2;
QgsLineString med3;
med1.setPoints( QgsPointSequence() << vertexAt( 0 ) << QgsGeometryUtils::midpoint( vertexAt( 1 ), vertexAt( 2 ) ) );
med2.setPoints( QgsPointSequence() << vertexAt( 1 ) << QgsGeometryUtils::midpoint( vertexAt( 0 ), vertexAt( 2 ) ) );
med3.setPoints( QgsPointSequence() << vertexAt( 2 ) << QgsGeometryUtils::midpoint( vertexAt( 0 ), vertexAt( 1 ) ) );
med.append( med1 );
med.append( med2 );
med.append( med3 );
return med;
}
QVector<QgsLineString> QgsTriangle::bisectors( double lengthTolerance ) const
{
QVector<QgsLineString> bis;
QgsLineString bis1;
QgsLineString bis2;
QgsLineString bis3;
QgsPointV2 incenter = inscribedCenter();
QgsPointV2 out;
QgsGeometryUtils::segmentIntersection( vertexAt( 0 ), incenter, vertexAt( 1 ), vertexAt( 2 ), out, lengthTolerance );
bis1.setPoints( QgsPointSequence() << vertexAt( 0 ) << out );
QgsGeometryUtils::segmentIntersection( vertexAt( 1 ), incenter, vertexAt( 0 ), vertexAt( 2 ), out, lengthTolerance );
bis2.setPoints( QgsPointSequence() << vertexAt( 1 ) << out );
QgsGeometryUtils::segmentIntersection( vertexAt( 2 ), incenter, vertexAt( 0 ), vertexAt( 1 ), out, lengthTolerance );
bis3.setPoints( QgsPointSequence() << vertexAt( 2 ) << out );
bis.append( bis1 );
bis.append( bis2 );
bis.append( bis3 );
return bis;
}
QgsTriangle QgsTriangle::medial() const
{
QgsPointV2 p1, p2, p3;
p1 = QgsGeometryUtils::midpoint( vertexAt( 0 ), vertexAt( 1 ) );
p2 = QgsGeometryUtils::midpoint( vertexAt( 1 ), vertexAt( 2 ) );
p3 = QgsGeometryUtils::midpoint( vertexAt( 2 ), vertexAt( 0 ) );
return QgsTriangle( p1, p2, p3 );
}
QgsPointV2 QgsTriangle::orthocenter( double lengthTolerance ) const
{
QVector<QgsLineString> alt = altitudes();
QgsPointV2 ortho;
QgsGeometryUtils::segmentIntersection( alt.at( 0 ).pointN( 0 ), alt.at( 0 ).pointN( 1 ), alt.at( 1 ).pointN( 0 ), alt.at( 1 ).pointN( 1 ), ortho, lengthTolerance );
return ortho;
}
QgsPointV2 QgsTriangle::circumscribedCenter() const
{
double r, x, y;
QgsGeometryUtils::circleCenterRadius( vertexAt( 0 ), vertexAt( 1 ), vertexAt( 2 ), r, x, y );
return QgsPointV2( x, y );
}
double QgsTriangle::circumscribedRadius() const
{
double r, x, y;
QgsGeometryUtils::circleCenterRadius( vertexAt( 0 ), vertexAt( 1 ), vertexAt( 2 ), r, x, y );
return r;
}
/*
QgsCircle QgsTriangle::circumscribedCircle() const
{
}*/
QgsPointV2 QgsTriangle::inscribedCenter() const
{
QVector<double> l = lengths();
double x = ( l.at( 0 ) * vertexAt( 2 ).x() +
l.at( 1 ) * vertexAt( 0 ).x() +
l.at( 2 ) * vertexAt( 1 ).x() ) / perimeter();
double y = ( l.at( 0 ) * vertexAt( 2 ).y() +
l.at( 1 ) * vertexAt( 0 ).y() +
l.at( 2 ) * vertexAt( 1 ).y() ) / perimeter();
return QgsPointV2( x, y );
}
double QgsTriangle::inscribedRadius() const
{
return ( 2.0 * area() / perimeter() );
}
bool QgsTriangle::validateGeom( const QgsPointV2 &p1, const QgsPointV2 &p2, const QgsPointV2 &p3 )
{
if ( ( ( p1 == p2 ) || ( p1 == p3 ) || ( p2 == p3 ) ) || qgsDoubleNear( QgsGeometryUtils::leftOfLine( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() ), 0.0 ) )
{
return false;
}
return true;
}
/*
QgsCircle QgsTriangle::inscribedCircle() const
{
}*/

View File

@ -0,0 +1,322 @@
/***************************************************************************
qgstriangle.h
-------------------
begin : January 2017
copyright : (C) 2017 by Loïc Bartoletti
email : lbartoletti at tuxfamily dot org
***************************************************************************/
/***************************************************************************
* *
* 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 QGSTRIANGLE_H
#define QGSTRIANGLE_H
#include "qgis_core.h"
#include "qgspolygon.h"
#include "qgslinestring.h"
/** \ingroup core
* \class QgsTriangle
* \brief Triangle geometry type.
* \note added in QGIS 3.0
*/
class CORE_EXPORT QgsTriangle : public QgsPolygonV2
{
public:
QgsTriangle();
/** Construct a QgsTriangle from three QgsPointV2.
* An empty triangle is returned if there are identical points or if the points are collinear.
* @param p1 first point
* @param p2 second point
* @param p3 third point
*/
QgsTriangle( const QgsPointV2 &p1, const QgsPointV2 &p2, const QgsPointV2 &p3 );
/** Construct a QgsTriangle from three QgsPoint.
* An empty triangle is returned if there are identical points or if the points are collinear.
* @param p1 first point
* @param p2 second point
* @param p3 third point
*/
explicit QgsTriangle( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3 );
/** Construct a QgsTriangle from three QPointF.
* An empty triangle is returned if there are identical points or if the points are collinear.
* @param p1 first point
* @param p2 second point
* @param p3 third point
*/
explicit QgsTriangle( const QPointF p1, const QPointF p2, const QPointF p3 );
// inherited: bool operator==( const QgsTriangle& other ) const;
// inherited: bool operator!=( const QgsTriangle& other ) const;
virtual QString geometryType() const override { return QStringLiteral( "Triangle" ); }
virtual QgsTriangle *clone() const override;
void clear() override;
virtual bool fromWkb( QgsConstWkbPtr &wkbPtr ) override;
bool fromWkt( const QString &wkt ) override;
// inherited: QString asWkt( int precision = 17 ) const;
// inherited: QDomElement asGML2( QDomDocument& doc, int precision = 17, const QString& ns = "gml" ) const;
// inherited: QDomElement asGML3( QDomDocument& doc, int precision = 17, const QString& ns = "gml" ) const;
// inherited: QString asJSON( int precision = 17 ) const;
QgsPolygonV2 *surfaceToPolygon() const override;
QgsAbstractGeometry *toCurveType() const override;
//! Inherited method not used. You cannot add an interior ring into a triangle.
void addInteriorRing( QgsCurve *ring ) override;
/** Inherited method not used. You cannot add an interior ring into a triangle.
* @note not available in Python bindings
*/
void setInteriorRings( const QList< QgsCurve *> &rings ) = delete;
//! Inherited method not used. You cannot delete or insert a vertex directly. Returns always false.
bool deleteVertex( QgsVertexId position ) override;
//! Inherited method not used. You cannot delete or insert a vertex directly. Returns always false.
bool insertVertex( QgsVertexId position, const QgsPointV2 &vertex ) override;
bool moveVertex( QgsVertexId vId, const QgsPointV2 &newPos ) override;
virtual void setExteriorRing( QgsCurve *ring ) override;
virtual QgsAbstractGeometry *boundary() const override;
// inherited: double pointDistanceToBoundary( double x, double y ) const;
/**
* Returns coordinates of a vertex.
* @param atVertex index of the vertex
* @return Coordinates of the vertex or QgsPointV2(0,0) on error (\a atVertex < 0 or > 3).
*/
QgsPointV2 vertexAt( int atVertex ) const;
/**
* Returns the three lengths of the triangle.
* @return Lengths of triangle ABC where [AB] is at 0, [BC] is at 1, [CA] is at 2
* * Example:
* \code{.py}
* tri = QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 5 ), QgsPointV2( 5, 5 ) )
* tri.lengths()
* # [5.0, 5.0, 7.0710678118654755]
* \endcode
*/
QVector<double> lengths() const;
/**
* Returns the three angles of the triangle.
* @return Angles in radians of triangle ABC where angle BAC is at 0, angle ABC is at 1, angle BCA is at 2
* * Example:
* \code{.py}
* tri = QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 5 ), QgsPointV2( 5, 5 ) )
* [math.degrees(i) for i in tri.angles()]
* # [45.0, 90.0, 45.0]
* \endcode
*/
QVector<double> angles() const;
/**
* Is the triangle isocele (two sides with the same length)?
* @param lengthTolerance The tolerance to use
* @return True or False
* * Example:
* \code{.py}
* tri = QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 5 ), QgsPointV2( 5, 5 ) )
* tri.lengths()
* # [5.0, 5.0, 7.0710678118654755]
* tri.isIsocele()
* # True
* # length of [AB] == length of [BC]
* \endcode
*/
bool isIsocele( double lengthTolerance = 0.0001 ) const;
/**
* Is the triangle equilateral (three sides with the same length)?
* @param lengthTolerance The tolerance to use
* @return True or False
* * Example:
* \code{.py}
* tri = QgsTriangle( QgsPointV2( 10, 10 ), QgsPointV2( 16, 10 ), QgsPointV2( 13, 15.1962 ) )
* tri.lengths()
* # [6.0, 6.0000412031918575, 6.0000412031918575]
* tri.isEquilateral()
* # True
* # All lengths are close to 6.0
* \endcode
*/
bool isEquilateral( double lengthTolerance = 0.0001 ) const;
/**
* Is the triangle right-angled?
* @param angleTolerance The tolerance to use
* @return True or False
* * Example:
* \code{.py}
* tri = QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 5 ), QgsPointV2( 5, 5 ) )
* [math.degrees(i) for i in tri.angles()]
* # [45.0, 90.0, 45.0]
* tri.isRight()
* # True
* # angle of ABC == 90
* \endcode
*/
bool isRight( double angleTolerance = 0.0001 ) const;
/**
* Is the triangle scalene (all sides have differen lengths)?
* @param lengthTolerance The tolerance to use
* @return True or False
* @return True or False
* * Example:
* \code{.py}
* tri = QgsTriangle( QgsPointV2( 7.2825, 4.2368 ), QgsPointV2( 13.0058, 3.3218 ), QgsPointV2( 9.2145, 6.5242 ) )
* tri.lengths()
* # [5.795980321740233, 4.962793714229921, 2.994131386562721]
* tri.isScalene()
* # True
* # All lengths are different
* \endcode
*/
bool isScalene( double lengthTolerance = 0.0001 ) const;
/**
* An altitude is a segment (defined by a QgsLineString) from a vertex to the opposite side (or, if necessary, to the extension of the opposite side).
* @return Three altitudes from this triangle
* * Example:
* \code{.py}
* tri = QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 5 ), QgsPointV2( 5, 5 ) )
* [alt.asWkt() for alt in tri.altitudes()]
* # ['LineString (0 0, 0 5)', 'LineString (0 5, 2.5 2.5)', 'LineString (5 5, 0 5)']
* \endcode
*/
QVector<QgsLineString> altitudes( ) const;
/**
* A median is a segment (defined by a QgsLineString) from a vertex to the midpoint of the opposite side.
* @return Three medians from this triangle
* * Example:
* \code{.py}
* tri = QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 5 ), QgsPointV2( 5, 5 ) )
* [med.asWkt() for med in tri.medians()]
* # ['LineString (0 0, 2.5 5)', 'LineString (0 5, 2.5 2.5)', 'LineString (5 5, 0 2.5)']
* \endcode
*/
QVector<QgsLineString> medians( ) const;
/**
* The segment (defined by a QgsLineString) returned bisect the angle of a vertex to the opposite side.
* @param lengthTolerance The tolerance to use
* @return Three angle bisector from this triangle
* * Example:
* \code{.py}
* tri = QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 5 ), QgsPointV2( 5, 5 ) )
* [bis.asWkt() for bis in tri.bisectors()]
* # ['LineString (0 0, 2.07106781186547462 5)', 'LineString (0 5, 2.5 2.5)', 'LineString (5 5, 0 2.92893218813452538)']
* \endcode
*/
QVector<QgsLineString> bisectors( double lengthTolerance = 0.0001 ) const;
/**
* Medial (or midpoint) triangle of a triangle ABC is the triangle with vertices at the midpoints of the triangle's sides.
* @return The medial from this triangle
* * Example:
* \code{.py}
* tri = QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 5 ), QgsPointV2( 5, 5 ) )
* tri.medial().asWkt()
* # 'Triangle ((0 2.5, 2.5 5, 2.5 2.5, 0 2.5))'
* \endcode
*/
QgsTriangle medial( ) const;
/**
* An orthocenter is the point of intersection of the altitudes of a triangle.
* @param lengthTolerance The tolerance to use
* @return The orthocenter of the triangle.
* * Example:
* \code{.py}
* tri = QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 5 ), QgsPointV2( 5, 5 ) )
* tri.orthocenter().asWkt()
* # 'Point (0 5)'
* \endcode
*/
QgsPointV2 orthocenter( double lengthTolerance = 0.0001 ) const;
/**
* Center of the circumscribed circle of the triangle.
* @return The center of the circumscribed circle of the triangle
* * Example:
* \code{.py}
* tri = QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 5 ), QgsPointV2( 5, 5 ) )
* tri.circumscribedCenter().asWkt()
* # 'Point (2.5 2.5)'
* \endcode
*/
QgsPointV2 circumscribedCenter( ) const;
/**
* Radius of the circumscribed circle of the triangle.
* @return The radius of the circumscribed circle of the triangle
* * Example:
* \code{.py}
* tri = QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 5 ), QgsPointV2( 5, 5 ) )
* tri.circumscribedRadius()
* # 3.5355339059327378
* \endcode
*/
double circumscribedRadius( ) const;
// TODO:
// QgsCircle circumscribedCircle ( ) const; // need QgsCircle (from CADDigitize.CADCircle)
/**
* Center of the inscribed circle of the triangle.
* @return The center of the inscribed circle of the triangle
* * Example:
* \code{.py}
* tri = QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 5 ), QgsPointV2( 5, 5 ) )
* tri.inscribedCenter().asWkt()
* # 'Point (1.46446609406726225 3.53553390593273775)'
* \endcode
*/
QgsPointV2 inscribedCenter( ) const;
/**
* Radius of the inscribed circle of the triangle.
* @return The radius of the inscribed circle of the triangle
* * Example:
* \code{.py}
* tri = QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 5 ), QgsPointV2( 5, 5 ) )
* tri.inscribedRadius()
* # 1.4644660940672622
* \endcode
*/
double inscribedRadius( ) const;
// TODO:
// QgsCircle inscribedCircle ( ) const; // need QgsCircle (from CADDigitize.CADCircle)
private:
/**
* @brief Convenient method checking the validity of geometry (no duplicate point(s), no colinearity).
* @param p1 first point
* @param p2 second point
* @param p3 third point
* @return True if the points can create a triangle, otherwise false.
* @note not available in Python bindings
*/
bool validateGeom( const QgsPointV2 &p1, const QgsPointV2 &p2, const QgsPointV2 &p3 );
};
#endif // QGSTRIANGLE_H

View File

@ -56,6 +56,11 @@ const QMap<QgsWkbTypes::Type, QgsWkbTypes::wkbEntry> QgsWkbTypes::ENTRIES
{ PolygonM, wkbEntry( QStringLiteral( "PolygonM" ), false, MultiPolygonM, PolygonM, Polygon, PolygonGeometry, false, true ) },
{ PolygonZM, wkbEntry( QStringLiteral( "PolygonZM" ), false, MultiPolygonZM, PolygonZM, Polygon, PolygonGeometry, true, true ) },
{ Polygon25D, wkbEntry( QStringLiteral( "Polygon25D" ), false, MultiPolygon25D, Polygon25D, Polygon, PolygonGeometry, true, false ) },
//triangle
{ Triangle, wkbEntry( QStringLiteral( "Triangle" ), false, Unknown, Triangle, Triangle, PolygonGeometry, false, false ) },
{ TriangleZ, wkbEntry( QStringLiteral( "TriangleZ" ), false, Unknown, TriangleZ, Triangle, PolygonGeometry, true, false ) },
{ TriangleM, wkbEntry( QStringLiteral( "TriangleM" ), false, Unknown, TriangleM, Triangle, PolygonGeometry, false, true ) },
{ TriangleZM, wkbEntry( QStringLiteral( "TriangleZM" ), false, Unknown, TriangleZM, Triangle, PolygonGeometry, true, true ) },
//curvepolygon
{ CurvePolygon, wkbEntry( QStringLiteral( "CurvePolygon" ), false, MultiSurface, CurvePolygon, CurvePolygon, PolygonGeometry, false, false ) },
{ CurvePolygonZ, wkbEntry( QStringLiteral( "CurvePolygonZ" ), false, MultiSurfaceZ, CurvePolygonZ, CurvePolygon, PolygonGeometry, true, false ) },

View File

@ -68,6 +68,7 @@ class CORE_EXPORT QgsWkbTypes
Point = 1,
LineString = 2,
Polygon = 3,
Triangle = 17,
MultiPoint = 4,
MultiLineString = 5,
MultiPolygon = 6,
@ -81,6 +82,7 @@ class CORE_EXPORT QgsWkbTypes
PointZ = 1001,
LineStringZ = 1002,
PolygonZ = 1003,
TriangleZ = 1017,
MultiPointZ = 1004,
MultiLineStringZ = 1005,
MultiPolygonZ = 1006,
@ -93,6 +95,7 @@ class CORE_EXPORT QgsWkbTypes
PointM = 2001,
LineStringM = 2002,
PolygonM = 2003,
TriangleM = 2017,
MultiPointM = 2004,
MultiLineStringM = 2005,
MultiPolygonM = 2006,
@ -114,6 +117,7 @@ class CORE_EXPORT QgsWkbTypes
CurvePolygonZM = 3010,
MultiCurveZM = 3011,
MultiSurfaceZM = 3012,
TriangleZM = 3017,
Point25D = 0x80000001,
LineString25D,
Polygon25D,
@ -201,6 +205,22 @@ class CORE_EXPORT QgsWkbTypes
case MultiPolygonZM:
return PolygonZM;
case Triangle:
// case MultiTriangle:
return Triangle;
case TriangleZ:
// case MultiTriangleZ:
return TriangleZ;
case TriangleM:
// case MultiTriangleM:
return TriangleM;
case TriangleZM:
// case MultiTriangleZM:
return TriangleZM;
case CircularString:
return CircularString;
@ -259,6 +279,7 @@ class CORE_EXPORT QgsWkbTypes
case Polygon25D:
case MultiPolygon25D:
return Polygon25D;
}
return Unknown;
}
@ -273,6 +294,10 @@ class CORE_EXPORT QgsWkbTypes
switch ( type )
{
case Unknown:
case Triangle:
case TriangleZ:
case TriangleM:
case TriangleZM:
return Unknown;
case GeometryCollection:
@ -422,6 +447,12 @@ class CORE_EXPORT QgsWkbTypes
case Polygon25D:
return Polygon;
case Triangle:
case TriangleZ:
case TriangleM:
case TriangleZM:
return Triangle;
case MultiPoint:
case MultiPointZ:
case MultiPointM:
@ -523,6 +554,7 @@ class CORE_EXPORT QgsWkbTypes
case Point:
case LineString:
case Polygon:
case Triangle:
case CircularString:
case CompoundCurve:
case CurvePolygon:
@ -530,18 +562,21 @@ class CORE_EXPORT QgsWkbTypes
case PointZ:
case LineStringZ:
case PolygonZ:
case TriangleZ:
case CircularStringZ:
case CompoundCurveZ:
case CurvePolygonZ:
case PointM:
case LineStringM:
case PolygonM:
case TriangleM:
case CircularStringM:
case CompoundCurveM:
case CurvePolygonM:
case PointZM:
case LineStringZM:
case PolygonZM:
case TriangleZM:
case CircularStringZM:
case CompoundCurveZM:
case CurvePolygonZM:
@ -662,12 +697,16 @@ class CORE_EXPORT QgsWkbTypes
case Polygon:
case MultiPolygon:
case Triangle:
case PolygonZ:
case TriangleZ:
case MultiPolygonZ:
case PolygonM:
case TriangleM:
case MultiPolygonM:
case PolygonZM:
case MultiPolygonZM:
case TriangleZM:
case Polygon25D:
case MultiPolygon25D:
case CurvePolygon:
@ -719,6 +758,7 @@ class CORE_EXPORT QgsWkbTypes
case PointZ:
case LineStringZ:
case PolygonZ:
case TriangleZ:
case MultiPointZ:
case MultiLineStringZ:
case MultiPolygonZ:
@ -731,6 +771,7 @@ class CORE_EXPORT QgsWkbTypes
case PointZM:
case LineStringZM:
case PolygonZM:
case TriangleZM:
case MultiPointZM:
case MultiLineStringZM:
case MultiPolygonZM:
@ -766,6 +807,7 @@ class CORE_EXPORT QgsWkbTypes
case PointM:
case LineStringM:
case PolygonM:
case TriangleM:
case MultiPointM:
case MultiLineStringM:
case MultiPolygonM:
@ -778,6 +820,7 @@ class CORE_EXPORT QgsWkbTypes
case PointZM:
case LineStringZM:
case PolygonZM:
case TriangleZM:
case MultiPointZM:
case MultiLineStringZM:
case MultiPolygonZM:

View File

@ -45,6 +45,7 @@
#include "qgsgeometrycollection.h"
#include "qgspointv2.h"
#include "qgspolygon.h"
#include "qgstriangle.h"
#include "qgsmultipoint.h"
#include "qgsmultilinestring.h"
#include "qgscurvepolygon.h"
@ -2190,6 +2191,33 @@ static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionC
return QVariant::fromValue( QgsGeometry( polygon ) );
}
static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent )
{
std::unique_ptr<QgsTriangle> tr( new QgsTriangle() );
std::unique_ptr<QgsLineString> lineString( new QgsLineString() );
lineString->clear();
Q_FOREACH ( const QVariant &value, values )
{
QgsGeometry geom = getGeometry( value, parent );
if ( geom.isNull() )
return QVariant();
if ( geom.type() != QgsWkbTypes::PointGeometry || geom.isMultipart() )
return QVariant();
QgsPointV2 *point = dynamic_cast< QgsPointV2 * >( geom.geometry() );
if ( !point )
return QVariant();
lineString->addVertex( *point );
}
tr->setExteriorRing( lineString.release() );
return QVariant::fromValue( QgsGeometry( tr.release() ) );
}
static QVariant pointAt( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) // helper function
{
FEAT_FROM_CONTEXT( context, f );
@ -3999,6 +4027,10 @@ const QList<QgsExpression::Function *> &QgsExpression::Functions()
<< new StaticFunction( QStringLiteral( "make_point_m" ), 3, fcnMakePointM, QStringLiteral( "GeometryGroup" ) )
<< new StaticFunction( QStringLiteral( "make_line" ), -1, fcnMakeLine, QStringLiteral( "GeometryGroup" ) )
<< new StaticFunction( QStringLiteral( "make_polygon" ), -1, fcnMakePolygon, QStringLiteral( "GeometryGroup" ) )
<< new StaticFunction( QStringLiteral( "make_triangle" ), ParameterList() << Parameter( QStringLiteral( "geometry" ) )
<< Parameter( QStringLiteral( "geometry" ) )
<< Parameter( QStringLiteral( "geometry" ) ),
fcnMakeTriangle, QStringLiteral( "GeometryGroup" ) )
<< new StaticFunction( QStringLiteral( "$x_at" ), 1, fcnXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "xat" ) << QStringLiteral( "x_at" ) )
<< new StaticFunction( QStringLiteral( "$y_at" ), 1, fcnYat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "yat" ) << QStringLiteral( "y_at" ) )
<< new StaticFunction( QStringLiteral( "x_min" ), 1, fcnXMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmin" ) )

View File

@ -771,6 +771,11 @@ class TestQgsExpression: public QObject
QTest::newRow( "make_polygon" ) << "geom_to_wkt(make_polygon(geom_from_wkt('LINESTRING( 0 0, 0 1, 1 1, 1 0, 0 0 )')))" << false << QVariant( "Polygon ((0 0, 0 1, 1 1, 1 0, 0 0))" );
QTest::newRow( "make_polygon rings" ) << "geom_to_wkt(make_polygon(geom_from_wkt('LINESTRING( 0 0, 0 1, 1 1, 1 0, 0 0 )'),geom_from_wkt('LINESTRING( 0.1 0.1, 0.1 0.2, 0.2 0.2, 0.2 0.1, 0.1 0.1 )'),geom_from_wkt('LINESTRING( 0.8 0.8, 0.8 0.9, 0.9 0.9, 0.9 0.8, 0.8 0.8 )')))" << false
<< QVariant( "Polygon ((0 0, 0 1, 1 1, 1 0, 0 0),(0.1 0.1, 0.1 0.2, 0.2 0.2, 0.2 0.1, 0.1 0.1),(0.8 0.8, 0.8 0.9, 0.9 0.9, 0.9 0.8, 0.8 0.8))" );
QTest::newRow( "make_triangle not geom" ) << "geom_to_wkt(make_triangle(make_point(2,4), 'g', make_point(3,5)))" << true << QVariant();
QTest::newRow( "make_triangle null" ) << "geom_to_wkt(make_triangle(make_point(2,4), NULL, make_point(3,5)))" << false << QVariant();
QTest::newRow( "make_triangle duplicated point" ) << "geom_to_wkt(make_triangle(make_point(2,4), make_point(2,4), make_point(3,5)))" << false << QVariant( "Triangle ()" );
QTest::newRow( "make_triangle colinear points" ) << "geom_to_wkt(make_triangle(make_point(0,1), make_point(0,2), make_point(0,3)))" << false << QVariant( "Triangle ()" );
QTest::newRow( "make_triangle" ) << "geom_to_wkt(make_triangle(make_point(0,0), make_point(5,5), make_point(0,10)))" << false << QVariant( "Triangle ((0 0, 5 5, 0 10, 0 0))" );
QTest::newRow( "x point" ) << "x(make_point(2.2,4.4))" << false << QVariant( 2.2 );
QTest::newRow( "y point" ) << "y(make_point(2.2,4.4))" << false << QVariant( 4.4 );
QTest::newRow( "z point" ) << "z(make_point(2.2,4.4,6.6))" << false << QVariant( 6.6 );

View File

@ -34,6 +34,7 @@
#include "qgspointv2.h"
#include "qgslinestring.h"
#include "qgspolygon.h"
#include "qgstriangle.h"
#include "qgsmultipoint.h"
#include "qgsmultilinestring.h"
#include "qgsmultipolygon.h"
@ -70,6 +71,7 @@ class TestQgsGeometry : public QObject
void point(); //test QgsPointV2
void lineString(); //test QgsLineString
void polygon(); //test QgsPolygonV2
void triangle();
void compoundCurve(); //test QgsCompoundCurve
void multiPoint();
void multiLineString();
@ -3181,6 +3183,434 @@ void TestQgsGeometry::polygon()
}
void TestQgsGeometry::triangle()
{
//test constructor
QgsTriangle t1;
QVERIFY( t1.isEmpty() );
QCOMPARE( t1.numInteriorRings(), 0 );
QCOMPARE( t1.nCoordinates(), 0 );
QCOMPARE( t1.ringCount(), 0 );
QCOMPARE( t1.partCount(), 0 );
QVERIFY( !t1.is3D() );
QVERIFY( !t1.isMeasure() );
QCOMPARE( t1.wkbType(), QgsWkbTypes::Triangle );
QCOMPARE( t1.wktTypeStr(), QString( "Triangle" ) );
QCOMPARE( t1.geometryType(), QString( "Triangle" ) );
QCOMPARE( t1.dimension(), 2 );
QVERIFY( !t1.hasCurvedSegments() );
QCOMPARE( t1.area(), 0.0 );
QCOMPARE( t1.perimeter(), 0.0 );
QVERIFY( !t1.exteriorRing() );
QVERIFY( !t1.interiorRing( 0 ) );
//set exterior ring
//try with no ring
QgsLineString *ext = 0;
t1.setExteriorRing( ext );
QVERIFY( t1.isEmpty() );
QCOMPARE( t1.numInteriorRings(), 0 );
QCOMPARE( t1.nCoordinates(), 0 );
QCOMPARE( t1.ringCount(), 0 );
QCOMPARE( t1.partCount(), 0 );
QVERIFY( !t1.exteriorRing() );
QVERIFY( !t1.interiorRing( 0 ) );
QCOMPARE( t1.wkbType(), QgsWkbTypes::Triangle );
//valid exterior ring
ext = new QgsLineString();
ext->setPoints( QgsPointSequence() << QgsPointV2( 0, 0 ) << QgsPointV2( 0, 10 ) << QgsPointV2( 10, 10 )
<< QgsPointV2( 0, 0 ) );
QVERIFY( ext->isClosed() );
t1.setExteriorRing( ext );
QVERIFY( !t1.isEmpty() );
QCOMPARE( t1.numInteriorRings(), 0 );
QCOMPARE( t1.nCoordinates(), 4 );
QCOMPARE( t1.ringCount(), 1 );
QCOMPARE( t1.partCount(), 1 );
QVERIFY( !t1.is3D() );
QVERIFY( !t1.isMeasure() );
QCOMPARE( t1.wkbType(), QgsWkbTypes::Triangle );
QCOMPARE( t1.wktTypeStr(), QString( "Triangle" ) );
QCOMPARE( t1.geometryType(), QString( "Triangle" ) );
QCOMPARE( t1.dimension(), 2 );
QVERIFY( !t1.hasCurvedSegments() );
QCOMPARE( t1.area(), 50.0 );
QGSCOMPARENEAR( t1.perimeter(), 34.1421, 0.001 );
QVERIFY( t1.exteriorRing() );
QVERIFY( !t1.interiorRing( 0 ) );
//retrieve exterior ring and check
QCOMPARE( *( static_cast< const QgsLineString * >( t1.exteriorRing() ) ), *ext );
//set new ExteriorRing
ext = new QgsLineString();
ext->setPoints( QgsPointSequence() << QgsPointV2( 0, 10 ) << QgsPointV2( 5, 5 ) << QgsPointV2( 10, 10 )
<< QgsPointV2( 0, 10 ) );
QVERIFY( ext->isClosed() );
t1.setExteriorRing( ext );
QVERIFY( !t1.isEmpty() );
QCOMPARE( t1.numInteriorRings(), 0 );
QCOMPARE( t1.nCoordinates(), 4 );
QCOMPARE( t1.ringCount(), 1 );
QCOMPARE( t1.partCount(), 1 );
QVERIFY( !t1.is3D() );
QVERIFY( !t1.isMeasure() );
QCOMPARE( t1.wkbType(), QgsWkbTypes::Triangle );
QCOMPARE( t1.wktTypeStr(), QString( "Triangle" ) );
QCOMPARE( t1.geometryType(), QString( "Triangle" ) );
QCOMPARE( t1.dimension(), 2 );
QVERIFY( !t1.hasCurvedSegments() );
QCOMPARE( t1.area(), 25.0 );
QGSCOMPARENEAR( t1.perimeter(), 24.1421, 0.001 );
QVERIFY( t1.exteriorRing() );
QVERIFY( !t1.interiorRing( 0 ) );
QCOMPARE( *( static_cast< const QgsLineString * >( t1.exteriorRing() ) ), *ext );
//test that a non closed exterior ring will be automatically closed
QgsTriangle t2;
ext = new QgsLineString();
ext->setPoints( QgsPointSequence() << QgsPointV2( 0, 0 ) << QgsPointV2( 0, 10 ) << QgsPointV2( 10, 10 ) );
QVERIFY( !ext->isClosed() );
t2.setExteriorRing( ext );
QVERIFY( !t2.isEmpty() );
QVERIFY( t2.exteriorRing()->isClosed() );
QCOMPARE( t2.nCoordinates(), 4 );
// invalid number of points
ext = new QgsLineString();
t2.clear();
ext->setPoints( QgsPointSequence() << QgsPointV2( 0, 0 ) << QgsPointV2( 0, 10 ) );
t2.setExteriorRing( ext );
QVERIFY( t2.isEmpty() );
ext = new QgsLineString();
t2.clear();
ext->setPoints( QgsPointSequence() << QgsPointV2( 0, 0 ) << QgsPointV2( 0, 10 ) << QgsPointV2( 10, 10 ) << QgsPointV2( 5, 10 ) << QgsPointV2( 8, 10 ) );
t2.setExteriorRing( ext );
QVERIFY( t2.isEmpty() );
// invalid exterior ring
ext = new QgsLineString();
t2.clear();
ext->setPoints( QgsPointSequence() << QgsPointV2( 0, 0 ) << QgsPointV2( 0, 10 ) << QgsPointV2( 10, 10 ) << QgsPointV2( 5, 10 ) );
t2.setExteriorRing( ext );
QVERIFY( t2.isEmpty() );
// circular ring
QgsCircularString *circularRing = new QgsCircularString();
t2.clear();
circularRing->setPoints( QgsPointSequence() << QgsPointV2( 0, 0 ) << QgsPointV2( 0, 10 ) << QgsPointV2( 10, 10 ) );
QVERIFY( circularRing->hasCurvedSegments() );
t2.setExteriorRing( circularRing );
QVERIFY( t2.isEmpty() );
//constructor with 3 points
// double points
QgsTriangle t3( QgsPointV2( 0, 0 ), QgsPointV2( 0, 0 ), QgsPointV2( 10, 10 ) );
QVERIFY( t3.isEmpty() );
QCOMPARE( t3.numInteriorRings(), 0 );
QCOMPARE( t3.nCoordinates(), 0 );
QCOMPARE( t3.ringCount(), 0 );
QCOMPARE( t3.partCount(), 0 );
QVERIFY( !t3.is3D() );
QVERIFY( !t3.isMeasure() );
QCOMPARE( t3.wkbType(), QgsWkbTypes::Triangle );
QCOMPARE( t3.wktTypeStr(), QString( "Triangle" ) );
QCOMPARE( t3.geometryType(), QString( "Triangle" ) );
QCOMPARE( t3.dimension(), 2 );
QVERIFY( !t3.hasCurvedSegments() );
QCOMPARE( t3.area(), 0.0 );
QCOMPARE( t3.perimeter(), 0.0 );
QVERIFY( !t3.exteriorRing() );
QVERIFY( !t3.interiorRing( 0 ) );
// colinear
t3 = QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 5 ), QgsPointV2( 0, 10 ) );
QVERIFY( t3.isEmpty() );
QCOMPARE( t3.numInteriorRings(), 0 );
QCOMPARE( t3.nCoordinates(), 0 );
QCOMPARE( t3.ringCount(), 0 );
QCOMPARE( t3.partCount(), 0 );
QVERIFY( !t3.is3D() );
QVERIFY( !t3.isMeasure() );
QCOMPARE( t3.wkbType(), QgsWkbTypes::Triangle );
QCOMPARE( t3.wktTypeStr(), QString( "Triangle" ) );
QCOMPARE( t3.geometryType(), QString( "Triangle" ) );
QCOMPARE( t3.dimension(), 2 );
QVERIFY( !t3.hasCurvedSegments() );
QCOMPARE( t3.area(), 0.0 );
QCOMPARE( t3.perimeter(), 0.0 );
QVERIFY( !t3.exteriorRing() );
QVERIFY( !t3.interiorRing( 0 ) );
t3 = QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 10 ), QgsPointV2( 10, 10 ) );
QVERIFY( !t3.isEmpty() );
QCOMPARE( t3.numInteriorRings(), 0 );
QCOMPARE( t3.nCoordinates(), 4 );
QCOMPARE( t3.ringCount(), 1 );
QCOMPARE( t3.partCount(), 1 );
QVERIFY( !t3.is3D() );
QVERIFY( !t3.isMeasure() );
QCOMPARE( t3.wkbType(), QgsWkbTypes::Triangle );
QCOMPARE( t3.wktTypeStr(), QString( "Triangle" ) );
QCOMPARE( t3.geometryType(), QString( "Triangle" ) );
QCOMPARE( t3.dimension(), 2 );
QVERIFY( !t3.hasCurvedSegments() );
QCOMPARE( t3.area(), 50.0 );
QGSCOMPARENEAR( t3.perimeter(), 34.1421, 0.001 );
QVERIFY( t3.exteriorRing() );
QVERIFY( !t3.interiorRing( 0 ) );
// clone
QgsTriangle *t4 = t3.clone();
QCOMPARE( t3, *t4 );
delete t4;
// constructor from QgsPoint and QPointF
QgsTriangle t_qgspoint = QgsTriangle( QgsPoint( 0, 0 ), QgsPoint( 0, 10 ), QgsPoint( 10, 10 ) );
QVERIFY( t3 == t_qgspoint );
QgsTriangle t_pointf = QgsTriangle( QPointF( 0, 0 ), QPointF( 0, 10 ), QPointF( 10, 10 ) );
QVERIFY( t3 == t_pointf );
// fromWkt
QgsTriangle t5;
ext = new QgsLineString();
ext->setPoints( QgsPointSequence() << QgsPointV2( QgsWkbTypes::PointZM, 0, 0, 1, 5 )
<< QgsPointV2( QgsWkbTypes::PointZM, 0, 10, 2, 6 ) << QgsPointV2( QgsWkbTypes::PointZM, 10, 10, 3, 7 ) );
t5.setExteriorRing( ext );
QString wkt = t5.asWkt();
QVERIFY( !wkt.isEmpty() );
QgsTriangle t6;
QVERIFY( t6.fromWkt( wkt ) );
QCOMPARE( t5, t6 );
// conversion
QgsPolygonV2 p1;
ext = new QgsLineString();
ext->setPoints( QgsPointSequence() << QgsPointV2( QgsWkbTypes::PointZM, 0, 0, 1, 5 )
<< QgsPointV2( QgsWkbTypes::PointZM, 0, 10, 2, 6 ) << QgsPointV2( QgsWkbTypes::PointZM, 10, 10, 3, 7 ) );
p1.setExteriorRing( ext );
//toPolygon
std::unique_ptr< QgsPolygonV2 > poly( t5.toPolygon() );
QCOMPARE( *poly, p1 );
//surfaceToPolygon
std::unique_ptr< QgsPolygonV2 > surface( t5.surfaceToPolygon() );
QCOMPARE( *surface, p1 );
//bad WKT
QVERIFY( !t6.fromWkt( "Point()" ) );
QVERIFY( t6.isEmpty() );
QVERIFY( !t6.exteriorRing() );
QCOMPARE( t6.numInteriorRings(), 0 );
QVERIFY( !t6.is3D() );
QVERIFY( !t6.isMeasure() );
QCOMPARE( t6.wkbType(), QgsWkbTypes::Triangle );
// WKB
QByteArray wkb = t5.asWkb();
t6.clear();
QgsConstWkbPtr wkb16ptr5( wkb );
t6.fromWkb( wkb16ptr5 );
QCOMPARE( t5.wkbType(), QgsWkbTypes::TriangleZM );
QCOMPARE( t5, t6 );
//bad WKB - check for no crash
t6.clear();
QgsConstWkbPtr nullPtr( nullptr, 0 );
QVERIFY( !t6.fromWkb( nullPtr ) );
QCOMPARE( t6.wkbType(), QgsWkbTypes::Triangle );
QgsPointV2 point( 1, 2 );
QByteArray wkbPoint = point.asWkb();
QgsConstWkbPtr wkbPointPtr( wkbPoint );
QVERIFY( !t6.fromWkb( wkbPointPtr ) );
QCOMPARE( t6.wkbType(), QgsWkbTypes::Triangle );
// lengths and angles
QgsTriangle t7( QgsPointV2( 0, 0 ), QgsPointV2( 0, 5 ), QgsPointV2( 5, 5 ) );
QVector<double> a_tested, a_t7 = t7.angles();
a_tested.append( M_PI / 4.0 );
a_tested.append( M_PI / 2.0 );
a_tested.append( M_PI / 4.0 );
QGSCOMPARENEAR( a_tested.at( 0 ), a_t7.at( 0 ), 0.0001 );
QGSCOMPARENEAR( a_tested.at( 1 ), a_t7.at( 1 ), 0.0001 );
QGSCOMPARENEAR( a_tested.at( 2 ), a_t7.at( 2 ), 0.0001 );
QVector<double> l_tested, l_t7 = t7.lengths();
l_tested.append( 5 );
l_tested.append( 5 );
l_tested.append( sqrt( 5 * 5 + 5 * 5 ) );
QGSCOMPARENEAR( l_tested.at( 0 ), l_t7.at( 0 ), 0.0001 );
QGSCOMPARENEAR( l_tested.at( 1 ), l_t7.at( 1 ), 0.0001 );
QGSCOMPARENEAR( l_tested.at( 2 ), l_t7.at( 2 ), 0.0001 );
// type of triangle
QVERIFY( t7.isRight() );
QVERIFY( t7.isIsocele() );
QVERIFY( !t7.isScalene() );
QVERIFY( !t7.isEquilateral() );
QgsTriangle t8( QgsPointV2( 7.2825, 4.2368 ), QgsPointV2( 13.0058, 3.3218 ), QgsPointV2( 9.2145, 6.5242 ) );
// angles in radians 58.8978;31.1036;89.9985
// length 5.79598;4.96279;2.99413
QVERIFY( t8.isRight() );
QVERIFY( !t8.isIsocele() );
QVERIFY( t8.isScalene() );
QVERIFY( !t8.isEquilateral() );
QgsTriangle t9( QgsPointV2( 10, 10 ), QgsPointV2( 16, 10 ), QgsPointV2( 13, 15.1962 ) );
QVERIFY( !t9.isRight() );
QVERIFY( t9.isIsocele() );
QVERIFY( !t9.isScalene() );
QVERIFY( t9.isEquilateral() );
// vertex
QCOMPARE( t9.vertexAt( -1 ), QgsPointV2( 0, 0 ) );
QCOMPARE( t9.vertexAt( 0 ), QgsPointV2( 10, 10 ) );
QCOMPARE( t9.vertexAt( 1 ), QgsPointV2( 16, 10 ) );
QCOMPARE( t9.vertexAt( 2 ), QgsPointV2( 13, 15.1962 ) );
QCOMPARE( t9.vertexAt( 3 ), QgsPointV2( 10, 10 ) );
QCOMPARE( t9.vertexAt( 4 ), QgsPointV2( 0, 0 ) );
// altitudes
QgsTriangle t10( QgsPointV2( 20, 2 ), QgsPointV2( 16, 6 ), QgsPointV2( 26, 2 ) );
QVector<QgsLineString> alt = t10.altitudes();
QGSCOMPARENEARPOINT( alt.at( 0 ).pointN( 1 ), QgsPointV2( 20.8276, 4.0690 ), 0.0001 );
QGSCOMPARENEARPOINT( alt.at( 1 ).pointN( 1 ), QgsPointV2( 16, 2 ), 0.0001 );
QGSCOMPARENEARPOINT( alt.at( 2 ).pointN( 1 ), QgsPointV2( 23, -1 ), 0.0001 );
// orthocenter
QCOMPARE( QgsPointV2( 16, -8 ), t10.orthocenter() );
QCOMPARE( QgsPointV2( 0, 5 ), t7.orthocenter() );
QGSCOMPARENEARPOINT( QgsPointV2( 13, 11.7321 ), t9.orthocenter(), 0.0001 );
// circumscribed circle
QCOMPARE( QgsPointV2( 2.5, 2.5 ), t7.circumscribedCenter() );
QGSCOMPARENEAR( 3.5355, t7.circumscribedRadius(), 0.0001 );
QCOMPARE( QgsPointV2( 23, 9 ), t10.circumscribedCenter() );
QGSCOMPARENEAR( 7.6158, t10.circumscribedRadius(), 0.0001 );
QGSCOMPARENEARPOINT( QgsPointV2( 13, 11.7321 ), t9.circumscribedCenter(), 0.0001 );
QGSCOMPARENEAR( 3.4641, t9.circumscribedRadius(), 0.0001 );
// inscribed circle
QGSCOMPARENEARPOINT( QgsPointV2( 1.4645, 3.5355 ), t7.inscribedCenter(), 0.001 );
QGSCOMPARENEAR( 1.4645, t7.inscribedRadius(), 0.0001 );
QGSCOMPARENEARPOINT( QgsPointV2( 20.4433, 3.0701 ), t10.inscribedCenter(), 0.001 );
QGSCOMPARENEAR( 1.0701, t10.inscribedRadius(), 0.0001 );
QGSCOMPARENEARPOINT( QgsPointV2( 13, 11.7321 ), t9.inscribedCenter(), 0.0001 );
QGSCOMPARENEAR( 1.7321, t9.inscribedRadius(), 0.0001 );
// medians
QVector<QgsLineString> med = t7.medians();
QCOMPARE( med.at( 0 ).pointN( 0 ), t7.vertexAt( 0 ) );
QGSCOMPARENEARPOINT( med.at( 0 ).pointN( 1 ), QgsPointV2( 2.5, 5 ), 0.0001 );
QCOMPARE( med.at( 1 ).pointN( 0 ), t7.vertexAt( 1 ) );
QGSCOMPARENEARPOINT( med.at( 1 ).pointN( 1 ), QgsPointV2( 2.5, 2.5 ), 0.0001 );
QCOMPARE( med.at( 2 ).pointN( 0 ), t7.vertexAt( 2 ) );
QGSCOMPARENEARPOINT( med.at( 2 ).pointN( 1 ), QgsPointV2( 0, 2.5 ), 0.0001 );
med.clear();
med = t10.medians();
QCOMPARE( med.at( 0 ).pointN( 0 ), t10.vertexAt( 0 ) );
QGSCOMPARENEARPOINT( med.at( 0 ).pointN( 1 ), QgsPointV2( 21, 4 ), 0.0001 );
QCOMPARE( med.at( 1 ).pointN( 0 ), t10.vertexAt( 1 ) );
QGSCOMPARENEARPOINT( med.at( 1 ).pointN( 1 ), QgsPointV2( 23, 2 ), 0.0001 );
QCOMPARE( med.at( 2 ).pointN( 0 ), t10.vertexAt( 2 ) );
QGSCOMPARENEARPOINT( med.at( 2 ).pointN( 1 ), QgsPointV2( 18, 4 ), 0.0001 );
med.clear();
alt.clear();
med = t9.medians();
alt = t9.altitudes();
QGSCOMPARENEARPOINT( med.at( 0 ).pointN( 0 ), alt.at( 0 ).pointN( 0 ), 0.0001 );
QGSCOMPARENEARPOINT( med.at( 0 ).pointN( 1 ), alt.at( 0 ).pointN( 1 ), 0.0001 );
QGSCOMPARENEARPOINT( med.at( 1 ).pointN( 0 ), alt.at( 1 ).pointN( 0 ), 0.0001 );
QGSCOMPARENEARPOINT( med.at( 1 ).pointN( 1 ), alt.at( 1 ).pointN( 1 ), 0.0001 );
QGSCOMPARENEARPOINT( med.at( 2 ).pointN( 0 ), alt.at( 2 ).pointN( 0 ), 0.0001 );
QGSCOMPARENEARPOINT( med.at( 2 ).pointN( 1 ), alt.at( 2 ).pointN( 1 ), 0.0001 );
// medial
QCOMPARE( t7.medial(), QgsTriangle( QgsPointV2( 0, 2.5 ), QgsPointV2( 2.5, 5 ), QgsPointV2( 2.5, 2.5 ) ) );
QCOMPARE( t9.medial(), QgsTriangle( QgsGeometryUtils::midpoint( t9.vertexAt( 0 ), t9.vertexAt( 1 ) ),
QgsGeometryUtils::midpoint( t9.vertexAt( 1 ), t9.vertexAt( 2 ) ),
QgsGeometryUtils::midpoint( t9.vertexAt( 2 ), t9.vertexAt( 0 ) ) ) );
// bisectors
QVector<QgsLineString> bis = t7.bisectors();
QCOMPARE( bis.at( 0 ).pointN( 0 ), t7.vertexAt( 0 ) );
QGSCOMPARENEARPOINT( bis.at( 0 ).pointN( 1 ), QgsPointV2( 2.0711, 5 ), 0.0001 );
QCOMPARE( bis.at( 1 ).pointN( 0 ), t7.vertexAt( 1 ) );
QGSCOMPARENEARPOINT( bis.at( 1 ).pointN( 1 ), QgsPointV2( 2.5, 2.5 ), 0.0001 );
QCOMPARE( bis.at( 2 ).pointN( 0 ), t7.vertexAt( 2 ) );
QGSCOMPARENEARPOINT( bis.at( 2 ).pointN( 1 ), QgsPointV2( 0, 2.9289 ), 0.0001 );
// "deleted" method
QgsTriangle t11( QgsPointV2( 0, 0 ), QgsPointV2( 100, 100 ), QgsPointV2( 0, 200 ) );
ext->setPoints( QgsPointSequence() << QgsPointV2( 5, 5 )
<< QgsPointV2( 50, 50 ) << QgsPointV2( 0, 25 )
<< QgsPointV2( 5, 5 ) );
t11.addInteriorRing( ext );
QCOMPARE( t11.asWkt(), QString( "Triangle ((0 0, 100 100, 0 200, 0 0))" ) );
/* QList<QgsCurve *> lc;
lc.append(ext);
t11.setInteriorRings( lc );
QCOMPARE( t11.asWkt(), QString( "Triangle ((0 0, 100 100, 0 200, 0 0))" ) );*/
QgsVertexId id( 0, 0, 1 );
QVERIFY( !t11.deleteVertex( id ) );
QCOMPARE( t11.asWkt(), QString( "Triangle ((0 0, 100 100, 0 200, 0 0))" ) );
QVERIFY( !t11.insertVertex( id, QgsPointV2( 5, 5 ) ) );
QCOMPARE( t11.asWkt(), QString( "Triangle ((0 0, 100 100, 0 200, 0 0))" ) );
//move vertex
QgsPointV2 pt1( 5, 5 );
// invalid part
id.part = -1;
QVERIFY( !t11.moveVertex( id, pt1 ) );
id.part = 1;
QVERIFY( !t11.moveVertex( id, pt1 ) );
// invalid ring
id.part = 0;
id.ring = -1;
QVERIFY( !t11.moveVertex( id, pt1 ) );
id.ring = 1;
QVERIFY( !t11.moveVertex( id, pt1 ) );
id.ring = 0;
id.vertex = -1;
QVERIFY( !t11.moveVertex( id, pt1 ) );
id.vertex = 5;
QVERIFY( !t11.moveVertex( id, pt1 ) );
// valid vertex
id.vertex = 0;
QVERIFY( t11.moveVertex( id, pt1 ) );
QCOMPARE( t11.asWkt(), QString( "Triangle ((5 5, 100 100, 0 200, 5 5))" ) );
pt1 = QgsPointV2();
QVERIFY( t11.moveVertex( id, pt1 ) );
QCOMPARE( t11.asWkt(), QString( "Triangle ((0 0, 100 100, 0 200, 0 0))" ) );
id.vertex = 4;
pt1 = QgsPointV2( 5, 5 );
QVERIFY( t11.moveVertex( id, pt1 ) );
QCOMPARE( t11.asWkt(), QString( "Triangle ((5 5, 100 100, 0 200, 5 5))" ) );
pt1 = QgsPointV2();
QVERIFY( t11.moveVertex( id, pt1 ) );
QCOMPARE( t11.asWkt(), QString( "Triangle ((0 0, 100 100, 0 200, 0 0))" ) );
id.vertex = 1;
pt1 = QgsPointV2( 5, 5 );
QVERIFY( t11.moveVertex( id, pt1 ) );
QCOMPARE( t11.asWkt(), QString( "Triangle ((0 0, 5 5, 0 200, 0 0))" ) );
// colinear
pt1 = QgsPointV2( 0, 100 );
QVERIFY( !t11.moveVertex( id, pt1 ) );
// duplicate point
pt1 = QgsPointV2( 0, 0 );
QVERIFY( !t11.moveVertex( id, pt1 ) );
}
void TestQgsGeometry::compoundCurve()
{
//test that area of a compound curve ring is equal to a closed linestring with the same vertices

View File

@ -52,6 +52,9 @@ class TestQgsGeometryUtils: public QObject
void testSqrDistToLine();
void testAngleThreePoints();
void testMidPoint();
void testGradient();
void testCoefficients();
void testPerpendicularSegment();
};
@ -549,5 +552,86 @@ void TestQgsGeometryUtils::testMidPoint()
QCOMPARE( QgsGeometryUtils::midpoint( p1, QgsPointV2( QgsWkbTypes::PointZM, 2, 2, 2, 2 ) ), QgsPointV2( QgsWkbTypes::PointZM, 3, 4, 1, 1 ) );
}
void TestQgsGeometryUtils::testGradient()
{
QVERIFY( QgsGeometryUtils::gradient( QgsPointV2( 4, 6 ), QgsPointV2( 4, 8 ) ) == INFINITY );
QGSCOMPARENEAR( QgsGeometryUtils::gradient( QgsPointV2( 2, 8 ), QgsPointV2( 3, 20 ) ), 12, 0.00000001 );
QGSCOMPARENEAR( QgsGeometryUtils::gradient( QgsPointV2( 2, -88 ), QgsPointV2( 4, -4 ) ), 42, 0.00000001 );
QGSCOMPARENEAR( QgsGeometryUtils::gradient( QgsPointV2( 4, 6 ), QgsPointV2( 8, 6 ) ), 0, 0.00000001 );
}
void TestQgsGeometryUtils::testCoefficients()
{
double a, b, c;
// pt1.x == pt2.x
QgsGeometryUtils::coefficients( QgsPointV2( 4, 6 ), QgsPointV2( 4, 8 ), a, b, c );
QGSCOMPARENEAR( a, 1, 0.00000001 );
QGSCOMPARENEAR( b, 0, 0.00000001 );
QGSCOMPARENEAR( c, -4, 0.00000001 );
// pt1.y == pt2.y
QgsGeometryUtils::coefficients( QgsPointV2( 6, 4 ), QgsPointV2( 8, 4 ), a, b, c );
QGSCOMPARENEAR( a, 0, 0.00000001 );
QGSCOMPARENEAR( b, 1, 0.00000001 );
QGSCOMPARENEAR( c, -4, 0.00000001 );
// else
QgsGeometryUtils::coefficients( QgsPointV2( 6, 4 ), QgsPointV2( 4, 8 ), a, b, c );
QGSCOMPARENEAR( a, -4, 0.00000001 );
QGSCOMPARENEAR( b, -2, 0.00000001 );
QGSCOMPARENEAR( c, 32, 0.00000001 );
QgsGeometryUtils::coefficients( QgsPointV2( -4, -2 ), QgsPointV2( 4, 2 ), a, b, c );
QGSCOMPARENEAR( a, -4, 0.00000001 );
QGSCOMPARENEAR( b, 8, 0.00000001 );
QGSCOMPARENEAR( c, 0, 0.00000001 );
}
void TestQgsGeometryUtils::testPerpendicularSegment()
{
QgsPointV2 p1( 3, 13 );
QgsPointV2 s1( 2, 3 );
QgsPointV2 s2( 7, 11 );
QgsLineString line_r = QgsGeometryUtils::perpendicularSegment( p1, s1, s2 );
// default case
QgsLineString line;
line.addVertex( p1 );
line.addVertex( QgsPointV2( 6.7753, 10.6404 ) );
QGSCOMPARENEARPOINT( line.pointN( 0 ), line_r.pointN( 0 ), 0.0001 );
QGSCOMPARENEARPOINT( line.pointN( 1 ), line_r.pointN( 1 ), 0.0001 );
// perpendicular line don't intersect segment
line.clear();
p1 = QgsPointV2( 11, 11 );
line_r = QgsGeometryUtils::perpendicularSegment( p1, s1, s2 );
line.addVertex( p1 );
line.addVertex( QgsPointV2( 8.1236, 12.7978 ) );
QGSCOMPARENEARPOINT( line.pointN( 0 ), line_r.pointN( 0 ), 0.0001 );
QGSCOMPARENEARPOINT( line.pointN( 1 ), line_r.pointN( 1 ), 0.0001 );
// horizontal
s1 = QgsPointV2( -3, 3 );
s2 = QgsPointV2( 2, 3 );
line.clear();
p1 = QgsPointV2( 3, 13 );
line_r = QgsGeometryUtils::perpendicularSegment( p1, s1, s2 );
line.addVertex( p1 );
line.addVertex( QgsPointV2( 3, 3 ) );
QCOMPARE( line.pointN( 0 ), line_r.pointN( 0 ) );
QCOMPARE( line.pointN( 1 ), line_r.pointN( 1 ) );
// vertical
s1 = QgsPointV2( 3, 13 );
s2 = QgsPointV2( 3, 3 );
line.clear();
p1 = QgsPointV2( -7, 8 );
line_r = QgsGeometryUtils::perpendicularSegment( p1, s1, s2 );
line.addVertex( p1 );
line.addVertex( QgsPointV2( 3, 8 ) );
QCOMPARE( line.pointN( 0 ), line_r.pointN( 0 ) );
QCOMPARE( line.pointN( 1 ), line_r.pointN( 1 ) );
}
QGSTEST_MAIN( TestQgsGeometryUtils )
#include "testqgsgeometryutils.moc"