QGIS API Documentation 3.99.0-Master (d270888f95f)
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 *
39 QgsGeometryUtilsBase::normalizedAngle( M_PI / 180.0 * ( mAzimuth + 90 ) );
40 }
41}
42
43QgsEllipse::QgsEllipse( const QgsPoint &center, const double axis_a, const double axis_b, const double azimuth )
44 : mCenter( center )
45 , mSemiMajorAxis( axis_a )
46 , mSemiMinorAxis( axis_b )
47 , mAzimuth( azimuth )
48{
49 normalizeAxis();
50}
51
52QgsEllipse QgsEllipse::fromFoci( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3 )
53{
54 const double dist_p1p2 = pt1.distance( pt2 );
55 const double dist_p1p3 = pt1.distance( pt3 );
56 const double dist_p2p3 = pt2.distance( pt3 );
57
58 const double dist = dist_p1p3 + dist_p2p3;
59 const double azimuth = 180.0 / M_PI * QgsGeometryUtilsBase::lineAngle( pt1.x(), pt1.y(), pt2.x(), pt2.y() );
61
62 const double axis_a = dist / 2.0;
63 const double axis_b = std::sqrt( std::pow( axis_a, 2.0 ) - std::pow( dist_p1p2 / 2.0, 2.0 ) );
64
66
67 return QgsEllipse( center, axis_a, axis_b, azimuth );
68}
69
71{
73 const double axis_a = std::fabs( pt2.x() - pt1.x() ) / 2.0;
74 const double axis_b = std::fabs( pt2.y() - pt1.y() ) / 2.0;
75 const double azimuth = 90.0;
76
78
79 return QgsEllipse( center, axis_a, axis_b, azimuth );
80}
81
83{
84 const double axis_a = std::fabs( pt1.x() - center.x() );
85 const double axis_b = std::fabs( pt1.y() - center.y() );
86 const double azimuth = 90.0;
87
88 QgsPoint centerPt( center );
90
91 return QgsEllipse( centerPt, axis_a, axis_b, azimuth );
92}
93
95{
96 const double azimuth = 180.0 / M_PI * QgsGeometryUtilsBase::lineAngle( center.x(), center.y(), pt1.x(), pt1.y() );
97 const double axis_a = center.distance( pt1 );
98
99 const double length = pt2.distance( QgsGeometryUtils::projectPointOnSegment( pt2, center, pt1 ) );
100 const QgsPoint pp = center.project( length, 90 + azimuth );
101 const double axis_b = center.distance( pp );
102
103 QgsPoint centerPt( center );
105
106 return QgsEllipse( centerPt, axis_a, axis_b, azimuth );
107}
108
109bool QgsEllipse::operator ==( const QgsEllipse &elp ) const
110{
111 return ( ( mCenter == elp.mCenter ) &&
114 qgsDoubleNear( mAzimuth, elp.mAzimuth, 1E-8 )
115 );
116}
117
118bool QgsEllipse::operator !=( const QgsEllipse &elp ) const
119{
120 return !operator==( elp );
121}
122
124{
125 return ( qgsDoubleNear( mSemiMajorAxis, 0.0, 1E-8 ) ||
126 qgsDoubleNear( mSemiMinorAxis, 0.0, 1E-8 ) );
127}
128
129void QgsEllipse::setSemiMajorAxis( const double axis_a )
130{
131 mSemiMajorAxis = axis_a;
132 normalizeAxis();
133}
134void QgsEllipse::setSemiMinorAxis( const double axis_b )
135{
136 mSemiMinorAxis = axis_b;
137 normalizeAxis();
138}
139
140void QgsEllipse::setAzimuth( const double azimuth )
141{
142 mAzimuth = 180.0 / M_PI *
144}
145
147{
149}
150
151QVector<QgsPoint> QgsEllipse::foci() const
152{
153 const double dist_focus = focusDistance();
154 return
155 {
156 mCenter.project( dist_focus, mAzimuth ),
157 mCenter.project( -dist_focus, mAzimuth ),
158 };
159}
160
162{
163 if ( isEmpty() )
164 {
165 return std::numeric_limits<double>::quiet_NaN();
166 }
167 return focusDistance() / mSemiMajorAxis;
168}
169
170double QgsEllipse::area() const
171{
172 return M_PI * mSemiMajorAxis * mSemiMinorAxis;
173}
174
176{
177 const double a = mSemiMajorAxis;
178 const double b = mSemiMinorAxis;
179 return M_PI * ( 3 * ( a + b ) - std::sqrt( 10 * a * b + 3 * ( a * a + b * b ) ) );
180}
181
182QVector<QgsPoint> QgsEllipse::quadrant() const
183{
184 return
185 {
186 mCenter.project( mSemiMajorAxis, mAzimuth ),
187 mCenter.project( mSemiMinorAxis, mAzimuth + 90 ),
188 mCenter.project( -mSemiMajorAxis, mAzimuth ),
189 mCenter.project( -mSemiMinorAxis, mAzimuth + 90 )
190 };
191}
192
193QgsPointSequence QgsEllipse::points( unsigned int segments ) const
194{
196
197 QVector<double> x;
198 QVector<double> y;
199 QVector<double> z;
200 QVector<double> m;
201
202 pointsInternal( segments, x, y, z, m );
203 const bool hasZ = !z.empty();
204 const bool hasM = !m.empty();
205 pts.reserve( x.size() );
206 for ( int i = 0; i < x.size(); ++i )
207 {
208 pts.append( QgsPoint( x[i], y[i],
209 hasZ ? z[i] : std::numeric_limits< double >::quiet_NaN(),
210 hasM ? m[i] : std::numeric_limits< double >::quiet_NaN() ) );
211 }
212 return pts;
213}
214
215void QgsEllipse::pointsInternal( unsigned int segments, QVector<double> &x, QVector<double> &y, QVector<double> &z, QVector<double> &m ) const
216{
217 if ( segments < 3 )
218 {
219 return;
220 }
221
222 const double centerX = mCenter.x();
223 const double centerY = mCenter.y();
224 const double centerZ = mCenter.z();
225 const double centerM = mCenter.m();
226 const bool hasZ = mCenter.is3D();
227 const bool hasM = mCenter.isMeasure();
228
229 std::vector<double> t( segments );
230 const QgsPoint p1 = mCenter.project( mSemiMajorAxis, mAzimuth );
231 const double azimuth = std::atan2( p1.y() - mCenter.y(), p1.x() - mCenter.x() );
232 for ( unsigned int i = 0; i < segments; ++i )
233 {
234 t[i] = 2 * M_PI - ( ( 2 * M_PI ) / segments * i ); // Since the algorithm used rotates in the trigonometric direction (counterclockwise)
235 }
236
237 x.resize( segments );
238 y.resize( segments );
239 if ( hasZ )
240 z.resize( segments );
241 if ( hasM )
242 m.resize( segments );
243 double *xOut = x.data();
244 double *yOut = y.data();
245 double *zOut = hasZ ? z.data() : nullptr;
246 double *mOut = hasM ? m.data() : nullptr;
247
248 const double cosAzimuth = std::cos( azimuth );
249 const double sinAzimuth = std::sin( azimuth );
250 for ( double it : t )
251 {
252 const double cosT{ std::cos( it ) };
253 const double sinT{ std::sin( it ) };
254 *xOut++ = centerX + mSemiMajorAxis * cosT * cosAzimuth -
255 mSemiMinorAxis * sinT * sinAzimuth;
256 *yOut++ = centerY + mSemiMajorAxis * cosT * sinAzimuth +
257 mSemiMinorAxis * sinT * cosAzimuth;
258 if ( zOut )
259 *zOut++ = centerZ;
260 if ( mOut )
261 *mOut++ = centerM;
262 }
263}
264
265QgsPolygon *QgsEllipse::toPolygon( unsigned int segments ) const
266{
267 auto polygon = std::make_unique<QgsPolygon>();
268 if ( segments < 3 )
269 {
270 return polygon.release();
271 }
272
273 polygon->setExteriorRing( toLineString( segments ) );
274
275 return polygon.release();
276}
277
278QgsLineString *QgsEllipse::toLineString( unsigned int segments ) const
279{
280 if ( segments < 3 )
281 {
282 return new QgsLineString();
283 }
284
285 QVector<double> x;
286 QVector<double> y;
287 QVector<double> z;
288 QVector<double> m;
289
290 pointsInternal( segments, x, y, z, m );
291 if ( x.empty() )
292 return new QgsLineString();
293
294 // close linestring
295 x.append( x.at( 0 ) );
296 y.append( y.at( 0 ) );
297 if ( !z.empty() )
298 z.append( z.at( 0 ) );
299 if ( !m.empty() )
300 m.append( m.at( 0 ) );
301
302 return new QgsLineString( x, y, z, m );
303}
304
306{
307 if ( isEmpty() )
308 {
309 return QgsRectangle();
310 }
311
312 const double angle = mAzimuth * M_PI / 180.0;
313
314 const double ux = mSemiMajorAxis * std::cos( angle );
315 const double uy = mSemiMinorAxis * std::sin( angle );
316 const double vx = mSemiMajorAxis * std::sin( angle );
317 const double vy = mSemiMinorAxis * std::cos( angle );
318
319 const double halfHeight = std::sqrt( ux * ux + uy * uy );
320 const double halfWidth = std::sqrt( vx * vx + vy * vy );
321
322 const QgsPointXY p1( mCenter.x() - halfWidth, mCenter.y() - halfHeight );
323 const QgsPointXY p2( mCenter.x() + halfWidth, mCenter.y() + halfHeight );
324
325 return QgsRectangle( p1, p2 );
326}
327
328QString QgsEllipse::toString( int pointPrecision, int axisPrecision, int azimuthPrecision ) const
329{
330 QString rep;
331 if ( isEmpty() )
332 rep = u"Empty"_s;
333 else
334 rep = u"Ellipse (Center: %1, Semi-Major Axis: %2, Semi-Minor Axis: %3, Azimuth: %4)"_s
335 .arg( mCenter.asWkt( pointPrecision ), 0, 's' )
336 .arg( qgsDoubleToString( mSemiMajorAxis, axisPrecision ), 0, 'f' )
337 .arg( qgsDoubleToString( mSemiMinorAxis, axisPrecision ), 0, 'f' )
338 .arg( qgsDoubleToString( mAzimuth, azimuthPrecision ), 0, 'f' );
339
340 return rep;
341}
342
344{
345 auto ombb = std::make_unique<QgsPolygon>();
346 if ( isEmpty() )
347 {
348 return ombb.release();
349 }
350
351 const QVector<QgsPoint> q = quadrant();
352
353 const QgsPoint p1 = q.at( 0 ).project( mSemiMinorAxis, mAzimuth - 90 );
354 const QgsPoint p2 = q.at( 0 ).project( mSemiMinorAxis, mAzimuth + 90 );
355 const QgsPoint p3 = q.at( 2 ).project( mSemiMinorAxis, mAzimuth + 90 );
356 const QgsPoint p4 = q.at( 2 ).project( mSemiMinorAxis, mAzimuth - 90 );
357
358 QgsLineString *ext = new QgsLineString();
359 ext->setPoints( QgsPointSequence() << p1 << p2 << p3 << p4 );
360
361 ombb->setExteriorRing( ext );
362
363 return ombb.release();
364}
virtual double eccentricity() const
The eccentricity of the ellipse.
QgsPoint mCenter
Definition qgsellipse.h:253
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:256
virtual QgsPointSequence points(unsigned int segments=36) const
Returns a list of points with segmentation from segments.
double mSemiMajorAxis
Definition qgsellipse.h:254
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:255
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:391
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:6817
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6900
QVector< QgsPoint > QgsPointSequence