QGIS API Documentation  2.10.1-Pisa
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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  return bbox;
70 }
71 
72 QgsRectangle QgsCircularStringV2::segmentBoundingBox( const QgsPointV2& pt1, const QgsPointV2& pt2, const QgsPointV2& pt3 )
73 {
74  double centerX, centerY, radius;
75  QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
76 
77  double p1Angle = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
78  if ( p1Angle > 360 )
79  {
80  p1Angle -= 360;
81  }
82  double p2Angle = QgsGeometryUtils::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
83  if ( p2Angle > 360 )
84  {
85  p2Angle -= 360;
86  }
87  double p3Angle = QgsGeometryUtils::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
88  if ( p3Angle > 360 )
89  {
90  p3Angle -= 360;
91  }
92 
93  //start point, end point and compass points in between can be on bounding box
94  QgsRectangle bbox( pt1.x(), pt1.y(), pt1.x(), pt1.y() );
95  bbox.combineExtentWith( pt3.x(), pt3.y() );
96 
97  QList<QgsPointV2> compassPoints = compassPointsOnSegment( p1Angle, p2Angle, p3Angle, centerX, centerY, radius );
98  QList<QgsPointV2>::const_iterator cpIt = compassPoints.constBegin();
99  for ( ; cpIt != compassPoints.constEnd(); ++cpIt )
100  {
101  bbox.combineExtentWith( cpIt->x(), cpIt->y() );
102  }
103  return bbox;
104 }
105 
106 QList<QgsPointV2> QgsCircularStringV2::compassPointsOnSegment( double p1Angle, double p2Angle, double p3Angle, double centerX, double centerY, double radius )
107 {
108  QList<QgsPointV2> pointList;
109 
110  QgsPointV2 nPoint( centerX, centerY + radius );
111  QgsPointV2 ePoint( centerX + radius, centerY );
112  QgsPointV2 sPoint( centerX, centerY - radius );
113  QgsPointV2 wPoint( centerX - radius, centerY );
114 
115  if ( p3Angle >= p1Angle )
116  {
117  if ( p2Angle > p1Angle && p2Angle < p3Angle )
118  {
119  if ( p1Angle <= 90 && p3Angle >= 90 )
120  {
121  pointList.append( nPoint );
122  }
123  if ( p1Angle <= 180 && p3Angle >= 180 )
124  {
125  pointList.append( wPoint );
126  }
127  if ( p1Angle <= 270 && p3Angle >= 270 )
128  {
129  pointList.append( sPoint );
130  }
131  }
132  else
133  {
134  pointList.append( ePoint );
135  if ( p1Angle >= 90 || p3Angle <= 90 )
136  {
137  pointList.append( nPoint );
138  }
139  if ( p1Angle >= 180 || p3Angle <= 180 )
140  {
141  pointList.append( wPoint );
142  }
143  if ( p1Angle >= 270 || p3Angle <= 270 )
144  {
145  pointList.append( sPoint );
146  }
147  }
148  }
149  else
150  {
151  if ( p2Angle < p1Angle && p2Angle > p3Angle )
152  {
153  if ( p1Angle >= 270 && p3Angle <= 270 )
154  {
155  pointList.append( sPoint );
156  }
157  if ( p1Angle >= 180 && p3Angle <= 180 )
158  {
159  pointList.append( wPoint );
160  }
161  if ( p1Angle >= 90 && p3Angle <= 90 )
162  {
163  pointList.append( nPoint );
164  }
165  }
166  else
167  {
168  pointList.append( ePoint );
169  if ( p1Angle <= 270 || p3Angle >= 270 )
170  {
171  pointList.append( sPoint );
172  }
173  if ( p1Angle <= 180 || p3Angle >= 180 )
174  {
175  pointList.append( wPoint );
176  }
177  if ( p1Angle <= 90 || p3Angle >= 90 )
178  {
179  pointList.append( nPoint );
180  }
181  }
182  }
183  return pointList;
184 }
185 
186 bool QgsCircularStringV2::fromWkb( const unsigned char* wkb )
187 {
188  if ( !wkb )
189  {
190  return false;
191  }
192 
193  QgsConstWkbPtr wkbPtr( wkb );
194  QgsWKBTypes::Type type = wkbPtr.readHeader();
196  {
197  return false;
198  }
199  mWkbType = type;
200 
201  //type
202  bool hasZ = is3D();
203  bool hasM = isMeasure();
204  int nVertices = 0;
205  wkbPtr >> nVertices;
206  mX.resize( nVertices );
207  mY.resize( nVertices );
208  hasZ ? mZ.resize( nVertices ) : mZ.clear();
209  hasM ? mM.resize( nVertices ) : mM.clear();
210  for ( int i = 0; i < nVertices; ++i )
211  {
212  wkbPtr >> mX[i];
213  wkbPtr >> mY[i];
214  if ( hasZ )
215  {
216  wkbPtr >> mZ[i];
217  }
218  if ( hasM )
219  {
220  wkbPtr >> mM[i];
221  }
222  }
223 
224  return true;
225 }
226 
228 {
229  clear();
230 
232 
233  if ( QgsWKBTypes::flatType( parts.first ) != QgsWKBTypes::parseType( geometryType() ) )
234  return false;
235  mWkbType = parts.first;
236 
237  setPoints( QgsGeometryUtils::pointsFromWKT( parts.second, is3D(), isMeasure() ) );
238  return true;
239 }
240 
242 {
243  int size = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
244  size += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
245  return size;
246 }
247 
248 unsigned char* QgsCircularStringV2::asWkb( int& binarySize ) const
249 {
250  binarySize = wkbSize();
251  unsigned char* geomPtr = new unsigned char[binarySize];
252  QgsWkbPtr wkb( geomPtr );
253  wkb << static_cast<char>( QgsApplication::endian() );
254  wkb << static_cast<quint32>( wkbType() );
255  QList<QgsPointV2> pts;
256  points( pts );
257  QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure() );
258  return geomPtr;
259 }
260 
261 QString QgsCircularStringV2::asWkt( int precision ) const
262 {
263  QString wkt = wktTypeStr() + " ";
264  QList<QgsPointV2> pts;
265  points( pts );
266  wkt += QgsGeometryUtils::pointsToWKT( pts, precision, is3D(), isMeasure() );
267  return wkt;
268 }
269 
270 QDomElement QgsCircularStringV2::asGML2( QDomDocument& doc, int precision, const QString& ns ) const
271 {
272  // GML2 does not support curves
273  QgsLineStringV2* line = curveToLine();
274  QDomElement gml = line->asGML2( doc, precision, ns );
275  delete line;
276  return gml;
277 }
278 
279 QDomElement QgsCircularStringV2::asGML3( QDomDocument& doc, int precision, const QString& ns ) const
280 {
281  QList<QgsPointV2> pts;
282  points( pts );
283 
284  QDomElement elemCurve = doc.createElementNS( ns, "Curve" );
285  QDomElement elemSegments = doc.createElementNS( ns, "segments" );
286  QDomElement elemArcString = doc.createElementNS( ns, "ArcString" );
287  elemArcString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D() ) );
288  elemSegments.appendChild( elemArcString );
289  elemCurve.appendChild( elemSegments );
290  return elemCurve;
291 }
292 
293 QString QgsCircularStringV2::asJSON( int precision ) const
294 {
295  // GeoJSON does not support curves
296  QgsLineStringV2* line = curveToLine();
297  QString json = line->asJSON( precision );
298  delete line;
299  return json;
300 }
301 
302 //curve interface
304 {
305  int nPoints = numPoints();
306  double length = 0;
307  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
308  {
309  length += QgsGeometryUtils::circleLength( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2] );
310  }
311  return length;
312 }
313 
315 {
316  if ( numPoints() < 1 )
317  {
318  return QgsPointV2();
319  }
320  return pointN( 0 );
321 }
322 
324 {
325  if ( numPoints() < 1 )
326  {
327  return QgsPointV2();
328  }
329  return pointN( numPoints() - 1 );
330 }
331 
333 {
334  QgsLineStringV2* line = new QgsLineStringV2();
336  int nPoints = numPoints();
337 
338  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
339  {
340  segmentize( pointN( i ), pointN( i + 1 ), pointN( i + 2 ), points );
341  }
342 
343  line->setPoints( points );
344  return line;
345 }
346 
348 {
349  return qMin( mX.size(), mY.size() );
350 }
351 
353 {
354  if ( qMin( mX.size(), mY.size() ) <= i )
355  {
356  return QgsPointV2();
357  }
358 
359  double x = mX.at( i );
360  double y = mY.at( i );
361  double z = 0;
362  double m = 0;
363 
364  if ( is3D() )
365  {
366  z = mZ.at( i );
367  }
368  if ( isMeasure() )
369  {
370  m = mM.at( i );
371  }
372 
374  if ( is3D() && isMeasure() )
375  {
377  }
378  else if ( is3D() )
379  {
381  }
382  else if ( isMeasure() )
383  {
385  }
386  return QgsPointV2( t, x, y, z, m );
387 }
388 
390 {
391  pts.clear();
392  int nPts = numPoints();
393  for ( int i = 0; i < nPts; ++i )
394  {
395  pts.push_back( pointN( i ) );
396  }
397 }
398 
400 {
401  if ( points.size() < 1 )
402  {
403  mWkbType = QgsWKBTypes::Unknown; mX.clear(); mY.clear(); mZ.clear(); mM.clear();
404  return;
405  }
406 
407  //get wkb type from first point
408  const QgsPointV2& firstPt = points.at( 0 );
409  bool hasZ = firstPt.is3D();
410  bool hasM = firstPt.isMeasure();
411 
413 
414  mX.resize( points.size() );
415  mY.resize( points.size() );
416  if ( hasZ )
417  {
418  mZ.resize( points.size() );
419  }
420  else
421  {
422  mZ.clear();
423  }
424  if ( hasM )
425  {
426  mM.resize( points.size() );
427  }
428  else
429  {
430  mM.clear();
431  }
432 
433  for ( int i = 0; i < points.size(); ++i )
434  {
435  mX[i] = points[i].x();
436  mY[i] = points[i].y();
437  if ( hasZ )
438  {
439  mZ[i] = points[i].z();
440  }
441  if ( hasM )
442  {
443  mM[i] = points[i].m();
444  }
445  }
446 }
447 
448 void QgsCircularStringV2::segmentize( const QgsPointV2& p1, const QgsPointV2& p2, const QgsPointV2& p3, QList<QgsPointV2>& points ) const
449 {
450  //adapted code from postgis
451  double radius = 0;
452  double centerX = 0;
453  double centerY = 0;
454  QgsGeometryUtils::circleCenterRadius( p1, p2, p3, radius, centerX, centerY );
455  int segSide = segmentSide( p1, p3, p2 );
456 
457  if ( radius < 0 || qgsDoubleNear( segSide, 0.0 ) ) //points are colinear
458  {
459  points.append( p1 );
460  points.append( p2 );
461  points.append( p3 );
462  return;
463  }
464 
465  bool clockwise = false;
466  if ( segSide == -1 )
467  {
468  clockwise = true;
469  }
470 
471  double increment = fabs( M_PI_2 / 90 ); //one segment per degree
472 
473  //angles of pt1, pt2, pt3
474  double a1 = atan2( p1.y() - centerY, p1.x() - centerX );
475  double a2 = atan2( p2.y() - centerY, p2.x() - centerX );
476  double a3 = atan2( p3.y() - centerY, p3.x() - centerX );
477 
478  if ( clockwise )
479  {
480  increment *= -1;
481  /* Adjust a3 down so we can decrement from a1 to a3 cleanly */
482  if ( a3 > a1 )
483  a3 -= 2.0 * M_PI;
484  if ( a2 > a1 )
485  a2 -= 2.0 * M_PI;
486  }
487  else
488  {
489  /* Adjust a3 up so we can increment from a1 to a3 cleanly */
490  if ( a3 < a1 )
491  a3 += 2.0 * M_PI;
492  if ( a2 < a1 )
493  a2 += 2.0 * M_PI;
494  }
495 
496  bool hasZ = is3D();
497  bool hasM = isMeasure();
498 
499  double x, y;
500  double z = 0;
501  double m = 0;
502 
503  points.append( p1 );
504  if ( p2 != p3 && p1 != p2 ) //draw straight line segment if two points have the same position
505  {
506  for ( double angle = a1 + increment; clockwise ? angle > a3 : angle < a3; angle += increment )
507  {
508  x = centerX + radius * cos( angle );
509  y = centerY + radius * sin( angle );
510 
511  if ( !hasZ && !hasM )
512  {
513  points.append( QgsPointV2( x, y ) );
514  continue;
515  }
516 
517  if ( hasZ )
518  {
519  z = interpolateArc( angle, a1, a2, a3, p1.z(), p2.z(), p3.z() );
520  }
521  if ( hasM )
522  {
523  m = interpolateArc( angle, a1, a2, a3, p1.m(), p2.m(), p3.m() );
524  }
525 
526  points.append( QgsPointV2( mWkbType, x, y, z, m ) );
527  }
528  }
529  points.append( p3 );
530 }
531 
532 int QgsCircularStringV2::segmentSide( const QgsPointV2& pt1, const QgsPointV2& pt3, const QgsPointV2& pt2 ) const
533 {
534  double side = (( pt2.x() - pt1.x() ) * ( pt3.y() - pt1.y() ) - ( pt3.x() - pt1.x() ) * ( pt2.y() - pt1.y() ) );
535  if ( side == 0.0 )
536  {
537  return 0;
538  }
539  else
540  {
541  if ( side < 0 )
542  {
543  return -1;
544  }
545  if ( side > 0 )
546  {
547  return 1;
548  }
549  return 0;
550  }
551 }
552 
553 double QgsCircularStringV2::interpolateArc( double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3 ) const
554 {
555  /* Counter-clockwise sweep */
556  if ( a1 < a2 )
557  {
558  if ( angle <= a2 )
559  return zm1 + ( zm2 - zm1 ) * ( angle - a1 ) / ( a2 - a1 );
560  else
561  return zm2 + ( zm3 - zm2 ) * ( angle - a2 ) / ( a3 - a2 );
562  }
563  /* Clockwise sweep */
564  else
565  {
566  if ( angle >= a2 )
567  return zm1 + ( zm2 - zm1 ) * ( a1 - angle ) / ( a1 - a2 );
568  else
569  return zm2 + ( zm3 - zm2 ) * ( a2 - angle ) / ( a2 - a3 );
570  }
571 }
572 
574 {
575  QPainterPath path;
576  addToPainterPath( path );
577  p.drawPath( path );
578 }
579 
581 {
582  double* zArray = mZ.data();
583 
584  bool hasZ = is3D();
585  int nPoints = numPoints();
586  if ( !hasZ )
587  {
588  zArray = new double[nPoints];
589  for ( int i = 0; i < nPoints; ++i )
590  {
591  zArray[i] = 0;
592  }
593  }
594  ct.transformCoords( nPoints, mX.data(), mY.data(), zArray );
595  if ( !hasZ )
596  {
597  delete[] zArray;
598  }
599 }
600 
602 {
603  int nPoints = numPoints();
604  for ( int i = 0; i < nPoints; ++i )
605  {
606 #ifdef QT_ARCH_ARM
607  qreal x, y;
608  t.map( mX[i], mY[i], &x, &y );
609  mX[i] = x; mY[i] = y;
610 #else
611  t.map( mX[i], mY[i], &mX[i], &mY[i] );
612 #endif
613  }
614 }
615 
616 #if 0
617 void QgsCircularStringV2::clip( const QgsRectangle& rect )
618 {
619  //todo...
620 }
621 #endif
622 
624 {
625  int nPoints = numPoints();
626  if ( nPoints < 1 )
627  {
628  return;
629  }
630 
631  if ( path.isEmpty() || path.currentPosition() != QPointF( mX[0], mY[0] ) )
632  {
633  path.moveTo( QPointF( mX[0], mY[0] ) );
634  }
635 
636  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
637  {
639  segmentize( QgsPointV2( mX[i], mY[i] ), QgsPointV2( mX[i + 1], mY[i + 1] ), QgsPointV2( mX[i + 2], mY[i + 2] ), pt );
640  for ( int j = 1; j < pt.size(); ++j )
641  {
642  path.lineTo( pt.at( j ).x(), pt.at( j ).y() );
643  }
644  //arcTo( path, QPointF( mX[i], mY[i] ), QPointF( mX[i + 1], mY[i + 1] ), QPointF( mX[i + 2], mY[i + 2] ) );
645  }
646 }
647 
648 void QgsCircularStringV2::arcTo( QPainterPath& path, const QPointF& pt1, const QPointF& pt2, const QPointF& pt3 )
649 {
650  double centerX, centerY, radius;
651  QgsGeometryUtils::circleCenterRadius( QgsPointV2( pt1.x(), pt1.y() ), QgsPointV2( pt2.x(), pt2.y() ), QgsPointV2( pt3.x(), pt3.y() ),
652  radius, centerX, centerY );
653 
654  double p1Angle = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
655  double sweepAngle = QgsGeometryUtils::sweepAngle( centerX, centerY, pt1.x(), pt1.y(), pt2.x(), pt2.y(), pt3.x(), pt3.y() );
656 
657  double diameter = 2 * radius;
658  path.arcTo( centerX - radius, centerY - radius, diameter, diameter, p1Angle, sweepAngle );
659 }
660 
662 {
663  draw( p );
664 }
665 
666 bool QgsCircularStringV2::insertVertex( const QgsVertexId& position, const QgsPointV2& vertex )
667 {
668  if ( position.vertex > mX.size() || position.vertex < 1 )
669  {
670  return false;
671  }
672 
673  mX.insert( position.vertex, vertex.x() );
674  mY.insert( position.vertex, vertex.y() );
675  if ( is3D() )
676  {
677  mZ.insert( position.vertex, vertex.z() );
678  }
679  if ( isMeasure() )
680  {
681  mM.insert( position.vertex, vertex.m() );
682  }
683 
684  bool vertexNrEven = ( position.vertex % 2 == 0 );
685  if ( vertexNrEven )
686  {
687  insertVertexBetween( position.vertex - 2, position.vertex - 1, position.vertex );
688  }
689  else
690  {
691  insertVertexBetween( position.vertex, position.vertex + 1, position.vertex - 1 );
692  }
693  return true;
694 }
695 
696 bool QgsCircularStringV2::moveVertex( const QgsVertexId& position, const QgsPointV2& newPos )
697 {
698  if ( position.vertex < 0 || position.vertex >= mX.size() )
699  {
700  return false;
701  }
702 
703  mX[position.vertex] = newPos.x();
704  mY[position.vertex] = newPos.y();
705  if ( is3D() && newPos.is3D() )
706  {
707  mZ[position.vertex] = newPos.z();
708  }
709  if ( isMeasure() && newPos.isMeasure() )
710  {
711  mM[position.vertex] = newPos.m();
712  }
713  mBoundingBox = QgsRectangle(); //set bounding box invalid
714  return true;
715 }
716 
718 {
719  int nVertices = this->numPoints();
720  if ( nVertices < 4 ) //circular string must have at least 3 vertices
721  {
722  return false;
723  }
724  if ( position.vertex < 1 || position.vertex > ( nVertices - 2 ) )
725  {
726  return false;
727  }
728 
729  if ( position.vertex < ( nVertices - 2 ) )
730  {
731  //remove this and the following vertex
732  deleteVertex( position.vertex + 1 );
733  deleteVertex( position.vertex );
734  }
735  else //remove this and the preceding vertex
736  {
737  deleteVertex( position.vertex );
738  deleteVertex( position.vertex - 1 );
739  }
740 
741  return true;
742 }
743 
745 {
746  mX.remove( i );
747  mY.remove( i );
748  if ( is3D() )
749  {
750  mZ.remove( i );
751  }
752  if ( isMeasure() )
753  {
754  mM.remove( i );
755  }
756 }
757 
758 double QgsCircularStringV2::closestSegment( const QgsPointV2& pt, QgsPointV2& segmentPt, QgsVertexId& vertexAfter, bool* leftOf, double epsilon ) const
759 {
760  Q_UNUSED( epsilon );
761  double minDist = std::numeric_limits<double>::max();
762  QgsPointV2 minDistSegmentPoint;
763  QgsVertexId minDistVertexAfter;
764  bool minDistLeftOf = false;
765 
766  double currentDist = 0.0;
767 
768  int nPoints = numPoints();
769  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
770  {
771  currentDist = closestPointOnArc( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2], pt, segmentPt, vertexAfter, leftOf, epsilon );
772  if ( currentDist < minDist )
773  {
774  minDist = currentDist;
775  minDistSegmentPoint = segmentPt;
776  minDistVertexAfter.vertex = vertexAfter.vertex + i;
777  if ( leftOf )
778  {
779  minDistLeftOf = *leftOf;
780  }
781  }
782  }
783 
784  segmentPt = minDistSegmentPoint;
785  vertexAfter = minDistVertexAfter;
786  vertexAfter.part = 0; vertexAfter.ring = 0;
787  if ( leftOf )
788  {
789  *leftOf = minDistLeftOf;
790  }
791  return minDist;
792 }
793 
795 {
796  if ( i >= numPoints() )
797  {
798  return false;
799  }
800  vertex = pointN( i );
801  type = ( i % 2 == 0 ) ? QgsVertexId::SegmentVertex : QgsVertexId::CurveVertex;
802  return true;
803 }
804 
805 void QgsCircularStringV2::sumUpArea( double& sum ) const
806 {
807  int maxIndex = numPoints() - 1;
808 
809  for ( int i = 0; i < maxIndex; i += 2 )
810  {
811  QgsPointV2 p1( mX[i], mY[i] );
812  QgsPointV2 p2( mX[i + 1], mY[i + 1] );
813  QgsPointV2 p3( mX[i + 2], mY[i + 2] );
814 
815  //segment is a full circle, p2 is the center point
816  if ( p1 == p3 )
817  {
818  double r2 = QgsGeometryUtils::sqrDistance2D( p1, p2 );
819  sum += M_PI * r2;
820  continue;
821  }
822 
823  sum += 0.5 * ( mX[i] * mY[i+2] - mY[i] * mX[i+2] );
824 
825  //calculate area between circle and chord, then sum / subtract from total area
826  double midPointX = ( p1.x() + p3.x() ) / 2.0;
827  double midPointY = ( p1.y() + p3.y() ) / 2.0;
828 
829  double radius, centerX, centerY;
830  QgsGeometryUtils::circleCenterRadius( p1, p2, p3, radius, centerX, centerY );
831 
832  double d = sqrt( QgsGeometryUtils::sqrDistance2D( QgsPointV2( centerX, centerY ), QgsPointV2( midPointX, midPointY ) ) );
833  double r2 = radius * radius;
834 
835  if ( d > radius )
836  {
837  //d cannot be greater than radius, something must be wrong...
838  continue;
839  }
840 
841  bool circlePointLeftOfLine = QgsGeometryUtils::leftOfLine( p2.x(), p2.y(), p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
842  bool centerPointLeftOfLine = QgsGeometryUtils::leftOfLine( centerX, centerY, p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
843 
844  double cov = 0.5 - d * sqrt( r2 - d * d ) / ( M_PI * r2 ) - 1 / M_PI * asin( d / radius );
845  double circleChordArea = 0;
846  if ( circlePointLeftOfLine == centerPointLeftOfLine )
847  {
848  circleChordArea = M_PI * r2 * ( 1 - cov );
849  }
850  else
851  {
852  circleChordArea = M_PI * r2 * cov;
853  }
854 
855  if ( !circlePointLeftOfLine )
856  {
857  sum += circleChordArea;
858  }
859  else
860  {
861  sum -= circleChordArea;
862  }
863  }
864 }
865 
866 double QgsCircularStringV2::closestPointOnArc( double x1, double y1, double x2, double y2, double x3, double y3,
867  const QgsPointV2& pt, QgsPointV2& segmentPt, QgsVertexId& vertexAfter, bool* leftOf, double epsilon )
868 {
869  double radius, centerX, centerY;
870  QgsPointV2 pt1( x1, y1 );
871  QgsPointV2 pt2( x2, y2 );
872  QgsPointV2 pt3( x3, y3 );
873 
874  QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
875  double angle = QgsGeometryUtils::ccwAngle( pt.y() - centerY, pt.x() - centerX );
876  double angle1 = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
877  double angle2 = QgsGeometryUtils::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
878  double angle3 = QgsGeometryUtils::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
879 
880  bool clockwise = QgsGeometryUtils::circleClockwise( angle1, angle2, angle3 );
881 
882  if ( QgsGeometryUtils::angleOnCircle( angle, angle1, angle2, angle3 ) )
883  {
884  //get point on line center -> pt with distance radius
885  segmentPt = QgsGeometryUtils::pointOnLineWithDistance( QgsPointV2( centerX, centerY ), pt, radius );
886 
887  //vertexAfter
888  vertexAfter.vertex = QgsGeometryUtils::circleAngleBetween( angle, angle1, angle2, clockwise ) ? 1 : 2;
889  }
890  else
891  {
892  double distPtPt1 = QgsGeometryUtils::sqrDistance2D( pt, pt1 );
893  double distPtPt3 = QgsGeometryUtils::sqrDistance2D( pt, pt3 );
894  segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
895  vertexAfter.vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
896  }
897 
898  double sqrDistance = QgsGeometryUtils::sqrDistance2D( segmentPt, pt );
899  //prevent rounding errors if the point is directly on the segment
900  if ( qgsDoubleNear( sqrDistance, 0.0, epsilon ) )
901  {
902  segmentPt.setX( pt.x() );
903  segmentPt.setY( pt.y() );
904  sqrDistance = 0.0;
905  }
906 
907  if ( leftOf )
908  {
909  *leftOf = clockwise ? sqrDistance > radius : sqrDistance < radius;
910  }
911 
912  return sqrDistance;
913 }
914 
915 void QgsCircularStringV2::insertVertexBetween( int after, int before, int pointOnCircle )
916 {
917  double xAfter = mX[after];
918  double yAfter = mY[after];
919  double xBefore = mX[before];
920  double yBefore = mY[before];
921  double xOnCircle = mX[ pointOnCircle ];
922  double yOnCircle = mY[ pointOnCircle ];
923 
924  double radius, centerX, centerY;
925  QgsGeometryUtils::circleCenterRadius( QgsPointV2( xAfter, yAfter ), QgsPointV2( xBefore, yBefore ), QgsPointV2( xOnCircle, yOnCircle ), radius, centerX, centerY );
926 
927  double x = ( xAfter + xBefore ) / 2.0;
928  double y = ( yAfter + yBefore ) / 2.0;
929 
930  QgsPointV2 newVertex = QgsGeometryUtils::pointOnLineWithDistance( QgsPointV2( centerX, centerY ), QgsPointV2( x, y ), radius );
931  mX.insert( before, newVertex.x() );
932  mY.insert( before, newVertex.y() );
933 
934  if ( is3D() )
935  {
936  mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
937  }
938  if ( isMeasure() )
939  {
940  mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
941  }
942 }
void clear()
void transformCoords(const int &numPoint, double *x, double *y, double *z, TransformDirection direction=ForwardTransform) const
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.
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:41
void push_back(const T &value)
virtual QgsAbstractGeometryV2 * clone() const override
Clones the geometry by performing a deep copy.
QPoint map(const QPoint &point) const
virtual bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
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.
Abstract base class for all geometries.
void moveTo(const QPointF &point)
void setX(double x)
Definition: qgspointv2.h:46
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:350
void setY(double y)
Definition: qgspointv2.h:47
int size() const
#define M_PI_2
Definition: util.cpp:58
virtual double length() const override
Returns the length (or perimeter for area geometries) of the geometry.
QgsPointV2 pointN(int i) const
Returns the point at index i within the circular string.
double y() const
Definition: qgspointv2.h:42
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.
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:43
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)
Point geometry type.
Definition: qgspointv2.h:29
void remove(int i)
#define M_PI
void sumUpArea(double &sum) const override
Calculates the area of the curve.
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)
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 moveVertex(const QgsVertexId &position, const QgsPointV2 &newPos) override
Moves a vertex within the geometry.
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.
void transform(const QgsCoordinateTransform &ct) override
Transforms the geometry using a coordinate transform.
QgsAbstractGeometryV2 * segmentize() const override
Returns a version of the geometry without curves.
Definition: qgscurvev2.cpp:84
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:44
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)
int max(int a, int b)
Definition: util.h:87
QString asJSON(int precision=17) const override
Returns a GeoJSON representation of the geometry.
int numPoints() const override
Returns the number of points in the curve.