QGIS API Documentation  2.12.0-Lyon
qgscircularstringv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscircularstringv2.cpp
3  -----------------------
4  begin : September 2014
5  copyright : (C) 2014 by Marco Hugentobler
6  email : marco at sourcepole dot ch
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgscircularstringv2.h"
19 #include "qgsapplication.h"
20 #include "qgscoordinatetransform.h"
21 #include "qgsgeometryutils.h"
22 #include "qgslinestringv2.h"
23 #include "qgsmaptopixel.h"
24 #include "qgspointv2.h"
25 #include "qgswkbptr.h"
26 #include <QPainter>
27 #include <QPainterPath>
28 
30 {
31 
32 }
33 
35 {
36 
37 }
38 
40 {
41  return new QgsCircularStringV2( *this );
42 }
43 
45 {
46  mX.clear();
47  mY.clear();
48  mZ.clear();
49  mM.clear();
51 }
52 
54 {
55  QgsRectangle bbox;
56  int nPoints = numPoints();
57  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
58  {
59  if ( i == 0 )
60  {
61  bbox = segmentBoundingBox( QgsPointV2( mX[i], mY[i] ), QgsPointV2( mX[i + 1], mY[i + 1] ), QgsPointV2( mX[i + 2], mY[i + 2] ) );
62  }
63  else
64  {
65  QgsRectangle segmentBox = segmentBoundingBox( QgsPointV2( mX[i], mY[i] ), QgsPointV2( mX[i + 1], mY[i + 1] ), QgsPointV2( mX[i + 2], mY[i + 2] ) );
66  bbox.combineExtentWith( &segmentBox );
67  }
68  }
69 
70  if ( nPoints > 0 && nPoints % 2 == 0 )
71  {
72  if ( nPoints == 2 )
73  {
74  bbox.combineExtentWith( mX[ 0 ], mY[ 0 ] );
75  }
76  bbox.combineExtentWith( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
77  }
78  return bbox;
79 }
80 
81 QgsRectangle QgsCircularStringV2::segmentBoundingBox( const QgsPointV2& pt1, const QgsPointV2& pt2, const QgsPointV2& pt3 )
82 {
83  double centerX, centerY, radius;
84  QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
85 
86  double p1Angle = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
87  if ( p1Angle > 360 )
88  {
89  p1Angle -= 360;
90  }
91  double p2Angle = QgsGeometryUtils::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
92  if ( p2Angle > 360 )
93  {
94  p2Angle -= 360;
95  }
96  double p3Angle = QgsGeometryUtils::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
97  if ( p3Angle > 360 )
98  {
99  p3Angle -= 360;
100  }
101 
102  //start point, end point and compass points in between can be on bounding box
103  QgsRectangle bbox( pt1.x(), pt1.y(), pt1.x(), pt1.y() );
104  bbox.combineExtentWith( pt3.x(), pt3.y() );
105 
106  QList<QgsPointV2> compassPoints = compassPointsOnSegment( p1Angle, p2Angle, p3Angle, centerX, centerY, radius );
107  QList<QgsPointV2>::const_iterator cpIt = compassPoints.constBegin();
108  for ( ; cpIt != compassPoints.constEnd(); ++cpIt )
109  {
110  bbox.combineExtentWith( cpIt->x(), cpIt->y() );
111  }
112  return bbox;
113 }
114 
115 QList<QgsPointV2> QgsCircularStringV2::compassPointsOnSegment( double p1Angle, double p2Angle, double p3Angle, double centerX, double centerY, double radius )
116 {
117  QList<QgsPointV2> pointList;
118 
119  QgsPointV2 nPoint( centerX, centerY + radius );
120  QgsPointV2 ePoint( centerX + radius, centerY );
121  QgsPointV2 sPoint( centerX, centerY - radius );
122  QgsPointV2 wPoint( centerX - radius, centerY );
123 
124  if ( p3Angle >= p1Angle )
125  {
126  if ( p2Angle > p1Angle && p2Angle < p3Angle )
127  {
128  if ( p1Angle <= 90 && p3Angle >= 90 )
129  {
130  pointList.append( nPoint );
131  }
132  if ( p1Angle <= 180 && p3Angle >= 180 )
133  {
134  pointList.append( wPoint );
135  }
136  if ( p1Angle <= 270 && p3Angle >= 270 )
137  {
138  pointList.append( sPoint );
139  }
140  }
141  else
142  {
143  pointList.append( ePoint );
144  if ( p1Angle >= 90 || p3Angle <= 90 )
145  {
146  pointList.append( nPoint );
147  }
148  if ( p1Angle >= 180 || p3Angle <= 180 )
149  {
150  pointList.append( wPoint );
151  }
152  if ( p1Angle >= 270 || p3Angle <= 270 )
153  {
154  pointList.append( sPoint );
155  }
156  }
157  }
158  else
159  {
160  if ( p2Angle < p1Angle && p2Angle > p3Angle )
161  {
162  if ( p1Angle >= 270 && p3Angle <= 270 )
163  {
164  pointList.append( sPoint );
165  }
166  if ( p1Angle >= 180 && p3Angle <= 180 )
167  {
168  pointList.append( wPoint );
169  }
170  if ( p1Angle >= 90 && p3Angle <= 90 )
171  {
172  pointList.append( nPoint );
173  }
174  }
175  else
176  {
177  pointList.append( ePoint );
178  if ( p1Angle <= 270 || p3Angle >= 270 )
179  {
180  pointList.append( sPoint );
181  }
182  if ( p1Angle <= 180 || p3Angle >= 180 )
183  {
184  pointList.append( wPoint );
185  }
186  if ( p1Angle <= 90 || p3Angle >= 90 )
187  {
188  pointList.append( nPoint );
189  }
190  }
191  }
192  return pointList;
193 }
194 
195 bool QgsCircularStringV2::fromWkb( const unsigned char* wkb )
196 {
197  if ( !wkb )
198  {
199  return false;
200  }
201 
202  QgsConstWkbPtr wkbPtr( wkb );
203  QgsWKBTypes::Type type = wkbPtr.readHeader();
205  {
206  return false;
207  }
208  mWkbType = type;
209 
210  //type
211  bool hasZ = is3D();
212  bool hasM = isMeasure();
213  int nVertices = 0;
214  wkbPtr >> nVertices;
215  mX.resize( nVertices );
216  mY.resize( nVertices );
217  hasZ ? mZ.resize( nVertices ) : mZ.clear();
218  hasM ? mM.resize( nVertices ) : mM.clear();
219  for ( int i = 0; i < nVertices; ++i )
220  {
221  wkbPtr >> mX[i];
222  wkbPtr >> mY[i];
223  if ( hasZ )
224  {
225  wkbPtr >> mZ[i];
226  }
227  if ( hasM )
228  {
229  wkbPtr >> mM[i];
230  }
231  }
232 
233  return true;
234 }
235 
237 {
238  clear();
239 
241 
242  if ( QgsWKBTypes::flatType( parts.first ) != QgsWKBTypes::parseType( geometryType() ) )
243  return false;
244  mWkbType = parts.first;
245 
246  setPoints( QgsGeometryUtils::pointsFromWKT( parts.second, is3D(), isMeasure() ) );
247  return true;
248 }
249 
251 {
252  int size = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
253  size += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
254  return size;
255 }
256 
257 unsigned char* QgsCircularStringV2::asWkb( int& binarySize ) const
258 {
259  binarySize = wkbSize();
260  unsigned char* geomPtr = new unsigned char[binarySize];
261  QgsWkbPtr wkb( geomPtr );
262  wkb << static_cast<char>( QgsApplication::endian() );
263  wkb << static_cast<quint32>( wkbType() );
264  QList<QgsPointV2> pts;
265  points( pts );
266  QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure() );
267  return geomPtr;
268 }
269 
270 QString QgsCircularStringV2::asWkt( int precision ) const
271 {
272  QString wkt = wktTypeStr() + " ";
273  QList<QgsPointV2> pts;
274  points( pts );
275  wkt += QgsGeometryUtils::pointsToWKT( pts, precision, is3D(), isMeasure() );
276  return wkt;
277 }
278 
279 QDomElement QgsCircularStringV2::asGML2( QDomDocument& doc, int precision, const QString& ns ) const
280 {
281  // GML2 does not support curves
282  QgsLineStringV2* line = curveToLine();
283  QDomElement gml = line->asGML2( doc, precision, ns );
284  delete line;
285  return gml;
286 }
287 
288 QDomElement QgsCircularStringV2::asGML3( QDomDocument& doc, int precision, const QString& ns ) const
289 {
290  QList<QgsPointV2> pts;
291  points( pts );
292 
293  QDomElement elemCurve = doc.createElementNS( ns, "Curve" );
294  QDomElement elemSegments = doc.createElementNS( ns, "segments" );
295  QDomElement elemArcString = doc.createElementNS( ns, "ArcString" );
296  elemArcString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D() ) );
297  elemSegments.appendChild( elemArcString );
298  elemCurve.appendChild( elemSegments );
299  return elemCurve;
300 }
301 
302 QString QgsCircularStringV2::asJSON( int precision ) const
303 {
304  // GeoJSON does not support curves
305  QgsLineStringV2* line = curveToLine();
306  QString json = line->asJSON( precision );
307  delete line;
308  return json;
309 }
310 
311 //curve interface
313 {
314  int nPoints = numPoints();
315  double length = 0;
316  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
317  {
318  length += QgsGeometryUtils::circleLength( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2] );
319  }
320  return length;
321 }
322 
324 {
325  if ( numPoints() < 1 )
326  {
327  return QgsPointV2();
328  }
329  return pointN( 0 );
330 }
331 
333 {
334  if ( numPoints() < 1 )
335  {
336  return QgsPointV2();
337  }
338  return pointN( numPoints() - 1 );
339 }
340 
342 {
343  QgsLineStringV2* line = new QgsLineStringV2();
345  int nPoints = numPoints();
346 
347  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
348  {
349  segmentize( pointN( i ), pointN( i + 1 ), pointN( i + 2 ), points );
350  }
351 
352  line->setPoints( points );
353  return line;
354 }
355 
357 {
358  return qMin( mX.size(), mY.size() );
359 }
360 
362 {
363  if ( qMin( mX.size(), mY.size() ) <= i )
364  {
365  return QgsPointV2();
366  }
367 
368  double x = mX.at( i );
369  double y = mY.at( i );
370  double z = 0;
371  double m = 0;
372 
373  if ( is3D() )
374  {
375  z = mZ.at( i );
376  }
377  if ( isMeasure() )
378  {
379  m = mM.at( i );
380  }
381 
383  if ( is3D() && isMeasure() )
384  {
386  }
387  else if ( is3D() )
388  {
390  }
391  else if ( isMeasure() )
392  {
394  }
395  return QgsPointV2( t, x, y, z, m );
396 }
397 
399 {
400  pts.clear();
401  int nPts = numPoints();
402  for ( int i = 0; i < nPts; ++i )
403  {
404  pts.push_back( pointN( i ) );
405  }
406 }
407 
409 {
410  if ( points.size() < 1 )
411  {
412  mWkbType = QgsWKBTypes::Unknown; mX.clear(); mY.clear(); mZ.clear(); mM.clear();
413  return;
414  }
415 
416  //get wkb type from first point
417  const QgsPointV2& firstPt = points.at( 0 );
418  bool hasZ = firstPt.is3D();
419  bool hasM = firstPt.isMeasure();
420 
422 
423  mX.resize( points.size() );
424  mY.resize( points.size() );
425  if ( hasZ )
426  {
427  mZ.resize( points.size() );
428  }
429  else
430  {
431  mZ.clear();
432  }
433  if ( hasM )
434  {
435  mM.resize( points.size() );
436  }
437  else
438  {
439  mM.clear();
440  }
441 
442  for ( int i = 0; i < points.size(); ++i )
443  {
444  mX[i] = points[i].x();
445  mY[i] = points[i].y();
446  if ( hasZ )
447  {
448  mZ[i] = points[i].z();
449  }
450  if ( hasM )
451  {
452  mM[i] = points[i].m();
453  }
454  }
455 }
456 
457 void QgsCircularStringV2::segmentize( const QgsPointV2& p1, const QgsPointV2& p2, const QgsPointV2& p3, QList<QgsPointV2>& points ) const
458 {
459  //adapted code from postgis
460  double radius = 0;
461  double centerX = 0;
462  double centerY = 0;
463  QgsGeometryUtils::circleCenterRadius( p1, p2, p3, radius, centerX, centerY );
464  int segSide = segmentSide( p1, p3, p2 );
465 
466  if ( p1 != p3 && ( radius < 0 || qgsDoubleNear( segSide, 0.0 ) ) ) //points are colinear
467  {
468  points.append( p1 );
469  points.append( p2 );
470  points.append( p3 );
471  return;
472  }
473 
474  bool clockwise = false;
475  if ( segSide == -1 )
476  {
477  clockwise = true;
478  }
479 
480  double increment = fabs( M_PI_2 / 90 ); //one segment per degree
481 
482  //angles of pt1, pt2, pt3
483  double a1 = atan2( p1.y() - centerY, p1.x() - centerX );
484  double a2 = atan2( p2.y() - centerY, p2.x() - centerX );
485  double a3 = atan2( p3.y() - centerY, p3.x() - centerX );
486 
487  if ( clockwise )
488  {
489  increment *= -1;
490  /* Adjust a3 down so we can decrement from a1 to a3 cleanly */
491  if ( a3 >= a1 )
492  a3 -= 2.0 * M_PI;
493  if ( a2 > a1 )
494  a2 -= 2.0 * M_PI;
495  }
496  else
497  {
498  /* Adjust a3 up so we can increment from a1 to a3 cleanly */
499  if ( a3 <= a1 )
500  a3 += 2.0 * M_PI;
501  if ( a2 < a1 )
502  a2 += 2.0 * M_PI;
503  }
504 
505  bool hasZ = is3D();
506  bool hasM = isMeasure();
507 
508  double x, y;
509  double z = 0;
510  double m = 0;
511 
512  points.append( p1 );
513  if ( p2 != p3 && p1 != p2 ) //draw straight line segment if two points have the same position
514  {
515  for ( double angle = a1 + increment; clockwise ? angle > a3 : angle < a3; angle += increment )
516  {
517  x = centerX + radius * cos( angle );
518  y = centerY + radius * sin( angle );
519 
520  if ( !hasZ && !hasM )
521  {
522  points.append( QgsPointV2( x, y ) );
523  continue;
524  }
525 
526  if ( hasZ )
527  {
528  z = interpolateArc( angle, a1, a2, a3, p1.z(), p2.z(), p3.z() );
529  }
530  if ( hasM )
531  {
532  m = interpolateArc( angle, a1, a2, a3, p1.m(), p2.m(), p3.m() );
533  }
534 
535  points.append( QgsPointV2( mWkbType, x, y, z, m ) );
536  }
537  }
538  points.append( p3 );
539 }
540 
541 int QgsCircularStringV2::segmentSide( const QgsPointV2& pt1, const QgsPointV2& pt3, const QgsPointV2& pt2 ) const
542 {
543  double side = (( pt2.x() - pt1.x() ) * ( pt3.y() - pt1.y() ) - ( pt3.x() - pt1.x() ) * ( pt2.y() - pt1.y() ) );
544  if ( side == 0.0 )
545  {
546  return 0;
547  }
548  else
549  {
550  if ( side < 0 )
551  {
552  return -1;
553  }
554  if ( side > 0 )
555  {
556  return 1;
557  }
558  return 0;
559  }
560 }
561 
562 double QgsCircularStringV2::interpolateArc( double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3 ) const
563 {
564  /* Counter-clockwise sweep */
565  if ( a1 < a2 )
566  {
567  if ( angle <= a2 )
568  return zm1 + ( zm2 - zm1 ) * ( angle - a1 ) / ( a2 - a1 );
569  else
570  return zm2 + ( zm3 - zm2 ) * ( angle - a2 ) / ( a3 - a2 );
571  }
572  /* Clockwise sweep */
573  else
574  {
575  if ( angle >= a2 )
576  return zm1 + ( zm2 - zm1 ) * ( a1 - angle ) / ( a1 - a2 );
577  else
578  return zm2 + ( zm3 - zm2 ) * ( a2 - angle ) / ( a2 - a3 );
579  }
580 }
581 
583 {
584  QPainterPath path;
585  addToPainterPath( path );
586  p.drawPath( path );
587 }
588 
590 {
591  double* zArray = mZ.data();
592 
593  bool hasZ = is3D();
594  int nPoints = numPoints();
595  if ( !hasZ )
596  {
597  zArray = new double[nPoints];
598  for ( int i = 0; i < nPoints; ++i )
599  {
600  zArray[i] = 0;
601  }
602  }
603  ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
604  if ( !hasZ )
605  {
606  delete[] zArray;
607  }
608 }
609 
611 {
612  int nPoints = numPoints();
613  for ( int i = 0; i < nPoints; ++i )
614  {
615  qreal x, y;
616  t.map( mX[i], mY[i], &x, &y );
617  mX[i] = x; mY[i] = y;
618  }
619 }
620 
621 #if 0
622 void QgsCircularStringV2::clip( const QgsRectangle& rect )
623 {
624  //todo...
625 }
626 #endif
627 
629 {
630  int nPoints = numPoints();
631  if ( nPoints < 1 )
632  {
633  return;
634  }
635 
636  if ( path.isEmpty() || path.currentPosition() != QPointF( mX[0], mY[0] ) )
637  {
638  path.moveTo( QPointF( mX[0], mY[0] ) );
639  }
640 
641  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
642  {
644  segmentize( QgsPointV2( mX[i], mY[i] ), QgsPointV2( mX[i + 1], mY[i + 1] ), QgsPointV2( mX[i + 2], mY[i + 2] ), pt );
645  for ( int j = 1; j < pt.size(); ++j )
646  {
647  path.lineTo( pt.at( j ).x(), pt.at( j ).y() );
648  }
649  //arcTo( path, QPointF( mX[i], mY[i] ), QPointF( mX[i + 1], mY[i + 1] ), QPointF( mX[i + 2], mY[i + 2] ) );
650  }
651 
652  //if number of points is even, connect to last point with straight line (even though the circular string is not valid)
653  if ( nPoints % 2 == 0 )
654  {
655  path.lineTo( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
656  }
657 }
658 
659 void QgsCircularStringV2::arcTo( QPainterPath& path, const QPointF& pt1, const QPointF& pt2, const QPointF& pt3 )
660 {
661  double centerX, centerY, radius;
662  QgsGeometryUtils::circleCenterRadius( QgsPointV2( pt1.x(), pt1.y() ), QgsPointV2( pt2.x(), pt2.y() ), QgsPointV2( pt3.x(), pt3.y() ),
663  radius, centerX, centerY );
664 
665  double p1Angle = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
666  double sweepAngle = QgsGeometryUtils::sweepAngle( centerX, centerY, pt1.x(), pt1.y(), pt2.x(), pt2.y(), pt3.x(), pt3.y() );
667 
668  double diameter = 2 * radius;
669  path.arcTo( centerX - radius, centerY - radius, diameter, diameter, p1Angle, sweepAngle );
670 }
671 
673 {
674  draw( p );
675 }
676 
677 bool QgsCircularStringV2::insertVertex( const QgsVertexId& position, const QgsPointV2& vertex )
678 {
679  if ( position.vertex > mX.size() || position.vertex < 1 )
680  {
681  return false;
682  }
683 
684  mX.insert( position.vertex, vertex.x() );
685  mY.insert( position.vertex, vertex.y() );
686  if ( is3D() )
687  {
688  mZ.insert( position.vertex, vertex.z() );
689  }
690  if ( isMeasure() )
691  {
692  mM.insert( position.vertex, vertex.m() );
693  }
694 
695  bool vertexNrEven = ( position.vertex % 2 == 0 );
696  if ( vertexNrEven )
697  {
698  insertVertexBetween( position.vertex - 2, position.vertex - 1, position.vertex );
699  }
700  else
701  {
702  insertVertexBetween( position.vertex, position.vertex + 1, position.vertex - 1 );
703  }
704  mBoundingBox = QgsRectangle(); //set bounding box invalid
705  return true;
706 }
707 
708 bool QgsCircularStringV2::moveVertex( const QgsVertexId& position, const QgsPointV2& newPos )
709 {
710  if ( position.vertex < 0 || position.vertex >= mX.size() )
711  {
712  return false;
713  }
714 
715  mX[position.vertex] = newPos.x();
716  mY[position.vertex] = newPos.y();
717  if ( is3D() && newPos.is3D() )
718  {
719  mZ[position.vertex] = newPos.z();
720  }
721  if ( isMeasure() && newPos.isMeasure() )
722  {
723  mM[position.vertex] = newPos.m();
724  }
725  mBoundingBox = QgsRectangle(); //set bounding box invalid
726  return true;
727 }
728 
730 {
731  int nVertices = this->numPoints();
732  if ( nVertices < 4 ) //circular string must have at least 3 vertices
733  {
734  return false;
735  }
736  if ( position.vertex < 1 || position.vertex > ( nVertices - 2 ) )
737  {
738  return false;
739  }
740 
741  if ( position.vertex < ( nVertices - 2 ) )
742  {
743  //remove this and the following vertex
744  deleteVertex( position.vertex + 1 );
745  deleteVertex( position.vertex );
746  }
747  else //remove this and the preceding vertex
748  {
749  deleteVertex( position.vertex );
750  deleteVertex( position.vertex - 1 );
751  }
752 
753  mBoundingBox = QgsRectangle(); //set bounding box invalid
754  return true;
755 }
756 
758 {
759  mX.remove( i );
760  mY.remove( i );
761  if ( is3D() )
762  {
763  mZ.remove( i );
764  }
765  if ( isMeasure() )
766  {
767  mM.remove( i );
768  }
769 }
770 
771 double QgsCircularStringV2::closestSegment( const QgsPointV2& pt, QgsPointV2& segmentPt, QgsVertexId& vertexAfter, bool* leftOf, double epsilon ) const
772 {
773  Q_UNUSED( epsilon );
774  double minDist = std::numeric_limits<double>::max();
775  QgsPointV2 minDistSegmentPoint;
776  QgsVertexId minDistVertexAfter;
777  bool minDistLeftOf = false;
778 
779  double currentDist = 0.0;
780 
781  int nPoints = numPoints();
782  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
783  {
784  currentDist = closestPointOnArc( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2], pt, segmentPt, vertexAfter, leftOf, epsilon );
785  if ( currentDist < minDist )
786  {
787  minDist = currentDist;
788  minDistSegmentPoint = segmentPt;
789  minDistVertexAfter.vertex = vertexAfter.vertex + i;
790  if ( leftOf )
791  {
792  minDistLeftOf = *leftOf;
793  }
794  }
795  }
796 
797  segmentPt = minDistSegmentPoint;
798  vertexAfter = minDistVertexAfter;
799  vertexAfter.part = 0; vertexAfter.ring = 0;
800  if ( leftOf )
801  {
802  *leftOf = minDistLeftOf;
803  }
804  return minDist;
805 }
806 
808 {
809  if ( i >= numPoints() )
810  {
811  return false;
812  }
813  vertex = pointN( i );
814  type = ( i % 2 == 0 ) ? QgsVertexId::SegmentVertex : QgsVertexId::CurveVertex;
815  return true;
816 }
817 
818 void QgsCircularStringV2::sumUpArea( double& sum ) const
819 {
820  int maxIndex = numPoints() - 1;
821 
822  for ( int i = 0; i < maxIndex; i += 2 )
823  {
824  QgsPointV2 p1( mX[i], mY[i] );
825  QgsPointV2 p2( mX[i + 1], mY[i + 1] );
826  QgsPointV2 p3( mX[i + 2], mY[i + 2] );
827 
828  //segment is a full circle, p2 is the center point
829  if ( p1 == p3 )
830  {
831  double r2 = QgsGeometryUtils::sqrDistance2D( p1, p2 );
832  sum += M_PI * r2;
833  continue;
834  }
835 
836  sum += 0.5 * ( mX[i] * mY[i+2] - mY[i] * mX[i+2] );
837 
838  //calculate area between circle and chord, then sum / subtract from total area
839  double midPointX = ( p1.x() + p3.x() ) / 2.0;
840  double midPointY = ( p1.y() + p3.y() ) / 2.0;
841 
842  double radius, centerX, centerY;
843  QgsGeometryUtils::circleCenterRadius( p1, p2, p3, radius, centerX, centerY );
844 
845  double d = sqrt( QgsGeometryUtils::sqrDistance2D( QgsPointV2( centerX, centerY ), QgsPointV2( midPointX, midPointY ) ) );
846  double r2 = radius * radius;
847 
848  if ( d > radius )
849  {
850  //d cannot be greater than radius, something must be wrong...
851  continue;
852  }
853 
854  bool circlePointLeftOfLine = QgsGeometryUtils::leftOfLine( p2.x(), p2.y(), p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
855  bool centerPointLeftOfLine = QgsGeometryUtils::leftOfLine( centerX, centerY, p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
856 
857  double cov = 0.5 - d * sqrt( r2 - d * d ) / ( M_PI * r2 ) - 1 / M_PI * asin( d / radius );
858  double circleChordArea = 0;
859  if ( circlePointLeftOfLine == centerPointLeftOfLine )
860  {
861  circleChordArea = M_PI * r2 * ( 1 - cov );
862  }
863  else
864  {
865  circleChordArea = M_PI * r2 * cov;
866  }
867 
868  if ( !circlePointLeftOfLine )
869  {
870  sum += circleChordArea;
871  }
872  else
873  {
874  sum -= circleChordArea;
875  }
876  }
877 }
878 
879 double QgsCircularStringV2::closestPointOnArc( double x1, double y1, double x2, double y2, double x3, double y3,
880  const QgsPointV2& pt, QgsPointV2& segmentPt, QgsVertexId& vertexAfter, bool* leftOf, double epsilon )
881 {
882  double radius, centerX, centerY;
883  QgsPointV2 pt1( x1, y1 );
884  QgsPointV2 pt2( x2, y2 );
885  QgsPointV2 pt3( x3, y3 );
886 
887  QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
888  double angle = QgsGeometryUtils::ccwAngle( pt.y() - centerY, pt.x() - centerX );
889  double angle1 = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
890  double angle2 = QgsGeometryUtils::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
891  double angle3 = QgsGeometryUtils::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
892 
893  bool clockwise = QgsGeometryUtils::circleClockwise( angle1, angle2, angle3 );
894 
895  if ( QgsGeometryUtils::angleOnCircle( angle, angle1, angle2, angle3 ) )
896  {
897  //get point on line center -> pt with distance radius
898  segmentPt = QgsGeometryUtils::pointOnLineWithDistance( QgsPointV2( centerX, centerY ), pt, radius );
899 
900  //vertexAfter
901  vertexAfter.vertex = QgsGeometryUtils::circleAngleBetween( angle, angle1, angle2, clockwise ) ? 1 : 2;
902  }
903  else
904  {
905  double distPtPt1 = QgsGeometryUtils::sqrDistance2D( pt, pt1 );
906  double distPtPt3 = QgsGeometryUtils::sqrDistance2D( pt, pt3 );
907  segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
908  vertexAfter.vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
909  }
910 
911  double sqrDistance = QgsGeometryUtils::sqrDistance2D( segmentPt, pt );
912  //prevent rounding errors if the point is directly on the segment
913  if ( qgsDoubleNear( sqrDistance, 0.0, epsilon ) )
914  {
915  segmentPt.setX( pt.x() );
916  segmentPt.setY( pt.y() );
917  sqrDistance = 0.0;
918  }
919 
920  if ( leftOf )
921  {
922  *leftOf = clockwise ? sqrDistance > radius : sqrDistance < radius;
923  }
924 
925  return sqrDistance;
926 }
927 
928 void QgsCircularStringV2::insertVertexBetween( int after, int before, int pointOnCircle )
929 {
930  double xAfter = mX[after];
931  double yAfter = mY[after];
932  double xBefore = mX[before];
933  double yBefore = mY[before];
934  double xOnCircle = mX[ pointOnCircle ];
935  double yOnCircle = mY[ pointOnCircle ];
936 
937  double radius, centerX, centerY;
938  QgsGeometryUtils::circleCenterRadius( QgsPointV2( xAfter, yAfter ), QgsPointV2( xBefore, yBefore ), QgsPointV2( xOnCircle, yOnCircle ), radius, centerX, centerY );
939 
940  double x = ( xAfter + xBefore ) / 2.0;
941  double y = ( yAfter + yBefore ) / 2.0;
942 
943  QgsPointV2 newVertex = QgsGeometryUtils::pointOnLineWithDistance( QgsPointV2( centerX, centerY ), QgsPointV2( x, y ), radius );
944  mX.insert( before, newVertex.x() );
945  mY.insert( before, newVertex.y() );
946 
947  if ( is3D() )
948  {
949  mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
950  }
951  if ( isMeasure() )
952  {
953  mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
954  }
955 }
956 
958 {
959  int before = vId.vertex - 1;
960  int vertex = vId.vertex;
961  int after = vId.vertex + 1;
962 
963  if ( vId.vertex % 2 != 0 ) // a curve vertex
964  {
965  if ( vId.vertex >= 1 && vId.vertex < numPoints() - 1 )
966  {
967  return QgsGeometryUtils::circleTangentDirection( QgsPointV2( mX[vertex], mY[vertex] ), QgsPointV2( mX[before], mY[before] ),
968  QgsPointV2( mX[vertex], mY[vertex] ), QgsPointV2( mX[after], mY[after] ) );
969  }
970  }
971  else //a point vertex
972  {
973  if ( vId.vertex == 0 )
974  {
975  return QgsGeometryUtils::circleTangentDirection( QgsPointV2( mX[0], mY[0] ), QgsPointV2( mX[0], mY[0] ),
976  QgsPointV2( mX[1], mY[1] ), QgsPointV2( mX[2], mY[2] ) );
977  }
978  if ( vId.vertex >= numPoints() - 1 )
979  {
980  if ( numPoints() < 3 )
981  {
982  return 0.0;
983  }
984  int a = numPoints() - 3;
985  int b = numPoints() - 2;
986  int c = numPoints() - 1;
987  return QgsGeometryUtils::circleTangentDirection( QgsPointV2( mX[c], mY[c] ), QgsPointV2( mX[a], mY[a] ),
988  QgsPointV2( mX[b], mY[b] ), QgsPointV2( mX[c], mY[c] ) );
989  }
990  else
991  {
992  if ( vId.vertex + 2 > numPoints() - 1 )
993  {
994  return 0.0;
995  }
996 
997  int vertex1 = vId.vertex - 2;
998  int vertex2 = vId.vertex - 1;
999  int vertex3 = vId.vertex;
1000  double angle1 = QgsGeometryUtils::circleTangentDirection( QgsPointV2( mX[vertex3], mY[vertex3] ),
1001  QgsPointV2( mX[vertex1], mY[vertex1] ), QgsPointV2( mX[vertex2], mY[vertex2] ), QgsPointV2( mX[vertex3], mY[vertex3] ) );
1002  int vertex4 = vId.vertex + 1;
1003  int vertex5 = vId.vertex + 2;
1004  double angle2 = QgsGeometryUtils::circleTangentDirection( QgsPointV2( mX[vertex3], mY[vertex3] ),
1005  QgsPointV2( mX[vertex3], mY[vertex3] ), QgsPointV2( mX[vertex4], mY[vertex4] ), QgsPointV2( mX[vertex5], mY[vertex5] ) );
1006  return QgsGeometryUtils::averageAngle( angle1, angle2 );
1007  }
1008  }
1009  return 0.0;
1010 }
1011 
1012 bool QgsCircularStringV2::addZValue( double zValue )
1013 {
1014  if ( QgsWKBTypes::hasZ( mWkbType ) )
1015  return false;
1016 
1018 
1019  int nPoints = numPoints();
1020  mZ.clear();
1021  mZ.reserve( nPoints );
1022  for ( int i = 0; i < nPoints; ++i )
1023  {
1024  mZ << zValue;
1025  }
1026  return true;
1027 }
1028 
1029 bool QgsCircularStringV2::addMValue( double mValue )
1030 {
1031  if ( QgsWKBTypes::hasM( mWkbType ) )
1032  return false;
1033 
1035 
1036  int nPoints = numPoints();
1037  mM.clear();
1038  mM.reserve( nPoints );
1039  for ( int i = 0; i < nPoints; ++i )
1040  {
1041  mM << mValue;
1042  }
1043  return true;
1044 }
void clear()
void transformCoords(const int &numPoint, double *x, double *y, double *z, TransformDirection direction=ForwardTransform) const
Transform an array of coordinates to a different Coordinate System If the direction is ForwardTransfo...
QgsWKBTypes::Type wkbType() const
Returns the WKB type of the geometry.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
static QgsPointV2 pointOnLineWithDistance(const QgsPointV2 &startPoint, const QgsPointV2 &directionPoint, double distance)
Returns a point a specified distance toward a second point.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
QDomElement asGML2(QDomDocument &doc, int precision=17, const QString &ns="gml") const override
Returns a GML2 representation of the geometry.
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) ...
virtual QgsRectangle calculateBoundingBox() const override
Calculates the minimal bounding box for the geometry.
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 (...
int wkbSize() const override
Returns the size of the WKB representation of the geometry.
virtual bool fromWkb(const unsigned char *wkb) override
Sets the geometry from a WKB string.
static bool circleAngleBetween(double angle, double angle1, double angle2, bool clockwise)
Returns true if, in a circle, angle is between angle1 and angle2.
QPointF currentPosition() const
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)
double x() const
Definition: qgspointv2.h:42
void push_back(const T &value)
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Angle between two linear segments.
QPoint map(const QPoint &point) const
Circular string geometry type.
virtual bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
virtual bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
const T & at(int i) const
static QString pointsToWKT(const QList< QgsPointV2 > &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
virtual QgsLineStringV2 * curveToLine() const override
Returns a new line string geometry corresponding to a segmentized approximation of the curve...
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
void insert(int i, const T &value)
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.
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
void moveTo(const QPointF &point)
void setX(double x)
Definition: qgspointv2.h:47
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.
virtual void clear() override
Clears the geometry, ie reset it to a null geometry.
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)
static endian_t endian()
Returns whether this machine uses big or little endian.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:268
void setY(double y)
Definition: qgspointv2.h:48
int size() const
#define M_PI_2
Definition: util.cpp:47
virtual double length() const override
Returns the length of the geometry.
QgsPointV2 pointN(int i) const
Returns the point at index i within the circular string.
double y() const
Definition: qgspointv2.h:43
double ANALYSIS_EXPORT max(double x, double y)
Returns the maximum of two doubles or the first argument if both are equal.
QgsWKBTypes::Type readHeader() const
Definition: qgswkbptr.cpp:8
T * data()
void clear()
static Type flatType(Type type)
Definition: qgswkbtypes.cpp:46
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
static bool circleClockwise(double angle1, double angle2, double angle3)
Returns true if circle is ordered clockwise.
void combineExtentWith(QgsRectangle *rect)
expand the rectangle so that covers both the original rectangle and the given rectangle ...
qreal x() const
qreal y() const
void append(const T &value)
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
static bool hasM(Type type)
Tests whether a WKB type contains m values.
void resize(int size)
void setPoints(const QList< QgsPointV2 > &points)
Sets the circular string's points.
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.
bool isMeasure() const
Returns true if the geometry contains m values.
double z() const
Definition: qgspointv2.h:44
QDomElement asGML2(QDomDocument &doc, int precision=17, const QString &ns="gml") const override
Returns a GML2 representation of the geometry.
Line string geometry type.
void lineTo(const QPointF &endPoint)
static Type addZ(Type type)
Adds the z dimension to a WKB type and returns the new type.
Point geometry type.
Definition: qgspointv2.h:29
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform) override
Transforms the geometry using a coordinate transform.
void remove(int i)
#define M_PI
void sumUpArea(double &sum) const override
Calculates the area of the curve.
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
void setZMTypeFromSubGeometry(const QgsAbstractGeometryV2 *subggeom, QgsWKBTypes::Type baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
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.
virtual QgsPointV2 startPoint() const override
Returns the starting point of the curve.
double closestSegment(const QgsPointV2 &pt, QgsPointV2 &segmentPt, QgsVertexId &vertexAfter, bool *leftOf, double epsilon) const override
Searches for the closest segment of the geometry to a given point.
virtual QString geometryType() const override
Returns a unique string representing the geometry type.
QString asJSON(int precision=17) const override
Returns a GeoJSON representation of the geometry.
virtual bool deleteVertex(const QgsVertexId &position) override
Deletes a vertex within the geometry.
unsigned char * asWkb(int &binarySize) const override
Returns a WKB representation of the geometry.
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)
void reserve(int size)
static QDomElement pointsToGML3(const QList< QgsPointV2 > &points, QDomDocument &doc, int precision, const QString &ns, bool is3D)
Returns a gml::posList DOM element.
virtual bool insertVertex(const QgsVertexId &position, const QgsPointV2 &vertex) override
Inserts a vertex into the geometry.
virtual bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
virtual bool moveVertex(const QgsVertexId &position, const QgsPointV2 &newPos) override
Moves a vertex within the geometry.
virtual QgsCircularStringV2 * clone() const override
Clones the geometry by performing a deep copy.
virtual QgsPointV2 endPoint() const override
Returns the end point of the curve.
const T & at(int i) const
bool pointAt(int i, QgsPointV2 &vertex, QgsVertexId::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
void points(QList< QgsPointV2 > &pts) const override
Returns a list of points within the curve.
void drawPath(const QPainterPath &path)
void setPoints(const QList< QgsPointV2 > &points)
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.
QgsAbstractGeometryV2 * segmentize() const override
Returns a version of the geometry without curves.
Definition: qgscurvev2.cpp:80
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
bool isEmpty() const
static QList< QgsPointV2 > pointsFromWKT(const QString &wktCoordinateList, bool is3D, bool isMeasure)
Returns a list of points contained in a WKT string.
Class for doing transforms between two map coordinate systems.
double m() const
Definition: qgspointv2.h:45
double vertexAngle(const QgsVertexId &vertex) const override
Returns approximate rotation angle for a vertex.
static Type parseType(const QString &wktStr)
Definition: qgswkbtypes.cpp:56
double ANALYSIS_EXPORT leftOf(Point3D *thepoint, Point3D *p1, Point3D *p2)
Returns whether 'thepoint' is left or right of the line from 'p1' to 'p2'.
const_iterator constEnd() const
const_iterator constBegin() const
Abstract base class for curved geometry type.
Definition: qgscurvev2.h:32
QDomElement asGML3(QDomDocument &doc, int precision=17, const QString &ns="gml") const override
Returns a GML3 representation of the geometry.
int size() const
static void pointsToWKB(QgsWkbPtr &wkb, const QList< QgsPointV2 > &points, bool is3D, bool isMeasure)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
void arcTo(const QRectF &rectangle, qreal startAngle, qreal sweepLength)
QString asJSON(int precision=17) const override
Returns a GeoJSON representation of the geometry.
static Type addM(Type type)
Adds the m dimension to a WKB type and returns the new type.
int numPoints() const override
Returns the number of points in the curve.