1 /***************************************************************************
2                          qgscircle.h
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 #ifndef QGSCIRCLE_H
19 #define QGSCIRCLE_H
20 
21 #include <QString>
22 
23 #include "qgis_core.h"
24 #include "qgsellipse.h"
25 #include "qgspolygon.h"
26 #include "qgsrectangle.h"
27 #include "qgscircularstring.h"
28 
29 
30 class QgsPoint;
31 
32 /**
33  * \ingroup core
34  * \class QgsCircle
35  * \brief Circle geometry type.
36  *
37  * A circle is defined by a center point with a radius and an azimuth.
38  * The azimuth is the north angle to the semi-major axis, in degrees. By default, the semi-major axis is oriented to the north (0 degrees).
39  * \since QGIS 3.0
40  */
41 
42 
43 class CORE_EXPORT QgsCircle : public QgsEllipse
44 {
45   public:
46     QgsCircle();
47 
48     /**
49      * Constructs a circle by defining all the members.
50      * \param center The center of the circle.
51      * \param radius The radius of the circle.
52      * \param azimuth Angle in degrees started from the North to the first quadrant.
53      */
54     QgsCircle( const QgsPoint &center, double radius, double azimuth = 0 ) SIP_HOLDGIL;
55 
56     /**
57      * Constructs a circle by 2 points on the circle.
58      * The center point can have m value which is the result from the midpoint
59      * operation between \a pt1 and \a pt2. Z dimension is also supported and
60      * is retrieved from the first 3D point amongst \a pt1 and \a pt2.
61      * The radius is calculated from the 2D distance between \a pt1 and \a pt2.
62      * The azimuth is the angle between \a pt1 and \a pt2.
63      * \param pt1 First point.
64      * \param pt2 Second point.
65      */
66     static QgsCircle from2Points( const QgsPoint &pt1, const QgsPoint &pt2 ) SIP_HOLDGIL;
67 
68     /**
69      * Constructs a circle by 3 points on the circle.
70      * M value is dropped for the center point.
71      * Z dimension is supported and is retrieved from the first 3D point
72      * amongst \a pt1, \a pt2 and \a pt3.
73      * The azimuth always takes the default value.
74      * If the points are colinear an empty circle is returned.
75      * \param pt1 First point.
76      * \param pt2 Second point.
77      * \param pt3 Third point.
78      * \param epsilon Value used to compare point.
79      */
80     static QgsCircle from3Points( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon = 1E-8 ) SIP_HOLDGIL;
81 
82     /**
83      * Constructs a circle by a center point and a diameter.
84      * The center point keeps z and m values from \a center.
85      * \param center Center point.
86      * \param diameter Diameter of the circle.
87      * \param azimuth Azimuth of the circle.
88      */
89     static QgsCircle fromCenterDiameter( const QgsPoint &center, double diameter, double azimuth = 0 ) SIP_HOLDGIL;
90 
91 
92     /**
93      * Constructs a circle by a center point and another point.
94      * The center point keeps z and m values from \a center.
95      * Axes are calculated from the 2D distance between \a center and \a pt1.
96      * The azimuth is the angle between \a center and \a pt1.
97      * \param center Center point.
98      * \param pt1 A point on the circle.
99      */
100     static QgsCircle fromCenterPoint( const QgsPoint &center, const QgsPoint &pt1 ) SIP_HOLDGIL;
101 
102 
103     /**
104      * Constructs a circle by 3 tangents on the circle (aka inscribed circle of a triangle).
105      * Z and m values are dropped for the center point.
106      * The azimuth always takes the default value.
107      * \param pt1_tg1 First point of the first tangent.
108      * \param pt2_tg1 Second point of the first tangent.
109      * \param pt1_tg2 First point of the second tangent.
110      * \param pt2_tg2 Second point of the second tangent.
111      * \param pt1_tg3 First point of the third tangent.
112      * \param pt2_tg3 Second point of the third tangent.
113      * \param epsilon Value used to compare point.
114      * \param pos Point to determine which circle use in case of multi return.
115      * If the solution is not unique and pos is an empty point, an empty circle is returned. -- This case happens only when two tangets are parallels. (since QGIS 3.18)
116      *
117      * \see from3TangentsMulti()
118      *
119      * ### Example
120      *
121      * \code{.py}
122      *  # [(0 0), (5 0)] and [(5 5), (10 5)] are parallels
123      *  QgsCircle.from3Tangents(QgsPoint(0, 0), QgsPoint(5, 0), QgsPoint(5, 5), QgsPoint(10, 5), QgsPoint(2.5, 0), QgsPoint(7.5, 5))
124      *  # <QgsCircle: Empty>
125      *  QgsCircle.from3Tangents(QgsPoint(0, 0), QgsPoint(5, 0), QgsPoint(5, 5), QgsPoint(10, 5), QgsPoint(2.5, 0), QgsPoint(7.5, 5), pos=QgsPoint(2, 0))
126      *  # <QgsCircle: Circle (Center: Point (1.46446609406726203 2.49999999999999911), Radius: 2.5, Azimuth: 0)>
127      *  QgsCircle.from3Tangents(QgsPoint(0, 0), QgsPoint(5, 0), QgsPoint(5, 5), QgsPoint(10, 5), QgsPoint(2.5, 0), QgsPoint(7.5, 5), pos=QgsPoint(3, 0))
128      *  # <QgsCircle: Circle (Center: Point (8.53553390593273775 2.5), Radius: 2.5, Azimuth: 0)>
129      * \endcode
130      */
131     static QgsCircle from3Tangents( const QgsPoint &pt1_tg1, const QgsPoint &pt2_tg1,
132                                     const QgsPoint &pt1_tg2, const QgsPoint &pt2_tg2,
133                                     const QgsPoint &pt1_tg3, const QgsPoint &pt2_tg3,
134                                     double epsilon = 1E-8,
135                                     const QgsPoint &pos = QgsPoint() ) SIP_HOLDGIL;
136 
137     /**
138      * Returns an array of circle constructed by 3 tangents on the circle (aka inscribed circle of a triangle).
139      *
140      * The vector can contain 0, 1 or 2 circles:
141      *
142      * - 0: Impossible to construct a circle from 3 tangents (three parallel tangents)
143      * - 1: The three tangents make a triangle or when two tangents are parallel there are two possible circles (see examples).
144      *   If pos is not an empty point, we use its coordinates to determine which circle will be returned.
145      *   More precisely the circle that will be returned will be the one whose center is on the same side as pos relative to the third tangent.
146      * - 2: Returns both solutions when two tangents are parallel (this implies that pos is an empty point).
147      *
148      * Z and m values are dropped for the center point.
149      * The azimuth always takes the default value.
150      * \param pt1_tg1 First point of the first tangent.
151      * \param pt2_tg1 Second point of the first tangent.
152      * \param pt1_tg2 First point of the second tangent.
153      * \param pt2_tg2 Second point of the second tangent.
154      * \param pt1_tg3 First point of the third tangent.
155      * \param pt2_tg3 Second point of the third tangent.
156      * \param epsilon Value used to compare point.
157      * \param pos (optional) Point to determine which circle use in case of multi return.
158      *
159      * \see from3Tangents()
160      *
161      * ### Example
162      *
163      * \code{.py}
164      *
165      *  # [(0 0), (5 0)] and [(5 5), (10 5)] are parallels
166      *  QgsCircle.from3TangentsMulti(QgsPoint(0, 0), QgsPoint(5, 0), QgsPoint(5, 5), QgsPoint(10, 5), QgsPoint(2.5, 0), QgsPoint(7.5, 5))
167      *  # [<QgsCircle: Circle (Center: Point (8.53553390593273775 2.5), Radius: 2.5, Azimuth: 0)>, <QgsCircle: Circle (Center: Point (1.46446609406726203 2.49999999999999911), Radius: 2.5, Azimuth: 0)>]
168      *  QgsCircle.from3TangentsMulti(QgsPoint(0, 0), QgsPoint(5, 0), QgsPoint(5, 5), QgsPoint(10, 5), QgsPoint(2.5, 0), QgsPoint(7.5, 5), pos=QgsPoint(2, 0))
169      *  # [<QgsCircle: Circle (Center: Point (1.46446609406726203 2.49999999999999911), Radius: 2.5, Azimuth: 0)>]
170      *  QgsCircle.from3TangentsMulti(QgsPoint(0, 0), QgsPoint(5, 0), QgsPoint(5, 5), QgsPoint(10, 5), QgsPoint(2.5, 0), QgsPoint(7.5, 5), pos=QgsPoint(3, 0))
171      *  # [<QgsCircle: Circle (Center: Point (8.53553390593273775 2.5), Radius: 2.5, Azimuth: 0)>]
172      *  # [(0 0), (5 0)], [(5 5), (10 5)] and [(15 5), (20 5)] are parallels
173      *  QgsCircle.from3TangentsMulti(QgsPoint(0, 0), QgsPoint(5, 0), QgsPoint(5, 5), QgsPoint(10, 5), QgsPoint(15, 5), QgsPoint(20, 5))
174      *  # []
175      * \endcode
176      */
177     static QVector<QgsCircle> from3TangentsMulti( const QgsPoint &pt1_tg1, const QgsPoint &pt2_tg1,
178         const QgsPoint &pt1_tg2, const QgsPoint &pt2_tg2,
179         const QgsPoint &pt1_tg3, const QgsPoint &pt2_tg3,
180         double epsilon = 1E-8,
181         const QgsPoint &pos = QgsPoint() ) SIP_HOLDGIL;
182 
183     /**
184      * Constructs a circle by an extent (aka bounding box / QgsRectangle).
185      * The center point can have m value which is the result from the midpoint
186      * operation between \a pt1 and \a pt2. Z dimension is also supported and
187      * is retrieved from the first 3D point amongst \a pt1 and \a pt2.
188      * Axes are calculated from the 2D distance between \a pt1 and \a pt2.
189      * The azimuth always takes the default value.
190      * \param pt1 First corner.
191      * \param pt2 Second corner.
192      */
193     static QgsCircle fromExtent( const QgsPoint &pt1, const QgsPoint &pt2 ) SIP_HOLDGIL;
194 
195     /**
196      * Constructs the smallest circle from 3 points.
197      * Z and m values are dropped for the center point.
198      * The azimuth always takes the default value.
199      * If the points are colinear an empty circle is returned.
200      * \param pt1 First point.
201      * \param pt2 Second point.
202      * \param pt3 Third point.
203      * \param epsilon Value used to compare point.
204      */
205     static QgsCircle minimalCircleFrom3Points( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon = 1E-8 ) SIP_HOLDGIL;
206 
207     /**
208      * Calculates the intersections points between this circle and an \a other circle.
209      *
210      * If found, the intersection points will be stored in \a intersection1 and \a intersection2.
211      *
212      * By default this method does not consider any z values and instead treats the circles as 2-dimensional.
213      * If \a useZ is set to TRUE, then an intersection will only occur if the z values of both circles are
214      * equal. In this case the points returned for \a intersection1 and \a intersection2 will contain
215      * the z value of the circle intersections.
216      *
217      * \returns number of intersection points found.
218      *
219      * \since QGIS 3.2
220      */
221     int intersections( const QgsCircle &other, QgsPoint &intersection1 SIP_OUT, QgsPoint &intersection2 SIP_OUT, bool useZ = false ) const;
222 
223     /**
224      * Calculates the tangent points between this circle and the point \a p.
225      *
226      * If found, the tangent points will be stored in \a pt1 and \a pt2.
227      *
228      * Note that this method is 2D only and does not consider the z-value of the circle.
229      *
230      * \returns TRUE if tangent was found.
231      *
232      *
233      * \see outerTangents() and innerTangents()
234      * \since QGIS 3.2
235      */
236     bool tangentToPoint( const QgsPointXY &p, QgsPointXY &pt1 SIP_OUT, QgsPointXY &pt2 SIP_OUT ) const;
237 
238     /**
239      * Calculates the outer tangent points between this circle
240      * and an \a other circle.
241      *
242      * The outer tangent points correspond to the points at which the two lines
243      * which are drawn so that they are tangential to both circles touch
244      * the circles.
245      *
246      * The first tangent line is described by the points
247      * stored in \a line1P1 and \a line1P2,
248      * and the second line is described by the points stored in \a line2P1
249      * and \a line2P2.
250      *
251      * Returns the number of tangents (either 0 or 2).
252      *
253      * Note that this method is 2D only and does not consider the z-value of the circle.
254      *
255      *
256      * \see tangentToPoint() and innerTangents()
257      * \since QGIS 3.2
258      */
259     int outerTangents( const QgsCircle &other,
260                        QgsPointXY &line1P1 SIP_OUT, QgsPointXY &line1P2 SIP_OUT,
261                        QgsPointXY &line2P1 SIP_OUT, QgsPointXY &line2P2 SIP_OUT ) const;
262 
263     /**
264      * Calculates the inner tangent points between this circle
265      * and an \a other circle.
266      *
267      * The inner tangent points correspond to the points at which the two lines
268      * which are drawn so that they are tangential to both circles but on
269      * different sides, touching the circles and crossing each other.
270      *
271      * The first tangent line is described by the points
272      * stored in \a line1P1 and \a line1P2,
273      * and the second line is described by the points stored in \a line2P1
274      * and \a line2P2.
275      *
276      * Returns the number of tangents (either 0 or 2).
277      *
278      * Note that this method is 2D only and does not consider the z-value of the circle.
279      *
280      *
281      * \see tangentToPoint() and outerTangents()
282      * \since QGIS 3.6
283      */
284     int innerTangents( const QgsCircle &other,
285                        QgsPointXY &line1P1 SIP_OUT, QgsPointXY &line1P2 SIP_OUT,
286                        QgsPointXY &line2P1 SIP_OUT, QgsPointXY &line2P2 SIP_OUT ) const;
287 
288     double area() const override SIP_HOLDGIL;
289     double perimeter() const override SIP_HOLDGIL;
290 
291     //inherited
292     // void setAzimuth(const double azimuth);
293     // double azimuth() const {return mAzimuth; }
294 
295 
296     /**
297      * Inherited method. Use setRadius instead.
298      * \see radius()
299      * \see setRadius()
300      */
301     void setSemiMajorAxis( double semiMajorAxis ) override SIP_HOLDGIL;
302 
303     /**
304      * Inherited method. Use setRadius instead.
305      * \see radius()
306      * \see setRadius()
307      */
308     void setSemiMinorAxis( double semiMinorAxis ) override SIP_HOLDGIL;
309 
310     //! Returns the radius of the circle
radius()311     double radius() const SIP_HOLDGIL {return mSemiMajorAxis;}
312     //! Sets the radius of the circle
setRadius(double radius)313     void setRadius( double radius ) SIP_HOLDGIL
314     {
315       mSemiMajorAxis = std::fabs( radius );
316       mSemiMinorAxis = mSemiMajorAxis;
317     }
318 
319     /**
320      * The four quadrants of the ellipse.
321      * They are oriented and started from North.
322      * \return quadrants defined by four points.
323      * \see quadrant()
324      */
325     QVector<QgsPoint> northQuadrant() const SIP_FACTORY;
326 
327     /**
328      * Returns a circular string from the circle.
329      * \param oriented If oriented is TRUE the start point is from azimuth instead from north.
330      */
331     QgsCircularString *toCircularString( bool oriented = false ) const;
332 
333     //! Returns TRUE if the circle contains the \a point.
334     bool contains( const QgsPoint &point, double epsilon = 1E-8 ) const;
335 
336     QgsRectangle boundingBox() const override;
337 
338     QString toString( int pointPrecision = 17, int radiusPrecision = 17, int azimuthPrecision = 2 ) const override;
339 
340     /**
341      * Returns a GML2 representation of the geometry.
342      * Since GML2 does not supports curve, it will be converted to a LineString.
343      * \param doc DOM document
344      * \param precision number of decimal places for coordinates
345      * \param ns XML namespace
346      * \param axisOrder Axis order for generated GML
347      * \see asGml3()
348      */
349     QDomElement asGml2( QDomDocument &doc, int precision = 17, const QString &ns = "gml", QgsAbstractGeometry::AxisOrder axisOrder = QgsAbstractGeometry::AxisOrder::XY ) const;
350 
351     /**
352      * Returns a GML3 representation of the geometry.
353      *
354      * From the GML3 description:
355      * A Circle is an arc whose ends coincide to form a simple closed loop.
356      * The three control points shall be distinct non-co-linear points for
357      * the circle to be unambiguously defined. The arc is simply extended
358      * past the third control point until the first control point is encountered.
359      *
360      * Coordinates are taken from quadrant North, East and South.
361      *
362      * \param doc DOM document
363      * \param precision number of decimal places for coordinates
364      * \param ns XML namespace
365      * \param axisOrder Axis order for generated GML
366      * \see asGml2()
367      */
368     QDomElement asGml3( QDomDocument &doc, int precision = 17, const QString &ns = "gml", QgsAbstractGeometry::AxisOrder axisOrder = QgsAbstractGeometry::AxisOrder::XY ) const;
369 
370 #ifdef SIP_RUN
371     SIP_PYOBJECT __repr__();
372     % MethodCode
373     QString str = QStringLiteral( "<QgsCircle: %1>" ).arg( sipCpp->toString() );
374     sipRes = PyUnicode_FromString( str.toUtf8().constData() );
375     % End
376 #endif
377 };
378 
379 #endif // QGSCIRCLE_H
380