QGIS API Documentation  3.17.0-Master (3b262f2a79)
qgscircularstring.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscircularstring.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 "qgscircularstring.h"
19 #include "qgsapplication.h"
20 #include "qgscoordinatetransform.h"
21 #include "qgsgeometryutils.h"
22 #include "qgslinestring.h"
23 #include "qgsmaptopixel.h"
24 #include "qgspoint.h"
25 #include "qgswkbptr.h"
26 #include "qgslogger.h"
27 
28 #include <QJsonObject>
29 #include <QPainter>
30 #include <QPainterPath>
31 #include <memory>
32 #include <nlohmann/json.hpp>
33 
35 {
37 }
38 
40 {
41  //get wkb type from first point
42  bool hasZ = p1.is3D();
43  bool hasM = p1.isMeasure();
45 
46  mX.resize( 3 );
47  mX[ 0 ] = p1.x();
48  mX[ 1 ] = p2.x();
49  mX[ 2 ] = p3.x();
50  mY.resize( 3 );
51  mY[ 0 ] = p1.y();
52  mY[ 1 ] = p2.y();
53  mY[ 2 ] = p3.y();
54  if ( hasZ )
55  {
57  mZ.resize( 3 );
58  mZ[ 0 ] = p1.z();
59  mZ[ 1 ] = p2.z();
60  mZ[ 2 ] = p3.z();
61  }
62  if ( hasM )
63  {
65  mM.resize( 3 );
66  mM[ 0 ] = p1.m();
67  mM[ 1 ] = p2.m();
68  mM[ 2 ] = p3.m();
69  }
70 }
71 
72 QgsCircularString QgsCircularString::fromTwoPointsAndCenter( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, const bool useShortestArc )
73 {
74  const QgsPoint midPoint = QgsGeometryUtils::segmentMidPointFromCenter( p1, p2, center, useShortestArc );
75  return QgsCircularString( p1, midPoint, p2 );
76 }
77 
78 bool QgsCircularString::equals( const QgsCurve &other ) const
79 {
80  const QgsCircularString *otherLine = dynamic_cast< const QgsCircularString * >( &other );
81  if ( !otherLine )
82  return false;
83 
84  if ( mWkbType != otherLine->mWkbType )
85  return false;
86 
87  if ( mX.count() != otherLine->mX.count() )
88  return false;
89 
90  for ( int i = 0; i < mX.count(); ++i )
91  {
92  if ( !qgsDoubleNear( mX.at( i ), otherLine->mX.at( i ) )
93  || !qgsDoubleNear( mY.at( i ), otherLine->mY.at( i ) ) )
94  return false;
95 
96  if ( is3D() && !qgsDoubleNear( mZ.at( i ), otherLine->mZ.at( i ) ) )
97  return false;
98 
99  if ( isMeasure() && !qgsDoubleNear( mM.at( i ), otherLine->mM.at( i ) ) )
100  return false;
101  }
102 
103  return true;
104 }
105 
107 {
108  auto result = qgis::make_unique< QgsCircularString >();
109  result->mWkbType = mWkbType;
110  return result.release();
111 }
112 
114 {
115  return QStringLiteral( "CircularString" );
116 }
117 
119 {
120  return 1;
121 }
122 
124 {
125  return new QgsCircularString( *this );
126 }
127 
129 {
131  mX.clear();
132  mY.clear();
133  mZ.clear();
134  mM.clear();
135  clearCache();
136 }
137 
139 {
140  QgsRectangle bbox;
141  int nPoints = numPoints();
142  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
143  {
144  if ( i == 0 )
145  {
146  bbox = segmentBoundingBox( QgsPoint( mX[i], mY[i] ), QgsPoint( mX[i + 1], mY[i + 1] ), QgsPoint( mX[i + 2], mY[i + 2] ) );
147  }
148  else
149  {
150  QgsRectangle segmentBox = segmentBoundingBox( QgsPoint( mX[i], mY[i] ), QgsPoint( mX[i + 1], mY[i + 1] ), QgsPoint( mX[i + 2], mY[i + 2] ) );
151  bbox.combineExtentWith( segmentBox );
152  }
153  }
154 
155  if ( nPoints > 0 && nPoints % 2 == 0 )
156  {
157  if ( nPoints == 2 )
158  {
159  bbox.combineExtentWith( mX[ 0 ], mY[ 0 ] );
160  }
161  bbox.combineExtentWith( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
162  }
163  return bbox;
164 }
165 
166 QgsRectangle QgsCircularString::segmentBoundingBox( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3 )
167 {
168  double centerX, centerY, radius;
169  QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
170 
171  double p1Angle = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
172  double p2Angle = QgsGeometryUtils::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
173  double p3Angle = QgsGeometryUtils::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
174 
175  //start point, end point and compass points in between can be on bounding box
176  QgsRectangle bbox( pt1.x(), pt1.y(), pt1.x(), pt1.y() );
177  bbox.combineExtentWith( pt3.x(), pt3.y() );
178 
179  QgsPointSequence compassPoints = compassPointsOnSegment( p1Angle, p2Angle, p3Angle, centerX, centerY, radius );
180  QgsPointSequence::const_iterator cpIt = compassPoints.constBegin();
181  for ( ; cpIt != compassPoints.constEnd(); ++cpIt )
182  {
183  bbox.combineExtentWith( cpIt->x(), cpIt->y() );
184  }
185  return bbox;
186 }
187 
188 QgsPointSequence QgsCircularString::compassPointsOnSegment( double p1Angle, double p2Angle, double p3Angle, double centerX, double centerY, double radius )
189 {
190  QgsPointSequence pointList;
191 
192  QgsPoint nPoint( centerX, centerY + radius );
193  QgsPoint ePoint( centerX + radius, centerY );
194  QgsPoint sPoint( centerX, centerY - radius );
195  QgsPoint wPoint( centerX - radius, centerY );
196 
197  if ( p3Angle >= p1Angle )
198  {
199  if ( p2Angle > p1Angle && p2Angle < p3Angle )
200  {
201  if ( p1Angle <= 90 && p3Angle >= 90 )
202  {
203  pointList.append( nPoint );
204  }
205  if ( p1Angle <= 180 && p3Angle >= 180 )
206  {
207  pointList.append( wPoint );
208  }
209  if ( p1Angle <= 270 && p3Angle >= 270 )
210  {
211  pointList.append( sPoint );
212  }
213  }
214  else
215  {
216  pointList.append( ePoint );
217  if ( p1Angle >= 90 || p3Angle <= 90 )
218  {
219  pointList.append( nPoint );
220  }
221  if ( p1Angle >= 180 || p3Angle <= 180 )
222  {
223  pointList.append( wPoint );
224  }
225  if ( p1Angle >= 270 || p3Angle <= 270 )
226  {
227  pointList.append( sPoint );
228  }
229  }
230  }
231  else
232  {
233  if ( p2Angle < p1Angle && p2Angle > p3Angle )
234  {
235  if ( p1Angle >= 270 && p3Angle <= 270 )
236  {
237  pointList.append( sPoint );
238  }
239  if ( p1Angle >= 180 && p3Angle <= 180 )
240  {
241  pointList.append( wPoint );
242  }
243  if ( p1Angle >= 90 && p3Angle <= 90 )
244  {
245  pointList.append( nPoint );
246  }
247  }
248  else
249  {
250  pointList.append( ePoint );
251  if ( p1Angle <= 270 || p3Angle >= 270 )
252  {
253  pointList.append( sPoint );
254  }
255  if ( p1Angle <= 180 || p3Angle >= 180 )
256  {
257  pointList.append( wPoint );
258  }
259  if ( p1Angle <= 90 || p3Angle >= 90 )
260  {
261  pointList.append( nPoint );
262  }
263  }
264  }
265  return pointList;
266 }
267 
269 {
270  if ( !wkbPtr )
271  return false;
272 
273  QgsWkbTypes::Type type = wkbPtr.readHeader();
275  {
276  return false;
277  }
278  clearCache();
279  mWkbType = type;
280 
281  //type
282  bool hasZ = is3D();
283  bool hasM = isMeasure();
284  int nVertices = 0;
285  wkbPtr >> nVertices;
286  mX.resize( nVertices );
287  mY.resize( nVertices );
288  hasZ ? mZ.resize( nVertices ) : mZ.clear();
289  hasM ? mM.resize( nVertices ) : mM.clear();
290  for ( int i = 0; i < nVertices; ++i )
291  {
292  wkbPtr >> mX[i];
293  wkbPtr >> mY[i];
294  if ( hasZ )
295  {
296  wkbPtr >> mZ[i];
297  }
298  if ( hasM )
299  {
300  wkbPtr >> mM[i];
301  }
302  }
303 
304  return true;
305 }
306 
307 bool QgsCircularString::fromWkt( const QString &wkt )
308 {
309  clear();
310 
311  QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
312 
313  if ( QgsWkbTypes::flatType( parts.first ) != QgsWkbTypes::CircularString )
314  return false;
315  mWkbType = parts.first;
316 
317  parts.second = parts.second.remove( '(' ).remove( ')' );
318  QString secondWithoutParentheses = parts.second;
319  secondWithoutParentheses = secondWithoutParentheses.simplified().remove( ' ' );
320  if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
321  secondWithoutParentheses.isEmpty() )
322  return true;
323 
325  if ( points.isEmpty() )
326  return false;
327 
328  setPoints( points );
329  return true;
330 }
331 
332 int QgsCircularString::wkbSize( QgsAbstractGeometry::WkbFlags ) const
333 {
334  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
335  binarySize += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
336  return binarySize;
337 }
338 
339 QByteArray QgsCircularString::asWkb( WkbFlags flags ) const
340 {
341  QByteArray wkbArray;
342  wkbArray.resize( QgsCircularString::wkbSize( flags ) );
343  QgsWkbPtr wkb( wkbArray );
344  wkb << static_cast<char>( QgsApplication::endian() );
345  wkb << static_cast<quint32>( wkbType() );
346  QgsPointSequence pts;
347  points( pts );
348  QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure() );
349  return wkbArray;
350 }
351 
353 {
354  QString wkt = wktTypeStr() + ' ';
355 
356  if ( isEmpty() )
357  wkt += QLatin1String( "EMPTY" );
358  else
359  {
360  QgsPointSequence pts;
361  points( pts );
362  wkt += QgsGeometryUtils::pointsToWKT( pts, precision, is3D(), isMeasure() );
363  }
364  return wkt;
365 }
366 
367 QDomElement QgsCircularString::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
368 {
369  // GML2 does not support curves
370  std::unique_ptr< QgsLineString > line( curveToLine() );
371  QDomElement gml = line->asGml2( doc, precision, ns, axisOrder );
372  return gml;
373 }
374 
375 QDomElement QgsCircularString::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
376 {
377  QgsPointSequence pts;
378  points( pts );
379 
380  QDomElement elemCurve = doc.createElementNS( ns, QStringLiteral( "Curve" ) );
381 
382  if ( isEmpty() )
383  return elemCurve;
384 
385  QDomElement elemSegments = doc.createElementNS( ns, QStringLiteral( "segments" ) );
386  QDomElement elemArcString = doc.createElementNS( ns, QStringLiteral( "ArcString" ) );
387  elemArcString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D(), axisOrder ) );
388  elemSegments.appendChild( elemArcString );
389  elemCurve.appendChild( elemSegments );
390  return elemCurve;
391 }
392 
393 
395 {
396  // GeoJSON does not support curves
397  std::unique_ptr< QgsLineString > line( curveToLine() );
398  return line->asJsonObject( precision );
399 }
400 
402 {
403  return mX.isEmpty();
404 }
405 
406 //curve interface
408 {
409  int nPoints = numPoints();
410  double length = 0;
411  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
412  {
413  length += QgsGeometryUtils::circleLength( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2] );
414  }
415  return length;
416 }
417 
419 {
420  if ( numPoints() < 1 )
421  {
422  return QgsPoint();
423  }
424  return pointN( 0 );
425 }
426 
428 {
429  if ( numPoints() < 1 )
430  {
431  return QgsPoint();
432  }
433  return pointN( numPoints() - 1 );
434 }
435 
437 {
438  QgsLineString *line = new QgsLineString();
440  int nPoints = numPoints();
441 
442  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
443  {
444  QgsGeometryUtils::segmentizeArc( pointN( i ), pointN( i + 1 ), pointN( i + 2 ), points, tolerance, toleranceType, is3D(), isMeasure() );
445  }
446 
447  line->setPoints( points );
448  return line;
449 }
450 
451 QgsCircularString *QgsCircularString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
452 {
453  // prepare result
454  std::unique_ptr<QgsCircularString> result { createEmptyWithSameType() };
455 
456  bool res = snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
457  result->mX, result->mY, result->mZ, result->mM );
458  if ( res )
459  return result.release();
460  else
461  return nullptr;
462 }
463 
464 bool QgsCircularString::removeDuplicateNodes( double epsilon, bool useZValues )
465 {
466  if ( mX.count() <= 3 )
467  return false; // don't create degenerate lines
468  bool result = false;
469  double prevX = mX.at( 0 );
470  double prevY = mY.at( 0 );
471  bool hasZ = is3D();
472  bool useZ = hasZ && useZValues;
473  double prevZ = useZ ? mZ.at( 0 ) : 0;
474  int i = 1;
475  int remaining = mX.count();
476  // we have to consider points in pairs, since a segment can validly have the same start and
477  // end if it has a different curve point
478  while ( i + 1 < remaining )
479  {
480  double currentCurveX = mX.at( i );
481  double currentCurveY = mY.at( i );
482  double currentX = mX.at( i + 1 );
483  double currentY = mY.at( i + 1 );
484  double currentZ = useZ ? mZ.at( i + 1 ) : 0;
485  if ( qgsDoubleNear( currentCurveX, prevX, epsilon ) &&
486  qgsDoubleNear( currentCurveY, prevY, epsilon ) &&
487  qgsDoubleNear( currentX, prevX, epsilon ) &&
488  qgsDoubleNear( currentY, prevY, epsilon ) &&
489  ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
490  {
491  result = true;
492  // remove point
493  mX.removeAt( i );
494  mX.removeAt( i );
495  mY.removeAt( i );
496  mY.removeAt( i );
497  if ( hasZ )
498  {
499  mZ.removeAt( i );
500  mZ.removeAt( i );
501  }
502  remaining -= 2;
503  }
504  else
505  {
506  prevX = currentX;
507  prevY = currentY;
508  prevZ = currentZ;
509  i += 2;
510  }
511  }
512  return result;
513 }
514 
516 {
517  return std::min( mX.size(), mY.size() );
518 }
519 
521 {
522  if ( i < 0 || std::min( mX.size(), mY.size() ) <= i )
523  {
524  return QgsPoint();
525  }
526 
527  double x = mX.at( i );
528  double y = mY.at( i );
529  double z = 0;
530  double m = 0;
531 
532  if ( is3D() )
533  {
534  z = mZ.at( i );
535  }
536  if ( isMeasure() )
537  {
538  m = mM.at( i );
539  }
540 
542  if ( is3D() && isMeasure() )
543  {
545  }
546  else if ( is3D() )
547  {
549  }
550  else if ( isMeasure() )
551  {
553  }
554  return QgsPoint( t, x, y, z, m );
555 }
556 
557 double QgsCircularString::xAt( int index ) const
558 {
559  if ( index >= 0 && index < mX.size() )
560  return mX.at( index );
561  else
562  return 0.0;
563 }
564 
565 double QgsCircularString::yAt( int index ) const
566 {
567  if ( index >= 0 && index < mY.size() )
568  return mY.at( index );
569  else
570  return 0.0;
571 }
572 
573 void QgsCircularString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
574 {
575  bool hasZ = is3D();
576  bool hasM = isMeasure();
577  int size = mX.size();
578 
579  double *srcX = mX.data(); // clazy:exclude=detaching-member
580  double *srcY = mY.data(); // clazy:exclude=detaching-member
581  double *srcM = hasM ? mM.data() : nullptr; // clazy:exclude=detaching-member
582  double *srcZ = hasZ ? mZ.data() : nullptr; // clazy:exclude=detaching-member
583 
584  double *destX = srcX;
585  double *destY = srcY;
586  double *destM = srcM;
587  double *destZ = srcZ;
588 
589  int filteredPoints = 0;
590  for ( int i = 0; i < size; ++i )
591  {
592  double x = *srcX++;
593  double y = *srcY++;
594  double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
595  double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
596 
597  if ( filter( QgsPoint( x, y, z, m ) ) )
598  {
599  filteredPoints++;
600  *destX++ = x;
601  *destY++ = y;
602  if ( hasM )
603  *destM++ = m;
604  if ( hasZ )
605  *destZ++ = z;
606  }
607  }
608 
609  mX.resize( filteredPoints );
610  mY.resize( filteredPoints );
611  if ( hasZ )
612  mZ.resize( filteredPoints );
613  if ( hasM )
614  mM.resize( filteredPoints );
615 
616  clearCache();
617 }
618 
619 void QgsCircularString::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
620 {
621  bool hasZ = is3D();
622  bool hasM = isMeasure();
623  int size = mX.size();
624 
625  double *srcX = mX.data();
626  double *srcY = mY.data();
627  double *srcM = hasM ? mM.data() : nullptr;
628  double *srcZ = hasZ ? mZ.data() : nullptr;
629 
630  for ( int i = 0; i < size; ++i )
631  {
632  double x = *srcX;
633  double y = *srcY;
634  double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
635  double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
636  QgsPoint res = transform( QgsPoint( x, y, z, m ) );
637  *srcX++ = res.x();
638  *srcY++ = res.y();
639  if ( hasM )
640  *srcM++ = res.m();
641  if ( hasZ )
642  *srcZ++ = res.z();
643  }
644  clearCache();
645 }
646 
648 {
649  pts.clear();
650  int nPts = numPoints();
651  for ( int i = 0; i < nPts; ++i )
652  {
653  pts.push_back( pointN( i ) );
654  }
655 }
656 
658 {
659  clearCache();
660 
661  if ( points.empty() )
662  {
664  mX.clear();
665  mY.clear();
666  mZ.clear();
667  mM.clear();
668  return;
669  }
670 
671  //get wkb type from first point
672  const QgsPoint &firstPt = points.at( 0 );
673  bool hasZ = firstPt.is3D();
674  bool hasM = firstPt.isMeasure();
675 
677 
678  mX.resize( points.size() );
679  mY.resize( points.size() );
680  if ( hasZ )
681  {
682  mZ.resize( points.size() );
683  }
684  else
685  {
686  mZ.clear();
687  }
688  if ( hasM )
689  {
690  mM.resize( points.size() );
691  }
692  else
693  {
694  mM.clear();
695  }
696 
697  for ( int i = 0; i < points.size(); ++i )
698  {
699  mX[i] = points[i].x();
700  mY[i] = points[i].y();
701  if ( hasZ )
702  {
703  double z = points.at( i ).z();
704  mZ[i] = std::isnan( z ) ? 0 : z;
705  }
706  if ( hasM )
707  {
708  double m = points.at( i ).m();
709  mM[i] = std::isnan( m ) ? 0 : m;
710  }
711  }
712 }
713 
714 void QgsCircularString::draw( QPainter &p ) const
715 {
716  QPainterPath path;
717  addToPainterPath( path );
718  p.drawPath( path );
719 }
720 
722 {
723  clearCache();
724 
725  double *zArray = mZ.data();
726 
727  bool hasZ = is3D();
728  int nPoints = numPoints();
729  bool useDummyZ = !hasZ || !transformZ;
730  if ( useDummyZ )
731  {
732  zArray = new double[nPoints];
733  for ( int i = 0; i < nPoints; ++i )
734  {
735  zArray[i] = 0;
736  }
737  }
738  ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
739  if ( useDummyZ )
740  {
741  delete[] zArray;
742  }
743 }
744 
745 void QgsCircularString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
746 {
747  clearCache();
748 
749  int nPoints = numPoints();
750  bool hasZ = is3D();
751  bool hasM = isMeasure();
752  for ( int i = 0; i < nPoints; ++i )
753  {
754  qreal x, y;
755  t.map( mX.at( i ), mY.at( i ), &x, &y );
756  mX[i] = x;
757  mY[i] = y;
758  if ( hasZ )
759  {
760  mZ[i] = mZ.at( i ) * zScale + zTranslate;
761  }
762  if ( hasM )
763  {
764  mM[i] = mM.at( i ) * mScale + mTranslate;
765  }
766  }
767 }
768 
769 void QgsCircularString::addToPainterPath( QPainterPath &path ) const
770 {
771  int nPoints = numPoints();
772  if ( nPoints < 1 )
773  {
774  return;
775  }
776 
777  if ( path.isEmpty() || path.currentPosition() != QPointF( mX[0], mY[0] ) )
778  {
779  path.moveTo( QPointF( mX[0], mY[0] ) );
780  }
781 
782  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
783  {
784  QgsPointSequence pt;
785  QgsGeometryUtils::segmentizeArc( QgsPoint( mX[i], mY[i] ), QgsPoint( mX[i + 1], mY[i + 1] ), QgsPoint( mX[i + 2], mY[i + 2] ), pt );
786  for ( int j = 1; j < pt.size(); ++j )
787  {
788  path.lineTo( pt.at( j ).x(), pt.at( j ).y() );
789  }
790 #if 0
791  //arcTo( path, QPointF( mX[i], mY[i] ), QPointF( mX[i + 1], mY[i + 1] ), QPointF( mX[i + 2], mY[i + 2] ) );
792 #endif
793  }
794 
795  //if number of points is even, connect to last point with straight line (even though the circular string is not valid)
796  if ( nPoints % 2 == 0 )
797  {
798  path.lineTo( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
799  }
800 }
801 
802 #if 0
803 void QgsCircularString::arcTo( QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3 )
804 {
805  double centerX, centerY, radius;
806  QgsGeometryUtils::circleCenterRadius( QgsPoint( pt1.x(), pt1.y() ), QgsPoint( pt2.x(), pt2.y() ), QgsPoint( pt3.x(), pt3.y() ),
807  radius, centerX, centerY );
808 
809  double p1Angle = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
810  double sweepAngle = QgsGeometryUtils::sweepAngle( centerX, centerY, pt1.x(), pt1.y(), pt2.x(), pt2.y(), pt3.x(), pt3.y() );
811 
812  double diameter = 2 * radius;
813  path.arcTo( centerX - radius, centerY - radius, diameter, diameter, p1Angle, sweepAngle );
814 }
815 #endif
816 
817 void QgsCircularString::drawAsPolygon( QPainter &p ) const
818 {
819  draw( p );
820 }
821 
822 bool QgsCircularString::insertVertex( QgsVertexId position, const QgsPoint &vertex )
823 {
824  if ( position.vertex >= mX.size() || position.vertex < 1 )
825  {
826  return false;
827  }
828 
829  mX.insert( position.vertex, vertex.x() );
830  mY.insert( position.vertex, vertex.y() );
831  if ( is3D() )
832  {
833  mZ.insert( position.vertex, vertex.z() );
834  }
835  if ( isMeasure() )
836  {
837  mM.insert( position.vertex, vertex.m() );
838  }
839 
840  bool vertexNrEven = ( position.vertex % 2 == 0 );
841  if ( vertexNrEven )
842  {
843  insertVertexBetween( position.vertex - 2, position.vertex - 1, position.vertex );
844  }
845  else
846  {
847  insertVertexBetween( position.vertex, position.vertex + 1, position.vertex - 1 );
848  }
849  clearCache(); //set bounding box invalid
850  return true;
851 }
852 
853 bool QgsCircularString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
854 {
855  if ( position.vertex < 0 || position.vertex >= mX.size() )
856  {
857  return false;
858  }
859 
860  mX[position.vertex] = newPos.x();
861  mY[position.vertex] = newPos.y();
862  if ( is3D() && newPos.is3D() )
863  {
864  mZ[position.vertex] = newPos.z();
865  }
866  if ( isMeasure() && newPos.isMeasure() )
867  {
868  mM[position.vertex] = newPos.m();
869  }
870  clearCache(); //set bounding box invalid
871  return true;
872 }
873 
875 {
876  int nVertices = this->numPoints();
877  if ( nVertices < 4 ) //circular string must have at least 3 vertices
878  {
879  clear();
880  return true;
881  }
882  if ( position.vertex < 0 || position.vertex > ( nVertices - 1 ) )
883  {
884  return false;
885  }
886 
887  if ( position.vertex < ( nVertices - 2 ) )
888  {
889  //remove this and the following vertex
890  deleteVertex( position.vertex + 1 );
891  deleteVertex( position.vertex );
892  }
893  else //remove this and the preceding vertex
894  {
895  deleteVertex( position.vertex );
896  deleteVertex( position.vertex - 1 );
897  }
898 
899  clearCache(); //set bounding box invalid
900  return true;
901 }
902 
904 {
905  mX.remove( i );
906  mY.remove( i );
907  if ( is3D() )
908  {
909  mZ.remove( i );
910  }
911  if ( isMeasure() )
912  {
913  mM.remove( i );
914  }
915  clearCache();
916 }
917 
918 double QgsCircularString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
919 {
920  double minDist = std::numeric_limits<double>::max();
921  QgsPoint minDistSegmentPoint;
922  QgsVertexId minDistVertexAfter;
923  int minDistLeftOf = 0;
924 
925  double currentDist = 0.0;
926 
927  int nPoints = numPoints();
928  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
929  {
930  currentDist = closestPointOnArc( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2], pt, segmentPt, vertexAfter, leftOf, epsilon );
931  if ( currentDist < minDist )
932  {
933  minDist = currentDist;
934  minDistSegmentPoint = segmentPt;
935  minDistVertexAfter.vertex = vertexAfter.vertex + i;
936  if ( leftOf )
937  {
938  minDistLeftOf = *leftOf;
939  }
940  }
941  }
942 
943  if ( minDist == std::numeric_limits<double>::max() )
944  return -1; // error: no segments
945 
946  segmentPt = minDistSegmentPoint;
947  vertexAfter = minDistVertexAfter;
948  vertexAfter.part = 0;
949  vertexAfter.ring = 0;
950  if ( leftOf )
951  {
952  *leftOf = qgsDoubleNear( minDist, 0.0 ) ? 0 : minDistLeftOf;
953  }
954  return minDist;
955 }
956 
957 bool QgsCircularString::pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const
958 {
959  if ( node < 0 || node >= numPoints() )
960  {
961  return false;
962  }
963  point = pointN( node );
964  type = ( node % 2 == 0 ) ? QgsVertexId::SegmentVertex : QgsVertexId::CurveVertex;
965  return true;
966 }
967 
968 void QgsCircularString::sumUpArea( double &sum ) const
969 {
970  int maxIndex = numPoints() - 2;
971 
972  for ( int i = 0; i < maxIndex; i += 2 )
973  {
974  QgsPoint p1( mX[i], mY[i] );
975  QgsPoint p2( mX[i + 1], mY[i + 1] );
976  QgsPoint p3( mX[i + 2], mY[i + 2] );
977 
978  //segment is a full circle, p2 is the center point
979  if ( p1 == p3 )
980  {
981  double r2 = QgsGeometryUtils::sqrDistance2D( p1, p2 ) / 4.0;
982  sum += M_PI * r2;
983  continue;
984  }
985 
986  sum += 0.5 * ( mX[i] * mY[i + 2] - mY[i] * mX[i + 2] );
987 
988  //calculate area between circle and chord, then sum / subtract from total area
989  double midPointX = ( p1.x() + p3.x() ) / 2.0;
990  double midPointY = ( p1.y() + p3.y() ) / 2.0;
991 
992  double radius, centerX, centerY;
993  QgsGeometryUtils::circleCenterRadius( p1, p2, p3, radius, centerX, centerY );
994 
995  double d = std::sqrt( QgsGeometryUtils::sqrDistance2D( QgsPoint( centerX, centerY ), QgsPoint( midPointX, midPointY ) ) );
996  double r2 = radius * radius;
997 
998  if ( d > radius )
999  {
1000  //d cannot be greater than radius, something must be wrong...
1001  continue;
1002  }
1003 
1004  bool circlePointLeftOfLine = QgsGeometryUtils::leftOfLine( p2.x(), p2.y(), p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
1005  bool centerPointLeftOfLine = QgsGeometryUtils::leftOfLine( centerX, centerY, p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
1006 
1007  double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - M_1_PI * std::asin( d / radius );
1008  double circleChordArea = 0;
1009  if ( circlePointLeftOfLine == centerPointLeftOfLine )
1010  {
1011  circleChordArea = M_PI * r2 * ( 1 - cov );
1012  }
1013  else
1014  {
1015  circleChordArea = M_PI * r2 * cov;
1016  }
1017 
1018  if ( !circlePointLeftOfLine )
1019  {
1020  sum += circleChordArea;
1021  }
1022  else
1023  {
1024  sum -= circleChordArea;
1025  }
1026  }
1027 }
1028 
1030 {
1031  return true;
1032 }
1033 
1034 double QgsCircularString::closestPointOnArc( double x1, double y1, double x2, double y2, double x3, double y3,
1035  const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon )
1036 {
1037  double radius, centerX, centerY;
1038  QgsPoint pt1( x1, y1 );
1039  QgsPoint pt2( x2, y2 );
1040  QgsPoint pt3( x3, y3 );
1041 
1042  QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
1043  double angle = QgsGeometryUtils::ccwAngle( pt.y() - centerY, pt.x() - centerX );
1044  double angle1 = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
1045  double angle2 = QgsGeometryUtils::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
1046  double angle3 = QgsGeometryUtils::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
1047 
1048  bool clockwise = QgsGeometryUtils::circleClockwise( angle1, angle2, angle3 );
1049 
1050  if ( QgsGeometryUtils::angleOnCircle( angle, angle1, angle2, angle3 ) )
1051  {
1052  //get point on line center -> pt with distance radius
1053  segmentPt = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), pt, radius );
1054 
1055  //vertexAfter
1056  vertexAfter.vertex = QgsGeometryUtils::circleAngleBetween( angle, angle1, angle2, clockwise ) ? 1 : 2;
1057  }
1058  else
1059  {
1060  double distPtPt1 = QgsGeometryUtils::sqrDistance2D( pt, pt1 );
1061  double distPtPt3 = QgsGeometryUtils::sqrDistance2D( pt, pt3 );
1062  segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
1063  vertexAfter.vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
1064  }
1065 
1066  double sqrDistance = QgsGeometryUtils::sqrDistance2D( segmentPt, pt );
1067  //prevent rounding errors if the point is directly on the segment
1068  if ( qgsDoubleNear( sqrDistance, 0.0, epsilon ) )
1069  {
1070  segmentPt.setX( pt.x() );
1071  segmentPt.setY( pt.y() );
1072  sqrDistance = 0.0;
1073  }
1074 
1075  if ( leftOf )
1076  {
1077  double sqrDistancePointToCenter = ( pt.x() - centerX ) * ( pt.x() - centerX ) + ( pt.y() - centerY ) * ( pt.y() - centerY );
1078  *leftOf = clockwise ? ( sqrDistancePointToCenter > radius * radius ? -1 : 1 )
1079  : ( sqrDistancePointToCenter < radius * radius ? -1 : 1 );
1080  }
1081 
1082  return sqrDistance;
1083 }
1084 
1085 void QgsCircularString::insertVertexBetween( int after, int before, int pointOnCircle )
1086 {
1087  double xAfter = mX.at( after );
1088  double yAfter = mY.at( after );
1089  double xBefore = mX.at( before );
1090  double yBefore = mY.at( before );
1091  double xOnCircle = mX.at( pointOnCircle );
1092  double yOnCircle = mY.at( pointOnCircle );
1093 
1094  double radius, centerX, centerY;
1095  QgsGeometryUtils::circleCenterRadius( QgsPoint( xAfter, yAfter ), QgsPoint( xBefore, yBefore ), QgsPoint( xOnCircle, yOnCircle ), radius, centerX, centerY );
1096 
1097  double x = ( xAfter + xBefore ) / 2.0;
1098  double y = ( yAfter + yBefore ) / 2.0;
1099 
1100  QgsPoint newVertex = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), QgsPoint( x, y ), radius );
1101  mX.insert( before, newVertex.x() );
1102  mY.insert( before, newVertex.y() );
1103 
1104  if ( is3D() )
1105  {
1106  mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
1107  }
1108  if ( isMeasure() )
1109  {
1110  mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
1111  }
1112  clearCache();
1113 }
1114 
1116 {
1117  if ( numPoints() < 3 )
1118  {
1119  //undefined
1120  return 0.0;
1121  }
1122 
1123  int before = vId.vertex - 1;
1124  int vertex = vId.vertex;
1125  int after = vId.vertex + 1;
1126 
1127  if ( vId.vertex % 2 != 0 ) // a curve vertex
1128  {
1129  if ( vId.vertex >= 1 && vId.vertex < numPoints() - 1 )
1130  {
1131  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[before], mY[before] ),
1132  QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[after], mY[after] ) );
1133  }
1134  }
1135  else //a point vertex
1136  {
1137  if ( vId.vertex == 0 )
1138  {
1139  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[0], mY[0] ), QgsPoint( mX[0], mY[0] ),
1140  QgsPoint( mX[1], mY[1] ), QgsPoint( mX[2], mY[2] ) );
1141  }
1142  if ( vId.vertex >= numPoints() - 1 )
1143  {
1144  int a = numPoints() - 3;
1145  int b = numPoints() - 2;
1146  int c = numPoints() - 1;
1147  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[c], mY[c] ), QgsPoint( mX[a], mY[a] ),
1148  QgsPoint( mX[b], mY[b] ), QgsPoint( mX[c], mY[c] ) );
1149  }
1150  else
1151  {
1152  if ( vId.vertex + 2 > numPoints() - 1 )
1153  {
1154  return 0.0;
1155  }
1156 
1157  int vertex1 = vId.vertex - 2;
1158  int vertex2 = vId.vertex - 1;
1159  int vertex3 = vId.vertex;
1160  double angle1 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1161  QgsPoint( mX[vertex1], mY[vertex1] ), QgsPoint( mX[vertex2], mY[vertex2] ), QgsPoint( mX[vertex3], mY[vertex3] ) );
1162  int vertex4 = vId.vertex + 1;
1163  int vertex5 = vId.vertex + 2;
1164  double angle2 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1165  QgsPoint( mX[vertex3], mY[vertex3] ), QgsPoint( mX[vertex4], mY[vertex4] ), QgsPoint( mX[vertex5], mY[vertex5] ) );
1166  return QgsGeometryUtils::averageAngle( angle1, angle2 );
1167  }
1168  }
1169  return 0.0;
1170 }
1171 
1173 {
1174  if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 2 )
1175  return 0.0;
1176 
1177  if ( startVertex.vertex % 2 == 1 )
1178  return 0.0; // curve point?
1179 
1180  double x1 = mX.at( startVertex.vertex );
1181  double y1 = mY.at( startVertex.vertex );
1182  double x2 = mX.at( startVertex.vertex + 1 );
1183  double y2 = mY.at( startVertex.vertex + 1 );
1184  double x3 = mX.at( startVertex.vertex + 2 );
1185  double y3 = mY.at( startVertex.vertex + 2 );
1186  return QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1187 }
1188 
1190 {
1191  QgsCircularString *copy = clone();
1192  std::reverse( copy->mX.begin(), copy->mX.end() );
1193  std::reverse( copy->mY.begin(), copy->mY.end() );
1194  if ( is3D() )
1195  {
1196  std::reverse( copy->mZ.begin(), copy->mZ.end() );
1197  }
1198  if ( isMeasure() )
1199  {
1200  std::reverse( copy->mM.begin(), copy->mM.end() );
1201  }
1202  return copy;
1203 }
1204 
1205 QgsPoint *QgsCircularString::interpolatePoint( const double distance ) const
1206 {
1207  if ( distance < 0 )
1208  return nullptr;
1209 
1210  double distanceTraversed = 0;
1211  const int totalPoints = numPoints();
1212  if ( totalPoints == 0 )
1213  return nullptr;
1214 
1216  if ( is3D() )
1217  pointType = QgsWkbTypes::PointZ;
1218  if ( isMeasure() )
1219  pointType = QgsWkbTypes::addM( pointType );
1220 
1221  const double *x = mX.constData();
1222  const double *y = mY.constData();
1223  const double *z = is3D() ? mZ.constData() : nullptr;
1224  const double *m = isMeasure() ? mM.constData() : nullptr;
1225 
1226  double prevX = *x++;
1227  double prevY = *y++;
1228  double prevZ = z ? *z++ : 0.0;
1229  double prevM = m ? *m++ : 0.0;
1230 
1231  if ( qgsDoubleNear( distance, 0.0 ) )
1232  {
1233  return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1234  }
1235 
1236  for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1237  {
1238  double x1 = prevX;
1239  double y1 = prevY;
1240  double z1 = prevZ;
1241  double m1 = prevM;
1242 
1243  double x2 = *x++;
1244  double y2 = *y++;
1245  double z2 = z ? *z++ : 0.0;
1246  double m2 = m ? *m++ : 0.0;
1247 
1248  double x3 = *x++;
1249  double y3 = *y++;
1250  double z3 = z ? *z++ : 0.0;
1251  double m3 = m ? *m++ : 0.0;
1252 
1253  const double segmentLength = QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1254  if ( distance < distanceTraversed + segmentLength || qgsDoubleNear( distance, distanceTraversed + segmentLength ) )
1255  {
1256  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1257  const double distanceToPoint = std::min( distance - distanceTraversed, segmentLength );
1258  return new QgsPoint( QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1259  QgsPoint( pointType, x2, y2, z2, m2 ),
1260  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToPoint ) );
1261  }
1262 
1263  distanceTraversed += segmentLength;
1264 
1265  prevX = x3;
1266  prevY = y3;
1267  prevZ = z3;
1268  prevM = m3;
1269  }
1270 
1271  return nullptr;
1272 }
1273 
1274 QgsCircularString *QgsCircularString::curveSubstring( double startDistance, double endDistance ) const
1275 {
1276  if ( startDistance < 0 && endDistance < 0 )
1277  return createEmptyWithSameType();
1278 
1279  endDistance = std::max( startDistance, endDistance );
1280 
1281  double distanceTraversed = 0;
1282  const int totalPoints = numPoints();
1283  if ( totalPoints == 0 )
1284  return clone();
1285 
1286  QVector< QgsPoint > substringPoints;
1287 
1289  if ( is3D() )
1290  pointType = QgsWkbTypes::PointZ;
1291  if ( isMeasure() )
1292  pointType = QgsWkbTypes::addM( pointType );
1293 
1294  const double *x = mX.constData();
1295  const double *y = mY.constData();
1296  const double *z = is3D() ? mZ.constData() : nullptr;
1297  const double *m = isMeasure() ? mM.constData() : nullptr;
1298 
1299  double prevX = *x++;
1300  double prevY = *y++;
1301  double prevZ = z ? *z++ : 0.0;
1302  double prevM = m ? *m++ : 0.0;
1303  bool foundStart = false;
1304 
1305  if ( qgsDoubleNear( startDistance, 0.0 ) || startDistance < 0 )
1306  {
1307  substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1308  foundStart = true;
1309  }
1310 
1311  substringPoints.reserve( totalPoints );
1312 
1313  for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1314  {
1315  double x1 = prevX;
1316  double y1 = prevY;
1317  double z1 = prevZ;
1318  double m1 = prevM;
1319 
1320  double x2 = *x++;
1321  double y2 = *y++;
1322  double z2 = z ? *z++ : 0.0;
1323  double m2 = m ? *m++ : 0.0;
1324 
1325  double x3 = *x++;
1326  double y3 = *y++;
1327  double z3 = z ? *z++ : 0.0;
1328  double m3 = m ? *m++ : 0.0;
1329 
1330  bool addedSegmentEnd = false;
1331  const double segmentLength = QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1332  if ( distanceTraversed < startDistance && distanceTraversed + segmentLength > startDistance )
1333  {
1334  // start point falls on this segment
1335  const double distanceToStart = startDistance - distanceTraversed;
1336  const QgsPoint startPoint = QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1337  QgsPoint( pointType, x2, y2, z2, m2 ),
1338  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToStart );
1339 
1340  // does end point also fall on this segment?
1341  const bool endPointOnSegment = distanceTraversed + segmentLength > endDistance;
1342  if ( endPointOnSegment )
1343  {
1344  const double distanceToEnd = endDistance - distanceTraversed;
1345  const double midPointDistance = ( distanceToEnd - distanceToStart ) * 0.5 + distanceToStart;
1346  substringPoints << startPoint
1347  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1348  QgsPoint( pointType, x2, y2, z2, m2 ),
1349  QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1350  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1351  QgsPoint( pointType, x2, y2, z2, m2 ),
1352  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1353  addedSegmentEnd = true;
1354  }
1355  else
1356  {
1357  const double midPointDistance = ( segmentLength - distanceToStart ) * 0.5 + distanceToStart;
1358  substringPoints << startPoint
1359  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1360  QgsPoint( pointType, x2, y2, z2, m2 ),
1361  QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1362  << QgsPoint( pointType, x3, y3, z3, m3 );
1363  addedSegmentEnd = true;
1364  }
1365  foundStart = true;
1366  }
1367  if ( !addedSegmentEnd && foundStart && ( distanceTraversed + segmentLength > endDistance ) )
1368  {
1369  // end point falls on this segment
1370  const double distanceToEnd = endDistance - distanceTraversed;
1371  // add mid point, at half way along this arc, then add the interpolated end point
1372  substringPoints << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1373  QgsPoint( pointType, x2, y2, z2, m2 ),
1374  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd / 2.0 )
1375 
1376  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1377  QgsPoint( pointType, x2, y2, z2, m2 ),
1378  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1379  }
1380  else if ( !addedSegmentEnd && foundStart )
1381  {
1382  substringPoints << QgsPoint( pointType, x2, y2, z2, m2 )
1383  << QgsPoint( pointType, x3, y3, z3, m3 );
1384  }
1385 
1386  distanceTraversed += segmentLength;
1387  if ( distanceTraversed > endDistance )
1388  break;
1389 
1390  prevX = x3;
1391  prevY = y3;
1392  prevZ = z3;
1393  prevM = m3;
1394  }
1395 
1396  std::unique_ptr< QgsCircularString > result = qgis::make_unique< QgsCircularString >();
1397  result->setPoints( substringPoints );
1398  return result.release();
1399 }
1400 
1401 bool QgsCircularString::addZValue( double zValue )
1402 {
1403  if ( QgsWkbTypes::hasZ( mWkbType ) )
1404  return false;
1405 
1406  clearCache();
1408 
1409  int nPoints = numPoints();
1410  mZ.clear();
1411  mZ.reserve( nPoints );
1412  for ( int i = 0; i < nPoints; ++i )
1413  {
1414  mZ << zValue;
1415  }
1416  return true;
1417 }
1418 
1419 bool QgsCircularString::addMValue( double mValue )
1420 {
1421  if ( QgsWkbTypes::hasM( mWkbType ) )
1422  return false;
1423 
1424  clearCache();
1426 
1427  int nPoints = numPoints();
1428  mM.clear();
1429  mM.reserve( nPoints );
1430  for ( int i = 0; i < nPoints; ++i )
1431  {
1432  mM << mValue;
1433  }
1434  return true;
1435 }
1436 
1438 {
1439  if ( !QgsWkbTypes::hasZ( mWkbType ) )
1440  return false;
1441 
1442  clearCache();
1443 
1445  mZ.clear();
1446  return true;
1447 }
1448 
1450 {
1451  if ( !QgsWkbTypes::hasM( mWkbType ) )
1452  return false;
1453 
1454  clearCache();
1455 
1457  mM.clear();
1458  return true;
1459 }
1460 
1462 {
1463  std::swap( mX, mY );
1464  clearCache();
1465 }
int dimension() const override SIP_HOLDGIL
Returns the inherent dimension of the geometry.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
int precision
static bool circleClockwise(double angle1, double angle2, double angle3) SIP_HOLDGIL
Returns true if the circle defined by three angles is ordered clockwise.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
double y
Definition: qgspoint.h:42
void filterVertices(const std::function< bool(const QgsPoint &) > &filter) override
Filters the vertices from the geometry in place, removing any which do not return true for the filter...
void setPoints(const QgsPointSequence &points)
Resets the line string to match the specified list of points.
static QgsPoint segmentMidPointFromCenter(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, bool useShortestArc=true) SIP_HOLDGIL
Calculates the midpoint on the circle passing through p1 and p2, with the specified center coordinate...
static bool angleOnCircle(double angle, double angle1, double angle2, double angle3) SIP_HOLDGIL
Returns true if an angle is between angle1 and angle3 on a circle described by angle1, angle2 and angle3.
void setY(double y) SIP_HOLDGIL
Sets the point&#39;s y-coordinate.
Definition: qgspoint.h:280
static Type dropM(Type type) SIP_HOLDGIL
Drops the m dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1225
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 (...
QgsPoint pointN(int i) const SIP_HOLDGIL
Returns the point at index i within the circular string.
bool pointAt(int node, QgsPoint &point, QgsVertexId::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:316
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:702
static QDomElement pointsToGML3(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D, QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::posList DOM element.
QgsRectangle calculateBoundingBox() const override
Default calculator for the minimal bounding box for the geometry.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1146
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgscurve.cpp:257
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
QgsPoint startPoint() const override SIP_HOLDGIL
Returns the starting point of the curve.
static endian_t endian()
Returns whether this machine uses big or little endian.
double xAt(int index) const override SIP_HOLDGIL
Returns the x-coordinate of the specified node in the line string.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
int vertex
Vertex number.
int part
Part number.
QgsWkbTypes::Type mWkbType
double yAt(int index) const override SIP_HOLDGIL
Returns the y-coordinate of the specified node in the line string.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
static Type addM(Type type) SIP_HOLDGIL
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1171
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3) SIP_HOLDGIL
Calculates the average angle (in radians) between the two linear segments from (x1, y1) to (x2, y2) and (x2, y2) to (x3, y3).
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
void clear() override
Clears the geometry, ie reset it to a null geometry.
static double sweepAngle(double centerX, double centerY, double x1, double y1, double x2, double y2, double x3, double y3) SIP_HOLDGIL
Calculates angle of a circular string part defined by pt1, pt2, pt3.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QString wktTypeStr() const
Returns the WKT type string of the geometry.
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1100
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
bool equals(const QgsCurve &other) const override
Checks whether this curve exactly equals another curve.
static QString pointsToWKT(const QgsPointSequence &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
QgsCircularString * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership...
static bool circleAngleBetween(double angle, double angle1, double angle2, bool clockwise) SIP_HOLDGIL
Returns true if, in a circle, angle is between angle1 and angle2.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:69
void setX(double x) SIP_HOLDGIL
Sets the point&#39;s x-coordinate.
Definition: qgspoint.h:269
static double circleTangentDirection(const QgsPoint &tangentPoint, const QgsPoint &cp1, const QgsPoint &cp2, const QgsPoint &cp3) SIP_HOLDGIL
Calculates the direction angle of a circle tangent (clockwise from north in radians) ...
static int leftOfLine(const double x, const double y, const double x1, const double y1, const double x2, const double y2) SIP_HOLDGIL
Returns a value < 0 if the point (x, y) is left of the line from (x1, y1) -> (x2, y2)...
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
Utility class for identifying a unique vertex within a geometry.
VertexType
Type of vertex.
double length() const override
Returns the planar, 2-dimensional length of the geometry.
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML2 representation of the geometry.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
static Type dropZ(Type type) SIP_HOLDGIL
Drops the z dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1207
QgsCircularString * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
An intermediate point on a segment defining the curvature of the segment.
static QgsPoint interpolatePointOnArc(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double distance) SIP_HOLDGIL
Interpolates a point on an arc defined by three points, pt1, pt2 and pt3.
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
void transformCoords(int numPoint, double *x, double *y, double *z, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transform an array of coordinates to the destination CRS.
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb()
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
static void circleCenterRadius(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double &radius, double &centerX, double &centerY) SIP_HOLDGIL
Returns radius and center of the circle through pt1, pt2, pt3.
AxisOrder
Axis order for GML generation.
static QgsCircularString fromTwoPointsAndCenter(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, bool useShortestArc=true)
Creates a circular string with a single arc representing the curve from p1 to p2 with the specified c...
static double sqrDistance2D(const QgsPoint &pt1, const QgsPoint &pt2) SIP_HOLDGIL
Returns the squared 2D distance between two points.
bool snapToGridPrivate(double hSpacing, double vSpacing, double dSpacing, double mSpacing, const QVector< double > &srcX, const QVector< double > &srcY, const QVector< double > &srcZ, const QVector< double > &srcM, QVector< double > &outX, QVector< double > &outY, QVector< double > &outZ, QVector< double > &outM) const
Helper function for QgsCurve subclasses to snap to grids.
Definition: qgscurve.cpp:280
The actual start or end point of a segment.
static double ccwAngle(double dy, double dx) SIP_HOLDGIL
Returns the counter clockwise angle between a line with components dx, dy and the line with dx > 0 an...
QVector< QgsPoint > QgsPointSequence
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle...
Definition: qgsrectangle.h:359
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1050
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
QgsPoint endPoint() const override SIP_HOLDGIL
Returns the end point of the curve.
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
static QgsPointSequence pointsFromWKT(const QString &wktCoordinateList, bool is3D, bool isMeasure)
Returns a list of points contained in a WKT string.
QgsCircularString * clone() const override
Clones the geometry by performing a deep copy.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
static QgsPoint pointOnLineWithDistance(const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance) SIP_HOLDGIL
Returns a point a specified distance toward a second point.
QgsCircularString * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid...
void setPoints(const QgsPointSequence &points)
Sets the circular string&#39;s points.
Class for doing transforms between two map coordinate systems.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
static void pointsToWKB(QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
double z
Definition: qgspoint.h:43
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
Circular string geometry type.
QgsCircularString() SIP_HOLDGIL
Constructs an empty circular string.
void transformVertices(const std::function< QgsPoint(const QgsPoint &) > &transform) override
Transforms the vertices from the geometry in place, applying the transform function to every vertex...
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
QgsCircularString * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
void swapXy() override
Swaps the x and y coordinates from the geometry.
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML3 representation of the geometry.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether &#39;thepoint&#39; is left or right of the line from &#39;p1&#39; to &#39;p2&#39;. Negative values mean left ...
Definition: MathUtils.cpp:292
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
int ring
Ring number.
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *std::numeric_limits< double >::epsilon()) const override
Searches for the closest segment of the geometry to a given point.
static double circleLength(double x1, double y1, double x2, double y2, double x3, double y3) SIP_HOLDGIL
Length of a circular string segment defined by pt1, pt2, pt3.
QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a new line string geometry corresponding to a segmentized approximation of the curve...
double m
Definition: qgspoint.h:44
bool dropMValue() override
Drops any measure values which exist in the geometry.
QString geometryType() const override SIP_HOLDGIL
Returns a unique string representing the geometry type.
static void segmentizeArc(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, QgsPointSequence &points, double tolerance=M_PI_2/90, QgsAbstractGeometry::SegmentationToleranceType toleranceType=QgsAbstractGeometry::MaximumAngle, bool hasZ=false, bool hasM=false)
Convert circular arc defined by p1, p2, p3 (p1/p3 being start resp.
double x
Definition: qgspoint.h:41