QGIS API Documentation  3.2.0-Bonn (bc43194)
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  double azimuth = std::atan2( quadrant().at( 0 ).y() - mCenter.y(), quadrant().at( 0 ).x() - mCenter.x() );
204  for ( unsigned int i = 0; i < segments; ++i )
205  {
206  t.append( 2 * M_PI - ( ( 2 * M_PI ) / segments * i ) ); // Since the algorithm used rotates in the trigonometric direction (counterclockwise)
207  }
208 
209  for ( QVector<double>::const_iterator it = t.constBegin(); it != t.constEnd(); ++it )
210  {
211  double x = mCenter.x() +
212  mSemiMajorAxis * std::cos( *it ) * std::cos( azimuth ) -
213  mSemiMinorAxis * std::sin( *it ) * std::sin( azimuth );
214  double y = mCenter.y() +
215  mSemiMajorAxis * std::cos( *it ) * std::sin( azimuth ) +
216  mSemiMinorAxis * std::sin( *it ) * std::cos( azimuth );
217  pts.push_back( QgsPoint( pType, x, y, z, m ) );
218  }
219 
220  return pts;
221 }
222 
223 QgsPolygon *QgsEllipse::toPolygon( unsigned int segments ) const
224 {
225  std::unique_ptr<QgsPolygon> polygon( new QgsPolygon() );
226  if ( segments < 3 )
227  {
228  return polygon.release();
229  }
230 
231  polygon->setExteriorRing( toLineString( segments ) );
232 
233  return polygon.release();
234 }
235 
236 QgsLineString *QgsEllipse::toLineString( unsigned int segments ) const
237 {
238  std::unique_ptr<QgsLineString> ext( new QgsLineString() );
239  if ( segments < 3 )
240  {
241  return ext.release();
242  }
243 
244  QgsPointSequence pts;
245  pts = points( segments );
246  pts.append( pts.at( 0 ) ); // close linestring
247 
248  ext->setPoints( pts );
249 
250  return ext.release();
251 }
252 
254 {
255  if ( isEmpty() )
256  {
257  return QgsRectangle();
258  }
259 
260  double angle = mAzimuth * M_PI / 180.0;
261 
262  double ux = mSemiMajorAxis * std::cos( angle );
263  double uy = mSemiMinorAxis * std::sin( angle );
264  double vx = mSemiMajorAxis * std::sin( angle );
265  double vy = mSemiMinorAxis * std::cos( angle );
266 
267  double halfHeight = std::sqrt( ux * ux + uy * uy );
268  double halfWidth = std::sqrt( vx * vx + vy * vy );
269 
270  QgsPointXY p1( mCenter.x() - halfWidth, mCenter.y() - halfHeight );
271  QgsPointXY p2( mCenter.x() + halfWidth, mCenter.y() + halfHeight );
272 
273  return QgsRectangle( p1, p2 );
274 }
275 
276 QString QgsEllipse::toString( int pointPrecision, int axisPrecision, int azimuthPrecision ) const
277 {
278  QString rep;
279  if ( isEmpty() )
280  rep = QStringLiteral( "Empty" );
281  else
282  rep = QStringLiteral( "Ellipse (Center: %1, Semi-Major Axis: %2, Semi-Minor Axis: %3, Azimuth: %4)" )
283  .arg( mCenter.asWkt( pointPrecision ), 0, 's' )
284  .arg( qgsDoubleToString( mSemiMajorAxis, axisPrecision ), 0, 'f' )
285  .arg( qgsDoubleToString( mSemiMinorAxis, axisPrecision ), 0, 'f' )
286  .arg( qgsDoubleToString( mAzimuth, azimuthPrecision ), 0, 'f' );
287 
288  return rep;
289 }
290 
292 {
293  std::unique_ptr<QgsPolygon> ombb( new QgsPolygon() );
294  if ( isEmpty() )
295  {
296  return ombb.release();
297  }
298 
299  QVector<QgsPoint> q = quadrant();
300 
301  QgsPoint p1 = q.at( 0 ).project( mSemiMinorAxis, mAzimuth - 90 );
302  QgsPoint p2 = q.at( 0 ).project( mSemiMinorAxis, mAzimuth + 90 );
303  QgsPoint p3 = q.at( 2 ).project( mSemiMinorAxis, mAzimuth + 90 );
304  QgsPoint p4 = q.at( 2 ).project( mSemiMinorAxis, mAzimuth - 90 );
305 
306  QgsLineString *ext = new QgsLineString();
307  ext->setPoints( QgsPointSequence() << p1 << p2 << p3 << p4 );
308 
309  ombb->setExteriorRing( ext );
310 
311  return ombb.release();
312 }
virtual bool operator!=(const QgsEllipse &elp) const
Definition: qgsellipse.cpp:113
A rectangle specified with double values.
Definition: qgsrectangle.h:40
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 distance between this point and a specified x, y coordinate.
Definition: qgspoint.h:276
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:251
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:67
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:223
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:237
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
Definition: qgspoint.cpp:223
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:631
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:276
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:236
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:253
virtual QgsPolygon * orientedBoundingBox() const
Returns the oriented minimal bounding box for the ellipse.
Definition: qgsellipse.cpp:291
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