QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
qgsgeometryutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgeometryutils.cpp
3  -------------------------------------------------------------------
4 Date : 21 Nov 2014
5 Copyright : (C) 2014 by Marco Hugentobler
6 email : 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 "qgscurvev2.h"
19 #include "qgscurvepolygonv2.h"
21 #include "qgslinestringv2.h"
22 #include "qgswkbptr.h"
23 
24 #include <QStringList>
25 #include <QVector>
26 
28 {
29  QList< QgsLineStringV2* > linestrings;
30  if ( !geom )
31  return linestrings;
32 
34  geometries << geom;
35  while ( ! geometries.isEmpty() )
36  {
37  const QgsAbstractGeometryV2* g = geometries.takeFirst();
38  if ( const QgsCurveV2* curve = dynamic_cast< const QgsCurveV2* >( g ) )
39  {
40  linestrings << static_cast< QgsLineStringV2* >( curve->segmentize() );
41  }
42  else if ( const QgsGeometryCollectionV2* collection = dynamic_cast< const QgsGeometryCollectionV2* >( g ) )
43  {
44  for ( int i = 0; i < collection->numGeometries(); ++i )
45  {
46  geometries.append( collection->geometryN( i ) );
47  }
48  }
49  else if ( const QgsCurvePolygonV2* curvePolygon = dynamic_cast< const QgsCurvePolygonV2* >( g ) )
50  {
51  if ( curvePolygon->exteriorRing() )
52  linestrings << static_cast< QgsLineStringV2* >( curvePolygon->exteriorRing()->segmentize() );
53 
54  for ( int i = 0; i < curvePolygon->numInteriorRings(); ++i )
55  {
56  linestrings << static_cast< QgsLineStringV2* >( curvePolygon->interiorRing( i )->segmentize() );
57  }
58  }
59  }
60  return linestrings;
61 }
62 
64 {
65  double minDist = std::numeric_limits<double>::max();
66  double currentDist = 0;
67  QgsPointV2 minDistPoint;
68  id = QgsVertexId(); // set as invalid
69 
70  QgsVertexId vertexId;
71  QgsPointV2 vertex;
72  while ( geom.nextVertex( vertexId, vertex ) )
73  {
74  currentDist = QgsGeometryUtils::sqrDistance2D( pt, vertex );
75  // The <= is on purpose: for geometries with closing vertices, this ensures
76  // that the closing vertex is retuned. For the node tool, the rubberband
77  // of the closing vertex is above the opening vertex, hence with the <=
78  // situations where the covered opening vertex rubberband is selected are
79  // avoided.
80  if ( currentDist <= minDist )
81  {
82  minDist = currentDist;
83  minDistPoint = vertex;
84  id.part = vertexId.part;
85  id.ring = vertexId.ring;
86  id.vertex = vertexId.vertex;
87  id.type = vertexId.type;
88  }
89  }
90 
91  return minDistPoint;
92 }
93 
95 {
96  double currentDist = 0;
97  QgsVertexId vertexId;
98  QgsPointV2 vertex;
99  QgsPointV2 previousVertex;
100 
101  bool first = true;
102  while ( geom.nextVertex( vertexId, vertex ) )
103  {
104  if ( !first )
105  {
106  currentDist += sqrt( QgsGeometryUtils::sqrDistance2D( previousVertex, vertex ) );
107  }
108 
109  previousVertex = vertex;
110  first = false;
111 
112  if ( vertexId == id )
113  {
114  //found target vertex
115  return currentDist;
116  }
117  }
118 
119  //could not find target vertex
120  return -1;
121 }
122 
123 bool QgsGeometryUtils::verticesAtDistance( const QgsAbstractGeometryV2& geometry, double distance, QgsVertexId& previousVertex, QgsVertexId& nextVertex )
124 {
125  double currentDist = 0;
126  previousVertex = QgsVertexId();
127  nextVertex = QgsVertexId();
128 
129  QgsPointV2 point;
130  QgsPointV2 previousPoint;
131 
132  if ( qgsDoubleNear( distance, 0.0 ) )
133  {
134  geometry.nextVertex( previousVertex, point );
135  nextVertex = previousVertex;
136  return true;
137  }
138 
139  bool first = true;
140  while ( currentDist < distance && geometry.nextVertex( nextVertex, point ) )
141  {
142  if ( !first )
143  {
144  currentDist += sqrt( QgsGeometryUtils::sqrDistance2D( previousPoint, point ) );
145  }
146 
147  if ( qgsDoubleNear( currentDist, distance ) )
148  {
149  // exact hit!
150  previousVertex = nextVertex;
151  return true;
152  }
153 
154  if ( currentDist > distance )
155  {
156  return true;
157  }
158 
159  previousVertex = nextVertex;
160  previousPoint = point;
161  first = false;
162  }
163 
164  //could not find target distance
165  return false;
166 }
167 
168 void QgsGeometryUtils::adjacentVertices( const QgsAbstractGeometryV2& geom, QgsVertexId atVertex, QgsVertexId& beforeVertex, QgsVertexId& afterVertex )
169 {
170  bool polygonType = ( geom.dimension() == 2 );
171 
173 
174  //get feature
175  if ( coords.size() <= atVertex.part )
176  {
177  return; //error, no such feature
178  }
179 
180  const QgsRingSequenceV2 &part = coords.at( atVertex.part );
181 
182  //get ring
183  if ( part.size() <= atVertex.ring )
184  {
185  return; //error, no such ring
186  }
187  const QgsPointSequenceV2 &ring = part.at( atVertex.ring );
188  if ( ring.size() <= atVertex.vertex )
189  {
190  return;
191  }
192 
193  //vertex in the middle
194  if ( atVertex.vertex > 0 && atVertex.vertex < ring.size() - 1 )
195  {
196  beforeVertex.part = atVertex.part;
197  beforeVertex.ring = atVertex.ring;
198  beforeVertex.vertex = atVertex.vertex - 1;
199  afterVertex.part = atVertex.part;
200  afterVertex.ring = atVertex.ring;
201  afterVertex.vertex = atVertex.vertex + 1;
202  }
203  else if ( atVertex.vertex == 0 )
204  {
205  if ( ring.size() > 1 )
206  {
207  afterVertex.part = atVertex.part;
208  afterVertex.ring = atVertex.ring;
209  afterVertex.vertex = atVertex.vertex + 1;
210  }
211  else
212  {
213  afterVertex = QgsVertexId(); //after vertex invalid
214  }
215  if ( polygonType && ring.size() > 3 )
216  {
217  beforeVertex.part = atVertex.part;
218  beforeVertex.ring = atVertex.ring;
219  beforeVertex.vertex = ring.size() - 2;
220  }
221  else
222  {
223  beforeVertex = QgsVertexId(); //before vertex invalid
224  }
225  }
226  else if ( atVertex.vertex == ring.size() - 1 )
227  {
228  beforeVertex.part = atVertex.part;
229  beforeVertex.ring = atVertex.ring;
230  beforeVertex.vertex = atVertex.vertex - 1;
231  if ( polygonType )
232  {
233  afterVertex.part = atVertex.part;
234  afterVertex.ring = atVertex.ring;
235  afterVertex.vertex = 1;
236  }
237  else
238  {
239  afterVertex = QgsVertexId(); //after vertex invalid
240  }
241  }
242 }
243 
244 double QgsGeometryUtils::sqrDistance2D( const QgsPointV2& pt1, const QgsPointV2& pt2 )
245 {
246  return ( pt1.x() - pt2.x() ) * ( pt1.x() - pt2.x() ) + ( pt1.y() - pt2.y() ) * ( pt1.y() - pt2.y() );
247 }
248 
249 double QgsGeometryUtils::sqrDistToLine( double ptX, double ptY, double x1, double y1, double x2, double y2, double& minDistX, double& minDistY, double epsilon )
250 {
251  minDistX = x1;
252  minDistY = y1;
253 
254  double dx = x2 - x1;
255  double dy = y2 - y1;
256 
257  if ( !qgsDoubleNear( dx, 0.0 ) || !qgsDoubleNear( dy, 0.0 ) )
258  {
259  double t = (( ptX - x1 ) * dx + ( ptY - y1 ) * dy ) / ( dx * dx + dy * dy );
260  if ( t > 1 )
261  {
262  minDistX = x2;
263  minDistY = y2;
264  }
265  else if ( t > 0 )
266  {
267  minDistX += dx * t ;
268  minDistY += dy * t ;
269  }
270  }
271 
272  dx = ptX - minDistX;
273  dy = ptY - minDistY;
274 
275  double dist = dx * dx + dy * dy;
276 
277  //prevent rounding errors if the point is directly on the segment
278  if ( qgsDoubleNear( dist, 0.0, epsilon ) )
279  {
280  minDistX = ptX;
281  minDistY = ptY;
282  return 0.0;
283  }
284 
285  return dist;
286 }
287 
289 {
290  double d = v.y() * w.x() - v.x() * w.y();
291 
292  if ( qgsDoubleNear( d, 0 ) )
293  return false;
294 
295  double dx = q1.x() - p1.x();
296  double dy = q1.y() - p1.y();
297  double k = ( dy * w.x() - dx * w.y() ) / d;
298 
299  inter = QgsPointV2( p1.x() + v.x() * k, p1.y() + v.y() * k );
300 
301  return true;
302 }
303 
304 bool QgsGeometryUtils::segmentIntersection( const QgsPointV2 &p1, const QgsPointV2 &p2, const QgsPointV2 &q1, const QgsPointV2 &q2, QgsPointV2 &inter, double tolerance )
305 {
306  QgsVector v( p2.x() - p1.x(), p2.y() - p1.y() );
307  QgsVector w( q2.x() - q1.x(), q2.y() - q1.y() );
308  double vl = v.length();
309  double wl = w.length();
310 
311  if ( qFuzzyIsNull( vl ) || qFuzzyIsNull( wl ) )
312  {
313  return false;
314  }
315  v = v / vl;
316  w = w / wl;
317 
318  if ( !QgsGeometryUtils::lineIntersection( p1, v, q1, w, inter ) )
319  return false;
320 
321  double lambdav = QgsVector( inter.x() - p1.x(), inter.y() - p1.y() ) * v;
322  if ( lambdav < 0. + tolerance || lambdav > vl - tolerance )
323  return false;
324 
325  double lambdaw = QgsVector( inter.x() - q1.x(), inter.y() - q1.y() ) * w;
326  if ( lambdaw < 0. + tolerance || lambdaw >= wl - tolerance )
327  return false;
328 
329  return true;
330 }
331 
333 {
334  QList<SelfIntersection> intersections;
335 
336  int n = geom->vertexCount( part, ring );
337  bool isClosed = geom->vertexAt( QgsVertexId( part, ring, 0 ) ) == geom->vertexAt( QgsVertexId( part, ring, n - 1 ) );
338 
339  // Check every pair of segments for intersections
340  for ( int i = 0, j = 1; j < n; i = j++ )
341  {
342  QgsPointV2 pi = geom->vertexAt( QgsVertexId( part, ring, i ) );
343  QgsPointV2 pj = geom->vertexAt( QgsVertexId( part, ring, j ) );
344  if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) < tolerance * tolerance ) continue;
345 
346  // Don't test neighboring edges
347  int start = j + 1;
348  int end = i == 0 && isClosed ? n - 1 : n;
349  for ( int k = start, l = start + 1; l < end; k = l++ )
350  {
351  QgsPointV2 pk = geom->vertexAt( QgsVertexId( part, ring, k ) );
352  QgsPointV2 pl = geom->vertexAt( QgsVertexId( part, ring, l ) );
353 
354  QgsPointV2 inter;
355  if ( !QgsGeometryUtils::segmentIntersection( pi, pj, pk, pl, inter, tolerance ) ) continue;
356 
358  s.segment1 = i;
359  s.segment2 = k;
360  if ( s.segment1 > s.segment2 )
361  {
362  qSwap( s.segment1, s.segment2 );
363  }
364  s.point = inter;
365  intersections.append( s );
366  }
367  }
368  return intersections;
369 }
370 
371 double QgsGeometryUtils::leftOfLine( double x, double y, double x1, double y1, double x2, double y2 )
372 {
373  double f1 = x - x1;
374  double f2 = y2 - y1;
375  double f3 = y - y1;
376  double f4 = x2 - x1;
377  return f1*f2 - f3*f4;
378 }
379 
380 QgsPointV2 QgsGeometryUtils::pointOnLineWithDistance( const QgsPointV2& startPoint, const QgsPointV2& directionPoint, double distance )
381 {
382  double dx = directionPoint.x() - startPoint.x();
383  double dy = directionPoint.y() - startPoint.y();
384  double length = sqrt( dx * dx + dy * dy );
385 
386  if ( qgsDoubleNear( length, 0.0 ) )
387  {
388  return startPoint;
389  }
390 
391  double scaleFactor = distance / length;
392  return QgsPointV2( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor );
393 }
394 
395 double QgsGeometryUtils::ccwAngle( double dy, double dx )
396 {
397  double angle = atan2( dy, dx ) * 180 / M_PI;
398  if ( angle < 0 )
399  {
400  return 360 + angle;
401  }
402  else if ( angle > 360 )
403  {
404  return 360 - angle;
405  }
406  return angle;
407 }
408 
409 void QgsGeometryUtils::circleCenterRadius( const QgsPointV2& pt1, const QgsPointV2& pt2, const QgsPointV2& pt3, double& radius, double& centerX, double& centerY )
410 {
411  double dx21, dy21, dx31, dy31, h21, h31, d;
412 
413  //closed circle
414  if ( qgsDoubleNear( pt1.x(), pt3.x() ) && qgsDoubleNear( pt1.y(), pt3.y() ) )
415  {
416  centerX = ( pt1.x() + pt2.x() ) / 2.0;
417  centerY = ( pt1.y() + pt2.y() ) / 2.0;
418  radius = sqrt( pow( centerX - pt1.x(), 2.0 ) + pow( centerY - pt1.y(), 2.0 ) );
419  return;
420  }
421 
422  // Using cartesian circumcenter eguations from page https://en.wikipedia.org/wiki/Circumscribed_circle
423  dx21 = pt2.x() - pt1.x();
424  dy21 = pt2.y() - pt1.y();
425  dx31 = pt3.x() - pt1.x();
426  dy31 = pt3.y() - pt1.y();
427 
428  h21 = pow( dx21, 2.0 ) + pow( dy21, 2.0 );
429  h31 = pow( dx31, 2.0 ) + pow( dy31, 2.0 );
430 
431  // 2*Cross product, d<0 means clockwise and d>0 counterclockwise sweeping angle
432  d = 2 * ( dx21 * dy31 - dx31 * dy21 );
433 
434  // Check colinearity, Cross product = 0
435  if ( qgsDoubleNear( fabs( d ), 0.0, 0.00000000001 ) )
436  {
437  radius = -1.0;
438  return;
439  }
440 
441  // Calculate centroid coordinates and radius
442  centerX = pt1.x() + ( h21 * dy31 - h31 * dy21 ) / d;
443  centerY = pt1.y() - ( h21 * dx31 - h31 * dx21 ) / d;
444  radius = sqrt( pow( centerX - pt1.x(), 2.0 ) + pow( centerY - pt1.y(), 2.0 ) );
445 }
446 
447 bool QgsGeometryUtils::circleClockwise( double angle1, double angle2, double angle3 )
448 {
449  if ( angle3 >= angle1 )
450  {
451  if ( angle2 > angle1 && angle2 < angle3 )
452  {
453  return false;
454  }
455  else
456  {
457  return true;
458  }
459  }
460  else
461  {
462  if ( angle2 > angle1 || angle2 < angle3 )
463  {
464  return false;
465  }
466  else
467  {
468  return true;
469  }
470  }
471 }
472 
473 bool QgsGeometryUtils::circleAngleBetween( double angle, double angle1, double angle2, bool clockwise )
474 {
475  if ( clockwise )
476  {
477  if ( angle2 < angle1 )
478  {
479  return ( angle <= angle1 && angle >= angle2 );
480  }
481  else
482  {
483  return ( angle <= angle1 || angle >= angle2 );
484  }
485  }
486  else
487  {
488  if ( angle2 > angle1 )
489  {
490  return ( angle >= angle1 && angle <= angle2 );
491  }
492  else
493  {
494  return ( angle >= angle1 || angle <= angle2 );
495  }
496  }
497 }
498 
499 bool QgsGeometryUtils::angleOnCircle( double angle, double angle1, double angle2, double angle3 )
500 {
501  bool clockwise = circleClockwise( angle1, angle2, angle3 );
502  return circleAngleBetween( angle, angle1, angle3, clockwise );
503 }
504 
505 double QgsGeometryUtils::circleLength( double x1, double y1, double x2, double y2, double x3, double y3 )
506 {
507  double centerX, centerY, radius;
508  circleCenterRadius( QgsPointV2( x1, y1 ), QgsPointV2( x2, y2 ), QgsPointV2( x3, y3 ), radius, centerX, centerY );
509  double length = M_PI / 180.0 * radius * sweepAngle( centerX, centerY, x1, y1, x2, y2, x3, y3 );
510  if ( length < 0 )
511  {
512  length = -length;
513  }
514  return length;
515 }
516 
517 double QgsGeometryUtils::sweepAngle( double centerX, double centerY, double x1, double y1, double x2, double y2, double x3, double y3 )
518 {
519  double p1Angle = QgsGeometryUtils::ccwAngle( y1 - centerY, x1 - centerX );
520  double p2Angle = QgsGeometryUtils::ccwAngle( y2 - centerY, x2 - centerX );
521  double p3Angle = QgsGeometryUtils::ccwAngle( y3 - centerY, x3 - centerX );
522 
523  if ( p3Angle >= p1Angle )
524  {
525  if ( p2Angle > p1Angle && p2Angle < p3Angle )
526  {
527  return( p3Angle - p1Angle );
528  }
529  else
530  {
531  return ( - ( p1Angle + ( 360 - p3Angle ) ) );
532  }
533  }
534  else
535  {
536  if ( p2Angle < p1Angle && p2Angle > p3Angle )
537  {
538  return( -( p1Angle - p3Angle ) );
539  }
540  else
541  {
542  return( p3Angle + ( 360 - p1Angle ) );
543  }
544  }
545 }
546 
547 bool QgsGeometryUtils::segmentMidPoint( const QgsPointV2& p1, const QgsPointV2& p2, QgsPointV2& result, double radius, const QgsPointV2& mousePos )
548 {
549  QgsPointV2 midPoint(( p1.x() + p2.x() ) / 2.0, ( p1.y() + p2.y() ) / 2.0 );
550  double midDist = sqrt( sqrDistance2D( p1, midPoint ) );
551  if ( radius < midDist )
552  {
553  return false;
554  }
555  double centerMidDist = sqrt( radius * radius - midDist * midDist );
556  double dist = radius - centerMidDist;
557 
558  double midDx = midPoint.x() - p1.x();
559  double midDy = midPoint.y() - p1.y();
560 
561  //get the four possible midpoints
562  QVector<QgsPointV2> possibleMidPoints;
563  possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPointV2( midPoint.x() - midDy, midPoint.y() + midDx ), dist ) );
564  possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPointV2( midPoint.x() - midDy, midPoint.y() + midDx ), 2 * radius - dist ) );
565  possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPointV2( midPoint.x() + midDy, midPoint.y() - midDx ), dist ) );
566  possibleMidPoints.append( pointOnLineWithDistance( midPoint, QgsPointV2( midPoint.x() + midDy, midPoint.y() - midDx ), 2 * radius - dist ) );
567 
568  //take the closest one
569  double minDist = std::numeric_limits<double>::max();
570  int minDistIndex = -1;
571  for ( int i = 0; i < possibleMidPoints.size(); ++i )
572  {
573  double currentDist = sqrDistance2D( mousePos, possibleMidPoints.at( i ) );
574  if ( currentDist < minDist )
575  {
576  minDistIndex = i;
577  minDist = currentDist;
578  }
579  }
580 
581  if ( minDistIndex == -1 )
582  {
583  return false;
584  }
585 
586  result = possibleMidPoints.at( minDistIndex );
587  return true;
588 }
589 
590 double QgsGeometryUtils::circleTangentDirection( const QgsPointV2& tangentPoint, const QgsPointV2& cp1,
591  const QgsPointV2& cp2, const QgsPointV2& cp3 )
592 {
593  //calculate circle midpoint
594  double mX, mY, radius;
595  circleCenterRadius( cp1, cp2, cp3, radius, mX, mY );
596 
597  double p1Angle = QgsGeometryUtils::ccwAngle( cp1.y() - mY, cp1.x() - mX );
598  double p2Angle = QgsGeometryUtils::ccwAngle( cp2.y() - mY, cp2.x() - mX );
599  double p3Angle = QgsGeometryUtils::ccwAngle( cp3.y() - mY, cp3.x() - mX );
600  if ( circleClockwise( p1Angle, p2Angle, p3Angle ) )
601  {
602  return lineAngle( tangentPoint.x(), tangentPoint.y(), mX, mY );
603  }
604  else
605  {
606  return lineAngle( mX, mY, tangentPoint.x(), tangentPoint.y() );
607  }
608 }
609 
610 QgsPointSequenceV2 QgsGeometryUtils::pointsFromWKT( const QString &wktCoordinateList, bool is3D, bool isMeasure )
611 {
612  int dim = 2 + is3D + isMeasure;
613  QgsPointSequenceV2 points;
614  QStringList coordList = wktCoordinateList.split( ',', QString::SkipEmptyParts );
615 
616  //first scan through for extra unexpected dimensions
617  bool foundZ = false;
618  bool foundM = false;
619  Q_FOREACH ( const QString& pointCoordinates, coordList )
620  {
621  QStringList coordinates = pointCoordinates.split( ' ', QString::SkipEmptyParts );
622  if ( coordinates.size() == 3 && !foundZ && !foundM && !is3D && !isMeasure )
623  {
624  // 3 dimensional coordinates, but not specifically marked as such. We allow this
625  // anyway and upgrade geometry to have Z dimension
626  foundZ = true;
627  }
628  else if ( coordinates.size() >= 4 && ( !( is3D || foundZ ) || !( isMeasure || foundM ) ) )
629  {
630  // 4 (or more) dimensional coordinates, but not specifically marked as such. We allow this
631  // anyway and upgrade geometry to have Z&M dimensions
632  foundZ = true;
633  foundM = true;
634  }
635  }
636 
637  Q_FOREACH ( const QString& pointCoordinates, coordList )
638  {
639  QStringList coordinates = pointCoordinates.split( ' ', QString::SkipEmptyParts );
640  if ( coordinates.size() < dim )
641  continue;
642 
643  int idx = 0;
644  double x = coordinates[idx++].toDouble();
645  double y = coordinates[idx++].toDouble();
646 
647  double z = 0;
648  if (( is3D || foundZ ) && coordinates.length() > idx )
649  z = coordinates[idx++].toDouble();
650 
651  double m = 0;
652  if (( isMeasure || foundM ) && coordinates.length() > idx )
653  m = coordinates[idx++].toDouble();
654 
656  if ( is3D || foundZ )
657  {
658  if ( isMeasure || foundM )
660  else
662  }
663  else
664  {
665  if ( isMeasure || foundM )
667  else
668  t = QgsWKBTypes::Point;
669  }
670 
671  points.append( QgsPointV2( t, x, y, z, m ) );
672  }
673 
674  return points;
675 }
676 
677 void QgsGeometryUtils::pointsToWKB( QgsWkbPtr& wkb, const QgsPointSequenceV2 &points, bool is3D, bool isMeasure )
678 {
679  wkb << static_cast<quint32>( points.size() );
680  Q_FOREACH ( const QgsPointV2& point, points )
681  {
682  wkb << point.x() << point.y();
683  if ( is3D )
684  {
685  wkb << point.z();
686  }
687  if ( isMeasure )
688  {
689  wkb << point.m();
690  }
691  }
692 }
693 
694 QString QgsGeometryUtils::pointsToWKT( const QgsPointSequenceV2 &points, int precision, bool is3D, bool isMeasure )
695 {
696  QString wkt = "(";
697  Q_FOREACH ( const QgsPointV2& p, points )
698  {
699  wkt += qgsDoubleToString( p.x(), precision );
700  wkt += ' ' + qgsDoubleToString( p.y(), precision );
701  if ( is3D )
702  wkt += ' ' + qgsDoubleToString( p.z(), precision );
703  if ( isMeasure )
704  wkt += ' ' + qgsDoubleToString( p.m(), precision );
705  wkt += ", ";
706  }
707  if ( wkt.endsWith( ", " ) )
708  wkt.chop( 2 ); // Remove last ", "
709  wkt += ')';
710  return wkt;
711 }
712 
713 QDomElement QgsGeometryUtils::pointsToGML2( const QgsPointSequenceV2 &points, QDomDocument& doc, int precision, const QString &ns )
714 {
715  QDomElement elemCoordinates = doc.createElementNS( ns, "coordinates" );
716 
717  // coordinate separator
718  QString cs = ",";
719  // tupel separator
720  QString ts = " ";
721 
722  elemCoordinates.setAttribute( "cs", cs );
723  elemCoordinates.setAttribute( "ts", ts );
724 
725  QString strCoordinates;
726 
727  Q_FOREACH ( const QgsPointV2& p, points )
728  strCoordinates += qgsDoubleToString( p.x(), precision ) + cs + qgsDoubleToString( p.y(), precision ) + ts;
729 
730  if ( strCoordinates.endsWith( ts ) )
731  strCoordinates.chop( 1 ); // Remove trailing space
732 
733  elemCoordinates.appendChild( doc.createTextNode( strCoordinates ) );
734  return elemCoordinates;
735 }
736 
737 QDomElement QgsGeometryUtils::pointsToGML3( const QgsPointSequenceV2 &points, QDomDocument& doc, int precision, const QString &ns, bool is3D )
738 {
739  QDomElement elemPosList = doc.createElementNS( ns, "posList" );
740  elemPosList.setAttribute( "srsDimension", is3D ? 3 : 2 );
741 
742  QString strCoordinates;
743  Q_FOREACH ( const QgsPointV2& p, points )
744  {
745  strCoordinates += qgsDoubleToString( p.x(), precision ) + ' ' + qgsDoubleToString( p.y(), precision ) + ' ';
746  if ( is3D )
747  strCoordinates += qgsDoubleToString( p.z(), precision ) + ' ';
748  }
749  if ( strCoordinates.endsWith( ' ' ) )
750  strCoordinates.chop( 1 ); // Remove trailing space
751 
752  elemPosList.appendChild( doc.createTextNode( strCoordinates ) );
753  return elemPosList;
754 }
755 
757 {
758  QString json = "[ ";
759  Q_FOREACH ( const QgsPointV2& p, points )
760  {
761  json += '[' + qgsDoubleToString( p.x(), precision ) + ", " + qgsDoubleToString( p.y(), precision ) + "], ";
762  }
763  if ( json.endsWith( ", " ) )
764  {
765  json.chop( 2 ); // Remove last ", "
766  }
767  json += ']';
768  return json;
769 }
770 
772 {
773  double clippedAngle = angle;
774  if ( clippedAngle >= M_PI * 2 || clippedAngle <= -2 * M_PI )
775  {
776  clippedAngle = fmod( clippedAngle, 2 * M_PI );
777  }
778  if ( clippedAngle < 0.0 )
779  {
780  clippedAngle += 2 * M_PI;
781  }
782  return clippedAngle;
783 }
784 
786 {
787  QgsWKBTypes::Type wkbType = QgsWKBTypes::parseType( wkt );
788 
789  QRegExp cooRegEx( "^[^\\(]*\\((.*)\\)[^\\)]*$" );
790  QString contents = cooRegEx.indexIn( wkt ) >= 0 ? cooRegEx.cap( 1 ) : QString();
791  return qMakePair( wkbType, contents );
792 }
793 
795 {
796  int level = 0;
797  QString block;
798  QStringList blocks;
799  for ( int i = 0, n = wkt.length(); i < n; ++i )
800  {
801  if ( wkt[i].isSpace() && level == 0 )
802  continue;
803 
804  if ( wkt[i] == ',' && level == 0 )
805  {
806  if ( !block.isEmpty() )
807  {
808  if ( block.startsWith( '(' ) && !defaultType.isEmpty() )
809  block.prepend( defaultType + ' ' );
810  blocks.append( block );
811  }
812  block.clear();
813  continue;
814  }
815  if ( wkt[i] == '(' )
816  ++level;
817  else if ( wkt[i] == ')' )
818  --level;
819  block += wkt[i];
820  }
821  if ( !block.isEmpty() )
822  {
823  if ( block.startsWith( '(' ) && !defaultType.isEmpty() )
824  block.prepend( defaultType + ' ' );
825  blocks.append( block );
826  }
827  return blocks;
828 }
829 
830 double QgsGeometryUtils::lineAngle( double x1, double y1, double x2, double y2 )
831 {
832  double at = atan2( y2 - y1, x2 - x1 );
833  double a = -at + M_PI / 2.0;
834  return normalizedAngle( a );
835 }
836 
837 double QgsGeometryUtils::linePerpendicularAngle( double x1, double y1, double x2, double y2 )
838 {
839  double a = lineAngle( x1, y1, x2, y2 );
840  a += ( M_PI / 2.0 );
841  return normalizedAngle( a );
842 }
843 
844 double QgsGeometryUtils::averageAngle( double x1, double y1, double x2, double y2, double x3, double y3 )
845 {
846  // calc average angle between the previous and next point
847  double a1 = lineAngle( x1, y1, x2, y2 );
848  double a2 = lineAngle( x2, y2, x3, y3 );
849  return averageAngle( a1, a2 );
850 }
851 
852 double QgsGeometryUtils::averageAngle( double a1, double a2 )
853 {
854  a1 = normalizedAngle( a1 );
855  a2 = normalizedAngle( a2 );
856  double clockwiseDiff = 0.0;
857  if ( a2 >= a1 )
858  {
859  clockwiseDiff = a2 - a1;
860  }
861  else
862  {
863  clockwiseDiff = a2 + ( 2 * M_PI - a1 );
864  }
865  double counterClockwiseDiff = 2 * M_PI - clockwiseDiff;
866 
867  double resultAngle = 0;
868  if ( clockwiseDiff <= counterClockwiseDiff )
869  {
870  resultAngle = a1 + clockwiseDiff / 2.0;
871  }
872  else
873  {
874  resultAngle = a1 - counterClockwiseDiff / 2.0;
875  }
876  return normalizedAngle( resultAngle );
877 }
static QgsPointV2 pointOnLineWithDistance(const QgsPointV2 &startPoint, const QgsPointV2 &directionPoint, double distance)
Returns a point a specified distance toward a second point.
QString cap(int nth) const
static double circleTangentDirection(const QgsPointV2 &tangentPoint, const QgsPointV2 &cp1, const QgsPointV2 &cp2, const QgsPointV2 &cp3)
Calculates the direction angle of a circle tangent (clockwise from north in radians) ...
static QPair< QgsWKBTypes::Type, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
static bool circleAngleBetween(double angle, double angle1, double angle2, bool clockwise)
Returns true if, in a circle, angle is between angle1 and angle2.
static bool verticesAtDistance(const QgsAbstractGeometryV2 &geometry, double distance, QgsVertexId &previousVertex, QgsVertexId &nextVertex)
Retrieves the vertices which are before and after the interpolated point at a specified distance alon...
virtual QgsCoordinateSequenceV2 coordinateSequence() const =0
Retrieves the sequence of geometries, rings and nodes.
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...
QDomNode appendChild(const QDomNode &newChild)
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...
void append(const T &value)
static double distanceToVertex(const QgsAbstractGeometryV2 &geom, const QgsVertexId &id)
Returns the distance along a geometry from its first vertex to the specified vertex.
int length() const
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Angle between two linear segments.
static void pointsToWKB(QgsWkbPtr &wkb, const QgsPointSequenceV2 &points, bool is3D, bool isMeasure)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
QString & prepend(QChar ch)
static bool lineIntersection(const QgsPointV2 &p1, QgsVector v, const QgsPointV2 &q1, QgsVector w, QgsPointV2 &inter)
Compute the intersection between two lines.
const T & at(int i) const
static void circleCenterRadius(const QgsPointV2 &pt1, const QgsPointV2 &pt2, const QgsPointV2 &pt3, double &radius, double &centerX, double &centerY)
Returns radius and center of the circle through pt1, pt2, pt3.
static QString pointsToJSON(const QgsPointSequenceV2 &points, int precision)
Returns a geoJSON coordinates string.
Abstract base class for all geometries.
static double linePerpendicularAngle(double x1, double y1, double x2, double y2)
Calculates the perpendicular angle to a line joining two points.
static QStringList wktGetChildBlocks(const QString &wkt, const QString &defaultType="")
Parses a WKT string and returns of list of blocks contained in the WKT.
static QgsPointV2 closestVertex(const QgsAbstractGeometryV2 &geom, const QgsPointV2 &pt, QgsVertexId &id)
Returns the closest vertex to a geometry for a specified point.
static double leftOfLine(double x, double y, double x1, double y1, double x2, double y2)
Returns < 0 if point(x/y) is left of the line x1,y1 -> x2,y2.
static double circleLength(double x1, double y1, double x2, double y2, double x3, double y3)
Length of a circular string segment defined by pt1, pt2, pt3.
QDomElement createElementNS(const QString &nsURI, const QString &qName)
double z() const
Returns the point&#39;s z-coordinate.
Definition: qgspointv2.h:80
double y() const
Returns the point&#39;s y-coordinate.
Definition: qgspointv2.h:74
void chop(int n)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:353
int size() const
void clear()
double ANALYSIS_EXPORT max(double x, double y)
Returns the maximum of two doubles or the first argument if both are equal.
int indexIn(const QString &str, int offset, CaretMode caretMode) const
static bool circleClockwise(double angle1, double angle2, double angle3)
Returns true if circle is ordered clockwise.
void append(const T &value)
static double sqrDistance2D(const QgsPointV2 &pt1, const QgsPointV2 &pt2)
Returns the squared 2D distance between two points.
Utility class for identifying a unique vertex within a geometry.
static QString pointsToWKT(const QgsPointSequenceV2 &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
static QList< SelfIntersection > getSelfIntersections(const QgsAbstractGeometryV2 *geom, int part, int ring, double tolerance)
Find self intersections in a polyline.
void setAttribute(const QString &name, const QString &value)
Point geometry type, with support for z-dimension and m-values.
Definition: qgspointv2.h:34
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:341
bool isEmpty() const
bool isEmpty() const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
#define M_PI
double x() const
Returns the point&#39;s x-coordinate.
Definition: qgspointv2.h:68
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
static void adjacentVertices(const QgsAbstractGeometryV2 &geom, QgsVertexId atVertex, QgsVertexId &beforeVertex, QgsVertexId &afterVertex)
Returns vertices adjacent to a specified vertex within a geometry.
static bool angleOnCircle(double angle, double angle1, double angle2, double angle3)
Returns true if an angle is between angle1 and angle3 on a circle described by angle1, angle2 and angle3.
static bool segmentIntersection(const QgsPointV2 &p1, const QgsPointV2 &p2, const QgsPointV2 &q1, const QgsPointV2 &q2, QgsPointV2 &inter, double tolerance)
Compute the intersection between two segments.
virtual bool nextVertex(QgsVertexId &id, QgsPointV2 &vertex) const =0
Returns next vertex id and coordinates.
static double normalizedAngle(double angle)
Ensures that an angle is in the range 0 <= angle < 2 pi.
static QDomElement pointsToGML3(const QgsPointSequenceV2 &points, QDomDocument &doc, int precision, const QString &ns, bool is3D)
Returns a gml::posList DOM element.
double length() const
Returns the length of the vector.
Definition: qgspoint.cpp:63
QDomText createTextNode(const QString &value)
double ANALYSIS_EXPORT angle(Point3D *p1, Point3D *p2, Point3D *p3, Point3D *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
A class to represent a vector.
Definition: qgspoint.h:32
const T & at(int i) const
static bool segmentMidPoint(const QgsPointV2 &p1, const QgsPointV2 &p2, QgsPointV2 &result, double radius, const QgsPointV2 &mousePos)
Calculates midpoint on circle passing through p1 and p2, closest to given coordinate.
T takeFirst()
static QList< QgsLineStringV2 * > extractLineStrings(const QgsAbstractGeometryV2 *geom)
Returns list of linestrings extracted from the passed geometry.
static QgsPointSequenceV2 pointsFromWKT(const QString &wktCoordinateList, bool is3D, bool isMeasure)
Returns a list of points contained in a WKT string.
virtual int vertexCount(int part=0, int ring=0) const =0
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.
virtual int dimension() const =0
Returns the inherent dimension of the geometry.
int length() const
static double sqrDistToLine(double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon)
Returns the squared distance between a point and a line.
double x() const
Returns the vector&#39;s x-component.
Definition: qgspoint.cpp:68
static Type parseType(const QString &wktStr)
Attempts to extract the WKB type from a WKT string.
Definition: qgswkbtypes.cpp:32
Curve polygon geometry type.
Abstract base class for curved geometry type.
Definition: qgscurvev2.h:32
int size() const
double y() const
Returns the vector&#39;s y-component.
Definition: qgspoint.cpp:73
double m() const
Returns the point&#39;s m value.
Definition: qgspointv2.h:86
static QDomElement pointsToGML2(const QgsPointSequenceV2 &points, QDomDocument &doc, int precision, const QString &ns)
Returns a gml::coordinates DOM element.
virtual QgsPointV2 vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.