mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-06 00:07:29 -04:00
New geometry class: QgsRegularPolygon (#4502)
This commit is contained in:
parent
55cb855003
commit
13c1318a5f
@ -393,6 +393,7 @@
|
||||
%Include geometry/qgspointv2.sip
|
||||
%Include geometry/qgspolygon.sip
|
||||
%Include geometry/qgsrectangle.sip
|
||||
%Include geometry/qgsregularpolygon.sip
|
||||
%Include geometry/qgssurface.sip
|
||||
%Include geometry/qgstriangle.sip
|
||||
%Include geometry/qgswkbtypes.sip
|
||||
|
@ -189,11 +189,11 @@ The circumference of the ellipse using first approximation of Ramanujan.
|
||||
:rtype: list of QgsPointV2
|
||||
%End
|
||||
|
||||
virtual void points( QgsPointSequence &pts, unsigned int segments = 36 ) const;
|
||||
virtual QgsPointSequence points( unsigned int segments = 36 ) const;
|
||||
%Docstring
|
||||
Returns a list of points into ``pts``, with segmentation from ``segments``.
|
||||
\param pts List of points returned.
|
||||
Returns a list of points with segmentation from ``segments``.
|
||||
\param segments Number of segments used to segment geometry.
|
||||
:rtype: QgsPointSequence
|
||||
%End
|
||||
|
||||
virtual QgsPolygonV2 *toPolygon( unsigned int segments = 36 ) const /Factory/;
|
||||
|
232
python/core/geometry/qgsregularpolygon.sip
Normal file
232
python/core/geometry/qgsregularpolygon.sip
Normal file
@ -0,0 +1,232 @@
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/geometry/qgsregularpolygon.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsRegularPolygon
|
||||
{
|
||||
%Docstring
|
||||
Regular Polygon geometry type.
|
||||
|
||||
A regular polygon is a polygon that is equiangular (all angles are equal in measure) and equilateral (all sides have the same length).
|
||||
The regular polygon is defined by a center point with a number of sides/vertices, a radius and the first vertex.
|
||||
|
||||
.. versionadded:: 3.0
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgsregularpolygon.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
enum ConstructionOption
|
||||
{
|
||||
InscribedCircle,
|
||||
CircumscribedCircle
|
||||
};
|
||||
|
||||
QgsRegularPolygon();
|
||||
|
||||
QgsRegularPolygon( const QgsPointV2 ¢er, const double radius, const double azimuth, const int numberSides, const ConstructionOption circle );
|
||||
%Docstring
|
||||
Constructs a regular polygon by ``center`` and parameters for the first vertex. An empty regular polygon is returned if ``numberSides`` < 3 or ``ConstructionOption`` isn't valid.
|
||||
\param center The center of the regular polygon.
|
||||
\param radius Distance from the center and the first vertex or sides (see ``ConstructionOption``).
|
||||
\param azimuth Angle in degrees started from the North to the first vertex.
|
||||
\param numberSides Number of sides of the regular polygon.
|
||||
.. seealso:: ConstructionOption
|
||||
%End
|
||||
|
||||
QgsRegularPolygon( const QgsPointV2 ¢er, const QgsPointV2 &pt1, const int numberSides, const ConstructionOption circle );
|
||||
%Docstring
|
||||
Constructs a regular polygon by ``center`` and another point.
|
||||
\param center The center of the regular polygon.
|
||||
\param pt1 The first vertex if the polygon is inscribed in circle or the midpoint of a side if the polygon is circumscribed about circle.
|
||||
\param numberSides Number of sides of the regular polygon.
|
||||
\param circle Option to create the polygon inscribed in circle (the radius is the distance between the center and vertices) or circumscribed about circle (the radius is the distance from the center to the midpoints of the sides).
|
||||
%End
|
||||
|
||||
QgsRegularPolygon( const QgsPointV2 &pt1, const QgsPointV2 &pt2, const int numberSides );
|
||||
%Docstring
|
||||
Constructs a regular polygon by two points of the first side.
|
||||
\param pt1 The first vertex of the first side, also first vertex of the regular polygon.
|
||||
\param pt2 The second vertex of the first side.
|
||||
\param numberSides Number of sides of the regular polygon.
|
||||
%End
|
||||
|
||||
bool operator ==( const QgsRegularPolygon &rp ) const;
|
||||
bool operator !=( const QgsRegularPolygon &rp ) const;
|
||||
|
||||
bool isEmpty() const;
|
||||
%Docstring
|
||||
A regular polygon is empty if radius equal to 0 or number of sides < 3
|
||||
:rtype: bool
|
||||
%End
|
||||
|
||||
QgsPointV2 center() const;
|
||||
%Docstring
|
||||
Returns the center point of the regular polygon.
|
||||
.. seealso:: setCenter()
|
||||
:rtype: QgsPointV2
|
||||
%End
|
||||
|
||||
double radius() const;
|
||||
%Docstring
|
||||
Returns the radius.
|
||||
This is also the radius of the circumscribing circle.
|
||||
.. seealso:: apothem()
|
||||
.. seealso:: setRadius()
|
||||
:rtype: float
|
||||
%End
|
||||
|
||||
QgsPointV2 firstVertex() const;
|
||||
%Docstring
|
||||
Returns the first vertex (corner) of the regular polygon.
|
||||
.. seealso:: setFirstVertex()
|
||||
:rtype: QgsPointV2
|
||||
%End
|
||||
|
||||
double apothem() const;
|
||||
%Docstring
|
||||
Returns the apothem of the regular polygon.
|
||||
The apothem is the radius of the inscribed circle.
|
||||
.. seealso:: radius()
|
||||
:rtype: float
|
||||
%End
|
||||
|
||||
int numberSides() const;
|
||||
%Docstring
|
||||
Returns the number of sides of the regular polygon.
|
||||
.. seealso:: setNumberSides()
|
||||
:rtype: int
|
||||
%End
|
||||
|
||||
void setCenter( const QgsPointV2 ¢er );
|
||||
%Docstring
|
||||
Sets the center point.
|
||||
Radius is unchanged. The first vertex is reprojected from the new center.
|
||||
.. seealso:: center()
|
||||
%End
|
||||
|
||||
void setRadius( const double radius );
|
||||
%Docstring
|
||||
Sets the radius.
|
||||
Center is unchanged. The first vertex is reprojected from the center with the new radius.
|
||||
.. seealso:: radius()
|
||||
%End
|
||||
|
||||
void setFirstVertex( const QgsPointV2 &firstVertex );
|
||||
%Docstring
|
||||
Sets the first vertex.
|
||||
Radius is unchanged. The center is reprojected from the new first vertex.
|
||||
.. seealso:: firstVertex()
|
||||
%End
|
||||
|
||||
void setNumberSides( const int numberSides );
|
||||
%Docstring
|
||||
Sets the number of sides.
|
||||
If numberSides < 3, the number of sides is unchanged.
|
||||
.. seealso:: numberSides()
|
||||
%End
|
||||
|
||||
QgsPointSequence points( ) const;
|
||||
%Docstring
|
||||
Returns a list including the vertices of the regular polygon.
|
||||
:rtype: QgsPointSequence
|
||||
%End
|
||||
|
||||
QgsPolygonV2 *toPolygon( ) const /Factory/;
|
||||
%Docstring
|
||||
Returns as a polygon.
|
||||
:rtype: QgsPolygonV2
|
||||
%End
|
||||
|
||||
QgsLineString *toLineString( ) const /Factory/;
|
||||
%Docstring
|
||||
Returns as a linestring.
|
||||
:rtype: QgsLineString
|
||||
%End
|
||||
|
||||
QgsTriangle toTriangle( ) const;
|
||||
%Docstring
|
||||
Returns as a triangle.
|
||||
An empty triangle is returned if the regular polygon is empty or if the number of sides is different from 3.
|
||||
:rtype: QgsTriangle
|
||||
%End
|
||||
|
||||
QList<QgsTriangle> triangulate( ) const;
|
||||
%Docstring
|
||||
Returns a triangulation (vertices from sides to the center) of the regular polygon.
|
||||
An empty list is returned if the regular polygon is empty.
|
||||
:rtype: list of QgsTriangle
|
||||
%End
|
||||
|
||||
QgsCircle inscribedCircle( ) const;
|
||||
%Docstring
|
||||
Returns the inscribed circle
|
||||
:rtype: QgsCircle
|
||||
%End
|
||||
|
||||
QgsCircle circumscribedCircle( ) const;
|
||||
%Docstring
|
||||
Returns the circumscribed circle
|
||||
:rtype: QgsCircle
|
||||
%End
|
||||
|
||||
QString toString( int pointPrecision = 17, int radiusPrecision = 17, int anglePrecision = 2 ) const;
|
||||
%Docstring
|
||||
Returns a string representation of the regular polygon.
|
||||
Members will be truncated to the specified precision.
|
||||
:rtype: str
|
||||
%End
|
||||
|
||||
double interiorAngle( ) const;
|
||||
%Docstring
|
||||
Returns the measure of the interior angles in degrees.
|
||||
:rtype: float
|
||||
%End
|
||||
|
||||
double centralAngle( ) const;
|
||||
%Docstring
|
||||
Returns the measure of the central angle (the angle subtended at the center of the polygon by one of its sides) in degrees.
|
||||
:rtype: float
|
||||
%End
|
||||
|
||||
double area( ) const;
|
||||
%Docstring
|
||||
Returns the area.
|
||||
Returns 0 if the regular polygon is empty.
|
||||
:rtype: float
|
||||
%End
|
||||
|
||||
double perimeter( ) const;
|
||||
%Docstring
|
||||
Returns the perimeter.
|
||||
Returns 0 if the regular polygon is empty.
|
||||
:rtype: float
|
||||
%End
|
||||
|
||||
double length( ) const;
|
||||
%Docstring
|
||||
Returns the length of a side.
|
||||
Returns 0 if the regular polygon is empty.
|
||||
:rtype: float
|
||||
%End
|
||||
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/geometry/qgsregularpolygon.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
@ -49,6 +49,14 @@ class QgsTriangle : QgsPolygonV2
|
||||
\param p3 third point
|
||||
%End
|
||||
|
||||
bool operator==( const QgsTriangle &other ) const;
|
||||
%Docstring
|
||||
:rtype: bool
|
||||
%End
|
||||
bool operator!=( const QgsTriangle &other ) const;
|
||||
%Docstring
|
||||
:rtype: bool
|
||||
%End
|
||||
|
||||
virtual QString geometryType() const;
|
||||
virtual QgsTriangle *clone() const /Factory/;
|
||||
|
@ -5,8 +5,8 @@
|
||||
"variableLenArguments": true,
|
||||
"arguments": [
|
||||
{"arg":"center", "description": "center point of the ellipse"},
|
||||
{"arg":"semi-major axis", "description": "semi-major axis of the ellipse"},
|
||||
{"arg":"semi-minor axis", "description": "semi-minor axis of the ellipse"},
|
||||
{"arg":"semi_major_axis", "description": "semi-major axis of the ellipse"},
|
||||
{"arg":"semi_minor_axis", "description": "semi-minor axis of the ellipse"},
|
||||
{"arg":"azimuth", "description": "orientation of the ellipse"},
|
||||
{"arg":"segment", "description": "optional argument for polygon segmentation. By default this value is 36"}],
|
||||
"examples": [ { "expression":"geom_to_wkt(make_ellipse(make_point(10,10), 5, 2, 90, 4))", "returns":"'Polygon ((15 10, 10 8, 5 10, 10 12, 15 10))'"},
|
||||
|
14
resources/function_help/json/make_regular_polygon
Normal file
14
resources/function_help/json/make_regular_polygon
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "make_regular_polygon",
|
||||
"type": "function",
|
||||
"description": "Creates a regular polygon.",
|
||||
"variableLenArguments": true,
|
||||
"arguments": [
|
||||
{"arg":"center", "description": "center of the regular polygon"},
|
||||
{"arg":"radius", "description": "second point. The first if the regular polygon is inscribed. The midpoint of the first side if the regular polygon is circumscribed."},
|
||||
{"arg":"number_sides", "description": "Number of sides/edges of the regular polygon"},
|
||||
{"arg":"circle", "description": "Optional argument to construct the regular polygon. By default this value is 0. Value can be 0 (inscribed) or 1 (circumscribed)"}],
|
||||
"examples": [ { "expression":"geom_to_wkt(make_regular_polygon(make_point(0,0), make_point(0,5), 5))", "returns":"'Polygon ((0 5, 4.76 1.55, 2.94 -4.05, -2.94 -4.05, -4.76 1.55, 0 5))'"},
|
||||
{ "expression":"geom_to_wkt(make_regular_polygon(make_point(0,0), project(make_point(0,0), 4.0451, radians(36)), 5))", "returns":"'Polygon ((0 5, 4.76 1.55, 2.94 -4.05, -2.94 -4.05, -4.76 1.55, 0 5))'"}
|
||||
]
|
||||
}
|
@ -406,6 +406,7 @@ SET(QGIS_CORE_SRCS
|
||||
geometry/qgspointv2.cpp
|
||||
geometry/qgspolygon.cpp
|
||||
geometry/qgsrectangle.cpp
|
||||
geometry/qgsregularpolygon.cpp
|
||||
geometry/qgstriangle.cpp
|
||||
geometry/qgswkbptr.cpp
|
||||
geometry/qgswkbtypes.cpp
|
||||
@ -974,6 +975,7 @@ SET(QGIS_CORE_HDRS
|
||||
geometry/qgspointv2.h
|
||||
geometry/qgspolygon.h
|
||||
geometry/qgsrectangle.h
|
||||
geometry/qgsregularpolygon.h
|
||||
geometry/qgstriangle.h
|
||||
geometry/qgssurface.h
|
||||
geometry/qgswkbptr.h
|
||||
|
@ -184,12 +184,13 @@ QVector<QgsPointV2> QgsEllipse::quadrant() const
|
||||
return quad;
|
||||
}
|
||||
|
||||
void QgsEllipse::points( QgsPointSequence &pts, unsigned int segments ) const
|
||||
QgsPointSequence QgsEllipse::points( unsigned int segments ) const
|
||||
{
|
||||
pts.clear();
|
||||
QgsPointSequence pts;
|
||||
|
||||
if ( segments < 3 )
|
||||
{
|
||||
return;
|
||||
return pts;
|
||||
}
|
||||
|
||||
|
||||
@ -214,6 +215,8 @@ void QgsEllipse::points( QgsPointSequence &pts, unsigned int segments ) const
|
||||
mSemiMinorAxis * sin( *it ) * cos( azimuth );
|
||||
pts.push_back( QgsPointV2( pType, x, y, z, m ) );
|
||||
}
|
||||
|
||||
return pts;
|
||||
}
|
||||
|
||||
QgsPolygonV2 *QgsEllipse::toPolygon( unsigned int segments ) const
|
||||
@ -238,7 +241,7 @@ QgsLineString *QgsEllipse::toLineString( unsigned int segments ) const
|
||||
}
|
||||
|
||||
QgsPointSequence pts;
|
||||
points( pts, segments );
|
||||
pts = points( segments );
|
||||
|
||||
ext->setPoints( pts );
|
||||
|
||||
|
@ -176,11 +176,10 @@ class CORE_EXPORT QgsEllipse
|
||||
*/
|
||||
virtual QVector<QgsPointV2> quadrant() const;
|
||||
|
||||
/** Returns a list of points into \a pts, with segmentation from \a segments.
|
||||
* \param pts List of points returned.
|
||||
/** Returns a list of points with segmentation from \a segments.
|
||||
* \param segments Number of segments used to segment geometry.
|
||||
*/
|
||||
virtual void points( QgsPointSequence &pts, unsigned int segments = 36 ) const;
|
||||
virtual QgsPointSequence points( unsigned int segments = 36 ) const;
|
||||
|
||||
/** Returns a segmented polygon.
|
||||
* \param segments Number of segments used to segment geometry.
|
||||
|
351
src/core/geometry/qgsregularpolygon.cpp
Normal file
351
src/core/geometry/qgsregularpolygon.cpp
Normal file
@ -0,0 +1,351 @@
|
||||
/***************************************************************************
|
||||
qgsregularpolygon.cpp
|
||||
--------------
|
||||
begin : May 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 "qgsregularpolygon.h"
|
||||
#include "qgsgeometryutils.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
QgsRegularPolygon::QgsRegularPolygon()
|
||||
: mCenter( QgsPointV2() )
|
||||
, mFirstVertex( QgsPointV2() )
|
||||
, mNumberSides( 0 )
|
||||
, mRadius( 0.0 )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
QgsRegularPolygon::QgsRegularPolygon( const QgsPointV2 ¢er, const double radius, const double azimuth, const int numSides, const ConstructionOption circle )
|
||||
: mCenter( center )
|
||||
, mFirstVertex( QgsPointV2() )
|
||||
, mNumberSides( 0 )
|
||||
, mRadius( 0.0 )
|
||||
{
|
||||
// TODO: inclination
|
||||
|
||||
if ( numSides >= 3 )
|
||||
{
|
||||
mNumberSides = numSides;
|
||||
|
||||
switch ( circle )
|
||||
{
|
||||
case InscribedCircle:
|
||||
{
|
||||
mRadius = qAbs( radius );
|
||||
mFirstVertex = mCenter.project( mRadius, azimuth );
|
||||
break;
|
||||
}
|
||||
case CircumscribedCircle:
|
||||
{
|
||||
mRadius = apothemToRadius( qAbs( radius ), numSides );
|
||||
mFirstVertex = mCenter.project( mRadius, azimuth - centralAngle( numSides ) / 2 );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QgsRegularPolygon::QgsRegularPolygon( const QgsPointV2 ¢er, const QgsPointV2 &pt1, const int numSides, const ConstructionOption circle )
|
||||
: mCenter( center )
|
||||
, mFirstVertex( QgsPointV2() )
|
||||
, mNumberSides( 0 )
|
||||
, mRadius( 0.0 )
|
||||
{
|
||||
if ( numSides >= 3 )
|
||||
{
|
||||
mNumberSides = numSides;
|
||||
|
||||
switch ( circle )
|
||||
{
|
||||
case InscribedCircle:
|
||||
{
|
||||
mFirstVertex = pt1;
|
||||
mRadius = center.distance( pt1 );
|
||||
break;
|
||||
}
|
||||
case CircumscribedCircle:
|
||||
{
|
||||
mRadius = apothemToRadius( center.distance( pt1 ), numSides );
|
||||
double azimuth = center.azimuth( pt1 );
|
||||
// TODO: inclination
|
||||
mFirstVertex = mCenter.project( mRadius, azimuth - centralAngle( numSides ) / 2 );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QgsRegularPolygon::QgsRegularPolygon( const QgsPointV2 &pt1, const QgsPointV2 &pt2, const int numSides )
|
||||
: mCenter( QgsPointV2() )
|
||||
, mFirstVertex( QgsPointV2() )
|
||||
, mNumberSides( 0 )
|
||||
, mRadius( 0.0 )
|
||||
{
|
||||
if ( numSides >= 3 )
|
||||
{
|
||||
mNumberSides = numSides;
|
||||
|
||||
double azimuth = pt1.azimuth( pt2 );
|
||||
QgsPointV2 pm = QgsGeometryUtils::midpoint( pt1, pt2 );
|
||||
double length = pt1.distance( pm );
|
||||
|
||||
double angle = ( 180 - ( 360 / numSides ) ) / 2.0;
|
||||
double hypothenuse = length / cos( angle * M_PI / 180 );
|
||||
// TODO: inclination
|
||||
|
||||
mCenter = pt1.project( hypothenuse, azimuth + angle );
|
||||
mFirstVertex = pt1;
|
||||
mRadius = qAbs( hypothenuse );
|
||||
}
|
||||
}
|
||||
|
||||
bool QgsRegularPolygon::operator ==( const QgsRegularPolygon &rp ) const
|
||||
{
|
||||
return ( ( mCenter == rp.mCenter ) &&
|
||||
( mFirstVertex == rp.mFirstVertex ) &&
|
||||
( mNumberSides == rp.mNumberSides )
|
||||
);
|
||||
}
|
||||
|
||||
bool QgsRegularPolygon::operator !=( const QgsRegularPolygon &rp ) const
|
||||
{
|
||||
return !operator==( rp );
|
||||
}
|
||||
|
||||
bool QgsRegularPolygon::isEmpty() const
|
||||
{
|
||||
return ( ( mNumberSides < 3 ) ||
|
||||
( mCenter == mFirstVertex )
|
||||
);
|
||||
}
|
||||
|
||||
void QgsRegularPolygon::setCenter( const QgsPointV2 ¢er )
|
||||
{
|
||||
double azimuth = mCenter.azimuth( mFirstVertex );
|
||||
// TODO: double inclination = mCenter.inclination(mFirstVertex);
|
||||
mCenter = center;
|
||||
mFirstVertex = center.project( mRadius, azimuth );
|
||||
}
|
||||
|
||||
void QgsRegularPolygon::setRadius( const double radius )
|
||||
{
|
||||
mRadius = qAbs( radius );
|
||||
double azimuth = mCenter.azimuth( mFirstVertex );
|
||||
// TODO: double inclination = mCenter.inclination(mFirstVertex);
|
||||
mFirstVertex = mCenter.project( mRadius, azimuth );
|
||||
}
|
||||
|
||||
void QgsRegularPolygon::setFirstVertex( const QgsPointV2 &firstVertex )
|
||||
{
|
||||
double azimuth = mCenter.azimuth( mFirstVertex );
|
||||
// TODO: double inclination = mCenter.inclination(firstVertex);
|
||||
mFirstVertex = firstVertex;
|
||||
mCenter = mFirstVertex.project( mRadius, azimuth );
|
||||
}
|
||||
|
||||
void QgsRegularPolygon::setNumberSides( const int numSides )
|
||||
{
|
||||
if ( numSides >= 3 )
|
||||
{
|
||||
mNumberSides = numSides;
|
||||
}
|
||||
}
|
||||
|
||||
QgsPointSequence QgsRegularPolygon::points( ) const
|
||||
{
|
||||
QgsPointSequence pts;
|
||||
if ( isEmpty() )
|
||||
{
|
||||
return pts;
|
||||
}
|
||||
|
||||
double azimuth = mCenter.azimuth( mFirstVertex );
|
||||
double azimuth_add = centralAngle();
|
||||
// TODO: inclination
|
||||
|
||||
unsigned int n = 1;
|
||||
while ( n <= mNumberSides )
|
||||
{
|
||||
pts.push_back( mCenter.project( mRadius, azimuth ) );
|
||||
azimuth += azimuth_add;
|
||||
if ( ( azimuth_add > 0 ) && ( azimuth > 180.0 ) )
|
||||
{
|
||||
azimuth -= 360.0;
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
return pts;
|
||||
}
|
||||
|
||||
QgsPolygonV2 *QgsRegularPolygon::toPolygon() const
|
||||
{
|
||||
std::unique_ptr<QgsPolygonV2> polygon( new QgsPolygonV2() );
|
||||
if ( isEmpty() )
|
||||
{
|
||||
return polygon.release();
|
||||
}
|
||||
|
||||
polygon->setExteriorRing( toLineString( ) );
|
||||
|
||||
return polygon.release();
|
||||
}
|
||||
|
||||
QgsLineString *QgsRegularPolygon::toLineString() const
|
||||
{
|
||||
std::unique_ptr<QgsLineString> ext( new QgsLineString() );
|
||||
if ( isEmpty() )
|
||||
{
|
||||
return ext.release();
|
||||
}
|
||||
|
||||
QgsPointSequence pts;
|
||||
pts = points( );
|
||||
|
||||
ext->setPoints( pts );
|
||||
|
||||
return ext.release();
|
||||
}
|
||||
|
||||
QgsTriangle QgsRegularPolygon::toTriangle() const
|
||||
{
|
||||
if ( isEmpty() || ( mNumberSides != 3 ) )
|
||||
{
|
||||
return QgsTriangle();
|
||||
}
|
||||
|
||||
QgsPointSequence pts;
|
||||
pts = points( );
|
||||
|
||||
return QgsTriangle( pts.at( 0 ), pts.at( 1 ), pts.at( 2 ) );
|
||||
}
|
||||
|
||||
QList<QgsTriangle> QgsRegularPolygon::triangulate() const
|
||||
{
|
||||
QList<QgsTriangle> l_tri;
|
||||
if ( isEmpty() )
|
||||
{
|
||||
return l_tri;
|
||||
}
|
||||
|
||||
QgsPointSequence pts;
|
||||
pts = points( );
|
||||
|
||||
unsigned int n = 0;
|
||||
while ( n < mNumberSides - 1 )
|
||||
{
|
||||
l_tri.append( QgsTriangle( pts.at( n ), pts.at( n + 1 ), mCenter ) );
|
||||
n++;
|
||||
}
|
||||
l_tri.append( QgsTriangle( pts.at( n ), pts.at( 0 ), mCenter ) );
|
||||
|
||||
return l_tri;
|
||||
}
|
||||
|
||||
QgsCircle QgsRegularPolygon::inscribedCircle() const
|
||||
{
|
||||
// TODO: inclined circle
|
||||
return QgsCircle( mCenter, apothem() );
|
||||
}
|
||||
|
||||
QgsCircle QgsRegularPolygon::circumscribedCircle() const
|
||||
{
|
||||
// TODO: inclined circle
|
||||
return QgsCircle( mCenter, mRadius );
|
||||
}
|
||||
|
||||
QString QgsRegularPolygon::toString( int pointPrecision, int radiusPrecision, int anglePrecision ) const
|
||||
{
|
||||
QString rep;
|
||||
if ( isEmpty() )
|
||||
rep = QStringLiteral( "Empty" );
|
||||
else
|
||||
rep = QStringLiteral( "RegularPolygon (Center: %1, First Vertex: %2, Radius: %3, Azimuth: %4)" )
|
||||
.arg( mCenter.asWkt( pointPrecision ), 0, 's' )
|
||||
.arg( mFirstVertex.asWkt( pointPrecision ), 0, 's' )
|
||||
.arg( qgsDoubleToString( mRadius, radiusPrecision ), 0, 'f' )
|
||||
.arg( qgsDoubleToString( mCenter.azimuth( mFirstVertex ), anglePrecision ), 0, 'f' );
|
||||
// TODO: inclination
|
||||
// .arg( qgsDoubleToString( mCenter.inclination(mFirstVertex), anglePrecision ), 0, 'f' );
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
double QgsRegularPolygon::area() const
|
||||
{
|
||||
if ( isEmpty() )
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return ( mRadius * mRadius * mNumberSides * sin( centralAngle() * M_PI / 180.0 ) ) / 2 ;
|
||||
}
|
||||
|
||||
double QgsRegularPolygon::perimeter() const
|
||||
{
|
||||
if ( isEmpty() )
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return length() * mNumberSides;
|
||||
}
|
||||
|
||||
double QgsRegularPolygon::length() const
|
||||
{
|
||||
if ( isEmpty() )
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return mRadius * 2 * sin( M_PI / mNumberSides );
|
||||
}
|
||||
|
||||
double QgsRegularPolygon::apothemToRadius( const double apothem, const unsigned int numSides ) const
|
||||
{
|
||||
return apothem / cos( M_PI / numSides );
|
||||
}
|
||||
|
||||
double QgsRegularPolygon::interiorAngle( const unsigned int nbSides ) const
|
||||
{
|
||||
return ( nbSides - 2 ) * 180 / nbSides;
|
||||
}
|
||||
|
||||
double QgsRegularPolygon::centralAngle( const unsigned int nbSides ) const
|
||||
{
|
||||
return 360.0 / nbSides;
|
||||
}
|
||||
|
||||
double QgsRegularPolygon::interiorAngle() const
|
||||
{
|
||||
return interiorAngle( mNumberSides );
|
||||
}
|
||||
|
||||
double QgsRegularPolygon::centralAngle() const
|
||||
{
|
||||
return centralAngle( mNumberSides );
|
||||
}
|
217
src/core/geometry/qgsregularpolygon.h
Normal file
217
src/core/geometry/qgsregularpolygon.h
Normal file
@ -0,0 +1,217 @@
|
||||
/***************************************************************************
|
||||
qgsregularpolygon.h
|
||||
--------------
|
||||
begin : May 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 QGSREGULARPOLYGON_H
|
||||
#define QGSREGULARPOLYGON_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "qgis_core.h"
|
||||
#include "qgspointv2.h"
|
||||
#include "qgspolygon.h"
|
||||
#include "qgslinestring.h"
|
||||
#include "qgscircle.h"
|
||||
#include "qgstriangle.h"
|
||||
|
||||
|
||||
/** \ingroup core
|
||||
* \class QgsRegularPolygon
|
||||
* \brief Regular Polygon geometry type.
|
||||
*
|
||||
* A regular polygon is a polygon that is equiangular (all angles are equal in measure) and equilateral (all sides have the same length).
|
||||
* The regular polygon is defined by a center point with a number of sides/vertices, a radius and the first vertex.
|
||||
*
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
class CORE_EXPORT QgsRegularPolygon
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* A regular polygon can be constructed inscribed in a circle or circumscribed about a circle.
|
||||
*
|
||||
*/
|
||||
enum ConstructionOption
|
||||
{
|
||||
InscribedCircle, //<! Inscribed in a circle (the radius is the distance between the center and vertices)
|
||||
CircumscribedCircle //<! Circumscribed about a circle (the radius is the distance from the center to the midpoints of the sides)
|
||||
};
|
||||
|
||||
QgsRegularPolygon();
|
||||
|
||||
/** Constructs a regular polygon by \a center and parameters for the first vertex. An empty regular polygon is returned if \a numberSides < 3 or \a ConstructionOption isn't valid.
|
||||
* \param center The center of the regular polygon.
|
||||
* \param radius Distance from the center and the first vertex or sides (see \a ConstructionOption).
|
||||
* \param azimuth Angle in degrees started from the North to the first vertex.
|
||||
* \param numberSides Number of sides of the regular polygon.
|
||||
* \param circle Option to create the polygon. \see ConstructionOption
|
||||
*/
|
||||
QgsRegularPolygon( const QgsPointV2 ¢er, const double radius, const double azimuth, const int numberSides, const ConstructionOption circle );
|
||||
|
||||
/** Constructs a regular polygon by \a center and another point.
|
||||
* \param center The center of the regular polygon.
|
||||
* \param pt1 The first vertex if the polygon is inscribed in circle or the midpoint of a side if the polygon is circumscribed about circle.
|
||||
* \param numberSides Number of sides of the regular polygon.
|
||||
* \param circle Option to create the polygon inscribed in circle (the radius is the distance between the center and vertices) or circumscribed about circle (the radius is the distance from the center to the midpoints of the sides).
|
||||
*/
|
||||
QgsRegularPolygon( const QgsPointV2 ¢er, const QgsPointV2 &pt1, const int numberSides, const ConstructionOption circle );
|
||||
|
||||
/** Constructs a regular polygon by two points of the first side.
|
||||
* \param pt1 The first vertex of the first side, also first vertex of the regular polygon.
|
||||
* \param pt2 The second vertex of the first side.
|
||||
* \param numberSides Number of sides of the regular polygon.
|
||||
*/
|
||||
QgsRegularPolygon( const QgsPointV2 &pt1, const QgsPointV2 &pt2, const int numberSides );
|
||||
|
||||
bool operator ==( const QgsRegularPolygon &rp ) const;
|
||||
bool operator !=( const QgsRegularPolygon &rp ) const;
|
||||
|
||||
//! A regular polygon is empty if radius equal to 0 or number of sides < 3
|
||||
bool isEmpty() const;
|
||||
|
||||
/** Returns the center point of the regular polygon.
|
||||
* \see setCenter()
|
||||
*/
|
||||
QgsPointV2 center() const { return mCenter; }
|
||||
|
||||
/** Returns the radius.
|
||||
* This is also the radius of the circumscribing circle.
|
||||
* \see apothem()
|
||||
* \see setRadius()
|
||||
*/
|
||||
double radius() const { return mRadius; }
|
||||
|
||||
/** Returns the first vertex (corner) of the regular polygon.
|
||||
* \see setFirstVertex()
|
||||
*/
|
||||
QgsPointV2 firstVertex() const { return mFirstVertex; }
|
||||
|
||||
/** Returns the apothem of the regular polygon.
|
||||
* The apothem is the radius of the inscribed circle.
|
||||
* \see radius()
|
||||
*/
|
||||
double apothem() const { return mRadius * cos( M_PI / mNumberSides ); }
|
||||
|
||||
/** Returns the number of sides of the regular polygon.
|
||||
* \see setNumberSides()
|
||||
*/
|
||||
int numberSides() const { return mNumberSides; }
|
||||
|
||||
/** Sets the center point.
|
||||
* Radius is unchanged. The first vertex is reprojected from the new center.
|
||||
* \see center()
|
||||
*/
|
||||
void setCenter( const QgsPointV2 ¢er );
|
||||
|
||||
/** Sets the radius.
|
||||
* Center is unchanged. The first vertex is reprojected from the center with the new radius.
|
||||
* \see radius()
|
||||
*/
|
||||
void setRadius( const double radius );
|
||||
|
||||
/** Sets the first vertex.
|
||||
* Radius is unchanged. The center is reprojected from the new first vertex.
|
||||
* \see firstVertex()
|
||||
*/
|
||||
void setFirstVertex( const QgsPointV2 &firstVertex );
|
||||
|
||||
/** Sets the number of sides.
|
||||
* If numberSides < 3, the number of sides is unchanged.
|
||||
* \see numberSides()
|
||||
*/
|
||||
void setNumberSides( const int numberSides );
|
||||
|
||||
/** Returns a list including the vertices of the regular polygon.
|
||||
*/
|
||||
QgsPointSequence points( ) const;
|
||||
|
||||
/** Returns as a polygon.
|
||||
*/
|
||||
QgsPolygonV2 *toPolygon( ) const SIP_FACTORY;
|
||||
|
||||
/** Returns as a linestring.
|
||||
*/
|
||||
QgsLineString *toLineString( ) const SIP_FACTORY;
|
||||
|
||||
/** Returns as a triangle.
|
||||
* An empty triangle is returned if the regular polygon is empty or if the number of sides is different from 3.
|
||||
*/
|
||||
QgsTriangle toTriangle( ) const;
|
||||
|
||||
/** Returns a triangulation (vertices from sides to the center) of the regular polygon.
|
||||
* An empty list is returned if the regular polygon is empty.
|
||||
*/
|
||||
QList<QgsTriangle> triangulate( ) const;
|
||||
|
||||
/** Returns the inscribed circle
|
||||
*/
|
||||
QgsCircle inscribedCircle( ) const;
|
||||
|
||||
/** Returns the circumscribed circle
|
||||
*/
|
||||
QgsCircle circumscribedCircle( ) const;
|
||||
|
||||
/**
|
||||
* Returns a string representation of the regular polygon.
|
||||
* Members will be truncated to the specified precision.
|
||||
*/
|
||||
QString toString( int pointPrecision = 17, int radiusPrecision = 17, int anglePrecision = 2 ) const;
|
||||
|
||||
/** Returns the measure of the interior angles in degrees.
|
||||
*/
|
||||
double interiorAngle( ) const;
|
||||
|
||||
/** Returns the measure of the central angle (the angle subtended at the center of the polygon by one of its sides) in degrees.
|
||||
*/
|
||||
double centralAngle( ) const;
|
||||
|
||||
/** Returns the area.
|
||||
* Returns 0 if the regular polygon is empty.
|
||||
*/
|
||||
double area( ) const;
|
||||
|
||||
/** Returns the perimeter.
|
||||
* Returns 0 if the regular polygon is empty.
|
||||
*/
|
||||
double perimeter( ) const;
|
||||
|
||||
/** Returns the length of a side.
|
||||
* Returns 0 if the regular polygon is empty.
|
||||
*/
|
||||
double length( ) const;
|
||||
|
||||
private:
|
||||
QgsPointV2 mCenter;
|
||||
QgsPointV2 mFirstVertex;
|
||||
unsigned int mNumberSides;
|
||||
double mRadius;
|
||||
|
||||
/** Convenient method to convert an apothem to a radius.
|
||||
*/
|
||||
double apothemToRadius( const double apothem, const unsigned int numberSides ) const;
|
||||
|
||||
/** Convenient method for interiorAngle used by constructors.
|
||||
*/
|
||||
double interiorAngle( const unsigned int nbSides ) const;
|
||||
|
||||
/** Convenient method for centralAngle used by constructors.
|
||||
*/
|
||||
double centralAngle( const unsigned int nbSides ) const;
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSREGULARPOLYGON_H
|
@ -85,6 +85,28 @@ QgsTriangle::QgsTriangle( const QPointF p1, const QPointF p2, const QPointF p3 )
|
||||
setExteriorRing( ext );
|
||||
}
|
||||
|
||||
bool QgsTriangle::operator==( const QgsTriangle &other ) const
|
||||
{
|
||||
if ( isEmpty() && other.isEmpty() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if ( isEmpty() || other.isEmpty() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ( ( vertexAt( 0 ) == other.vertexAt( 0 ) ) &&
|
||||
( vertexAt( 1 ) == other.vertexAt( 1 ) ) &&
|
||||
( vertexAt( 2 ) == other.vertexAt( 2 ) )
|
||||
);
|
||||
}
|
||||
|
||||
bool QgsTriangle::operator!=( const QgsTriangle &other ) const
|
||||
{
|
||||
return !operator==( other );
|
||||
}
|
||||
|
||||
void QgsTriangle::clear()
|
||||
{
|
||||
QgsCurvePolygon::clear();
|
||||
|
@ -58,8 +58,8 @@ class CORE_EXPORT QgsTriangle : public QgsPolygonV2
|
||||
*/
|
||||
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;
|
||||
bool operator==( const QgsTriangle &other ) const;
|
||||
bool operator!=( const QgsTriangle &other ) const;
|
||||
|
||||
virtual QString geometryType() const override { return QStringLiteral( "Triangle" ); }
|
||||
virtual QgsTriangle *clone() const override SIP_FACTORY;
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "qgstriangle.h"
|
||||
#include "qgscircle.h"
|
||||
#include "qgsellipse.h"
|
||||
#include "qgsregularpolygon.h"
|
||||
#include "qgsmultipoint.h"
|
||||
#include "qgsmultilinestring.h"
|
||||
#include "qgscurvepolygon.h"
|
||||
@ -2270,6 +2271,45 @@ static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionC
|
||||
return QVariant::fromValue( QgsGeometry( elp.toPolygon( segment ) ) );
|
||||
}
|
||||
|
||||
static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent )
|
||||
{
|
||||
|
||||
QgsGeometry pt1 = getGeometry( values.at( 0 ), parent );
|
||||
if ( pt1.isNull() )
|
||||
return QVariant();
|
||||
|
||||
if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
|
||||
return QVariant();
|
||||
|
||||
QgsGeometry pt2 = getGeometry( values.at( 1 ), parent );
|
||||
if ( pt2.isNull() )
|
||||
return QVariant();
|
||||
|
||||
if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
|
||||
return QVariant();
|
||||
|
||||
unsigned int nbEdges = static_cast<unsigned int>( getIntValue( values.at( 2 ), parent ) );
|
||||
if ( nbEdges < 3 )
|
||||
{
|
||||
parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( getIntValue( values.at( 3 ), parent ) );
|
||||
if ( ( option < QgsRegularPolygon::InscribedCircle ) || ( option > QgsRegularPolygon::CircumscribedCircle ) )
|
||||
{
|
||||
parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
|
||||
return QVariant();
|
||||
}
|
||||
QgsPointV2 *center = static_cast< QgsPointV2 * >( pt1.geometry() );
|
||||
QgsPointV2 *corner = static_cast< QgsPointV2 * >( pt2.geometry() );
|
||||
|
||||
QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
|
||||
|
||||
return QVariant::fromValue( QgsGeometry( rp.toPolygon( ) ) );
|
||||
|
||||
}
|
||||
|
||||
static QVariant pointAt( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) // helper function
|
||||
{
|
||||
FEAT_FROM_CONTEXT( context, f );
|
||||
@ -4132,11 +4172,17 @@ const QList<QgsExpression::Function *> &QgsExpression::Functions()
|
||||
fcnMakeCircle, QStringLiteral( "GeometryGroup" ) )
|
||||
<< new StaticFunction( QStringLiteral( "make_ellipse" ), ParameterList()
|
||||
<< Parameter( QStringLiteral( "geometry" ) )
|
||||
<< Parameter( QStringLiteral( "semi-major axis" ) )
|
||||
<< Parameter( QStringLiteral( "semi-minor axis" ) )
|
||||
<< Parameter( QStringLiteral( "semi_major_axis" ) )
|
||||
<< Parameter( QStringLiteral( "semi_minor_axis" ) )
|
||||
<< Parameter( QStringLiteral( "azimuth" ) )
|
||||
<< Parameter( QStringLiteral( "segments" ), true, 36 ),
|
||||
fcnMakeEllipse, QStringLiteral( "GeometryGroup" ) );
|
||||
fcnMakeEllipse, QStringLiteral( "GeometryGroup" ) )
|
||||
<< new StaticFunction( QStringLiteral( "make_regular_polygon" ), ParameterList()
|
||||
<< Parameter( QStringLiteral( "geometry" ) )
|
||||
<< Parameter( QStringLiteral( "geometry" ) )
|
||||
<< Parameter( QStringLiteral( "number_sides" ) )
|
||||
<< Parameter( QStringLiteral( "circle" ), true, 0 ),
|
||||
fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) );
|
||||
|
||||
StaticFunction *xAtFunc = new StaticFunction( QStringLiteral( "$x_at" ), 1, fcnXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "xat" ) << QStringLiteral( "x_at" ) );
|
||||
xAtFunc->setIsStatic( false );
|
||||
|
@ -784,6 +784,15 @@ class TestQgsExpression: public QObject
|
||||
QTest::newRow( "make_ellipse null" ) << "make_ellipse(NULL, 5, 2, 0)" << false << QVariant();
|
||||
QTest::newRow( "make_ellipse bad" ) << "make_ellipse(make_line(make_point(1,2), make_point(3,4)), 5, 2, 0)" << false << QVariant();
|
||||
QTest::newRow( "make_ellipse" ) << "geom_to_wkt(make_ellipse(make_point(10,10), 5, 2, 90, 4))" << false << QVariant( "Polygon ((15 10, 10 8, 5 10, 10 12, 15 10))" );
|
||||
QTest::newRow( "make_regular_polygon not geom (center)" ) << "make_regular_polygon('a', make_point(0,5), 5)" << true << QVariant();
|
||||
QTest::newRow( "make_regular_polygon not geom (vertice)" ) << "make_regular_polygon(make_point(0,0), 'a', 5)" << true << QVariant();
|
||||
QTest::newRow( "make_regular_polygon bad (center)" ) << "make_regular_polygon(make_line(make_point(1,2), make_point(3,4)), make_point(0,5), 5)" << false << QVariant();
|
||||
QTest::newRow( "make_regular_polygon bad (vertice)" ) << "make_regular_polygon(make_point(0,0), make_line(make_point(1,2), make_point(3,4)), 5)" << false << QVariant();
|
||||
QTest::newRow( "make_regular_polygon bad (numEdges < 3)" ) << "make_regular_polygon(make_point(0,0), make_point(0,5), 2)" << true << QVariant();
|
||||
QTest::newRow( "make_regular_polygon bad (invalid option)" ) << "make_regular_polygon(make_point(0,0), make_point(0,5), 5, 5)" << true << QVariant();
|
||||
QTest::newRow( "make_regular_polygon bad (numEdges < 3)" ) << "make_regular_polygon(make_point(0,0), make_point(0,5), 2)" << true << QVariant();
|
||||
QTest::newRow( "make_regular_polygon" ) << "geom_to_wkt(make_regular_polygon(make_point(0,0), make_point(0,5), 5), 2)" << false << QVariant( "Polygon ((0 5, 4.76 1.55, 2.94 -4.05, -2.94 -4.05, -4.76 1.55, 0 5))" );
|
||||
QTest::newRow( "make_regular_polygon" ) << "geom_to_wkt(make_regular_polygon(make_point(0,0), project(make_point(0,0), 4.0451, radians(36)), 5, 1), 2)" << false << QVariant( "Polygon ((0 5, 4.76 1.55, 2.94 -4.05, -2.94 -4.05, -4.76 1.55, 0 5))" );
|
||||
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 );
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "qgstriangle.h"
|
||||
#include "qgscircle.h"
|
||||
#include "qgsellipse.h"
|
||||
#include "qgsregularpolygon.h"
|
||||
#include "qgsmultipoint.h"
|
||||
#include "qgsmultilinestring.h"
|
||||
#include "qgsmultipolygon.h"
|
||||
@ -76,6 +77,7 @@ class TestQgsGeometry : public QObject
|
||||
void triangle();
|
||||
void circle();
|
||||
void ellipse();
|
||||
void regularPolygon();
|
||||
void compoundCurve(); //test QgsCompoundCurve
|
||||
void multiPoint();
|
||||
void multiLineString();
|
||||
@ -3492,6 +3494,14 @@ void TestQgsGeometry::triangle()
|
||||
QVERIFY( t3.exteriorRing() );
|
||||
QVERIFY( !t3.interiorRing( 0 ) );
|
||||
|
||||
// equality
|
||||
QVERIFY( QgsTriangle() == QgsTriangle( ) ); // empty
|
||||
QVERIFY( QgsTriangle() == QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 5 ), QgsPointV2( 0, 10 ) ) ); // empty
|
||||
QVERIFY( QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 5 ), QgsPointV2( 0, 10 ) ) == QgsTriangle() ); // empty
|
||||
QVERIFY( QgsTriangle() != QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 5, 5 ), QgsPointV2( 0, 10 ) ) );
|
||||
QVERIFY( QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 5, 5 ), QgsPointV2( 0, 10 ) ) != QgsTriangle() );
|
||||
QVERIFY( QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 5, 5 ), QgsPointV2( 0, 10 ) ) != QgsTriangle( QgsPointV2( 0, 10 ), QgsPointV2( 5, 5 ), QgsPointV2( 0, 0 ) ) );
|
||||
|
||||
// clone
|
||||
QgsTriangle *t4 = t3.clone();
|
||||
QCOMPARE( t3, *t4 );
|
||||
@ -3918,7 +3928,7 @@ void TestQgsGeometry::ellipse()
|
||||
//test conversion
|
||||
// points
|
||||
QgsPointSequence pts;
|
||||
QgsEllipse( QgsPointV2( 0, 0 ), 5, 2, 0 ).points( pts, 4 );
|
||||
pts = QgsEllipse( QgsPointV2( 0, 0 ), 5, 2, 0 ).points( 4 );
|
||||
q = QgsEllipse( QgsPointV2( 0, 0 ), 5, 2, 0 ).quadrant();
|
||||
QCOMPARE( pts.length(), 4 );
|
||||
QGSCOMPARENEARPOINT( q.at( 0 ), pts.at( 0 ), 2 );
|
||||
@ -4204,6 +4214,169 @@ void TestQgsGeometry::circle()
|
||||
QGSCOMPARENEAR( 31.4159, QgsCircle( QgsPointV2( 0, 0 ), 5 ).perimeter(), 0.0001 );
|
||||
}
|
||||
|
||||
void TestQgsGeometry::regularPolygon()
|
||||
{
|
||||
// constructors
|
||||
QgsRegularPolygon rp1 = QgsRegularPolygon();
|
||||
QCOMPARE( rp1.center(), QgsPointV2() );
|
||||
QCOMPARE( rp1.firstVertex(), QgsPointV2() );
|
||||
QCOMPARE( rp1.numberSides(), 0 );
|
||||
QCOMPARE( rp1.radius(), 0.0 );
|
||||
QVERIFY( rp1.isEmpty() );
|
||||
|
||||
QgsRegularPolygon rp2;
|
||||
QgsRegularPolygon( QgsPointV2(), 5, 0, 2, QgsRegularPolygon::InscribedCircle );
|
||||
QVERIFY( rp2.isEmpty() );
|
||||
QgsRegularPolygon( QgsPointV2(), 5, 0, 5, static_cast< QgsRegularPolygon::ConstructionOption >( 4 ) );
|
||||
QVERIFY( rp2.isEmpty() );
|
||||
|
||||
rp2 = QgsRegularPolygon( QgsPointV2(), 5, 0, 5, QgsRegularPolygon::InscribedCircle );
|
||||
QVERIFY( !rp2.isEmpty() );
|
||||
QCOMPARE( rp2.center(), QgsPointV2() );
|
||||
QCOMPARE( rp2.firstVertex(), QgsPointV2( 0, 5 ) );
|
||||
QCOMPARE( rp2.numberSides(), 5 );
|
||||
QCOMPARE( rp2.radius(), 5.0 );
|
||||
QGSCOMPARENEAR( rp2.apothem(), 4.0451, 10E-4 );
|
||||
QVERIFY( rp2 == QgsRegularPolygon( QgsPointV2(), -5, 0, 5, QgsRegularPolygon::InscribedCircle ) );
|
||||
|
||||
QgsRegularPolygon rp3 = QgsRegularPolygon( QgsPointV2(), rp2.apothem(), 36.0, 5, QgsRegularPolygon::CircumscribedCircle );
|
||||
QVERIFY( rp2 == rp3 );
|
||||
QVERIFY( rp2 == QgsRegularPolygon( QgsPointV2(), -rp2.apothem(), 36.0, 5, QgsRegularPolygon::CircumscribedCircle ) );
|
||||
QVERIFY( rp1 != rp3 );
|
||||
QVERIFY( rp1 != QgsRegularPolygon( QgsPointV2( 5, 5 ), rp2.apothem(), 36.0, 5, QgsRegularPolygon::CircumscribedCircle ) );
|
||||
QVERIFY( rp1 != QgsRegularPolygon( QgsPointV2( 0, 0 ), 5, 36.0, 5, QgsRegularPolygon::CircumscribedCircle ) );
|
||||
QVERIFY( rp1 != QgsRegularPolygon( QgsPointV2( 0, 0 ), 5, 36.0, 5, QgsRegularPolygon::InscribedCircle ) );
|
||||
|
||||
QgsRegularPolygon rp4 = QgsRegularPolygon( QgsPointV2(), QgsPointV2( 0, 5 ), 2, QgsRegularPolygon::InscribedCircle );
|
||||
QVERIFY( rp4.isEmpty() );
|
||||
rp4 = QgsRegularPolygon( QgsPointV2(), QgsPointV2( 0, 5 ), 5, static_cast< QgsRegularPolygon::ConstructionOption >( 4 ) );
|
||||
QVERIFY( rp4.isEmpty() );
|
||||
rp4 = QgsRegularPolygon( QgsPointV2(), QgsPointV2( 0, 5 ), 5, QgsRegularPolygon::InscribedCircle );
|
||||
QVERIFY( rp4 == rp2 );
|
||||
|
||||
QgsRegularPolygon rp5 = QgsRegularPolygon( QgsPointV2(), QgsPointV2( 0, 0 ).project( rp2.apothem(), 36.0 ), 2, QgsRegularPolygon::CircumscribedCircle );
|
||||
QVERIFY( rp5.isEmpty() );
|
||||
rp5 = QgsRegularPolygon( QgsPointV2(), QgsPointV2( 0, 0 ).project( rp2.apothem(), 36.0 ), 5, static_cast< QgsRegularPolygon::ConstructionOption >( 4 ) );
|
||||
QVERIFY( rp5.isEmpty() );
|
||||
rp5 = QgsRegularPolygon( QgsPointV2(), QgsPointV2( 0, 0 ).project( rp2.apothem(), 36.0 ), 5, QgsRegularPolygon::CircumscribedCircle );
|
||||
QVERIFY( rp5 == rp2 );
|
||||
|
||||
QgsRegularPolygon rp6 = QgsRegularPolygon( QgsPointV2( 0, 5 ), QgsPointV2( 0, 0 ).project( 5.0, 72 ), 5 );
|
||||
QVERIFY( rp6 == rp2 );
|
||||
|
||||
|
||||
// setters and getters
|
||||
QgsRegularPolygon rp7 = QgsRegularPolygon();
|
||||
|
||||
rp7.setCenter( QgsPointV2( 5, 5 ) );
|
||||
QVERIFY( rp7.isEmpty() );
|
||||
QCOMPARE( rp7.center(), QgsPointV2( 5, 5 ) );
|
||||
|
||||
rp7.setNumberSides( 2 );
|
||||
QVERIFY( rp7.isEmpty() );
|
||||
QCOMPARE( rp7.numberSides(), 0 );
|
||||
rp7.setNumberSides( 5 );
|
||||
QVERIFY( rp7.isEmpty() );
|
||||
QCOMPARE( rp7.numberSides(), 5 );
|
||||
rp7.setNumberSides( 2 );
|
||||
QVERIFY( rp7.isEmpty() );
|
||||
QCOMPARE( rp7.numberSides(), 5 );
|
||||
rp7.setNumberSides( 3 );
|
||||
QVERIFY( rp7.isEmpty() );
|
||||
QCOMPARE( rp7.numberSides(), 3 );
|
||||
|
||||
rp7.setRadius( -6 );
|
||||
QVERIFY( !rp7.isEmpty() );
|
||||
QCOMPARE( rp7.radius(), 6.0 );
|
||||
QCOMPARE( rp7.firstVertex(), rp7.center().project( 6, 0 ) );
|
||||
|
||||
rp7.setFirstVertex( QgsPointV2( 4, 4 ) );
|
||||
QCOMPARE( rp7.firstVertex(), QgsPointV2( 4, 4 ) );
|
||||
QCOMPARE( rp7.radius(), rp7.center().distance3D( QgsPointV2( 4, 4 ) ) );
|
||||
|
||||
rp7 = QgsRegularPolygon( QgsPointV2(), QgsPointV2( 0, 5 ), 5, QgsRegularPolygon::InscribedCircle );
|
||||
rp7.setCenter( QgsPointV2( 5, 5 ) );
|
||||
QCOMPARE( rp7.radius(), 5.0 );
|
||||
QCOMPARE( rp7.firstVertex(), QgsPointV2( 5, 10 ) );
|
||||
rp7.setNumberSides( 3 );
|
||||
QCOMPARE( rp7.radius(), 5.0 );
|
||||
QCOMPARE( rp7.firstVertex(), QgsPointV2( 5, 10 ) );
|
||||
rp7.setNumberSides( 2 );
|
||||
QCOMPARE( rp7.radius(), 5.0 );
|
||||
QCOMPARE( rp7.firstVertex(), QgsPointV2( 5, 10 ) );
|
||||
|
||||
// measures
|
||||
QGSCOMPARENEAR( rp1.length(), 0.0, 10e-4 );
|
||||
QGSCOMPARENEAR( rp1.area(), 0.0, 10e-4 );
|
||||
QGSCOMPARENEAR( rp1.perimeter(), 0.0, 10e-4 );
|
||||
QGSCOMPARENEAR( rp2.length(), 5.8779, 10e-4 );
|
||||
QGSCOMPARENEAR( rp2.area(), 59.4410, 10e-4 );
|
||||
QGSCOMPARENEAR( rp2.perimeter(), 29.3893, 10e-4 );
|
||||
QCOMPARE( rp2.interiorAngle(), 108.0 );
|
||||
QCOMPARE( rp2.centralAngle(), 72.0 );
|
||||
QgsRegularPolygon rp8 = QgsRegularPolygon( QgsPointV2( 0, 0 ), QgsPointV2( 5, 0 ), 5 );
|
||||
QGSCOMPARENEAR( rp8.area(), 43.0119, 10e-4 );
|
||||
QCOMPARE( rp8.perimeter(), 25.0 );
|
||||
QCOMPARE( rp8.length(), 5.0 );
|
||||
QCOMPARE( rp8.interiorAngle(), 108.0 );
|
||||
QCOMPARE( rp8.centralAngle(), 72.0 );
|
||||
rp8.setNumberSides( 4 );
|
||||
QCOMPARE( rp8.interiorAngle(), 90.0 );
|
||||
QCOMPARE( rp8.centralAngle(), 90.0 );
|
||||
rp8.setNumberSides( 3 );
|
||||
QCOMPARE( rp8.interiorAngle(), 60.0 );
|
||||
QCOMPARE( rp8.centralAngle(), 120.0 );
|
||||
|
||||
|
||||
//test conversions
|
||||
// circle
|
||||
QVERIFY( QgsCircle( QgsPointV2( 0, 0 ), 5 ) == rp2.circumscribedCircle() );
|
||||
QVERIFY( rp2.inscribedCircle() == QgsRegularPolygon( QgsPointV2( 0, 0 ), rp2.apothem(), 36.0, 5, QgsRegularPolygon::InscribedCircle ).circumscribedCircle() );
|
||||
|
||||
// triangle
|
||||
QCOMPARE( QgsTriangle(), rp2.toTriangle() );
|
||||
QCOMPARE( QgsTriangle(), QgsRegularPolygon().toTriangle() );
|
||||
QgsRegularPolygon rp9 = QgsRegularPolygon( QgsPointV2( 0, 0 ), 5, 0, 3, QgsRegularPolygon::InscribedCircle );
|
||||
|
||||
QVERIFY( QgsCircle( QgsPointV2( 0, 0 ), 5 ) == rp9.toTriangle().circumscribedCircle() );
|
||||
|
||||
QgsRegularPolygon rp10 = QgsRegularPolygon( QgsPointV2( 0, 0 ), QgsPointV2( 0, 4 ), 4 );
|
||||
QList<QgsTriangle> rp10_tri = rp10.triangulate();
|
||||
QCOMPARE( rp10_tri.length(), ( int )rp10.numberSides() );
|
||||
QVERIFY( rp10_tri.at( 0 ) == QgsTriangle( QgsPointV2( 0, 0 ), QgsPointV2( 0, 4 ), rp10.center() ) );
|
||||
QVERIFY( rp10_tri.at( 1 ) == QgsTriangle( QgsPointV2( 0, 4 ), QgsPointV2( 4, 4 ), rp10.center() ) );
|
||||
QVERIFY( rp10_tri.at( 2 ) == QgsTriangle( QgsPointV2( 4, 4 ), QgsPointV2( 4, 0 ), rp10.center() ) );
|
||||
QVERIFY( rp10_tri.at( 3 ) == QgsTriangle( QgsPointV2( 4, 0 ), QgsPointV2( 0, 0 ), rp10.center() ) );
|
||||
|
||||
// polygon
|
||||
QgsPointSequence ptsPol;
|
||||
std::unique_ptr< QgsPolygonV2 > pol( new QgsPolygonV2() );
|
||||
pol.reset( rp10.toPolygon( ) );
|
||||
QCOMPARE( pol->numInteriorRings(), 0 );
|
||||
QCOMPARE( pol->exteriorRing()->numPoints(), 5 );
|
||||
|
||||
pol->exteriorRing()->points( ptsPol );
|
||||
QCOMPARE( ptsPol.length(), 5 );
|
||||
QVERIFY( ptsPol.at( 0 ) == QgsPointV2( 0, 0 ) );
|
||||
QVERIFY( ptsPol.at( 1 ) == QgsPointV2( 0, 4 ) );
|
||||
QVERIFY( ptsPol.at( 2 ) == QgsPointV2( 4, 4 ) );
|
||||
QVERIFY( ptsPol.at( 3 ) == QgsPointV2( 4, 0 ) );
|
||||
QVERIFY( ptsPol.at( 4 ) == QgsPointV2( 0, 0 ) );
|
||||
ptsPol.pop_back();
|
||||
|
||||
std::unique_ptr< QgsLineString > l( new QgsLineString() );
|
||||
l.reset( rp10.toLineString( ) );
|
||||
QCOMPARE( l->numPoints(), 4 );
|
||||
QgsPointSequence pts_l;
|
||||
l->points( pts_l );
|
||||
QCOMPARE( ptsPol, pts_l );
|
||||
|
||||
//test toString
|
||||
QCOMPARE( rp1.toString(), QString( "Empty" ) );
|
||||
QCOMPARE( rp2.toString(), QString( "RegularPolygon (Center: Point (0 0), First Vertex: Point (0 5), Radius: 5, Azimuth: 0)" ) );
|
||||
|
||||
}
|
||||
|
||||
void TestQgsGeometry::compoundCurve()
|
||||
{
|
||||
//test that area of a compound curve ring is equal to a closed linestring with the same vertices
|
||||
|
Loading…
x
Reference in New Issue
Block a user