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 ¢er, 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 ¢er, 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 ¢er, 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