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