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