QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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  if ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 )
318  return true;
319 
321  return true;
322 }
323 
324 QByteArray QgsCircularString::asWkb( WkbFlags ) const
325 {
326  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
327  binarySize += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
328 
329  QByteArray wkbArray;
330  wkbArray.resize( binarySize );
331  QgsWkbPtr wkb( wkbArray );
332  wkb << static_cast<char>( QgsApplication::endian() );
333  wkb << static_cast<quint32>( wkbType() );
334  QgsPointSequence pts;
335  points( pts );
336  QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure() );
337  return wkbArray;
338 }
339 
341 {
342  QString wkt = wktTypeStr() + ' ';
343 
344  if ( isEmpty() )
345  wkt += QStringLiteral( "EMPTY" );
346  else
347  {
348  QgsPointSequence pts;
349  points( pts );
351  }
352  return wkt;
353 }
354 
355 QDomElement QgsCircularString::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
356 {
357  // GML2 does not support curves
358  std::unique_ptr< QgsLineString > line( curveToLine() );
359  QDomElement gml = line->asGml2( doc, precision, ns, axisOrder );
360  return gml;
361 }
362 
363 QDomElement QgsCircularString::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
364 {
365  QgsPointSequence pts;
366  points( pts );
367 
368  QDomElement elemCurve = doc.createElementNS( ns, QStringLiteral( "Curve" ) );
369 
370  if ( isEmpty() )
371  return elemCurve;
372 
373  QDomElement elemSegments = doc.createElementNS( ns, QStringLiteral( "segments" ) );
374  QDomElement elemArcString = doc.createElementNS( ns, QStringLiteral( "ArcString" ) );
375  elemArcString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D(), axisOrder ) );
376  elemSegments.appendChild( elemArcString );
377  elemCurve.appendChild( elemSegments );
378  return elemCurve;
379 }
380 
381 
383 {
384  // GeoJSON does not support curves
385  std::unique_ptr< QgsLineString > line( curveToLine() );
386  return line->asJsonObject( precision );
387 }
388 
390 {
391  return mX.isEmpty();
392 }
393 
394 //curve interface
396 {
397  int nPoints = numPoints();
398  double length = 0;
399  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
400  {
401  length += QgsGeometryUtils::circleLength( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2] );
402  }
403  return length;
404 }
405 
407 {
408  if ( numPoints() < 1 )
409  {
410  return QgsPoint();
411  }
412  return pointN( 0 );
413 }
414 
416 {
417  if ( numPoints() < 1 )
418  {
419  return QgsPoint();
420  }
421  return pointN( numPoints() - 1 );
422 }
423 
425 {
426  QgsLineString *line = new QgsLineString();
428  int nPoints = numPoints();
429 
430  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
431  {
432  QgsGeometryUtils::segmentizeArc( pointN( i ), pointN( i + 1 ), pointN( i + 2 ), points, tolerance, toleranceType, is3D(), isMeasure() );
433  }
434 
435  line->setPoints( points );
436  return line;
437 }
438 
439 QgsCircularString *QgsCircularString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
440 {
441  // prepare result
442  std::unique_ptr<QgsCircularString> result { createEmptyWithSameType() };
443 
444  bool res = snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
445  result->mX, result->mY, result->mZ, result->mM );
446  if ( res )
447  return result.release();
448  else
449  return nullptr;
450 }
451 
452 bool QgsCircularString::removeDuplicateNodes( double epsilon, bool useZValues )
453 {
454  if ( mX.count() <= 3 )
455  return false; // don't create degenerate lines
456  bool result = false;
457  double prevX = mX.at( 0 );
458  double prevY = mY.at( 0 );
459  bool hasZ = is3D();
460  bool useZ = hasZ && useZValues;
461  double prevZ = useZ ? mZ.at( 0 ) : 0;
462  int i = 1;
463  int remaining = mX.count();
464  // we have to consider points in pairs, since a segment can validly have the same start and
465  // end if it has a different curve point
466  while ( i + 1 < remaining )
467  {
468  double currentCurveX = mX.at( i );
469  double currentCurveY = mY.at( i );
470  double currentX = mX.at( i + 1 );
471  double currentY = mY.at( i + 1 );
472  double currentZ = useZ ? mZ.at( i + 1 ) : 0;
473  if ( qgsDoubleNear( currentCurveX, prevX, epsilon ) &&
474  qgsDoubleNear( currentCurveY, prevY, epsilon ) &&
475  qgsDoubleNear( currentX, prevX, epsilon ) &&
476  qgsDoubleNear( currentY, prevY, epsilon ) &&
477  ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
478  {
479  result = true;
480  // remove point
481  mX.removeAt( i );
482  mX.removeAt( i );
483  mY.removeAt( i );
484  mY.removeAt( i );
485  if ( hasZ )
486  {
487  mZ.removeAt( i );
488  mZ.removeAt( i );
489  }
490  remaining -= 2;
491  }
492  else
493  {
494  prevX = currentX;
495  prevY = currentY;
496  prevZ = currentZ;
497  i += 2;
498  }
499  }
500  return result;
501 }
502 
504 {
505  return std::min( mX.size(), mY.size() );
506 }
507 
509 {
510  if ( i < 0 || std::min( mX.size(), mY.size() ) <= i )
511  {
512  return QgsPoint();
513  }
514 
515  double x = mX.at( i );
516  double y = mY.at( i );
517  double z = 0;
518  double m = 0;
519 
520  if ( is3D() )
521  {
522  z = mZ.at( i );
523  }
524  if ( isMeasure() )
525  {
526  m = mM.at( i );
527  }
528 
530  if ( is3D() && isMeasure() )
531  {
533  }
534  else if ( is3D() )
535  {
537  }
538  else if ( isMeasure() )
539  {
541  }
542  return QgsPoint( t, x, y, z, m );
543 }
544 
545 double QgsCircularString::xAt( int index ) const
546 {
547  if ( index >= 0 && index < mX.size() )
548  return mX.at( index );
549  else
550  return 0.0;
551 }
552 
553 double QgsCircularString::yAt( int index ) const
554 {
555  if ( index >= 0 && index < mY.size() )
556  return mY.at( index );
557  else
558  return 0.0;
559 }
560 
561 void QgsCircularString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
562 {
563  bool hasZ = is3D();
564  bool hasM = isMeasure();
565  int size = mX.size();
566 
567  double *srcX = mX.data(); // clazy:exclude=detaching-member
568  double *srcY = mY.data(); // clazy:exclude=detaching-member
569  double *srcM = hasM ? mM.data() : nullptr; // clazy:exclude=detaching-member
570  double *srcZ = hasZ ? mZ.data() : nullptr; // clazy:exclude=detaching-member
571 
572  double *destX = srcX;
573  double *destY = srcY;
574  double *destM = srcM;
575  double *destZ = srcZ;
576 
577  int filteredPoints = 0;
578  for ( int i = 0; i < size; ++i )
579  {
580  double x = *srcX++;
581  double y = *srcY++;
582  double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
583  double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
584 
585  if ( filter( QgsPoint( x, y, z, m ) ) )
586  {
587  filteredPoints++;
588  *destX++ = x;
589  *destY++ = y;
590  if ( hasM )
591  *destM++ = m;
592  if ( hasZ )
593  *destZ++ = z;
594  }
595  }
596 
597  mX.resize( filteredPoints );
598  mY.resize( filteredPoints );
599  if ( hasZ )
600  mZ.resize( filteredPoints );
601  if ( hasM )
602  mM.resize( filteredPoints );
603 
604  clearCache();
605 }
606 
607 void QgsCircularString::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
608 {
609  bool hasZ = is3D();
610  bool hasM = isMeasure();
611  int size = mX.size();
612 
613  double *srcX = mX.data();
614  double *srcY = mY.data();
615  double *srcM = hasM ? mM.data() : nullptr;
616  double *srcZ = hasZ ? mZ.data() : nullptr;
617 
618  for ( int i = 0; i < size; ++i )
619  {
620  double x = *srcX;
621  double y = *srcY;
622  double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
623  double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
624  QgsPoint res = transform( QgsPoint( x, y, z, m ) );
625  *srcX++ = res.x();
626  *srcY++ = res.y();
627  if ( hasM )
628  *srcM++ = res.m();
629  if ( hasZ )
630  *srcZ++ = res.z();
631  }
632  clearCache();
633 }
634 
636 {
637  pts.clear();
638  int nPts = numPoints();
639  for ( int i = 0; i < nPts; ++i )
640  {
641  pts.push_back( pointN( i ) );
642  }
643 }
644 
646 {
647  clearCache();
648 
649  if ( points.empty() )
650  {
652  mX.clear();
653  mY.clear();
654  mZ.clear();
655  mM.clear();
656  return;
657  }
658 
659  //get wkb type from first point
660  const QgsPoint &firstPt = points.at( 0 );
661  bool hasZ = firstPt.is3D();
662  bool hasM = firstPt.isMeasure();
663 
665 
666  mX.resize( points.size() );
667  mY.resize( points.size() );
668  if ( hasZ )
669  {
670  mZ.resize( points.size() );
671  }
672  else
673  {
674  mZ.clear();
675  }
676  if ( hasM )
677  {
678  mM.resize( points.size() );
679  }
680  else
681  {
682  mM.clear();
683  }
684 
685  for ( int i = 0; i < points.size(); ++i )
686  {
687  mX[i] = points[i].x();
688  mY[i] = points[i].y();
689  if ( hasZ )
690  {
691  double z = points.at( i ).z();
692  mZ[i] = std::isnan( z ) ? 0 : z;
693  }
694  if ( hasM )
695  {
696  double m = points.at( i ).m();
697  mM[i] = std::isnan( m ) ? 0 : m;
698  }
699  }
700 }
701 
702 void QgsCircularString::draw( QPainter &p ) const
703 {
704  QPainterPath path;
705  addToPainterPath( path );
706  p.drawPath( path );
707 }
708 
710 {
711  clearCache();
712 
713  double *zArray = mZ.data();
714 
715  bool hasZ = is3D();
716  int nPoints = numPoints();
717  bool useDummyZ = !hasZ || !transformZ;
718  if ( useDummyZ )
719  {
720  zArray = new double[nPoints];
721  for ( int i = 0; i < nPoints; ++i )
722  {
723  zArray[i] = 0;
724  }
725  }
726  ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
727  if ( useDummyZ )
728  {
729  delete[] zArray;
730  }
731 }
732 
733 void QgsCircularString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
734 {
735  clearCache();
736 
737  int nPoints = numPoints();
738  bool hasZ = is3D();
739  bool hasM = isMeasure();
740  for ( int i = 0; i < nPoints; ++i )
741  {
742  qreal x, y;
743  t.map( mX.at( i ), mY.at( i ), &x, &y );
744  mX[i] = x;
745  mY[i] = y;
746  if ( hasZ )
747  {
748  mZ[i] = mZ.at( i ) * zScale + zTranslate;
749  }
750  if ( hasM )
751  {
752  mM[i] = mM.at( i ) * mScale + mTranslate;
753  }
754  }
755 }
756 
757 void QgsCircularString::addToPainterPath( QPainterPath &path ) const
758 {
759  int nPoints = numPoints();
760  if ( nPoints < 1 )
761  {
762  return;
763  }
764 
765  if ( path.isEmpty() || path.currentPosition() != QPointF( mX[0], mY[0] ) )
766  {
767  path.moveTo( QPointF( mX[0], mY[0] ) );
768  }
769 
770  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
771  {
772  QgsPointSequence pt;
773  QgsGeometryUtils::segmentizeArc( QgsPoint( mX[i], mY[i] ), QgsPoint( mX[i + 1], mY[i + 1] ), QgsPoint( mX[i + 2], mY[i + 2] ), pt );
774  for ( int j = 1; j < pt.size(); ++j )
775  {
776  path.lineTo( pt.at( j ).x(), pt.at( j ).y() );
777  }
778 #if 0
779  //arcTo( path, QPointF( mX[i], mY[i] ), QPointF( mX[i + 1], mY[i + 1] ), QPointF( mX[i + 2], mY[i + 2] ) );
780 #endif
781  }
782 
783  //if number of points is even, connect to last point with straight line (even though the circular string is not valid)
784  if ( nPoints % 2 == 0 )
785  {
786  path.lineTo( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
787  }
788 }
789 
790 #if 0
791 void QgsCircularString::arcTo( QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3 )
792 {
793  double centerX, centerY, radius;
794  QgsGeometryUtils::circleCenterRadius( QgsPoint( pt1.x(), pt1.y() ), QgsPoint( pt2.x(), pt2.y() ), QgsPoint( pt3.x(), pt3.y() ),
795  radius, centerX, centerY );
796 
797  double p1Angle = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
798  double sweepAngle = QgsGeometryUtils::sweepAngle( centerX, centerY, pt1.x(), pt1.y(), pt2.x(), pt2.y(), pt3.x(), pt3.y() );
799 
800  double diameter = 2 * radius;
801  path.arcTo( centerX - radius, centerY - radius, diameter, diameter, p1Angle, sweepAngle );
802 }
803 #endif
804 
805 void QgsCircularString::drawAsPolygon( QPainter &p ) const
806 {
807  draw( p );
808 }
809 
810 bool QgsCircularString::insertVertex( QgsVertexId position, const QgsPoint &vertex )
811 {
812  if ( position.vertex >= mX.size() || position.vertex < 1 )
813  {
814  return false;
815  }
816 
817  mX.insert( position.vertex, vertex.x() );
818  mY.insert( position.vertex, vertex.y() );
819  if ( is3D() )
820  {
821  mZ.insert( position.vertex, vertex.z() );
822  }
823  if ( isMeasure() )
824  {
825  mM.insert( position.vertex, vertex.m() );
826  }
827 
828  bool vertexNrEven = ( position.vertex % 2 == 0 );
829  if ( vertexNrEven )
830  {
831  insertVertexBetween( position.vertex - 2, position.vertex - 1, position.vertex );
832  }
833  else
834  {
835  insertVertexBetween( position.vertex, position.vertex + 1, position.vertex - 1 );
836  }
837  clearCache(); //set bounding box invalid
838  return true;
839 }
840 
841 bool QgsCircularString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
842 {
843  if ( position.vertex < 0 || position.vertex >= mX.size() )
844  {
845  return false;
846  }
847 
848  mX[position.vertex] = newPos.x();
849  mY[position.vertex] = newPos.y();
850  if ( is3D() && newPos.is3D() )
851  {
852  mZ[position.vertex] = newPos.z();
853  }
854  if ( isMeasure() && newPos.isMeasure() )
855  {
856  mM[position.vertex] = newPos.m();
857  }
858  clearCache(); //set bounding box invalid
859  return true;
860 }
861 
863 {
864  int nVertices = this->numPoints();
865  if ( nVertices < 4 ) //circular string must have at least 3 vertices
866  {
867  clear();
868  return true;
869  }
870  if ( position.vertex < 0 || position.vertex > ( nVertices - 1 ) )
871  {
872  return false;
873  }
874 
875  if ( position.vertex < ( nVertices - 2 ) )
876  {
877  //remove this and the following vertex
878  deleteVertex( position.vertex + 1 );
879  deleteVertex( position.vertex );
880  }
881  else //remove this and the preceding vertex
882  {
883  deleteVertex( position.vertex );
884  deleteVertex( position.vertex - 1 );
885  }
886 
887  clearCache(); //set bounding box invalid
888  return true;
889 }
890 
892 {
893  mX.remove( i );
894  mY.remove( i );
895  if ( is3D() )
896  {
897  mZ.remove( i );
898  }
899  if ( isMeasure() )
900  {
901  mM.remove( i );
902  }
903  clearCache();
904 }
905 
906 double QgsCircularString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
907 {
908  double minDist = std::numeric_limits<double>::max();
909  QgsPoint minDistSegmentPoint;
910  QgsVertexId minDistVertexAfter;
911  int minDistLeftOf = 0;
912 
913  double currentDist = 0.0;
914 
915  int nPoints = numPoints();
916  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
917  {
918  currentDist = closestPointOnArc( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2], pt, segmentPt, vertexAfter, leftOf, epsilon );
919  if ( currentDist < minDist )
920  {
921  minDist = currentDist;
922  minDistSegmentPoint = segmentPt;
923  minDistVertexAfter.vertex = vertexAfter.vertex + i;
924  if ( leftOf )
925  {
926  minDistLeftOf = *leftOf;
927  }
928  }
929  }
930 
931  if ( minDist == std::numeric_limits<double>::max() )
932  return -1; // error: no segments
933 
934  segmentPt = minDistSegmentPoint;
935  vertexAfter = minDistVertexAfter;
936  vertexAfter.part = 0;
937  vertexAfter.ring = 0;
938  if ( leftOf )
939  {
940  *leftOf = qgsDoubleNear( minDist, 0.0 ) ? 0 : minDistLeftOf;
941  }
942  return minDist;
943 }
944 
945 bool QgsCircularString::pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const
946 {
947  if ( node < 0 || node >= numPoints() )
948  {
949  return false;
950  }
951  point = pointN( node );
952  type = ( node % 2 == 0 ) ? QgsVertexId::SegmentVertex : QgsVertexId::CurveVertex;
953  return true;
954 }
955 
956 void QgsCircularString::sumUpArea( double &sum ) const
957 {
958  int maxIndex = numPoints() - 2;
959 
960  for ( int i = 0; i < maxIndex; i += 2 )
961  {
962  QgsPoint p1( mX[i], mY[i] );
963  QgsPoint p2( mX[i + 1], mY[i + 1] );
964  QgsPoint p3( mX[i + 2], mY[i + 2] );
965 
966  //segment is a full circle, p2 is the center point
967  if ( p1 == p3 )
968  {
969  double r2 = QgsGeometryUtils::sqrDistance2D( p1, p2 ) / 4.0;
970  sum += M_PI * r2;
971  continue;
972  }
973 
974  sum += 0.5 * ( mX[i] * mY[i + 2] - mY[i] * mX[i + 2] );
975 
976  //calculate area between circle and chord, then sum / subtract from total area
977  double midPointX = ( p1.x() + p3.x() ) / 2.0;
978  double midPointY = ( p1.y() + p3.y() ) / 2.0;
979 
980  double radius, centerX, centerY;
981  QgsGeometryUtils::circleCenterRadius( p1, p2, p3, radius, centerX, centerY );
982 
983  double d = std::sqrt( QgsGeometryUtils::sqrDistance2D( QgsPoint( centerX, centerY ), QgsPoint( midPointX, midPointY ) ) );
984  double r2 = radius * radius;
985 
986  if ( d > radius )
987  {
988  //d cannot be greater than radius, something must be wrong...
989  continue;
990  }
991 
992  bool circlePointLeftOfLine = QgsGeometryUtils::leftOfLine( p2.x(), p2.y(), p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
993  bool centerPointLeftOfLine = QgsGeometryUtils::leftOfLine( centerX, centerY, p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
994 
995  double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - M_1_PI * std::asin( d / radius );
996  double circleChordArea = 0;
997  if ( circlePointLeftOfLine == centerPointLeftOfLine )
998  {
999  circleChordArea = M_PI * r2 * ( 1 - cov );
1000  }
1001  else
1002  {
1003  circleChordArea = M_PI * r2 * cov;
1004  }
1005 
1006  if ( !circlePointLeftOfLine )
1007  {
1008  sum += circleChordArea;
1009  }
1010  else
1011  {
1012  sum -= circleChordArea;
1013  }
1014  }
1015 }
1016 
1018 {
1019  return true;
1020 }
1021 
1022 double QgsCircularString::closestPointOnArc( double x1, double y1, double x2, double y2, double x3, double y3,
1023  const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon )
1024 {
1025  double radius, centerX, centerY;
1026  QgsPoint pt1( x1, y1 );
1027  QgsPoint pt2( x2, y2 );
1028  QgsPoint pt3( x3, y3 );
1029 
1030  QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
1031  double angle = QgsGeometryUtils::ccwAngle( pt.y() - centerY, pt.x() - centerX );
1032  double angle1 = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
1033  double angle2 = QgsGeometryUtils::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
1034  double angle3 = QgsGeometryUtils::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
1035 
1036  bool clockwise = QgsGeometryUtils::circleClockwise( angle1, angle2, angle3 );
1037 
1038  if ( QgsGeometryUtils::angleOnCircle( angle, angle1, angle2, angle3 ) )
1039  {
1040  //get point on line center -> pt with distance radius
1041  segmentPt = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), pt, radius );
1042 
1043  //vertexAfter
1044  vertexAfter.vertex = QgsGeometryUtils::circleAngleBetween( angle, angle1, angle2, clockwise ) ? 1 : 2;
1045  }
1046  else
1047  {
1048  double distPtPt1 = QgsGeometryUtils::sqrDistance2D( pt, pt1 );
1049  double distPtPt3 = QgsGeometryUtils::sqrDistance2D( pt, pt3 );
1050  segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
1051  vertexAfter.vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
1052  }
1053 
1054  double sqrDistance = QgsGeometryUtils::sqrDistance2D( segmentPt, pt );
1055  //prevent rounding errors if the point is directly on the segment
1056  if ( qgsDoubleNear( sqrDistance, 0.0, epsilon ) )
1057  {
1058  segmentPt.setX( pt.x() );
1059  segmentPt.setY( pt.y() );
1060  sqrDistance = 0.0;
1061  }
1062 
1063  if ( leftOf )
1064  {
1065  double sqrDistancePointToCenter = ( pt.x() - centerX ) * ( pt.x() - centerX ) + ( pt.y() - centerY ) * ( pt.y() - centerY );
1066  *leftOf = clockwise ? ( sqrDistancePointToCenter > radius * radius ? -1 : 1 )
1067  : ( sqrDistancePointToCenter < radius * radius ? -1 : 1 );
1068  }
1069 
1070  return sqrDistance;
1071 }
1072 
1073 void QgsCircularString::insertVertexBetween( int after, int before, int pointOnCircle )
1074 {
1075  double xAfter = mX.at( after );
1076  double yAfter = mY.at( after );
1077  double xBefore = mX.at( before );
1078  double yBefore = mY.at( before );
1079  double xOnCircle = mX.at( pointOnCircle );
1080  double yOnCircle = mY.at( pointOnCircle );
1081 
1082  double radius, centerX, centerY;
1083  QgsGeometryUtils::circleCenterRadius( QgsPoint( xAfter, yAfter ), QgsPoint( xBefore, yBefore ), QgsPoint( xOnCircle, yOnCircle ), radius, centerX, centerY );
1084 
1085  double x = ( xAfter + xBefore ) / 2.0;
1086  double y = ( yAfter + yBefore ) / 2.0;
1087 
1088  QgsPoint newVertex = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), QgsPoint( x, y ), radius );
1089  mX.insert( before, newVertex.x() );
1090  mY.insert( before, newVertex.y() );
1091 
1092  if ( is3D() )
1093  {
1094  mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
1095  }
1096  if ( isMeasure() )
1097  {
1098  mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
1099  }
1100  clearCache();
1101 }
1102 
1104 {
1105  if ( numPoints() < 3 )
1106  {
1107  //undefined
1108  return 0.0;
1109  }
1110 
1111  int before = vId.vertex - 1;
1112  int vertex = vId.vertex;
1113  int after = vId.vertex + 1;
1114 
1115  if ( vId.vertex % 2 != 0 ) // a curve vertex
1116  {
1117  if ( vId.vertex >= 1 && vId.vertex < numPoints() - 1 )
1118  {
1119  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[before], mY[before] ),
1120  QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[after], mY[after] ) );
1121  }
1122  }
1123  else //a point vertex
1124  {
1125  if ( vId.vertex == 0 )
1126  {
1127  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[0], mY[0] ), QgsPoint( mX[0], mY[0] ),
1128  QgsPoint( mX[1], mY[1] ), QgsPoint( mX[2], mY[2] ) );
1129  }
1130  if ( vId.vertex >= numPoints() - 1 )
1131  {
1132  int a = numPoints() - 3;
1133  int b = numPoints() - 2;
1134  int c = numPoints() - 1;
1135  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[c], mY[c] ), QgsPoint( mX[a], mY[a] ),
1136  QgsPoint( mX[b], mY[b] ), QgsPoint( mX[c], mY[c] ) );
1137  }
1138  else
1139  {
1140  if ( vId.vertex + 2 > numPoints() - 1 )
1141  {
1142  return 0.0;
1143  }
1144 
1145  int vertex1 = vId.vertex - 2;
1146  int vertex2 = vId.vertex - 1;
1147  int vertex3 = vId.vertex;
1148  double angle1 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1149  QgsPoint( mX[vertex1], mY[vertex1] ), QgsPoint( mX[vertex2], mY[vertex2] ), QgsPoint( mX[vertex3], mY[vertex3] ) );
1150  int vertex4 = vId.vertex + 1;
1151  int vertex5 = vId.vertex + 2;
1152  double angle2 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1153  QgsPoint( mX[vertex3], mY[vertex3] ), QgsPoint( mX[vertex4], mY[vertex4] ), QgsPoint( mX[vertex5], mY[vertex5] ) );
1154  return QgsGeometryUtils::averageAngle( angle1, angle2 );
1155  }
1156  }
1157  return 0.0;
1158 }
1159 
1161 {
1162  if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 2 )
1163  return 0.0;
1164 
1165  if ( startVertex.vertex % 2 == 1 )
1166  return 0.0; // curve point?
1167 
1168  double x1 = mX.at( startVertex.vertex );
1169  double y1 = mY.at( startVertex.vertex );
1170  double x2 = mX.at( startVertex.vertex + 1 );
1171  double y2 = mY.at( startVertex.vertex + 1 );
1172  double x3 = mX.at( startVertex.vertex + 2 );
1173  double y3 = mY.at( startVertex.vertex + 2 );
1174  return QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1175 }
1176 
1178 {
1179  QgsCircularString *copy = clone();
1180  std::reverse( copy->mX.begin(), copy->mX.end() );
1181  std::reverse( copy->mY.begin(), copy->mY.end() );
1182  if ( is3D() )
1183  {
1184  std::reverse( copy->mZ.begin(), copy->mZ.end() );
1185  }
1186  if ( isMeasure() )
1187  {
1188  std::reverse( copy->mM.begin(), copy->mM.end() );
1189  }
1190  return copy;
1191 }
1192 
1193 QgsPoint *QgsCircularString::interpolatePoint( const double distance ) const
1194 {
1195  if ( distance < 0 )
1196  return nullptr;
1197 
1198  double distanceTraversed = 0;
1199  const int totalPoints = numPoints();
1200  if ( totalPoints == 0 )
1201  return nullptr;
1202 
1204  if ( is3D() )
1205  pointType = QgsWkbTypes::PointZ;
1206  if ( isMeasure() )
1207  pointType = QgsWkbTypes::addM( pointType );
1208 
1209  const double *x = mX.constData();
1210  const double *y = mY.constData();
1211  const double *z = is3D() ? mZ.constData() : nullptr;
1212  const double *m = isMeasure() ? mM.constData() : nullptr;
1213 
1214  double prevX = *x++;
1215  double prevY = *y++;
1216  double prevZ = z ? *z++ : 0.0;
1217  double prevM = m ? *m++ : 0.0;
1218 
1219  if ( qgsDoubleNear( distance, 0.0 ) )
1220  {
1221  return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1222  }
1223 
1224  for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1225  {
1226  double x1 = prevX;
1227  double y1 = prevY;
1228  double z1 = prevZ;
1229  double m1 = prevM;
1230 
1231  double x2 = *x++;
1232  double y2 = *y++;
1233  double z2 = z ? *z++ : 0.0;
1234  double m2 = m ? *m++ : 0.0;
1235 
1236  double x3 = *x++;
1237  double y3 = *y++;
1238  double z3 = z ? *z++ : 0.0;
1239  double m3 = m ? *m++ : 0.0;
1240 
1241  const double segmentLength = QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1242  if ( distance < distanceTraversed + segmentLength || qgsDoubleNear( distance, distanceTraversed + segmentLength ) )
1243  {
1244  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1245  const double distanceToPoint = std::min( distance - distanceTraversed, segmentLength );
1246  return new QgsPoint( QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1247  QgsPoint( pointType, x2, y2, z2, m2 ),
1248  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToPoint ) );
1249  }
1250 
1251  distanceTraversed += segmentLength;
1252 
1253  prevX = x3;
1254  prevY = y3;
1255  prevZ = z3;
1256  prevM = m3;
1257  }
1258 
1259  return nullptr;
1260 }
1261 
1262 QgsCircularString *QgsCircularString::curveSubstring( double startDistance, double endDistance ) const
1263 {
1264  if ( startDistance < 0 && endDistance < 0 )
1265  return createEmptyWithSameType();
1266 
1267  endDistance = std::max( startDistance, endDistance );
1268 
1269  double distanceTraversed = 0;
1270  const int totalPoints = numPoints();
1271  if ( totalPoints == 0 )
1272  return clone();
1273 
1274  QVector< QgsPoint > substringPoints;
1275 
1277  if ( is3D() )
1278  pointType = QgsWkbTypes::PointZ;
1279  if ( isMeasure() )
1280  pointType = QgsWkbTypes::addM( pointType );
1281 
1282  const double *x = mX.constData();
1283  const double *y = mY.constData();
1284  const double *z = is3D() ? mZ.constData() : nullptr;
1285  const double *m = isMeasure() ? mM.constData() : nullptr;
1286 
1287  double prevX = *x++;
1288  double prevY = *y++;
1289  double prevZ = z ? *z++ : 0.0;
1290  double prevM = m ? *m++ : 0.0;
1291  bool foundStart = false;
1292 
1293  if ( qgsDoubleNear( startDistance, 0.0 ) || startDistance < 0 )
1294  {
1295  substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1296  foundStart = true;
1297  }
1298 
1299  substringPoints.reserve( totalPoints );
1300 
1301  for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1302  {
1303  double x1 = prevX;
1304  double y1 = prevY;
1305  double z1 = prevZ;
1306  double m1 = prevM;
1307 
1308  double x2 = *x++;
1309  double y2 = *y++;
1310  double z2 = z ? *z++ : 0.0;
1311  double m2 = m ? *m++ : 0.0;
1312 
1313  double x3 = *x++;
1314  double y3 = *y++;
1315  double z3 = z ? *z++ : 0.0;
1316  double m3 = m ? *m++ : 0.0;
1317 
1318  bool addedSegmentEnd = false;
1319  const double segmentLength = QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1320  if ( distanceTraversed < startDistance && distanceTraversed + segmentLength > startDistance )
1321  {
1322  // start point falls on this segment
1323  const double distanceToStart = startDistance - distanceTraversed;
1324  const QgsPoint startPoint = QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1325  QgsPoint( pointType, x2, y2, z2, m2 ),
1326  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToStart );
1327 
1328  // does end point also fall on this segment?
1329  const bool endPointOnSegment = distanceTraversed + segmentLength > endDistance;
1330  if ( endPointOnSegment )
1331  {
1332  const double distanceToEnd = endDistance - distanceTraversed;
1333  const double midPointDistance = ( distanceToEnd - distanceToStart ) * 0.5 + distanceToStart;
1334  substringPoints << startPoint
1335  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1336  QgsPoint( pointType, x2, y2, z2, m2 ),
1337  QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1338  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1339  QgsPoint( pointType, x2, y2, z2, m2 ),
1340  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1341  addedSegmentEnd = true;
1342  }
1343  else
1344  {
1345  const double midPointDistance = ( segmentLength - 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  << QgsPoint( pointType, x3, y3, z3, m3 );
1351  addedSegmentEnd = true;
1352  }
1353  foundStart = true;
1354  }
1355  if ( !addedSegmentEnd && foundStart && ( distanceTraversed + segmentLength > endDistance ) )
1356  {
1357  // end point falls on this segment
1358  const double distanceToEnd = endDistance - distanceTraversed;
1359  // add mid point, at half way along this arc, then add the interpolated end point
1360  substringPoints << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1361  QgsPoint( pointType, x2, y2, z2, m2 ),
1362  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd / 2.0 )
1363 
1364  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1365  QgsPoint( pointType, x2, y2, z2, m2 ),
1366  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1367  }
1368  else if ( !addedSegmentEnd && foundStart )
1369  {
1370  substringPoints << QgsPoint( pointType, x2, y2, z2, m2 )
1371  << QgsPoint( pointType, x3, y3, z3, m3 );
1372  }
1373 
1374  distanceTraversed += segmentLength;
1375  if ( distanceTraversed > endDistance )
1376  break;
1377 
1378  prevX = x3;
1379  prevY = y3;
1380  prevZ = z3;
1381  prevM = m3;
1382  }
1383 
1384  std::unique_ptr< QgsCircularString > result = qgis::make_unique< QgsCircularString >();
1385  result->setPoints( substringPoints );
1386  return result.release();
1387 }
1388 
1389 bool QgsCircularString::addZValue( double zValue )
1390 {
1391  if ( QgsWkbTypes::hasZ( mWkbType ) )
1392  return false;
1393 
1394  clearCache();
1396 
1397  int nPoints = numPoints();
1398  mZ.clear();
1399  mZ.reserve( nPoints );
1400  for ( int i = 0; i < nPoints; ++i )
1401  {
1402  mZ << zValue;
1403  }
1404  return true;
1405 }
1406 
1407 bool QgsCircularString::addMValue( double mValue )
1408 {
1409  if ( QgsWkbTypes::hasM( mWkbType ) )
1410  return false;
1411 
1412  clearCache();
1414 
1415  int nPoints = numPoints();
1416  mM.clear();
1417  mM.reserve( nPoints );
1418  for ( int i = 0; i < nPoints; ++i )
1419  {
1420  mM << mValue;
1421  }
1422  return true;
1423 }
1424 
1426 {
1427  if ( !QgsWkbTypes::hasZ( mWkbType ) )
1428  return false;
1429 
1430  clearCache();
1431 
1433  mZ.clear();
1434  return true;
1435 }
1436 
1438 {
1439  if ( !QgsWkbTypes::hasM( mWkbType ) )
1440  return false;
1441 
1442  clearCache();
1443 
1445  mM.clear();
1446  return true;
1447 }
1448 
1450 {
1451  std::swap( mX, mY );
1452  clearCache();
1453 }
QgsCurve
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
QgsCircularString::asWkt
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
Definition: qgscircularstring.cpp:340
QgsCircularString::startPoint
QgsPoint startPoint() const override
Returns the starting point of the curve.
Definition: qgscircularstring.cpp:406
QgsVertexId::part
int part
Definition: qgsabstractgeometry.h:1080
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:607
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:709
QgsPoint::setY
void setY(double y)
Sets the point's y-coordinate.
Definition: qgspoint.h:298
QgsGeometryUtils::circleClockwise
static bool circleClockwise(double angle1, double angle2, double angle3)
Returns true if the circle defined by three angles is ordered clockwise.
Definition: qgsgeometryutils.cpp:711
QgsVertexId::vertex
int vertex
Definition: qgsabstractgeometry.h:1082
QgsCircularString::addToPainterPath
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
Definition: qgscircularstring.cpp:757
QgsGeometryUtils::sqrDistance2D
static double sqrDistance2D(const QgsPoint &pt1, const QgsPoint &pt2)
Returns the squared 2D distance between two points.
Definition: qgsgeometryutils.cpp:198
QgsAbstractGeometry::wkbType
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
Definition: qgsabstractgeometry.h:189
QgsCircularString::fromWkt
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
Definition: qgscircularstring.cpp:307
QgsLineString::setPoints
void setPoints(const QgsPointSequence &points)
Resets the line string to match the specified list of points.
Definition: qgslinestring.cpp:756
QgsWkbTypes::Point
@ Point
Definition: qgswkbtypes.h:71
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
Definition: qgsabstractgeometry.h:1037
qgslinestring.h
QgsGeometryUtils::sweepAngle
static double sweepAngle(double centerX, double centerY, double x1, double y1, double x2, double y2, double x3, double y3)
Calculates angle of a circular string part defined by pt1, pt2, pt3.
Definition: qgsgeometryutils.cpp:767
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:561
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:945
QgsCircularString::isEmpty
bool isEmpty() const override
Returns true if the geometry is empty.
Definition: qgscircularstring.cpp:389
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
QgsGeometryUtils::pointOnLineWithDistance
static QgsPoint pointOnLineWithDistance(const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance)
Returns a point a specified distance toward a second point.
Definition: qgsgeometryutils.cpp:600
qgswkbptr.h
QgsCircularString::dropMValue
bool dropMValue() override
Drops any measure values which exist in the geometry.
Definition: qgscircularstring.cpp:1437
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
QgsAbstractGeometry::parts
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
Definition: qgsabstractgeometry.cpp:266
QgsCoordinateTransform::TransformDirection
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
Definition: qgscoordinatetransform.h:58
QgsCircularString::hasCurvedSegments
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
Definition: qgscircularstring.cpp:1017
QgsCircularString::moveVertex
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
Definition: qgscircularstring.cpp:841
QgsCircularString::yAt
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
Definition: qgscircularstring.cpp:553
QgsAbstractGeometry::wktTypeStr
QString wktTypeStr() const
Returns the WKT type string of the geometry.
Definition: qgsabstractgeometry.cpp:147
QgsPoint::z
double z
Definition: qgspoint.h:60
QgsCircularString::equals
bool equals(const QgsCurve &other) const override
Checks whether this curve exactly equals another curve.
Definition: qgscircularstring.cpp:78
QgsCircularString::numPoints
int numPoints() const override
Returns the number of points in the curve.
Definition: qgscircularstring.cpp:503
QgsCircularString::segmentLength
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
Definition: qgscircularstring.cpp:1160
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
QgsGeometryUtils::circleCenterRadius
static void circleCenterRadius(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double &radius, double &centerX, double &centerY)
Returns radius and center of the circle through pt1, pt2, pt3.
Definition: qgsgeometryutils.cpp:673
QgsCircularString::pointN
QgsPoint pointN(int i) const
Returns the point at index i within the circular string.
Definition: qgscircularstring.cpp:508
QgsWkbTypes::hasZ
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1042
QgsAbstractGeometry::SegmentationToleranceType
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
Definition: qgsabstractgeometry.h:112
qgspoint.h
QgsAbstractGeometry::mWkbType
QgsWkbTypes::Type mWkbType
Definition: qgsabstractgeometry.h:1005
QgsWkbTypes::dropM
static Type dropM(Type type)
Drops the m dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1212
QgsLineString
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
QgsWkbTypes::addM
static Type addM(Type type)
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1163
QgsGeometryUtils::angleOnCircle
static bool angleOnCircle(double angle, double angle1, double angle2, double angle3)
Returns true if an angle is between angle1 and angle3 on a circle described by angle1,...
Definition: qgsgeometryutils.cpp:749
QgsCircularString::draw
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
Definition: qgscircularstring.cpp:702
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
Definition: qgsrectangle.h:41
QgsCurve::clearCache
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgscurve.cpp:256
QgsCircularString::insertVertex
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
Definition: qgscircularstring.cpp:810
qgsapplication.h
QgsGeometryUtils::ccwAngle
static double ccwAngle(double dy, double dx)
Returns the counter clockwise angle between a line with components dx, dy and the line with dx > 0 an...
Definition: qgsgeometryutils.cpp:659
QgsCircularString::xAt
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
Definition: qgscircularstring.cpp:545
QgsWkbTypes::PointM
@ PointM
Definition: qgswkbtypes.h:98
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:1103
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:59
precision
int precision
Definition: qgswfsgetfeature.cpp:103
QgsCircularString::dimension
int dimension() const override
Returns the inherent dimension of the geometry.
Definition: qgscircularstring.cpp:118
QgsCircularString::deleteVertex
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
Definition: qgscircularstring.cpp:862
QgsWkbTypes::PointZM
@ PointZM
Definition: qgswkbtypes.h:111
QgsGeometryUtils::interpolatePointOnArc
static QgsPoint interpolatePointOnArc(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double distance)
Interpolates a point on an arc defined by three points, pt1, pt2 and pt3.
Definition: qgsgeometryutils.cpp:634
QgsCircularString
Circular string geometry type.
Definition: qgscircularstring.h:34
QgsAbstractGeometry::AxisOrder
AxisOrder
Axis order for GML generation.
Definition: qgsabstractgeometry.h:128
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:355
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:1167
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
QgsPoint::setX
void setX(double x)
Sets the point's x-coordinate.
Definition: qgspoint.h:287
QgsConstWkbPtr
Definition: qgswkbptr.h:127
QgsCircularString::geometryType
QString geometryType() const override
Returns a unique string representing the geometry type.
Definition: qgscircularstring.cpp:113
QgsCircularString::points
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
Definition: qgscircularstring.cpp:635
QgsGeometryUtils::averageAngle
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the average angle (in radians) between the two linear segments from (x1,...
Definition: qgsgeometryutils.cpp:1506
QgsCircularString::interpolatePoint
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
Definition: qgscircularstring.cpp:1193
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:956
QgsWkbTypes::PointZ
@ PointZ
Definition: qgswkbtypes.h:85
QgsApplication::endian
static endian_t endian()
Returns whether this machine uses big or little endian.
Definition: qgsapplication.cpp:1230
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:61
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:1177
QgsAbstractGeometry::is3D
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
Definition: qgsabstractgeometry.h:202
QgsGeometryUtils::segmentMidPointFromCenter
static QgsPoint segmentMidPointFromCenter(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, bool useShortestArc=true)
Calculates the midpoint on the circle passing through p1 and p2, with the specified center coordinate...
Definition: qgsgeometryutils.cpp:844
QgsCircularString::addZValue
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition: qgscircularstring.cpp:1389
QgsWkbPtr
Definition: qgswkbptr.h:42
QgsCircularString::length
double length() const override
Returns the planar, 2-dimensional length of the geometry.
Definition: qgscircularstring.cpp:395
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:279
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:439
QgsGeometryUtils::circleAngleBetween
static bool circleAngleBetween(double angle, double angle1, double angle2, bool clockwise)
Returns true if, in a circle, angle is between angle1 and angle2.
Definition: qgsgeometryutils.cpp:723
qgsgeometryutils.h
QgsCircularString::endPoint
QgsPoint endPoint() const override
Returns the end point of the curve.
Definition: qgscircularstring.cpp:415
QgsWkbTypes::addZ
static Type addZ(Type type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1138
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:1230
QgsGeometryUtils::circleTangentDirection
static double circleTangentDirection(const QgsPoint &tangentPoint, const QgsPoint &cp1, const QgsPoint &cp2, const QgsPoint &cp3)
Calculates the direction angle of a circle tangent (clockwise from north in radians)
Definition: qgsgeometryutils.cpp:853
QgsWkbTypes::hasM
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1092
QgsWkbTypes::CircularString
@ CircularString
Definition: qgswkbtypes.h:79
QgsCircularString::swapXy
void swapXy() override
Swaps the x and y coordinates from the geometry.
Definition: qgscircularstring.cpp:1449
QgsCircularString::fromWkb
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
Definition: qgscircularstring.cpp:268
QgsPointSequence
QVector< QgsPoint > QgsPointSequence
Definition: qgsabstractgeometry.h:44
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:805
QgsGeometryUtils::pointsToWKT
static QString pointsToWKT(const QgsPointSequence &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
Definition: qgsgeometryutils.cpp:1184
QgsVertexId
Utility class for identifying a unique vertex within a geometry.
Definition: qgsabstractgeometry.h:1033
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
QgsGeometryUtils::leftOfLine
static int leftOfLine(const double x, const double y, const double x1, const double y1, const double x2, const double y2)
Returns a value < 0 if the point (x, y) is left of the line from (x1, y1) -> (x2, y2).
Definition: qgsgeometryutils.cpp:589
QgsVertexId::ring
int ring
Definition: qgsabstractgeometry.h:1081
QgsAbstractGeometry::isMeasure
bool isMeasure() const
Returns true if the geometry contains m values.
Definition: qgsabstractgeometry.h:211
QgsCircularString::addMValue
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition: qgscircularstring.cpp:1407
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:906
QgsVertexId::VertexType
VertexType
Definition: qgsabstractgeometry.h:1035
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:424
QgsCircularString::curveSubstring
QgsCircularString * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
Definition: qgscircularstring.cpp:1262
QgsCircularString::asWkb
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Definition: qgscircularstring.cpp:324
QgsWkbTypes::dropZ
static Type dropZ(Type type)
Drops the z dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1194
QgsConstWkbPtr::readHeader
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
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:1299
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:452
QgsVertexId::CurveVertex
@ CurveVertex
Definition: qgsabstractgeometry.h:1038
QgsCoordinateTransform
Definition: qgscoordinatetransform.h:52
QgsGeometryUtils::circleLength
static double circleLength(double x1, double y1, double x2, double y2, double x3, double y3)
Length of a circular string segment defined by pt1, pt2, pt3.
Definition: qgsgeometryutils.cpp:755
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:645
QgsPoint::x
double x
Definition: qgspoint.h:58
QgsCircularString::QgsCircularString
QgsCircularString()
Constructs an empty circular string.
Definition: qgscircularstring.cpp:34
QgsWkbTypes::flatType
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:701
QgsCircularString::asJsonObject
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
Definition: qgscircularstring.cpp:382
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:363
QgsCircularString::dropZValue
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Definition: qgscircularstring.cpp:1425