QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
qgsgeometryutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgeometryutils.cpp
3 -------------------------------------------------------------------
4Date : 21 Nov 2014
5Copyright : (C) 2014 by Marco Hugentobler
6email : 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"
21#include "qgslinestring.h"
22#include "qgswkbptr.h"
23
24#include <memory>
25#include <QStringList>
26#include <QVector>
27#include <QRegularExpression>
28#include <nlohmann/json.hpp>
29
30QVector<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 if ( geom.isEmpty() || pt.isEmpty() )
74 return minDistPoint;
75
76 QgsVertexId vertexId;
77 QgsPoint vertex;
78 while ( geom.nextVertex( vertexId, vertex ) )
79 {
80 currentDist = QgsGeometryUtils::sqrDistance2D( pt, vertex );
81 // The <= is on purpose: for geometries with closing vertices, this ensures
82 // that the closing vertex is returned. For the vertex tool, the rubberband
83 // of the closing vertex is above the opening vertex, hence with the <=
84 // situations where the covered opening vertex rubberband is selected are
85 // avoided.
86 if ( currentDist <= minDist )
87 {
88 minDist = currentDist;
89 minDistPoint = vertex;
90 id.part = vertexId.part;
91 id.ring = vertexId.ring;
92 id.vertex = vertexId.vertex;
93 id.type = vertexId.type;
94 }
95 }
96
97 return minDistPoint;
98}
99
101{
103 QgsVertexId vertexAfter;
104 geometry.closestSegment( point, closestPoint, vertexAfter, nullptr, DEFAULT_SEGMENT_EPSILON );
105 if ( vertexAfter.isValid() )
106 {
107 const QgsPoint pointAfter = geometry.vertexAt( vertexAfter );
108 if ( vertexAfter.vertex > 0 )
109 {
110 QgsVertexId vertexBefore = vertexAfter;
111 vertexBefore.vertex--;
112 const QgsPoint pointBefore = geometry.vertexAt( vertexBefore );
113 const double length = pointBefore.distance( pointAfter );
114 const double distance = pointBefore.distance( closestPoint );
115
116 if ( qgsDoubleNear( distance, 0.0 ) )
117 closestPoint = pointBefore;
118 else if ( qgsDoubleNear( distance, length ) )
119 closestPoint = pointAfter;
120 else
121 {
122 if ( QgsWkbTypes::hasZ( geometry.wkbType() ) && length )
123 closestPoint.addZValue( pointBefore.z() + ( pointAfter.z() - pointBefore.z() ) * distance / length );
124 if ( QgsWkbTypes::hasM( geometry.wkbType() ) )
125 closestPoint.addMValue( pointBefore.m() + ( pointAfter.m() - pointBefore.m() ) * distance / length );
126 }
127 }
128 }
129
130 return closestPoint;
131}
132
134{
135 double currentDist = 0;
136 QgsVertexId vertexId;
137 QgsPoint vertex;
138 while ( geom.nextVertex( vertexId, vertex ) )
139 {
140 if ( vertexId == id )
141 {
142 //found target vertex
143 return currentDist;
144 }
145 currentDist += geom.segmentLength( vertexId );
146 }
147
148 //could not find target vertex
149 return -1;
150}
151
152bool QgsGeometryUtils::verticesAtDistance( const QgsAbstractGeometry &geometry, double distance, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
153{
154 double currentDist = 0;
155 previousVertex = QgsVertexId();
156 nextVertex = QgsVertexId();
157
158 if ( geometry.isEmpty() )
159 {
160 return false;
161 }
162
163 QgsPoint point;
164 QgsPoint previousPoint;
165
166 if ( qgsDoubleNear( distance, 0.0 ) )
167 {
168 geometry.nextVertex( previousVertex, point );
169 nextVertex = previousVertex;
170 return true;
171 }
172
173 bool first = true;
174 while ( currentDist < distance && geometry.nextVertex( nextVertex, point ) )
175 {
176 if ( !first && nextVertex.part == previousVertex.part && nextVertex.ring == previousVertex.ring )
177 {
178 currentDist += std::sqrt( QgsGeometryUtils::sqrDistance2D( previousPoint, point ) );
179 }
180
181 if ( qgsDoubleNear( currentDist, distance ) )
182 {
183 // exact hit!
184 previousVertex = nextVertex;
185 return true;
186 }
187
188 if ( currentDist > distance )
189 {
190 return true;
191 }
192
193 previousVertex = nextVertex;
194 previousPoint = point;
195 first = false;
196 }
197
198 //could not find target distance
199 return false;
200}
201
202double QgsGeometryUtils::sqrDistance2D( const QgsPoint &pt1, const QgsPoint &pt2 )
203{
204 return ( pt1.x() - pt2.x() ) * ( pt1.x() - pt2.x() ) + ( pt1.y() - pt2.y() ) * ( pt1.y() - pt2.y() );
205}
206
207double QgsGeometryUtils::sqrDistToLine( double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon )
208{
209 minDistX = x1;
210 minDistY = y1;
211
212 double dx = x2 - x1;
213 double dy = y2 - y1;
214
215 if ( !qgsDoubleNear( dx, 0.0 ) || !qgsDoubleNear( dy, 0.0 ) )
216 {
217 const double t = ( ( ptX - x1 ) * dx + ( ptY - y1 ) * dy ) / ( dx * dx + dy * dy );
218 if ( t > 1 )
219 {
220 minDistX = x2;
221 minDistY = y2;
222 }
223 else if ( t > 0 )
224 {
225 minDistX += dx * t;
226 minDistY += dy * t;
227 }
228 }
229
230 dx = ptX - minDistX;
231 dy = ptY - minDistY;
232
233 const double dist = dx * dx + dy * dy;
234
235 //prevent rounding errors if the point is directly on the segment
236 if ( qgsDoubleNear( dist, 0.0, epsilon ) )
237 {
238 minDistX = ptX;
239 minDistY = ptY;
240 return 0.0;
241 }
242
243 return dist;
244}
245
246double QgsGeometryUtils::distToInfiniteLine( const QgsPoint &point, const QgsPoint &linePoint1, const QgsPoint &linePoint2, double epsilon )
247{
248 const double area = std::abs(
249 ( linePoint1.x() - linePoint2.x() ) * ( point.y() - linePoint2.y() ) -
250 ( linePoint1.y() - linePoint2.y() ) * ( point.x() - linePoint2.x() )
251 );
252
253 const double length = std::sqrt(
254 std::pow( linePoint1.x() - linePoint2.x(), 2 ) +
255 std::pow( linePoint1.y() - linePoint2.y(), 2 )
256 );
257
258 const double distance = area / length;
259 return qgsDoubleNear( distance, 0.0, epsilon ) ? 0.0 : distance;
260}
261
262bool QgsGeometryUtils::lineIntersection( const QgsPoint &p1, QgsVector v1, const QgsPoint &p2, QgsVector v2, QgsPoint &intersection )
263{
264 const double d = v1.y() * v2.x() - v1.x() * v2.y();
265
266 if ( qgsDoubleNear( d, 0 ) )
267 return false;
268
269 const double dx = p2.x() - p1.x();
270 const double dy = p2.y() - p1.y();
271 const double k = ( dy * v2.x() - dx * v2.y() ) / d;
272
273 intersection = QgsPoint( p1.x() + v1.x() * k, p1.y() + v1.y() * k );
274
275 // z and m support for intersection point
277
278 return true;
279}
280
281bool QgsGeometryUtils::segmentIntersection( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q1, const QgsPoint &q2, QgsPoint &intersectionPoint, bool &isIntersection, const double tolerance, bool acceptImproperIntersection )
282{
283 isIntersection = false;
284
285 QgsVector v( p2.x() - p1.x(), p2.y() - p1.y() );
286 QgsVector w( q2.x() - q1.x(), q2.y() - q1.y() );
287 const double vl = v.length();
288 const double wl = w.length();
289
290 if ( qgsDoubleNear( vl, 0.0, tolerance ) || qgsDoubleNear( wl, 0.0, tolerance ) )
291 {
292 return false;
293 }
294 v = v / vl;
295 w = w / wl;
296
297 if ( !QgsGeometryUtils::lineIntersection( p1, v, q1, w, intersectionPoint ) )
298 {
299 return false;
300 }
301
302 isIntersection = true;
303 if ( acceptImproperIntersection )
304 {
305 if ( ( p1 == q1 ) || ( p1 == q2 ) )
306 {
307 intersectionPoint = p1;
308 return true;
309 }
310 else if ( ( p2 == q1 ) || ( p2 == q2 ) )
311 {
312 intersectionPoint = p2;
313 return true;
314 }
315
316 double x, y;
317 if (
318 // intersectionPoint = p1
319 qgsDoubleNear( QgsGeometryUtils::sqrDistToLine( p1.x(), p1.y(), q1.x(), q1.y(), q2.x(), q2.y(), x, y, tolerance ), 0.0, tolerance ) ||
320 // intersectionPoint = p2
321 qgsDoubleNear( QgsGeometryUtils::sqrDistToLine( p2.x(), p2.y(), q1.x(), q1.y(), q2.x(), q2.y(), x, y, tolerance ), 0.0, tolerance ) ||
322 // intersectionPoint = q1
323 qgsDoubleNear( QgsGeometryUtils::sqrDistToLine( q1.x(), q1.y(), p1.x(), p1.y(), p2.x(), p2.y(), x, y, tolerance ), 0.0, tolerance ) ||
324 // intersectionPoint = q2
325 qgsDoubleNear( QgsGeometryUtils::sqrDistToLine( q2.x(), q2.y(), p1.x(), p1.y(), p2.x(), p2.y(), x, y, tolerance ), 0.0, tolerance )
326 )
327 {
328 return true;
329 }
330 }
331
332 const double lambdav = QgsVector( intersectionPoint.x() - p1.x(), intersectionPoint.y() - p1.y() ) * v;
333 if ( lambdav < 0. + tolerance || lambdav > vl - tolerance )
334 return false;
335
336 const double lambdaw = QgsVector( intersectionPoint.x() - q1.x(), intersectionPoint.y() - q1.y() ) * w;
337 return !( lambdaw < 0. + tolerance || lambdaw >= wl - tolerance );
338
339}
340
341bool QgsGeometryUtils::lineCircleIntersection( const QgsPointXY &center, const double radius,
342 const QgsPointXY &linePoint1, const QgsPointXY &linePoint2,
343 QgsPointXY &intersection )
344{
345 // formula taken from http://mathworld.wolfram.com/Circle-LineIntersection.html
346
347 const double x1 = linePoint1.x() - center.x();
348 const double y1 = linePoint1.y() - center.y();
349 const double x2 = linePoint2.x() - center.x();
350 const double y2 = linePoint2.y() - center.y();
351 const double dx = x2 - x1;
352 const double dy = y2 - y1;
353
354 const double dr2 = std::pow( dx, 2 ) + std::pow( dy, 2 );
355 const double d = x1 * y2 - x2 * y1;
356
357 const double disc = std::pow( radius, 2 ) * dr2 - std::pow( d, 2 );
358
359 if ( disc < 0 )
360 {
361 //no intersection or tangent
362 return false;
363 }
364 else
365 {
366 // two solutions
367 const int sgnDy = dy < 0 ? -1 : 1;
368
369 const double sqrDisc = std::sqrt( disc );
370
371 const double ax = center.x() + ( d * dy + sgnDy * dx * sqrDisc ) / dr2;
372 const double ay = center.y() + ( -d * dx + std::fabs( dy ) * sqrDisc ) / dr2;
373 const QgsPointXY p1( ax, ay );
374
375 const double bx = center.x() + ( d * dy - sgnDy * dx * sqrDisc ) / dr2;
376 const double by = center.y() + ( -d * dx - std::fabs( dy ) * sqrDisc ) / dr2;
377 const QgsPointXY p2( bx, by );
378
379 // snap to nearest intersection
380
381 if ( intersection.sqrDist( p1 ) < intersection.sqrDist( p2 ) )
382 {
383 intersection.set( p1.x(), p1.y() );
384 }
385 else
386 {
387 intersection.set( p2.x(), p2.y() );
388 }
389 return true;
390 }
391}
392
393// based on public domain work by 3/26/2005 Tim Voght
394// see http://paulbourke.net/geometry/circlesphere/tvoght.c
395int QgsGeometryUtils::circleCircleIntersections( const QgsPointXY &center1, const double r1, const QgsPointXY &center2, const double r2, QgsPointXY &intersection1, QgsPointXY &intersection2 )
396{
397 // determine the straight-line distance between the centers
398 const double d = center1.distance( center2 );
399
400 // check for solvability
401 if ( d > ( r1 + r2 ) )
402 {
403 // no solution. circles do not intersect.
404 return 0;
405 }
406 else if ( d < std::fabs( r1 - r2 ) )
407 {
408 // no solution. one circle is contained in the other
409 return 0;
410 }
411 else if ( qgsDoubleNear( d, 0 ) && ( qgsDoubleNear( r1, r2 ) ) )
412 {
413 // no solutions, the circles coincide
414 return 0;
415 }
416
417 /* 'point 2' is the point where the line through the circle
418 * intersection points crosses the line between the circle
419 * centers.
420 */
421
422 // Determine the distance from point 0 to point 2.
423 const double a = ( ( r1 * r1 ) - ( r2 * r2 ) + ( d * d ) ) / ( 2.0 * d ) ;
424
425 /* dx and dy are the vertical and horizontal distances between
426 * the circle centers.
427 */
428 const double dx = center2.x() - center1.x();
429 const double dy = center2.y() - center1.y();
430
431 // Determine the coordinates of point 2.
432 const double x2 = center1.x() + ( dx * a / d );
433 const double y2 = center1.y() + ( dy * a / d );
434
435 /* Determine the distance from point 2 to either of the
436 * intersection points.
437 */
438 const double h = std::sqrt( ( r1 * r1 ) - ( a * a ) );
439
440 /* Now determine the offsets of the intersection points from
441 * point 2.
442 */
443 const double rx = dy * ( h / d );
444 const double ry = dx * ( h / d );
445
446 // determine the absolute intersection points
447 intersection1 = QgsPointXY( x2 + rx, y2 - ry );
448 intersection2 = QgsPointXY( x2 - rx, y2 + ry );
449
450 // see if we have 1 or 2 solutions
451 if ( qgsDoubleNear( d, r1 + r2 ) )
452 return 1;
453
454 return 2;
455}
456
457// Using https://stackoverflow.com/a/1351794/1861260
458// and inspired by http://csharphelper.com/blog/2014/11/find-the-tangent-lines-between-a-point-and-a-circle-in-c/
459bool QgsGeometryUtils::tangentPointAndCircle( const QgsPointXY &center, double radius, const QgsPointXY &p, QgsPointXY &pt1, QgsPointXY &pt2 )
460{
461 // distance from point to center of circle
462 const double dx = center.x() - p.x();
463 const double dy = center.y() - p.y();
464 const double distanceSquared = dx * dx + dy * dy;
465 const double radiusSquared = radius * radius;
466 if ( distanceSquared < radiusSquared )
467 {
468 // point is inside circle!
469 return false;
470 }
471
472 // distance from point to tangent point, using pythagoras
473 const double distanceToTangent = std::sqrt( distanceSquared - radiusSquared );
474
475 // tangent points are those where the original circle intersects a circle centered
476 // on p with radius distanceToTangent
477 circleCircleIntersections( center, radius, p, distanceToTangent, pt1, pt2 );
478
479 return true;
480}
481
482// inspired by http://csharphelper.com/blog/2014/12/find-the-tangent-lines-between-two-circles-in-c/
483int QgsGeometryUtils::circleCircleOuterTangents( const QgsPointXY &center1, double radius1, const QgsPointXY &center2, double radius2, QgsPointXY &line1P1, QgsPointXY &line1P2, QgsPointXY &line2P1, QgsPointXY &line2P2 )
484{
485 if ( radius1 > radius2 )
486 return circleCircleOuterTangents( center2, radius2, center1, radius1, line1P1, line1P2, line2P1, line2P2 );
487
488 const double radius2a = radius2 - radius1;
489 if ( !tangentPointAndCircle( center2, radius2a, center1, line1P2, line2P2 ) )
490 {
491 // there are no tangents
492 return 0;
493 }
494
495 // get the vector perpendicular to the
496 // first tangent with length radius1
497 QgsVector v1( -( line1P2.y() - center1.y() ), line1P2.x() - center1.x() );
498 const double v1Length = v1.length();
499 v1 = v1 * ( radius1 / v1Length );
500
501 // offset the tangent vector's points
502 line1P1 = center1 + v1;
503 line1P2 = line1P2 + v1;
504
505 // get the vector perpendicular to the
506 // second tangent with length radius1
507 QgsVector v2( line2P2.y() - center1.y(), -( line2P2.x() - center1.x() ) );
508 const double v2Length = v2.length();
509 v2 = v2 * ( radius1 / v2Length );
510
511 // offset the tangent vector's points
512 line2P1 = center1 + v2;
513 line2P2 = line2P2 + v2;
514
515 return 2;
516}
517
518// inspired by http://csharphelper.com/blog/2014/12/find-the-tangent-lines-between-two-circles-in-c/
519int QgsGeometryUtils::circleCircleInnerTangents( const QgsPointXY &center1, double radius1, const QgsPointXY &center2, double radius2, QgsPointXY &line1P1, QgsPointXY &line1P2, QgsPointXY &line2P1, QgsPointXY &line2P2 )
520{
521 if ( radius1 > radius2 )
522 return circleCircleInnerTangents( center2, radius2, center1, radius1, line1P1, line1P2, line2P1, line2P2 );
523
524 // determine the straight-line distance between the centers
525 const double d = center1.distance( center2 );
526 const double radius1a = radius1 + radius2;
527
528 // check for solvability
529 if ( d <= radius1a || qgsDoubleNear( d, radius1a ) )
530 {
531 // no solution. circles intersect or touch.
532 return 0;
533 }
534
535 if ( !tangentPointAndCircle( center1, radius1a, center2, line1P2, line2P2 ) )
536 {
537 // there are no tangents
538 return 0;
539 }
540
541 // get the vector perpendicular to the
542 // first tangent with length radius2
543 QgsVector v1( ( line1P2.y() - center2.y() ), -( line1P2.x() - center2.x() ) );
544 const double v1Length = v1.length();
545 v1 = v1 * ( radius2 / v1Length );
546
547 // offset the tangent vector's points
548 line1P1 = center2 + v1;
549 line1P2 = line1P2 + v1;
550
551 // get the vector perpendicular to the
552 // second tangent with length radius2
553 QgsVector v2( -( line2P2.y() - center2.y() ), line2P2.x() - center2.x() );
554 const double v2Length = v2.length();
555 v2 = v2 * ( radius2 / v2Length );
556
557 // offset the tangent vector's points in opposite direction
558 line2P1 = center2 + v2;
559 line2P2 = line2P2 + v2;
560
561 return 2;
562}
563
564QVector<QgsGeometryUtils::SelfIntersection> QgsGeometryUtils::selfIntersections( const QgsAbstractGeometry *geom, int part, int ring, double tolerance )
565{
566 QVector<SelfIntersection> intersections;
567
568 const int n = geom->vertexCount( part, ring );
569 const bool isClosed = geom->vertexAt( QgsVertexId( part, ring, 0 ) ) == geom->vertexAt( QgsVertexId( part, ring, n - 1 ) );
570
571 // Check every pair of segments for intersections
572 for ( int i = 0, j = 1; j < n; i = j++ )
573 {
574 const QgsPoint pi = geom->vertexAt( QgsVertexId( part, ring, i ) );
575 const QgsPoint pj = geom->vertexAt( QgsVertexId( part, ring, j ) );
576 if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) < tolerance * tolerance ) continue;
577
578 // Don't test neighboring edges
579 const int start = j + 1;
580 const int end = i == 0 && isClosed ? n - 1 : n;
581 for ( int k = start, l = start + 1; l < end; k = l++ )
582 {
583 const QgsPoint pk = geom->vertexAt( QgsVertexId( part, ring, k ) );
584 const QgsPoint pl = geom->vertexAt( QgsVertexId( part, ring, l ) );
585
586 QgsPoint inter;
587 bool intersection = false;
588 if ( !QgsGeometryUtils::segmentIntersection( pi, pj, pk, pl, inter, intersection, tolerance ) ) continue;
589
591 s.segment1 = i;
592 s.segment2 = k;
593 if ( s.segment1 > s.segment2 )
594 {
595 std::swap( s.segment1, s.segment2 );
596 }
597 s.point = inter;
598 intersections.append( s );
599 }
600 }
601 return intersections;
602}
603
604int QgsGeometryUtils::leftOfLine( const QgsPoint &point, const QgsPoint &p1, const QgsPoint &p2 )
605{
606 return leftOfLine( point.x(), point.y(), p1.x(), p1.y(), p2.x(), p2.y() );
607}
608
609int QgsGeometryUtils::leftOfLine( const double x, const double y, const double x1, const double y1, const double x2, const double y2 )
610{
611 const double f1 = x - x1;
612 const double f2 = y2 - y1;
613 const double f3 = y - y1;
614 const double f4 = x2 - x1;
615 const double test = ( f1 * f2 - f3 * f4 );
616 // return -1, 0, or 1
617 return qgsDoubleNear( test, 0.0 ) ? 0 : ( test < 0 ? -1 : 1 );
618}
619
620QgsPoint QgsGeometryUtils::pointOnLineWithDistance( const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance )
621{
622 double x, y;
623 pointOnLineWithDistance( startPoint.x(), startPoint.y(), directionPoint.x(), directionPoint.y(), distance, x, y );
624 return QgsPoint( x, y );
625}
626
627void QgsGeometryUtils::pointOnLineWithDistance( double x1, double y1, double x2, double y2, double distance, double &x, double &y, double *z1, double *z2, double *z, double *m1, double *m2, double *m )
628{
629 const double dx = x2 - x1;
630 const double dy = y2 - y1;
631 const double length = std::sqrt( dx * dx + dy * dy );
632
633 if ( qgsDoubleNear( length, 0.0 ) )
634 {
635 x = x1;
636 y = y1;
637 if ( z && z1 )
638 *z = *z1;
639 if ( m && m1 )
640 *m = *m1;
641 }
642 else
643 {
644 const double scaleFactor = distance / length;
645 x = x1 + dx * scaleFactor;
646 y = y1 + dy * scaleFactor;
647 if ( z && z1 && z2 )
648 *z = *z1 + ( *z2 - *z1 ) * scaleFactor;
649 if ( m && m1 && m2 )
650 *m = *m1 + ( *m2 - *m1 ) * scaleFactor;
651 }
652}
653
654void QgsGeometryUtils::perpendicularOffsetPointAlongSegment( double x1, double y1, double x2, double y2, double proportion, double offset, double *x, double *y )
655{
656 // calculate point along segment
657 const double mX = x1 + ( x2 - x1 ) * proportion;
658 const double mY = y1 + ( y2 - y1 ) * proportion;
659 const double pX = x1 - x2;
660 const double pY = y1 - y2;
661 double normalX = -pY;
662 double normalY = pX; //#spellok
663 const double normalLength = sqrt( ( normalX * normalX ) + ( normalY * normalY ) ); //#spellok
664 normalX /= normalLength;
665 normalY /= normalLength; //#spellok
666
667 *x = mX + offset * normalX;
668 *y = mY + offset * normalY; //#spellok
669}
670
671QgsPoint QgsGeometryUtils::interpolatePointOnArc( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double distance )
672{
673 double centerX, centerY, radius;
674 circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
675
676 const double theta = distance / radius; // angle subtended
677 const double anglePt1 = std::atan2( pt1.y() - centerY, pt1.x() - centerX );
678 const double anglePt2 = std::atan2( pt2.y() - centerY, pt2.x() - centerX );
679 const double anglePt3 = std::atan2( pt3.y() - centerY, pt3.x() - centerX );
680 const bool isClockwise = circleClockwise( anglePt1, anglePt2, anglePt3 );
681 const double angleDest = anglePt1 + ( isClockwise ? -theta : theta );
682
683 const double x = centerX + radius * ( std::cos( angleDest ) );
684 const double y = centerY + radius * ( std::sin( angleDest ) );
685
686 const double z = pt1.is3D() ?
687 interpolateArcValue( angleDest, anglePt1, anglePt2, anglePt3, pt1.z(), pt2.z(), pt3.z() )
688 : 0;
689 const double m = pt1.isMeasure() ?
690 interpolateArcValue( angleDest, anglePt1, anglePt2, anglePt3, pt1.m(), pt2.m(), pt3.m() )
691 : 0;
692
693 return QgsPoint( pt1.wkbType(), x, y, z, m );
694}
695
696double QgsGeometryUtils::ccwAngle( double dy, double dx )
697{
698 const double angle = std::atan2( dy, dx ) * 180 / M_PI;
699 if ( angle < 0 )
700 {
701 return 360 + angle;
702 }
703 else if ( angle > 360 )
704 {
705 return 360 - angle;
706 }
707 return angle;
708}
709
710void QgsGeometryUtils::circleCenterRadius( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double &radius, double &centerX, double &centerY )
711{
712 double dx21, dy21, dx31, dy31, h21, h31, d;
713
714 //closed circle
715 if ( qgsDoubleNear( pt1.x(), pt3.x() ) && qgsDoubleNear( pt1.y(), pt3.y() ) )
716 {
717 centerX = ( pt1.x() + pt2.x() ) / 2.0;
718 centerY = ( pt1.y() + pt2.y() ) / 2.0;
719 radius = std::sqrt( std::pow( centerX - pt1.x(), 2.0 ) + std::pow( centerY - pt1.y(), 2.0 ) );
720 return;
721 }
722
723 // Using Cartesian circumcenter eguations from page https://en.wikipedia.org/wiki/Circumscribed_circle
724 dx21 = pt2.x() - pt1.x();
725 dy21 = pt2.y() - pt1.y();
726 dx31 = pt3.x() - pt1.x();
727 dy31 = pt3.y() - pt1.y();
728
729 h21 = std::pow( dx21, 2.0 ) + std::pow( dy21, 2.0 );
730 h31 = std::pow( dx31, 2.0 ) + std::pow( dy31, 2.0 );
731
732 // 2*Cross product, d<0 means clockwise and d>0 counterclockwise sweeping angle
733 d = 2 * ( dx21 * dy31 - dx31 * dy21 );
734
735 // Check colinearity, Cross product = 0
736 if ( qgsDoubleNear( std::fabs( d ), 0.0, 0.00000000001 ) )
737 {
738 radius = -1.0;
739 return;
740 }
741
742 // Calculate centroid coordinates and radius
743 centerX = pt1.x() + ( h21 * dy31 - h31 * dy21 ) / d;
744 centerY = pt1.y() - ( h21 * dx31 - h31 * dx21 ) / d;
745 radius = std::sqrt( std::pow( centerX - pt1.x(), 2.0 ) + std::pow( centerY - pt1.y(), 2.0 ) );
746}
747
748bool QgsGeometryUtils::circleClockwise( double angle1, double angle2, double angle3 )
749{
750 if ( angle3 >= angle1 )
751 {
752 return !( angle2 > angle1 && angle2 < angle3 );
753 }
754 else
755 {
756 return !( angle2 > angle1 || angle2 < angle3 );
757 }
758}
759
760bool QgsGeometryUtils::circleAngleBetween( double angle, double angle1, double angle2, bool clockwise )
761{
762 if ( clockwise )
763 {
764 if ( angle2 < angle1 )
765 {
766 return ( angle <= angle1 && angle >= angle2 );
767 }
768 else
769 {
770 return ( angle <= angle1 || angle >= angle2 );
771 }
772 }
773 else
774 {
775 if ( angle2 > angle1 )
776 {
777 return ( angle >= angle1 && angle <= angle2 );
778 }
779 else
780 {
781 return ( angle >= angle1 || angle <= angle2 );
782 }
783 }
784}
785
786bool QgsGeometryUtils::angleOnCircle( double angle, double angle1, double angle2, double angle3 )
787{
788 const bool clockwise = circleClockwise( angle1, angle2, angle3 );
789 return circleAngleBetween( angle, angle1, angle3, clockwise );
790}
791
792double QgsGeometryUtils::circleLength( double x1, double y1, double x2, double y2, double x3, double y3 )
793{
794 double centerX, centerY, radius;
795 circleCenterRadius( QgsPoint( x1, y1 ), QgsPoint( x2, y2 ), QgsPoint( x3, y3 ), radius, centerX, centerY );
796 double length = M_PI / 180.0 * radius * sweepAngle( centerX, centerY, x1, y1, x2, y2, x3, y3 );
797 if ( length < 0 )
798 {
799 length = -length;
800 }
801 return length;
802}
803
804double QgsGeometryUtils::sweepAngle( double centerX, double centerY, double x1, double y1, double x2, double y2, double x3, double y3 )
805{
806 const double p1Angle = QgsGeometryUtils::ccwAngle( y1 - centerY, x1 - centerX );
807 const double p2Angle = QgsGeometryUtils::ccwAngle( y2 - centerY, x2 - centerX );
808 const double p3Angle = QgsGeometryUtils::ccwAngle( y3 - centerY, x3 - centerX );
809
810 if ( p3Angle >= p1Angle )
811 {
812 if ( p2Angle > p1Angle && p2Angle < p3Angle )
813 {
814 return ( p3Angle - p1Angle );
815 }
816 else
817 {
818 return ( - ( p1Angle + ( 360 - p3Angle ) ) );
819 }
820 }
821 else
822 {
823 if ( p2Angle < p1Angle && p2Angle > p3Angle )
824 {
825 return ( -( p1Angle - p3Angle ) );
826 }
827 else
828 {
829 return ( p3Angle + ( 360 - p1Angle ) );
830 }
831 }
832}
833
834bool QgsGeometryUtils::segmentMidPoint( const QgsPoint &p1, const QgsPoint &p2, QgsPoint &result, double radius, const QgsPoint &mousePos )
835{
836 const QgsPoint midPoint( ( p1.x() + p2.x() ) / 2.0, ( p1.y() + p2.y() ) / 2.0 );
837 const double midDist = std::sqrt( sqrDistance2D( p1, midPoint ) );
838 if ( radius < midDist )
839 {
840 return false;
841 }
842 const double centerMidDist = std::sqrt( radius * radius - midDist * midDist );
843 const double dist = radius - centerMidDist;
844
845 const double midDx = midPoint.x() - p1.x();
846 const double midDy = midPoint.y() - p1.y();
847
848 //get the four possible midpoints
849 QVector<QgsPoint> possibleMidPoints;
850 possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPoint( midPoint.x() - midDy, midPoint.y() + midDx ), dist ) );
851 possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPoint( midPoint.x() - midDy, midPoint.y() + midDx ), 2 * radius - dist ) );
852 possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPoint( midPoint.x() + midDy, midPoint.y() - midDx ), dist ) );
853 possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPoint( midPoint.x() + midDy, midPoint.y() - midDx ), 2 * radius - dist ) );
854
855 //take the closest one
856 double minDist = std::numeric_limits<double>::max();
857 int minDistIndex = -1;
858 for ( int i = 0; i < possibleMidPoints.size(); ++i )
859 {
860 const double currentDist = sqrDistance2D( mousePos, possibleMidPoints.at( i ) );
861 if ( currentDist < minDist )
862 {
863 minDistIndex = i;
864 minDist = currentDist;
865 }
866 }
867
868 if ( minDistIndex == -1 )
869 {
870 return false;
871 }
872
873 result = possibleMidPoints.at( minDistIndex );
874
875 // add z and m support if necessary
877
878 return true;
879}
880
881QgsPoint QgsGeometryUtils::segmentMidPointFromCenter( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, const bool useShortestArc )
882{
883 double midPointAngle = averageAngle( lineAngle( center.x(), center.y(), p1.x(), p1.y() ),
884 lineAngle( center.x(), center.y(), p2.x(), p2.y() ) );
885 if ( !useShortestArc )
886 midPointAngle += M_PI;
887 return center.project( center.distance( p1 ), midPointAngle * 180 / M_PI );
888}
889
890double QgsGeometryUtils::circleTangentDirection( const QgsPoint &tangentPoint, const QgsPoint &cp1,
891 const QgsPoint &cp2, const QgsPoint &cp3 )
892{
893 //calculate circle midpoint
894 double mX, mY, radius;
895 circleCenterRadius( cp1, cp2, cp3, radius, mX, mY );
896
897 const double p1Angle = QgsGeometryUtils::ccwAngle( cp1.y() - mY, cp1.x() - mX );
898 const double p2Angle = QgsGeometryUtils::ccwAngle( cp2.y() - mY, cp2.x() - mX );
899 const double p3Angle = QgsGeometryUtils::ccwAngle( cp3.y() - mY, cp3.x() - mX );
900 double angle = 0;
901 if ( circleClockwise( p1Angle, p2Angle, p3Angle ) )
902 {
903 angle = lineAngle( tangentPoint.x(), tangentPoint.y(), mX, mY ) - M_PI_2;
904 }
905 else
906 {
907 angle = lineAngle( mX, mY, tangentPoint.x(), tangentPoint.y() ) - M_PI_2;
908 }
909 if ( angle < 0 )
910 angle += 2 * M_PI;
911 return angle;
912}
913
914// Ported from PostGIS' pt_continues_arc
915bool QgsGeometryUtils::pointContinuesArc( const QgsPoint &a1, const QgsPoint &a2, const QgsPoint &a3, const QgsPoint &b, double distanceTolerance, double pointSpacingAngleTolerance )
916{
917 double centerX = 0;
918 double centerY = 0;
919 double radius = 0;
920 circleCenterRadius( a1, a2, a3, radius, centerX, centerY );
921
922 // Co-linear a1/a2/a3
923 if ( radius < 0.0 )
924 return false;
925
926 // distance of candidate point to center of arc a1-a2-a3
927 const double bDistance = std::sqrt( ( b.x() - centerX ) * ( b.x() - centerX ) +
928 ( b.y() - centerY ) * ( b.y() - centerY ) );
929
930 double diff = std::fabs( radius - bDistance );
931
932 auto arcAngle = []( const QgsPoint & a, const QgsPoint & b, const QgsPoint & c )->double
933 {
934 const double abX = b.x() - a.x();
935 const double abY = b.y() - a.y();
936
937 const double cbX = b.x() - c.x();
938 const double cbY = b.y() - c.y();
939
940 const double dot = ( abX * cbX + abY * cbY ); /* dot product */
941 const double cross = ( abX * cbY - abY * cbX ); /* cross product */
942
943 const double alpha = std::atan2( cross, dot );
944
945 return alpha;
946 };
947
948 // Is the point b on the circle?
949 if ( diff < distanceTolerance )
950 {
951 const double angle1 = arcAngle( a1, a2, a3 );
952 const double angle2 = arcAngle( a2, a3, b );
953
954 // Is the sweep angle similar to the previous one?
955 // We only consider a segment replaceable by an arc if the points within
956 // it are regularly spaced
957 diff = std::fabs( angle1 - angle2 );
958 if ( diff > pointSpacingAngleTolerance )
959 {
960 return false;
961 }
962
963 const int a2Side = leftOfLine( a2.x(), a2.y(), a1.x(), a1.y(), a3.x(), a3.y() );
964 const int bSide = leftOfLine( b.x(), b.y(), a1.x(), a1.y(), a3.x(), a3.y() );
965
966 // Is the point b on the same side of a1/a3 as the mid-point a2 is?
967 // If not, it's in the unbounded part of the circle, so it continues the arc, return true.
968 if ( bSide != a2Side )
969 return true;
970 }
971 return false;
972}
973
974void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, QgsPointSequence &points, double tolerance, QgsAbstractGeometry::SegmentationToleranceType toleranceType, bool hasZ, bool hasM )
975{
976 bool reversed = false;
977 const int segSide = segmentSide( p1, p3, p2 );
978
979 QgsPoint circlePoint1;
980 const QgsPoint &circlePoint2 = p2;
981 QgsPoint circlePoint3;
982
983 if ( segSide == -1 )
984 {
985 // Reverse !
986 circlePoint1 = p3;
987 circlePoint3 = p1;
988 reversed = true;
989 }
990 else
991 {
992 circlePoint1 = p1;
993 circlePoint3 = p3;
994 }
995
996 //adapted code from PostGIS
997 double radius = 0;
998 double centerX = 0;
999 double centerY = 0;
1000 circleCenterRadius( circlePoint1, circlePoint2, circlePoint3, radius, centerX, centerY );
1001
1002 if ( circlePoint1 != circlePoint3 && ( radius < 0 || qgsDoubleNear( segSide, 0.0 ) ) ) //points are colinear
1003 {
1004 points.append( p1 );
1005 points.append( p2 );
1006 points.append( p3 );
1007 return;
1008 }
1009
1010 double increment = tolerance; //one segment per degree
1011 if ( toleranceType == QgsAbstractGeometry::MaximumDifference )
1012 {
1013 // Ensure tolerance is not higher than twice the radius
1014 tolerance = std::min( tolerance, radius * 2 );
1015 const double halfAngle = std::acos( -tolerance / radius + 1 );
1016 increment = 2 * halfAngle;
1017 }
1018
1019 //angles of pt1, pt2, pt3
1020 const double a1 = std::atan2( circlePoint1.y() - centerY, circlePoint1.x() - centerX );
1021 double a2 = std::atan2( circlePoint2.y() - centerY, circlePoint2.x() - centerX );
1022 double a3 = std::atan2( circlePoint3.y() - centerY, circlePoint3.x() - centerX );
1023
1024 // Make segmentation symmetric
1025 const bool symmetric = true;
1026 if ( symmetric )
1027 {
1028 double angle = a3 - a1;
1029 // angle == 0 when full circle
1030 if ( angle <= 0 ) angle += M_PI * 2;
1031
1032 /* Number of segments in output */
1033 const int segs = ceil( angle / increment );
1034 /* Tweak increment to be regular for all the arc */
1035 increment = angle / segs;
1036 }
1037
1038 /* Adjust a3 up so we can increment from a1 to a3 cleanly */
1039 // a3 == a1 when full circle
1040 if ( a3 <= a1 )
1041 a3 += 2.0 * M_PI;
1042 if ( a2 < a1 )
1043 a2 += 2.0 * M_PI;
1044
1045 double x, y;
1046 double z = 0;
1047 double m = 0;
1048
1049 QVector<QgsPoint> stringPoints;
1050 stringPoints.insert( 0, circlePoint1 );
1051 if ( circlePoint2 != circlePoint3 && circlePoint1 != circlePoint2 ) //draw straight line segment if two points have the same position
1052 {
1053 Qgis::WkbType pointWkbType = Qgis::WkbType::Point;
1054 if ( hasZ )
1055 pointWkbType = QgsWkbTypes::addZ( pointWkbType );
1056 if ( hasM )
1057 pointWkbType = QgsWkbTypes::addM( pointWkbType );
1058
1059 // As we're adding the last point in any case, we'll avoid
1060 // including a point which is at less than 1% increment distance
1061 // from it (may happen to find them due to numbers approximation).
1062 // NOTE that this effectively allows in output some segments which
1063 // are more distant than requested. This is at most 1% off
1064 // from requested MaxAngle and less for MaxError.
1065 const double tolError = increment / 100;
1066 const double stopAngle = a3 - tolError;
1067 for ( double angle = a1 + increment; angle < stopAngle; angle += increment )
1068 {
1069 x = centerX + radius * std::cos( angle );
1070 y = centerY + radius * std::sin( angle );
1071
1072 if ( hasZ )
1073 {
1074 z = interpolateArcValue( angle, a1, a2, a3, circlePoint1.z(), circlePoint2.z(), circlePoint3.z() );
1075 }
1076 if ( hasM )
1077 {
1078 m = interpolateArcValue( angle, a1, a2, a3, circlePoint1.m(), circlePoint2.m(), circlePoint3.m() );
1079 }
1080
1081 stringPoints.insert( stringPoints.size(), QgsPoint( pointWkbType, x, y, z, m ) );
1082 }
1083 }
1084 stringPoints.insert( stringPoints.size(), circlePoint3 );
1085
1086 // TODO: check if or implement QgsPointSequence directly taking an iterator to append
1087 if ( reversed )
1088 {
1089 std::reverse( stringPoints.begin(), stringPoints.end() );
1090 }
1091 if ( ! points.empty() && stringPoints.front() == points.back() ) stringPoints.pop_front();
1092 points.append( stringPoints );
1093}
1094
1095int QgsGeometryUtils::segmentSide( const QgsPoint &pt1, const QgsPoint &pt3, const QgsPoint &pt2 )
1096{
1097 const double side = ( ( pt2.x() - pt1.x() ) * ( pt3.y() - pt1.y() ) - ( pt3.x() - pt1.x() ) * ( pt2.y() - pt1.y() ) );
1098 if ( side == 0.0 )
1099 {
1100 return 0;
1101 }
1102 else
1103 {
1104 if ( side < 0 )
1105 {
1106 return -1;
1107 }
1108 if ( side > 0 )
1109 {
1110 return 1;
1111 }
1112 return 0;
1113 }
1114}
1115
1116double QgsGeometryUtils::interpolateArcValue( double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3 )
1117{
1118 /* Counter-clockwise sweep */
1119 if ( a1 < a2 )
1120 {
1121 if ( angle <= a2 )
1122 return zm1 + ( zm2 - zm1 ) * ( angle - a1 ) / ( a2 - a1 );
1123 else
1124 return zm2 + ( zm3 - zm2 ) * ( angle - a2 ) / ( a3 - a2 );
1125 }
1126 /* Clockwise sweep */
1127 else
1128 {
1129 if ( angle >= a2 )
1130 return zm1 + ( zm2 - zm1 ) * ( a1 - angle ) / ( a1 - a2 );
1131 else
1132 return zm2 + ( zm3 - zm2 ) * ( a2 - angle ) / ( a2 - a3 );
1133 }
1134}
1135
1136QgsPointSequence QgsGeometryUtils::pointsFromWKT( const QString &wktCoordinateList, bool is3D, bool isMeasure )
1137{
1138 const int dim = 2 + is3D + isMeasure;
1139 QgsPointSequence points;
1140
1141#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1142 const QStringList coordList = wktCoordinateList.split( ',', QString::SkipEmptyParts );
1143#else
1144 const QStringList coordList = wktCoordinateList.split( ',', Qt::SkipEmptyParts );
1145#endif
1146
1147 //first scan through for extra unexpected dimensions
1148 bool foundZ = false;
1149 bool foundM = false;
1150 const thread_local QRegularExpression rx( QStringLiteral( "\\s" ) );
1151 const thread_local QRegularExpression rxIsNumber( QStringLiteral( "^[+-]?(\\d\\.?\\d*[Ee][+\\-]?\\d+|(\\d+\\.\\d*|\\d*\\.\\d+)|\\d+)$" ) );
1152 for ( const QString &pointCoordinates : coordList )
1153 {
1154#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1155 QStringList coordinates = pointCoordinates.split( rx, QString::SkipEmptyParts );
1156#else
1157 const QStringList coordinates = pointCoordinates.split( rx, Qt::SkipEmptyParts );
1158#endif
1159
1160 // exit with an empty set if one list contains invalid value.
1161 if ( coordinates.filter( rxIsNumber ).size() != coordinates.size() )
1162 return points;
1163
1164 if ( coordinates.size() == 3 && !foundZ && !foundM && !is3D && !isMeasure )
1165 {
1166 // 3 dimensional coordinates, but not specifically marked as such. We allow this
1167 // anyway and upgrade geometry to have Z dimension
1168 foundZ = true;
1169 }
1170 else if ( coordinates.size() >= 4 && ( !( is3D || foundZ ) || !( isMeasure || foundM ) ) )
1171 {
1172 // 4 (or more) dimensional coordinates, but not specifically marked as such. We allow this
1173 // anyway and upgrade geometry to have Z&M dimensions
1174 foundZ = true;
1175 foundM = true;
1176 }
1177 }
1178
1179 for ( const QString &pointCoordinates : coordList )
1180 {
1181#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1182 QStringList coordinates = pointCoordinates.split( rx, QString::SkipEmptyParts );
1183#else
1184 QStringList coordinates = pointCoordinates.split( rx, Qt::SkipEmptyParts );
1185#endif
1186 if ( coordinates.size() < dim )
1187 continue;
1188
1189 int idx = 0;
1190 const double x = coordinates[idx++].toDouble();
1191 const double y = coordinates[idx++].toDouble();
1192
1193 double z = 0;
1194 if ( ( is3D || foundZ ) && coordinates.length() > idx )
1195 z = coordinates[idx++].toDouble();
1196
1197 double m = 0;
1198 if ( ( isMeasure || foundM ) && coordinates.length() > idx )
1199 m = coordinates[idx++].toDouble();
1200
1202 if ( is3D || foundZ )
1203 {
1204 if ( isMeasure || foundM )
1206 else
1208 }
1209 else
1210 {
1211 if ( isMeasure || foundM )
1213 else
1215 }
1216
1217 points.append( QgsPoint( t, x, y, z, m ) );
1218 }
1219
1220 return points;
1221}
1222
1223void QgsGeometryUtils::pointsToWKB( QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure, QgsAbstractGeometry::WkbFlags flags )
1224{
1225 wkb << static_cast<quint32>( points.size() );
1226 for ( const QgsPoint &point : points )
1227 {
1228 wkb << point.x() << point.y();
1229 if ( is3D )
1230 {
1231 double z = point.z();
1233 && std::isnan( z ) )
1234 z = -std::numeric_limits<double>::max();
1235
1236 wkb << z;
1237 }
1238 if ( isMeasure )
1239 {
1240 double m = point.m();
1242 && std::isnan( m ) )
1243 m = -std::numeric_limits<double>::max();
1244
1245 wkb << m;
1246 }
1247 }
1248}
1249
1250QString QgsGeometryUtils::pointsToWKT( const QgsPointSequence &points, int precision, bool is3D, bool isMeasure )
1251{
1252 QString wkt = QStringLiteral( "(" );
1253 for ( const QgsPoint &p : points )
1254 {
1255 wkt += qgsDoubleToString( p.x(), precision );
1256 wkt += ' ' + qgsDoubleToString( p.y(), precision );
1257 if ( is3D )
1258 wkt += ' ' + qgsDoubleToString( p.z(), precision );
1259 if ( isMeasure )
1260 wkt += ' ' + qgsDoubleToString( p.m(), precision );
1261 wkt += QLatin1String( ", " );
1262 }
1263 if ( wkt.endsWith( QLatin1String( ", " ) ) )
1264 wkt.chop( 2 ); // Remove last ", "
1265 wkt += ')';
1266 return wkt;
1267}
1268
1269QDomElement QgsGeometryUtils::pointsToGML2( const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, QgsAbstractGeometry::AxisOrder axisOrder )
1270{
1271 QDomElement elemCoordinates = doc.createElementNS( ns, QStringLiteral( "coordinates" ) );
1272
1273 // coordinate separator
1274 const QString cs = QStringLiteral( "," );
1275 // tuple separator
1276 const QString ts = QStringLiteral( " " );
1277
1278 elemCoordinates.setAttribute( QStringLiteral( "cs" ), cs );
1279 elemCoordinates.setAttribute( QStringLiteral( "ts" ), ts );
1280
1281 QString strCoordinates;
1282
1283 for ( const QgsPoint &p : points )
1284 if ( axisOrder == QgsAbstractGeometry::AxisOrder::XY )
1285 strCoordinates += qgsDoubleToString( p.x(), precision ) + cs + qgsDoubleToString( p.y(), precision ) + ts;
1286 else
1287 strCoordinates += qgsDoubleToString( p.y(), precision ) + cs + qgsDoubleToString( p.x(), precision ) + ts;
1288
1289 if ( strCoordinates.endsWith( ts ) )
1290 strCoordinates.chop( 1 ); // Remove trailing space
1291
1292 elemCoordinates.appendChild( doc.createTextNode( strCoordinates ) );
1293 return elemCoordinates;
1294}
1295
1296QDomElement QgsGeometryUtils::pointsToGML3( const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D, QgsAbstractGeometry::AxisOrder axisOrder )
1297{
1298 QDomElement elemPosList = doc.createElementNS( ns, QStringLiteral( "posList" ) );
1299 elemPosList.setAttribute( QStringLiteral( "srsDimension" ), is3D ? 3 : 2 );
1300
1301 QString strCoordinates;
1302 for ( const QgsPoint &p : points )
1303 {
1304 if ( axisOrder == QgsAbstractGeometry::AxisOrder::XY )
1305 strCoordinates += qgsDoubleToString( p.x(), precision ) + ' ' + qgsDoubleToString( p.y(), precision ) + ' ';
1306 else
1307 strCoordinates += qgsDoubleToString( p.y(), precision ) + ' ' + qgsDoubleToString( p.x(), precision ) + ' ';
1308 if ( is3D )
1309 strCoordinates += qgsDoubleToString( p.z(), precision ) + ' ';
1310 }
1311 if ( strCoordinates.endsWith( ' ' ) )
1312 strCoordinates.chop( 1 ); // Remove trailing space
1313
1314 elemPosList.appendChild( doc.createTextNode( strCoordinates ) );
1315 return elemPosList;
1316}
1317
1319{
1320 QString json = QStringLiteral( "[ " );
1321 for ( const QgsPoint &p : points )
1322 {
1323 json += '[' + qgsDoubleToString( p.x(), precision ) + QLatin1String( ", " ) + qgsDoubleToString( p.y(), precision ) + QLatin1String( "], " );
1324 }
1325 if ( json.endsWith( QLatin1String( ", " ) ) )
1326 {
1327 json.chop( 2 ); // Remove last ", "
1328 }
1329 json += ']';
1330 return json;
1331}
1332
1333
1335{
1336 json coordinates( json::array() );
1337 for ( const QgsPoint &p : points )
1338 {
1339 if ( p.is3D() )
1340 {
1341 coordinates.push_back( { qgsRound( p.x(), precision ), qgsRound( p.y(), precision ), qgsRound( p.z(), precision ) } );
1342 }
1343 else
1344 {
1345 coordinates.push_back( { qgsRound( p.x(), precision ), qgsRound( p.y(), precision ) } );
1346 }
1347 }
1348 return coordinates;
1349}
1350
1352{
1353 double clippedAngle = angle;
1354 if ( clippedAngle >= M_PI * 2 || clippedAngle <= -2 * M_PI )
1355 {
1356 clippedAngle = std::fmod( clippedAngle, 2 * M_PI );
1357 }
1358 if ( clippedAngle < 0.0 )
1359 {
1360 clippedAngle += 2 * M_PI;
1361 }
1362 return clippedAngle;
1363}
1364
1365QPair<Qgis::WkbType, QString> QgsGeometryUtils::wktReadBlock( const QString &wkt )
1366{
1367 QString wktParsed = wkt;
1368 QString contents;
1369 if ( wkt.contains( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) )
1370 {
1371 const thread_local QRegularExpression sWktRegEx( QStringLiteral( "^\\s*(\\w+)\\s+(\\w+)\\s*$" ), QRegularExpression::DotMatchesEverythingOption );
1372 const QRegularExpressionMatch match = sWktRegEx.match( wkt );
1373 if ( match.hasMatch() )
1374 {
1375 wktParsed = match.captured( 1 );
1376 contents = match.captured( 2 ).toUpper();
1377 }
1378 }
1379 else
1380 {
1381 const int openedParenthesisCount = wktParsed.count( '(' );
1382 const int closedParenthesisCount = wktParsed.count( ')' );
1383 // closes missing parentheses
1384 for ( int i = 0 ; i < openedParenthesisCount - closedParenthesisCount; ++i )
1385 wktParsed.push_back( ')' );
1386 // removes extra parentheses
1387 wktParsed.truncate( wktParsed.size() - ( closedParenthesisCount - openedParenthesisCount ) );
1388
1389 const thread_local QRegularExpression cooRegEx( QStringLiteral( "^[^\\(]*\\((.*)\\)[^\\)]*$" ), QRegularExpression::DotMatchesEverythingOption );
1390 const QRegularExpressionMatch match = cooRegEx.match( wktParsed );
1391 contents = match.hasMatch() ? match.captured( 1 ) : QString();
1392 }
1393 const Qgis::WkbType wkbType = QgsWkbTypes::parseType( wktParsed );
1394 return qMakePair( wkbType, contents );
1395}
1396
1397QStringList QgsGeometryUtils::wktGetChildBlocks( const QString &wkt, const QString &defaultType )
1398{
1399 int level = 0;
1400 QString block;
1401 block.reserve( wkt.size() );
1402 QStringList blocks;
1403
1404 const QChar *wktData = wkt.data();
1405 const int wktLength = wkt.length();
1406 for ( int i = 0, n = wktLength; i < n; ++i, ++wktData )
1407 {
1408 if ( ( wktData->isSpace() || *wktData == '\n' || *wktData == '\t' ) && level == 0 )
1409 continue;
1410
1411 if ( *wktData == ',' && level == 0 )
1412 {
1413 if ( !block.isEmpty() )
1414 {
1415 if ( block.startsWith( '(' ) && !defaultType.isEmpty() )
1416 block.prepend( defaultType + ' ' );
1417 blocks.append( block );
1418 }
1419 block.resize( 0 );
1420 continue;
1421 }
1422 if ( *wktData == '(' )
1423 ++level;
1424 else if ( *wktData == ')' )
1425 --level;
1426 block += *wktData;
1427 }
1428 if ( !block.isEmpty() )
1429 {
1430 if ( block.startsWith( '(' ) && !defaultType.isEmpty() )
1431 block.prepend( defaultType + ' ' );
1432 blocks.append( block );
1433 }
1434 return blocks;
1435}
1436
1437int QgsGeometryUtils::closestSideOfRectangle( double right, double bottom, double left, double top, double x, double y )
1438{
1439 // point outside rectangle
1440 if ( x <= left && y <= bottom )
1441 {
1442 const double dx = left - x;
1443 const double dy = bottom - y;
1444 if ( qgsDoubleNear( dx, dy ) )
1445 return 6;
1446 else if ( dx < dy )
1447 return 5;
1448 else
1449 return 7;
1450 }
1451 else if ( x >= right && y >= top )
1452 {
1453 const double dx = x - right;
1454 const double dy = y - top;
1455 if ( qgsDoubleNear( dx, dy ) )
1456 return 2;
1457 else if ( dx < dy )
1458 return 1;
1459 else
1460 return 3;
1461 }
1462 else if ( x >= right && y <= bottom )
1463 {
1464 const double dx = x - right;
1465 const double dy = bottom - y;
1466 if ( qgsDoubleNear( dx, dy ) )
1467 return 4;
1468 else if ( dx < dy )
1469 return 5;
1470 else
1471 return 3;
1472 }
1473 else if ( x <= left && y >= top )
1474 {
1475 const double dx = left - x;
1476 const double dy = y - top;
1477 if ( qgsDoubleNear( dx, dy ) )
1478 return 8;
1479 else if ( dx < dy )
1480 return 1;
1481 else
1482 return 7;
1483 }
1484 else if ( x <= left )
1485 return 7;
1486 else if ( x >= right )
1487 return 3;
1488 else if ( y <= bottom )
1489 return 5;
1490 else if ( y >= top )
1491 return 1;
1492
1493 // point is inside rectangle
1494 const double smallestX = std::min( right - x, x - left );
1495 const double smallestY = std::min( top - y, y - bottom );
1496 if ( smallestX < smallestY )
1497 {
1498 // closer to left/right side
1499 if ( right - x < x - left )
1500 return 3; // closest to right side
1501 else
1502 return 7;
1503 }
1504 else
1505 {
1506 // closer to top/bottom side
1507 if ( top - y < y - bottom )
1508 return 1; // closest to top side
1509 else
1510 return 5;
1511 }
1512}
1513
1515{
1517
1518
1519 const double x = ( pt1.x() + pt2.x() ) / 2.0;
1520 const double y = ( pt1.y() + pt2.y() ) / 2.0;
1521 double z = std::numeric_limits<double>::quiet_NaN();
1522 double m = std::numeric_limits<double>::quiet_NaN();
1523
1524 if ( pt1.is3D() || pt2.is3D() )
1525 {
1526 pType = QgsWkbTypes::addZ( pType );
1527 z = ( pt1.z() + pt2.z() ) / 2.0;
1528 }
1529
1530 if ( pt1.isMeasure() || pt2.isMeasure() )
1531 {
1532 pType = QgsWkbTypes::addM( pType );
1533 m = ( pt1.m() + pt2.m() ) / 2.0;
1534 }
1535
1536 return QgsPoint( pType, x, y, z, m );
1537}
1538
1539QgsPoint QgsGeometryUtils::interpolatePointOnLine( const QgsPoint &p1, const QgsPoint &p2, const double fraction )
1540{
1541 const double _fraction = 1 - fraction;
1542 return QgsPoint( p1.wkbType(),
1543 p1.x() * _fraction + p2.x() * fraction,
1544 p1.y() * _fraction + p2.y() * fraction,
1545 p1.is3D() ? p1.z() * _fraction + p2.z() * fraction : std::numeric_limits<double>::quiet_NaN(),
1546 p1.isMeasure() ? p1.m() * _fraction + p2.m() * fraction : std::numeric_limits<double>::quiet_NaN() );
1547}
1548
1549QgsPointXY QgsGeometryUtils::interpolatePointOnLine( const double x1, const double y1, const double x2, const double y2, const double fraction )
1550{
1551 const double deltaX = ( x2 - x1 ) * fraction;
1552 const double deltaY = ( y2 - y1 ) * fraction;
1553 return QgsPointXY( x1 + deltaX, y1 + deltaY );
1554}
1555
1556QgsPointXY QgsGeometryUtils::interpolatePointOnLineByValue( const double x1, const double y1, const double v1, const double x2, const double y2, const double v2, const double value )
1557{
1558 if ( qgsDoubleNear( v1, v2 ) )
1559 return QgsPointXY( x1, y1 );
1560
1561 const double fraction = ( value - v1 ) / ( v2 - v1 );
1562 return interpolatePointOnLine( x1, y1, x2, y2, fraction );
1563}
1564
1565double QgsGeometryUtils::gradient( const QgsPoint &pt1, const QgsPoint &pt2 )
1566{
1567 const double delta_x = pt2.x() - pt1.x();
1568 const double delta_y = pt2.y() - pt1.y();
1569 if ( qgsDoubleNear( delta_x, 0.0 ) )
1570 {
1571 return INFINITY;
1572 }
1573
1574 return delta_y / delta_x;
1575}
1576
1577void QgsGeometryUtils::coefficients( const QgsPoint &pt1, const QgsPoint &pt2, double &a, double &b, double &c )
1578{
1579 if ( qgsDoubleNear( pt1.x(), pt2.x() ) )
1580 {
1581 a = 1;
1582 b = 0;
1583 c = -pt1.x();
1584 }
1585 else if ( qgsDoubleNear( pt1.y(), pt2.y() ) )
1586 {
1587 a = 0;
1588 b = 1;
1589 c = -pt1.y();
1590 }
1591 else
1592 {
1593 a = pt1.y() - pt2.y();
1594 b = pt2.x() - pt1.x();
1595 c = pt1.x() * pt2.y() - pt1.y() * pt2.x();
1596 }
1597
1598}
1599
1601{
1602 QgsLineString line;
1603 QgsPoint p2;
1604
1605 if ( ( p == s1 ) || ( p == s2 ) )
1606 {
1607 return line;
1608 }
1609
1610 double a, b, c;
1611 coefficients( s1, s2, a, b, c );
1612
1613 if ( qgsDoubleNear( a, 0 ) )
1614 {
1615 p2 = QgsPoint( p.x(), s1.y() );
1616 }
1617 else if ( qgsDoubleNear( b, 0 ) )
1618 {
1619 p2 = QgsPoint( s1.x(), p.y() );
1620 }
1621 else
1622 {
1623 const double y = ( -c - a * p.x() ) / b;
1624 const double m = gradient( s1, s2 );
1625 const double d2 = 1 + m * m;
1626 const double H = p.y() - y;
1627 const double dx = m * H / d2;
1628 const double dy = m * dx;
1629 p2 = QgsPoint( p.x() + dx, y + dy );
1630 }
1631
1632 line.addVertex( p );
1633 line.addVertex( p2 );
1634
1635 return line;
1636}
1637
1638void QgsGeometryUtils::perpendicularCenterSegment( double pointx, double pointy, double segmentPoint1x, double segmentPoint1y, double segmentPoint2x, double segmentPoint2y, double &perpendicularSegmentPoint1x, double &perpendicularSegmentPoint1y, double &perpendicularSegmentPoint2x, double &perpendicularSegmentPoint2y, double desiredSegmentLength )
1639{
1640 QgsVector segmentVector = QgsVector( segmentPoint2x - segmentPoint1x, segmentPoint2y - segmentPoint1y );
1641 QgsVector perpendicularVector = segmentVector.perpVector();
1642 if ( desiredSegmentLength != 0 )
1643 {
1644 perpendicularVector = perpendicularVector.normalized() * ( desiredSegmentLength ) / 2;
1645 }
1646
1647 perpendicularSegmentPoint1x = pointx - perpendicularVector.x();
1648 perpendicularSegmentPoint1y = pointy - perpendicularVector.y();
1649 perpendicularSegmentPoint2x = pointx + perpendicularVector.x();
1650 perpendicularSegmentPoint2y = pointy + perpendicularVector.y();
1651}
1652
1653double QgsGeometryUtils::lineAngle( double x1, double y1, double x2, double y2 )
1654{
1655 const double at = std::atan2( y2 - y1, x2 - x1 );
1656 const double a = -at + M_PI_2;
1657 return normalizedAngle( a );
1658}
1659
1660double QgsGeometryUtils::angleBetweenThreePoints( double x1, double y1, double x2, double y2, double x3, double y3 )
1661{
1662 const double angle1 = std::atan2( y1 - y2, x1 - x2 );
1663 const double angle2 = std::atan2( y3 - y2, x3 - x2 );
1664 return normalizedAngle( angle1 - angle2 );
1665}
1666
1667double QgsGeometryUtils::linePerpendicularAngle( double x1, double y1, double x2, double y2 )
1668{
1669 double a = lineAngle( x1, y1, x2, y2 );
1670 a += M_PI_2;
1671 return normalizedAngle( a );
1672}
1673
1674double QgsGeometryUtils::averageAngle( double x1, double y1, double x2, double y2, double x3, double y3 )
1675{
1676 // calc average angle between the previous and next point
1677 const double a1 = lineAngle( x1, y1, x2, y2 );
1678 const double a2 = lineAngle( x2, y2, x3, y3 );
1679 return averageAngle( a1, a2 );
1680}
1681
1682double QgsGeometryUtils::averageAngle( double a1, double a2 )
1683{
1684 a1 = normalizedAngle( a1 );
1685 a2 = normalizedAngle( a2 );
1686 double clockwiseDiff = 0.0;
1687 if ( a2 >= a1 )
1688 {
1689 clockwiseDiff = a2 - a1;
1690 }
1691 else
1692 {
1693 clockwiseDiff = a2 + ( 2 * M_PI - a1 );
1694 }
1695 const double counterClockwiseDiff = 2 * M_PI - clockwiseDiff;
1696
1697 double resultAngle = 0;
1698 if ( clockwiseDiff <= counterClockwiseDiff )
1699 {
1700 resultAngle = a1 + clockwiseDiff / 2.0;
1701 }
1702 else
1703 {
1704 resultAngle = a1 - counterClockwiseDiff / 2.0;
1705 }
1706 return normalizedAngle( resultAngle );
1707}
1708
1710 const QgsVector3D &P2, const QgsVector3D &P22 )
1711{
1712 const QgsVector3D u1 = P12 - P1;
1713 const QgsVector3D u2 = P22 - P2;
1715 if ( u3.length() == 0 ) return 1;
1716 u3.normalize();
1717 const QgsVector3D dir = P1 - P2;
1718 return std::fabs( ( QgsVector3D::dotProduct( dir, u3 ) ) ); // u3 is already normalized
1719}
1720
1722 const QgsVector3D &P2, const QgsVector3D &P22,
1723 QgsVector3D &X1, double epsilon )
1724{
1725 const QgsVector3D d = P2 - P1;
1726 QgsVector3D u1 = P12 - P1;
1727 u1.normalize();
1728 QgsVector3D u2 = P22 - P2;
1729 u2.normalize();
1730 const QgsVector3D u3 = QgsVector3D::crossProduct( u1, u2 );
1731
1732 if ( std::fabs( u3.x() ) <= epsilon &&
1733 std::fabs( u3.y() ) <= epsilon &&
1734 std::fabs( u3.z() ) <= epsilon )
1735 {
1736 // The rays are almost parallel.
1737 return false;
1738 }
1739
1740 // X1 and X2 are the closest points on lines
1741 // we want to find X1 (lies on u1)
1742 // solving the linear equation in r1 and r2: Xi = Pi + ri*ui
1743 // we are only interested in X1 so we only solve for r1.
1744 float a1 = QgsVector3D::dotProduct( u1, u1 ), b1 = QgsVector3D::dotProduct( u1, u2 ), c1 = QgsVector3D::dotProduct( u1, d );
1745 float a2 = QgsVector3D::dotProduct( u1, u2 ), b2 = QgsVector3D::dotProduct( u2, u2 ), c2 = QgsVector3D::dotProduct( u2, d );
1746 if ( !( std::fabs( b1 ) > epsilon ) )
1747 {
1748 // Denominator is close to zero.
1749 return false;
1750 }
1751 if ( !( a2 != -1 && a2 != 1 ) )
1752 {
1753 // Lines are parallel
1754 return false;
1755 }
1756
1757 const double r1 = ( c2 - b2 * c1 / b1 ) / ( a2 - b2 * a1 / b1 );
1758 X1 = P1 + u1 * r1;
1759
1760 return true;
1761}
1762
1764 const QgsVector3D &Lb1, const QgsVector3D &Lb2,
1765 QgsVector3D &intersection )
1766{
1767
1768 // if all Vector are on the same plane (have the same Z), use the 2D intersection
1769 // else return a false result
1770 if ( qgsDoubleNear( La1.z(), La2.z() ) && qgsDoubleNear( La1.z(), Lb1.z() ) && qgsDoubleNear( La1.z(), Lb2.z() ) )
1771 {
1772 QgsPoint ptInter;
1773 bool isIntersection;
1774 segmentIntersection( QgsPoint( La1.x(), La1.y() ),
1775 QgsPoint( La2.x(), La2.y() ),
1776 QgsPoint( Lb1.x(), Lb1.y() ),
1777 QgsPoint( Lb2.x(), Lb2.y() ),
1778 ptInter,
1779 isIntersection,
1780 1e-8,
1781 true );
1782 intersection.set( ptInter.x(), ptInter.y(), La1.z() );
1783 return true;
1784 }
1785
1786 // first check if lines have an exact intersection point
1787 // do it by checking if the shortest distance is exactly 0
1788 const float distance = skewLinesDistance( La1, La2, Lb1, Lb2 );
1789 if ( qgsDoubleNear( distance, 0.0 ) )
1790 {
1791 // 3d lines have exact intersection point.
1792 const QgsVector3D C = La2;
1793 const QgsVector3D D = Lb2;
1794 const QgsVector3D e = La1 - La2;
1795 const QgsVector3D f = Lb1 - Lb2;
1796 const QgsVector3D g = D - C;
1797 if ( qgsDoubleNear( ( QgsVector3D::crossProduct( f, g ) ).length(), 0.0 ) || qgsDoubleNear( ( QgsVector3D::crossProduct( f, e ) ).length(), 0.0 ) )
1798 {
1799 // Lines have no intersection, are they parallel?
1800 return false;
1801 }
1802
1804 fgn.normalize();
1805
1807 fen.normalize();
1808
1809 int di = -1;
1810 if ( fgn == fen ) // same direction?
1811 di *= -1;
1812
1813 intersection = C + e * di * ( QgsVector3D::crossProduct( f, g ).length() / QgsVector3D::crossProduct( f, e ).length() );
1814 return true;
1815 }
1816
1817 // try to calculate the approximate intersection point
1818 QgsVector3D X1, X2;
1819 const bool firstIsDone = skewLinesProjection( La1, La2, Lb1, Lb2, X1 );
1820 const bool secondIsDone = skewLinesProjection( Lb1, Lb2, La1, La2, X2 );
1821
1822 if ( !firstIsDone || !secondIsDone )
1823 {
1824 // Could not obtain projection point.
1825 return false;
1826 }
1827
1828 intersection = ( X1 + X2 ) / 2.0;
1829 return true;
1830}
1831
1832double QgsGeometryUtils::triangleArea( double aX, double aY, double bX, double bY, double cX, double cY )
1833{
1834 return 0.5 * std::abs( ( aX - cX ) * ( bY - aY ) - ( aX - bX ) * ( cY - aY ) );
1835}
1836
1837void QgsGeometryUtils::weightedPointInTriangle( const double aX, const double aY, const double bX, const double bY, const double cX, const double cY,
1838 double weightB, double weightC, double &pointX, double &pointY )
1839{
1840 // if point will be outside of the triangle, invert weights
1841 if ( weightB + weightC > 1 )
1842 {
1843 weightB = 1 - weightB;
1844 weightC = 1 - weightC;
1845 }
1846
1847 const double rBx = weightB * ( bX - aX );
1848 const double rBy = weightB * ( bY - aY );
1849 const double rCx = weightC * ( cX - aX );
1850 const double rCy = weightC * ( cY - aY );
1851
1852 pointX = rBx + rCx + aX;
1853 pointY = rBy + rCy + aY;
1854}
1855
1857{
1858 bool rc = false;
1859
1860 for ( const QgsPoint &pt : points )
1861 {
1862 if ( pt.isMeasure() )
1863 {
1864 point.convertTo( QgsWkbTypes::addM( point.wkbType() ) );
1865 point.setM( pt.m() );
1866 rc = true;
1867 break;
1868 }
1869 }
1870
1871 return rc;
1872}
1873
1875{
1876 bool rc = false;
1877
1878 for ( const QgsPoint &pt : points )
1879 {
1880 if ( pt.is3D() )
1881 {
1882 point.convertTo( QgsWkbTypes::addZ( point.wkbType() ) );
1883 point.setZ( pt.z() );
1884 rc = true;
1885 break;
1886 }
1887 }
1888
1889 return rc;
1890}
1891
1892bool QgsGeometryUtils::angleBisector( double aX, double aY, double bX, double bY, double cX, double cY, double dX, double dY,
1893 double &pointX SIP_OUT, double &pointY SIP_OUT, double &angle SIP_OUT )
1894{
1895 const QgsPoint pA = QgsPoint( aX, aY );
1896 const QgsPoint pB = QgsPoint( bX, bY );
1897 const QgsPoint pC = QgsPoint( cX, cY );
1898 const QgsPoint pD = QgsPoint( dX, dY );
1899 angle = ( pA.azimuth( pB ) + pC.azimuth( pD ) ) / 2.0;
1900
1901 QgsPoint pOut;
1902 bool intersection = false;
1903 QgsGeometryUtils::segmentIntersection( pA, pB, pC, pD, pOut, intersection );
1904
1905 pointX = pOut.x();
1906 pointY = pOut.y();
1907
1908 return intersection;
1909}
1910
1911bool QgsGeometryUtils::bisector( double aX, double aY, double bX, double bY, double cX, double cY,
1912 double &pointX SIP_OUT, double &pointY SIP_OUT )
1913{
1914 const QgsPoint pA = QgsPoint( aX, aY );
1915 const QgsPoint pB = QgsPoint( bX, bY );
1916 const QgsPoint pC = QgsPoint( cX, cY );
1917 const double angle = ( pA.azimuth( pB ) + pA.azimuth( pC ) ) / 2.0;
1918
1919 QgsPoint pOut;
1920 bool intersection = false;
1921 QgsGeometryUtils::segmentIntersection( pB, pC, pA, pA.project( 1.0, angle ), pOut, intersection );
1922
1923 pointX = pOut.x();
1924 pointY = pOut.y();
1925
1926 return intersection;
1927}
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition: qgis.h:154
@ PointM
PointM.
@ PointZ
PointZ.
@ PointZM
PointZM.
Abstract base class for all geometries.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
@ MaximumDifference
Maximum distance between an arbitrary point on the original curve and closest point on its approximat...
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
virtual int vertexCount(int part=0, int ring=0) const =0
Returns the number of vertices of which this geometry is built.
AxisOrder
Axis order for GML generation.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual bool isEmpty() const
Returns true if the geometry is empty.
Qgis::WkbType wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
@ FlagExportNanAsDoubleMin
Use -DOUBLE_MAX to represent NaN (since QGIS 3.30)
virtual double segmentLength(QgsVertexId startVertex) const =0
Returns the length of the segment of the geometry which begins at startVertex.
virtual bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const =0
Returns next vertex id and coordinates.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
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.
Curve polygon geometry type.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
Geometry collection.
static double circleTangentDirection(const QgsPoint &tangentPoint, const QgsPoint &cp1, const QgsPoint &cp2, const QgsPoint &cp3) SIP_HOLDGIL
Calculates the direction angle of a circle tangent (clockwise from north in radians)
static QString pointsToJSON(const QgsPointSequence &points, int precision)
Returns a geoJSON coordinates string.
static QgsPoint midpoint(const QgsPoint &pt1, const QgsPoint &pt2) SIP_HOLDGIL
Returns a middle point between points pt1 and pt2.
static bool bisector(double aX, double aY, double bX, double bY, double cX, double cY, double &pointX, double &pointY) SIP_HOLDGIL
Returns the point (pointX, pointY) forming the bisector from point (aX, aY) to the segment (bX,...
static double sqrDistance2D(const QgsPoint &pt1, const QgsPoint &pt2) SIP_HOLDGIL
Returns the squared 2D distance between two points.
static bool skewLinesProjection(const QgsVector3D &P1, const QgsVector3D &P12, const QgsVector3D &P2, const QgsVector3D &P22, QgsVector3D &X1, double epsilon=0.0001) SIP_HOLDGIL
A method to project one skew line onto another.
static double ccwAngle(double dy, double dx) SIP_HOLDGIL
Returns the counter clockwise angle between a line with components dx, dy and the line with dx > 0 an...
static double triangleArea(double aX, double aY, double bX, double bY, double cX, double cY) SIP_HOLDGIL
Returns the area of the triangle denoted by the points (aX, aY), (bX, bY) and (cX,...
static bool segmentMidPoint(const QgsPoint &p1, const QgsPoint &p2, QgsPoint &result, double radius, const QgsPoint &mousePos) SIP_HOLDGIL
Calculates midpoint on circle passing through p1 and p2, closest to the given coordinate mousePos.
static json pointsToJson(const QgsPointSequence &points, int precision)
Returns coordinates as json object.
static int circleCircleInnerTangents(const QgsPointXY &center1, double radius1, const QgsPointXY &center2, double radius2, QgsPointXY &line1P1, QgsPointXY &line1P2, QgsPointXY &line2P1, QgsPointXY &line2P2) SIP_HOLDGIL
Calculates the inner tangent points for two circles, centered at center1 and center2 and with radii o...
static QVector< QgsLineString * > extractLineStrings(const QgsAbstractGeometry *geom)
Returns list of linestrings extracted from the passed geometry.
static bool lineIntersection(const QgsPoint &p1, QgsVector v1, const QgsPoint &p2, QgsVector v2, QgsPoint &intersection) SIP_HOLDGIL
Computes the intersection between two lines.
static double normalizedAngle(double angle) SIP_HOLDGIL
Ensures that an angle is in the range 0 <= angle < 2 pi.
static QgsPointXY interpolatePointOnLineByValue(double x1, double y1, double v1, double x2, double y2, double v2, double value) SIP_HOLDGIL
Interpolates the position of a point along the line from (x1, y1) to (x2, y2).
static QVector< SelfIntersection > selfIntersections(const QgsAbstractGeometry *geom, int part, int ring, double tolerance)
Find self intersections in a polyline.
static bool transferFirstZValueToPoint(const QgsPointSequence &points, QgsPoint &point)
A Z dimension is added to point if one of the point in the list points is in 3D.
static QgsPoint closestPoint(const QgsAbstractGeometry &geometry, const QgsPoint &point)
Returns the nearest point on a segment of a geometry for the specified point.
static int segmentSide(const QgsPoint &pt1, const QgsPoint &pt3, const QgsPoint &pt2) SIP_HOLDGIL
For line defined by points pt1 and pt3, find out on which side of the line is point pt3.
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 QStringList wktGetChildBlocks(const QString &wkt, const QString &defaultType=QString())
Parses a WKT string and returns of list of blocks contained in the WKT.
static void perpendicularCenterSegment(double centerPointX, double centerPointY, double segmentPoint1x, double segmentPoint1y, double segmentPoint2x, double segmentPoint2y, double &perpendicularSegmentPoint1x, double &perpendicularSegmentPoint1y, double &perpendicularSegmentPoint2x, double &perpendicularSegmentPoint2y, double segmentLength=0) SIP_HOLDGIL
Create a perpendicular line segment to a given segment [segmentPoint1,segmentPoint2] with its center ...
static QgsPoint segmentMidPointFromCenter(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, bool useShortestArc=true) SIP_HOLDGIL
Calculates the midpoint on the circle passing through p1 and p2, with the specified center coordinate...
static bool lineCircleIntersection(const QgsPointXY &center, double radius, const QgsPointXY &linePoint1, const QgsPointXY &linePoint2, QgsPointXY &intersection) SIP_HOLDGIL
Compute the intersection of a line and a circle.
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) SIP_HOLDGIL
Compute the intersection between two segments.
static void circleCenterRadius(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double &radius, double &centerX, double &centerY) SIP_HOLDGIL
Returns radius and center of the circle through pt1, pt2, pt3.
static void pointsToWKB(QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure, QgsAbstractGeometry::WkbFlags flags)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
static double distanceToVertex(const QgsAbstractGeometry &geom, QgsVertexId id)
Returns the distance along a geometry from its first vertex to the specified vertex.
static QPair< Qgis::WkbType, 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 skewLinesDistance(const QgsVector3D &P1, const QgsVector3D &P12, const QgsVector3D &P2, const QgsVector3D &P22) SIP_HOLDGIL
An algorithm to calculate the shortest distance between two skew lines.
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3) SIP_HOLDGIL
Calculates the average angle (in radians) between the two linear segments from (x1,...
static bool angleOnCircle(double angle, double angle1, double angle2, double angle3) SIP_HOLDGIL
Returns true if an angle is between angle1 and angle3 on a circle described by angle1,...
static double lineAngle(double x1, double y1, double x2, double y2) SIP_HOLDGIL
Calculates the direction of line joining two points in radians, clockwise from the north direction.
static double distToInfiniteLine(const QgsPoint &point, const QgsPoint &linePoint1, const QgsPoint &linePoint2, double epsilon=1e-7)
Returns the distance between a point and an infinite line.
static double sqrDistToLine(double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon) SIP_HOLDGIL
Returns the squared distance between a point and a line.
static void perpendicularOffsetPointAlongSegment(double x1, double y1, double x2, double y2, double proportion, double offset, double *x, double *y)
Calculates a point a certain proportion of the way along the segment from (x1, y1) to (x2,...
static bool circleClockwise(double angle1, double angle2, double angle3) SIP_HOLDGIL
Returns true if the circle defined by three angles is ordered clockwise.
static bool angleBisector(double aX, double aY, double bX, double bY, double cX, double cY, double dX, double dY, double &pointX, double &pointY, double &angle) SIP_HOLDGIL
Returns the point (pointX, pointY) forming the bisector from segment (aX aY) (bX bY) and segment (bX,...
static int circleCircleOuterTangents(const QgsPointXY &center1, double radius1, const QgsPointXY &center2, double radius2, QgsPointXY &line1P1, QgsPointXY &line1P2, QgsPointXY &line2P1, QgsPointXY &line2P2) SIP_HOLDGIL
Calculates the outer tangent points for two circles, centered at center1 and center2 and with radii o...
static bool transferFirstMValueToPoint(const QgsPointSequence &points, QgsPoint &point)
A M dimension is added to point if one of the points in the list points contains an M value.
static double gradient(const QgsPoint &pt1, const QgsPoint &pt2) SIP_HOLDGIL
Returns the gradient of a line defined by points pt1 and pt2.
static double sweepAngle(double centerX, double centerY, double x1, double y1, double x2, double y2, double x3, double y3) SIP_HOLDGIL
Calculates angle of a circular string part defined by pt1, pt2, pt3.
static double angleBetweenThreePoints(double x1, double y1, double x2, double y2, double x3, double y3) SIP_HOLDGIL
Calculates the angle between the lines AB and BC, where AB and BC described by points a,...
static QgsPoint closestVertex(const QgsAbstractGeometry &geom, const QgsPoint &pt, QgsVertexId &id)
Returns the closest vertex to a geometry for a specified point.
static double interpolateArcValue(double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3) SIP_HOLDGIL
Interpolate a value at given angle on circular arc given values (zm1, zm2, zm3) at three different an...
static double circleLength(double x1, double y1, double x2, double y2, double x3, double y3) SIP_HOLDGIL
Length of a circular string segment defined by pt1, pt2, pt3.
static QgsLineString perpendicularSegment(const QgsPoint &p, const QgsPoint &s1, const QgsPoint &s2) SIP_HOLDGIL
Create a perpendicular line segment from p to segment [s1, s2].
static int circleCircleIntersections(const QgsPointXY &center1, double radius1, const QgsPointXY &center2, double radius2, QgsPointXY &intersection1, QgsPointXY &intersection2) SIP_HOLDGIL
Calculates the intersections points between the circle with center center1 and radius radius1 and the...
static void coefficients(const QgsPoint &pt1, const QgsPoint &pt2, double &a, double &b, double &c) SIP_HOLDGIL
Returns the coefficients (a, b, c for equation "ax + by + c = 0") of a line defined by points pt1 and...
static double linePerpendicularAngle(double x1, double y1, double x2, double y2) SIP_HOLDGIL
Calculates the perpendicular angle to a line joining two points.
static bool tangentPointAndCircle(const QgsPointXY &center, double radius, const QgsPointXY &p, QgsPointXY &pt1, QgsPointXY &pt2) SIP_HOLDGIL
Calculates the tangent points between the circle with the specified center and radius and the point p...
static QgsPoint pointOnLineWithDistance(const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance) SIP_HOLDGIL
Returns a point a specified distance toward a second point.
static int closestSideOfRectangle(double right, double bottom, double left, double top, double x, double y)
Returns a number representing the closest side of a rectangle defined by /a right,...
static bool pointContinuesArc(const QgsPoint &a1, const QgsPoint &a2, const QgsPoint &a3, const QgsPoint &b, double distanceTolerance, double pointSpacingAngleTolerance) SIP_HOLDGIL
Returns true if point b is on the arc formed by points a1, a2, and a3, but not within that arc portio...
static void weightedPointInTriangle(double aX, double aY, double bX, double bY, double cX, double cY, double weightB, double weightC, double &pointX, double &pointY) SIP_HOLDGIL
Returns a weighted point inside the triangle denoted by the points (aX, aY), (bX, bY) and (cX,...
static QgsPointSequence pointsFromWKT(const QString &wktCoordinateList, bool is3D, bool isMeasure)
Returns a list of points contained in a WKT string.
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 bool circleAngleBetween(double angle, double angle1, double angle2, bool clockwise) SIP_HOLDGIL
Returns true if, in a circle, angle is between angle1 and angle2.
static QDomElement pointsToGML2(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::coordinates DOM element.
static bool transferFirstZOrMValueToPoint(Iterator verticesBegin, Iterator verticesEnd, QgsPoint &point)
A Z or M dimension is added to point if one of the points in the list points contains Z or M value.
static QgsPoint interpolatePointOnArc(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double distance) SIP_HOLDGIL
Interpolates a point on an arc defined by three points, pt1, pt2 and pt3.
static QDomElement pointsToGML3(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D, QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::posList DOM element.
static bool linesIntersection3D(const QgsVector3D &La1, const QgsVector3D &La2, const QgsVector3D &Lb1, const QgsVector3D &Lb2, QgsVector3D &intersection) SIP_HOLDGIL
An algorithm to calculate an (approximate) intersection of two lines in 3D.
static int leftOfLine(const double x, const double y, const double x1, const double y1, const double x2, const double y2) SIP_HOLDGIL
Returns a value < 0 if the point (x, y) is left of the line from (x1, y1) -> (x2, y2).
static QString pointsToWKT(const QgsPointSequence &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
static QgsPointXY interpolatePointOnLine(double x1, double y1, double x2, double y2, double fraction) SIP_HOLDGIL
Interpolates the position of a point a fraction of the way along the line from (x1,...
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
A class to represent a 2D point.
Definition: qgspointxy.h:59
void set(double x, double y) SIP_HOLDGIL
Sets the x and y value of the point.
Definition: qgspointxy.h:139
double sqrDist(double x, double y) const SIP_HOLDGIL
Returns the squared distance between this point a specified x, y coordinate.
Definition: qgspointxy.h:190
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
double distance(double x, double y) const SIP_HOLDGIL
Returns the distance between this point and a specified x, y coordinate.
Definition: qgspointxy.h:211
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
double distance(double x, double y) const SIP_HOLDGIL
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition: qgspoint.h:343
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:562
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgspoint.cpp:767
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:551
QgsPoint project(double distance, double azimuth, double inclination=90.0) const SIP_HOLDGIL
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition: qgspoint.cpp:735
Q_GADGET double x
Definition: qgspoint.h:52
void setZ(double z) SIP_HOLDGIL
Sets the point's z-coordinate.
Definition: qgspoint.h:304
double z
Definition: qgspoint.h:54
bool convertTo(Qgis::WkbType type) override
Converts the geometry to a specified type.
Definition: qgspoint.cpp:620
double azimuth(const QgsPoint &other) const SIP_HOLDGIL
Calculates Cartesian azimuth between this point and other one (clockwise in degree,...
Definition: qgspoint.cpp:716
double m
Definition: qgspoint.h:55
double y
Definition: qgspoint.h:53
void setM(double m) SIP_HOLDGIL
Sets the point's m-value.
Definition: qgspoint.h:319
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:51
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:53
static double dotProduct(const QgsVector3D &v1, const QgsVector3D &v2)
Returns the dot product of two vectors.
Definition: qgsvector3d.h:99
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:49
void normalize()
Normalizes the current vector in place.
Definition: qgsvector3d.h:119
static QgsVector3D crossProduct(const QgsVector3D &v1, const QgsVector3D &v2)
Returns the cross product of two vectors.
Definition: qgsvector3d.h:105
void set(double x, double y, double z)
Sets vector coordinates.
Definition: qgsvector3d.h:56
double length() const
Returns the length of the vector.
Definition: qgsvector3d.h:113
A class to represent a vector.
Definition: qgsvector.h:30
QgsVector perpVector() const SIP_HOLDGIL
Returns the perpendicular vector to this vector (rotated 90 degrees counter-clockwise)
Definition: qgsvector.h:164
QgsVector normalized() const SIP_THROW(QgsException)
Returns the vector's normalized (or "unit") vector (ie same angle but length of 1....
Definition: qgsvector.cpp:28
double y() const SIP_HOLDGIL
Returns the vector's y-component.
Definition: qgsvector.h:156
double x() const SIP_HOLDGIL
Returns the vector's x-component.
Definition: qgsvector.h:147
double length() const SIP_HOLDGIL
Returns the length of the vector.
Definition: qgsvector.h:128
WKB pointer handler.
Definition: qgswkbptr.h:44
static Qgis::WkbType addZ(Qgis::WkbType type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1073
static Qgis::WkbType addM(Qgis::WkbType type) SIP_HOLDGIL
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1098
static Qgis::WkbType parseType(const QString &wktStr)
Attempts to extract the WKB type from a WKT string.
static bool hasZ(Qgis::WkbType type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:977
static bool hasM(Qgis::WkbType type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1027
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
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
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:3927
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition: qgis.h:4042
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:3988
const double DEFAULT_SEGMENT_EPSILON
Default snapping tolerance for segments.
Definition: qgis.h:4531
#define SIP_OUT
Definition: qgis_sip.h:58
QVector< QgsPoint > QgsPointSequence
bool isClockwise(std::array< Direction, 4 > dirs)
Checks whether the 4 directions in dirs make up a clockwise rectangle.
int precision
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:31
int vertex
Vertex number.
Definition: qgsvertexid.h:95
int part
Part number.
Definition: qgsvertexid.h:89
Qgis::VertexType type
Vertex type.
Definition: qgsvertexid.h:98
int ring
Ring number.
Definition: qgsvertexid.h:92
bool isValid() const SIP_HOLDGIL
Returns true if the vertex id is valid.
Definition: qgsvertexid.h:46