QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsgeometryutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgeometryutils.cpp
3-------------------------------------------------------------------
4Date : 21 Nov 2014
5Copyright : (C) 2014 by Marco Hugentobler
6email : marco.hugentobler at sourcepole dot com
7***************************************************************************
8* *
9* This program is free software; you can redistribute it and/or modify *
10* it under the terms of the GNU General Public License as published by *
11* the Free Software Foundation; either version 2 of the License, or *
12* (at your option) any later version. *
13* *
14***************************************************************************/
15
16#include "qgsgeometryutils.h"
17
18#include "qgscurve.h"
19#include "qgscurvepolygon.h"
21#include "qgslinestring.h"
22#include "qgswkbptr.h"
23
24#include <memory>
25#include <QStringList>
26#include <QVector>
27#include <QRegularExpression>
28#include <nlohmann/json.hpp>
29
30QVector<QgsLineString *> QgsGeometryUtils::extractLineStrings( const QgsAbstractGeometry *geom )
31{
32 QVector< QgsLineString * > linestrings;
33 if ( !geom )
34 return linestrings;
35
36 QVector< const QgsAbstractGeometry * > geometries;
37 geometries << geom;
38 while ( ! geometries.isEmpty() )
39 {
40 const QgsAbstractGeometry *g = geometries.takeFirst();
41 if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( g ) )
42 {
43 linestrings << static_cast< QgsLineString * >( curve->segmentize() );
44 }
45 else if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g ) )
46 {
47 for ( int i = 0; i < collection->numGeometries(); ++i )
48 {
49 geometries.append( collection->geometryN( i ) );
50 }
51 }
52 else if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( g ) )
53 {
54 if ( curvePolygon->exteriorRing() )
55 linestrings << static_cast< QgsLineString * >( curvePolygon->exteriorRing()->segmentize() );
56
57 for ( int i = 0; i < curvePolygon->numInteriorRings(); ++i )
58 {
59 linestrings << static_cast< QgsLineString * >( curvePolygon->interiorRing( i )->segmentize() );
60 }
61 }
62 }
63 return linestrings;
64}
65
67{
68 double minDist = std::numeric_limits<double>::max();
69 double currentDist = 0;
70 QgsPoint minDistPoint;
71 id = QgsVertexId(); // set as invalid
72
73 if ( geom.isEmpty() || pt.isEmpty() )
74 return minDistPoint;
75
76 QgsVertexId vertexId;
77 QgsPoint vertex;
78 while ( geom.nextVertex( vertexId, vertex ) )
79 {
80 currentDist = QgsGeometryUtils::sqrDistance2D( pt, vertex );
81 // The <= is on purpose: for geometries with closing vertices, this ensures
82 // that the closing vertex is returned. For the vertex tool, the rubberband
83 // of the closing vertex is above the opening vertex, hence with the <=
84 // situations where the covered opening vertex rubberband is selected are
85 // avoided.
86 if ( currentDist <= minDist )
87 {
88 minDist = currentDist;
89 minDistPoint = vertex;
90 id.part = vertexId.part;
91 id.ring = vertexId.ring;
92 id.vertex = vertexId.vertex;
93 id.type = vertexId.type;
94 }
95 }
96
97 return minDistPoint;
98}
99
101{
103 QgsVertexId vertexAfter;
104 geometry.closestSegment( point, closestPoint, vertexAfter, nullptr, DEFAULT_SEGMENT_EPSILON );
105 if ( vertexAfter.isValid() )
106 {
107 const QgsPoint pointAfter = geometry.vertexAt( vertexAfter );
108 if ( vertexAfter.vertex > 0 )
109 {
110 QgsVertexId vertexBefore = vertexAfter;
111 vertexBefore.vertex--;
112 const QgsPoint pointBefore = geometry.vertexAt( vertexBefore );
113 const double length = pointBefore.distance( pointAfter );
114 const double distance = pointBefore.distance( closestPoint );
115
116 if ( qgsDoubleNear( distance, 0.0 ) )
117 closestPoint = pointBefore;
118 else if ( qgsDoubleNear( distance, length ) )
119 closestPoint = pointAfter;
120 else
121 {
122 if ( QgsWkbTypes::hasZ( geometry.wkbType() ) && length )
123 closestPoint.addZValue( pointBefore.z() + ( pointAfter.z() - pointBefore.z() ) * distance / length );
124 if ( QgsWkbTypes::hasM( geometry.wkbType() ) )
125 closestPoint.addMValue( pointBefore.m() + ( pointAfter.m() - pointBefore.m() ) * distance / length );
126 }
127 }
128 }
129
130 return closestPoint;
131}
132
134{
135 double currentDist = 0;
136 QgsVertexId vertexId;
137 QgsPoint vertex;
138 while ( geom.nextVertex( vertexId, vertex ) )
139 {
140 if ( vertexId == id )
141 {
142 //found target vertex
143 return currentDist;
144 }
145 currentDist += geom.segmentLength( vertexId );
146 }
147
148 //could not find target vertex
149 return -1;
150}
151
152bool QgsGeometryUtils::verticesAtDistance( const QgsAbstractGeometry &geometry, double distance, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
153{
154 double currentDist = 0;
155 previousVertex = QgsVertexId();
156 nextVertex = QgsVertexId();
157
158 if ( geometry.isEmpty() )
159 {
160 return false;
161 }
162
163 QgsPoint point;
164 QgsPoint previousPoint;
165
166 if ( qgsDoubleNear( distance, 0.0 ) )
167 {
168 geometry.nextVertex( previousVertex, point );
169 nextVertex = previousVertex;
170 return true;
171 }
172
173 bool first = true;
174 while ( currentDist < distance && geometry.nextVertex( nextVertex, point ) )
175 {
176 if ( !first && nextVertex.part == previousVertex.part && nextVertex.ring == previousVertex.ring )
177 {
178 currentDist += std::sqrt( QgsGeometryUtils::sqrDistance2D( previousPoint, point ) );
179 }
180
181 if ( qgsDoubleNear( currentDist, distance ) )
182 {
183 // exact hit!
184 previousVertex = nextVertex;
185 return true;
186 }
187
188 if ( currentDist > distance )
189 {
190 return true;
191 }
192
193 previousVertex = nextVertex;
194 previousPoint = point;
195 first = false;
196 }
197
198 //could not find target distance
199 return false;
200}
201
202double QgsGeometryUtils::distToInfiniteLine( const QgsPoint &point, const QgsPoint &linePoint1, const QgsPoint &linePoint2, double epsilon )
203{
204 const double area = std::abs(
205 ( linePoint1.x() - linePoint2.x() ) * ( point.y() - linePoint2.y() ) -
206 ( linePoint1.y() - linePoint2.y() ) * ( point.x() - linePoint2.x() )
207 );
208
209 const double length = std::sqrt(
210 std::pow( linePoint1.x() - linePoint2.x(), 2 ) +
211 std::pow( linePoint1.y() - linePoint2.y(), 2 )
212 );
213
214 const double distance = area / length;
215 return qgsDoubleNear( distance, 0.0, epsilon ) ? 0.0 : distance;
216}
217
218bool QgsGeometryUtils::lineCircleIntersection( const QgsPointXY &center, const double radius,
219 const QgsPointXY &linePoint1, const QgsPointXY &linePoint2,
220 QgsPointXY &intersection )
221{
222 // formula taken from http://mathworld.wolfram.com/Circle-LineIntersection.html
223
224 const double x1 = linePoint1.x() - center.x();
225 const double y1 = linePoint1.y() - center.y();
226 const double x2 = linePoint2.x() - center.x();
227 const double y2 = linePoint2.y() - center.y();
228 const double dx = x2 - x1;
229 const double dy = y2 - y1;
230
231 const double dr2 = std::pow( dx, 2 ) + std::pow( dy, 2 );
232 const double d = x1 * y2 - x2 * y1;
233
234 const double disc = std::pow( radius, 2 ) * dr2 - std::pow( d, 2 );
235
236 if ( disc < 0 )
237 {
238 //no intersection or tangent
239 return false;
240 }
241 else
242 {
243 // two solutions
244 const int sgnDy = dy < 0 ? -1 : 1;
245
246 const double sqrDisc = std::sqrt( disc );
247
248 const double ax = center.x() + ( d * dy + sgnDy * dx * sqrDisc ) / dr2;
249 const double ay = center.y() + ( -d * dx + std::fabs( dy ) * sqrDisc ) / dr2;
250 const QgsPointXY p1( ax, ay );
251
252 const double bx = center.x() + ( d * dy - sgnDy * dx * sqrDisc ) / dr2;
253 const double by = center.y() + ( -d * dx - std::fabs( dy ) * sqrDisc ) / dr2;
254 const QgsPointXY p2( bx, by );
255
256 // snap to nearest intersection
257
258 if ( intersection.sqrDist( p1 ) < intersection.sqrDist( p2 ) )
259 {
260 intersection.set( p1.x(), p1.y() );
261 }
262 else
263 {
264 intersection.set( p2.x(), p2.y() );
265 }
266 return true;
267 }
268}
269
270// based on public domain work by 3/26/2005 Tim Voght
271// see http://paulbourke.net/geometry/circlesphere/tvoght.c
272int QgsGeometryUtils::circleCircleIntersections( const QgsPointXY &center1, const double r1, const QgsPointXY &center2, const double r2, QgsPointXY &intersection1, QgsPointXY &intersection2 )
273{
274 // determine the straight-line distance between the centers
275 const double d = center1.distance( center2 );
276
277 // check if the circles intersect at only 1 point, either "externally" or "internally"
278 const bool singleSolutionExt = qgsDoubleNear( d, r1 + r2 );
279 const bool singleSolutionInt = qgsDoubleNear( d, std::fabs( r1 - r2 ) );
280
281 // check for solvability
282 if ( !singleSolutionExt && d > ( r1 + r2 ) )
283 {
284 // no solution. circles do not intersect.
285 return 0;
286 }
287 else if ( !singleSolutionInt && d < std::fabs( r1 - r2 ) )
288 {
289 // no solution. one circle is contained in the other
290 return 0;
291 }
292 else if ( qgsDoubleNear( d, 0 ) && ( qgsDoubleNear( r1, r2 ) ) )
293 {
294 // no solutions, the circles coincide
295 return 0;
296 }
297
298 /* 'point 2' is the point where the line through the circle
299 * intersection points crosses the line between the circle
300 * centers.
301 */
302
303 /* Determine the distance 'a' from point 0 to point 2.
304 * In the general case, a = ( ( r1 * r1 ) - ( r2 * r2 ) + ( d * d ) ) / ( 2.0 * d ).
305 * 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.
306 */
307 const double a = singleSolutionExt ? r1 : ( singleSolutionInt ? ( r1 > r2 ? r1 : -r1 ) : ( ( r1 * r1 ) - ( r2 * r2 ) + ( d * d ) ) / ( 2.0 * d ) );
308
309 /* dx and dy are the vertical and horizontal distances between
310 * the circle centers.
311 */
312 const double dx = center2.x() - center1.x();
313 const double dy = center2.y() - center1.y();
314
315 // Determine the coordinates of point 2.
316 const double x2 = center1.x() + ( dx * a / d );
317 const double y2 = center1.y() + ( dy * a / d );
318
319 // only 1 solution
320 if ( singleSolutionExt || singleSolutionInt )
321 {
322 intersection1 = QgsPointXY( x2, y2 );
323 intersection2 = QgsPointXY( x2, y2 );
324
325 return 1;
326 }
327
328 // 2 solutions
329
330 /* Determine the distance from point 2 to either of the
331 * intersection points.
332 */
333 const double h = std::sqrt( ( r1 * r1 ) - ( a * a ) );
334
335 /* Now determine the offsets of the intersection points from
336 * point 2.
337 */
338 const double rx = dy * ( h / d );
339 const double ry = dx * ( h / d );
340
341 // determine the absolute intersection points
342 intersection1 = QgsPointXY( x2 + rx, y2 - ry );
343 intersection2 = QgsPointXY( x2 - rx, y2 + ry );
344
345 return 2;
346}
347
348// Using https://stackoverflow.com/a/1351794/1861260
349// and inspired by http://csharphelper.com/blog/2014/11/find-the-tangent-lines-between-a-point-and-a-circle-in-c/
350bool QgsGeometryUtils::tangentPointAndCircle( const QgsPointXY &center, double radius, const QgsPointXY &p, QgsPointXY &pt1, QgsPointXY &pt2 )
351{
352 // distance from point to center of circle
353 const double dx = center.x() - p.x();
354 const double dy = center.y() - p.y();
355 const double distanceSquared = dx * dx + dy * dy;
356 const double radiusSquared = radius * radius;
357 if ( distanceSquared < radiusSquared )
358 {
359 // point is inside circle!
360 return false;
361 }
362
363 // distance from point to tangent point, using pythagoras
364 const double distanceToTangent = std::sqrt( distanceSquared - radiusSquared );
365
366 // tangent points are those where the original circle intersects a circle centered
367 // on p with radius distanceToTangent
368 circleCircleIntersections( center, radius, p, distanceToTangent, pt1, pt2 );
369
370 return true;
371}
372
373// inspired by http://csharphelper.com/blog/2014/12/find-the-tangent-lines-between-two-circles-in-c/
374int QgsGeometryUtils::circleCircleOuterTangents( const QgsPointXY &center1, double radius1, const QgsPointXY &center2, double radius2, QgsPointXY &line1P1, QgsPointXY &line1P2, QgsPointXY &line2P1, QgsPointXY &line2P2 )
375{
376 if ( radius1 > radius2 )
377 return circleCircleOuterTangents( center2, radius2, center1, radius1, line1P1, line1P2, line2P1, line2P2 );
378
379 const double radius2a = radius2 - radius1;
380 if ( !tangentPointAndCircle( center2, radius2a, center1, line1P2, line2P2 ) )
381 {
382 // there are no tangents
383 return 0;
384 }
385
386 // get the vector perpendicular to the
387 // first tangent with length radius1
388 QgsVector v1( -( line1P2.y() - center1.y() ), line1P2.x() - center1.x() );
389 const double v1Length = v1.length();
390 v1 = v1 * ( radius1 / v1Length );
391
392 // offset the tangent vector's points
393 line1P1 = center1 + v1;
394 line1P2 = line1P2 + v1;
395
396 // get the vector perpendicular to the
397 // second tangent with length radius1
398 QgsVector v2( line2P2.y() - center1.y(), -( line2P2.x() - center1.x() ) );
399 const double v2Length = v2.length();
400 v2 = v2 * ( radius1 / v2Length );
401
402 // offset the tangent vector's points
403 line2P1 = center1 + v2;
404 line2P2 = line2P2 + v2;
405
406 return 2;
407}
408
409// inspired by http://csharphelper.com/blog/2014/12/find-the-tangent-lines-between-two-circles-in-c/
410int QgsGeometryUtils::circleCircleInnerTangents( const QgsPointXY &center1, double radius1, const QgsPointXY &center2, double radius2, QgsPointXY &line1P1, QgsPointXY &line1P2, QgsPointXY &line2P1, QgsPointXY &line2P2 )
411{
412 if ( radius1 > radius2 )
413 return circleCircleInnerTangents( center2, radius2, center1, radius1, line1P1, line1P2, line2P1, line2P2 );
414
415 // determine the straight-line distance between the centers
416 const double d = center1.distance( center2 );
417 const double radius1a = radius1 + radius2;
418
419 // check for solvability
420 if ( d <= radius1a || qgsDoubleNear( d, radius1a ) )
421 {
422 // no solution. circles intersect or touch.
423 return 0;
424 }
425
426 if ( !tangentPointAndCircle( center1, radius1a, center2, line1P2, line2P2 ) )
427 {
428 // there are no tangents
429 return 0;
430 }
431
432 // get the vector perpendicular to the
433 // first tangent with length radius2
434 QgsVector v1( ( line1P2.y() - center2.y() ), -( line1P2.x() - center2.x() ) );
435 const double v1Length = v1.length();
436 v1 = v1 * ( radius2 / v1Length );
437
438 // offset the tangent vector's points
439 line1P1 = center2 + v1;
440 line1P2 = line1P2 + v1;
441
442 // get the vector perpendicular to the
443 // second tangent with length radius2
444 QgsVector v2( -( line2P2.y() - center2.y() ), line2P2.x() - center2.x() );
445 const double v2Length = v2.length();
446 v2 = v2 * ( radius2 / v2Length );
447
448 // offset the tangent vector's points in opposite direction
449 line2P1 = center2 + v2;
450 line2P2 = line2P2 + v2;
451
452 return 2;
453}
454
455QVector<QgsGeometryUtils::SelfIntersection> QgsGeometryUtils::selfIntersections( const QgsAbstractGeometry *geom, int part, int ring, double tolerance )
456{
457 QVector<SelfIntersection> intersections;
458
459 const int n = geom->vertexCount( part, ring );
460 const bool isClosed = geom->vertexAt( QgsVertexId( part, ring, 0 ) ) == geom->vertexAt( QgsVertexId( part, ring, n - 1 ) );
461
462 // Check every pair of segments for intersections
463 for ( int i = 0, j = 1; j < n; i = j++ )
464 {
465 const QgsPoint pi = geom->vertexAt( QgsVertexId( part, ring, i ) );
466 const QgsPoint pj = geom->vertexAt( QgsVertexId( part, ring, j ) );
467 if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) < tolerance * tolerance ) continue;
468
469 // Don't test neighboring edges
470 const int start = j + 1;
471 const int end = i == 0 && isClosed ? n - 1 : n;
472 for ( int k = start, l = start + 1; l < end; k = l++ )
473 {
474 const QgsPoint pk = geom->vertexAt( QgsVertexId( part, ring, k ) );
475 const QgsPoint pl = geom->vertexAt( QgsVertexId( part, ring, l ) );
476
477 QgsPoint inter;
478 bool intersection = false;
479 if ( !QgsGeometryUtils::segmentIntersection( pi, pj, pk, pl, inter, intersection, tolerance ) ) continue;
480
482 s.segment1 = i;
483 s.segment2 = k;
484 if ( s.segment1 > s.segment2 )
485 {
486 std::swap( s.segment1, s.segment2 );
487 }
488 s.point = inter;
489 intersections.append( s );
490 }
491 }
492 return intersections;
493}
494
495int QgsGeometryUtils::leftOfLine( const QgsPoint &point, const QgsPoint &p1, const QgsPoint &p2 )
496{
497 return QgsGeometryUtilsBase::leftOfLine( point.x(), point.y(), p1.x(), p1.y(), p2.x(), p2.y() );
498}
499
500QgsPoint QgsGeometryUtils::pointOnLineWithDistance( const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance )
501{
502 double x, y;
503 QgsGeometryUtilsBase::pointOnLineWithDistance( startPoint.x(), startPoint.y(), directionPoint.x(), directionPoint.y(), distance, x, y );
504 return QgsPoint( x, y );
505}
506
507
508QgsPoint QgsGeometryUtils::interpolatePointOnArc( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double distance )
509{
510 double centerX, centerY, radius;
511 circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
512
513 const double theta = distance / radius; // angle subtended
514 const double anglePt1 = std::atan2( pt1.y() - centerY, pt1.x() - centerX );
515 const double anglePt2 = std::atan2( pt2.y() - centerY, pt2.x() - centerX );
516 const double anglePt3 = std::atan2( pt3.y() - centerY, pt3.x() - centerX );
517 const bool isClockwise = QgsGeometryUtilsBase::circleClockwise( anglePt1, anglePt2, anglePt3 );
518 const double angleDest = anglePt1 + ( isClockwise ? -theta : theta );
519
520 const double x = centerX + radius * ( std::cos( angleDest ) );
521 const double y = centerY + radius * ( std::sin( angleDest ) );
522
523 const double z = pt1.is3D() ?
524 QgsGeometryUtilsBase::interpolateArcValue( angleDest, anglePt1, anglePt2, anglePt3, pt1.z(), pt2.z(), pt3.z() )
525 : 0;
526 const double m = pt1.isMeasure() ?
527 QgsGeometryUtilsBase::interpolateArcValue( angleDest, anglePt1, anglePt2, anglePt3, pt1.m(), pt2.m(), pt3.m() )
528 : 0;
529
530 return QgsPoint( pt1.wkbType(), x, y, z, m );
531}
532
533bool QgsGeometryUtils::segmentMidPoint( const QgsPoint &p1, const QgsPoint &p2, QgsPoint &result, double radius, const QgsPoint &mousePos )
534{
535 const QgsPoint midPoint( ( p1.x() + p2.x() ) / 2.0, ( p1.y() + p2.y() ) / 2.0 );
536 const double midDist = std::sqrt( sqrDistance2D( p1, midPoint ) );
537 if ( radius < midDist )
538 {
539 return false;
540 }
541 const double centerMidDist = std::sqrt( radius * radius - midDist * midDist );
542 const double dist = radius - centerMidDist;
543
544 const double midDx = midPoint.x() - p1.x();
545 const double midDy = midPoint.y() - p1.y();
546
547 //get the four possible midpoints
548 QVector<QgsPoint> possibleMidPoints;
549 possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPoint( midPoint.x() - midDy, midPoint.y() + midDx ), dist ) );
550 possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPoint( midPoint.x() - midDy, midPoint.y() + midDx ), 2 * radius - dist ) );
551 possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPoint( midPoint.x() + midDy, midPoint.y() - midDx ), dist ) );
552 possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPoint( midPoint.x() + midDy, midPoint.y() - midDx ), 2 * radius - dist ) );
553
554 //take the closest one
555 double minDist = std::numeric_limits<double>::max();
556 int minDistIndex = -1;
557 for ( int i = 0; i < possibleMidPoints.size(); ++i )
558 {
559 const double currentDist = sqrDistance2D( mousePos, possibleMidPoints.at( i ) );
560 if ( currentDist < minDist )
561 {
562 minDistIndex = i;
563 minDist = currentDist;
564 }
565 }
566
567 if ( minDistIndex == -1 )
568 {
569 return false;
570 }
571
572 result = possibleMidPoints.at( minDistIndex );
573
574 // add z and m support if necessary
576
577 return true;
578}
579
580QgsPoint QgsGeometryUtils::segmentMidPointFromCenter( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, const bool useShortestArc )
581{
582 double midPointAngle = QgsGeometryUtilsBase::averageAngle( QgsGeometryUtilsBase::lineAngle( center.x(), center.y(), p1.x(), p1.y() ),
583 QgsGeometryUtilsBase::lineAngle( center.x(), center.y(), p2.x(), p2.y() ) );
584 if ( !useShortestArc )
585 midPointAngle += M_PI;
586 return center.project( center.distance( p1 ), midPointAngle * 180 / M_PI );
587}
588
589double QgsGeometryUtils::circleTangentDirection( const QgsPoint &tangentPoint, const QgsPoint &cp1,
590 const QgsPoint &cp2, const QgsPoint &cp3 )
591{
592 //calculate circle midpoint
593 double mX, mY, radius;
594 circleCenterRadius( cp1, cp2, cp3, radius, mX, mY );
595
596 const double p1Angle = QgsGeometryUtilsBase::ccwAngle( cp1.y() - mY, cp1.x() - mX );
597 const double p2Angle = QgsGeometryUtilsBase::ccwAngle( cp2.y() - mY, cp2.x() - mX );
598 const double p3Angle = QgsGeometryUtilsBase::ccwAngle( cp3.y() - mY, cp3.x() - mX );
599 double angle = 0;
600 if ( QgsGeometryUtilsBase::circleClockwise( p1Angle, p2Angle, p3Angle ) )
601 {
602 angle = QgsGeometryUtilsBase::lineAngle( tangentPoint.x(), tangentPoint.y(), mX, mY ) - M_PI_2;
603 }
604 else
605 {
606 angle = QgsGeometryUtilsBase::lineAngle( mX, mY, tangentPoint.x(), tangentPoint.y() ) - M_PI_2;
607 }
608 if ( angle < 0 )
609 angle += 2 * M_PI;
610 return angle;
611}
612
613// Ported from PostGIS' pt_continues_arc
614bool QgsGeometryUtils::pointContinuesArc( const QgsPoint &a1, const QgsPoint &a2, const QgsPoint &a3, const QgsPoint &b, double distanceTolerance, double pointSpacingAngleTolerance )
615{
616 double centerX = 0;
617 double centerY = 0;
618 double radius = 0;
619 circleCenterRadius( a1, a2, a3, radius, centerX, centerY );
620
621 // Co-linear a1/a2/a3
622 if ( radius < 0.0 )
623 return false;
624
625 // distance of candidate point to center of arc a1-a2-a3
626 const double bDistance = std::sqrt( ( b.x() - centerX ) * ( b.x() - centerX ) +
627 ( b.y() - centerY ) * ( b.y() - centerY ) );
628
629 double diff = std::fabs( radius - bDistance );
630
631 auto arcAngle = []( const QgsPoint & a, const QgsPoint & b, const QgsPoint & c )->double
632 {
633 const double abX = b.x() - a.x();
634 const double abY = b.y() - a.y();
635
636 const double cbX = b.x() - c.x();
637 const double cbY = b.y() - c.y();
638
639 const double dot = ( abX * cbX + abY * cbY ); /* dot product */
640 const double cross = ( abX * cbY - abY * cbX ); /* cross product */
641
642 const double alpha = std::atan2( cross, dot );
643
644 return alpha;
645 };
646
647 // Is the point b on the circle?
648 if ( diff < distanceTolerance )
649 {
650 const double angle1 = arcAngle( a1, a2, a3 );
651 const double angle2 = arcAngle( a2, a3, b );
652
653 // Is the sweep angle similar to the previous one?
654 // We only consider a segment replaceable by an arc if the points within
655 // it are regularly spaced
656 diff = std::fabs( angle1 - angle2 );
657 if ( diff > pointSpacingAngleTolerance )
658 {
659 return false;
660 }
661
662 const int a2Side = QgsGeometryUtilsBase::leftOfLine( a2.x(), a2.y(), a1.x(), a1.y(), a3.x(), a3.y() );
663 const int bSide = QgsGeometryUtilsBase::leftOfLine( b.x(), b.y(), a1.x(), a1.y(), a3.x(), a3.y() );
664
665 // Is the point b on the same side of a1/a3 as the mid-point a2 is?
666 // If not, it's in the unbounded part of the circle, so it continues the arc, return true.
667 if ( bSide != a2Side )
668 return true;
669 }
670 return false;
671}
672
673void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, QgsPointSequence &points, double tolerance, QgsAbstractGeometry::SegmentationToleranceType toleranceType, bool hasZ, bool hasM )
674{
675 bool reversed = false;
676 const int segSide = segmentSide( p1, p3, p2 );
677
678 QgsPoint circlePoint1;
679 const QgsPoint &circlePoint2 = p2;
680 QgsPoint circlePoint3;
681
682 if ( segSide == -1 )
683 {
684 // Reverse !
685 circlePoint1 = p3;
686 circlePoint3 = p1;
687 reversed = true;
688 }
689 else
690 {
691 circlePoint1 = p1;
692 circlePoint3 = p3;
693 }
694
695 //adapted code from PostGIS
696 double radius = 0;
697 double centerX = 0;
698 double centerY = 0;
699 circleCenterRadius( circlePoint1, circlePoint2, circlePoint3, radius, centerX, centerY );
700
701 if ( circlePoint1 != circlePoint3 && ( radius < 0 || qgsDoubleNear( segSide, 0.0 ) ) ) //points are colinear
702 {
703 points.append( p1 );
704 points.append( p2 );
705 points.append( p3 );
706 return;
707 }
708
709 double increment = tolerance; //one segment per degree
710 if ( toleranceType == QgsAbstractGeometry::MaximumDifference )
711 {
712 // Ensure tolerance is not higher than twice the radius
713 tolerance = std::min( tolerance, radius * 2 );
714 const double halfAngle = std::acos( -tolerance / radius + 1 );
715 increment = 2 * halfAngle;
716 }
717
718 //angles of pt1, pt2, pt3
719 const double a1 = std::atan2( circlePoint1.y() - centerY, circlePoint1.x() - centerX );
720 double a2 = std::atan2( circlePoint2.y() - centerY, circlePoint2.x() - centerX );
721 double a3 = std::atan2( circlePoint3.y() - centerY, circlePoint3.x() - centerX );
722
723 // Make segmentation symmetric
724 const bool symmetric = true;
725 if ( symmetric )
726 {
727 double angle = a3 - a1;
728 // angle == 0 when full circle
729 if ( angle <= 0 ) angle += M_PI * 2;
730
731 /* Number of segments in output */
732 const int segs = ceil( angle / increment );
733 /* Tweak increment to be regular for all the arc */
734 increment = angle / segs;
735 }
736
737 /* Adjust a3 up so we can increment from a1 to a3 cleanly */
738 // a3 == a1 when full circle
739 if ( a3 <= a1 )
740 a3 += 2.0 * M_PI;
741 if ( a2 < a1 )
742 a2 += 2.0 * M_PI;
743
744 double x, y;
745 double z = 0;
746 double m = 0;
747
748 QVector<QgsPoint> stringPoints;
749 stringPoints.insert( 0, circlePoint1 );
750 if ( circlePoint2 != circlePoint3 && circlePoint1 != circlePoint2 ) //draw straight line segment if two points have the same position
751 {
752 Qgis::WkbType pointWkbType = Qgis::WkbType::Point;
753 if ( hasZ )
754 pointWkbType = QgsWkbTypes::addZ( pointWkbType );
755 if ( hasM )
756 pointWkbType = QgsWkbTypes::addM( pointWkbType );
757
758 // As we're adding the last point in any case, we'll avoid
759 // including a point which is at less than 1% increment distance
760 // from it (may happen to find them due to numbers approximation).
761 // NOTE that this effectively allows in output some segments which
762 // are more distant than requested. This is at most 1% off
763 // from requested MaxAngle and less for MaxError.
764 const double tolError = increment / 100;
765 const double stopAngle = a3 - tolError;
766 for ( double angle = a1 + increment; angle < stopAngle; angle += increment )
767 {
768 x = centerX + radius * std::cos( angle );
769 y = centerY + radius * std::sin( angle );
770
771 if ( hasZ )
772 {
773 z = QgsGeometryUtilsBase::interpolateArcValue( angle, a1, a2, a3, circlePoint1.z(), circlePoint2.z(), circlePoint3.z() );
774 }
775 if ( hasM )
776 {
777 m = QgsGeometryUtilsBase::interpolateArcValue( angle, a1, a2, a3, circlePoint1.m(), circlePoint2.m(), circlePoint3.m() );
778 }
779
780 stringPoints.insert( stringPoints.size(), QgsPoint( pointWkbType, x, y, z, m ) );
781 }
782 }
783 stringPoints.insert( stringPoints.size(), circlePoint3 );
784
785 // TODO: check if or implement QgsPointSequence directly taking an iterator to append
786 if ( reversed )
787 {
788 std::reverse( stringPoints.begin(), stringPoints.end() );
789 }
790 if ( ! points.empty() && stringPoints.front() == points.back() ) stringPoints.pop_front();
791 points.append( stringPoints );
792}
793
794int QgsGeometryUtils::segmentSide( const QgsPoint &pt1, const QgsPoint &pt3, const QgsPoint &pt2 )
795{
796 const double side = ( ( pt2.x() - pt1.x() ) * ( pt3.y() - pt1.y() ) - ( pt3.x() - pt1.x() ) * ( pt2.y() - pt1.y() ) );
797 if ( side == 0.0 )
798 {
799 return 0;
800 }
801 else
802 {
803 if ( side < 0 )
804 {
805 return -1;
806 }
807 if ( side > 0 )
808 {
809 return 1;
810 }
811 return 0;
812 }
813}
814
815QgsPointSequence QgsGeometryUtils::pointsFromWKT( const QString &wktCoordinateList, bool is3D, bool isMeasure )
816{
817 const int dim = 2 + is3D + isMeasure;
818 QgsPointSequence points;
819
820 const QStringList coordList = wktCoordinateList.split( ',', Qt::SkipEmptyParts );
821
822 //first scan through for extra unexpected dimensions
823 bool foundZ = false;
824 bool foundM = false;
825 const thread_local QRegularExpression rx( QStringLiteral( "\\s" ) );
826 const thread_local QRegularExpression rxIsNumber( QStringLiteral( "^[+-]?(\\d\\.?\\d*[Ee][+\\-]?\\d+|(\\d+\\.\\d*|\\d*\\.\\d+)|\\d+)$" ) );
827 for ( const QString &pointCoordinates : coordList )
828 {
829 const QStringList coordinates = pointCoordinates.split( rx, Qt::SkipEmptyParts );
830
831 // exit with an empty set if one list contains invalid value.
832 if ( coordinates.filter( rxIsNumber ).size() != coordinates.size() )
833 return points;
834
835 if ( coordinates.size() == 3 && !foundZ && !foundM && !is3D && !isMeasure )
836 {
837 // 3 dimensional coordinates, but not specifically marked as such. We allow this
838 // anyway and upgrade geometry to have Z dimension
839 foundZ = true;
840 }
841 else if ( coordinates.size() >= 4 && ( !( is3D || foundZ ) || !( isMeasure || foundM ) ) )
842 {
843 // 4 (or more) dimensional coordinates, but not specifically marked as such. We allow this
844 // anyway and upgrade geometry to have Z&M dimensions
845 foundZ = true;
846 foundM = true;
847 }
848 }
849
850 for ( const QString &pointCoordinates : coordList )
851 {
852 QStringList coordinates = pointCoordinates.split( rx, Qt::SkipEmptyParts );
853 if ( coordinates.size() < dim )
854 continue;
855
856 int idx = 0;
857 const double x = coordinates[idx++].toDouble();
858 const double y = coordinates[idx++].toDouble();
859
860 double z = 0;
861 if ( ( is3D || foundZ ) && coordinates.length() > idx )
862 z = coordinates[idx++].toDouble();
863
864 double m = 0;
865 if ( ( isMeasure || foundM ) && coordinates.length() > idx )
866 m = coordinates[idx++].toDouble();
867
869 if ( is3D || foundZ )
870 {
871 if ( isMeasure || foundM )
873 else
875 }
876 else
877 {
878 if ( isMeasure || foundM )
880 else
882 }
883
884 points.append( QgsPoint( t, x, y, z, m ) );
885 }
886
887 return points;
888}
889
890void QgsGeometryUtils::pointsToWKB( QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure, QgsAbstractGeometry::WkbFlags flags )
891{
892 wkb << static_cast<quint32>( points.size() );
893 for ( const QgsPoint &point : points )
894 {
895 wkb << point.x() << point.y();
896 if ( is3D )
897 {
898 double z = point.z();
900 && std::isnan( z ) )
901 z = -std::numeric_limits<double>::max();
902
903 wkb << z;
904 }
905 if ( isMeasure )
906 {
907 double m = point.m();
909 && std::isnan( m ) )
910 m = -std::numeric_limits<double>::max();
911
912 wkb << m;
913 }
914 }
915}
916
917QString QgsGeometryUtils::pointsToWKT( const QgsPointSequence &points, int precision, bool is3D, bool isMeasure )
918{
919 QString wkt = QStringLiteral( "(" );
920 for ( const QgsPoint &p : points )
921 {
922 wkt += qgsDoubleToString( p.x(), precision );
923 wkt += ' ' + qgsDoubleToString( p.y(), precision );
924 if ( is3D )
925 wkt += ' ' + qgsDoubleToString( p.z(), precision );
926 if ( isMeasure )
927 wkt += ' ' + qgsDoubleToString( p.m(), precision );
928 wkt += QLatin1String( ", " );
929 }
930 if ( wkt.endsWith( QLatin1String( ", " ) ) )
931 wkt.chop( 2 ); // Remove last ", "
932 wkt += ')';
933 return wkt;
934}
935
936QDomElement QgsGeometryUtils::pointsToGML2( const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, QgsAbstractGeometry::AxisOrder axisOrder )
937{
938 QDomElement elemCoordinates = doc.createElementNS( ns, QStringLiteral( "coordinates" ) );
939
940 // coordinate separator
941 const QString cs = QStringLiteral( "," );
942 // tuple separator
943 const QString ts = QStringLiteral( " " );
944
945 elemCoordinates.setAttribute( QStringLiteral( "cs" ), cs );
946 elemCoordinates.setAttribute( QStringLiteral( "ts" ), ts );
947
948 QString strCoordinates;
949
950 for ( const QgsPoint &p : points )
951 if ( axisOrder == QgsAbstractGeometry::AxisOrder::XY )
952 strCoordinates += qgsDoubleToString( p.x(), precision ) + cs + qgsDoubleToString( p.y(), precision ) + ts;
953 else
954 strCoordinates += qgsDoubleToString( p.y(), precision ) + cs + qgsDoubleToString( p.x(), precision ) + ts;
955
956 if ( strCoordinates.endsWith( ts ) )
957 strCoordinates.chop( 1 ); // Remove trailing space
958
959 elemCoordinates.appendChild( doc.createTextNode( strCoordinates ) );
960 return elemCoordinates;
961}
962
963QDomElement QgsGeometryUtils::pointsToGML3( const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D, QgsAbstractGeometry::AxisOrder axisOrder )
964{
965 QDomElement elemPosList = doc.createElementNS( ns, QStringLiteral( "posList" ) );
966 elemPosList.setAttribute( QStringLiteral( "srsDimension" ), is3D ? 3 : 2 );
967
968 QString strCoordinates;
969 for ( const QgsPoint &p : points )
970 {
971 if ( axisOrder == QgsAbstractGeometry::AxisOrder::XY )
972 strCoordinates += qgsDoubleToString( p.x(), precision ) + ' ' + qgsDoubleToString( p.y(), precision ) + ' ';
973 else
974 strCoordinates += qgsDoubleToString( p.y(), precision ) + ' ' + qgsDoubleToString( p.x(), precision ) + ' ';
975 if ( is3D )
976 strCoordinates += qgsDoubleToString( p.z(), precision ) + ' ';
977 }
978 if ( strCoordinates.endsWith( ' ' ) )
979 strCoordinates.chop( 1 ); // Remove trailing space
980
981 elemPosList.appendChild( doc.createTextNode( strCoordinates ) );
982 return elemPosList;
983}
984
986{
987 QString json = QStringLiteral( "[ " );
988 for ( const QgsPoint &p : points )
989 {
990 json += '[' + qgsDoubleToString( p.x(), precision ) + QLatin1String( ", " ) + qgsDoubleToString( p.y(), precision ) + QLatin1String( "], " );
991 }
992 if ( json.endsWith( QLatin1String( ", " ) ) )
993 {
994 json.chop( 2 ); // Remove last ", "
995 }
996 json += ']';
997 return json;
998}
999
1000
1002{
1003 json coordinates( json::array() );
1004 for ( const QgsPoint &p : points )
1005 {
1006 if ( p.is3D() )
1007 {
1008 coordinates.push_back( { qgsRound( p.x(), precision ), qgsRound( p.y(), precision ), qgsRound( p.z(), precision ) } );
1009 }
1010 else
1011 {
1012 coordinates.push_back( { qgsRound( p.x(), precision ), qgsRound( p.y(), precision ) } );
1013 }
1014 }
1015 return coordinates;
1016}
1017
1018QPair<Qgis::WkbType, QString> QgsGeometryUtils::wktReadBlock( const QString &wkt )
1019{
1020 QString wktParsed = wkt;
1021 QString contents;
1022 const QLatin1String empty { "EMPTY" };
1023 if ( wkt.contains( empty, Qt::CaseInsensitive ) )
1024 {
1025 const thread_local QRegularExpression whiteSpaces( "\\s" );
1026 wktParsed.remove( whiteSpaces );
1027 const int index = wktParsed.indexOf( empty, 0, Qt::CaseInsensitive );
1028
1029 if ( index == wktParsed.length() - empty.size() )
1030 {
1031 // "EMPTY" found at the end of the QString
1032 // Extract the part of the QString to the left of "EMPTY"
1033 wktParsed = wktParsed.left( index );
1034 contents = empty;
1035 }
1036 }
1037 else
1038 {
1039 const int openedParenthesisCount = wktParsed.count( '(' );
1040 const int closedParenthesisCount = wktParsed.count( ')' );
1041 // closes missing parentheses
1042 for ( int i = 0 ; i < openedParenthesisCount - closedParenthesisCount; ++i )
1043 wktParsed.push_back( ')' );
1044 // removes extra parentheses
1045 wktParsed.truncate( wktParsed.size() - ( closedParenthesisCount - openedParenthesisCount ) );
1046
1047 const thread_local QRegularExpression cooRegEx( QStringLiteral( "^[^\\(]*\\((.*)\\)[^\\)]*$" ), QRegularExpression::DotMatchesEverythingOption );
1048 const QRegularExpressionMatch match = cooRegEx.match( wktParsed );
1049 contents = match.hasMatch() ? match.captured( 1 ) : QString();
1050 }
1051 const Qgis::WkbType wkbType = QgsWkbTypes::parseType( wktParsed );
1052 return qMakePair( wkbType, contents );
1053}
1054
1055QStringList QgsGeometryUtils::wktGetChildBlocks( const QString &wkt, const QString &defaultType )
1056{
1057 int level = 0;
1058 QString block;
1059 block.reserve( wkt.size() );
1060 QStringList blocks;
1061
1062 const QChar *wktData = wkt.data();
1063 const int wktLength = wkt.length();
1064 for ( int i = 0, n = wktLength; i < n; ++i, ++wktData )
1065 {
1066 if ( ( wktData->isSpace() || *wktData == '\n' || *wktData == '\t' ) && level == 0 )
1067 continue;
1068
1069 if ( *wktData == ',' && level == 0 )
1070 {
1071 if ( !block.isEmpty() )
1072 {
1073 if ( block.startsWith( '(' ) && !defaultType.isEmpty() )
1074 block.prepend( defaultType + ' ' );
1075 blocks.append( block );
1076 }
1077 block.resize( 0 );
1078 continue;
1079 }
1080 if ( *wktData == '(' )
1081 ++level;
1082 else if ( *wktData == ')' )
1083 --level;
1084 block += *wktData;
1085 }
1086 if ( !block.isEmpty() )
1087 {
1088 if ( block.startsWith( '(' ) && !defaultType.isEmpty() )
1089 block.prepend( defaultType + ' ' );
1090 blocks.append( block );
1091 }
1092 return blocks;
1093}
1094
1096{
1098
1099
1100 const double x = ( pt1.x() + pt2.x() ) / 2.0;
1101 const double y = ( pt1.y() + pt2.y() ) / 2.0;
1102 double z = std::numeric_limits<double>::quiet_NaN();
1103 double m = std::numeric_limits<double>::quiet_NaN();
1104
1105 if ( pt1.is3D() || pt2.is3D() )
1106 {
1107 pType = QgsWkbTypes::addZ( pType );
1108 z = ( pt1.z() + pt2.z() ) / 2.0;
1109 }
1110
1111 if ( pt1.isMeasure() || pt2.isMeasure() )
1112 {
1113 pType = QgsWkbTypes::addM( pType );
1114 m = ( pt1.m() + pt2.m() ) / 2.0;
1115 }
1116
1117 return QgsPoint( pType, x, y, z, m );
1118}
1119
1120QgsPoint QgsGeometryUtils::interpolatePointOnLine( const QgsPoint &p1, const QgsPoint &p2, const double fraction )
1121{
1122 const double _fraction = 1 - fraction;
1123 return QgsPoint( p1.wkbType(),
1124 p1.x() * _fraction + p2.x() * fraction,
1125 p1.y() * _fraction + p2.y() * fraction,
1126 p1.is3D() ? p1.z() * _fraction + p2.z() * fraction : std::numeric_limits<double>::quiet_NaN(),
1127 p1.isMeasure() ? p1.m() * _fraction + p2.m() * fraction : std::numeric_limits<double>::quiet_NaN() );
1128}
1129
1130QgsPointXY QgsGeometryUtils::interpolatePointOnLine( const double x1, const double y1, const double x2, const double y2, const double fraction )
1131{
1132 const double deltaX = ( x2 - x1 ) * fraction;
1133 const double deltaY = ( y2 - y1 ) * fraction;
1134 return QgsPointXY( x1 + deltaX, y1 + deltaY );
1135}
1136
1137QgsPointXY QgsGeometryUtils::interpolatePointOnLineByValue( const double x1, const double y1, const double v1, const double x2, const double y2, const double v2, const double value )
1138{
1139 if ( qgsDoubleNear( v1, v2 ) )
1140 return QgsPointXY( x1, y1 );
1141
1142 const double fraction = ( value - v1 ) / ( v2 - v1 );
1143 return interpolatePointOnLine( x1, y1, x2, y2, fraction );
1144}
1145
1146double QgsGeometryUtils::gradient( const QgsPoint &pt1, const QgsPoint &pt2 )
1147{
1148 const double delta_x = pt2.x() - pt1.x();
1149 const double delta_y = pt2.y() - pt1.y();
1150 if ( qgsDoubleNear( delta_x, 0.0 ) )
1151 {
1152 return INFINITY;
1153 }
1154
1155 return delta_y / delta_x;
1156}
1157
1158void QgsGeometryUtils::coefficients( const QgsPoint &pt1, const QgsPoint &pt2, double &a, double &b, double &c )
1159{
1160 if ( qgsDoubleNear( pt1.x(), pt2.x() ) )
1161 {
1162 a = 1;
1163 b = 0;
1164 c = -pt1.x();
1165 }
1166 else if ( qgsDoubleNear( pt1.y(), pt2.y() ) )
1167 {
1168 a = 0;
1169 b = 1;
1170 c = -pt1.y();
1171 }
1172 else
1173 {
1174 a = pt1.y() - pt2.y();
1175 b = pt2.x() - pt1.x();
1176 c = pt1.x() * pt2.y() - pt1.y() * pt2.x();
1177 }
1178
1179}
1180
1182{
1183 QgsLineString line;
1184 QgsPoint p2;
1185
1186 if ( ( p == s1 ) || ( p == s2 ) )
1187 {
1188 return line;
1189 }
1190
1191 double a, b, c;
1192 coefficients( s1, s2, a, b, c );
1193
1194 if ( qgsDoubleNear( a, 0 ) )
1195 {
1196 p2 = QgsPoint( p.x(), s1.y() );
1197 }
1198 else if ( qgsDoubleNear( b, 0 ) )
1199 {
1200 p2 = QgsPoint( s1.x(), p.y() );
1201 }
1202 else
1203 {
1204 const double y = ( -c - a * p.x() ) / b;
1205 const double m = gradient( s1, s2 );
1206 const double d2 = 1 + m * m;
1207 const double H = p.y() - y;
1208 const double dx = m * H / d2;
1209 const double dy = m * dx;
1210 p2 = QgsPoint( p.x() + dx, y + dy );
1211 }
1212
1213 line.addVertex( p );
1214 line.addVertex( p2 );
1215
1216 return line;
1217}
1218
1220{
1221 bool rc = false;
1222
1223 for ( const QgsPoint &pt : points )
1224 {
1225 if ( pt.isMeasure() )
1226 {
1227 point.convertTo( QgsWkbTypes::addM( point.wkbType() ) );
1228 point.setM( pt.m() );
1229 rc = true;
1230 break;
1231 }
1232 }
1233
1234 return rc;
1235}
1236
1238{
1239 bool rc = false;
1240
1241 for ( const QgsPoint &pt : points )
1242 {
1243 if ( pt.is3D() )
1244 {
1245 point.convertTo( QgsWkbTypes::addZ( point.wkbType() ) );
1246 point.setZ( pt.z() );
1247 rc = true;
1248 break;
1249 }
1250 }
1251
1252 return rc;
1253}
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition: qgis.h:182
@ PointM
PointM.
@ PointZ
PointZ.
@ PointZM
PointZM.
Abstract base class for all geometries.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
@ MaximumDifference
Maximum distance between an arbitrary point on the original curve and closest point on its approximat...
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.
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 (since QGIS 3.30)
virtual double segmentLength(QgsVertexId startVertex) const =0
Returns the length of the segment of the geometry which begins at startVertex.
virtual bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const =0
Returns next vertex id and coordinates.
virtual double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *std::numeric_limits< double >::epsilon()) const =0
Searches for the closest segment of the geometry to a given point.
Curve polygon geometry type.
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
Geometry collection.
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 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 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 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 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 QVector< QgsLineString * > extractLineStrings(const QgsAbstractGeometry *geom)
Returns list of linestrings extracted from the passed geometry.
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 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 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 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 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...
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
A class to represent a 2D point.
Definition: qgspointxy.h:60
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
Definition: qgspointxy.h:187
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Definition: qgspointxy.h:207
void set(double x, double y)
Sets the x and y value of the point.
Definition: qgspointxy.h:137
double y
Definition: qgspointxy.h:64
Q_GADGET double x
Definition: qgspointxy.h:63
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:564
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:553
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
void setM(double m)
Sets the point's m-value.
Definition: qgspoint.h:371
bool convertTo(Qgis::WkbType type) override
Converts the geometry to a specified type.
Definition: qgspoint.cpp:622
bool isEmpty() const override
Returns true if the geometry is empty.
Definition: qgspoint.cpp:733
double distance(double x, double y) const
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition: qgspoint.h:393
void setZ(double z)
Sets the point's z-coordinate.
Definition: qgspoint.h:356
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:701
double y
Definition: qgspoint.h:53
A class to represent a vector.
Definition: qgsvector.h:30
double length() const
Returns the length of the vector.
Definition: qgsvector.h:124
WKB pointer handler.
Definition: qgswkbptr.h:44
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.
Definition: qgswkbtypes.h:1092
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1068
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:973
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1023
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:716
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:5124
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition: qgis.h:5248
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5207
const double DEFAULT_SEGMENT_EPSILON
Default snapping tolerance for segments.
Definition: qgis.h:5735
QVector< QgsPoint > QgsPointSequence
bool isClockwise(std::array< Direction, 4 > dirs)
Checks whether the 4 directions in dirs make up a clockwise rectangle.
int precision
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h: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