QGIS API Documentation 3.99.0-Master (26c88405ac0)
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 <limits>
19#include <memory>
20#include <nlohmann/json.hpp>
21
22#include "qgsabstractgeometry.h"
23#include "qgscircularstring.h"
24#include "qgscompoundcurve.h"
25#include "qgscurve.h"
26#include "qgscurvepolygon.h"
28#include "qgslinestring.h"
29#include "qgslogger.h"
30#include "qgspoint.h"
31#include "qgsvertexid.h"
32#include "qgswkbptr.h"
33
34#include <QRegularExpression>
35#include <QStringList>
36#include <QVector>
37
38QVector<QgsLineString *> QgsGeometryUtils::extractLineStrings( const QgsAbstractGeometry *geom )
39{
40 QVector< QgsLineString * > linestrings;
41 if ( !geom )
42 return linestrings;
43
44 QVector< const QgsAbstractGeometry * > geometries;
45 geometries << geom;
46 while ( ! geometries.isEmpty() )
47 {
48 const QgsAbstractGeometry *g = geometries.takeFirst();
49 if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( g ) )
50 {
51 linestrings << static_cast< QgsLineString * >( curve->segmentize() );
52 }
54 {
55 for ( int i = 0; i < collection->numGeometries(); ++i )
56 {
57 geometries.append( collection->geometryN( i ) );
58 }
59 }
60 else if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( g ) )
61 {
62 if ( curvePolygon->exteriorRing() )
63 linestrings << static_cast< QgsLineString * >( curvePolygon->exteriorRing()->segmentize() );
64
65 for ( int i = 0; i < curvePolygon->numInteriorRings(); ++i )
66 {
67 linestrings << static_cast< QgsLineString * >( curvePolygon->interiorRing( i )->segmentize() );
68 }
69 }
70 }
71 return linestrings;
72}
73
75{
76 double minDist = std::numeric_limits<double>::max();
77 double currentDist = 0;
78 QgsPoint minDistPoint;
79 id = QgsVertexId(); // set as invalid
80
81 if ( geom.isEmpty() || pt.isEmpty() )
82 return minDistPoint;
83
84 QgsVertexId vertexId;
85 QgsPoint vertex;
86 while ( geom.nextVertex( vertexId, vertex ) )
87 {
88 currentDist = QgsGeometryUtils::sqrDistance2D( pt, vertex );
89 // The <= is on purpose: for geometries with closing vertices, this ensures
90 // that the closing vertex is returned. For the vertex tool, the rubberband
91 // of the closing vertex is above the opening vertex, hence with the <=
92 // situations where the covered opening vertex rubberband is selected are
93 // avoided.
94 if ( currentDist <= minDist )
95 {
96 minDist = currentDist;
97 minDistPoint = vertex;
98 id.part = vertexId.part;
99 id.ring = vertexId.ring;
100 id.vertex = vertexId.vertex;
101 id.type = vertexId.type;
102 }
103 }
104
105 return minDistPoint;
106}
107
109{
111 QgsVertexId vertexAfter;
112 geometry.closestSegment( point, closestPoint, vertexAfter, nullptr, Qgis::DEFAULT_SEGMENT_EPSILON );
113 if ( vertexAfter.isValid() )
114 {
115 const QgsPoint pointAfter = geometry.vertexAt( vertexAfter );
116 if ( vertexAfter.vertex > 0 )
117 {
118 QgsVertexId vertexBefore = vertexAfter;
119 vertexBefore.vertex--;
120 const QgsPoint pointBefore = geometry.vertexAt( vertexBefore );
121 const double length = pointBefore.distance( pointAfter );
122 const double distance = pointBefore.distance( closestPoint );
123
124 if ( qgsDoubleNear( distance, 0.0 ) )
125 closestPoint = pointBefore;
126 else if ( qgsDoubleNear( distance, length ) )
127 closestPoint = pointAfter;
128 else
129 {
130 if ( QgsWkbTypes::hasZ( geometry.wkbType() ) && length )
131 closestPoint.addZValue( pointBefore.z() + ( pointAfter.z() - pointBefore.z() ) * distance / length );
132 if ( QgsWkbTypes::hasM( geometry.wkbType() ) )
133 closestPoint.addMValue( pointBefore.m() + ( pointAfter.m() - pointBefore.m() ) * distance / length );
134 }
135 }
136 }
137
138 return closestPoint;
139}
140
142{
143 double currentDist = 0;
144 QgsVertexId vertexId;
145 QgsPoint vertex;
146 while ( geom.nextVertex( vertexId, vertex ) )
147 {
148 if ( vertexId == id )
149 {
150 //found target vertex
151 return currentDist;
152 }
153 currentDist += geom.segmentLength( vertexId );
154 }
155
156 //could not find target vertex
157 return -1;
158}
159
160bool QgsGeometryUtils::verticesAtDistance( const QgsAbstractGeometry &geometry, double distance, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
161{
162 double currentDist = 0;
163 previousVertex = QgsVertexId();
164 nextVertex = QgsVertexId();
165
166 if ( geometry.isEmpty() )
167 {
168 return false;
169 }
170
171 QgsPoint point;
172 QgsPoint previousPoint;
173
174 if ( qgsDoubleNear( distance, 0.0 ) )
175 {
176 geometry.nextVertex( previousVertex, point );
177 nextVertex = previousVertex;
178 return true;
179 }
180
181 bool first = true;
182 while ( currentDist < distance && geometry.nextVertex( nextVertex, point ) )
183 {
184 if ( !first && nextVertex.part == previousVertex.part && nextVertex.ring == previousVertex.ring )
185 {
186 currentDist += std::sqrt( QgsGeometryUtils::sqrDistance2D( previousPoint, point ) );
187 }
188
189 if ( qgsDoubleNear( currentDist, distance ) )
190 {
191 // exact hit!
192 previousVertex = nextVertex;
193 return true;
194 }
195
196 if ( currentDist > distance )
197 {
198 return true;
199 }
200
201 previousVertex = nextVertex;
202 previousPoint = point;
203 first = false;
204 }
205
206 //could not find target distance
207 return false;
208}
209
210double QgsGeometryUtils::distToInfiniteLine( const QgsPoint &point, const QgsPoint &linePoint1, const QgsPoint &linePoint2, double epsilon )
211{
212 const double area = std::abs(
213 ( linePoint1.x() - linePoint2.x() ) * ( point.y() - linePoint2.y() ) -
214 ( linePoint1.y() - linePoint2.y() ) * ( point.x() - linePoint2.x() )
215 );
216
217 const double length = std::sqrt(
218 std::pow( linePoint1.x() - linePoint2.x(), 2 ) +
219 std::pow( linePoint1.y() - linePoint2.y(), 2 )
220 );
221
222 const double distance = area / length;
223 return qgsDoubleNear( distance, 0.0, epsilon ) ? 0.0 : distance;
224}
225
226bool QgsGeometryUtils::lineCircleIntersection( const QgsPointXY &center, const double radius,
227 const QgsPointXY &linePoint1, const QgsPointXY &linePoint2,
228 QgsPointXY &intersection )
229{
230 // formula taken from http://mathworld.wolfram.com/Circle-LineIntersection.html
231
232 const double x1 = linePoint1.x() - center.x();
233 const double y1 = linePoint1.y() - center.y();
234 const double x2 = linePoint2.x() - center.x();
235 const double y2 = linePoint2.y() - center.y();
236 const double dx = x2 - x1;
237 const double dy = y2 - y1;
238
239 const double dr2 = std::pow( dx, 2 ) + std::pow( dy, 2 );
240 const double d = x1 * y2 - x2 * y1;
241
242 const double disc = std::pow( radius, 2 ) * dr2 - std::pow( d, 2 );
243
244 if ( disc < 0 )
245 {
246 //no intersection or tangent
247 return false;
248 }
249 else
250 {
251 // two solutions
252 const int sgnDy = dy < 0 ? -1 : 1;
253
254 const double sqrDisc = std::sqrt( disc );
255
256 const double ax = center.x() + ( d * dy + sgnDy * dx * sqrDisc ) / dr2;
257 const double ay = center.y() + ( -d * dx + std::fabs( dy ) * sqrDisc ) / dr2;
258 const QgsPointXY p1( ax, ay );
259
260 const double bx = center.x() + ( d * dy - sgnDy * dx * sqrDisc ) / dr2;
261 const double by = center.y() + ( -d * dx - std::fabs( dy ) * sqrDisc ) / dr2;
262 const QgsPointXY p2( bx, by );
263
264 // snap to nearest intersection
265
266 if ( intersection.sqrDist( p1 ) < intersection.sqrDist( p2 ) )
267 {
268 intersection.set( p1.x(), p1.y() );
269 }
270 else
271 {
272 intersection.set( p2.x(), p2.y() );
273 }
274 return true;
275 }
276}
277
278// based on public domain work by 3/26/2005 Tim Voght
279// see http://paulbourke.net/geometry/circlesphere/tvoght.c
280int QgsGeometryUtils::circleCircleIntersections( const QgsPointXY &center1, const double r1, const QgsPointXY &center2, const double r2, QgsPointXY &intersection1, QgsPointXY &intersection2 )
281{
282 // determine the straight-line distance between the centers
283 const double d = center1.distance( center2 );
284
285 // check if the circles intersect at only 1 point, either "externally" or "internally"
286 const bool singleSolutionExt = qgsDoubleNear( d, r1 + r2 );
287 const bool singleSolutionInt = qgsDoubleNear( d, std::fabs( r1 - r2 ) );
288
289 // check for solvability
290 if ( !singleSolutionExt && d > ( r1 + r2 ) )
291 {
292 // no solution. circles do not intersect.
293 return 0;
294 }
295 else if ( !singleSolutionInt && d < std::fabs( r1 - r2 ) )
296 {
297 // no solution. one circle is contained in the other
298 return 0;
299 }
300 else if ( qgsDoubleNear( d, 0 ) && ( qgsDoubleNear( r1, r2 ) ) )
301 {
302 // no solutions, the circles coincide
303 return 0;
304 }
305
306 /* 'point 2' is the point where the line through the circle
307 * intersection points crosses the line between the circle
308 * centers.
309 */
310
311 /* Determine the distance 'a' from point 0 to point 2.
312 * In the general case, a = ( ( r1 * r1 ) - ( r2 * r2 ) + ( d * d ) ) / ( 2.0 * d ).
313 * If d = r1 + r2 or d = r1 - r2 (i.e. r1 > r2), then a = r1; if d = r2 - r1 (i.e. r2 > r1), then a = -r1.
314 */
315 const double a = singleSolutionExt ? r1 : ( singleSolutionInt ? ( r1 > r2 ? r1 : -r1 ) : ( ( r1 * r1 ) - ( r2 * r2 ) + ( d * d ) ) / ( 2.0 * d ) );
316
317 /* dx and dy are the vertical and horizontal distances between
318 * the circle centers.
319 */
320 const double dx = center2.x() - center1.x();
321 const double dy = center2.y() - center1.y();
322
323 // Determine the coordinates of point 2.
324 const double x2 = center1.x() + ( dx * a / d );
325 const double y2 = center1.y() + ( dy * a / d );
326
327 // only 1 solution
328 if ( singleSolutionExt || singleSolutionInt )
329 {
330 intersection1 = QgsPointXY( x2, y2 );
331 intersection2 = QgsPointXY( x2, y2 );
332
333 return 1;
334 }
335
336 // 2 solutions
337
338 /* Determine the distance from point 2 to either of the
339 * intersection points.
340 */
341 const double h = std::sqrt( ( r1 * r1 ) - ( a * a ) );
342
343 /* Now determine the offsets of the intersection points from
344 * point 2.
345 */
346 const double rx = dy * ( h / d );
347 const double ry = dx * ( h / d );
348
349 // determine the absolute intersection points
350 intersection1 = QgsPointXY( x2 + rx, y2 - ry );
351 intersection2 = QgsPointXY( x2 - rx, y2 + ry );
352
353 return 2;
354}
355
356// Using https://stackoverflow.com/a/1351794/1861260
357// and inspired by http://csharphelper.com/blog/2014/11/find-the-tangent-lines-between-a-point-and-a-circle-in-c/
358bool QgsGeometryUtils::tangentPointAndCircle( const QgsPointXY &center, double radius, const QgsPointXY &p, QgsPointXY &pt1, QgsPointXY &pt2 )
359{
360 // distance from point to center of circle
361 const double dx = center.x() - p.x();
362 const double dy = center.y() - p.y();
363 const double distanceSquared = dx * dx + dy * dy;
364 const double radiusSquared = radius * radius;
365 if ( distanceSquared < radiusSquared )
366 {
367 // point is inside circle!
368 return false;
369 }
370
371 // distance from point to tangent point, using pythagoras
372 const double distanceToTangent = std::sqrt( distanceSquared - radiusSquared );
373
374 // tangent points are those where the original circle intersects a circle centered
375 // on p with radius distanceToTangent
376 circleCircleIntersections( center, radius, p, distanceToTangent, pt1, pt2 );
377
378 return true;
379}
380
381// inspired by http://csharphelper.com/blog/2014/12/find-the-tangent-lines-between-two-circles-in-c/
382int QgsGeometryUtils::circleCircleOuterTangents( const QgsPointXY &center1, double radius1, const QgsPointXY &center2, double radius2, QgsPointXY &line1P1, QgsPointXY &line1P2, QgsPointXY &line2P1, QgsPointXY &line2P2 )
383{
384 if ( radius1 > radius2 )
385 return circleCircleOuterTangents( center2, radius2, center1, radius1, line1P1, line1P2, line2P1, line2P2 );
386
387 const double radius2a = radius2 - radius1;
388 if ( !tangentPointAndCircle( center2, radius2a, center1, line1P2, line2P2 ) )
389 {
390 // there are no tangents
391 return 0;
392 }
393
394 // get the vector perpendicular to the
395 // first tangent with length radius1
396 QgsVector v1( -( line1P2.y() - center1.y() ), line1P2.x() - center1.x() );
397 const double v1Length = v1.length();
398 v1 = v1 * ( radius1 / v1Length );
399
400 // offset the tangent vector's points
401 line1P1 = center1 + v1;
402 line1P2 = line1P2 + v1;
403
404 // get the vector perpendicular to the
405 // second tangent with length radius1
406 QgsVector v2( line2P2.y() - center1.y(), -( line2P2.x() - center1.x() ) );
407 const double v2Length = v2.length();
408 v2 = v2 * ( radius1 / v2Length );
409
410 // offset the tangent vector's points
411 line2P1 = center1 + v2;
412 line2P2 = line2P2 + v2;
413
414 return 2;
415}
416
417// inspired by http://csharphelper.com/blog/2014/12/find-the-tangent-lines-between-two-circles-in-c/
418int QgsGeometryUtils::circleCircleInnerTangents( const QgsPointXY &center1, double radius1, const QgsPointXY &center2, double radius2, QgsPointXY &line1P1, QgsPointXY &line1P2, QgsPointXY &line2P1, QgsPointXY &line2P2 )
419{
420 if ( radius1 > radius2 )
421 return circleCircleInnerTangents( center2, radius2, center1, radius1, line1P1, line1P2, line2P1, line2P2 );
422
423 // determine the straight-line distance between the centers
424 const double d = center1.distance( center2 );
425 const double radius1a = radius1 + radius2;
426
427 // check for solvability
428 if ( d <= radius1a || qgsDoubleNear( d, radius1a ) )
429 {
430 // no solution. circles intersect or touch.
431 return 0;
432 }
433
434 if ( !tangentPointAndCircle( center1, radius1a, center2, line1P2, line2P2 ) )
435 {
436 // there are no tangents
437 return 0;
438 }
439
440 // get the vector perpendicular to the
441 // first tangent with length radius2
442 QgsVector v1( ( line1P2.y() - center2.y() ), -( line1P2.x() - center2.x() ) );
443 const double v1Length = v1.length();
444 v1 = v1 * ( radius2 / v1Length );
445
446 // offset the tangent vector's points
447 line1P1 = center2 + v1;
448 line1P2 = line1P2 + v1;
449
450 // get the vector perpendicular to the
451 // second tangent with length radius2
452 QgsVector v2( -( line2P2.y() - center2.y() ), line2P2.x() - center2.x() );
453 const double v2Length = v2.length();
454 v2 = v2 * ( radius2 / v2Length );
455
456 // offset the tangent vector's points in opposite direction
457 line2P1 = center2 + v2;
458 line2P2 = line2P2 + v2;
459
460 return 2;
461}
462
463QVector<QgsGeometryUtils::SelfIntersection> QgsGeometryUtils::selfIntersections( const QgsAbstractGeometry *geom, int part, int ring, double tolerance )
464{
465 QVector<SelfIntersection> intersections;
466
467 const int n = geom->vertexCount( part, ring );
468 const bool isClosed = geom->vertexAt( QgsVertexId( part, ring, 0 ) ) == geom->vertexAt( QgsVertexId( part, ring, n - 1 ) );
469
470 // Check every pair of segments for intersections
471 for ( int i = 0, j = 1; j < n; i = j++ )
472 {
473 const QgsPoint pi = geom->vertexAt( QgsVertexId( part, ring, i ) );
474 const QgsPoint pj = geom->vertexAt( QgsVertexId( part, ring, j ) );
475 if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) < tolerance * tolerance ) continue;
476
477 // Don't test neighboring edges
478 const int start = j + 1;
479 const int end = i == 0 && isClosed ? n - 1 : n;
480 for ( int k = start, l = start + 1; l < end; k = l++ )
481 {
482 const QgsPoint pk = geom->vertexAt( QgsVertexId( part, ring, k ) );
483 const QgsPoint pl = geom->vertexAt( QgsVertexId( part, ring, l ) );
484
485 QgsPoint inter;
486 bool intersection = false;
487 if ( !QgsGeometryUtils::segmentIntersection( pi, pj, pk, pl, inter, intersection, tolerance ) ) continue;
488
490 s.segment1 = i;
491 s.segment2 = k;
492 if ( s.segment1 > s.segment2 )
493 {
494 std::swap( s.segment1, s.segment2 );
495 }
496 s.point = inter;
497 intersections.append( s );
498 }
499 }
500 return intersections;
501}
502
503int QgsGeometryUtils::leftOfLine( const QgsPoint &point, const QgsPoint &p1, const QgsPoint &p2 )
504{
505 return QgsGeometryUtilsBase::leftOfLine( point.x(), point.y(), p1.x(), p1.y(), p2.x(), p2.y() );
506}
507
508QgsPoint QgsGeometryUtils::pointOnLineWithDistance( const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance )
509{
510 double x, y;
511 QgsGeometryUtilsBase::pointOnLineWithDistance( startPoint.x(), startPoint.y(), directionPoint.x(), directionPoint.y(), distance, x, y );
512 return QgsPoint( x, y );
513}
514
515
516QgsPoint QgsGeometryUtils::interpolatePointOnArc( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double distance )
517{
518 double centerX, centerY, radius;
519 circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
520
521 const double theta = distance / radius; // angle subtended
522 const double anglePt1 = std::atan2( pt1.y() - centerY, pt1.x() - centerX );
523 const double anglePt2 = std::atan2( pt2.y() - centerY, pt2.x() - centerX );
524 const double anglePt3 = std::atan2( pt3.y() - centerY, pt3.x() - centerX );
525 const bool isClockwise = QgsGeometryUtilsBase::circleClockwise( anglePt1, anglePt2, anglePt3 );
526 const double angleDest = anglePt1 + ( isClockwise ? -theta : theta );
527
528 const double x = centerX + radius * ( std::cos( angleDest ) );
529 const double y = centerY + radius * ( std::sin( angleDest ) );
530
531 const double z = pt1.is3D() ?
532 QgsGeometryUtilsBase::interpolateArcValue( angleDest, anglePt1, anglePt2, anglePt3, pt1.z(), pt2.z(), pt3.z() )
533 : 0;
534 const double m = pt1.isMeasure() ?
535 QgsGeometryUtilsBase::interpolateArcValue( angleDest, anglePt1, anglePt2, anglePt3, pt1.m(), pt2.m(), pt3.m() )
536 : 0;
537
538 return QgsPoint( pt1.wkbType(), x, y, z, m );
539}
540
541bool QgsGeometryUtils::segmentMidPoint( const QgsPoint &p1, const QgsPoint &p2, QgsPoint &result, double radius, const QgsPoint &mousePos )
542{
543 const QgsPoint midPoint( ( p1.x() + p2.x() ) / 2.0, ( p1.y() + p2.y() ) / 2.0 );
544 const double midDist = std::sqrt( sqrDistance2D( p1, midPoint ) );
545 if ( radius < midDist )
546 {
547 return false;
548 }
549 const double centerMidDist = std::sqrt( radius * radius - midDist * midDist );
550 const double dist = radius - centerMidDist;
551
552 const double midDx = midPoint.x() - p1.x();
553 const double midDy = midPoint.y() - p1.y();
554
555 //get the four possible midpoints
556 QVector<QgsPoint> possibleMidPoints;
557 possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPoint( midPoint.x() - midDy, midPoint.y() + midDx ), dist ) );
558 possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPoint( midPoint.x() - midDy, midPoint.y() + midDx ), 2 * radius - dist ) );
559 possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPoint( midPoint.x() + midDy, midPoint.y() - midDx ), dist ) );
560 possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPoint( midPoint.x() + midDy, midPoint.y() - midDx ), 2 * radius - dist ) );
561
562 //take the closest one
563 double minDist = std::numeric_limits<double>::max();
564 int minDistIndex = -1;
565 for ( int i = 0; i < possibleMidPoints.size(); ++i )
566 {
567 const double currentDist = sqrDistance2D( mousePos, possibleMidPoints.at( i ) );
568 if ( currentDist < minDist )
569 {
570 minDistIndex = i;
571 minDist = currentDist;
572 }
573 }
574
575 if ( minDistIndex == -1 )
576 {
577 return false;
578 }
579
580 result = possibleMidPoints.at( minDistIndex );
581
582 // add z and m support if necessary
584
585 return true;
586}
587
588QgsPoint QgsGeometryUtils::segmentMidPointFromCenter( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, const bool useShortestArc )
589{
590 double midPointAngle = QgsGeometryUtilsBase::averageAngle( QgsGeometryUtilsBase::lineAngle( center.x(), center.y(), p1.x(), p1.y() ),
591 QgsGeometryUtilsBase::lineAngle( center.x(), center.y(), p2.x(), p2.y() ) );
592 if ( !useShortestArc )
593 midPointAngle += M_PI;
594 return center.project( center.distance( p1 ), midPointAngle * 180 / M_PI );
595}
596
597double QgsGeometryUtils::circleTangentDirection( const QgsPoint &tangentPoint, const QgsPoint &cp1,
598 const QgsPoint &cp2, const QgsPoint &cp3 )
599{
600 //calculate circle midpoint
601 double mX, mY, radius;
602 circleCenterRadius( cp1, cp2, cp3, radius, mX, mY );
603
604 const double p1Angle = QgsGeometryUtilsBase::ccwAngle( cp1.y() - mY, cp1.x() - mX );
605 const double p2Angle = QgsGeometryUtilsBase::ccwAngle( cp2.y() - mY, cp2.x() - mX );
606 const double p3Angle = QgsGeometryUtilsBase::ccwAngle( cp3.y() - mY, cp3.x() - mX );
607 double angle = 0;
608 if ( QgsGeometryUtilsBase::circleClockwise( p1Angle, p2Angle, p3Angle ) )
609 {
610 angle = QgsGeometryUtilsBase::lineAngle( tangentPoint.x(), tangentPoint.y(), mX, mY ) - M_PI_2;
611 }
612 else
613 {
614 angle = QgsGeometryUtilsBase::lineAngle( mX, mY, tangentPoint.x(), tangentPoint.y() ) - M_PI_2;
615 }
616 if ( angle < 0 )
617 angle += 2 * M_PI;
618 return angle;
619}
620
621// Ported from PostGIS' pt_continues_arc
622bool QgsGeometryUtils::pointContinuesArc( const QgsPoint &a1, const QgsPoint &a2, const QgsPoint &a3, const QgsPoint &b, double distanceTolerance, double pointSpacingAngleTolerance )
623{
624 double centerX = 0;
625 double centerY = 0;
626 double radius = 0;
627 circleCenterRadius( a1, a2, a3, radius, centerX, centerY );
628
629 // Co-linear a1/a2/a3
630 if ( radius < 0.0 )
631 return false;
632
633 // distance of candidate point to center of arc a1-a2-a3
634 const double bDistance = std::sqrt( ( b.x() - centerX ) * ( b.x() - centerX ) +
635 ( b.y() - centerY ) * ( b.y() - centerY ) );
636
637 double diff = std::fabs( radius - bDistance );
638
639 auto arcAngle = []( const QgsPoint & a, const QgsPoint & b, const QgsPoint & c )->double
640 {
641 const double abX = b.x() - a.x();
642 const double abY = b.y() - a.y();
643
644 const double cbX = b.x() - c.x();
645 const double cbY = b.y() - c.y();
646
647 const double dot = ( abX * cbX + abY * cbY ); /* dot product */
648 const double cross = ( abX * cbY - abY * cbX ); /* cross product */
649
650 const double alpha = std::atan2( cross, dot );
651
652 return alpha;
653 };
654
655 // Is the point b on the circle?
656 if ( diff < distanceTolerance )
657 {
658 const double angle1 = arcAngle( a1, a2, a3 );
659 const double angle2 = arcAngle( a2, a3, b );
660
661 // Is the sweep angle similar to the previous one?
662 // We only consider a segment replaceable by an arc if the points within
663 // it are regularly spaced
664 diff = std::fabs( angle1 - angle2 );
665 if ( diff > pointSpacingAngleTolerance )
666 {
667 return false;
668 }
669
670 const int a2Side = QgsGeometryUtilsBase::leftOfLine( a2.x(), a2.y(), a1.x(), a1.y(), a3.x(), a3.y() );
671 const int bSide = QgsGeometryUtilsBase::leftOfLine( b.x(), b.y(), a1.x(), a1.y(), a3.x(), a3.y() );
672
673 // Is the point b on the same side of a1/a3 as the mid-point a2 is?
674 // If not, it's in the unbounded part of the circle, so it continues the arc, return true.
675 if ( bSide != a2Side )
676 return true;
677 }
678 return false;
679}
680
681void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, QgsPointSequence &points, double tolerance, QgsAbstractGeometry::SegmentationToleranceType toleranceType, bool hasZ, bool hasM )
682{
683 bool reversed = false;
684 const int segSide = segmentSide( p1, p3, p2 );
685
686 QgsPoint circlePoint1;
687 const QgsPoint &circlePoint2 = p2;
688 QgsPoint circlePoint3;
689
690 if ( segSide == -1 )
691 {
692 // Reverse !
693 circlePoint1 = p3;
694 circlePoint3 = p1;
695 reversed = true;
696 }
697 else
698 {
699 circlePoint1 = p1;
700 circlePoint3 = p3;
701 }
702
703 //adapted code from PostGIS
704 double radius = 0;
705 double centerX = 0;
706 double centerY = 0;
707 circleCenterRadius( circlePoint1, circlePoint2, circlePoint3, radius, centerX, centerY );
708
709 if ( circlePoint1 != circlePoint3 && ( radius < 0 || qgsDoubleNear( segSide, 0.0 ) ) ) //points are colinear
710 {
711 points.append( p1 );
712 points.append( p2 );
713 points.append( p3 );
714 return;
715 }
716
717 double increment = tolerance; //one segment per degree
718 if ( toleranceType == QgsAbstractGeometry::MaximumDifference )
719 {
720 // Ensure tolerance is not higher than twice the radius
721 tolerance = std::min( tolerance, radius * 2 );
722 const double halfAngle = std::acos( -tolerance / radius + 1 );
723 increment = 2 * halfAngle;
724 }
725
726 //angles of pt1, pt2, pt3
727 const double a1 = std::atan2( circlePoint1.y() - centerY, circlePoint1.x() - centerX );
728 double a2 = std::atan2( circlePoint2.y() - centerY, circlePoint2.x() - centerX );
729 double a3 = std::atan2( circlePoint3.y() - centerY, circlePoint3.x() - centerX );
730
731 // Make segmentation symmetric
732 const bool symmetric = true;
733 if ( symmetric )
734 {
735 double angle = a3 - a1;
736 // angle == 0 when full circle
737 if ( angle <= 0 ) angle += M_PI * 2;
738
739 /* Number of segments in output */
740 const int segs = ceil( angle / increment );
741 /* Tweak increment to be regular for all the arc */
742 increment = angle / segs;
743 }
744
745 /* Adjust a3 up so we can increment from a1 to a3 cleanly */
746 // a3 == a1 when full circle
747 if ( a3 <= a1 )
748 a3 += 2.0 * M_PI;
749 if ( a2 < a1 )
750 a2 += 2.0 * M_PI;
751
752 double x, y;
753 double z = 0;
754 double m = 0;
755
756 QVector<QgsPoint> stringPoints;
757 stringPoints.insert( 0, circlePoint1 );
758 if ( circlePoint2 != circlePoint3 && circlePoint1 != circlePoint2 ) //draw straight line segment if two points have the same position
759 {
760 Qgis::WkbType pointWkbType = Qgis::WkbType::Point;
761 if ( hasZ )
762 pointWkbType = QgsWkbTypes::addZ( pointWkbType );
763 if ( hasM )
764 pointWkbType = QgsWkbTypes::addM( pointWkbType );
765
766 // As we're adding the last point in any case, we'll avoid
767 // including a point which is at less than 1% increment distance
768 // from it (may happen to find them due to numbers approximation).
769 // NOTE that this effectively allows in output some segments which
770 // are more distant than requested. This is at most 1% off
771 // from requested MaxAngle and less for MaxError.
772 const double tolError = increment / 100;
773 const double stopAngle = a3 - tolError;
774 for ( double angle = a1 + increment; angle < stopAngle; angle += increment )
775 {
776 x = centerX + radius * std::cos( angle );
777 y = centerY + radius * std::sin( angle );
778
779 if ( hasZ )
780 {
781 z = QgsGeometryUtilsBase::interpolateArcValue( angle, a1, a2, a3, circlePoint1.z(), circlePoint2.z(), circlePoint3.z() );
782 }
783 if ( hasM )
784 {
785 m = QgsGeometryUtilsBase::interpolateArcValue( angle, a1, a2, a3, circlePoint1.m(), circlePoint2.m(), circlePoint3.m() );
786 }
787
788 stringPoints.insert( stringPoints.size(), QgsPoint( pointWkbType, x, y, z, m ) );
789 }
790 }
791 stringPoints.insert( stringPoints.size(), circlePoint3 );
792
793 // TODO: check if or implement QgsPointSequence directly taking an iterator to append
794 if ( reversed )
795 {
796 std::reverse( stringPoints.begin(), stringPoints.end() );
797 }
798 if ( ! points.empty() && stringPoints.front() == points.back() ) stringPoints.pop_front();
799 points.append( stringPoints );
800}
801
802int QgsGeometryUtils::segmentSide( const QgsPoint &pt1, const QgsPoint &pt3, const QgsPoint &pt2 )
803{
804 const double side = ( ( pt2.x() - pt1.x() ) * ( pt3.y() - pt1.y() ) - ( pt3.x() - pt1.x() ) * ( pt2.y() - pt1.y() ) );
805 if ( side == 0.0 )
806 {
807 return 0;
808 }
809 else
810 {
811 if ( side < 0 )
812 {
813 return -1;
814 }
815 if ( side > 0 )
816 {
817 return 1;
818 }
819 return 0;
820 }
821}
822
823QgsPointSequence QgsGeometryUtils::pointsFromWKT( const QString &wktCoordinateList, bool is3D, bool isMeasure )
824{
825 const int dim = 2 + is3D + isMeasure;
826 QgsPointSequence points;
827
828 const QStringList coordList = wktCoordinateList.split( ',', Qt::SkipEmptyParts );
829
830 //first scan through for extra unexpected dimensions
831 bool foundZ = false;
832 bool foundM = false;
833 const thread_local QRegularExpression rx( QStringLiteral( "\\s" ) );
834 const thread_local QRegularExpression rxIsNumber( QStringLiteral( "^[+-]?(\\d\\.?\\d*[Ee][+\\-]?\\d+|(\\d+\\.\\d*|\\d*\\.\\d+)|\\d+)$" ) );
835 for ( const QString &pointCoordinates : coordList )
836 {
837 const QStringList coordinates = pointCoordinates.split( rx, Qt::SkipEmptyParts );
838
839 // exit with an empty set if one list contains invalid value.
840 if ( coordinates.filter( rxIsNumber ).size() != coordinates.size() )
841 return points;
842
843 if ( coordinates.size() == 3 && !foundZ && !foundM && !is3D && !isMeasure )
844 {
845 // 3 dimensional coordinates, but not specifically marked as such. We allow this
846 // anyway and upgrade geometry to have Z dimension
847 foundZ = true;
848 }
849 else if ( coordinates.size() >= 4 && ( !( is3D || foundZ ) || !( isMeasure || foundM ) ) )
850 {
851 // 4 (or more) dimensional coordinates, but not specifically marked as such. We allow this
852 // anyway and upgrade geometry to have Z&M dimensions
853 foundZ = true;
854 foundM = true;
855 }
856 }
857
858 for ( const QString &pointCoordinates : coordList )
859 {
860 QStringList coordinates = pointCoordinates.split( rx, Qt::SkipEmptyParts );
861 if ( coordinates.size() < dim )
862 continue;
863
864 int idx = 0;
865 const double x = coordinates[idx++].toDouble();
866 const double y = coordinates[idx++].toDouble();
867
868 double z = 0;
869 if ( ( is3D || foundZ ) && coordinates.length() > idx )
870 z = coordinates[idx++].toDouble();
871
872 double m = 0;
873 if ( ( isMeasure || foundM ) && coordinates.length() > idx )
874 m = coordinates[idx++].toDouble();
875
877 if ( is3D || foundZ )
878 {
879 if ( isMeasure || foundM )
881 else
883 }
884 else
885 {
886 if ( isMeasure || foundM )
888 else
890 }
891
892 points.append( QgsPoint( t, x, y, z, m ) );
893 }
894
895 return points;
896}
897
898void QgsGeometryUtils::pointsToWKB( QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure, QgsAbstractGeometry::WkbFlags flags )
899{
900 wkb << static_cast<quint32>( points.size() );
901 for ( const QgsPoint &point : points )
902 {
903 wkb << point.x() << point.y();
904 if ( is3D )
905 {
906 double z = point.z();
908 && std::isnan( z ) )
909 z = -std::numeric_limits<double>::max();
910
911 wkb << z;
912 }
913 if ( isMeasure )
914 {
915 double m = point.m();
917 && std::isnan( m ) )
918 m = -std::numeric_limits<double>::max();
919
920 wkb << m;
921 }
922 }
923}
924
925QString QgsGeometryUtils::pointsToWKT( const QgsPointSequence &points, int precision, bool is3D, bool isMeasure )
926{
927 QString wkt = QStringLiteral( "(" );
928 for ( const QgsPoint &p : points )
929 {
930 wkt += qgsDoubleToString( p.x(), precision );
931 wkt += ' ' + qgsDoubleToString( p.y(), precision );
932 if ( is3D )
933 wkt += ' ' + qgsDoubleToString( p.z(), precision );
934 if ( isMeasure )
935 wkt += ' ' + qgsDoubleToString( p.m(), precision );
936 wkt += QLatin1String( ", " );
937 }
938 if ( wkt.endsWith( QLatin1String( ", " ) ) )
939 wkt.chop( 2 ); // Remove last ", "
940 wkt += ')';
941 return wkt;
942}
943
944QDomElement QgsGeometryUtils::pointsToGML2( const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, QgsAbstractGeometry::AxisOrder axisOrder )
945{
946 QDomElement elemCoordinates = doc.createElementNS( ns, QStringLiteral( "coordinates" ) );
947
948 // coordinate separator
949 const QString cs = QStringLiteral( "," );
950 // tuple separator
951 const QString ts = QStringLiteral( " " );
952
953 elemCoordinates.setAttribute( QStringLiteral( "cs" ), cs );
954 elemCoordinates.setAttribute( QStringLiteral( "ts" ), ts );
955
956 QString strCoordinates;
957
958 for ( const QgsPoint &p : points )
959 if ( axisOrder == QgsAbstractGeometry::AxisOrder::XY )
960 strCoordinates += qgsDoubleToString( p.x(), precision ) + cs + qgsDoubleToString( p.y(), precision ) + ts;
961 else
962 strCoordinates += qgsDoubleToString( p.y(), precision ) + cs + qgsDoubleToString( p.x(), precision ) + ts;
963
964 if ( strCoordinates.endsWith( ts ) )
965 strCoordinates.chop( 1 ); // Remove trailing space
966
967 elemCoordinates.appendChild( doc.createTextNode( strCoordinates ) );
968 return elemCoordinates;
969}
970
971QDomElement QgsGeometryUtils::pointsToGML3( const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D, QgsAbstractGeometry::AxisOrder axisOrder )
972{
973 QDomElement elemPosList = doc.createElementNS( ns, QStringLiteral( "posList" ) );
974 elemPosList.setAttribute( QStringLiteral( "srsDimension" ), is3D ? 3 : 2 );
975
976 QString strCoordinates;
977 for ( const QgsPoint &p : points )
978 {
979 if ( axisOrder == QgsAbstractGeometry::AxisOrder::XY )
980 strCoordinates += qgsDoubleToString( p.x(), precision ) + ' ' + qgsDoubleToString( p.y(), precision ) + ' ';
981 else
982 strCoordinates += qgsDoubleToString( p.y(), precision ) + ' ' + qgsDoubleToString( p.x(), precision ) + ' ';
983 if ( is3D )
984 strCoordinates += qgsDoubleToString( p.z(), precision ) + ' ';
985 }
986 if ( strCoordinates.endsWith( ' ' ) )
987 strCoordinates.chop( 1 ); // Remove trailing space
988
989 elemPosList.appendChild( doc.createTextNode( strCoordinates ) );
990 return elemPosList;
991}
992
993QString QgsGeometryUtils::pointsToJSON( const QgsPointSequence &points, int precision )
994{
995 QString json = QStringLiteral( "[ " );
996 for ( const QgsPoint &p : points )
997 {
998 json += '[' + qgsDoubleToString( p.x(), precision ) + QLatin1String( ", " ) + qgsDoubleToString( p.y(), precision ) + QLatin1String( "], " );
999 }
1000 if ( json.endsWith( QLatin1String( ", " ) ) )
1001 {
1002 json.chop( 2 ); // Remove last ", "
1003 }
1004 json += ']';
1005 return json;
1006}
1007
1008
1009json QgsGeometryUtils::pointsToJson( const QgsPointSequence &points, int precision )
1010{
1011 json coordinates( json::array() );
1012 for ( const QgsPoint &p : points )
1013 {
1014 if ( p.is3D() )
1015 {
1016 coordinates.push_back( { qgsRound( p.x(), precision ), qgsRound( p.y(), precision ), qgsRound( p.z(), precision ) } );
1017 }
1018 else
1019 {
1020 coordinates.push_back( { qgsRound( p.x(), precision ), qgsRound( p.y(), precision ) } );
1021 }
1022 }
1023 return coordinates;
1024}
1025
1026QPair<Qgis::WkbType, QString> QgsGeometryUtils::wktReadBlock( const QString &wkt )
1027{
1028 QString wktParsed = wkt;
1029 QString contents;
1030 bool isEmpty = false;
1031 const QLatin1String empty { "EMPTY" };
1032 if ( wkt.contains( empty, Qt::CaseInsensitive ) )
1033 {
1034 const thread_local QRegularExpression whiteSpaces( "\\s" );
1035 wktParsed.remove( whiteSpaces );
1036 const int index = wktParsed.indexOf( empty, 0, Qt::CaseInsensitive );
1037
1038 if ( index == wktParsed.length() - empty.size() )
1039 {
1040 // "EMPTY" found at the end of the QString
1041 // Extract the part of the QString to the left of "EMPTY"
1042 wktParsed = wktParsed.left( index );
1043 contents = empty;
1044 isEmpty = true;
1045 }
1046 else
1047 {
1048 wktParsed = wkt; // reset to original content
1049 }
1050 }
1051 if ( !isEmpty )
1052 {
1053 const int openedParenthesisCount = wktParsed.count( '(' );
1054 const int closedParenthesisCount = wktParsed.count( ')' );
1055 // closes missing parentheses
1056 for ( int i = 0 ; i < openedParenthesisCount - closedParenthesisCount; ++i )
1057 wktParsed.push_back( ')' );
1058 // removes extra parentheses
1059 wktParsed.truncate( wktParsed.size() - ( closedParenthesisCount - openedParenthesisCount ) );
1060
1061 const thread_local QRegularExpression cooRegEx( QStringLiteral( "^[^\\(]*\\((.*)\\)[^\\)]*$" ), QRegularExpression::DotMatchesEverythingOption );
1062 const QRegularExpressionMatch match = cooRegEx.match( wktParsed );
1063 contents = match.hasMatch() ? match.captured( 1 ) : QString();
1064 }
1065 const Qgis::WkbType wkbType = QgsWkbTypes::parseType( wktParsed );
1066 return qMakePair( wkbType, contents );
1067}
1068
1069QStringList QgsGeometryUtils::wktGetChildBlocks( const QString &wkt, const QString &defaultType )
1070{
1071 int level = 0;
1072 QString block;
1073 block.reserve( wkt.size() );
1074 QStringList blocks;
1075
1076 const QChar *wktData = wkt.data();
1077 const int wktLength = wkt.length();
1078 for ( int i = 0, n = wktLength; i < n; ++i, ++wktData )
1079 {
1080 if ( ( wktData->isSpace() || *wktData == '\n' || *wktData == '\t' ) && level == 0 )
1081 continue;
1082
1083 if ( *wktData == ',' && level == 0 )
1084 {
1085 if ( !block.isEmpty() )
1086 {
1087 if ( block.startsWith( '(' ) && !defaultType.isEmpty() )
1088 block.prepend( defaultType + ' ' );
1089 blocks.append( block );
1090 }
1091 block.resize( 0 );
1092 continue;
1093 }
1094 if ( *wktData == '(' )
1095 ++level;
1096 else if ( *wktData == ')' )
1097 --level;
1098 block += *wktData;
1099 }
1100 if ( !block.isEmpty() )
1101 {
1102 if ( block.startsWith( '(' ) && !defaultType.isEmpty() )
1103 block.prepend( defaultType + ' ' );
1104 blocks.append( block );
1105 }
1106 return blocks;
1107}
1108
1110{
1112
1113
1114 const double x = ( pt1.x() + pt2.x() ) / 2.0;
1115 const double y = ( pt1.y() + pt2.y() ) / 2.0;
1116 double z = std::numeric_limits<double>::quiet_NaN();
1117 double m = std::numeric_limits<double>::quiet_NaN();
1118
1119 if ( pt1.is3D() || pt2.is3D() )
1120 {
1121 pType = QgsWkbTypes::addZ( pType );
1122 z = ( pt1.z() + pt2.z() ) / 2.0;
1123 }
1124
1125 if ( pt1.isMeasure() || pt2.isMeasure() )
1126 {
1127 pType = QgsWkbTypes::addM( pType );
1128 m = ( pt1.m() + pt2.m() ) / 2.0;
1129 }
1130
1131 return QgsPoint( pType, x, y, z, m );
1132}
1133
1134QgsPoint QgsGeometryUtils::interpolatePointOnLine( const QgsPoint &p1, const QgsPoint &p2, const double fraction )
1135{
1136 const double _fraction = 1 - fraction;
1137 return QgsPoint( p1.wkbType(),
1138 p1.x() * _fraction + p2.x() * fraction,
1139 p1.y() * _fraction + p2.y() * fraction,
1140 p1.is3D() ? p1.z() * _fraction + p2.z() * fraction : std::numeric_limits<double>::quiet_NaN(),
1141 p1.isMeasure() ? p1.m() * _fraction + p2.m() * fraction : std::numeric_limits<double>::quiet_NaN() );
1142}
1143
1144QgsPointXY QgsGeometryUtils::interpolatePointOnLine( const double x1, const double y1, const double x2, const double y2, const double fraction )
1145{
1146 const double deltaX = ( x2 - x1 ) * fraction;
1147 const double deltaY = ( y2 - y1 ) * fraction;
1148 return QgsPointXY( x1 + deltaX, y1 + deltaY );
1149}
1150
1151QgsPointXY QgsGeometryUtils::interpolatePointOnLineByValue( const double x1, const double y1, const double v1, const double x2, const double y2, const double v2, const double value )
1152{
1153 if ( qgsDoubleNear( v1, v2 ) )
1154 return QgsPointXY( x1, y1 );
1155
1156 const double fraction = ( value - v1 ) / ( v2 - v1 );
1157 return interpolatePointOnLine( x1, y1, x2, y2, fraction );
1158}
1159
1160double QgsGeometryUtils::gradient( const QgsPoint &pt1, const QgsPoint &pt2 )
1161{
1162 const double delta_x = pt2.x() - pt1.x();
1163 const double delta_y = pt2.y() - pt1.y();
1164 if ( qgsDoubleNear( delta_x, 0.0 ) )
1165 {
1166 return INFINITY;
1167 }
1168
1169 return delta_y / delta_x;
1170}
1171
1172void QgsGeometryUtils::coefficients( const QgsPoint &pt1, const QgsPoint &pt2, double &a, double &b, double &c )
1173{
1174 if ( qgsDoubleNear( pt1.x(), pt2.x() ) )
1175 {
1176 a = 1;
1177 b = 0;
1178 c = -pt1.x();
1179 }
1180 else if ( qgsDoubleNear( pt1.y(), pt2.y() ) )
1181 {
1182 a = 0;
1183 b = 1;
1184 c = -pt1.y();
1185 }
1186 else
1187 {
1188 a = pt1.y() - pt2.y();
1189 b = pt2.x() - pt1.x();
1190 c = pt1.x() * pt2.y() - pt1.y() * pt2.x();
1191 }
1192
1193}
1194
1196{
1197 QgsLineString line;
1198 QgsPoint p2;
1199
1200 if ( ( p == s1 ) || ( p == s2 ) )
1201 {
1202 return line;
1203 }
1204
1205 double a, b, c;
1206 coefficients( s1, s2, a, b, c );
1207
1208 if ( qgsDoubleNear( a, 0 ) )
1209 {
1210 p2 = QgsPoint( p.x(), s1.y() );
1211 }
1212 else if ( qgsDoubleNear( b, 0 ) )
1213 {
1214 p2 = QgsPoint( s1.x(), p.y() );
1215 }
1216 else
1217 {
1218 const double y = ( -c - a * p.x() ) / b;
1219 const double m = gradient( s1, s2 );
1220 const double d2 = 1 + m * m;
1221 const double H = p.y() - y;
1222 const double dx = m * H / d2;
1223 const double dy = m * dx;
1224 p2 = QgsPoint( p.x() + dx, y + dy );
1225 }
1226
1227 line.addVertex( p );
1228 line.addVertex( p2 );
1229
1230 return line;
1231}
1232
1234{
1235 bool rc = false;
1236
1237 for ( const QgsPoint &pt : points )
1238 {
1239 if ( pt.isMeasure() )
1240 {
1241 point.convertTo( QgsWkbTypes::addM( point.wkbType() ) );
1242 point.setM( pt.m() );
1243 rc = true;
1244 break;
1245 }
1246 }
1247
1248 return rc;
1249}
1250
1252{
1253 bool rc = false;
1254
1255 for ( const QgsPoint &pt : points )
1256 {
1257 if ( pt.is3D() )
1258 {
1259 point.convertTo( QgsWkbTypes::addZ( point.wkbType() ) );
1260 point.setZ( pt.z() );
1261 rc = true;
1262 break;
1263 }
1264 }
1265
1266 return rc;
1267}
1268
1270{
1271 if ( reference.is3D() && reference.isMeasure() )
1272 return QgsPoint( Qgis::WkbType::PointZM, x, y, 0.0, 0.0 );
1273 else if ( reference.is3D() )
1274 return QgsPoint( Qgis::WkbType::PointZ, x, y, 0.0 );
1275 else if ( reference.isMeasure() )
1276 return QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, 0.0 );
1277 else
1278 return QgsPoint( x, y );
1279}
1280
1282 const QgsPoint &segmentStart, const QgsPoint &segmentEnd )
1283{
1284 QgsPoint result = createPointWithMatchingDimensions( x, y, segmentStart );
1285 const double distanceFromStart = QgsGeometryUtilsBase::distance2D( segmentStart.x(), segmentStart.y(), x, y );
1286
1287 if ( segmentStart.is3D() && segmentEnd.is3D() )
1288 {
1289 double z1 = segmentStart.z();
1290 double z2 = segmentEnd.z();
1291 double interpolatedZ;
1292 double tempX, tempY;
1294 segmentStart.x(), segmentStart.y(), segmentEnd.x(), segmentEnd.y(),
1295 distanceFromStart, tempX, tempY, &z1, &z2, &interpolatedZ );
1296 result.setZ( interpolatedZ );
1297 }
1298
1299 if ( segmentStart.isMeasure() && segmentEnd.isMeasure() )
1300 {
1301 double m1 = segmentStart.m();
1302 double m2 = segmentEnd.m();
1303 double interpolatedM;
1304 double tempX, tempY;
1306 segmentStart.x(), segmentStart.y(), segmentEnd.x(), segmentEnd.y(),
1307 distanceFromStart, tempX, tempY, nullptr, nullptr, nullptr, &m1, &m2, &interpolatedM );
1308 result.setM( interpolatedM );
1309 }
1310
1311 return result;
1312}
1313
1314bool QgsGeometryUtils::createChamfer( const QgsPoint &segment1Start, const QgsPoint &segment1End,
1315 const QgsPoint &segment2Start, const QgsPoint &segment2End,
1316 double distance1, double distance2,
1317 QgsPoint &chamferStart, QgsPoint &chamferEnd,
1318 double epsilon )
1319{
1320 // Create chamfer points using the utility function
1321 double chamferStartX, chamferStartY, chamferEndX, chamferEndY;
1322
1324 segment1Start.x(), segment1Start.y(), segment1End.x(), segment1End.y(),
1325 segment2Start.x(), segment2Start.y(), segment2End.x(), segment2End.y(),
1326 distance1, distance2,
1327 chamferStartX, chamferStartY,
1328 chamferEndX, chamferEndY,
1329 nullptr, nullptr, nullptr, nullptr,
1330 nullptr, nullptr, nullptr, nullptr,
1331 epsilon
1332 );
1333
1334 chamferStart = interpolatePointOnSegment( chamferStartX, chamferStartY, segment1Start, segment1End );
1335 chamferEnd = interpolatePointOnSegment( chamferEndX, chamferEndY, segment2Start, segment2End );
1336
1337 return true;
1338}
1339
1340bool QgsGeometryUtils::createFillet( const QgsPoint &segment1Start, const QgsPoint &segment1End,
1341 const QgsPoint &segment2Start, const QgsPoint &segment2End,
1342 double radius,
1343 QgsPoint &filletPoint1,
1344 QgsPoint &filletMidPoint,
1345 QgsPoint &filletPoint2,
1346 double epsilon )
1347{
1348 // Create fillet arc using the utility function
1349 double filletPointsX[3], filletPointsY[3];
1350
1352 segment1Start.x(), segment1Start.y(), segment1End.x(), segment1End.y(),
1353 segment2Start.x(), segment2Start.y(), segment2End.x(), segment2End.y(),
1354 radius,
1355 filletPointsX, filletPointsY,
1356 nullptr, nullptr, nullptr, nullptr,
1357 nullptr, nullptr, nullptr, nullptr,
1358 epsilon
1359 );
1360
1361 filletPoint1 = interpolatePointOnSegment( filletPointsX[0], filletPointsY[0], segment1Start, segment1End );
1362 filletMidPoint = createPointWithMatchingDimensions( filletPointsX[1], filletPointsY[1], segment1Start );
1363 filletPoint2 = interpolatePointOnSegment( filletPointsX[2], filletPointsY[2], segment2Start, segment2End );
1364
1365 // Interpolate Z and M for midpoint
1366 if ( segment1Start.is3D() && segment1End.is3D() && segment2Start.is3D() && segment2End.is3D() )
1367 {
1368 filletMidPoint.setZ( ( filletPoint1.z() + filletPoint2.z() ) / 2.0 );
1369 }
1370 if ( segment1Start.isMeasure() && segment1End.isMeasure() && segment2Start.isMeasure() && segment2End.isMeasure() )
1371 {
1372 filletMidPoint.setM( ( filletPoint1.m() + filletPoint2.m() ) / 2.0 );
1373 }
1374
1375 return true;
1376}
1377
1378bool QgsGeometryUtils::createFilletArray( const QgsPoint &segment1Start, const QgsPoint &segment1End,
1379 const QgsPoint &segment2Start, const QgsPoint &segment2End,
1380 double radius,
1381 QgsPoint filletPoints[3],
1382 double epsilon )
1383{
1384 QgsPoint p1, p2, p3;
1385 createFillet( segment1Start, segment1End, segment2Start, segment2End, radius, p1, p2, p3, epsilon );
1386 filletPoints[0] = p1;
1387 filletPoints[1] = p2;
1388 filletPoints[2] = p3;
1389 return true;
1390}
1391
1392std::unique_ptr<QgsLineString> QgsGeometryUtils::createChamferGeometry(
1393 const QgsPoint &segment1Start, const QgsPoint &segment1End,
1394 const QgsPoint &segment2Start, const QgsPoint &segment2End,
1395 double distance1, double distance2 )
1396{
1397 QgsPoint chamferStart, chamferEnd;
1398 createChamfer( segment1Start, segment1End, segment2Start, segment2End, distance1, distance2, chamferStart, chamferEnd );
1399
1400 return std::make_unique<QgsLineString>(
1401 QVector<QgsPoint> { segment1Start, chamferStart, chamferEnd, segment2Start } );
1402}
1403
1404std::unique_ptr<QgsAbstractGeometry> QgsGeometryUtils::createFilletGeometry(
1405 const QgsPoint &segment1Start, const QgsPoint &segment1End,
1406 const QgsPoint &segment2Start, const QgsPoint &segment2End,
1407 double radius, int segments )
1408{
1409 QgsPoint filletPoints[3];
1410 createFilletArray( segment1Start, segment1End, segment2Start, segment2End, radius, filletPoints );
1411
1412 // Calculate far endpoints for complete geometry
1413 double intersectionX, intersectionY;
1414 bool isIntersection;
1416 segment1Start.x(), segment1Start.y(), segment1End.x(), segment1End.y(),
1417 segment2Start.x(), segment2Start.y(), segment2End.x(), segment2End.y(),
1418 intersectionX, intersectionY, isIntersection, 1e-8, true );
1419
1420 if ( !isIntersection )
1421 {
1422 throw QgsInvalidArgumentException( "Segments do not intersect." );
1423 }
1424
1425 const double dist1ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1Start.x(), segment1Start.y() );
1426 const double dist1ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1End.x(), segment1End.y() );
1427 const double dist2ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2Start.x(), segment2Start.y() );
1428 const double dist2ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2End.x(), segment2End.y() );
1429
1430 const QgsPoint segment1FarEnd = ( dist1ToStart < dist1ToEnd ) ? segment1End : segment1Start;
1431 const QgsPoint segment2FarEnd = ( dist2ToStart < dist2ToEnd ) ? segment2End : segment2Start;
1432
1433 if ( segments <= 0 )
1434 {
1435 // Return CompoundCurve with circular arc
1436 auto completeCurve = std::make_unique<QgsCompoundCurve>();
1437
1438 // First linear segment
1439 auto firstSegment = std::make_unique<QgsLineString>(
1440 QVector<QgsPoint> { segment1FarEnd, filletPoints[0] } );
1441 completeCurve->addCurve( firstSegment.release() );
1442
1443 // Circular arc segment
1444 auto circularString = std::make_unique<QgsCircularString>();
1445 circularString->setPoints( {filletPoints[0], filletPoints[1], filletPoints[2]} );
1446 completeCurve->addCurve( circularString.release() );
1447
1448 // Last linear segment
1449 auto lastSegment = std::make_unique<QgsLineString>(
1450 QVector<QgsPoint> { filletPoints[2], segment2FarEnd } );
1451 completeCurve->addCurve( lastSegment.release() );
1452
1453 return completeCurve;
1454 }
1455 else
1456 {
1457 // Return segmented LineString
1458 QVector<QgsPoint> points;
1459 points.append( segment1FarEnd );
1460
1461 // Convert circular arc to line segments with specified number of segments
1462 QgsCircularString tempArc;
1463 tempArc.setPoints( {filletPoints[0], filletPoints[1], filletPoints[2]} );
1464
1465 // Calculate appropriate tolerance based on desired number of segments
1466 // For segments > 0: use angle tolerance = 2*PI / (4 * segments) to get approximately 'segments' segments for a quarter circle
1467 const double angleTolerance = ( 2.0 * M_PI ) / ( 4.0 * segments );
1468
1469 std::unique_ptr<QgsLineString> segmentizedArc( tempArc.curveToLine( angleTolerance, QgsAbstractGeometry::MaximumAngle ) );
1470
1471 for ( int i = 0; i < segmentizedArc->numPoints(); ++i )
1472 {
1473 points.append( segmentizedArc->vertexAt( QgsVertexId( 0, 0, i ) ) );
1474 }
1475
1476 points.append( segment2FarEnd );
1477
1478 return std::make_unique<QgsLineString>( points );
1479 }
1480}
1481
1482double QgsGeometryUtils::maxFilletRadius( const QgsPoint &segment1Start, const QgsPoint &segment1End,
1483 const QgsPoint &segment2Start, const QgsPoint &segment2End,
1484 double epsilon )
1485{
1486 return QgsGeometryUtilsBase::maxFilletRadius( segment1Start.x(), segment1Start.y(), segment1End.x(), segment1End.y(), segment2Start.x(), segment2Start.y(), segment2End.x(), segment2End.y(), epsilon );
1487}
1488
1489std::unique_ptr<QgsAbstractGeometry> QgsGeometryUtils::chamferVertex(
1490 const QgsCurve *curve, int vertexIndex,
1491 double distance1, double distance2 )
1492{
1493 return doChamferFilletOnVertex( QgsGeometry::ChamferFilletOperationType::Chamfer, curve, vertexIndex, distance1, distance2, 0 );
1494}
1495
1496std::unique_ptr<QgsAbstractGeometry> QgsGeometryUtils::filletVertex(
1497 const QgsCurve *curve, int vertexIndex,
1498 double radius, int segments )
1499{
1500 return doChamferFilletOnVertex( QgsGeometry::ChamferFilletOperationType::Fillet, curve, vertexIndex, radius, 0.0, segments );
1501}
1502
1503std::unique_ptr< QgsAbstractGeometry > QgsGeometryUtils::doChamferFilletOnVertex(
1504 QgsGeometry::ChamferFilletOperationType operation, const QgsCurve *curve, int vertexIndex,
1505 double value1, double value2, int segments
1506)
1507{
1508 if ( !curve )
1509 throw QgsInvalidArgumentException( "Curve is null." );
1510 if ( curve->isClosed() )
1511 {
1512 if ( curve->numPoints() < 4 )
1513 throw QgsInvalidArgumentException( "Closed curve must have at least 4 vertex." );
1514 if ( vertexIndex < 0 || vertexIndex > curve->numPoints() - 1 )
1515 throw QgsInvalidArgumentException( QStringLiteral( "Vertex index out of range. %1 must be in [0, %2]." ).arg( vertexIndex ).arg( curve->numPoints() - 1 ) );
1516 }
1517 else
1518 {
1519 if ( curve->numPoints() < 3 )
1520 throw QgsInvalidArgumentException( "Opened curve must have at least 3 points." );
1521 if ( vertexIndex <= 0 || vertexIndex >= curve->numPoints() - 1 )
1522 throw QgsInvalidArgumentException( QStringLiteral( "Vertex index out of range. %1 must be in (0, %2)." ).arg( vertexIndex ).arg( curve->numPoints() - 1 ) );
1523 }
1524
1525 // Extract the three consecutive vertices
1526 QgsPoint pPrev = curve->vertexAt( QgsVertexId( 0, 0, vertexIndex - 1 ) );
1527 const QgsPoint p = curve->vertexAt( QgsVertexId( 0, 0, vertexIndex ) );
1528 QgsPoint pNext = curve->vertexAt( QgsVertexId( 0, 0, vertexIndex + 1 ) );
1529 if ( curve->isClosed() )
1530 {
1531 if ( vertexIndex - 1 < 0 )
1532 pPrev = curve->vertexAt( QgsVertexId( 0, 0, curve->numPoints() - 2 ) );
1533 if ( vertexIndex + 1 >= curve->numPoints() )
1534 pNext = curve->vertexAt( QgsVertexId( 0, 0, 1 ) );
1535 }
1536
1537 QgsPoint firstNewPoint, middlePoint, lastNewPoint;
1538
1540 {
1541 double rad = std::min( value1, pPrev.distance( p ) * 0.95 );
1542 rad = std::min( rad, pNext.distance( p ) * 0.95 );
1543
1544 // Create fillet
1545 QgsPoint filletPoints[3];
1546 createFilletArray( pPrev, p, p, pNext, rad, filletPoints );
1547
1548 firstNewPoint = filletPoints[0];
1549 middlePoint = filletPoints[1];
1550 lastNewPoint = filletPoints[2];
1551 }
1553 {
1554 // Create chamfer
1555 createChamfer( pPrev, p, p, pNext, value1, value2, firstNewPoint, lastNewPoint );
1556 }
1557 else
1558 throw QgsInvalidArgumentException( QStringLiteral( "Operation '%1' is unknown." ).arg( qgsEnumValueToKey( operation ) ) );
1559
1560 // Handle LineString geometries
1562 {
1563 QVector<QgsPoint> points;
1564
1565 int min = 0;
1566 if ( curve->isClosed() && vertexIndex == curve->numPoints() - 1 )
1567 min = 1;
1568
1569 // Add points before the operated vertex
1570 for ( int i = min; i < vertexIndex; ++i )
1571 {
1572 points.append( curve->vertexAt( QgsVertexId( 0, 0, i ) ) );
1573 }
1574
1576 {
1577 if ( firstNewPoint != pPrev )
1578 points.append( firstNewPoint );
1579
1580 if ( segments > 0 )
1581 {
1582 QgsCircularString tempArc;
1583 tempArc.setPoints( { firstNewPoint, middlePoint, lastNewPoint } );
1584
1585 const double angleTolerance = ( 2.0 * M_PI ) / ( 4.0 * segments );
1586 std::unique_ptr<QgsLineString> segmentizedArc( tempArc.curveToLine( angleTolerance, QgsAbstractGeometry::MaximumAngle ) );
1587
1588 for ( int i = 1; i < segmentizedArc->numPoints() - 1; ++i )
1589 {
1590 points.append( segmentizedArc->vertexAt( QgsVertexId( 0, 0, i ) ) );
1591 }
1592 }
1593 else
1594 {
1595 points.append( middlePoint );
1596 }
1597
1598 if ( lastNewPoint != pNext )
1599 points.append( lastNewPoint );
1600 }
1601 else
1602 {
1603 if ( firstNewPoint != pPrev )
1604 points.append( firstNewPoint );
1605 if ( lastNewPoint != pNext )
1606 points.append( lastNewPoint );
1607 }
1608
1609 int max = curve->numPoints();
1610 if ( curve->isClosed() && vertexIndex == 0 )
1611 max = curve->numPoints() - 1;
1612
1613 for ( int i = vertexIndex + 1; i < max; ++i )
1614 {
1615 points.append( curve->vertexAt( QgsVertexId( 0, 0, i ) ) );
1616 }
1617
1618 return std::make_unique<QgsLineString>( points );
1619 }
1620
1621 if ( const QgsCompoundCurve *compound = qgsgeometry_cast<const QgsCompoundCurve *>( curve ) )
1622 {
1623 auto newCompound = std::make_unique<QgsCompoundCurve>();
1624
1625 int globalVertexIndex = 0;
1626 int targetCurveIndex = -1;
1627 int vertexInCurve = -1;
1628
1629 for ( int curveIdx = 0; curveIdx < compound->nCurves(); ++curveIdx )
1630 {
1631 const QgsCurve *subcurve = compound->curveAt( curveIdx );
1632 const int subcurvePoints = subcurve->numPoints();
1633
1634 if ( globalVertexIndex + subcurvePoints > vertexIndex )
1635 {
1636 targetCurveIndex = curveIdx;
1637 vertexInCurve = vertexIndex - globalVertexIndex;
1638 break;
1639 }
1640 globalVertexIndex += subcurvePoints - 1;
1641 }
1642
1643 if ( targetCurveIndex == -1 )
1644 {
1645 throw QgsInvalidArgumentException( "While generating output: unable to find curve within compound." );
1646 }
1647
1648 const QgsCurve *targetCurve = compound->curveAt( targetCurveIndex );
1649
1650 // Add curves before the target curve
1651 for ( int i = 0; i < targetCurveIndex; ++i )
1652 {
1653 std::unique_ptr<QgsCurve> tmpCurv( compound->curveAt( i )->clone() );
1654 if ( curve->isClosed() && vertexIndex == curve->numPoints() - 1 )
1655 {
1656 tmpCurv->insertVertex( QgsVertexId( 0, 0, 1 ), lastNewPoint );
1657 tmpCurv->deleteVertex( QgsVertexId( 0, 0, 0 ) );
1658 }
1659 newCompound->addCurve( tmpCurv.release() );
1660 }
1661
1662 // Handle the curve containing the vertex
1663 if ( vertexInCurve > 0 )
1664 {
1665 QVector<QgsPoint> beforePoints;
1666 for ( int j = 0; j < vertexInCurve; ++j )
1667 {
1668 beforePoints.append( targetCurve->vertexAt( QgsVertexId( 0, 0, j ) ) );
1669 }
1670 beforePoints.append( firstNewPoint );
1671
1672 if ( beforePoints.size() > 1 )
1673 {
1674 auto beforeVertex = std::make_unique<QgsLineString>( beforePoints );
1675 newCompound->addCurve( beforeVertex.release() );
1676 }
1677 }
1678
1680 {
1681 // Add the fillet arc - for CompoundCurve, preserve circular nature unless segments > 0
1682 if ( segments <= 0 )
1683 {
1684 // Preserve circular arc
1685 auto filletArc = std::make_unique<QgsCircularString>();
1686 filletArc->setPoints( { firstNewPoint, middlePoint, lastNewPoint } );
1687 newCompound->addCurve( filletArc.release() );
1688 }
1689 else
1690 {
1691 // Segmentize the arc
1692 QgsCircularString tempArc;
1693 tempArc.setPoints( { firstNewPoint, middlePoint, lastNewPoint } );
1694
1695 const double angleTolerance = ( 2.0 * M_PI ) / ( 4.0 * segments );
1696 std::unique_ptr<QgsLineString> segmentizedArc( tempArc.curveToLine( angleTolerance, QgsAbstractGeometry::MaximumAngle ) );
1697
1698 newCompound->addCurve( segmentizedArc.release() );
1699 }
1700 }
1701 else
1702 {
1703 auto chamferLine = std::make_unique<QgsLineString>(
1704 QVector<QgsPoint> { firstNewPoint, lastNewPoint }
1705 );
1706 newCompound->addCurve( chamferLine.release() );
1707 }
1708
1709 if ( vertexInCurve < targetCurve->numPoints() - 1 )
1710 {
1711 QVector<QgsPoint> afterPoints;
1712 afterPoints.append( lastNewPoint );
1713 for ( int j = vertexInCurve + 1; j < targetCurve->numPoints(); ++j )
1714 {
1715 afterPoints.append( targetCurve->vertexAt( QgsVertexId( 0, 0, j ) ) );
1716 }
1717
1718 if ( afterPoints.size() > 1 )
1719 {
1720 auto afterVertex = std::make_unique<QgsLineString>( afterPoints );
1721 newCompound->addCurve( afterVertex.release() );
1722 }
1723 }
1724
1725 // Add curves after the target curve
1726 for ( int i = targetCurveIndex + 1; i < compound->nCurves(); ++i )
1727 {
1728 std::unique_ptr<QgsCurve> tmpCurv( compound->curveAt( i )->clone() );
1729 if ( curve->isClosed() && vertexIndex == 0 )
1730 {
1731 tmpCurv->insertVertex( QgsVertexId( 0, 0, tmpCurv->numPoints() - 1 ), firstNewPoint );
1732 tmpCurv->deleteVertex( QgsVertexId( 0, 0, tmpCurv->numPoints() - 1 ) );
1733 }
1734 newCompound->addCurve( tmpCurv.release() );
1735 }
1736
1737 return newCompound;
1738 }
1739
1740 throw QgsInvalidArgumentException( "While generating output: curse is not a QgsLineString nor a QgsCompoundCurve." );
1741}
static const double DEFAULT_SEGMENT_EPSILON
Default snapping tolerance for segments.
Definition qgis.h:6246
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:277
@ Point
Point.
Definition qgis.h:279
@ PointM
PointM.
Definition qgis.h:310
@ PointZ
PointZ.
Definition qgis.h:295
@ PointZM
PointZM.
Definition qgis.h:325
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...
@ MaximumAngle
Maximum angle between generating radii (lines from arc center to output vertices).
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.
QFlags< WkbFlag > WkbFlags
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.
Qgis::WkbType 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.
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.
Circular string geometry type.
void setPoints(const QgsPointSequence &points)
Sets the circular string's points.
QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a new line string geometry corresponding to a segmentized approximation of the curve.
Curve polygon geometry type.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
virtual int numPoints() const =0
Returns the number of points in the curve.
virtual bool isClosed() const
Returns true if the curve is closed.
Definition qgscurve.cpp:54
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
Definition qgscurve.cpp:199
static void pointOnLineWithDistance(double x1, double y1, double x2, double y2, double distance, double &x, double &y, double *z1=nullptr, double *z2=nullptr, double *z=nullptr, double *m1=nullptr, double *m2=nullptr, double *m=nullptr)
Calculates the point a specified distance from (x1, y1) toward a second point (x2,...
static double ccwAngle(double dy, double dx)
Returns the counter clockwise angle between a line with components dx, dy and the line with dx > 0 an...
static bool circleClockwise(double angle1, double angle2, double angle3)
Returns true if the circle defined by three angles is ordered clockwise.
static double distance2D(double x1, double y1, double x2, double y2)
Returns the 2D distance between (x1, y1) and (x2, y2).
static bool createChamfer(const double segment1StartX, const double segment1StartY, const double segment1EndX, const double segment1EndY, const double segment2StartX, const double segment2StartY, const double segment2EndX, const double segment2EndY, const double distance1, const double distance2, double &chamferStartX, double &chamferStartY, double &chamferEndX, double &chamferEndY, double *trim1StartX=nullptr, double *trim1StartY=nullptr, double *trim1EndX=nullptr, double *trim1EndY=nullptr, double *trim2StartX=nullptr, double *trim2StartY=nullptr, double *trim2EndX=nullptr, double *trim2EndY=nullptr, const double epsilon=1e-8)
Creates a chamfer (angled corner) between two line segments.
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 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 double interpolateArcValue(double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3)
Interpolate a value at given angle on circular arc given values (zm1, zm2, zm3) at three different an...
static double maxFilletRadius(const double segment1StartX, const double segment1StartY, const double segment1EndX, const double segment1EndY, const double segment2StartX, const double segment2StartY, const double segment2EndX, const double segment2EndY, double epsilon=1e-8)
Calculates the maximum allowed fillet radius for the given segment configuration.
static bool segmentIntersection(double p1x, double p1y, double p2x, double p2y, double q1x, double q1y, double q2x, double q2y, double &intersectionPointX, double &intersectionPointY, bool &isIntersection, double tolerance=1e-8, bool acceptImproperIntersection=false)
Compute the intersection between two segments.
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 bool createFillet(const double segment1StartX, const double segment1StartY, const double segment1EndX, const double segment1EndY, const double segment2StartX, const double segment2StartY, const double segment2EndX, const double segment2EndY, const double radius, double *filletPointsX, double *filletPointsY, double *trim1StartX=nullptr, double *trim1StartY=nullptr, double *trim1EndX=nullptr, double *trim1EndY=nullptr, double *trim2StartX=nullptr, double *trim2StartY=nullptr, double *trim2EndX=nullptr, double *trim2EndY=nullptr, const double epsilon=1e-8)
Creates a fillet (rounded corner) between two line segments.
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 std::unique_ptr< QgsLineString > createChamferGeometry(const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double distance1, double distance2)
Creates a complete chamfer geometry connecting two segments.
static QString pointsToJSON(const QgsPointSequence &points, int precision)
Returns a geoJSON coordinates string.
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 int leftOfLine(const QgsPoint &point, const QgsPoint &p1, const QgsPoint &p2)
Returns a value < 0 if the point point is left of the line from p1 -> p2.
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 maxFilletRadius(const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double epsilon=1e-8)
Calculates the maximum allowed fillet radius for the given segment configuration.
static QgsPoint createPointWithMatchingDimensions(double x, double y, const QgsPoint &reference)
Creates a QgsPoint with dimensions matching a reference point.
static QVector< QgsLineString * > extractLineStrings(const QgsAbstractGeometry *geom)
Returns list of linestrings extracted from the passed geometry.
static std::unique_ptr< QgsAbstractGeometry > createFilletGeometry(const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double radius, int segments)
Creates a complete fillet geometry connecting two segments.
static QgsPoint interpolatePointOnSegment(double x, double y, const QgsPoint &segmentStart, const QgsPoint &segmentEnd)
Interpolates a point on a segment with proper Z and M value interpolation.
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 QgsPoint closestPoint(const QgsAbstractGeometry &geometry, const QgsPoint &point)
Returns the nearest point on a segment of a geometry for the specified point.
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 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 QgsLineString perpendicularSegment(const QgsPoint &p, const QgsPoint &s1, const QgsPoint &s2)
Create a perpendicular line segment from p to segment [s1, s2].
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 pt2.
static void pointsToWKB(QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure, QgsAbstractGeometry::WkbFlags flags)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
static bool createChamfer(const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double distance1, double distance2, QgsPoint &chamferStart, QgsPoint &chamferEnd, double epsilon=1e-8)
Creates a chamfer between two line segments using QgsPoint.
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 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 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.
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 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 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 bool createFillet(const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double radius, QgsPoint &filletPoint1, QgsPoint &filletMidPoint, QgsPoint &filletPoint2, double epsilon=1e-8)
Creates a fillet (rounded corner) between two line segments using QgsPoint.
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 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 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 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 Q_DECL_DEPRECATED double sqrDistance2D(double x1, double y1, double x2, double y2)
Returns the squared 2D distance between (x1, y1) and (x2, y2).
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 std::unique_ptr< QgsAbstractGeometry > chamferVertex(const QgsCurve *curve, int vertexIndex, double distance1, double distance2)
Applies chamfer to a vertex in a curve geometry.
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 createFilletArray(const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double radius, QgsPoint filletPoints[3], double epsilon=1e-8)
Convenient method of createFillet using array output.
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 std::unique_ptr< QgsAbstractGeometry > filletVertex(const QgsCurve *curve, int vertexIndex, double radius, int segments)
Applies fillet to a vertex in a curve geometry.
ChamferFilletOperationType
Privatly used in chamfer/fillet functions.
Custom exception class when argument are invalid.
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.
Represents a 2D point.
Definition qgspointxy.h:60
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
Definition qgspointxy.h:186
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Definition qgspointxy.h:206
void set(double x, double y)
Sets the x and y value of the point.
Definition qgspointxy.h:136
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
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:365
bool convertTo(Qgis::WkbType type) override
Converts the geometry to a specified type.
Definition qgspoint.cpp:630
bool isEmpty() const override
Returns true if the geometry is empty.
Definition qgspoint.cpp:739
double distance(double x, double y) const
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition qgspoint.h:387
void setZ(double z)
Sets the point's z-coordinate.
Definition qgspoint.h:350
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:707
double y
Definition qgspoint.h:53
Represent a 2-dimensional vector.
Definition qgsvector.h:31
double length() const
Returns the length of the vector.
Definition qgsvector.h:125
WKB pointer handler.
Definition qgswkbptr.h:45
static Qgis::WkbType parseType(const QString &wktStr)
Attempts to extract the WKB type from a WKT string.
static Qgis::WkbType addM(Qgis::WkbType type)
Adds the m dimension to a WKB type and returns the new type.
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
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:6524
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:6798
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:6648
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsPoint > QgsPointSequence
bool isClockwise(std::array< Direction, 4 > dirs)
Checks whether the 4 directions in dirs make up a clockwise rectangle.
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:30
int vertex
Vertex number.
Definition qgsvertexid.h:94
bool isValid() const
Returns true if the vertex id is valid.
Definition qgsvertexid.h:45
int part
Part number.
Definition qgsvertexid.h:88
Qgis::VertexType type
Vertex type.
Definition qgsvertexid.h:97
int ring
Ring number.
Definition qgsvertexid.h:91