QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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 : lituus at free dot fr
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
27#include <QString>
28
29using namespace Qt::StringLiterals;
30
31void QgsEllipse::normalizeAxis()
32{
33 mSemiMajorAxis = std::fabs( mSemiMajorAxis );
34 mSemiMinorAxis = std::fabs( mSemiMinorAxis );
36 {
37 std::swap( mSemiMajorAxis, mSemiMinorAxis );
38 mAzimuth = 180.0 / M_PI * QgsGeometryUtilsBase::normalizedAngle( M_PI / 180.0 * ( mAzimuth + 90 ) );
39 }
40}
41
42QgsEllipse::QgsEllipse( const QgsPoint &center, const double axis_a, const double axis_b, const double azimuth )
43 : mCenter( center )
44 , mSemiMajorAxis( axis_a )
45 , mSemiMinorAxis( axis_b )
46 , mAzimuth( azimuth )
47{
48 normalizeAxis();
49}
50
51QgsEllipse QgsEllipse::fromFoci( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3 )
52{
53 const double dist_p1p2 = pt1.distance( pt2 );
54 const double dist_p1p3 = pt1.distance( pt3 );
55 const double dist_p2p3 = pt2.distance( pt3 );
56
57 const double dist = dist_p1p3 + dist_p2p3;
58 const double azimuth = 180.0 / M_PI * QgsGeometryUtilsBase::lineAngle( pt1.x(), pt1.y(), pt2.x(), pt2.y() );
60
61 const double axis_a = dist / 2.0;
62 const double axis_b = std::sqrt( std::pow( axis_a, 2.0 ) - std::pow( dist_p1p2 / 2.0, 2.0 ) );
63
65
66 return QgsEllipse( center, axis_a, axis_b, azimuth );
67}
68
70{
72 const double axis_a = std::fabs( pt2.x() - pt1.x() ) / 2.0;
73 const double axis_b = std::fabs( pt2.y() - pt1.y() ) / 2.0;
74 const double azimuth = 90.0;
75
77
78 return QgsEllipse( center, axis_a, axis_b, azimuth );
79}
80
82{
83 const double axis_a = std::fabs( pt1.x() - center.x() );
84 const double axis_b = std::fabs( pt1.y() - center.y() );
85 const double azimuth = 90.0;
86
87 QgsPoint centerPt( center );
89
90 return QgsEllipse( centerPt, axis_a, axis_b, azimuth );
91}
92
94{
95 const double azimuth = 180.0 / M_PI * QgsGeometryUtilsBase::lineAngle( center.x(), center.y(), pt1.x(), pt1.y() );
96 const double axis_a = center.distance( pt1 );
97
98 const double length = pt2.distance( QgsGeometryUtils::projectPointOnSegment( pt2, center, pt1 ) );
99 const QgsPoint pp = center.project( length, 90 + azimuth );
100 const double axis_b = center.distance( pp );
101
102 QgsPoint centerPt( center );
104
105 return QgsEllipse( centerPt, axis_a, axis_b, azimuth );
106}
107
108bool QgsEllipse::operator==( const QgsEllipse &elp ) const
109{
110 return (
112 );
113}
114
115bool QgsEllipse::operator!=( const QgsEllipse &elp ) const
116{
117 return !operator==( elp );
118}
119
121{
122 return ( qgsDoubleNear( mSemiMajorAxis, 0.0, 1E-8 ) || 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 * QgsGeometryUtilsBase::normalizedAngle( M_PI / 180.0 * azimuth );
139}
140
142{
144}
145
146QVector<QgsPoint> QgsEllipse::foci() const
147{
148 const double dist_focus = focusDistance();
149 return {
150 mCenter.project( dist_focus, mAzimuth ),
151 mCenter.project( -dist_focus, mAzimuth ),
152 };
153}
154
156{
157 if ( isEmpty() )
158 {
159 return std::numeric_limits<double>::quiet_NaN();
160 }
161 return focusDistance() / mSemiMajorAxis;
162}
163
164double QgsEllipse::area() const
165{
166 return M_PI * mSemiMajorAxis * mSemiMinorAxis;
167}
168
170{
171 const double a = mSemiMajorAxis;
172 const double b = mSemiMinorAxis;
173 return M_PI * ( 3 * ( a + b ) - std::sqrt( 10 * a * b + 3 * ( a * a + b * b ) ) );
174}
175
176QVector<QgsPoint> QgsEllipse::quadrant() const
177{
178 return { mCenter.project( mSemiMajorAxis, mAzimuth ), mCenter.project( mSemiMinorAxis, mAzimuth + 90 ), mCenter.project( -mSemiMajorAxis, mAzimuth ), mCenter.project( -mSemiMinorAxis, mAzimuth + 90 ) };
179}
180
181QgsPointSequence QgsEllipse::points( unsigned int segments ) const
182{
184
185 QVector<double> x;
186 QVector<double> y;
187 QVector<double> z;
188 QVector<double> m;
189
190 pointsInternal( segments, x, y, z, m );
191 const bool hasZ = !z.empty();
192 const bool hasM = !m.empty();
193 pts.reserve( x.size() );
194 for ( int i = 0; i < x.size(); ++i )
195 {
196 pts.append( QgsPoint( x[i], y[i], hasZ ? z[i] : std::numeric_limits< double >::quiet_NaN(), hasM ? m[i] : std::numeric_limits< double >::quiet_NaN() ) );
197 }
198 return pts;
199}
200
201void QgsEllipse::pointsInternal( unsigned int segments, QVector<double> &x, QVector<double> &y, QVector<double> &z, QVector<double> &m ) const
202{
203 if ( segments < 3 )
204 {
205 return;
206 }
207
208 const double centerX = mCenter.x();
209 const double centerY = mCenter.y();
210 const double centerZ = mCenter.z();
211 const double centerM = mCenter.m();
212 const bool hasZ = mCenter.is3D();
213 const bool hasM = mCenter.isMeasure();
214
215 std::vector<double> t( segments );
216 const QgsPoint p1 = mCenter.project( mSemiMajorAxis, mAzimuth );
217 const double azimuth = std::atan2( p1.y() - mCenter.y(), p1.x() - mCenter.x() );
218 for ( unsigned int i = 0; i < segments; ++i )
219 {
220 t[i] = 2 * M_PI - ( ( 2 * M_PI ) / segments * i ); // Since the algorithm used rotates in the trigonometric direction (counterclockwise)
221 }
222
223 x.resize( segments );
224 y.resize( segments );
225 if ( hasZ )
226 z.resize( segments );
227 if ( hasM )
228 m.resize( segments );
229 double *xOut = x.data();
230 double *yOut = y.data();
231 double *zOut = hasZ ? z.data() : nullptr;
232 double *mOut = hasM ? m.data() : nullptr;
233
234 const double cosAzimuth = std::cos( azimuth );
235 const double sinAzimuth = std::sin( azimuth );
236 for ( double it : t )
237 {
238 const double cosT { std::cos( it ) };
239 const double sinT { std::sin( it ) };
240 *xOut++ = centerX + mSemiMajorAxis * cosT * cosAzimuth - mSemiMinorAxis * sinT * sinAzimuth;
241 *yOut++ = centerY + mSemiMajorAxis * cosT * sinAzimuth + mSemiMinorAxis * sinT * cosAzimuth;
242 if ( zOut )
243 *zOut++ = centerZ;
244 if ( mOut )
245 *mOut++ = centerM;
246 }
247}
248
249QgsPolygon *QgsEllipse::toPolygon( unsigned int segments ) const
250{
251 auto polygon = std::make_unique<QgsPolygon>();
252 if ( segments < 3 )
253 {
254 return polygon.release();
255 }
256
257 polygon->setExteriorRing( toLineString( segments ) );
258
259 return polygon.release();
260}
261
262QgsLineString *QgsEllipse::toLineString( unsigned int segments ) const
263{
264 if ( segments < 3 )
265 {
266 return new QgsLineString();
267 }
268
269 QVector<double> x;
270 QVector<double> y;
271 QVector<double> z;
272 QVector<double> m;
273
274 pointsInternal( segments, x, y, z, m );
275 if ( x.empty() )
276 return new QgsLineString();
277
278 // close linestring
279 x.append( x.at( 0 ) );
280 y.append( y.at( 0 ) );
281 if ( !z.empty() )
282 z.append( z.at( 0 ) );
283 if ( !m.empty() )
284 m.append( m.at( 0 ) );
285
286 return new QgsLineString( x, y, z, m );
287}
288
290{
291 if ( isEmpty() )
292 {
293 return QgsRectangle();
294 }
295
296 const double angle = mAzimuth * M_PI / 180.0;
297
298 const double ux = mSemiMajorAxis * std::cos( angle );
299 const double uy = mSemiMinorAxis * std::sin( angle );
300 const double vx = mSemiMajorAxis * std::sin( angle );
301 const double vy = mSemiMinorAxis * std::cos( angle );
302
303 const double halfHeight = std::sqrt( ux * ux + uy * uy );
304 const double halfWidth = std::sqrt( vx * vx + vy * vy );
305
306 const QgsPointXY p1( mCenter.x() - halfWidth, mCenter.y() - halfHeight );
307 const QgsPointXY p2( mCenter.x() + halfWidth, mCenter.y() + halfHeight );
308
309 return QgsRectangle( p1, p2 );
310}
311
312QString QgsEllipse::toString( int pointPrecision, int axisPrecision, int azimuthPrecision ) const
313{
314 QString rep;
315 if ( isEmpty() )
316 rep = u"Empty"_s;
317 else
318 rep = u"Ellipse (Center: %1, Semi-Major Axis: %2, Semi-Minor Axis: %3, Azimuth: %4)"_s.arg( mCenter.asWkt( pointPrecision ), 0, 's' )
319 .arg( qgsDoubleToString( mSemiMajorAxis, axisPrecision ), 0, 'f' )
320 .arg( qgsDoubleToString( mSemiMinorAxis, axisPrecision ), 0, 'f' )
321 .arg( qgsDoubleToString( mAzimuth, azimuthPrecision ), 0, 'f' );
322
323 return rep;
324}
325
327{
328 auto ombb = std::make_unique<QgsPolygon>();
329 if ( isEmpty() )
330 {
331 return ombb.release();
332 }
333
334 const QVector<QgsPoint> q = quadrant();
335
336 const QgsPoint p1 = q.at( 0 ).project( mSemiMinorAxis, mAzimuth - 90 );
337 const QgsPoint p2 = q.at( 0 ).project( mSemiMinorAxis, mAzimuth + 90 );
338 const QgsPoint p3 = q.at( 2 ).project( mSemiMinorAxis, mAzimuth + 90 );
339 const QgsPoint p4 = q.at( 2 ).project( mSemiMinorAxis, mAzimuth - 90 );
340
341 QgsLineString *ext = new QgsLineString();
342 ext->setPoints( QgsPointSequence() << p1 << p2 << p3 << p4 );
343
344 ombb->setExteriorRing( ext );
345
346 return ombb.release();
347}
virtual double eccentricity() const
The eccentricity of the ellipse.
QgsPoint mCenter
Definition qgsellipse.h:255
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:122
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:258
virtual QgsPointSequence points(unsigned int segments=36) const
Returns a list of points with segmentation from segments.
double mSemiMajorAxis
Definition qgsellipse.h:256
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:140
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:257
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:62
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
double x
Definition qgspoint.h:56
double distance(double x, double y) const
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition qgspoint.h:466
double y
Definition qgspoint.h:57
Polygon geometry type.
Definition qgspolygon.h:37
A rectangle specified with double values.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:6893
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6975
QVector< QgsPoint > QgsPointSequence