QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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 "qgsellipse.h"
19
20#include <limits>
21#include <memory>
22
23#include "qgsgeometryutils.h"
24#include "qgslinestring.h"
25#include "qgsunittypes.h"
26
27void QgsEllipse::normalizeAxis()
28{
29 mSemiMajorAxis = std::fabs( mSemiMajorAxis );
30 mSemiMinorAxis = std::fabs( mSemiMinorAxis );
32 {
33 std::swap( mSemiMajorAxis, mSemiMinorAxis );
34 mAzimuth = 180.0 / M_PI *
35 QgsGeometryUtilsBase::normalizedAngle( M_PI / 180.0 * ( mAzimuth + 90 ) );
36 }
37}
38
39QgsEllipse::QgsEllipse( const QgsPoint &center, const double axis_a, const double axis_b, const double azimuth )
40 : mCenter( center )
41 , mSemiMajorAxis( axis_a )
42 , mSemiMinorAxis( axis_b )
43 , mAzimuth( azimuth )
44{
45 normalizeAxis();
46}
47
48QgsEllipse QgsEllipse::fromFoci( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3 )
49{
50 const double dist_p1p2 = pt1.distance( pt2 );
51 const double dist_p1p3 = pt1.distance( pt3 );
52 const double dist_p2p3 = pt2.distance( pt3 );
53
54 const double dist = dist_p1p3 + dist_p2p3;
55 const double azimuth = 180.0 / M_PI * QgsGeometryUtilsBase::lineAngle( pt1.x(), pt1.y(), pt2.x(), pt2.y() );
57
58 const double axis_a = dist / 2.0;
59 const double axis_b = std::sqrt( std::pow( axis_a, 2.0 ) - std::pow( dist_p1p2 / 2.0, 2.0 ) );
60
62
63 return QgsEllipse( center, axis_a, axis_b, azimuth );
64}
65
67{
69 const double axis_a = std::fabs( pt2.x() - pt1.x() ) / 2.0;
70 const double axis_b = std::fabs( pt2.y() - pt1.y() ) / 2.0;
71 const double azimuth = 90.0;
72
74
75 return QgsEllipse( center, axis_a, axis_b, azimuth );
76}
77
79{
80 const double axis_a = std::fabs( pt1.x() - center.x() );
81 const double axis_b = std::fabs( pt1.y() - center.y() );
82 const double azimuth = 90.0;
83
84 QgsPoint centerPt( center );
86
87 return QgsEllipse( centerPt, axis_a, axis_b, azimuth );
88}
89
91{
92 const double azimuth = 180.0 / M_PI * QgsGeometryUtilsBase::lineAngle( center.x(), center.y(), pt1.x(), pt1.y() );
93 const double axis_a = center.distance( pt1 );
94
95 const double length = pt2.distance( QgsGeometryUtils::projectPointOnSegment( pt2, center, pt1 ) );
96 const QgsPoint pp = center.project( length, 90 + azimuth );
97 const double axis_b = center.distance( pp );
98
99 QgsPoint centerPt( center );
101
102 return QgsEllipse( centerPt, axis_a, axis_b, azimuth );
103}
104
105bool QgsEllipse::operator ==( const QgsEllipse &elp ) const
106{
107 return ( ( mCenter == elp.mCenter ) &&
110 qgsDoubleNear( mAzimuth, elp.mAzimuth, 1E-8 )
111 );
112}
113
114bool QgsEllipse::operator !=( const QgsEllipse &elp ) const
115{
116 return !operator==( elp );
117}
118
120{
121 return ( qgsDoubleNear( mSemiMajorAxis, 0.0, 1E-8 ) ||
122 qgsDoubleNear( mSemiMinorAxis, 0.0, 1E-8 ) );
123}
124
125void QgsEllipse::setSemiMajorAxis( const double axis_a )
126{
127 mSemiMajorAxis = axis_a;
128 normalizeAxis();
129}
130void QgsEllipse::setSemiMinorAxis( const double axis_b )
131{
132 mSemiMinorAxis = axis_b;
133 normalizeAxis();
134}
135
136void QgsEllipse::setAzimuth( const double azimuth )
137{
138 mAzimuth = 180.0 / M_PI *
140}
141
143{
145}
146
147QVector<QgsPoint> QgsEllipse::foci() const
148{
149 const double dist_focus = focusDistance();
150 return
151 {
152 mCenter.project( dist_focus, mAzimuth ),
153 mCenter.project( -dist_focus, mAzimuth ),
154 };
155}
156
158{
159 if ( isEmpty() )
160 {
161 return std::numeric_limits<double>::quiet_NaN();
162 }
163 return focusDistance() / mSemiMajorAxis;
164}
165
166double QgsEllipse::area() const
167{
168 return M_PI * mSemiMajorAxis * mSemiMinorAxis;
169}
170
172{
173 const double a = mSemiMajorAxis;
174 const double b = mSemiMinorAxis;
175 return M_PI * ( 3 * ( a + b ) - std::sqrt( 10 * a * b + 3 * ( a * a + b * b ) ) );
176}
177
178QVector<QgsPoint> QgsEllipse::quadrant() const
179{
180 return
181 {
182 mCenter.project( mSemiMajorAxis, mAzimuth ),
183 mCenter.project( mSemiMinorAxis, mAzimuth + 90 ),
184 mCenter.project( -mSemiMajorAxis, mAzimuth ),
185 mCenter.project( -mSemiMinorAxis, mAzimuth + 90 )
186 };
187}
188
189QgsPointSequence QgsEllipse::points( unsigned int segments ) const
190{
192
193 QVector<double> x;
194 QVector<double> y;
195 QVector<double> z;
196 QVector<double> m;
197
198 pointsInternal( segments, x, y, z, m );
199 const bool hasZ = !z.empty();
200 const bool hasM = !m.empty();
201 pts.reserve( x.size() );
202 for ( int i = 0; i < x.size(); ++i )
203 {
204 pts.append( QgsPoint( x[i], y[i],
205 hasZ ? z[i] : std::numeric_limits< double >::quiet_NaN(),
206 hasM ? m[i] : std::numeric_limits< double >::quiet_NaN() ) );
207 }
208 return pts;
209}
210
211void QgsEllipse::pointsInternal( unsigned int segments, QVector<double> &x, QVector<double> &y, QVector<double> &z, QVector<double> &m ) const
212{
213 if ( segments < 3 )
214 {
215 return;
216 }
217
218 const double centerX = mCenter.x();
219 const double centerY = mCenter.y();
220 const double centerZ = mCenter.z();
221 const double centerM = mCenter.m();
222 const bool hasZ = mCenter.is3D();
223 const bool hasM = mCenter.isMeasure();
224
225 std::vector<double> t( segments );
226 const QgsPoint p1 = mCenter.project( mSemiMajorAxis, mAzimuth );
227 const double azimuth = std::atan2( p1.y() - mCenter.y(), p1.x() - mCenter.x() );
228 for ( unsigned int i = 0; i < segments; ++i )
229 {
230 t[i] = 2 * M_PI - ( ( 2 * M_PI ) / segments * i ); // Since the algorithm used rotates in the trigonometric direction (counterclockwise)
231 }
232
233 x.resize( segments );
234 y.resize( segments );
235 if ( hasZ )
236 z.resize( segments );
237 if ( hasM )
238 m.resize( segments );
239 double *xOut = x.data();
240 double *yOut = y.data();
241 double *zOut = hasZ ? z.data() : nullptr;
242 double *mOut = hasM ? m.data() : nullptr;
243
244 const double cosAzimuth = std::cos( azimuth );
245 const double sinAzimuth = std::sin( azimuth );
246 for ( double it : t )
247 {
248 const double cosT{ std::cos( it ) };
249 const double sinT{ std::sin( it ) };
250 *xOut++ = centerX + mSemiMajorAxis * cosT * cosAzimuth -
251 mSemiMinorAxis * sinT * sinAzimuth;
252 *yOut++ = centerY + mSemiMajorAxis * cosT * sinAzimuth +
253 mSemiMinorAxis * sinT * cosAzimuth;
254 if ( zOut )
255 *zOut++ = centerZ;
256 if ( mOut )
257 *mOut++ = centerM;
258 }
259}
260
261QgsPolygon *QgsEllipse::toPolygon( unsigned int segments ) const
262{
263 auto polygon = std::make_unique<QgsPolygon>();
264 if ( segments < 3 )
265 {
266 return polygon.release();
267 }
268
269 polygon->setExteriorRing( toLineString( segments ) );
270
271 return polygon.release();
272}
273
274QgsLineString *QgsEllipse::toLineString( unsigned int segments ) const
275{
276 if ( segments < 3 )
277 {
278 return new QgsLineString();
279 }
280
281 QVector<double> x;
282 QVector<double> y;
283 QVector<double> z;
284 QVector<double> m;
285
286 pointsInternal( segments, x, y, z, m );
287 if ( x.empty() )
288 return new QgsLineString();
289
290 // close linestring
291 x.append( x.at( 0 ) );
292 y.append( y.at( 0 ) );
293 if ( !z.empty() )
294 z.append( z.at( 0 ) );
295 if ( !m.empty() )
296 m.append( m.at( 0 ) );
297
298 return new QgsLineString( x, y, z, m );
299}
300
302{
303 if ( isEmpty() )
304 {
305 return QgsRectangle();
306 }
307
308 const double angle = mAzimuth * M_PI / 180.0;
309
310 const double ux = mSemiMajorAxis * std::cos( angle );
311 const double uy = mSemiMinorAxis * std::sin( angle );
312 const double vx = mSemiMajorAxis * std::sin( angle );
313 const double vy = mSemiMinorAxis * std::cos( angle );
314
315 const double halfHeight = std::sqrt( ux * ux + uy * uy );
316 const double halfWidth = std::sqrt( vx * vx + vy * vy );
317
318 const QgsPointXY p1( mCenter.x() - halfWidth, mCenter.y() - halfHeight );
319 const QgsPointXY p2( mCenter.x() + halfWidth, mCenter.y() + halfHeight );
320
321 return QgsRectangle( p1, p2 );
322}
323
324QString QgsEllipse::toString( int pointPrecision, int axisPrecision, int azimuthPrecision ) const
325{
326 QString rep;
327 if ( isEmpty() )
328 rep = QStringLiteral( "Empty" );
329 else
330 rep = QStringLiteral( "Ellipse (Center: %1, Semi-Major Axis: %2, Semi-Minor Axis: %3, Azimuth: %4)" )
331 .arg( mCenter.asWkt( pointPrecision ), 0, 's' )
332 .arg( qgsDoubleToString( mSemiMajorAxis, axisPrecision ), 0, 'f' )
333 .arg( qgsDoubleToString( mSemiMinorAxis, axisPrecision ), 0, 'f' )
334 .arg( qgsDoubleToString( mAzimuth, azimuthPrecision ), 0, 'f' );
335
336 return rep;
337}
338
340{
341 auto ombb = std::make_unique<QgsPolygon>();
342 if ( isEmpty() )
343 {
344 return ombb.release();
345 }
346
347 const QVector<QgsPoint> q = quadrant();
348
349 const QgsPoint p1 = q.at( 0 ).project( mSemiMinorAxis, mAzimuth - 90 );
350 const QgsPoint p2 = q.at( 0 ).project( mSemiMinorAxis, mAzimuth + 90 );
351 const QgsPoint p3 = q.at( 2 ).project( mSemiMinorAxis, mAzimuth + 90 );
352 const QgsPoint p4 = q.at( 2 ).project( mSemiMinorAxis, mAzimuth - 90 );
353
354 QgsLineString *ext = new QgsLineString();
355 ext->setPoints( QgsPointSequence() << p1 << p2 << p3 << p4 );
356
357 ombb->setExteriorRing( ext );
358
359 return ombb.release();
360}
virtual double eccentricity() const
The eccentricity of the ellipse.
QgsPoint mCenter
Definition qgsellipse.h:251
virtual QgsPolygon * toPolygon(unsigned int segments=36) const
Returns a segmented polygon.
virtual bool operator==(const QgsEllipse &elp) const
QgsPoint center() const
Returns the center point.
Definition qgsellipse.h:120
virtual QVector< QgsPoint > foci() const
Two foci of the ellipse.
virtual QString toString(int pointPrecision=17, int axisPrecision=17, int azimuthPrecision=2) const
returns a string representation of the ellipse.
static QgsEllipse fromCenterPoint(const QgsPoint &ptc, const QgsPoint &pt1)
Constructs an ellipse by a center point and a another point.
double mAzimuth
Definition qgsellipse.h:254
virtual QgsPointSequence points(unsigned int segments=36) const
Returns a list of points with segmentation from segments.
double mSemiMajorAxis
Definition qgsellipse.h:252
virtual QgsLineString * toLineString(unsigned int segments=36) const
Returns a segmented linestring.
virtual double focusDistance() const
The distance between the center and each foci.
virtual bool operator!=(const QgsEllipse &elp) const
double azimuth() const
Returns the azimuth.
Definition qgsellipse.h:138
virtual QVector< QgsPoint > quadrant() const
The four quadrants of the ellipse.
QgsEllipse()=default
Constructor for QgsEllipse.
virtual double perimeter() const
The circumference of the ellipse using first approximation of Ramanujan.
void setAzimuth(double azimuth)
Sets the azimuth (orientation).
virtual QgsPolygon * orientedBoundingBox() const
Returns the oriented minimal bounding box for the ellipse.
virtual void setSemiMinorAxis(double semiMinorAxis)
Sets the semi-minor axis.
static QgsEllipse fromExtent(const QgsPoint &pt1, const QgsPoint &pt2)
Constructs an ellipse by an extent (aka bounding box / QgsRectangle).
virtual double area() const
The area of the ellipse.
virtual bool isEmpty() const
An ellipse is empty if axes are equal to 0.
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.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the ellipse.
virtual void setSemiMajorAxis(double semiMajorAxis)
Sets the semi-major axis.
static QgsEllipse fromCenter2Points(const QgsPoint &ptc, const QgsPoint &pt1, const QgsPoint &pt2)
Constructs an ellipse by a central point and two other points.
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.
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.
Represents a 2D point.
Definition qgspointxy.h:60
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double x
Definition qgspoint.h:52
double distance(double x, double y) const
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition qgspoint.h:387
double y
Definition qgspoint.h:53
Polygon geometry type.
Definition qgspolygon.h:33
A rectangle specified with double values.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:6524
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607
QVector< QgsPoint > QgsPointSequence