1 /*************************************************************************** 2 qgsgeometryutils.h 3 ------------------------------------------------------------------- 4 Date : 21 Nov 2014 5 Copyright : (C) 2014 by Marco Hugentobler 6 email : marco.hugentobler at sourcepole dot com 7 *************************************************************************** 8 * * 9 * This program is free software; you can redistribute it and/or modify * 10 * it under the terms of the GNU General Public License as published by * 11 * the Free Software Foundation; either version 2 of the License, or * 12 * (at your option) any later version. * 13 * * 14 ***************************************************************************/ 15 16 #ifndef QGSGEOMETRYUTILS_H 17 #define QGSGEOMETRYUTILS_H 18 19 #include <limits> 20 21 #include "qgis_core.h" 22 #include "qgis_sip.h" 23 #include "qgspoint.h" 24 #include "qgsvertexid.h" 25 #include "qgsgeometry.h" 26 #include "qgsvector3d.h" 27 28 #include <QJsonArray> 29 30 class QgsLineString; 31 32 /** 33 * \ingroup core 34 * \class QgsGeometryUtils 35 * \brief Contains various geometry utility functions. 36 * \since QGIS 2.10 37 */ 38 class CORE_EXPORT QgsGeometryUtils 39 { 40 public: 41 42 /** 43 * Returns list of linestrings extracted from the passed geometry. The returned objects 44 * have to be deleted by the caller. 45 */ 46 static QVector<QgsLineString *> extractLineStrings( const QgsAbstractGeometry *geom ) SIP_FACTORY; 47 48 /** 49 * Returns the closest vertex to a geometry for a specified point. 50 * On error null point will be returned and "id" argument will be invalid. 51 */ 52 static QgsPoint closestVertex( const QgsAbstractGeometry &geom, const QgsPoint &pt, QgsVertexId &id SIP_OUT ); 53 54 /** 55 * Returns the nearest point on a segment of a \a geometry 56 * for the specified \a point. The z and m values will be linearly interpolated between 57 * the two neighbouring vertices. 58 */ 59 static QgsPoint closestPoint( const QgsAbstractGeometry &geometry, const QgsPoint &point ); 60 61 /** 62 * Returns the distance along a geometry from its first vertex to the specified vertex. 63 * \param geom geometry 64 * \param id vertex id to find distance to 65 * \returns distance to vertex (following geometry) 66 * \since QGIS 2.16 67 */ 68 static double distanceToVertex( const QgsAbstractGeometry &geom, QgsVertexId id ); 69 70 /** 71 * Retrieves the vertices which are before and after the interpolated point at a specified distance along a linestring 72 * (or polygon boundary). 73 * \param geometry line or polygon geometry 74 * \param distance distance to traverse along geometry 75 * \param previousVertex will be set to previous vertex ID 76 * \param nextVertex will be set to next vertex ID 77 * \returns TRUE if vertices were successfully retrieved 78 * \note if the distance coincides exactly with a vertex, then both previousVertex and nextVertex will be set to this vertex 79 * \since QGIS 3.0 80 */ 81 static bool verticesAtDistance( const QgsAbstractGeometry &geometry, 82 double distance, 83 QgsVertexId &previousVertex SIP_OUT, 84 QgsVertexId &nextVertex SIP_OUT ); 85 86 /** 87 * Returns the squared 2D distance between two points. 88 */ 89 static double sqrDistance2D( const QgsPoint &pt1, const QgsPoint &pt2 ) SIP_HOLDGIL; 90 91 /** 92 * Returns the squared distance between a point and a line. 93 */ 94 static double sqrDistToLine( double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX SIP_OUT, double &minDistY SIP_OUT, double epsilon ) SIP_HOLDGIL; 95 96 /** 97 * Computes the intersection between two lines. Z dimension is 98 * supported and is retrieved from the first 3D point amongst \a p1 and 99 * \a p2. 100 * \param p1 Point on the first line 101 * \param v1 Direction vector of the first line 102 * \param p2 Point on the second line 103 * \param v2 Direction vector of the second line 104 * \param intersection Output parameter, the intersection point 105 * \returns Whether the lines intersect 106 */ 107 static bool lineIntersection( const QgsPoint &p1, QgsVector v1, const QgsPoint &p2, QgsVector v2, QgsPoint &intersection SIP_OUT ) SIP_HOLDGIL; 108 109 /** 110 * \brief Compute the intersection between two segments 111 * \param p1 First segment start point 112 * \param p2 First segment end point 113 * \param q1 Second segment start point 114 * \param q2 Second segment end point 115 * \param intersectionPoint Output parameter, the intersection point 116 * \param isIntersection Output parameter, return TRUE if an intersection is found 117 * \param tolerance The tolerance to use 118 * \param acceptImproperIntersection By default, this method returns TRUE only if segments have proper intersection. If set true, returns also TRUE if segments have improper intersection (end of one segment on other segment ; continuous segments). 119 * \returns Whether the segments intersect 120 * 121 * ### Example 122 * 123 * \code{.py} 124 * ret = QgsGeometryUtils.segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 1 ), QgsPoint( 1, 1 ), QgsPoint( 1, 0 ) ) 125 * ret[0], ret[1].asWkt(), ret[2] 126 * # Whether the segments intersect, the intersection point, is intersect 127 * # (False, 'Point (0 0)', False) 128 * ret = QgsGeometryUtils.segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 5 ), QgsPoint( 1, 5 ) ) 129 * ret[0], ret[1].asWkt(), ret[2] 130 * # (False, 'Point (0 5)', True) 131 * ret = QgsGeometryUtils.segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 5 ), QgsPoint( 1, 5 ), acceptImproperIntersection=True ) 132 * ret[0], ret[1].asWkt(), ret[2] 133 * # (True, 'Point (0 5)', True) 134 * ret = QgsGeometryUtils.segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 2 ), QgsPoint( 1, 5 ) ) 135 * ret[0], ret[1].asWkt(), ret[2] 136 * # (False, 'Point (0 2)', True) 137 * ret = QgsGeometryUtils.segmentIntersection( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 2 ), QgsPoint( 1, 5 ), acceptImproperIntersection=True ) 138 * ret[0], ret[1].asWkt(), ret[2] 139 * # (True, 'Point (0 2)', True) 140 * ret = QgsGeometryUtils.segmentIntersection( QgsPoint( 0, -5 ), QgsPoint( 0, 5 ), QgsPoint( 2, 0 ), QgsPoint( -1, 0 ) ) 141 * ret[0], ret[1].asWkt(), ret[2] 142 * # (True, 'Point (0 0)', True) 143 * \endcode 144 */ 145 static bool segmentIntersection( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q1, const QgsPoint &q2, QgsPoint &intersectionPoint SIP_OUT, bool &isIntersection SIP_OUT, double tolerance = 1e-8, bool acceptImproperIntersection = false ) SIP_HOLDGIL; 146 147 /** 148 * \brief Compute the intersection of a line and a circle. 149 * If the intersection has two solutions (points), 150 * the closest point to the initial \a intersection point is returned. 151 * \param center the center of the circle 152 * \param radius the radius of the circle 153 * \param linePoint1 a first point on the line 154 * \param linePoint2 a second point on the line 155 * \param intersection the initial point and the returned intersection point 156 * \return TRUE if an intersection has been found 157 */ 158 static bool lineCircleIntersection( const QgsPointXY ¢er, double radius, 159 const QgsPointXY &linePoint1, const QgsPointXY &linePoint2, 160 QgsPointXY &intersection SIP_INOUT ) SIP_HOLDGIL; 161 162 /** 163 * Calculates the intersections points between the circle with center \a center1 and 164 * radius \a radius1 and the circle with center \a center2 and radius \a radius2. 165 * 166 * If found, the intersection points will be stored in \a intersection1 and \a intersection2. 167 * 168 * \returns number of intersection points found. 169 * 170 * \since QGIS 3.2 171 */ 172 static int circleCircleIntersections( const QgsPointXY ¢er1, double radius1, 173 const QgsPointXY ¢er2, double radius2, 174 QgsPointXY &intersection1 SIP_OUT, QgsPointXY &intersection2 SIP_OUT ) SIP_HOLDGIL; 175 176 /** 177 * Calculates the tangent points between the circle with the specified \a center and \a radius 178 * and the point \a p. 179 * 180 * If found, the tangent points will be stored in \a pt1 and \a pt2. 181 * 182 * \since QGIS 3.2 183 */ 184 static bool tangentPointAndCircle( const QgsPointXY ¢er, double radius, 185 const QgsPointXY &p, QgsPointXY &pt1 SIP_OUT, QgsPointXY &pt2 SIP_OUT ) SIP_HOLDGIL; 186 187 /** 188 * Calculates the outer tangent points for two circles, centered at \a center1 and 189 * \a center2 and with radii of \a radius1 and \a radius2 respectively. 190 * 191 * The outer tangent points correspond to the points at which the two lines 192 * which are drawn so that they are tangential to both circles touch 193 * the circles. 194 * 195 * The first tangent line is described by the points 196 * stored in \a line1P1 and \a line1P2, 197 * and the second line is described by the points stored in \a line2P1 198 * and \a line2P2. 199 * 200 * Returns the number of tangents (either 0 or 2). 201 * 202 * \since QGIS 3.2 203 */ 204 static int circleCircleOuterTangents( 205 const QgsPointXY ¢er1, double radius1, const QgsPointXY ¢er2, double radius2, 206 QgsPointXY &line1P1 SIP_OUT, QgsPointXY &line1P2 SIP_OUT, 207 QgsPointXY &line2P1 SIP_OUT, QgsPointXY &line2P2 SIP_OUT ) SIP_HOLDGIL; 208 209 /** 210 * Calculates the inner tangent points for two circles, centered at \a 211 * center1 and \a center2 and with radii of \a radius1 and \a radius2 212 * respectively. 213 * 214 * The inner tangent points correspond to the points at which the two lines 215 * which are drawn so that they are tangential to both circles and are 216 * crossing each other. 217 * 218 * The first tangent line is described by the points 219 * stored in \a line1P1 and \a line1P2, 220 * and the second line is described by the points stored in \a line2P1 221 * and \a line2P2. 222 * 223 * Returns the number of tangents (either 0 or 2). 224 * 225 * \since QGIS 3.6 226 */ 227 static int circleCircleInnerTangents( 228 const QgsPointXY ¢er1, double radius1, const QgsPointXY ¢er2, double radius2, 229 QgsPointXY &line1P1 SIP_OUT, QgsPointXY &line1P2 SIP_OUT, 230 QgsPointXY &line2P1 SIP_OUT, QgsPointXY &line2P2 SIP_OUT ) SIP_HOLDGIL; 231 232 /** 233 * \brief Project the point on a segment 234 * \param p The point 235 * \param s1 The segment start point 236 * \param s2 The segment end point 237 * \returns The projection of the point on the segment 238 */ projectPointOnSegment(const QgsPoint & p,const QgsPoint & s1,const QgsPoint & s2)239 static QgsPoint projectPointOnSegment( const QgsPoint &p, const QgsPoint &s1, const QgsPoint &s2 ) SIP_HOLDGIL 240 { 241 const double nx = s2.y() - s1.y(); 242 const double ny = -( s2.x() - s1.x() ); 243 const double t = ( p.x() * ny - p.y() * nx - s1.x() * ny + s1.y() * nx ) / ( ( s2.x() - s1.x() ) * ny - ( s2.y() - s1.y() ) * nx ); 244 return t < 0. ? s1 : t > 1. ? s2 : QgsPoint( s1.x() + ( s2.x() - s1.x() ) * t, s1.y() + ( s2.y() - s1.y() ) * t ); 245 } 246 247 //! \note not available in Python bindings 248 struct SelfIntersection SIP_SKIP 249 { 250 int segment1; 251 int segment2; 252 QgsPoint point; 253 }; 254 255 /** 256 * \brief Find self intersections in a polyline 257 * \param geom The geometry to check 258 * \param part The part of the geometry to check 259 * \param ring The ring of the geometry part to check 260 * \param tolerance The tolerance to use 261 * \returns The list of self intersections 262 * \note not available in Python bindings 263 * \since QGIS 2.12 264 */ 265 static QVector<SelfIntersection> selfIntersections( const QgsAbstractGeometry *geom, int part, int ring, double tolerance ) SIP_SKIP; 266 267 /** 268 * Returns a value < 0 if the point (\a x, \a y) is left of the line from (\a x1, \a y1) -> (\a x2, \a y2). 269 * A positive return value indicates the point is to the right of the line. 270 * 271 * If the return value is 0, then the test was unsuccessful (e.g. due to testing a point exactly 272 * on the line, or exactly in line with the segment) and the result is undefined. 273 */ 274 static int leftOfLine( const double x, const double y, const double x1, const double y1, const double x2, const double y2 ) SIP_HOLDGIL; 275 276 /** 277 * Returns a value < 0 if the point \a point is left of the line from \a p1 -> \a p2. 278 * A positive return value indicates the point is to the right of the line. 279 * 280 * If the return value is 0, then the test was unsuccessful (e.g. due to testing a point exactly 281 * on the line, or exactly in line with the segment) and the result is undefined. 282 * 283 * \since QGIS 3.6 284 */ 285 static int leftOfLine( const QgsPoint &point, const QgsPoint &p1, const QgsPoint &p2 ) SIP_HOLDGIL; 286 287 /** 288 * Returns a point a specified \a distance toward a second point. 289 */ 290 static QgsPoint pointOnLineWithDistance( const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance ) SIP_HOLDGIL; 291 292 /** 293 * Calculates the point a specified \a distance from (\a x1, \a y1) toward a second point (\a x2, \a y2). 294 * 295 * Optionally, interpolated z and m values can be obtained by specifying the \a z1, \a z2 and \a z arguments 296 * and/or the \a m1, \a m2, \a m arguments. 297 * 298 * \note Not available in Python bindings 299 * \since QGIS 3.4 300 */ 301 static void pointOnLineWithDistance( double x1, double y1, double x2, double y2, double distance, double &x, double &y, 302 double *z1 = nullptr, double *z2 = nullptr, double *z = nullptr, 303 double *m1 = nullptr, double *m2 = nullptr, double *m = nullptr ) SIP_SKIP; 304 305 /** 306 * Calculates a point a certain \a proportion of the way along the segment from (\a x1, \a y1) to (\a x2, \a y2), 307 * offset from the segment by the specified \a offset amount. 308 * 309 * \param x1 x-coordinate of start of segment 310 * \param y1 y-coordinate of start of segment 311 * \param x2 x-coordinate of end of segment 312 * \param y2 y-coordinate of end of segment 313 * \param proportion proportion of the segment's length at which to place the point (between 0.0 and 1.0) 314 * \param offset perpendicular offset from segment to apply to point. A negative \a offset shifts the point to the left of the segment, while a positive \a offset will shift it to the right of the segment. 315 * \param x calculated point x-coordinate 316 * \param y calculated point y-coordinate 317 * 318 * ### Example 319 * 320 * \code{.py} 321 * # Offset point at center of segment by 2 units to the right 322 * x, y = QgsGeometryUtils.perpendicularOffsetPointAlongSegment( 1, 5, 11, 5, 0.5, 2 ) 323 * # (6.0, 3.0) 324 * 325 * # Offset point at center of segment by 2 units to the left 326 * x, y = QgsGeometryUtils.perpendicularOffsetPointAlongSegment( 1, 5, 11, 5, 0.5, -2 ) 327 * # (6.0, 7.0) 328 * \endcode 329 * 330 * \since QGIS 3.20 331 */ 332 static void perpendicularOffsetPointAlongSegment( double x1, double y1, double x2, double y2, double proportion, double offset, double *x SIP_OUT, double *y SIP_OUT ); 333 334 /** 335 * Interpolates a point on an arc defined by three points, \a pt1, \a pt2 and \a pt3. The arc will be 336 * interpolated by the specified \a distance from \a pt1. 337 * 338 * Any z or m values present in the points will also be linearly interpolated in the output. 339 * 340 * \since QGIS 3.4 341 */ 342 static QgsPoint interpolatePointOnArc( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double distance ) SIP_HOLDGIL; 343 344 //! Returns the counter clockwise angle between a line with components dx, dy and the line with dx > 0 and dy = 0 345 static double ccwAngle( double dy, double dx ) SIP_HOLDGIL; 346 347 //! Returns radius and center of the circle through pt1, pt2, pt3 348 static void circleCenterRadius( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double &radius SIP_OUT, 349 double ¢erX SIP_OUT, double ¢erY SIP_OUT ) SIP_HOLDGIL; 350 351 /** 352 * Returns TRUE if the circle defined by three angles is ordered clockwise. 353 * 354 * The angles are defined counter-clockwise from the origin, i.e. using 355 * Euclidean angles as opposed to geographic "North up" angles. 356 */ 357 static bool circleClockwise( double angle1, double angle2, double angle3 ) SIP_HOLDGIL; 358 359 //! Returns TRUE if, in a circle, angle is between angle1 and angle2 360 static bool circleAngleBetween( double angle, double angle1, double angle2, bool clockwise ) SIP_HOLDGIL; 361 362 /** 363 * Returns TRUE if an angle is between angle1 and angle3 on a circle described by 364 * angle1, angle2 and angle3. 365 */ 366 static bool angleOnCircle( double angle, double angle1, double angle2, double angle3 ) SIP_HOLDGIL; 367 368 //! Length of a circular string segment defined by pt1, pt2, pt3 369 static double circleLength( double x1, double y1, double x2, double y2, double x3, double y3 ) SIP_HOLDGIL; 370 371 //! Calculates angle of a circular string part defined by pt1, pt2, pt3 372 static double sweepAngle( double centerX, double centerY, double x1, double y1, double x2, double y2, double x3, double y3 ) SIP_HOLDGIL; 373 374 /** 375 * Calculates midpoint on circle passing through \a p1 and \a p2, closest to 376 * the given coordinate \a mousePos. Z dimension is supported and is retrieved from the 377 * first 3D point amongst \a p1 and \a p2. 378 * \see segmentMidPointFromCenter() 379 */ 380 static bool segmentMidPoint( const QgsPoint &p1, const QgsPoint &p2, QgsPoint &result SIP_OUT, double radius, const QgsPoint &mousePos ) SIP_HOLDGIL; 381 382 /** 383 * Calculates the midpoint on the circle passing through \a p1 and \a p2, 384 * with the specified \a center coordinate. 385 * 386 * If \a useShortestArc is TRUE, then the midpoint returned will be that corresponding 387 * to the shorter arc from \a p1 to \a p2. If it is FALSE, the longer arc from \a p1 388 * to \a p2 will be used (i.e. winding the other way around the circle). 389 * 390 * \see segmentMidPoint() 391 * \since QGIS 3.2 392 */ 393 static QgsPoint segmentMidPointFromCenter( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint ¢er, bool useShortestArc = true ) SIP_HOLDGIL; 394 395 //! Calculates the direction angle of a circle tangent (clockwise from north in radians) 396 static double circleTangentDirection( const QgsPoint &tangentPoint, const QgsPoint &cp1, const QgsPoint &cp2, const QgsPoint &cp3 ) SIP_HOLDGIL; 397 398 /** 399 * Convert circular arc defined by p1, p2, p3 (p1/p3 being start resp. end point, p2 lies on the arc) into a sequence of points. 400 * \since 3.0 401 */ 402 static void segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, 403 QgsPointSequence SIP_PYALTERNATIVETYPE( QVector<QgsPoint> ) &points SIP_OUT, double tolerance = M_PI_2 / 90, 404 QgsAbstractGeometry::SegmentationToleranceType toleranceType = QgsAbstractGeometry::MaximumAngle, 405 bool hasZ = false, bool hasM = false ); 406 407 /** 408 * Returns TRUE if point \a b is on the arc formed by points \a a1, \a a2, and \a a3, but not within 409 * that arc portion already described by \a a1, \a a2 and \a a3. 410 * 411 * The \a distanceTolerance specifies the maximum deviation allowed between the original location 412 * of point \b and where it would fall on the candidate arc. 413 * 414 * This method only consider a segments as continuing an arc if the points are all regularly spaced 415 * on the candidate arc. The \a pointSpacingAngleTolerance parameter specifies the maximum 416 * angular deviation (in radians) allowed when testing for regular point spacing. 417 * 418 * \note The API is considered EXPERIMENTAL and can be changed without a notice 419 * 420 * \since QGIS 3.14 421 */ 422 static bool pointContinuesArc( const QgsPoint &a1, const QgsPoint &a2, const QgsPoint &a3, const QgsPoint &b, double distanceTolerance, 423 double pointSpacingAngleTolerance ) SIP_HOLDGIL; 424 425 /** 426 * For line defined by points pt1 and pt3, find out on which side of the line is point pt3. 427 * Returns -1 if pt3 on the left side, 1 if pt3 is on the right side or 0 if pt3 lies on the line. 428 * \since 3.0 429 */ 430 static int segmentSide( const QgsPoint &pt1, const QgsPoint &pt3, const QgsPoint &pt2 ) SIP_HOLDGIL; 431 432 /** 433 * Interpolate a value at given angle on circular arc given values (zm1, zm2, zm3) at three different angles (a1, a2, a3). 434 * \since 3.0 435 */ 436 static double interpolateArcValue( double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3 ) SIP_HOLDGIL; 437 438 /** 439 * Returns a list of points contained in a WKT string. 440 * \note not available in Python bindings 441 */ 442 static QgsPointSequence pointsFromWKT( const QString &wktCoordinateList, bool is3D, bool isMeasure ) SIP_SKIP; 443 444 /** 445 * Returns a LinearRing { uint32 numPoints; Point points[numPoints]; } 446 * \note not available in Python bindings 447 */ 448 static void pointsToWKB( QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure ) SIP_SKIP; 449 450 /** 451 * Returns a WKT coordinate list 452 * \note not available in Python bindings 453 */ 454 static QString pointsToWKT( const QgsPointSequence &points, int precision, bool is3D, bool isMeasure ) SIP_SKIP; 455 456 /** 457 * Returns a gml::coordinates DOM element. 458 * \note not available in Python bindings 459 */ 460 static QDomElement pointsToGML2( const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, QgsAbstractGeometry::AxisOrder axisOrder = QgsAbstractGeometry::AxisOrder::XY ) SIP_SKIP; 461 462 /** 463 * Returns a gml::posList DOM element. 464 * \note not available in Python bindings 465 */ 466 static QDomElement pointsToGML3( const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D, QgsAbstractGeometry::AxisOrder axisOrder = QgsAbstractGeometry::AxisOrder::XY ) SIP_SKIP; 467 468 /** 469 * Returns a geoJSON coordinates string. 470 * \note not available in Python bindings 471 */ 472 static QString pointsToJSON( const QgsPointSequence &points, int precision ) SIP_SKIP; 473 474 /** 475 * Returns coordinates as json object. 476 * \note not available in Python bindings 477 */ 478 static json pointsToJson( const QgsPointSequence &points, int precision ) SIP_SKIP; 479 480 /** 481 * Ensures that an angle is in the range 0 <= angle < 2 pi. 482 * \param angle angle in radians 483 * \returns equivalent angle within the range [0, 2 pi) 484 */ 485 static double normalizedAngle( double angle ) SIP_HOLDGIL; 486 487 /** 488 * Calculates the direction of line joining two points in radians, clockwise from the north direction. 489 * \param x1 x-coordinate of line start 490 * \param y1 y-coordinate of line start 491 * \param x2 x-coordinate of line end 492 * \param y2 y-coordinate of line end 493 * \returns angle in radians. Returned value is undefined if start and end point are the same. 494 */ 495 static double lineAngle( double x1, double y1, double x2, double y2 ) SIP_HOLDGIL; 496 497 /** 498 * Calculates the angle between the lines AB and BC, where AB and BC described 499 * by points a, b and b, c. 500 * \param x1 x-coordinate of point a 501 * \param y1 y-coordinate of point a 502 * \param x2 x-coordinate of point b 503 * \param y2 y-coordinate of point b 504 * \param x3 x-coordinate of point c 505 * \param y3 y-coordinate of point c 506 * \returns angle between lines in radians. Returned value is undefined if two or more points are equal. 507 */ 508 static double angleBetweenThreePoints( double x1, double y1, double x2, double y2, 509 double x3, double y3 ) SIP_HOLDGIL; 510 511 /** 512 * Calculates the perpendicular angle to a line joining two points. Returned angle is in radians, 513 * clockwise from the north direction. 514 * \param x1 x-coordinate of line start 515 * \param y1 y-coordinate of line start 516 * \param x2 x-coordinate of line end 517 * \param y2 y-coordinate of line end 518 * \returns angle in radians. Returned value is undefined if start and end point are the same. 519 */ 520 static double linePerpendicularAngle( double x1, double y1, double x2, double y2 ) SIP_HOLDGIL; 521 522 /** 523 * Calculates the average angle (in radians) between the two linear segments from 524 * (\a x1, \a y1) to (\a x2, \a y2) and (\a x2, \a y2) to (\a x3, \a y3). 525 */ 526 static double averageAngle( double x1, double y1, double x2, double y2, double x3, double y3 ) SIP_HOLDGIL; 527 528 /** 529 * Averages two angles, correctly handling negative angles and ensuring the result is between 0 and 2 pi. 530 * \param a1 first angle (in radians) 531 * \param a2 second angle (in radians) 532 * \returns average angle (in radians) 533 */ 534 static double averageAngle( double a1, double a2 ) SIP_HOLDGIL; 535 536 /** 537 * Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents ("Pair(wkbType, "contents")") 538 * \note not available in Python bindings 539 */ 540 static QPair<QgsWkbTypes::Type, QString> wktReadBlock( const QString &wkt ) SIP_SKIP; 541 542 /** 543 * Parses a WKT string and returns of list of blocks contained in the WKT. 544 * \param wkt WKT string in the format "TYPE1 (contents1), TYPE2 (TYPE3 (contents3), TYPE4 (contents4))" 545 * \param defaultType default geometry type for children 546 * \returns list of WKT child block strings, e.g., List("TYPE1 (contents1)", "TYPE2 (TYPE3 (contents3), TYPE4 (contents4))") 547 * \note not available in Python bindings 548 */ 549 static QStringList wktGetChildBlocks( const QString &wkt, const QString &defaultType = QString() ) SIP_SKIP; 550 551 /** 552 * Returns a number representing the closest side of a rectangle defined by /a right, 553 * \a bottom, \a left, \a top to the point at (\a x, \a y), where 554 * the point may be in the interior of the rectangle or outside it. 555 * 556 * The returned value may be: 557 * 558 * 1. Point is closest to top side of rectangle 559 * 2. Point is located on the top-right diagonal of rectangle, equally close to the top and right sides 560 * 3. Point is closest to right side of rectangle 561 * 4. Point is located on the bottom-right diagonal of rectangle, equally close to the bottom and right sides 562 * 5. Point is closest to bottom side of rectangle 563 * 6. Point is located on the bottom-left diagonal of rectangle, equally close to the bottom and left sides 564 * 7. Point is closest to left side of rectangle 565 * 8. Point is located on the top-left diagonal of rectangle, equally close to the top and left sides 566 * 567 * \note This method effectively partitions the space outside of the rectangle into Voronoi cells, so a point 568 * to the top left of the rectangle may be assigned to the left or top sides based on its position relative 569 * to the diagonal line extended from the rectangle's top-left corner. 570 * 571 * \since QGIS 3.20 572 */ 573 static int closestSideOfRectangle( double right, double bottom, double left, double top, double x, double y ); 574 575 /** 576 * Returns a middle point between points pt1 and pt2. 577 * Z value is computed if one of this point have Z. 578 * M value is computed if one of this point have M. 579 * \param pt1 first point. 580 * \param pt2 second point. 581 * \returns New point at middle between points pt1 and pt2. 582 * 583 * ### Example 584 * 585 * \code{.py} 586 * p = QgsPoint( 4, 6 ) # 2D point 587 * pr = midpoint ( p, QgsPoint( 2, 2 ) ) 588 * # pr is a 2D point: 'Point (3 4)' 589 * pr = midpoint ( p, QgsPoint( QgsWkbTypes.PointZ, 2, 2, 2 ) ) 590 * # pr is a 3D point: 'PointZ (3 4 1)' 591 * pr = midpoint ( p, QgsPoint( QgsWkbTypes.PointM, 2, 2, 0, 2 ) ) 592 * # pr is a 3D point: 'PointM (3 4 1)' 593 * pr = midpoint ( p, QgsPoint( QgsWkbTypes.PointZM, 2, 2, 2, 2 ) ) 594 * # pr is a 3D point: 'PointZM (3 4 1 1)' 595 * \endcode 596 * \since QGIS 3.0 597 */ 598 static QgsPoint midpoint( const QgsPoint &pt1, const QgsPoint &pt2 ) SIP_HOLDGIL; 599 600 /** 601 * Interpolates the position of a point a \a fraction of the way along 602 * the line from (\a x1, \a y1) to (\a x2, \a y2). 603 * 604 * Usually the \a fraction should be between 0 and 1, where 0 represents the 605 * point at the start of the line (\a x1, \a y1) and 1 represents 606 * the end of the line (\a x2, \a y2). However, it is possible to 607 * use a \a fraction < 0 or > 1, in which case the returned point 608 * is extrapolated from the supplied line. 609 * 610 * \see interpolatePointOnLineByValue() 611 * \since QGIS 3.0.2 612 */ 613 static QgsPointXY interpolatePointOnLine( double x1, double y1, double x2, double y2, double fraction ) SIP_HOLDGIL; 614 615 /** 616 * Interpolates the position of a point a \a fraction of the way along 617 * the line from \a p1 to \a p2. 618 * 619 * Usually the \a fraction should be between 0 and 1, where 0 represents the 620 * point at the start of the line (\a p1) and 1 represents 621 * the end of the line (\a p2). However, it is possible to 622 * use a \a fraction < 0 or > 1, in which case the returned point 623 * is extrapolated from the supplied line. 624 * 625 * Any Z or M values present in the input points will also be interpolated 626 * and present in the returned point. 627 * 628 * \see interpolatePointOnLineByValue() 629 * \since QGIS 3.0.2 630 */ 631 static QgsPoint interpolatePointOnLine( const QgsPoint &p1, const QgsPoint &p2, double fraction ) SIP_HOLDGIL; 632 633 /** 634 * Interpolates the position of a point along the line from (\a x1, \a y1) 635 * to (\a x2, \a y2). 636 * 637 * The position is interpolated using a supplied target \a value and the value 638 * at the start of the line (\a v1) and end of the line (\a v2). The returned 639 * point will be linearly interpolated to match position corresponding to 640 * the target \a value. 641 * 642 * \see interpolatePointOnLine() 643 * \since QGIS 3.0.2 644 */ 645 static QgsPointXY interpolatePointOnLineByValue( double x1, double y1, double v1, double x2, double y2, double v2, double value ) SIP_HOLDGIL; 646 647 /** 648 * Returns the gradient of a line defined by points \a pt1 and \a pt2. 649 * \param pt1 first point. 650 * \param pt2 second point. 651 * \returns The gradient of this linear entity, or infinity if vertical 652 * \since QGIS 3.0 653 */ 654 static double gradient( const QgsPoint &pt1, const QgsPoint &pt2 ) SIP_HOLDGIL; 655 656 /** 657 * Returns the coefficients (a, b, c for equation "ax + by + c = 0") of a line defined by points \a pt1 and \a pt2. 658 * \param pt1 first point. 659 * \param pt2 second point. 660 * \param a Output parameter, a coefficient of the equation. 661 * \param b Output parameter, b coefficient of the equation. 662 * \param c Output parameter, c coefficient of the equation. 663 * \since QGIS 3.0 664 */ 665 static void coefficients( const QgsPoint &pt1, const QgsPoint &pt2, 666 double &a SIP_OUT, double &b SIP_OUT, double &c SIP_OUT ) SIP_HOLDGIL; 667 668 /** 669 * \brief Create a perpendicular line segment from p to segment [s1, s2] 670 * \param p The point 671 * \param s1 The segment start point 672 * \param s2 The segment end point 673 * \returns A line (segment) from p to perpendicular point on segment [s1, s2] 674 */ 675 static QgsLineString perpendicularSegment( const QgsPoint &p, const QgsPoint &s1, const QgsPoint &s2 ) SIP_HOLDGIL; 676 677 678 /** 679 * An algorithm to calculate the shortest distance between two skew lines. 680 * \param P1 is the first point of the first line, 681 * \param P12 is the second point on the first line, 682 * \param P2 is the first point on the second line, 683 * \param P22 is the second point on the second line. 684 * \return the shortest distance 685 */ 686 static double skewLinesDistance( const QgsVector3D &P1, const QgsVector3D &P12, 687 const QgsVector3D &P2, const QgsVector3D &P22 ) SIP_HOLDGIL; 688 689 /** 690 * A method to project one skew line onto another. 691 * \param P1 is a first point that belonds to first skew line, 692 * \param P12 is the second point that belongs to first skew line, 693 * \param P2 is the first point that belongs to second skew line, 694 * \param P22 is the second point that belongs to second skew line, 695 * \param X1 is the result projection point of line P2P22 onto line P1P12, 696 * \param epsilon the tolerance to use. 697 * \return TRUE if such point exists, FALSE - otherwise. 698 */ 699 static bool skewLinesProjection( const QgsVector3D &P1, const QgsVector3D &P12, 700 const QgsVector3D &P2, const QgsVector3D &P22, 701 QgsVector3D &X1 SIP_OUT, 702 double epsilon = 0.0001 ) SIP_HOLDGIL; 703 704 /** 705 * An algorithm to calculate an (approximate) intersection of two lines in 3D. 706 * \param La1 is the first point on the first line, 707 * \param La2 is the second point on the first line, 708 * \param Lb1 is the first point on the second line, 709 * \param Lb2 is the second point on the second line, 710 * \param intersection is the result intersection, of it can be found. 711 * \return TRUE if the intersection can be found, FALSE - otherwise. 712 * 713 * ### Example 714 * 715 * \code{.py} 716 * QgsGeometryUtils.linesIntersection3D(QgsVector3D(0,0,0), QgsVector3D(5,0,0), QgsVector3D(2,1,0), QgsVector3D(2,3,0)) 717 * # (True, PyQt5.QtGui.QgsVector3D(2.0, 0.0, 0.0)) 718 * QgsGeometryUtils.linesIntersection3D(QgsVector3D(0,0,0), QgsVector3D(5,0,0), QgsVector3D(2,1,0), QgsVector3D(2,0,0)) 719 * # (True, PyQt5.QtGui.QgsVector3D(2.0, 0.0, 0.0)) 720 * QgsGeometryUtils.linesIntersection3D(QgsVector3D(0,0,0), QgsVector3D(5,0,0), QgsVector3D(0,1,0), QgsVector3D(0,3,0)) 721 * # (True, PyQt5.QtGui.QgsVector3D(0.0, 0.0, 0.0)) 722 * QgsGeometryUtils.linesIntersection3D(QgsVector3D(0,0,0), QgsVector3D(5,0,0), QgsVector3D(0,1,0), QgsVector3D(0,0,0)) 723 * # (True, PyQt5.QtGui.QgsVector3D(0.0, 0.0, 0.0)) 724 * QgsGeometryUtils.linesIntersection3D(QgsVector3D(0,0,0), QgsVector3D(5,0,0), QgsVector3D(5,1,0), QgsVector3D(5,3,0)) 725 * # (False, PyQt5.QtGui.QgsVector3D(0.0, 0.0, 0.0)) 726 * QgsGeometryUtils.linesIntersection3D(QgsVector3D(0,0,0), QgsVector3D(5,0,0), QgsVector3D(5,1,0), QgsVector3D(5,0,0)) 727 * # (False, PyQt5.QtGui.QgsVector3D(0.0, 0.0, 0.0)) 728 * QgsGeometryUtils.linesIntersection3D(QgsVector3D(1,1,0), QgsVector3D(2,2,0), QgsVector3D(3,1,0), QgsVector3D(3,2,0)) 729 * # (True, PyQt5.QtGui.QgsVector3D(3.0, 3.0, 0.0)) 730 * QgsGeometryUtils.linesIntersection3D(QgsVector3D(1,1,0), QgsVector3D(2,2,0), QgsVector3D(3,2,0), QgsVector3D(3,1,0)) 731 * # (True, PyQt5.QtGui.QgsVector3D(3.0, 3.0, 0.0)) 732 * QgsGeometryUtils.linesIntersection3D(QgsVector3D(5,5,5), QgsVector3D(0,0,0), QgsVector3D(0,5,5), QgsVector3D(5,0,0)) 733 * # (True, PyQt5.QtGui.QgsVector3D(2.5, 2.5, 2.5)) 734 * QgsGeometryUtils.linesIntersection3D(QgsVector3D(2.5,2.5,2.5), QgsVector3D(0,5,0), QgsVector3D(2.5,2.5,2.5), QgsVector3D(5,0,0)) 735 * # (True, PyQt5.QtGui.QgsVector3D(2.5, 2.5, 2.5)) 736 * QgsGeometryUtils.linesIntersection3D(QgsVector3D(2.5,2.5,2.5), QgsVector3D(5,0,0), QgsVector3D(0,5,5), QgsVector3D(5,5,5)) 737 * # (True, PyQt5.QtGui.QgsVector3D(0.0, 5.0, 5.0)) 738 * \endcode 739 */ 740 static bool linesIntersection3D( const QgsVector3D &La1, const QgsVector3D &La2, 741 const QgsVector3D &Lb1, const QgsVector3D &Lb2, 742 QgsVector3D &intersection SIP_OUT ) SIP_HOLDGIL; 743 744 /** 745 * Returns the area of the triangle denoted by the points (\a aX, \a aY), (\a bX, \a bY) and 746 * (\a cX, \a cY). 747 * 748 * \since QGIS 3.10 749 */ 750 static double triangleArea( double aX, double aY, double bX, double bY, double cX, double cY ) SIP_HOLDGIL; 751 752 /** 753 * Returns a weighted point inside the triangle denoted by the points (\a aX, \a aY), (\a bX, \a bY) and 754 * (\a cX, \a cY). 755 * 756 * \param aX x-coordinate of first vertex in triangle 757 * \param aY y-coordinate of first vertex in triangle 758 * \param bX x-coordinate of second vertex in triangle 759 * \param bY y-coordinate of second vertex in triangle 760 * \param cX x-coordinate of third vertex in triangle 761 * \param cY y-coordinate of third vertex in triangle 762 * \param weightB weighting factor along axis A-B (between 0 and 1) 763 * \param weightC weighting factor along axis A-C (between 0 and 1) 764 * \param pointX x-coordinate of generated point 765 * \param pointY y-coordinate of generated point 766 * 767 * \since QGIS 3.10 768 */ 769 static void weightedPointInTriangle( double aX, double aY, double bX, double bY, double cX, double cY, 770 double weightB, double weightC, double &pointX SIP_OUT, double &pointY SIP_OUT ) SIP_HOLDGIL; 771 772 /** 773 * A Z dimension is added to \a point if one of the point in the list 774 * \a points is in 3D. Moreover, the Z value of \a point is updated 775 * with the first Z value found in list \a points even if \a point 776 * already contains a Z value. 777 * 778 * \param points List of points in which a 3D point is searched. 779 * \param point The point to update with Z dimension and value. 780 * \returns TRUE if the point is updated, FALSE otherwise 781 * 782 * \warning This method does not copy the z value of the coordinate from the 783 * points whose z value is closest to the original x/y point, but only the first one found. 784 * 785 * \since QGIS 3.0 786 * \deprecated since QGIS 3.20 use transferFirstZValueToPoint( const QgsPointSequence &points, QgsPoint &point ) instead 787 */ setZValueFromPoints(const QgsPointSequence & points,QgsPoint & point)788 Q_DECL_DEPRECATED static bool setZValueFromPoints( const QgsPointSequence &points, QgsPoint &point ) SIP_DEPRECATED 789 { 790 return transferFirstZValueToPoint( points, point ); 791 } 792 793 /** 794 * A Z dimension is added to \a point if one of the point in the list 795 * \a points is in 3D. Moreover, the Z value of \a point is updated 796 * with the first Z value found in list \a points even if \a point 797 * already contains a Z value. 798 * 799 * \param points List of points in which a 3D point is searched. 800 * \param point The point to update with Z dimension and value. 801 * \returns TRUE if the point is updated, FALSE otherwise 802 * 803 * \warning This method does not copy the z value of the coordinate from the 804 * points whose z value is closest to the original x/y point, but only the first one found. 805 * 806 * \since QGIS 3.20 807 */ 808 static bool transferFirstZValueToPoint( const QgsPointSequence &points, QgsPoint &point ); 809 810 /** 811 * A M dimension is added to \a point if one of the points in the list 812 * \a points contains an M value. Moreover, the M value of \a point is 813 * updated with the first M value found in list \a points even if \a point 814 * already contains a M value. 815 * 816 * \param points List of points in which a M point is searched. 817 * \param point The point to update with M dimension and value. 818 * \returns TRUE if the point is updated, FALSE otherwise 819 * 820 * \warning This method does not copy the m value of the coordinate from the 821 * points whose m value is closest to the original x/y point, but only the first one found. 822 * 823 * \since QGIS 3.20 824 */ 825 static bool transferFirstMValueToPoint( const QgsPointSequence &points, QgsPoint &point ); 826 827 /** 828 * A Z or M dimension is added to \a point if one of the points in the list 829 * \a points contains Z or M value. 830 * 831 * This method is equivalent to successively calling Z and M but avoiding 832 * looping twice over the set of points. 833 * 834 * \param verticesBegin begin vertex which a Z or M point is searched. 835 * \param verticesEnd end vertex which a Z or M point is searched. 836 * \param point The point to update with Z or M dimension and value. 837 * \returns TRUE if the point is updated, FALSE otherwise 838 * 839 * \warning This method does not copy the z or m value of the coordinate from the 840 * points whose z or m value is closest to the original x/y point, but only the first one found. 841 * 842 * \note Not available in Python bindings 843 * \since QGIS 3.20 844 */ transferFirstZOrMValueToPoint(Iterator verticesBegin,Iterator verticesEnd,QgsPoint & point)845 template <class Iterator> static bool transferFirstZOrMValueToPoint( Iterator verticesBegin, Iterator verticesEnd, QgsPoint &point ) SIP_SKIP 846 { 847 bool zFound = false; 848 bool mFound = false; 849 850 for ( auto it = verticesBegin ; it != verticesEnd ; ++it ) 851 { 852 if ( !mFound && ( *it ).isMeasure() ) 853 { 854 point.convertTo( QgsWkbTypes::addM( point.wkbType() ) ); 855 point.setM( ( *it ).m() ); 856 mFound = true; 857 } 858 if ( !zFound && ( *it ).is3D() ) 859 { 860 point.convertTo( QgsWkbTypes::addZ( point.wkbType() ) ); 861 point.setZ( ( *it ).z() ); 862 zFound = true; 863 } 864 if ( zFound && mFound ) 865 break; 866 } 867 868 return zFound || mFound; 869 } 870 871 /** 872 * A Z or M dimension is added to \a point if one of the points in the list 873 * \a points contains Z or M value. 874 * 875 * This method is equivalent to successively calling Z and M but avoiding 876 * looping twice over the set of points. 877 * 878 * \param points List of points in which a M point is searched. 879 * \param point The point to update with Z or M dimension and value. 880 * \returns TRUE if the point is updated, FALSE otherwise 881 * 882 * \warning This method does not copy the z or m value of the coordinate from the 883 * points whose z or m value is closest to the original x/y point, but only the first one found. 884 * 885 * \since QGIS 3.20 886 */ transferFirstZOrMValueToPoint(const QgsPointSequence & points,QgsPoint & point)887 static bool transferFirstZOrMValueToPoint( const QgsPointSequence &points, QgsPoint &point ) 888 { 889 return QgsGeometryUtils::transferFirstZOrMValueToPoint( points.constBegin(), points.constEnd(), point ); 890 } 891 892 /** 893 * A Z or M dimension is added to \a point if one of the points in the list 894 * \a points contains Z or M value. 895 * 896 * This method is equivalent to successively calling Z and M but avoiding 897 * looping twice over the set of points. 898 * 899 * \param geom geometry in which a M point is searched. 900 * \param point The point to update with Z or M dimension and value. 901 * \returns TRUE if the point is updated, FALSE otherwise 902 * 903 * \warning This method does not copy the z or m value of the coordinate from the 904 * points whose z or m value is closest to the original x/y point, but only the first one found. 905 * 906 * \since QGIS 3.20 907 */ transferFirstZOrMValueToPoint(const QgsGeometry & geom,QgsPoint & point)908 static bool transferFirstZOrMValueToPoint( const QgsGeometry &geom, QgsPoint &point ) 909 { 910 return QgsGeometryUtils::transferFirstZOrMValueToPoint( geom.vertices_begin(), geom.vertices_end(), point ); 911 } 912 913 /** 914 * Returns the point (\a pointX, \a pointY) forming the bisector from segment (\a aX \a aY) (\a bX \a bY) 915 * and segment (\a bX, \a bY) (\a dX, \a dY). 916 * The bisector segment of AB-CD is (point, projection of point by \a angle) 917 * 918 * \param aX x-coordinate of first vertex of the segment ab 919 * \param aY y-coordinate of first vertex of the segment ab 920 * \param bX x-coordinate of second vertex of the segment ab 921 * \param bY y-coordinate of second vertex of the segment ab 922 * \param cX x-coordinate of first vertex of the segment cd 923 * \param cY y-coordinate of first vertex of the segment cd 924 * \param dX x-coordinate of second vertex of the segment cd 925 * \param dY y-coordinate of second vertex of the segment cd 926 * \param pointX x-coordinate of generated point 927 * \param pointY y-coordinate of generated point 928 * \param angle angle of the bisector from pointX, pointY origin on [ab-cd] 929 * \returns TRUE if the bisector exists (A B and C D are not collinear) 930 * 931 * \since QGIS 3.18 932 */ 933 934 static bool angleBisector( double aX, double aY, double bX, double bY, double cX, double cY, double dX, double dY, 935 double &pointX SIP_OUT, double &pointY SIP_OUT, double &angle SIP_OUT ) SIP_HOLDGIL; 936 937 /** 938 * Returns the point (\a pointX, \a pointY) forming the bisector from point (\a aX, \a aY) to the segment (\a bX, \a bY) (\a cX, \a cY). 939 * The bisector segment of ABC is (A-point) 940 * 941 * \param aX x-coordinate of first vertex in triangle 942 * \param aY y-coordinate of first vertex in triangle 943 * \param bX x-coordinate of second vertex in triangle 944 * \param bY y-coordinate of second vertex in triangle 945 * \param cX x-coordinate of third vertex in triangle 946 * \param cY y-coordinate of third vertex in triangle 947 * \param pointX x-coordinate of generated point 948 * \param pointY y-coordinate of generated point 949 * \returns TRUE if the bisector exists (A B and C are not collinear) 950 * 951 * \since QGIS 3.18 952 */ 953 static bool bisector( double aX, double aY, double bX, double bY, double cX, double cY, 954 double &pointX SIP_OUT, double &pointY SIP_OUT ) SIP_HOLDGIL; 955 956 //! \note not available in Python bindings 957 enum ComponentType SIP_SKIP 958 { 959 Vertex, 960 Ring, 961 Part 962 }; 963 964 //! \note not available in Python bindings closestSegmentFromComponents(T & container,ComponentType ctype,const QgsPoint & pt,QgsPoint & segmentPt,QgsVertexId & vertexAfter,int * leftOf,double epsilon)965 template<class T> static double closestSegmentFromComponents( T &container, ComponentType ctype, const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) SIP_SKIP 966 { 967 double minDist = std::numeric_limits<double>::max(); 968 double minDistSegmentX = 0.0, minDistSegmentY = 0.0; 969 QgsVertexId minDistVertexAfter; 970 int minDistLeftOf = 0; 971 double sqrDist = 0.0; 972 int vertexOffset = 0; 973 int ringOffset = 0; 974 int partOffset = 0; 975 976 for ( int i = 0; i < container.size(); ++i ) 977 { 978 sqrDist = container.at( i )->closestSegment( pt, segmentPt, vertexAfter, leftOf, epsilon ); 979 if ( sqrDist >= 0 && sqrDist < minDist ) 980 { 981 minDist = sqrDist; 982 minDistSegmentX = segmentPt.x(); 983 minDistSegmentY = segmentPt.y(); 984 minDistVertexAfter = vertexAfter; 985 minDistVertexAfter.vertex = vertexAfter.vertex + vertexOffset; 986 minDistVertexAfter.part = vertexAfter.part + partOffset; 987 minDistVertexAfter.ring = vertexAfter.ring + ringOffset; 988 if ( leftOf ) 989 { 990 minDistLeftOf = *leftOf; 991 } 992 } 993 994 if ( ctype == Vertex ) 995 { 996 //-1 because compoundcurve counts duplicated vertices of neighbour curves as one node 997 vertexOffset += container.at( i )->nCoordinates() - 1; 998 } 999 else if ( ctype == Ring ) 1000 { 1001 ringOffset += 1; 1002 } 1003 else if ( ctype == Part ) 1004 { 1005 partOffset += 1; 1006 } 1007 } 1008 1009 if ( minDist == std::numeric_limits<double>::max() ) 1010 return -1; // error: no segments 1011 1012 segmentPt.setX( minDistSegmentX ); 1013 segmentPt.setY( minDistSegmentY ); 1014 vertexAfter = minDistVertexAfter; 1015 if ( leftOf ) 1016 { 1017 *leftOf = minDistLeftOf; 1018 } 1019 return minDist; 1020 } 1021 }; 1022 1023 #endif // QGSGEOMETRYUTILS_H 1024