QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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 
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 );
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 }
QgsCurve
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
QgsCircularString::asWkt
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
Definition: qgscircularstring.cpp:352
QgsVertexId::part
int part
Part number.
Definition: qgsabstractgeometry.h:1131
QgsCircularString::transformVertices
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.
Definition: qgscircularstring.cpp:619
QgsCircularString::transform
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
Definition: qgscircularstring.cpp:721
QgsVertexId::vertex
int vertex
Vertex number.
Definition: qgsabstractgeometry.h:1137
QgsCircularString::addToPainterPath
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
Definition: qgscircularstring.cpp:769
QgsCircularString::geometryType
QString geometryType() const override SIP_HOLDGIL
Returns a unique string representing the geometry type.
Definition: qgscircularstring.cpp:113
QgsCircularString::fromWkt
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
Definition: qgscircularstring.cpp:307
QgsWkbTypes::dropM
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:1213
QgsLineString::setPoints
void setPoints(const QgsPointSequence &points)
Resets the line string to match the specified list of points.
Definition: qgslinestring.cpp:809
QgsWkbTypes::Point
@ Point
Definition: qgswkbtypes.h:72
QgsRectangle::combineExtentWith
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:359
QgsVertexId::SegmentVertex
@ SegmentVertex
The actual start or end point of a segment.
Definition: qgsabstractgeometry.h:1066
qgslinestring.h
QgsCircularString::filterVertices
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...
Definition: qgscircularstring.cpp:573
QgsCircularString::pointAt
bool pointAt(int node, QgsPoint &point, QgsVertexId::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
Definition: qgscircularstring.cpp:957
QgsGeometryUtils::ccwAngle
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...
Definition: qgsgeometryutils.cpp:659
QgsCircularString::pointN
QgsPoint pointN(int i) const SIP_HOLDGIL
Returns the point at index i within the circular string.
Definition: qgscircularstring.cpp:520
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:38
qgswkbptr.h
QgsCircularString::dropMValue
bool dropMValue() override
Drops any measure values which exist in the geometry.
Definition: qgscircularstring.cpp:1449
QgsCircularString::xAt
double xAt(int index) const override SIP_HOLDGIL
Returns the x-coordinate of the specified node in the line string.
Definition: qgscircularstring.cpp:557
qgsmaptopixel.h
QgsGeometryUtils::segmentizeArc
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.
Definition: qgsgeometryutils.cpp:937
QgsWkbTypes::flatType
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:702
QgsAbstractGeometry::parts
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
Definition: qgsabstractgeometry.cpp:271
QgsCoordinateTransform::TransformDirection
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
Definition: qgscoordinatetransform.h:59
QgsCircularString::hasCurvedSegments
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
Definition: qgscircularstring.cpp:1029
QgsWkbTypes::addZ
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1139
QgsCircularString::moveVertex
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
Definition: qgscircularstring.cpp:853
QgsAbstractGeometry::wktTypeStr
QString wktTypeStr() const
Returns the WKT type string of the geometry.
Definition: qgsabstractgeometry.cpp:147
QgsPoint::z
double z
Definition: qgspoint.h:43
QgsCircularString::equals
bool equals(const QgsCurve &other) const override
Checks whether this curve exactly equals another curve.
Definition: qgscircularstring.cpp:78
QgsCircularString::segmentLength
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
Definition: qgscircularstring.cpp:1172
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
QgsAbstractGeometry::SegmentationToleranceType
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
Definition: qgsabstractgeometry.h:115
qgspoint.h
QgsAbstractGeometry::mWkbType
QgsWkbTypes::Type mWkbType
Definition: qgsabstractgeometry.h:1030
QgsGeometryUtils::leftOfLine
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).
Definition: qgsgeometryutils.cpp:589
QgsGeometryUtils::sqrDistance2D
static double sqrDistance2D(const QgsPoint &pt1, const QgsPoint &pt2) SIP_HOLDGIL
Returns the squared 2D distance between two points.
Definition: qgsgeometryutils.cpp:198
QgsLineString
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
QgsCircularString::draw
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
Definition: qgscircularstring.cpp:714
QgsCircularString::clear
void clear() override
Clears the geometry, ie reset it to a null geometry.
Definition: qgscircularstring.cpp:128
QgsGeometryUtils::pointsFromWKT
static QgsPointSequence pointsFromWKT(const QString &wktCoordinateList, bool is3D, bool isMeasure)
Returns a list of points contained in a WKT string.
Definition: qgsgeometryutils.cpp:1099
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QgsCurve::clearCache
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgscurve.cpp:257
QgsCircularString::insertVertex
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
Definition: qgscircularstring.cpp:822
QgsGeometryUtils::circleCenterRadius
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.
Definition: qgsgeometryutils.cpp:673
qgsapplication.h
QgsAbstractGeometry::isMeasure
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
Definition: qgsabstractgeometry.h:215
QgsWkbTypes::PointM
@ PointM
Definition: qgswkbtypes.h:99
QgsCircularString::calculateBoundingBox
QgsRectangle calculateBoundingBox() const override
Default calculator for the minimal bounding box for the geometry.
Definition: qgscircularstring.cpp:138
QgsCircularString::vertexAngle
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
Definition: qgscircularstring.cpp:1115
MathUtils::leftOf
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether 'thepoint' is left or right of the line from 'p1' to 'p2'. Negative values mean left ...
Definition: MathUtils.cpp:292
QgsPoint::y
double y
Definition: qgspoint.h:42
QgsWkbTypes::addM
static Type addM(Type type) SIP_HOLDGIL
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1164
precision
int precision
Definition: qgswfsgetfeature.cpp:49
QgsGeometryUtils::circleClockwise
static bool circleClockwise(double angle1, double angle2, double angle3) SIP_HOLDGIL
Returns true if the circle defined by three angles is ordered clockwise.
Definition: qgsgeometryutils.cpp:711
QgsCircularString::deleteVertex
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
Definition: qgscircularstring.cpp:874
QgsWkbTypes::PointZM
@ PointZM
Definition: qgswkbtypes.h:112
QgsCircularString
Circular string geometry type.
Definition: qgscircularstring.h:35
QgsAbstractGeometry::wkbType
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
Definition: qgsabstractgeometry.h:193
QgsAbstractGeometry::AxisOrder
AxisOrder
Axis order for GML generation.
Definition: qgsabstractgeometry.h:133
QgsCircularString::asGml2
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.
Definition: qgscircularstring.cpp:367
QgsCircularString::startPoint
QgsPoint startPoint() const override SIP_HOLDGIL
Returns the starting point of the curve.
Definition: qgscircularstring.cpp:418
QgsGeometryUtils::pointsToWKB
static void pointsToWKB(QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
Definition: qgsgeometryutils.cpp:1174
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
QgsConstWkbPtr
Definition: qgswkbptr.h:128
QgsCircularString::points
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
Definition: qgscircularstring.cpp:647
QgsCircularString::yAt
double yAt(int index) const override SIP_HOLDGIL
Returns the y-coordinate of the specified node in the line string.
Definition: qgscircularstring.cpp:565
QgsCircularString::interpolatePoint
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
Definition: qgscircularstring.cpp:1205
QgsGeometryUtils::averageAngle
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,...
Definition: qgsgeometryutils.cpp:1521
QgsGeometryUtils::pointOnLineWithDistance
static QgsPoint pointOnLineWithDistance(const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance) SIP_HOLDGIL
Returns a point a specified distance toward a second point.
Definition: qgsgeometryutils.cpp:600
QgsGeometryUtils::circleLength
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.
Definition: qgsgeometryutils.cpp:755
QgsCircularString::sumUpArea
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
Definition: qgscircularstring.cpp:968
QgsPoint::setX
void setX(double x) SIP_HOLDGIL
Sets the point's x-coordinate.
Definition: qgspoint.h:269
QgsGeometryUtils::segmentMidPointFromCenter
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...
Definition: qgsgeometryutils.cpp:844
QgsWkbTypes::hasM
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1093
QgsWkbTypes::PointZ
@ PointZ
Definition: qgswkbtypes.h:86
QgsPoint::x
Q_GADGET double x
Definition: qgspoint.h:41
QgsApplication::endian
static endian_t endian()
Returns whether this machine uses big or little endian.
Definition: qgsapplication.cpp:1250
QgsGeometryUtils::angleOnCircle
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,...
Definition: qgsgeometryutils.cpp:749
QgsCircularString::createEmptyWithSameType
QgsCircularString * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
Definition: qgscircularstring.cpp:106
QgsPoint::m
double m
Definition: qgspoint.h:44
qgscoordinatetransform.h
QgsCircularString::reversed
QgsCircularString * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
Definition: qgscircularstring.cpp:1189
QgsWkbTypes::dropZ
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:1195
QgsGeometryUtils::circleTangentDirection
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)
Definition: qgsgeometryutils.cpp:853
QgsCircularString::addZValue
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition: qgscircularstring.cpp:1401
QgsWkbPtr
Definition: qgswkbptr.h:43
QgsCircularString::length
double length() const override
Returns the planar, 2-dimensional length of the geometry.
Definition: qgscircularstring.cpp:407
QgsCurve::snapToGridPrivate
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
qgscircularstring.h
QgsCoordinateTransform::transformCoords
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.
Definition: qgscoordinatetransform.cpp:630
QgsCircularString::snappedToGrid
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.
Definition: qgscircularstring.cpp:451
qgsgeometryutils.h
QgsAbstractGeometry::is3D
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
Definition: qgsabstractgeometry.h:206
QgsGeometryUtils::pointsToGML3
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.
Definition: qgsgeometryutils.cpp:1237
QgsWkbTypes::CircularString
@ CircularString
Definition: qgswkbtypes.h:80
QgsCircularString::swapXy
void swapXy() override
Swaps the x and y coordinates from the geometry.
Definition: qgscircularstring.cpp:1461
QgsCircularString::numPoints
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
Definition: qgscircularstring.cpp:515
QgsGeometryUtils::sweepAngle
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.
Definition: qgsgeometryutils.cpp:767
QgsCircularString::fromWkb
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
Definition: qgscircularstring.cpp:268
QgsCircularString::wkbSize
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb()
Definition: qgscircularstring.cpp:332
QgsPointSequence
QVector< QgsPoint > QgsPointSequence
Definition: qgsabstractgeometry.h:46
c
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
Definition: porting_processing.dox:1
QgsCircularString::drawAsPolygon
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
Definition: qgscircularstring.cpp:817
QgsGeometryUtils::pointsToWKT
static QString pointsToWKT(const QgsPointSequence &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
Definition: qgsgeometryutils.cpp:1191
QgsCircularString::isEmpty
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgscircularstring.cpp:401
QgsVertexId
Utility class for identifying a unique vertex within a geometry.
Definition: qgsabstractgeometry.h:1059
QgsCircularString::fromTwoPointsAndCenter
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...
Definition: qgscircularstring.cpp:72
QgsCircularString::endPoint
QgsPoint endPoint() const override SIP_HOLDGIL
Returns the end point of the curve.
Definition: qgscircularstring.cpp:427
QgsVertexId::ring
int ring
Ring number.
Definition: qgsabstractgeometry.h:1134
QgsCircularString::addMValue
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition: qgscircularstring.cpp:1419
QgsCircularString::closestSegment
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.
Definition: qgscircularstring.cpp:918
QgsVertexId::VertexType
VertexType
Type of vertex.
Definition: qgsabstractgeometry.h:1065
QgsCircularString::curveToLine
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.
Definition: qgscircularstring.cpp:436
QgsCircularString::curveSubstring
QgsCircularString * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
Definition: qgscircularstring.cpp:1274
QgsWkbTypes::hasZ
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1043
QgsCircularString::asWkb
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Definition: qgscircularstring.cpp:339
QgsGeometryUtils::circleAngleBetween
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.
Definition: qgsgeometryutils.cpp:723
QgsConstWkbPtr::readHeader
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
QgsPoint::setY
void setY(double y) SIP_HOLDGIL
Sets the point's y-coordinate.
Definition: qgspoint.h:280
QgsGeometryUtils::wktReadBlock
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 (...
Definition: qgsgeometryutils.cpp:1306
qgslogger.h
QgsAbstractGeometry::setZMTypeFromSubGeometry
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
Definition: qgsabstractgeometry.cpp:43
QgsCircularString::removeDuplicateNodes
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...
Definition: qgscircularstring.cpp:464
QgsVertexId::CurveVertex
@ CurveVertex
An intermediate point on a segment defining the curvature of the segment.
Definition: qgsabstractgeometry.h:1067
QgsCoordinateTransform
Class for doing transforms between two map coordinate systems.
Definition: qgscoordinatetransform.h:53
QgsCircularString::QgsCircularString
QgsCircularString() SIP_HOLDGIL
Constructs an empty circular string.
Definition: qgscircularstring.cpp:34
QgsCircularString::clone
QgsCircularString * clone() const override
Clones the geometry by performing a deep copy.
Definition: qgscircularstring.cpp:123
MathUtils::angle
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
QgsCircularString::setPoints
void setPoints(const QgsPointSequence &points)
Sets the circular string's points.
Definition: qgscircularstring.cpp:657
QgsCircularString::dimension
int dimension() const override SIP_HOLDGIL
Returns the inherent dimension of the geometry.
Definition: qgscircularstring.cpp:118
QgsCircularString::asJsonObject
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
Definition: qgscircularstring.cpp:394
QgsCircularString::asGml3
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.
Definition: qgscircularstring.cpp:375
QgsGeometryUtils::interpolatePointOnArc
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.
Definition: qgsgeometryutils.cpp:634
QgsCircularString::dropZValue
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Definition: qgscircularstring.cpp:1437