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