mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
[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:
parent
0710fb5da8
commit
fb3d07f3f5
@ -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
|
||||
|
@ -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 );
|
||||
|
||||
};
|
||||
|
63
python/core/geometry/qgstriangle.sip
Normal file
63
python/core/geometry/qgstriangle.sip
Normal 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)
|
||||
};
|
@ -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
|
||||
|
@ -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 );
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -210,6 +210,7 @@ class CORE_EXPORT QgsLineString: public QgsCurve
|
||||
void fromWkbPoints( QgsWkbTypes::Type type, const QgsConstWkbPtr &wkb );
|
||||
|
||||
friend class QgsPolygonV2;
|
||||
friend class QgsTriangle;
|
||||
|
||||
};
|
||||
|
||||
|
529
src/core/geometry/qgstriangle.cpp
Normal file
529
src/core/geometry/qgstriangle.cpp
Normal 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
|
||||
{
|
||||
|
||||
}*/
|
||||
|
||||
|
||||
|
322
src/core/geometry/qgstriangle.h
Normal file
322
src/core/geometry/qgstriangle.h
Normal 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
|
@ -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 ) },
|
||||
|
@ -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:
|
||||
|
@ -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" ) )
|
||||
|
@ -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 );
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user