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