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