QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsellipse.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsellipse.cpp
3 --------------
4 begin : March 2017
5 copyright : (C) 2017 by Loîc Bartoletti
6 email : lbartoletti at tuxfamily dot org
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgsunittypes.h"
19#include "qgslinestring.h"
20#include "qgsellipse.h"
21#include "qgsgeometryutils.h"
22
23#include <memory>
24#include <limits>
25
26void QgsEllipse::normalizeAxis()
27{
28 mSemiMajorAxis = std::fabs( mSemiMajorAxis );
29 mSemiMinorAxis = std::fabs( mSemiMinorAxis );
31 {
32 std::swap( mSemiMajorAxis, mSemiMinorAxis );
33 mAzimuth = 180.0 / M_PI *
34 QgsGeometryUtilsBase::normalizedAngle( M_PI / 180.0 * ( mAzimuth + 90 ) );
35 }
36}
37
38QgsEllipse::QgsEllipse( const QgsPoint &center, const double axis_a, const double axis_b, const double azimuth )
39 : mCenter( center )
40 , mSemiMajorAxis( axis_a )
41 , mSemiMinorAxis( axis_b )
42 , mAzimuth( azimuth )
43{
44 normalizeAxis();
45}
46
47QgsEllipse QgsEllipse::fromFoci( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3 )
48{
49 const double dist_p1p2 = pt1.distance( pt2 );
50 const double dist_p1p3 = pt1.distance( pt3 );
51 const double dist_p2p3 = pt2.distance( pt3 );
52
53 const double dist = dist_p1p3 + dist_p2p3;
54 const double azimuth = 180.0 / M_PI * QgsGeometryUtilsBase::lineAngle( pt1.x(), pt1.y(), pt2.x(), pt2.y() );
56
57 const double axis_a = dist / 2.0;
58 const double axis_b = std::sqrt( std::pow( axis_a, 2.0 ) - std::pow( dist_p1p2 / 2.0, 2.0 ) );
59
61
62 return QgsEllipse( center, axis_a, axis_b, azimuth );
63}
64
66{
68 const double axis_a = std::fabs( pt2.x() - pt1.x() ) / 2.0;
69 const double axis_b = std::fabs( pt2.y() - pt1.y() ) / 2.0;
70 const double azimuth = 90.0;
71
73
74 return QgsEllipse( center, axis_a, axis_b, azimuth );
75}
76
78{
79 const double axis_a = std::fabs( pt1.x() - center.x() );
80 const double axis_b = std::fabs( pt1.y() - center.y() );
81 const double azimuth = 90.0;
82
83 QgsPoint centerPt( center );
85
86 return QgsEllipse( centerPt, axis_a, axis_b, azimuth );
87}
88
89QgsEllipse QgsEllipse::fromCenter2Points( const QgsPoint &center, const QgsPoint &pt1, const QgsPoint &pt2 )
90{
91 const double azimuth = 180.0 / M_PI * QgsGeometryUtilsBase::lineAngle( center.x(), center.y(), pt1.x(), pt1.y() );
92 const double axis_a = center.distance( pt1 );
93
94 const double length = pt2.distance( QgsGeometryUtils::projectPointOnSegment( pt2, center, pt1 ) );
95 const QgsPoint pp = center.project( length, 90 + azimuth );
96 const double axis_b = center.distance( pp );
97
98 QgsPoint centerPt( center );
100
101 return QgsEllipse( centerPt, axis_a, axis_b, azimuth );
102}
103
104bool QgsEllipse::operator ==( const QgsEllipse &elp ) const
105{
106 return ( ( mCenter == elp.mCenter ) &&
109 qgsDoubleNear( mAzimuth, elp.mAzimuth, 1E-8 )
110 );
111}
112
113bool QgsEllipse::operator !=( const QgsEllipse &elp ) const
114{
115 return !operator==( elp );
116}
117
119{
120 return ( qgsDoubleNear( mSemiMajorAxis, 0.0, 1E-8 ) ||
121 qgsDoubleNear( mSemiMinorAxis, 0.0, 1E-8 ) );
122}
123
124void QgsEllipse::setSemiMajorAxis( const double axis_a )
125{
126 mSemiMajorAxis = axis_a;
127 normalizeAxis();
128}
129void QgsEllipse::setSemiMinorAxis( const double axis_b )
130{
131 mSemiMinorAxis = axis_b;
132 normalizeAxis();
133}
134
135void QgsEllipse::setAzimuth( const double azimuth )
136{
137 mAzimuth = 180.0 / M_PI *
139}
140
142{
144}
145
146QVector<QgsPoint> QgsEllipse::foci() const
147{
148 QVector<QgsPoint> f;
149 const double dist_focus = focusDistance();
150 f.append( mCenter.project( dist_focus, mAzimuth ) );
151 f.append( mCenter.project( -dist_focus, mAzimuth ) );
152
153 return f;
154}
155
157{
158 if ( isEmpty() )
159 {
160 return std::numeric_limits<double>::quiet_NaN();
161 }
162 return focusDistance() / mSemiMajorAxis;
163}
164
165double QgsEllipse::area() const
166{
167 return M_PI * mSemiMajorAxis * mSemiMinorAxis;
168}
169
171{
172 const double a = mSemiMajorAxis;
173 const double b = mSemiMinorAxis;
174 return M_PI * ( 3 * ( a + b ) - std::sqrt( 10 * a * b + 3 * ( a * a + b * b ) ) );
175}
176
177QVector<QgsPoint> QgsEllipse::quadrant() const
178{
179 QVector<QgsPoint> quad;
180 quad.append( mCenter.project( mSemiMajorAxis, mAzimuth ) );
181 quad.append( mCenter.project( mSemiMinorAxis, mAzimuth + 90 ) );
182 quad.append( mCenter.project( -mSemiMajorAxis, mAzimuth ) );
183 quad.append( mCenter.project( -mSemiMinorAxis, mAzimuth + 90 ) );
184
185 return quad;
186}
187
188QgsPointSequence QgsEllipse::points( unsigned int segments ) const
189{
191
192 if ( segments < 3 )
193 {
194 return pts;
195 }
196
197
198 const Qgis::WkbType pType( mCenter.wkbType() );
199 const double z = mCenter.z();
200 const double m = mCenter.m();
201
202 QVector<double> t;
203 t.reserve( segments );
204 const double azimuth = std::atan2( quadrant().at( 0 ).y() - mCenter.y(), quadrant().at( 0 ).x() - mCenter.x() );
205 for ( unsigned int i = 0; i < segments; ++i )
206 {
207 t.append( 2 * M_PI - ( ( 2 * M_PI ) / segments * i ) ); // Since the algorithm used rotates in the trigonometric direction (counterclockwise)
208 }
209
210 for ( QVector<double>::const_iterator it = t.constBegin(); it != t.constEnd(); ++it )
211 {
212 const double x = mCenter.x() +
213 mSemiMajorAxis * std::cos( *it ) * std::cos( azimuth ) -
214 mSemiMinorAxis * std::sin( *it ) * std::sin( azimuth );
215 const double y = mCenter.y() +
216 mSemiMajorAxis * std::cos( *it ) * std::sin( azimuth ) +
217 mSemiMinorAxis * std::sin( *it ) * std::cos( azimuth );
218 pts.push_back( QgsPoint( pType, x, y, z, m ) );
219 }
220
221 return pts;
222}
223
224QgsPolygon *QgsEllipse::toPolygon( unsigned int segments ) const
225{
226 std::unique_ptr<QgsPolygon> polygon( new QgsPolygon() );
227 if ( segments < 3 )
228 {
229 return polygon.release();
230 }
231
232 polygon->setExteriorRing( toLineString( segments ) );
233
234 return polygon.release();
235}
236
237QgsLineString *QgsEllipse::toLineString( unsigned int segments ) const
238{
239 std::unique_ptr<QgsLineString> ext( new QgsLineString() );
240 if ( segments < 3 )
241 {
242 return ext.release();
243 }
244
246 pts = points( segments );
247 pts.append( pts.at( 0 ) ); // close linestring
248
249 ext->setPoints( pts );
250
251 return ext.release();
252}
253
255{
256 if ( isEmpty() )
257 {
258 return QgsRectangle();
259 }
260
261 const double angle = mAzimuth * M_PI / 180.0;
262
263 const double ux = mSemiMajorAxis * std::cos( angle );
264 const double uy = mSemiMinorAxis * std::sin( angle );
265 const double vx = mSemiMajorAxis * std::sin( angle );
266 const double vy = mSemiMinorAxis * std::cos( angle );
267
268 const double halfHeight = std::sqrt( ux * ux + uy * uy );
269 const double halfWidth = std::sqrt( vx * vx + vy * vy );
270
271 const QgsPointXY p1( mCenter.x() - halfWidth, mCenter.y() - halfHeight );
272 const QgsPointXY p2( mCenter.x() + halfWidth, mCenter.y() + halfHeight );
273
274 return QgsRectangle( p1, p2 );
275}
276
277QString QgsEllipse::toString( int pointPrecision, int axisPrecision, int azimuthPrecision ) const
278{
279 QString rep;
280 if ( isEmpty() )
281 rep = QStringLiteral( "Empty" );
282 else
283 rep = QStringLiteral( "Ellipse (Center: %1, Semi-Major Axis: %2, Semi-Minor Axis: %3, Azimuth: %4)" )
284 .arg( mCenter.asWkt( pointPrecision ), 0, 's' )
285 .arg( qgsDoubleToString( mSemiMajorAxis, axisPrecision ), 0, 'f' )
286 .arg( qgsDoubleToString( mSemiMinorAxis, axisPrecision ), 0, 'f' )
287 .arg( qgsDoubleToString( mAzimuth, azimuthPrecision ), 0, 'f' );
288
289 return rep;
290}
291
293{
294 std::unique_ptr<QgsPolygon> ombb( new QgsPolygon() );
295 if ( isEmpty() )
296 {
297 return ombb.release();
298 }
299
300 const QVector<QgsPoint> q = quadrant();
301
302 const QgsPoint p1 = q.at( 0 ).project( mSemiMinorAxis, mAzimuth - 90 );
303 const QgsPoint p2 = q.at( 0 ).project( mSemiMinorAxis, mAzimuth + 90 );
304 const QgsPoint p3 = q.at( 2 ).project( mSemiMinorAxis, mAzimuth + 90 );
305 const QgsPoint p4 = q.at( 2 ).project( mSemiMinorAxis, mAzimuth - 90 );
306
307 QgsLineString *ext = new QgsLineString();
308 ext->setPoints( QgsPointSequence() << p1 << p2 << p3 << p4 );
309
310 ombb->setExteriorRing( ext );
311
312 return ombb.release();
313}
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition: qgis.h:182
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
Ellipse geometry type.
Definition: qgsellipse.h:39
virtual double eccentricity() const
The eccentricity of the ellipse.
Definition: qgsellipse.cpp:156
QgsPoint mCenter
Definition: qgsellipse.h:251
virtual QgsPolygon * toPolygon(unsigned int segments=36) const
Returns a segmented polygon.
Definition: qgsellipse.cpp:224
virtual bool operator==(const QgsEllipse &elp) const
Definition: qgsellipse.cpp:104
QgsPoint center() const
Returns the center point.
Definition: qgsellipse.h:120
virtual QVector< QgsPoint > foci() const
Two foci of the ellipse.
Definition: qgsellipse.cpp:146
virtual QString toString(int pointPrecision=17, int axisPrecision=17, int azimuthPrecision=2) const
returns a string representation of the ellipse.
Definition: qgsellipse.cpp:277
static QgsEllipse fromCenterPoint(const QgsPoint &ptc, const QgsPoint &pt1)
Constructs an ellipse by a center point and a another point.
Definition: qgsellipse.cpp:77
double mAzimuth
Definition: qgsellipse.h:254
virtual QgsPointSequence points(unsigned int segments=36) const
Returns a list of points with segmentation from segments.
Definition: qgsellipse.cpp:188
double mSemiMajorAxis
Definition: qgsellipse.h:252
virtual QgsLineString * toLineString(unsigned int segments=36) const
Returns a segmented linestring.
Definition: qgsellipse.cpp:237
virtual double focusDistance() const
The distance between the center and each foci.
Definition: qgsellipse.cpp:141
virtual bool operator!=(const QgsEllipse &elp) const
Definition: qgsellipse.cpp:113
double azimuth() const
Returns the azimuth.
Definition: qgsellipse.h:138
virtual QVector< QgsPoint > quadrant() const
The four quadrants of the ellipse.
Definition: qgsellipse.cpp:177
QgsEllipse()=default
Constructor for QgsEllipse.
virtual double perimeter() const
The circumference of the ellipse using first approximation of Ramanujan.
Definition: qgsellipse.cpp:170
void setAzimuth(double azimuth)
Sets the azimuth (orientation).
Definition: qgsellipse.cpp:135
virtual QgsPolygon * orientedBoundingBox() const
Returns the oriented minimal bounding box for the ellipse.
Definition: qgsellipse.cpp:292
virtual void setSemiMinorAxis(double semiMinorAxis)
Sets the semi-minor axis.
Definition: qgsellipse.cpp:129
static QgsEllipse fromExtent(const QgsPoint &pt1, const QgsPoint &pt2)
Constructs an ellipse by an extent (aka bounding box / QgsRectangle).
Definition: qgsellipse.cpp:65
virtual double area() const
The area of the ellipse.
Definition: qgsellipse.cpp:165
virtual bool isEmpty() const
An ellipse is empty if axes are equal to 0.
Definition: qgsellipse.cpp:118
double mSemiMinorAxis
Definition: qgsellipse.h:253
static QgsEllipse fromFoci(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3)
Constructs an ellipse by foci (pt1 and pt2) and a point pt3.
Definition: qgsellipse.cpp:47
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the ellipse.
Definition: qgsellipse.cpp:254
virtual void setSemiMajorAxis(double semiMajorAxis)
Sets the semi-major axis.
Definition: qgsellipse.cpp:124
static QgsEllipse fromCenter2Points(const QgsPoint &ptc, const QgsPoint &pt1, const QgsPoint &pt2)
Constructs an ellipse by a central point and two other points.
Definition: qgsellipse.cpp:89
static double lineAngle(double x1, double y1, double x2, double y2)
Calculates the direction of line joining two points in radians, clockwise from the north direction.
static double normalizedAngle(double angle)
Ensures that an angle is in the range 0 <= angle < 2 pi.
static QgsPoint projectPointOnSegment(const QgsPoint &p, const QgsPoint &s1, const QgsPoint &s2)
Project the point on a segment.
static QgsPoint midpoint(const QgsPoint &pt1, const QgsPoint &pt2)
Returns a middle point between points pt1 and pt2.
static bool transferFirstZOrMValueToPoint(Iterator verticesBegin, Iterator verticesEnd, QgsPoint &point)
A Z or M dimension is added to point if one of the points in the list points contains Z or M value.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
void setPoints(size_t size, const double *x, const double *y, const double *z=nullptr, const double *m=nullptr)
Resets the line string to match the specified point data.
A class to represent a 2D point.
Definition: qgspointxy.h:60
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
Definition: qgspoint.cpp:261
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
double distance(double x, double y) const
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition: qgspoint.h:393
double m
Definition: qgspoint.h:55
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition: qgspoint.cpp:701
double y
Definition: qgspoint.h:53
Polygon geometry type.
Definition: qgspolygon.h:33
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:716
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:5124
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5207
QVector< QgsPoint > QgsPointSequence