QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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
static QgsEllipse fromCenterPoint(const QgsPoint &ptc, const QgsPoint &pt1)
Constructs an ellipse by a center point and a another point.
Definition: qgsellipse.cpp:77
void setPoints(const QgsPointSequence &points)
Resets the line string to match the specified list of points.
QgsEllipse()=default
Constructor for QgsEllipse.
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...
double distance(double x, double y) const
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition: qgspoint.h:325
A class to represent a 2D point.
Definition: qgspointxy.h:43
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:280
QgsPoint center() const
Returns the center point.
Definition: qgsellipse.h:121
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
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
QgsPoint mCenter
Definition: qgsellipse.h:252
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 azimuth() const
Returns the azimuth.
Definition: qgsellipse.h:139
static QgsEllipse fromExtent(const QgsPoint &pt1, const QgsPoint &pt2)
Constructs an ellipse by an extent (aka bounding box / QgsRectangle).
Definition: qgsellipse.cpp:65
double mAzimuth
Definition: qgsellipse.h:255
virtual void setSemiMinorAxis(double semiMinorAxis)
Sets the semi-minor axis.
Definition: qgsellipse.cpp:129
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
static QgsPoint midpoint(const QgsPoint &pt1, const QgsPoint &pt2)
Returns a middle point between points pt1 and pt2.
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:240
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
Definition: qgspoint.cpp:229
static double normalizedAngle(double angle)
Ensures that an angle is in the range 0 <= angle < 2 pi.
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which correspond to this point projected by a specified distance with specified a...
Definition: qgspoint.cpp:675
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
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
virtual QVector< QgsPoint > foci() const
Two foci of the ellipse.
Definition: qgsellipse.cpp:146
double mSemiMinorAxis
Definition: qgsellipse.h:254
virtual void setSemiMajorAxis(double semiMajorAxis)
Sets the semi-major axis.
Definition: qgsellipse.cpp:124
QVector< QgsPoint > QgsPointSequence
virtual QString toString(int pointPrecision=17, int axisPrecision=17, int azimuthPrecision=2) const
returns a string representation of the ellipse.
Definition: qgsellipse.cpp:277
virtual bool isEmpty() const
An ellipse is empty if axes are equal to 0.
Definition: qgsellipse.cpp:118
virtual double area() const
The area of the ellipse.
Definition: qgsellipse.cpp:165
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
static QgsPoint projectPointOnSegment(const QgsPoint &p, const QgsPoint &s1, const QgsPoint &s2)
Project the point on a segment.
void setAzimuth(double azimuth)
Sets the azimuth (orientation).
Definition: qgsellipse.cpp:135
virtual QgsLineString * toLineString(unsigned int segments=36) const
Returns a segmented linestring.
Definition: qgsellipse.cpp:237
double z
Definition: qgspoint.h:43
virtual double focusDistance() const
The distance between the center and each foci.
Definition: qgsellipse.cpp:141
Ellipse geometry type.
Definition: qgsellipse.h:39
Polygon geometry type.
Definition: qgspolygon.h:31
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
virtual double perimeter() const
The circumference of the ellipse using first approximation of Ramanujan.
Definition: qgsellipse.cpp:170
double m
Definition: qgspoint.h:44
virtual double eccentricity() const
The eccentricity of the ellipse.
Definition: qgsellipse.cpp:156
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