QGIS API Documentation  3.2.0-Bonn (bc43194)
qgsgeometryutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgeometryutils.cpp
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 #include "qgsgeometryutils.h"
17 
18 #include "qgscurve.h"
19 #include "qgscurvepolygon.h"
20 #include "qgsgeometrycollection.h"
21 #include "qgslinestring.h"
22 #include "qgswkbptr.h"
23 #include "qgslogger.h"
24 
25 #include <memory>
26 #include <QStringList>
27 #include <QVector>
28 #include <QRegularExpression>
29 
30 QVector<QgsLineString *> QgsGeometryUtils::extractLineStrings( const QgsAbstractGeometry *geom )
31 {
32  QVector< QgsLineString * > linestrings;
33  if ( !geom )
34  return linestrings;
35 
36  QVector< const QgsAbstractGeometry * > geometries;
37  geometries << geom;
38  while ( ! geometries.isEmpty() )
39  {
40  const QgsAbstractGeometry *g = geometries.takeFirst();
41  if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( g ) )
42  {
43  linestrings << static_cast< QgsLineString * >( curve->segmentize() );
44  }
45  else if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g ) )
46  {
47  for ( int i = 0; i < collection->numGeometries(); ++i )
48  {
49  geometries.append( collection->geometryN( i ) );
50  }
51  }
52  else if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( g ) )
53  {
54  if ( curvePolygon->exteriorRing() )
55  linestrings << static_cast< QgsLineString * >( curvePolygon->exteriorRing()->segmentize() );
56 
57  for ( int i = 0; i < curvePolygon->numInteriorRings(); ++i )
58  {
59  linestrings << static_cast< QgsLineString * >( curvePolygon->interiorRing( i )->segmentize() );
60  }
61  }
62  }
63  return linestrings;
64 }
65 
67 {
68  double minDist = std::numeric_limits<double>::max();
69  double currentDist = 0;
70  QgsPoint minDistPoint;
71  id = QgsVertexId(); // set as invalid
72 
73  QgsVertexId vertexId;
74  QgsPoint vertex;
75  while ( geom.nextVertex( vertexId, vertex ) )
76  {
77  currentDist = QgsGeometryUtils::sqrDistance2D( pt, vertex );
78  // The <= is on purpose: for geometries with closing vertices, this ensures
79  // that the closing vertex is returned. For the vertex tool, the rubberband
80  // of the closing vertex is above the opening vertex, hence with the <=
81  // situations where the covered opening vertex rubberband is selected are
82  // avoided.
83  if ( currentDist <= minDist )
84  {
85  minDist = currentDist;
86  minDistPoint = vertex;
87  id.part = vertexId.part;
88  id.ring = vertexId.ring;
89  id.vertex = vertexId.vertex;
90  id.type = vertexId.type;
91  }
92  }
93 
94  return minDistPoint;
95 }
96 
98 {
100  QgsVertexId vertexAfter;
101  geometry.closestSegment( point, closestPoint, vertexAfter, nullptr, DEFAULT_SEGMENT_EPSILON );
102  if ( vertexAfter.isValid() )
103  {
104  QgsPoint pointAfter = geometry.vertexAt( vertexAfter );
105  if ( vertexAfter.vertex > 0 )
106  {
107  QgsVertexId vertexBefore = vertexAfter;
108  vertexBefore.vertex--;
109  QgsPoint pointBefore = geometry.vertexAt( vertexBefore );
110  double length = pointBefore.distance( pointAfter );
111  double distance = pointBefore.distance( closestPoint );
112 
113  if ( qgsDoubleNear( distance, 0.0 ) )
114  closestPoint = pointBefore;
115  else if ( qgsDoubleNear( distance, length ) )
116  closestPoint = pointAfter;
117  else
118  {
119  if ( QgsWkbTypes::hasZ( geometry.wkbType() ) && length )
120  closestPoint.addZValue( pointBefore.z() + ( pointAfter.z() - pointBefore.z() ) * distance / length );
121  if ( QgsWkbTypes::hasM( geometry.wkbType() ) )
122  closestPoint.addMValue( pointBefore.m() + ( pointAfter.m() - pointBefore.m() ) * distance / length );
123  }
124  }
125  }
126 
127  return closestPoint;
128 }
129 
131 {
132  double currentDist = 0;
133  QgsVertexId vertexId;
134  QgsPoint vertex;
135  while ( geom.nextVertex( vertexId, vertex ) )
136  {
137  if ( vertexId == id )
138  {
139  //found target vertex
140  return currentDist;
141  }
142  currentDist += geom.segmentLength( vertexId );
143  }
144 
145  //could not find target vertex
146  return -1;
147 }
148 
149 bool QgsGeometryUtils::verticesAtDistance( const QgsAbstractGeometry &geometry, double distance, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
150 {
151  double currentDist = 0;
152  previousVertex = QgsVertexId();
153  nextVertex = QgsVertexId();
154 
155  QgsPoint point;
156  QgsPoint previousPoint;
157 
158  if ( qgsDoubleNear( distance, 0.0 ) )
159  {
160  geometry.nextVertex( previousVertex, point );
161  nextVertex = previousVertex;
162  return true;
163  }
164 
165  bool first = true;
166  while ( currentDist < distance && geometry.nextVertex( nextVertex, point ) )
167  {
168  if ( !first )
169  {
170  currentDist += std::sqrt( QgsGeometryUtils::sqrDistance2D( previousPoint, point ) );
171  }
172 
173  if ( qgsDoubleNear( currentDist, distance ) )
174  {
175  // exact hit!
176  previousVertex = nextVertex;
177  return true;
178  }
179 
180  if ( currentDist > distance )
181  {
182  return true;
183  }
184 
185  previousVertex = nextVertex;
186  previousPoint = point;
187  first = false;
188  }
189 
190  //could not find target distance
191  return false;
192 }
193 
194 double QgsGeometryUtils::sqrDistance2D( const QgsPoint &pt1, const QgsPoint &pt2 )
195 {
196  return ( pt1.x() - pt2.x() ) * ( pt1.x() - pt2.x() ) + ( pt1.y() - pt2.y() ) * ( pt1.y() - pt2.y() );
197 }
198 
199 double QgsGeometryUtils::sqrDistToLine( double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon )
200 {
201  minDistX = x1;
202  minDistY = y1;
203 
204  double dx = x2 - x1;
205  double dy = y2 - y1;
206 
207  if ( !qgsDoubleNear( dx, 0.0 ) || !qgsDoubleNear( dy, 0.0 ) )
208  {
209  double t = ( ( ptX - x1 ) * dx + ( ptY - y1 ) * dy ) / ( dx * dx + dy * dy );
210  if ( t > 1 )
211  {
212  minDistX = x2;
213  minDistY = y2;
214  }
215  else if ( t > 0 )
216  {
217  minDistX += dx * t;
218  minDistY += dy * t;
219  }
220  }
221 
222  dx = ptX - minDistX;
223  dy = ptY - minDistY;
224 
225  double dist = dx * dx + dy * dy;
226 
227  //prevent rounding errors if the point is directly on the segment
228  if ( qgsDoubleNear( dist, 0.0, epsilon ) )
229  {
230  minDistX = ptX;
231  minDistY = ptY;
232  return 0.0;
233  }
234 
235  return dist;
236 }
237 
238 bool QgsGeometryUtils::lineIntersection( const QgsPoint &p1, QgsVector v1, const QgsPoint &p2, QgsVector v2, QgsPoint &intersection )
239 {
240  double d = v1.y() * v2.x() - v1.x() * v2.y();
241 
242  if ( qgsDoubleNear( d, 0 ) )
243  return false;
244 
245  double dx = p2.x() - p1.x();
246  double dy = p2.y() - p1.y();
247  double k = ( dy * v2.x() - dx * v2.y() ) / d;
248 
249  intersection = QgsPoint( p1.x() + v1.x() * k, p1.y() + v1.y() * k );
250 
251  // z support for intersection point
252  QgsGeometryUtils::setZValueFromPoints( QgsPointSequence() << p1 << p2, intersection );
253 
254  return true;
255 }
256 
257 bool QgsGeometryUtils::segmentIntersection( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q1, const QgsPoint &q2, QgsPoint &intersectionPoint, bool &isIntersection, const double tolerance, bool acceptImproperIntersection )
258 {
259  isIntersection = false;
260 
261  QgsVector v( p2.x() - p1.x(), p2.y() - p1.y() );
262  QgsVector w( q2.x() - q1.x(), q2.y() - q1.y() );
263  double vl = v.length();
264  double wl = w.length();
265 
266  if ( qgsDoubleNear( vl, 0.0, tolerance ) || qgsDoubleNear( wl, 0.0, tolerance ) )
267  {
268  return false;
269  }
270  v = v / vl;
271  w = w / wl;
272 
273  if ( !QgsGeometryUtils::lineIntersection( p1, v, q1, w, intersectionPoint ) )
274  {
275  return false;
276  }
277 
278  isIntersection = true;
279  if ( acceptImproperIntersection )
280  {
281  if ( ( p1 == q1 ) || ( p1 == q2 ) )
282  {
283  intersectionPoint = p1;
284  return true;
285  }
286  else if ( ( p2 == q1 ) || ( p2 == q2 ) )
287  {
288  intersectionPoint = p2;
289  return true;
290  }
291 
292  double x, y;
293  if (
294  // intersectionPoint = p1
295  qgsDoubleNear( QgsGeometryUtils::sqrDistToLine( p1.x(), p1.y(), q1.x(), q1.y(), q2.x(), q2.y(), x, y, tolerance ), 0.0, tolerance ) ||
296  // intersectionPoint = p2
297  qgsDoubleNear( QgsGeometryUtils::sqrDistToLine( p2.x(), p2.y(), q1.x(), q1.y(), q2.x(), q2.y(), x, y, tolerance ), 0.0, tolerance ) ||
298  // intersectionPoint = q1
299  qgsDoubleNear( QgsGeometryUtils::sqrDistToLine( q1.x(), q1.y(), p1.x(), p1.y(), p2.x(), p2.y(), x, y, tolerance ), 0.0, tolerance ) ||
300  // intersectionPoint = q2
301  qgsDoubleNear( QgsGeometryUtils::sqrDistToLine( q2.x(), q2.y(), p1.x(), p1.y(), p2.x(), p2.y(), x, y, tolerance ), 0.0, tolerance )
302  )
303  {
304  return true;
305  }
306  }
307 
308  double lambdav = QgsVector( intersectionPoint.x() - p1.x(), intersectionPoint.y() - p1.y() ) * v;
309  if ( lambdav < 0. + tolerance || lambdav > vl - tolerance )
310  return false;
311 
312  double lambdaw = QgsVector( intersectionPoint.x() - q1.x(), intersectionPoint.y() - q1.y() ) * w;
313  return !( lambdaw < 0. + tolerance || lambdaw >= wl - tolerance );
314 
315 }
316 
317 bool QgsGeometryUtils::lineCircleIntersection( const QgsPointXY &center, const double radius,
318  const QgsPointXY &linePoint1, const QgsPointXY &linePoint2,
319  QgsPointXY &intersection )
320 {
321  // formula taken from http://mathworld.wolfram.com/Circle-LineIntersection.html
322 
323  const double x1 = linePoint1.x() - center.x();
324  const double y1 = linePoint1.y() - center.y();
325  const double x2 = linePoint2.x() - center.x();
326  const double y2 = linePoint2.y() - center.y();
327  const double dx = x2 - x1;
328  const double dy = y2 - y1;
329 
330  const double dr = std::sqrt( std::pow( dx, 2 ) + std::pow( dy, 2 ) );
331  const double d = x1 * y2 - x2 * y1;
332 
333  const double disc = std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 );
334 
335  if ( disc < 0 )
336  {
337  //no intersection or tangent
338  return false;
339  }
340  else
341  {
342  // two solutions
343  const int sgnDy = dy < 0 ? -1 : 1;
344 
345  const double ax = center.x() + ( d * dy + sgnDy * dx * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) );
346  const double ay = center.y() + ( -d * dx + std::fabs( dy ) * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) );
347  const QgsPointXY p1( ax, ay );
348 
349  const double bx = center.x() + ( d * dy - sgnDy * dx * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) );
350  const double by = center.y() + ( -d * dx - std::fabs( dy ) * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) );
351  const QgsPointXY p2( bx, by );
352 
353  // snap to nearest intersection
354 
355  if ( intersection.sqrDist( p1 ) < intersection.sqrDist( p2 ) )
356  {
357  intersection.set( p1.x(), p1.y() );
358  }
359  else
360  {
361  intersection.set( p2.x(), p2.y() );
362  }
363  return true;
364  }
365 }
366 
367 // based on public domain work by 3/26/2005 Tim Voght
368 // see http://paulbourke.net/geometry/circlesphere/tvoght.c
369 int QgsGeometryUtils::circleCircleIntersections( QgsPointXY center0, const double r0, QgsPointXY center1, const double r1, QgsPointXY &intersection1, QgsPointXY &intersection2 )
370 {
371  // determine the straight-line distance between the centers
372  const double d = center0.distance( center1 );
373 
374  // check for solvability
375  if ( d > ( r0 + r1 ) )
376  {
377  // no solution. circles do not intersect.
378  return 0;
379  }
380  else if ( d < std::fabs( r0 - r1 ) )
381  {
382  // no solution. one circle is contained in the other
383  return 0;
384  }
385  else if ( qgsDoubleNear( d, 0 ) && ( qgsDoubleNear( r0, r1 ) ) )
386  {
387  // no solutions, the circles coincide
388  return 0;
389  }
390 
391  /* 'point 2' is the point where the line through the circle
392  * intersection points crosses the line between the circle
393  * centers.
394  */
395 
396  // Determine the distance from point 0 to point 2.
397  const double a = ( ( r0 * r0 ) - ( r1 * r1 ) + ( d * d ) ) / ( 2.0 * d ) ;
398 
399  /* dx and dy are the vertical and horizontal distances between
400  * the circle centers.
401  */
402  const double dx = center1.x() - center0.x();
403  const double dy = center1.y() - center0.y();
404 
405  // Determine the coordinates of point 2.
406  const double x2 = center0.x() + ( dx * a / d );
407  const double y2 = center0.y() + ( dy * a / d );
408 
409  /* Determine the distance from point 2 to either of the
410  * intersection points.
411  */
412  const double h = std::sqrt( ( r0 * r0 ) - ( a * a ) );
413 
414  /* Now determine the offsets of the intersection points from
415  * point 2.
416  */
417  const double rx = dy * ( h / d );
418  const double ry = dx * ( h / d );
419 
420  // determine the absolute intersection points
421  intersection1 = QgsPointXY( x2 + rx, y2 - ry );
422  intersection2 = QgsPointXY( x2 - rx, y2 + ry );
423 
424  // see if we have 1 or 2 solutions
425  if ( qgsDoubleNear( d, r0 + r1 ) )
426  return 1;
427 
428  return 2;
429 }
430 
431 // Using https://stackoverflow.com/a/1351794/1861260
432 // and inspired by http://csharphelper.com/blog/2014/11/find-the-tangent-lines-between-a-point-and-a-circle-in-c/
433 bool QgsGeometryUtils::tangentPointAndCircle( const QgsPointXY &center, double radius, const QgsPointXY &p, QgsPointXY &pt1, QgsPointXY &pt2 )
434 {
435  // distance from point to center of circle
436  const double dx = center.x() - p.x();
437  const double dy = center.y() - p.y();
438  const double distanceSquared = dx * dx + dy * dy;
439  const double radiusSquared = radius * radius;
440  if ( distanceSquared < radiusSquared )
441  {
442  // point is inside circle!
443  return false;
444  }
445 
446  // distance from point to tangent point, using pythagoras
447  const double distanceToTangent = std::sqrt( distanceSquared - radiusSquared );
448 
449  // tangent points are those where the original circle intersects a circle centered
450  // on p with radius distanceToTangent
451  circleCircleIntersections( center, radius, p, distanceToTangent, pt1, pt2 );
452 
453  return true;
454 }
455 
456 // inspired by http://csharphelper.com/blog/2014/12/find-the-tangent-lines-between-two-circles-in-c/
457 int QgsGeometryUtils::circleCircleOuterTangents( const QgsPointXY &center1, double radius1, const QgsPointXY &center2, double radius2, QgsPointXY &line1P1, QgsPointXY &line1P2, QgsPointXY &line2P1, QgsPointXY &line2P2 )
458 {
459  if ( radius1 > radius2 )
460  return circleCircleOuterTangents( center2, radius2, center1, radius1, line1P1, line1P2, line2P1, line2P2 );
461 
462  const double radius2a = radius2 - radius1;
463  if ( !tangentPointAndCircle( center2, radius2a, center1, line1P2, line2P2 ) )
464  {
465  // there are no tangents
466  return 0;
467  }
468 
469  // get the vector perpendicular to the
470  // first tangent with length radius1
471  QgsVector v1( -( line1P2.y() - center1.y() ), line1P2.x() - center1.x() );
472  const double v1Length = v1.length();
473  v1 = v1 * ( radius1 / v1Length );
474 
475  // offset the tangent vector's points
476  line1P1 = center1 + v1;
477  line1P2 = line1P2 + v1;
478 
479  // get the vector perpendicular to the
480  // second tangent with length radius1
481  QgsVector v2( line2P2.y() - center1.y(), -( line2P2.x() - center1.x() ) );
482  const double v2Length = v2.length();
483  v2 = v2 * ( radius1 / v2Length );
484 
485  // offset the tangent vector's points
486  line2P1 = center1 + v2;
487  line2P2 = line2P2 + v2;
488 
489  return 2;
490 }
491 
492 QVector<QgsGeometryUtils::SelfIntersection> QgsGeometryUtils::selfIntersections( const QgsAbstractGeometry *geom, int part, int ring, double tolerance )
493 {
494  QVector<SelfIntersection> intersections;
495 
496  int n = geom->vertexCount( part, ring );
497  bool isClosed = geom->vertexAt( QgsVertexId( part, ring, 0 ) ) == geom->vertexAt( QgsVertexId( part, ring, n - 1 ) );
498 
499  // Check every pair of segments for intersections
500  for ( int i = 0, j = 1; j < n; i = j++ )
501  {
502  QgsPoint pi = geom->vertexAt( QgsVertexId( part, ring, i ) );
503  QgsPoint pj = geom->vertexAt( QgsVertexId( part, ring, j ) );
504  if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) < tolerance * tolerance ) continue;
505 
506  // Don't test neighboring edges
507  int start = j + 1;
508  int end = i == 0 && isClosed ? n - 1 : n;
509  for ( int k = start, l = start + 1; l < end; k = l++ )
510  {
511  QgsPoint pk = geom->vertexAt( QgsVertexId( part, ring, k ) );
512  QgsPoint pl = geom->vertexAt( QgsVertexId( part, ring, l ) );
513 
514  QgsPoint inter;
515  bool intersection = false;
516  if ( !QgsGeometryUtils::segmentIntersection( pi, pj, pk, pl, inter, intersection, tolerance ) ) continue;
517 
519  s.segment1 = i;
520  s.segment2 = k;
521  if ( s.segment1 > s.segment2 )
522  {
523  std::swap( s.segment1, s.segment2 );
524  }
525  s.point = inter;
526  intersections.append( s );
527  }
528  }
529  return intersections;
530 }
531 
532 int QgsGeometryUtils::leftOfLine( double x, double y, double x1, double y1, double x2, double y2 )
533 {
534  double f1 = x - x1;
535  double f2 = y2 - y1;
536  double f3 = y - y1;
537  double f4 = x2 - x1;
538  double test = ( f1 * f2 - f3 * f4 );
539  // return -1, 0, or 1
540  return qgsDoubleNear( test, 0.0 ) ? 0 : ( test < 0 ? -1 : 1 );
541 }
542 
543 QgsPoint QgsGeometryUtils::pointOnLineWithDistance( const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance )
544 {
545  double dx = directionPoint.x() - startPoint.x();
546  double dy = directionPoint.y() - startPoint.y();
547  double length = std::sqrt( dx * dx + dy * dy );
548 
549  if ( qgsDoubleNear( length, 0.0 ) )
550  {
551  return startPoint;
552  }
553 
554  double scaleFactor = distance / length;
555  return QgsPoint( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor );
556 }
557 
558 double QgsGeometryUtils::ccwAngle( double dy, double dx )
559 {
560  double angle = std::atan2( dy, dx ) * 180 / M_PI;
561  if ( angle < 0 )
562  {
563  return 360 + angle;
564  }
565  else if ( angle > 360 )
566  {
567  return 360 - angle;
568  }
569  return angle;
570 }
571 
572 void QgsGeometryUtils::circleCenterRadius( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double &radius, double &centerX, double &centerY )
573 {
574  double dx21, dy21, dx31, dy31, h21, h31, d;
575 
576  //closed circle
577  if ( qgsDoubleNear( pt1.x(), pt3.x() ) && qgsDoubleNear( pt1.y(), pt3.y() ) )
578  {
579  centerX = ( pt1.x() + pt2.x() ) / 2.0;
580  centerY = ( pt1.y() + pt2.y() ) / 2.0;
581  radius = std::sqrt( std::pow( centerX - pt1.x(), 2.0 ) + std::pow( centerY - pt1.y(), 2.0 ) );
582  return;
583  }
584 
585  // Using Cartesian circumcenter eguations from page https://en.wikipedia.org/wiki/Circumscribed_circle
586  dx21 = pt2.x() - pt1.x();
587  dy21 = pt2.y() - pt1.y();
588  dx31 = pt3.x() - pt1.x();
589  dy31 = pt3.y() - pt1.y();
590 
591  h21 = std::pow( dx21, 2.0 ) + std::pow( dy21, 2.0 );
592  h31 = std::pow( dx31, 2.0 ) + std::pow( dy31, 2.0 );
593 
594  // 2*Cross product, d<0 means clockwise and d>0 counterclockwise sweeping angle
595  d = 2 * ( dx21 * dy31 - dx31 * dy21 );
596 
597  // Check colinearity, Cross product = 0
598  if ( qgsDoubleNear( std::fabs( d ), 0.0, 0.00000000001 ) )
599  {
600  radius = -1.0;
601  return;
602  }
603 
604  // Calculate centroid coordinates and radius
605  centerX = pt1.x() + ( h21 * dy31 - h31 * dy21 ) / d;
606  centerY = pt1.y() - ( h21 * dx31 - h31 * dx21 ) / d;
607  radius = std::sqrt( std::pow( centerX - pt1.x(), 2.0 ) + std::pow( centerY - pt1.y(), 2.0 ) );
608 }
609 
610 bool QgsGeometryUtils::circleClockwise( double angle1, double angle2, double angle3 )
611 {
612  if ( angle3 >= angle1 )
613  {
614  return !( angle2 > angle1 && angle2 < angle3 );
615  }
616  else
617  {
618  return !( angle2 > angle1 || angle2 < angle3 );
619  }
620 }
621 
622 bool QgsGeometryUtils::circleAngleBetween( double angle, double angle1, double angle2, bool clockwise )
623 {
624  if ( clockwise )
625  {
626  if ( angle2 < angle1 )
627  {
628  return ( angle <= angle1 && angle >= angle2 );
629  }
630  else
631  {
632  return ( angle <= angle1 || angle >= angle2 );
633  }
634  }
635  else
636  {
637  if ( angle2 > angle1 )
638  {
639  return ( angle >= angle1 && angle <= angle2 );
640  }
641  else
642  {
643  return ( angle >= angle1 || angle <= angle2 );
644  }
645  }
646 }
647 
648 bool QgsGeometryUtils::angleOnCircle( double angle, double angle1, double angle2, double angle3 )
649 {
650  bool clockwise = circleClockwise( angle1, angle2, angle3 );
651  return circleAngleBetween( angle, angle1, angle3, clockwise );
652 }
653 
654 double QgsGeometryUtils::circleLength( double x1, double y1, double x2, double y2, double x3, double y3 )
655 {
656  double centerX, centerY, radius;
657  circleCenterRadius( QgsPoint( x1, y1 ), QgsPoint( x2, y2 ), QgsPoint( x3, y3 ), radius, centerX, centerY );
658  double length = M_PI / 180.0 * radius * sweepAngle( centerX, centerY, x1, y1, x2, y2, x3, y3 );
659  if ( length < 0 )
660  {
661  length = -length;
662  }
663  return length;
664 }
665 
666 double QgsGeometryUtils::sweepAngle( double centerX, double centerY, double x1, double y1, double x2, double y2, double x3, double y3 )
667 {
668  double p1Angle = QgsGeometryUtils::ccwAngle( y1 - centerY, x1 - centerX );
669  double p2Angle = QgsGeometryUtils::ccwAngle( y2 - centerY, x2 - centerX );
670  double p3Angle = QgsGeometryUtils::ccwAngle( y3 - centerY, x3 - centerX );
671 
672  if ( p3Angle >= p1Angle )
673  {
674  if ( p2Angle > p1Angle && p2Angle < p3Angle )
675  {
676  return ( p3Angle - p1Angle );
677  }
678  else
679  {
680  return ( - ( p1Angle + ( 360 - p3Angle ) ) );
681  }
682  }
683  else
684  {
685  if ( p2Angle < p1Angle && p2Angle > p3Angle )
686  {
687  return ( -( p1Angle - p3Angle ) );
688  }
689  else
690  {
691  return ( p3Angle + ( 360 - p1Angle ) );
692  }
693  }
694 }
695 
696 bool QgsGeometryUtils::segmentMidPoint( const QgsPoint &p1, const QgsPoint &p2, QgsPoint &result, double radius, const QgsPoint &mousePos )
697 {
698  QgsPoint midPoint( ( p1.x() + p2.x() ) / 2.0, ( p1.y() + p2.y() ) / 2.0 );
699  double midDist = std::sqrt( sqrDistance2D( p1, midPoint ) );
700  if ( radius < midDist )
701  {
702  return false;
703  }
704  double centerMidDist = std::sqrt( radius * radius - midDist * midDist );
705  double dist = radius - centerMidDist;
706 
707  double midDx = midPoint.x() - p1.x();
708  double midDy = midPoint.y() - p1.y();
709 
710  //get the four possible midpoints
711  QVector<QgsPoint> possibleMidPoints;
712  possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPoint( midPoint.x() - midDy, midPoint.y() + midDx ), dist ) );
713  possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPoint( midPoint.x() - midDy, midPoint.y() + midDx ), 2 * radius - dist ) );
714  possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPoint( midPoint.x() + midDy, midPoint.y() - midDx ), dist ) );
715  possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPoint( midPoint.x() + midDy, midPoint.y() - midDx ), 2 * radius - dist ) );
716 
717  //take the closest one
718  double minDist = std::numeric_limits<double>::max();
719  int minDistIndex = -1;
720  for ( int i = 0; i < possibleMidPoints.size(); ++i )
721  {
722  double currentDist = sqrDistance2D( mousePos, possibleMidPoints.at( i ) );
723  if ( currentDist < minDist )
724  {
725  minDistIndex = i;
726  minDist = currentDist;
727  }
728  }
729 
730  if ( minDistIndex == -1 )
731  {
732  return false;
733  }
734 
735  result = possibleMidPoints.at( minDistIndex );
736 
737  // add z support if necessary
739 
740  return true;
741 }
742 
743 QgsPoint QgsGeometryUtils::segmentMidPointFromCenter( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, const bool useShortestArc )
744 {
745  double midPointAngle = averageAngle( lineAngle( center.x(), center.y(), p1.x(), p1.y() ),
746  lineAngle( center.x(), center.y(), p2.x(), p2.y() ) );
747  if ( !useShortestArc )
748  midPointAngle += M_PI;
749  return center.project( center.distance( p1 ), midPointAngle * 180 / M_PI );
750 }
751 
752 double QgsGeometryUtils::circleTangentDirection( const QgsPoint &tangentPoint, const QgsPoint &cp1,
753  const QgsPoint &cp2, const QgsPoint &cp3 )
754 {
755  //calculate circle midpoint
756  double mX, mY, radius;
757  circleCenterRadius( cp1, cp2, cp3, radius, mX, mY );
758 
759  double p1Angle = QgsGeometryUtils::ccwAngle( cp1.y() - mY, cp1.x() - mX );
760  double p2Angle = QgsGeometryUtils::ccwAngle( cp2.y() - mY, cp2.x() - mX );
761  double p3Angle = QgsGeometryUtils::ccwAngle( cp3.y() - mY, cp3.x() - mX );
762  double angle = 0;
763  if ( circleClockwise( p1Angle, p2Angle, p3Angle ) )
764  {
765  angle = lineAngle( tangentPoint.x(), tangentPoint.y(), mX, mY ) - M_PI_2;
766  }
767  else
768  {
769  angle = lineAngle( mX, mY, tangentPoint.x(), tangentPoint.y() ) - M_PI_2;
770  }
771  if ( angle < 0 )
772  angle += 2 * M_PI;
773  return angle;
774 }
775 
776 void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, QgsPointSequence &points, double tolerance, QgsAbstractGeometry::SegmentationToleranceType toleranceType, bool hasZ, bool hasM )
777 {
778  bool reversed = false;
779  int segSide = segmentSide( p1, p3, p2 );
780 
781  QgsPoint circlePoint1;
782  const QgsPoint circlePoint2 = p2;
783  QgsPoint circlePoint3;
784 
785  if ( segSide == -1 )
786  {
787  // Reverse !
788  circlePoint1 = p3;
789  circlePoint3 = p1;
790  reversed = true;
791  }
792  else
793  {
794  circlePoint1 = p1;
795  circlePoint3 = p3;
796  }
797 
798  //adapted code from PostGIS
799  double radius = 0;
800  double centerX = 0;
801  double centerY = 0;
802  circleCenterRadius( circlePoint1, circlePoint2, circlePoint3, radius, centerX, centerY );
803 
804  if ( circlePoint1 != circlePoint3 && ( radius < 0 || qgsDoubleNear( segSide, 0.0 ) ) ) //points are colinear
805  {
806  points.append( p1 );
807  points.append( p2 );
808  points.append( p3 );
809  return;
810  }
811 
812  double increment = tolerance; //one segment per degree
813  if ( toleranceType == QgsAbstractGeometry::MaximumDifference )
814  {
815  double halfAngle = std::acos( -tolerance / radius + 1 );
816  increment = 2 * halfAngle;
817  }
818 
819  //angles of pt1, pt2, pt3
820  double a1 = std::atan2( circlePoint1.y() - centerY, circlePoint1.x() - centerX );
821  double a2 = std::atan2( circlePoint2.y() - centerY, circlePoint2.x() - centerX );
822  double a3 = std::atan2( circlePoint3.y() - centerY, circlePoint3.x() - centerX );
823 
824  // Make segmentation symmetric
825  const bool symmetric = true;
826  if ( symmetric )
827  {
828  double angle = a3 - a1;
829  if ( angle < 0 ) angle += M_PI * 2;
830 
831  /* Number of segments in output */
832  int segs = ceil( angle / increment );
833  /* Tweak increment to be regular for all the arc */
834  increment = angle / segs;
835  }
836 
837  /* Adjust a3 up so we can increment from a1 to a3 cleanly */
838  if ( a3 < a1 )
839  a3 += 2.0 * M_PI;
840  if ( a2 < a1 )
841  a2 += 2.0 * M_PI;
842 
843  double x, y;
844  double z = 0;
845  double m = 0;
846 
847  QVector<QgsPoint> stringPoints;
848  stringPoints.insert( 0, circlePoint1 );
849  if ( circlePoint2 != circlePoint3 && circlePoint1 != circlePoint2 ) //draw straight line segment if two points have the same position
850  {
851  QgsWkbTypes::Type pointWkbType = QgsWkbTypes::Point;
852  if ( hasZ )
853  pointWkbType = QgsWkbTypes::addZ( pointWkbType );
854  if ( hasM )
855  pointWkbType = QgsWkbTypes::addM( pointWkbType );
856 
857  // As we're adding the last point in any case, we'll avoid
858  // including a point which is at less than 1% increment distance
859  // from it (may happen to find them due to numbers approximation).
860  // NOTE that this effectively allows in output some segments which
861  // are more distant than requested. This is at most 1% off
862  // from requested MaxAngle and less for MaxError.
863  double tolError = increment / 100;
864  double stopAngle = a3 - tolError;
865  for ( double angle = a1 + increment; angle < stopAngle; angle += increment )
866  {
867  x = centerX + radius * std::cos( angle );
868  y = centerY + radius * std::sin( angle );
869 
870  if ( hasZ )
871  {
872  z = interpolateArcValue( angle, a1, a2, a3, circlePoint1.z(), circlePoint2.z(), circlePoint3.z() );
873  }
874  if ( hasM )
875  {
876  m = interpolateArcValue( angle, a1, a2, a3, circlePoint1.m(), circlePoint2.m(), circlePoint3.m() );
877  }
878 
879  stringPoints.insert( stringPoints.size(), QgsPoint( pointWkbType, x, y, z, m ) );
880  }
881  }
882  stringPoints.insert( stringPoints.size(), circlePoint3 );
883 
884  // TODO: check if or implement QgsPointSequence directly taking an iterator to append
885  if ( reversed )
886  {
887  std::reverse( stringPoints.begin(), stringPoints.end() );
888  }
889  if ( ! points.empty() && stringPoints.front() == points.back() ) stringPoints.pop_front();
890  points.append( stringPoints );
891 }
892 
893 int QgsGeometryUtils::segmentSide( const QgsPoint &pt1, const QgsPoint &pt3, const QgsPoint &pt2 )
894 {
895  double side = ( ( pt2.x() - pt1.x() ) * ( pt3.y() - pt1.y() ) - ( pt3.x() - pt1.x() ) * ( pt2.y() - pt1.y() ) );
896  if ( side == 0.0 )
897  {
898  return 0;
899  }
900  else
901  {
902  if ( side < 0 )
903  {
904  return -1;
905  }
906  if ( side > 0 )
907  {
908  return 1;
909  }
910  return 0;
911  }
912 }
913 
914 double QgsGeometryUtils::interpolateArcValue( double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3 )
915 {
916  /* Counter-clockwise sweep */
917  if ( a1 < a2 )
918  {
919  if ( angle <= a2 )
920  return zm1 + ( zm2 - zm1 ) * ( angle - a1 ) / ( a2 - a1 );
921  else
922  return zm2 + ( zm3 - zm2 ) * ( angle - a2 ) / ( a3 - a2 );
923  }
924  /* Clockwise sweep */
925  else
926  {
927  if ( angle >= a2 )
928  return zm1 + ( zm2 - zm1 ) * ( a1 - angle ) / ( a1 - a2 );
929  else
930  return zm2 + ( zm3 - zm2 ) * ( a2 - angle ) / ( a2 - a3 );
931  }
932 }
933 
934 QgsPointSequence QgsGeometryUtils::pointsFromWKT( const QString &wktCoordinateList, bool is3D, bool isMeasure )
935 {
936  int dim = 2 + is3D + isMeasure;
937  QgsPointSequence points;
938  const QStringList coordList = wktCoordinateList.split( ',', QString::SkipEmptyParts );
939 
940  //first scan through for extra unexpected dimensions
941  bool foundZ = false;
942  bool foundM = false;
943  QRegularExpression rx( QStringLiteral( "\\s" ) );
944  for ( const QString &pointCoordinates : coordList )
945  {
946  QStringList coordinates = pointCoordinates.split( rx, QString::SkipEmptyParts );
947  if ( coordinates.size() == 3 && !foundZ && !foundM && !is3D && !isMeasure )
948  {
949  // 3 dimensional coordinates, but not specifically marked as such. We allow this
950  // anyway and upgrade geometry to have Z dimension
951  foundZ = true;
952  }
953  else if ( coordinates.size() >= 4 && ( !( is3D || foundZ ) || !( isMeasure || foundM ) ) )
954  {
955  // 4 (or more) dimensional coordinates, but not specifically marked as such. We allow this
956  // anyway and upgrade geometry to have Z&M dimensions
957  foundZ = true;
958  foundM = true;
959  }
960  }
961 
962  for ( const QString &pointCoordinates : coordList )
963  {
964  QStringList coordinates = pointCoordinates.split( rx, QString::SkipEmptyParts );
965  if ( coordinates.size() < dim )
966  continue;
967 
968  int idx = 0;
969  double x = coordinates[idx++].toDouble();
970  double y = coordinates[idx++].toDouble();
971 
972  double z = 0;
973  if ( ( is3D || foundZ ) && coordinates.length() > idx )
974  z = coordinates[idx++].toDouble();
975 
976  double m = 0;
977  if ( ( isMeasure || foundM ) && coordinates.length() > idx )
978  m = coordinates[idx++].toDouble();
979 
981  if ( is3D || foundZ )
982  {
983  if ( isMeasure || foundM )
985  else
987  }
988  else
989  {
990  if ( isMeasure || foundM )
992  else
993  t = QgsWkbTypes::Point;
994  }
995 
996  points.append( QgsPoint( t, x, y, z, m ) );
997  }
998 
999  return points;
1000 }
1001 
1003 {
1004  wkb << static_cast<quint32>( points.size() );
1005  for ( const QgsPoint &point : points )
1006  {
1007  wkb << point.x() << point.y();
1008  if ( is3D )
1009  {
1010  wkb << point.z();
1011  }
1012  if ( isMeasure )
1013  {
1014  wkb << point.m();
1015  }
1016  }
1017 }
1018 
1019 QString QgsGeometryUtils::pointsToWKT( const QgsPointSequence &points, int precision, bool is3D, bool isMeasure )
1020 {
1021  QString wkt = QStringLiteral( "(" );
1022  for ( const QgsPoint &p : points )
1023  {
1024  wkt += qgsDoubleToString( p.x(), precision );
1025  wkt += ' ' + qgsDoubleToString( p.y(), precision );
1026  if ( is3D )
1027  wkt += ' ' + qgsDoubleToString( p.z(), precision );
1028  if ( isMeasure )
1029  wkt += ' ' + qgsDoubleToString( p.m(), precision );
1030  wkt += QLatin1String( ", " );
1031  }
1032  if ( wkt.endsWith( QLatin1String( ", " ) ) )
1033  wkt.chop( 2 ); // Remove last ", "
1034  wkt += ')';
1035  return wkt;
1036 }
1037 
1038 QDomElement QgsGeometryUtils::pointsToGML2( const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder &axisOrder )
1039 {
1040  QDomElement elemCoordinates = doc.createElementNS( ns, QStringLiteral( "coordinates" ) );
1041 
1042  // coordinate separator
1043  QString cs = QStringLiteral( "," );
1044  // tupel separator
1045  QString ts = QStringLiteral( " " );
1046 
1047  elemCoordinates.setAttribute( QStringLiteral( "cs" ), cs );
1048  elemCoordinates.setAttribute( QStringLiteral( "ts" ), ts );
1049 
1050  QString strCoordinates;
1051 
1052  for ( const QgsPoint &p : points )
1053  if ( axisOrder == QgsAbstractGeometry::AxisOrder::XY )
1054  strCoordinates += qgsDoubleToString( p.x(), precision ) + cs + qgsDoubleToString( p.y(), precision ) + ts;
1055  else
1056  strCoordinates += qgsDoubleToString( p.y(), precision ) + cs + qgsDoubleToString( p.x(), precision ) + ts;
1057 
1058  if ( strCoordinates.endsWith( ts ) )
1059  strCoordinates.chop( 1 ); // Remove trailing space
1060 
1061  elemCoordinates.appendChild( doc.createTextNode( strCoordinates ) );
1062  return elemCoordinates;
1063 }
1064 
1065 QDomElement QgsGeometryUtils::pointsToGML3( const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D, const QgsAbstractGeometry::AxisOrder &axisOrder )
1066 {
1067  QDomElement elemPosList = doc.createElementNS( ns, QStringLiteral( "posList" ) );
1068  elemPosList.setAttribute( QStringLiteral( "srsDimension" ), is3D ? 3 : 2 );
1069 
1070  QString strCoordinates;
1071  for ( const QgsPoint &p : points )
1072  {
1073  if ( axisOrder == QgsAbstractGeometry::AxisOrder::XY )
1074  strCoordinates += qgsDoubleToString( p.x(), precision ) + ' ' + qgsDoubleToString( p.y(), precision ) + ' ';
1075  else
1076  strCoordinates += qgsDoubleToString( p.y(), precision ) + ' ' + qgsDoubleToString( p.x(), precision ) + ' ';
1077  if ( is3D )
1078  strCoordinates += qgsDoubleToString( p.z(), precision ) + ' ';
1079  }
1080  if ( strCoordinates.endsWith( ' ' ) )
1081  strCoordinates.chop( 1 ); // Remove trailing space
1082 
1083  elemPosList.appendChild( doc.createTextNode( strCoordinates ) );
1084  return elemPosList;
1085 }
1086 
1087 QString QgsGeometryUtils::pointsToJSON( const QgsPointSequence &points, int precision )
1088 {
1089  QString json = QStringLiteral( "[ " );
1090  for ( const QgsPoint &p : points )
1091  {
1092  json += '[' + qgsDoubleToString( p.x(), precision ) + ", " + qgsDoubleToString( p.y(), precision ) + "], ";
1093  }
1094  if ( json.endsWith( QLatin1String( ", " ) ) )
1095  {
1096  json.chop( 2 ); // Remove last ", "
1097  }
1098  json += ']';
1099  return json;
1100 }
1101 
1103 {
1104  double clippedAngle = angle;
1105  if ( clippedAngle >= M_PI * 2 || clippedAngle <= -2 * M_PI )
1106  {
1107  clippedAngle = std::fmod( clippedAngle, 2 * M_PI );
1108  }
1109  if ( clippedAngle < 0.0 )
1110  {
1111  clippedAngle += 2 * M_PI;
1112  }
1113  return clippedAngle;
1114 }
1115 
1116 QPair<QgsWkbTypes::Type, QString> QgsGeometryUtils::wktReadBlock( const QString &wkt )
1117 {
1119 
1120  QRegularExpression cooRegEx( QStringLiteral( "^[^\\(]*\\((.*)\\)[^\\)]*$" ) );
1121  cooRegEx.setPatternOptions( QRegularExpression::DotMatchesEverythingOption );
1122  QRegularExpressionMatch match = cooRegEx.match( wkt );
1123  QString contents = match.hasMatch() ? match.captured( 1 ) : QString();
1124  return qMakePair( wkbType, contents );
1125 }
1126 
1127 QStringList QgsGeometryUtils::wktGetChildBlocks( const QString &wkt, const QString &defaultType )
1128 {
1129  int level = 0;
1130  QString block;
1131  QStringList blocks;
1132  for ( int i = 0, n = wkt.length(); i < n; ++i )
1133  {
1134  if ( ( wkt[i].isSpace() || wkt[i] == '\n' || wkt[i] == '\t' ) && level == 0 )
1135  continue;
1136 
1137  if ( wkt[i] == ',' && level == 0 )
1138  {
1139  if ( !block.isEmpty() )
1140  {
1141  if ( block.startsWith( '(' ) && !defaultType.isEmpty() )
1142  block.prepend( defaultType + ' ' );
1143  blocks.append( block );
1144  }
1145  block.clear();
1146  continue;
1147  }
1148  if ( wkt[i] == '(' )
1149  ++level;
1150  else if ( wkt[i] == ')' )
1151  --level;
1152  block += wkt[i];
1153  }
1154  if ( !block.isEmpty() )
1155  {
1156  if ( block.startsWith( '(' ) && !defaultType.isEmpty() )
1157  block.prepend( defaultType + ' ' );
1158  blocks.append( block );
1159  }
1160  return blocks;
1161 }
1162 
1164 {
1166 
1167 
1168  double x = ( pt1.x() + pt2.x() ) / 2.0;
1169  double y = ( pt1.y() + pt2.y() ) / 2.0;
1170  double z = std::numeric_limits<double>::quiet_NaN();
1171  double m = std::numeric_limits<double>::quiet_NaN();
1172 
1173  if ( pt1.is3D() || pt2.is3D() )
1174  {
1175  pType = QgsWkbTypes::addZ( pType );
1176  z = ( pt1.z() + pt2.z() ) / 2.0;
1177  }
1178 
1179  if ( pt1.isMeasure() || pt2.isMeasure() )
1180  {
1181  pType = QgsWkbTypes::addM( pType );
1182  m = ( pt1.m() + pt2.m() ) / 2.0;
1183  }
1184 
1185  return QgsPoint( pType, x, y, z, m );
1186 }
1187 
1188 QgsPoint QgsGeometryUtils::interpolatePointOnLine( const QgsPoint &p1, const QgsPoint &p2, const double fraction )
1189 {
1190  const double _fraction = 1 - fraction;
1191  return QgsPoint( p1.wkbType(),
1192  p1.x() * _fraction + p2.x() * fraction,
1193  p1.y() * _fraction + p2.y() * fraction,
1194  p1.is3D() ? p1.z() * _fraction + p2.z() * fraction : std::numeric_limits<double>::quiet_NaN(),
1195  p1.isMeasure() ? p1.m() * _fraction + p2.m() * fraction : std::numeric_limits<double>::quiet_NaN() );
1196 }
1197 
1198 QgsPointXY QgsGeometryUtils::interpolatePointOnLine( const double x1, const double y1, const double x2, const double y2, const double fraction )
1199 {
1200  const double deltaX = ( x2 - x1 ) * fraction;
1201  const double deltaY = ( y2 - y1 ) * fraction;
1202  return QgsPointXY( x1 + deltaX, y1 + deltaY );
1203 }
1204 
1205 QgsPointXY QgsGeometryUtils::interpolatePointOnLineByValue( const double x1, const double y1, const double v1, const double x2, const double y2, const double v2, const double value )
1206 {
1207  if ( qgsDoubleNear( v1, v2 ) )
1208  return QgsPointXY( x1, y1 );
1209 
1210  const double fraction = ( value - v1 ) / ( v2 - v1 );
1211  return interpolatePointOnLine( x1, y1, x2, y2, fraction );
1212 }
1213 
1214 double QgsGeometryUtils::gradient( const QgsPoint &pt1, const QgsPoint &pt2 )
1215 {
1216  double delta_x = pt2.x() - pt1.x();
1217  double delta_y = pt2.y() - pt1.y();
1218  if ( qgsDoubleNear( delta_x, 0.0 ) )
1219  {
1220  return INFINITY;
1221  }
1222 
1223  return delta_y / delta_x;
1224 }
1225 
1226 void QgsGeometryUtils::coefficients( const QgsPoint &pt1, const QgsPoint &pt2, double &a, double &b, double &c )
1227 {
1228  if ( qgsDoubleNear( pt1.x(), pt2.x() ) )
1229  {
1230  a = 1;
1231  b = 0;
1232  c = -pt1.x();
1233  }
1234  else if ( qgsDoubleNear( pt1.y(), pt2.y() ) )
1235  {
1236  a = 0;
1237  b = 1;
1238  c = -pt1.y();
1239  }
1240  else
1241  {
1242  a = pt1.y() - pt2.y();
1243  b = pt2.x() - pt1.x();
1244  c = pt1.x() * pt2.y() - pt1.y() * pt2.x();
1245  }
1246 
1247 }
1248 
1250 {
1251  QgsLineString line;
1252  QgsPoint p2;
1253 
1254  if ( ( p == s1 ) || ( p == s2 ) )
1255  {
1256  return line;
1257  }
1258 
1259  double a, b, c;
1260  coefficients( s1, s2, a, b, c );
1261 
1262  if ( qgsDoubleNear( a, 0 ) )
1263  {
1264  p2 = QgsPoint( p.x(), s1.y() );
1265  }
1266  else if ( qgsDoubleNear( b, 0 ) )
1267  {
1268  p2 = QgsPoint( s1.x(), p.y() );
1269  }
1270  else
1271  {
1272  double y = ( -c - a * p.x() ) / b;
1273  double m = gradient( s1, s2 );
1274  double d2 = 1 + m * m;
1275  double H = p.y() - y;
1276  double dx = m * H / d2;
1277  double dy = m * dx;
1278  p2 = QgsPoint( p.x() + dx, y + dy );
1279  }
1280 
1281  line.addVertex( p );
1282  line.addVertex( p2 );
1283 
1284  return line;
1285 }
1286 
1287 double QgsGeometryUtils::lineAngle( double x1, double y1, double x2, double y2 )
1288 {
1289  double at = std::atan2( y2 - y1, x2 - x1 );
1290  double a = -at + M_PI_2;
1291  return normalizedAngle( a );
1292 }
1293 
1294 double QgsGeometryUtils::angleBetweenThreePoints( double x1, double y1, double x2, double y2, double x3, double y3 )
1295 {
1296  double angle1 = std::atan2( y1 - y2, x1 - x2 );
1297  double angle2 = std::atan2( y3 - y2, x3 - x2 );
1298  return normalizedAngle( angle1 - angle2 );
1299 }
1300 
1301 double QgsGeometryUtils::linePerpendicularAngle( double x1, double y1, double x2, double y2 )
1302 {
1303  double a = lineAngle( x1, y1, x2, y2 );
1304  a += M_PI_2;
1305  return normalizedAngle( a );
1306 }
1307 
1308 double QgsGeometryUtils::averageAngle( double x1, double y1, double x2, double y2, double x3, double y3 )
1309 {
1310  // calc average angle between the previous and next point
1311  double a1 = lineAngle( x1, y1, x2, y2 );
1312  double a2 = lineAngle( x2, y2, x3, y3 );
1313  return averageAngle( a1, a2 );
1314 }
1315 
1316 double QgsGeometryUtils::averageAngle( double a1, double a2 )
1317 {
1318  a1 = normalizedAngle( a1 );
1319  a2 = normalizedAngle( a2 );
1320  double clockwiseDiff = 0.0;
1321  if ( a2 >= a1 )
1322  {
1323  clockwiseDiff = a2 - a1;
1324  }
1325  else
1326  {
1327  clockwiseDiff = a2 + ( 2 * M_PI - a1 );
1328  }
1329  double counterClockwiseDiff = 2 * M_PI - clockwiseDiff;
1330 
1331  double resultAngle = 0;
1332  if ( clockwiseDiff <= counterClockwiseDiff )
1333  {
1334  resultAngle = a1 + clockwiseDiff / 2.0;
1335  }
1336  else
1337  {
1338  resultAngle = a1 - counterClockwiseDiff / 2.0;
1339  }
1340  return normalizedAngle( resultAngle );
1341 }
1342 
1344  const QgsVector3D &P2, const QgsVector3D &P22 )
1345 {
1346  QgsVector3D u1 = P12 - P1;
1347  QgsVector3D u2 = P22 - P2;
1348  QgsVector3D u3 = QgsVector3D::crossProduct( u1, u2 );
1349  if ( u3.length() == 0 ) return 1;
1350  u3.normalize();
1351  QgsVector3D dir = P1 - P2;
1352  return std::fabs( ( QgsVector3D::dotProduct( dir, u3 ) ) ); // u3 is already normalized
1353 }
1354 
1356  const QgsVector3D &P2, const QgsVector3D &P22,
1357  QgsVector3D &X1, double epsilon )
1358 {
1359  QgsVector3D d = P2 - P1;
1360  QgsVector3D u1 = P12 - P1;
1361  u1.normalize();
1362  QgsVector3D u2 = P22 - P2;
1363  u2.normalize();
1364  QgsVector3D u3 = QgsVector3D::crossProduct( u1, u2 );
1365 
1366  if ( std::fabs( u3.x() ) <= epsilon &&
1367  std::fabs( u3.y() ) <= epsilon &&
1368  std::fabs( u3.z() ) <= epsilon )
1369  {
1370  // The rays are almost parallel.
1371  return false;
1372  }
1373 
1374  // X1 and X2 are the closest points on lines
1375  // we want to find X1 (lies on u1)
1376  // solving the linear equation in r1 and r2: Xi = Pi + ri*ui
1377  // we are only interested in X1 so we only solve for r1.
1378  float a1 = QgsVector3D::dotProduct( u1, u1 ), b1 = QgsVector3D::dotProduct( u1, u2 ), c1 = QgsVector3D::dotProduct( u1, d );
1379  float a2 = QgsVector3D::dotProduct( u1, u2 ), b2 = QgsVector3D::dotProduct( u2, u2 ), c2 = QgsVector3D::dotProduct( u2, d );
1380  if ( !( std::fabs( b1 ) > epsilon ) )
1381  {
1382  // Denominator is close to zero.
1383  return false;
1384  }
1385  if ( !( a2 != -1 && a2 != 1 ) )
1386  {
1387  // Lines are parallel
1388  return false;
1389  }
1390 
1391  double r1 = ( c2 - b2 * c1 / b1 ) / ( a2 - b2 * a1 / b1 );
1392  X1 = P1 + u1 * r1;
1393 
1394  return true;
1395 }
1396 
1398  const QgsVector3D &Lb1, const QgsVector3D &Lb2,
1399  QgsVector3D &intersection )
1400 {
1401 
1402  // if all Vector are on the same plane (have the same Z), use the 2D intersection
1403  // else return a false result
1404  if ( qgsDoubleNear( La1.z(), La2.z() ) && qgsDoubleNear( La1.z(), Lb1.z() ) && qgsDoubleNear( La1.z(), Lb2.z() ) )
1405  {
1406  QgsPoint ptInter;
1407  bool isIntersection;
1408  segmentIntersection( QgsPoint( La1.x(), La1.y() ),
1409  QgsPoint( La2.x(), La2.y() ),
1410  QgsPoint( Lb1.x(), Lb1.y() ),
1411  QgsPoint( Lb2.x(), Lb2.y() ),
1412  ptInter,
1413  isIntersection,
1414  1e-8,
1415  true );
1416  intersection.set( ptInter.x(), ptInter.y(), La1.z() );
1417  return true;
1418  }
1419 
1420  // first check if lines have an exact intersection point
1421  // do it by checking if the shortest distance is exactly 0
1422  float distance = skewLinesDistance( La1, La2, Lb1, Lb2 );
1423  if ( qgsDoubleNear( distance, 0.0 ) )
1424  {
1425  // 3d lines have exact intersection point.
1426  QgsVector3D C = La2;
1427  QgsVector3D D = Lb2;
1428  QgsVector3D e = La1 - La2;
1429  QgsVector3D f = Lb1 - Lb2;
1430  QgsVector3D g = D - C;
1431  if ( qgsDoubleNear( ( QgsVector3D::crossProduct( f, g ) ).length(), 0.0 ) || qgsDoubleNear( ( QgsVector3D::crossProduct( f, e ) ).length(), 0.0 ) )
1432  {
1433  // Lines have no intersection, are they parallel?
1434  return false;
1435  }
1436 
1437  QgsVector3D fgn = QgsVector3D::crossProduct( f, g );
1438  fgn.normalize();
1439 
1440  QgsVector3D fen = QgsVector3D::crossProduct( f, e );
1441  fen.normalize();
1442 
1443  int di = -1;
1444  if ( fgn == fen ) // same direction?
1445  di *= -1;
1446 
1447  intersection = C + e * di * ( QgsVector3D::crossProduct( f, g ).length() / QgsVector3D::crossProduct( f, e ).length() );
1448  return true;
1449  }
1450 
1451  // try to calculate the approximate intersection point
1452  QgsVector3D X1, X2;
1453  bool firstIsDone = skewLinesProjection( La1, La2, Lb1, Lb2, X1 );
1454  bool secondIsDone = skewLinesProjection( Lb1, Lb2, La1, La2, X2 );
1455 
1456  if ( !firstIsDone || !secondIsDone )
1457  {
1458  // Could not obtain projection point.
1459  return false;
1460  }
1461 
1462  intersection = ( X1 + X2 ) / 2.0;
1463  return true;
1464 }
1465 
1467 {
1468  bool rc = false;
1469 
1470  for ( const QgsPoint &pt : points )
1471  {
1472  if ( pt.is3D() )
1473  {
1474  point.convertTo( QgsWkbTypes::addZ( point.wkbType() ) );
1475  point.setZ( pt.z() );
1476  rc = true;
1477  break;
1478  }
1479  }
1480 
1481  return rc;
1482 }
bool isMeasure() const
Returns true if the geometry contains m values.
Maximum distance between an arbitrary point on the original curve and closest point on its approximat...
static bool lineCircleIntersection(const QgsPointXY &center, double radius, const QgsPointXY &linePoint1, const QgsPointXY &linePoint2, QgsPointXY &intersection)
Compute the intersection of a line and a circle.
3 Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double preci...
Definition: qgsvector3d.h:29
static double skewLinesDistance(const QgsVector3D &P1, const QgsVector3D &P12, const QgsVector3D &P2, const QgsVector3D &P22)
An algorithm to calculate the shortest distance between two skew lines.
static QString pointsToJSON(const QgsPointSequence &points, int precision)
Returns a geoJSON coordinates string.
void set(double x, double y)
Sets the x and y value of the point.
Definition: qgspointxy.h:119
double y
Definition: qgspoint.h:42
static bool circleAngleBetween(double angle, double angle1, double angle2, bool clockwise)
Returns true if, in a circle, angle is between angle1 and angle2.
static double interpolateArcValue(double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3)
Interpolate a value at given angle on circular arc given values (zm1, zm2, zm3) at three different an...
static double ccwAngle(double dy, double dx)
Returns the counter clockwise angle between a line with components dx, dy and the line with dx > 0 an...
static bool verticesAtDistance(const QgsAbstractGeometry &geometry, double distance, QgsVertexId &previousVertex, QgsVertexId &nextVertex)
Retrieves the vertices which are before and after the interpolated point at a specified distance alon...
static bool segmentIntersection(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q1, const QgsPoint &q2, QgsPoint &intersectionPoint, bool &isIntersection, double tolerance=1e-8, bool acceptImproperIntersection=false)
Compute the intersection between two segments.
static QVector< SelfIntersection > selfIntersections(const QgsAbstractGeometry *geom, int part, int ring, double tolerance)
Find self intersections in a polyline.
static int segmentSide(const QgsPoint &pt1, const QgsPoint &pt3, const QgsPoint &pt2)
For line defined by points pt1 and pt3, find out on which side of the line is point pt3...
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:469
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...
bool isValid() const
Returns true if the vertex id is valid.
void setZ(double z)
Sets the point&#39;s z-coordinate.
Definition: qgspoint.h:237
static QPair< QgsWkbTypes::Type, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the average angle (in radians) between the two linear segments from (x1, y1) to (x2, y2) and (x2, y2) to (x3, y3).
static double angleBetweenThreePoints(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the angle between the lines AB and BC, where AB and BC described by points a...
static QDomElement pointsToGML2(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder &axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::coordinates DOM element.
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Definition: qgspoint.h:276
static double gradient(const QgsPoint &pt1, const QgsPoint &pt2)
Returns the gradient of a line defined by points pt1 and pt2.
double length() const
Returns the length of the vector.
Definition: qgsvector3d.h:106
double y
Definition: qgspointxy.h:48
A class to represent a 2D point.
Definition: qgspointxy.h:43
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:251
static QgsPoint segmentMidPointFromCenter(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, bool useShortestArc=true)
Calculates the midpoint on the circle passing through p1 and p2, with the specified center coordinate...
Curve polygon geometry type.
static double linePerpendicularAngle(double x1, double y1, double x2, double y2)
Calculates the perpendicular angle to a line joining two points.
static Type parseType(const QString &wktStr)
Attempts to extract the WKB type from a WKT string.
static bool segmentMidPoint(const QgsPoint &p1, const QgsPoint &p2, QgsPoint &result, double radius, const QgsPoint &mousePos)
Calculates midpoint on circle passing through p1 and p2, closest to the given coordinate mousePos...
static double circleLength(double x1, double y1, double x2, double y2, double x3, double y3)
Length of a circular string segment defined by pt1, pt2, pt3.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
static QgsPoint closestVertex(const QgsAbstractGeometry &geom, const QgsPoint &pt, QgsVertexId &id)
Returns the closest vertex to a geometry for a specified point.
virtual bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const =0
Returns next vertex id and coordinates.
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:45
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:768
static int circleCircleOuterTangents(const QgsPointXY &center1, double radius1, const QgsPointXY &center2, double radius2, QgsPointXY &line1P1, QgsPointXY &line1P2, QgsPointXY &line2P1, QgsPointXY &line2P2)
Calculates the outer tangent points for two circles, centered at center1 and center2 and with radii o...
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
static QStringList wktGetChildBlocks(const QString &wkt, const QString &defaultType=QString())
Parses a WKT string and returns of list of blocks contained in the WKT.
void set(double x, double y, double z)
Sets vector coordinates.
Definition: qgsvector3d.h:50
static double dotProduct(const QgsVector3D &v1, const QgsVector3D &v2)
Returns the dot product of two vectors.
Definition: qgsvector3d.h:92
static QgsVector3D crossProduct(const QgsVector3D &v1, const QgsVector3D &v2)
Returns the cross product of two vectors.
Definition: qgsvector3d.h:98
void normalize()
Normalizes the current vector in place.
Definition: qgsvector3d.h:112
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
Definition: qgspointxy.h:169
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
virtual double length() const
Returns the length of the geometry.
static bool circleClockwise(double angle1, double angle2, double angle3)
Returns true if circle is ordered clockwise.
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:47
static QString pointsToWKT(const QgsPointSequence &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:67
virtual double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *std::numeric_limits< double >::epsilon()) const =0
Searches for the closest segment of the geometry to a given point.
static bool setZValueFromPoints(const QgsPointSequence &points, QgsPoint &point)
A Z dimension is added to point if one of the point in the list points is in 3D.
virtual double segmentLength(QgsVertexId startVertex) const =0
Returns the length of the segment of the geometry which begins at startVertex.
static Type addM(Type type)
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:889
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
Utility class for identifying a unique vertex within a geometry.
const double DEFAULT_SEGMENT_EPSILON
Default snapping tolerance for segments.
Definition: qgis.h:487
Geometry collection.
static QgsPoint midpoint(const QgsPoint &pt1, const QgsPoint &pt2)
Returns a middle point between points pt1 and pt2.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:237
static QDomElement pointsToGML3(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D, const QgsAbstractGeometry::AxisOrder &axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::posList DOM element.
static Type addZ(Type type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:864
static double circleTangentDirection(const QgsPoint &tangentPoint, const QgsPoint &cp1, const QgsPoint &cp2, const QgsPoint &cp3)
Calculates the direction angle of a circle tangent (clockwise from north in radians) ...
static bool angleOnCircle(double angle, double angle1, double angle2, double angle3)
Returns true if an angle is between angle1 and angle3 on a circle described by angle1, angle2 and angle3.
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
static bool linesIntersection3D(const QgsVector3D &La1, const QgsVector3D &La2, const QgsVector3D &Lb1, const QgsVector3D &Lb2, QgsVector3D &intersection)
An algorithm to calculate an (approximate) intersection of two lines in 3D.
static QgsPointXY interpolatePointOnLine(double x1, double y1, double x2, double y2, double fraction)
Interpolates the position of a point a fraction of the way along the line from (x1, y1) to (x2, y2).
Abstract base class for all geometries.
static QVector< QgsLineString * > extractLineStrings(const QgsAbstractGeometry *geom)
Returns list of linestrings extracted from the passed geometry.
static double normalizedAngle(double angle)
Ensures that an angle is in the range 0 <= angle < 2 pi.
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Definition: qgspointxy.h:190
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which correspond to this point projected by a specified distance with specified a...
Definition: qgspoint.cpp:631
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
AxisOrder
Axis order for GML generation.
double length() const
Returns the length of the vector.
Definition: qgsvector.cpp:71
static bool lineIntersection(const QgsPoint &p1, QgsVector v1, const QgsPoint &p2, QgsVector v2, QgsPoint &intersection)
Computes the intersection between two lines.
double x
Definition: qgspointxy.h:47
A class to represent a vector.
Definition: qgsvector.h:28
static bool skewLinesProjection(const QgsVector3D &P1, const QgsVector3D &P12, const QgsVector3D &P2, const QgsVector3D &P22, QgsVector3D &X1, double epsilon=0.0001)
A method to project one skew line onto another.
QVector< QgsPoint > QgsPointSequence
static QgsPoint pointOnLineWithDistance(const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance)
Returns a point a specified distance toward a second point.
static double sqrDistance2D(const QgsPoint &pt1, const QgsPoint &pt2)
Returns the squared 2D distance between two points.
static double sweepAngle(double centerX, double centerY, double x1, double y1, double x2, double y2, double x3, double y3)
Calculates angle of a circular string part defined by pt1, pt2, pt3.
static QgsLineString perpendicularSegment(const QgsPoint &p, const QgsPoint &s1, const QgsPoint &s2)
Create a perpendicular line segment from p to segment [s1, s2].
static QgsPointSequence pointsFromWKT(const QString &wktCoordinateList, bool is3D, bool isMeasure)
Returns a list of points contained in a WKT string.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
virtual int vertexCount(int part=0, int ring=0) const =0
Returns the number of vertices of which this geometry is built.
static int leftOfLine(double x, double y, double x1, double y1, double x2, double y2)
Returns a value < 0 if the point (x, y) is left of the line from (x1, y1) -> ( x2, y2).
static bool tangentPointAndCircle(const QgsPointXY &center, double radius, const QgsPointXY &p, QgsPointXY &pt1, QgsPointXY &pt2)
Calculates the tangent points between the circle with the specified center and radius and the point p...
static void circleCenterRadius(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double &radius, double &centerX, double &centerY)
Returns radius and center of the circle through pt1, pt2, pt3.
bool convertTo(QgsWkbTypes::Type type) override
Converts the geometry to a specified type.
Definition: qgspoint.cpp:538
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:480
static void pointsToWKB(QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
static int circleCircleIntersections(QgsPointXY center1, double radius1, QgsPointXY center2, double radius2, QgsPointXY &intersection1, QgsPointXY &intersection2)
Calculates the intersections points between the circle with center center1 and radius radius1 and the...
static double sqrDistToLine(double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon)
Returns the squared distance between a point and a line.
double z
Definition: qgspoint.h:43
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:818
double x() const
Returns the vector&#39;s x-component.
Definition: qgsvector.cpp:76
static QgsPoint closestPoint(const QgsAbstractGeometry &geometry, const QgsPoint &point)
Returns the nearest point on a segment of a geometry for the specified point.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
double y() const
Returns the vector&#39;s y-component.
Definition: qgsvector.cpp:81
static double distanceToVertex(const QgsAbstractGeometry &geom, QgsVertexId id)
Returns the distance along a geometry from its first vertex to the specified vertex.
static QgsPointXY interpolatePointOnLineByValue(double x1, double y1, double v1, double x2, double y2, double v2, double value)
Interpolates the position of a point along the line from (x1, y1) to (x2, y2).
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:43
double m
Definition: qgspoint.h:44
static void segmentizeArc(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, QgsPointSequence &points, double tolerance=M_PI_2/90, QgsAbstractGeometry::SegmentationToleranceType toleranceType=QgsAbstractGeometry::MaximumAngle, bool hasZ=false, bool hasM=false)
Convert circular arc defined by p1, p2, p3 (p1/p3 being start resp.
static void coefficients(const QgsPoint &pt1, const QgsPoint &pt2, double &a, double &b, double &c)
Returns the coefficients (a, b, c for equation "ax + by + c = 0") of a line defined by points pt1 and...
double x
Definition: qgspoint.h:41