QGIS API Documentation  3.17.0-Master (3b262f2a79)
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 
26 void 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  QgsGeometryUtils::normalizedAngle( M_PI / 180.0 * ( mAzimuth + 90 ) );
35  }
36 }
37 
38 QgsEllipse::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 
47 QgsEllipse QgsEllipse::fromFoci( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3 )
48 {
49  double dist_p1p2 = pt1.distance( pt2 );
50  double dist_p1p3 = pt1.distance( pt3 );
51  double dist_p2p3 = pt2.distance( pt3 );
52 
53  double dist = dist_p1p3 + dist_p2p3;
54  double azimuth = 180.0 / M_PI * QgsGeometryUtils::lineAngle( pt1.x(), pt1.y(), pt2.x(), pt2.y() );
56 
57  double axis_a = dist / 2.0;
58  double axis_b = std::sqrt( std::pow( axis_a, 2.0 ) - std::pow( dist_p1p2 / 2.0, 2.0 ) );
59 
60  QgsGeometryUtils::setZValueFromPoints( QgsPointSequence() << pt1 << pt2 << pt3, center );
61 
62  return QgsEllipse( center, axis_a, axis_b, azimuth );
63 }
64 
66 {
68  double axis_a = std::fabs( pt2.x() - pt1.x() ) / 2.0;
69  double axis_b = std::fabs( pt2.y() - pt1.y() ) / 2.0;
70  double azimuth = 90.0;
71 
73 
74  return QgsEllipse( center, axis_a, axis_b, azimuth );
75 }
76 
78 {
79  double axis_a = std::fabs( pt1.x() - center.x() );
80  double axis_b = std::fabs( pt1.y() - center.y() );
81  double azimuth = 90.0;
82 
83  QgsPoint centerPt( center );
84  QgsGeometryUtils::setZValueFromPoints( QgsPointSequence() << center << pt1, centerPt );
85 
86  return QgsEllipse( centerPt, axis_a, axis_b, azimuth );
87 }
88 
90 {
91  double azimuth = 180.0 / M_PI * QgsGeometryUtils::lineAngle( center.x(), center.y(), pt1.x(), pt1.y() );
92  double axis_a = center.distance( pt1 );
93 
94  double length = pt2.distance( QgsGeometryUtils::projectPointOnSegment( pt2, center, pt1 ) );
95  QgsPoint pp = center.project( length, 90 + azimuth );
96  double axis_b = center.distance( pp );
97 
98  QgsPoint centerPt( center );
99  QgsGeometryUtils::setZValueFromPoints( QgsPointSequence() << center << pt1 << pt2, centerPt );
100 
101  return QgsEllipse( centerPt, axis_a, axis_b, azimuth );
102 }
103 
104 bool QgsEllipse::operator ==( const QgsEllipse &elp ) const
105 {
106  return ( ( mCenter == elp.mCenter ) &&
109  qgsDoubleNear( mAzimuth, elp.mAzimuth, 1E-8 )
110  );
111 }
112 
113 bool 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 
124 void QgsEllipse::setSemiMajorAxis( const double axis_a )
125 {
126  mSemiMajorAxis = axis_a;
127  normalizeAxis();
128 }
129 void QgsEllipse::setSemiMinorAxis( const double axis_b )
130 {
131  mSemiMinorAxis = axis_b;
132  normalizeAxis();
133 }
134 
135 void QgsEllipse::setAzimuth( const double azimuth )
136 {
137  mAzimuth = 180.0 / M_PI *
138  QgsGeometryUtils::normalizedAngle( M_PI / 180.0 * azimuth );
139 }
140 
142 {
144 }
145 
146 QVector<QgsPoint> QgsEllipse::foci() const
147 {
148  QVector<QgsPoint> f;
149  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 
165 double QgsEllipse::area() const
166 {
167  return M_PI * mSemiMajorAxis * mSemiMinorAxis;
168 }
169 
170 double QgsEllipse::perimeter() const
171 {
172  double a = mSemiMajorAxis;
173  double b = mSemiMinorAxis;
174  return M_PI * ( 3 * ( a + b ) - std::sqrt( 10 * a * b + 3 * ( a * a + b * b ) ) );
175 }
176 
177 QVector<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 
188 QgsPointSequence QgsEllipse::points( unsigned int segments ) const
189 {
190  QgsPointSequence pts;
191 
192  if ( segments < 3 )
193  {
194  return pts;
195  }
196 
197 
198  QgsWkbTypes::Type pType( mCenter.wkbType() );
199  double z = mCenter.z();
200  double m = mCenter.m();
201 
202  QVector<double> t;
203  t.reserve( segments );
204  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  double x = mCenter.x() +
213  mSemiMajorAxis * std::cos( *it ) * std::cos( azimuth ) -
214  mSemiMinorAxis * std::sin( *it ) * std::sin( azimuth );
215  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 
224 QgsPolygon *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 
237 QgsLineString *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 
245  QgsPointSequence pts;
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  double angle = mAzimuth * M_PI / 180.0;
262 
263  double ux = mSemiMajorAxis * std::cos( angle );
264  double uy = mSemiMinorAxis * std::sin( angle );
265  double vx = mSemiMajorAxis * std::sin( angle );
266  double vy = mSemiMinorAxis * std::cos( angle );
267 
268  double halfHeight = std::sqrt( ux * ux + uy * uy );
269  double halfWidth = std::sqrt( vx * vx + vy * vy );
270 
271  QgsPointXY p1( mCenter.x() - halfWidth, mCenter.y() - halfHeight );
272  QgsPointXY p2( mCenter.x() + halfWidth, mCenter.y() + halfHeight );
273 
274  return QgsRectangle( p1, p2 );
275 }
276 
277 QString 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  QVector<QgsPoint> q = quadrant();
301 
302  QgsPoint p1 = q.at( 0 ).project( mSemiMinorAxis, mAzimuth - 90 );
303  QgsPoint p2 = q.at( 0 ).project( mSemiMinorAxis, mAzimuth + 90 );
304  QgsPoint p3 = q.at( 2 ).project( mSemiMinorAxis, mAzimuth + 90 );
305  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 }
virtual bool operator!=(const QgsEllipse &elp) const
Definition: qgsellipse.cpp:113
A rectangle specified with double values.
Definition: qgsrectangle.h:41
double y
Definition: qgspoint.h:42
void setAzimuth(double azimuth) SIP_HOLDGIL
Sets the azimuth (orientation).
Definition: qgsellipse.cpp:135
void setPoints(const QgsPointSequence &points)
Resets the line string to match the specified list of points.
static QgsEllipse fromExtent(const QgsPoint &pt1, const QgsPoint &pt2) SIP_HOLDGIL
Constructs an ellipse by an extent (aka bounding box / QgsRectangle).
Definition: qgsellipse.cpp:65
virtual double eccentricity() const SIP_HOLDGIL
The eccentricity of the ellipse.
Definition: qgsellipse.cpp:156
virtual double perimeter() const SIP_HOLDGIL
The circumference of the ellipse using first approximation of Ramanujan.
Definition: qgsellipse.cpp:170
double azimuth() const SIP_HOLDGIL
Returns the azimuth.
Definition: qgsellipse.h:139
A class to represent a 2D point.
Definition: qgspointxy.h:43
virtual void setSemiMajorAxis(double semiMajorAxis) SIP_HOLDGIL
Sets the semi-major axis.
Definition: qgsellipse.cpp:124
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:316
static QgsPoint midpoint(const QgsPoint &pt1, const QgsPoint &pt2) SIP_HOLDGIL
Returns a middle point between points pt1 and pt2.
QgsPoint project(double distance, double azimuth, double inclination=90.0) const SIP_HOLDGIL
Returns a new point which correspond to this point projected by a specified distance with specified a...
Definition: qgspoint.cpp:715
QgsEllipse() SIP_HOLDGIL=default
Constructor for QgsEllipse.
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:786
virtual bool operator==(const QgsEllipse &elp) const
Definition: qgsellipse.cpp:104
static QgsEllipse fromFoci(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3) SIP_HOLDGIL
Constructs an ellipse by foci (pt1 and pt2) and a point pt3.
Definition: qgsellipse.cpp:47
static QgsEllipse fromCenter2Points(const QgsPoint &ptc, const QgsPoint &pt1, const QgsPoint &pt2) SIP_HOLDGIL
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) SIP_HOLDGIL
Calculates the direction of line joining two points in radians, clockwise from the north direction...
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:69
QgsPoint mCenter
Definition: qgsellipse.h:252
static QgsPoint projectPointOnSegment(const QgsPoint &p, const QgsPoint &s1, const QgsPoint &s2) SIP_HOLDGIL
Project the point on a segment.
double mSemiMajorAxis
Definition: qgsellipse.h:253
static bool setZValueFromPoints(const QgsPointSequence &points, QgsPoint &point)
A Z dimension is added to point if one of the point in the list points is in 3D.
double mAzimuth
Definition: qgsellipse.h:255
virtual QgsPolygon * toPolygon(unsigned int segments=36) const
Returns a segmented polygon.
Definition: qgsellipse.cpp:224
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:276
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
Definition: qgspoint.cpp:259
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
virtual void setSemiMinorAxis(double semiMinorAxis) SIP_HOLDGIL
Sets the semi-minor axis.
Definition: qgsellipse.cpp:129
virtual QVector< QgsPoint > foci() const
Two foci of the ellipse.
Definition: qgsellipse.cpp:146
double mSemiMinorAxis
Definition: qgsellipse.h:254
QVector< QgsPoint > QgsPointSequence
static double normalizedAngle(double angle) SIP_HOLDGIL
Ensures that an angle is in the range 0 <= angle < 2 pi.
static QgsEllipse fromCenterPoint(const QgsPoint &ptc, const QgsPoint &pt1) SIP_HOLDGIL
Constructs an ellipse by a center point and a another point.
Definition: qgsellipse.cpp:77
virtual QString toString(int pointPrecision=17, int axisPrecision=17, int azimuthPrecision=2) const
returns a string representation of the ellipse.
Definition: qgsellipse.cpp:277
double distance(double x, double y) const SIP_HOLDGIL
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition: qgspoint.h:332
virtual bool isEmpty() const SIP_HOLDGIL
An ellipse is empty if axes are equal to 0.
Definition: qgsellipse.cpp:118
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
virtual QgsLineString * toLineString(unsigned int segments=36) const
Returns a segmented linestring.
Definition: qgsellipse.cpp:237
double z
Definition: qgspoint.h:43
virtual double area() const SIP_HOLDGIL
The area of the ellipse.
Definition: qgsellipse.cpp:165
QgsPoint center() const SIP_HOLDGIL
Returns the center point.
Definition: qgsellipse.h:121
Ellipse geometry type.
Definition: qgsellipse.h:39
virtual double focusDistance() const SIP_HOLDGIL
The distance between the center and each foci.
Definition: qgsellipse.cpp:141
Polygon geometry type.
Definition: qgspolygon.h:33
virtual QVector< QgsPoint > quadrant() const
The four quadrants of the ellipse.
Definition: qgsellipse.cpp:177
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the ellipse.
Definition: qgsellipse.cpp:254
virtual QgsPolygon * orientedBoundingBox() const
Returns the oriented minimal bounding box for the ellipse.
Definition: qgsellipse.cpp:292
double m
Definition: qgspoint.h:44
double x
Definition: qgspoint.h:41
virtual QgsPointSequence points(unsigned int segments=36) const
Returns a list of points with segmentation from segments.
Definition: qgsellipse.cpp:188