QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 #include "qgsgeometrytransformer.h"
28 #include "qgsfeedback.h"
29 
30 #include <QJsonObject>
31 #include <QPainter>
32 #include <QPainterPath>
33 #include <memory>
34 #include <nlohmann/json.hpp>
35 
37 {
39 }
40 
42 {
43  //get wkb type from first point
44  bool hasZ = p1.is3D();
45  bool hasM = p1.isMeasure();
47 
48  mX.resize( 3 );
49  mX[ 0 ] = p1.x();
50  mX[ 1 ] = p2.x();
51  mX[ 2 ] = p3.x();
52  mY.resize( 3 );
53  mY[ 0 ] = p1.y();
54  mY[ 1 ] = p2.y();
55  mY[ 2 ] = p3.y();
56  if ( hasZ )
57  {
59  mZ.resize( 3 );
60  mZ[ 0 ] = p1.z();
61  mZ[ 1 ] = p2.z();
62  mZ[ 2 ] = p3.z();
63  }
64  if ( hasM )
65  {
67  mM.resize( 3 );
68  mM[ 0 ] = p1.m();
69  mM[ 1 ] = p2.m();
70  mM[ 2 ] = p3.m();
71  }
72 }
73 
74 QgsCircularString::QgsCircularString( const QVector<double> &x, const QVector<double> &y, const QVector<double> &z, const QVector<double> &m )
75 {
77  int pointCount = std::min( x.size(), y.size() );
78  if ( x.size() == pointCount )
79  {
80  mX = x;
81  }
82  else
83  {
84  mX = x.mid( 0, pointCount );
85  }
86  if ( y.size() == pointCount )
87  {
88  mY = y;
89  }
90  else
91  {
92  mY = y.mid( 0, pointCount );
93  }
94  if ( !z.isEmpty() && z.count() >= pointCount )
95  {
97  if ( z.size() == pointCount )
98  {
99  mZ = z;
100  }
101  else
102  {
103  mZ = z.mid( 0, pointCount );
104  }
105  }
106  if ( !m.isEmpty() && m.count() >= pointCount )
107  {
109  if ( m.size() == pointCount )
110  {
111  mM = m;
112  }
113  else
114  {
115  mM = m.mid( 0, pointCount );
116  }
117  }
118 }
119 
120 QgsCircularString QgsCircularString::fromTwoPointsAndCenter( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, const bool useShortestArc )
121 {
122  const QgsPoint midPoint = QgsGeometryUtils::segmentMidPointFromCenter( p1, p2, center, useShortestArc );
123  return QgsCircularString( p1, midPoint, p2 );
124 }
125 
126 bool QgsCircularString::equals( const QgsCurve &other ) const
127 {
128  const QgsCircularString *otherLine = dynamic_cast< const QgsCircularString * >( &other );
129  if ( !otherLine )
130  return false;
131 
132  if ( mWkbType != otherLine->mWkbType )
133  return false;
134 
135  if ( mX.count() != otherLine->mX.count() )
136  return false;
137 
138  for ( int i = 0; i < mX.count(); ++i )
139  {
140  if ( !qgsDoubleNear( mX.at( i ), otherLine->mX.at( i ) )
141  || !qgsDoubleNear( mY.at( i ), otherLine->mY.at( i ) ) )
142  return false;
143 
144  if ( is3D() && !qgsDoubleNear( mZ.at( i ), otherLine->mZ.at( i ) ) )
145  return false;
146 
147  if ( isMeasure() && !qgsDoubleNear( mM.at( i ), otherLine->mM.at( i ) ) )
148  return false;
149  }
150 
151  return true;
152 }
153 
155 {
156  auto result = std::make_unique< QgsCircularString >();
157  result->mWkbType = mWkbType;
158  return result.release();
159 }
160 
162 {
163  const QgsCircularString *otherLine = qgsgeometry_cast<const QgsCircularString *>( other );
164  if ( !otherLine )
165  return -1;
166 
167  const int size = mX.size();
168  const int otherSize = otherLine->mX.size();
169  if ( size > otherSize )
170  {
171  return 1;
172  }
173  else if ( size < otherSize )
174  {
175  return -1;
176  }
177 
178  if ( is3D() && !otherLine->is3D() )
179  return 1;
180  else if ( !is3D() && otherLine->is3D() )
181  return -1;
182  const bool considerZ = is3D();
183 
184  if ( isMeasure() && !otherLine->isMeasure() )
185  return 1;
186  else if ( !isMeasure() && otherLine->isMeasure() )
187  return -1;
188  const bool considerM = isMeasure();
189 
190  for ( int i = 0; i < size; i++ )
191  {
192  const double x = mX[i];
193  const double otherX = otherLine->mX[i];
194  if ( x < otherX )
195  {
196  return -1;
197  }
198  else if ( x > otherX )
199  {
200  return 1;
201  }
202 
203  const double y = mY[i];
204  const double otherY = otherLine->mY[i];
205  if ( y < otherY )
206  {
207  return -1;
208  }
209  else if ( y > otherY )
210  {
211  return 1;
212  }
213 
214  if ( considerZ )
215  {
216  const double z = mZ[i];
217  const double otherZ = otherLine->mZ[i];
218 
219  if ( z < otherZ )
220  {
221  return -1;
222  }
223  else if ( z > otherZ )
224  {
225  return 1;
226  }
227  }
228 
229  if ( considerM )
230  {
231  const double m = mM[i];
232  const double otherM = otherLine->mM[i];
233 
234  if ( m < otherM )
235  {
236  return -1;
237  }
238  else if ( m > otherM )
239  {
240  return 1;
241  }
242  }
243  }
244  return 0;
245 }
246 
248 {
249  return QStringLiteral( "CircularString" );
250 }
251 
253 {
254  return 1;
255 }
256 
258 {
259  return new QgsCircularString( *this );
260 }
261 
263 {
265  mX.clear();
266  mY.clear();
267  mZ.clear();
268  mM.clear();
269  clearCache();
270 }
271 
273 {
274  QgsRectangle bbox;
275  int nPoints = numPoints();
276  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
277  {
278  if ( i == 0 )
279  {
280  bbox = segmentBoundingBox( QgsPoint( mX[i], mY[i] ), QgsPoint( mX[i + 1], mY[i + 1] ), QgsPoint( mX[i + 2], mY[i + 2] ) );
281  }
282  else
283  {
284  QgsRectangle segmentBox = segmentBoundingBox( QgsPoint( mX[i], mY[i] ), QgsPoint( mX[i + 1], mY[i + 1] ), QgsPoint( mX[i + 2], mY[i + 2] ) );
285  bbox.combineExtentWith( segmentBox );
286  }
287  }
288 
289  if ( nPoints > 0 && nPoints % 2 == 0 )
290  {
291  if ( nPoints == 2 )
292  {
293  bbox.combineExtentWith( mX[ 0 ], mY[ 0 ] );
294  }
295  bbox.combineExtentWith( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
296  }
297  return bbox;
298 }
299 
300 void QgsCircularString::scroll( int index )
301 {
302  const int size = mX.size();
303  if ( index < 1 || index >= size - 1 )
304  return;
305 
306  const bool useZ = is3D();
307  const bool useM = isMeasure();
308 
309  QVector<double> newX( size );
310  QVector<double> newY( size );
311  QVector<double> newZ( useZ ? size : 0 );
312  QVector<double> newM( useM ? size : 0 );
313  auto it = std::copy( mX.constBegin() + index, mX.constEnd() - 1, newX.begin() );
314  it = std::copy( mX.constBegin(), mX.constBegin() + index, it );
315  *it = *newX.constBegin();
316  mX = std::move( newX );
317 
318  it = std::copy( mY.constBegin() + index, mY.constEnd() - 1, newY.begin() );
319  it = std::copy( mY.constBegin(), mY.constBegin() + index, it );
320  *it = *newY.constBegin();
321  mY = std::move( newY );
322  if ( useZ )
323  {
324  it = std::copy( mZ.constBegin() + index, mZ.constEnd() - 1, newZ.begin() );
325  it = std::copy( mZ.constBegin(), mZ.constBegin() + index, it );
326  *it = *newZ.constBegin();
327  mZ = std::move( newZ );
328  }
329  if ( useM )
330  {
331  it = std::copy( mM.constBegin() + index, mM.constEnd() - 1, newM.begin() );
332  it = std::copy( mM.constBegin(), mM.constBegin() + index, it );
333  *it = *newM.constBegin();
334  mM = std::move( newM );
335  }
336 }
337 
338 QgsRectangle QgsCircularString::segmentBoundingBox( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3 )
339 {
340  double centerX, centerY, radius;
341  QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
342 
343  double p1Angle = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
344  double p2Angle = QgsGeometryUtils::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
345  double p3Angle = QgsGeometryUtils::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
346 
347  //start point, end point and compass points in between can be on bounding box
348  QgsRectangle bbox( pt1.x(), pt1.y(), pt1.x(), pt1.y() );
349  bbox.combineExtentWith( pt3.x(), pt3.y() );
350 
351  QgsPointSequence compassPoints = compassPointsOnSegment( p1Angle, p2Angle, p3Angle, centerX, centerY, radius );
352  QgsPointSequence::const_iterator cpIt = compassPoints.constBegin();
353  for ( ; cpIt != compassPoints.constEnd(); ++cpIt )
354  {
355  bbox.combineExtentWith( cpIt->x(), cpIt->y() );
356  }
357  return bbox;
358 }
359 
360 QgsPointSequence QgsCircularString::compassPointsOnSegment( double p1Angle, double p2Angle, double p3Angle, double centerX, double centerY, double radius )
361 {
362  QgsPointSequence pointList;
363 
364  QgsPoint nPoint( centerX, centerY + radius );
365  QgsPoint ePoint( centerX + radius, centerY );
366  QgsPoint sPoint( centerX, centerY - radius );
367  QgsPoint wPoint( centerX - radius, centerY );
368 
369  if ( p3Angle >= p1Angle )
370  {
371  if ( p2Angle > p1Angle && p2Angle < p3Angle )
372  {
373  if ( p1Angle <= 90 && p3Angle >= 90 )
374  {
375  pointList.append( nPoint );
376  }
377  if ( p1Angle <= 180 && p3Angle >= 180 )
378  {
379  pointList.append( wPoint );
380  }
381  if ( p1Angle <= 270 && p3Angle >= 270 )
382  {
383  pointList.append( sPoint );
384  }
385  }
386  else
387  {
388  pointList.append( ePoint );
389  if ( p1Angle >= 90 || p3Angle <= 90 )
390  {
391  pointList.append( nPoint );
392  }
393  if ( p1Angle >= 180 || p3Angle <= 180 )
394  {
395  pointList.append( wPoint );
396  }
397  if ( p1Angle >= 270 || p3Angle <= 270 )
398  {
399  pointList.append( sPoint );
400  }
401  }
402  }
403  else
404  {
405  if ( p2Angle < p1Angle && p2Angle > p3Angle )
406  {
407  if ( p1Angle >= 270 && p3Angle <= 270 )
408  {
409  pointList.append( sPoint );
410  }
411  if ( p1Angle >= 180 && p3Angle <= 180 )
412  {
413  pointList.append( wPoint );
414  }
415  if ( p1Angle >= 90 && p3Angle <= 90 )
416  {
417  pointList.append( nPoint );
418  }
419  }
420  else
421  {
422  pointList.append( ePoint );
423  if ( p1Angle <= 270 || p3Angle >= 270 )
424  {
425  pointList.append( sPoint );
426  }
427  if ( p1Angle <= 180 || p3Angle >= 180 )
428  {
429  pointList.append( wPoint );
430  }
431  if ( p1Angle <= 90 || p3Angle >= 90 )
432  {
433  pointList.append( nPoint );
434  }
435  }
436  }
437  return pointList;
438 }
439 
441 {
442  if ( !wkbPtr )
443  return false;
444 
445  QgsWkbTypes::Type type = wkbPtr.readHeader();
447  {
448  return false;
449  }
450  clearCache();
451  mWkbType = type;
452 
453  //type
454  bool hasZ = is3D();
455  bool hasM = isMeasure();
456  int nVertices = 0;
457  wkbPtr >> nVertices;
458  mX.resize( nVertices );
459  mY.resize( nVertices );
460  hasZ ? mZ.resize( nVertices ) : mZ.clear();
461  hasM ? mM.resize( nVertices ) : mM.clear();
462  for ( int i = 0; i < nVertices; ++i )
463  {
464  wkbPtr >> mX[i];
465  wkbPtr >> mY[i];
466  if ( hasZ )
467  {
468  wkbPtr >> mZ[i];
469  }
470  if ( hasM )
471  {
472  wkbPtr >> mM[i];
473  }
474  }
475 
476  return true;
477 }
478 
479 bool QgsCircularString::fromWkt( const QString &wkt )
480 {
481  clear();
482 
483  QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
484 
486  return false;
487  mWkbType = parts.first;
488 
489  parts.second = parts.second.remove( '(' ).remove( ')' );
490  QString secondWithoutParentheses = parts.second;
491  secondWithoutParentheses = secondWithoutParentheses.simplified().remove( ' ' );
492  if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
493  secondWithoutParentheses.isEmpty() )
494  return true;
495 
497  if ( points.isEmpty() )
498  return false;
499 
500  setPoints( points );
501  return true;
502 }
503 
504 int QgsCircularString::wkbSize( QgsAbstractGeometry::WkbFlags ) const
505 {
506  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
507  binarySize += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
508  return binarySize;
509 }
510 
511 QByteArray QgsCircularString::asWkb( WkbFlags flags ) const
512 {
513  QByteArray wkbArray;
514  wkbArray.resize( QgsCircularString::wkbSize( flags ) );
515  QgsWkbPtr wkb( wkbArray );
516  wkb << static_cast<char>( QgsApplication::endian() );
517  wkb << static_cast<quint32>( wkbType() );
518  QgsPointSequence pts;
519  points( pts );
520  QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure() );
521  return wkbArray;
522 }
523 
525 {
526  QString wkt = wktTypeStr() + ' ';
527 
528  if ( isEmpty() )
529  wkt += QLatin1String( "EMPTY" );
530  else
531  {
532  QgsPointSequence pts;
533  points( pts );
535  }
536  return wkt;
537 }
538 
539 QDomElement QgsCircularString::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
540 {
541  // GML2 does not support curves
542  std::unique_ptr< QgsLineString > line( curveToLine() );
543  QDomElement gml = line->asGml2( doc, precision, ns, axisOrder );
544  return gml;
545 }
546 
547 QDomElement QgsCircularString::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
548 {
549  QgsPointSequence pts;
550  points( pts );
551 
552  QDomElement elemCurve = doc.createElementNS( ns, QStringLiteral( "Curve" ) );
553 
554  if ( isEmpty() )
555  return elemCurve;
556 
557  QDomElement elemSegments = doc.createElementNS( ns, QStringLiteral( "segments" ) );
558  QDomElement elemArcString = doc.createElementNS( ns, QStringLiteral( "ArcString" ) );
559  elemArcString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D(), axisOrder ) );
560  elemSegments.appendChild( elemArcString );
561  elemCurve.appendChild( elemSegments );
562  return elemCurve;
563 }
564 
565 
567 {
568  // GeoJSON does not support curves
569  std::unique_ptr< QgsLineString > line( curveToLine() );
570  return line->asJsonObject( precision );
571 }
572 
574 {
575  return mX.isEmpty();
576 }
577 
578 bool QgsCircularString::isValid( QString &error, Qgis::GeometryValidityFlags flags ) const
579 {
580  if ( !isEmpty() && ( numPoints() < 3 ) )
581  {
582  error = QObject::tr( "CircularString has less than 3 points and is not empty." );
583  return false;
584  }
585  return QgsCurve::isValid( error, flags );
586 }
587 
588 //curve interface
590 {
591  int nPoints = numPoints();
592  double length = 0;
593  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
594  {
595  length += QgsGeometryUtils::circleLength( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2] );
596  }
597  return length;
598 }
599 
601 {
602  if ( numPoints() < 1 )
603  {
604  return QgsPoint();
605  }
606  return pointN( 0 );
607 }
608 
610 {
611  if ( numPoints() < 1 )
612  {
613  return QgsPoint();
614  }
615  return pointN( numPoints() - 1 );
616 }
617 
619 {
620  QgsLineString *line = new QgsLineString();
622  int nPoints = numPoints();
623 
624  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
625  {
626  QgsGeometryUtils::segmentizeArc( pointN( i ), pointN( i + 1 ), pointN( i + 2 ), points, tolerance, toleranceType, is3D(), isMeasure() );
627  }
628 
629  line->setPoints( points );
630  return line;
631 }
632 
633 QgsCircularString *QgsCircularString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
634 {
635  // prepare result
636  std::unique_ptr<QgsCircularString> result { createEmptyWithSameType() };
637 
638  bool res = snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
639  result->mX, result->mY, result->mZ, result->mM );
640  if ( res )
641  return result.release();
642  else
643  return nullptr;
644 }
645 
646 bool QgsCircularString::removeDuplicateNodes( double epsilon, bool useZValues )
647 {
648  if ( mX.count() <= 3 )
649  return false; // don't create degenerate lines
650  bool result = false;
651  double prevX = mX.at( 0 );
652  double prevY = mY.at( 0 );
653  bool hasZ = is3D();
654  bool useZ = hasZ && useZValues;
655  double prevZ = useZ ? mZ.at( 0 ) : 0;
656  int i = 1;
657  int remaining = mX.count();
658  // we have to consider points in pairs, since a segment can validly have the same start and
659  // end if it has a different curve point
660  while ( i + 1 < remaining )
661  {
662  double currentCurveX = mX.at( i );
663  double currentCurveY = mY.at( i );
664  double currentX = mX.at( i + 1 );
665  double currentY = mY.at( i + 1 );
666  double currentZ = useZ ? mZ.at( i + 1 ) : 0;
667  if ( qgsDoubleNear( currentCurveX, prevX, epsilon ) &&
668  qgsDoubleNear( currentCurveY, prevY, epsilon ) &&
669  qgsDoubleNear( currentX, prevX, epsilon ) &&
670  qgsDoubleNear( currentY, prevY, epsilon ) &&
671  ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
672  {
673  result = true;
674  // remove point
675  mX.removeAt( i );
676  mX.removeAt( i );
677  mY.removeAt( i );
678  mY.removeAt( i );
679  if ( hasZ )
680  {
681  mZ.removeAt( i );
682  mZ.removeAt( i );
683  }
684  remaining -= 2;
685  }
686  else
687  {
688  prevX = currentX;
689  prevY = currentY;
690  prevZ = currentZ;
691  i += 2;
692  }
693  }
694  return result;
695 }
696 
698 {
699  return std::min( mX.size(), mY.size() );
700 }
701 
702 int QgsCircularString::indexOf( const QgsPoint &point ) const
703 {
704  const int size = mX.size();
705  if ( size == 0 )
706  return -1;
707 
708  const double *x = mX.constData();
709  const double *y = mY.constData();
710  const bool useZ = is3D();
711  const bool useM = isMeasure();
712  const double *z = useZ ? mZ.constData() : nullptr;
713  const double *m = useM ? mM.constData() : nullptr;
714 
715  for ( int i = 0; i < size; i += 2 )
716  {
717  if ( qgsDoubleNear( *x, point.x() )
718  && qgsDoubleNear( *y, point.y() )
719  && ( !useZ || qgsDoubleNear( *z, point.z() ) )
720  && ( !useM || qgsDoubleNear( *m, point.m() ) ) )
721  return i;
722 
723  // we skip over curve points!
724  x++;
725  x++;
726  y++;
727  y++;
728  if ( useZ )
729  {
730  z++;
731  z++;
732  }
733  if ( useM )
734  {
735  m++;
736  m++;
737  }
738  }
739  return -1;
740 }
741 
743 {
744  if ( i < 0 || std::min( mX.size(), mY.size() ) <= i )
745  {
746  return QgsPoint();
747  }
748 
749  double x = mX.at( i );
750  double y = mY.at( i );
751  double z = 0;
752  double m = 0;
753 
754  if ( is3D() )
755  {
756  z = mZ.at( i );
757  }
758  if ( isMeasure() )
759  {
760  m = mM.at( i );
761  }
762 
764  if ( is3D() && isMeasure() )
765  {
767  }
768  else if ( is3D() )
769  {
771  }
772  else if ( isMeasure() )
773  {
775  }
776  return QgsPoint( t, x, y, z, m );
777 }
778 
779 double QgsCircularString::xAt( int index ) const
780 {
781  if ( index >= 0 && index < mX.size() )
782  return mX.at( index );
783  else
784  return 0.0;
785 }
786 
787 double QgsCircularString::yAt( int index ) const
788 {
789  if ( index >= 0 && index < mY.size() )
790  return mY.at( index );
791  else
792  return 0.0;
793 }
794 
796 {
797  if ( !transformer )
798  return false;
799 
800  bool hasZ = is3D();
801  bool hasM = isMeasure();
802  int size = mX.size();
803 
804  double *srcX = mX.data();
805  double *srcY = mY.data();
806  double *srcM = hasM ? mM.data() : nullptr;
807  double *srcZ = hasZ ? mZ.data() : nullptr;
808 
809  bool res = true;
810  for ( int i = 0; i < size; ++i )
811  {
812  double x = *srcX;
813  double y = *srcY;
814  double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
815  double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
816  if ( !transformer->transformPoint( x, y, z, m ) )
817  {
818  res = false;
819  break;
820  }
821 
822  *srcX++ = x;
823  *srcY++ = y;
824  if ( hasM )
825  *srcM++ = m;
826  if ( hasZ )
827  *srcZ++ = z;
828 
829  if ( feedback && feedback->isCanceled() )
830  {
831  res = false;
832  break;
833  }
834  }
835  clearCache();
836  return res;
837 }
838 
839 void QgsCircularString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
840 {
841  bool hasZ = is3D();
842  bool hasM = isMeasure();
843  int size = mX.size();
844 
845  double *srcX = mX.data(); // clazy:exclude=detaching-member
846  double *srcY = mY.data(); // clazy:exclude=detaching-member
847  double *srcM = hasM ? mM.data() : nullptr; // clazy:exclude=detaching-member
848  double *srcZ = hasZ ? mZ.data() : nullptr; // clazy:exclude=detaching-member
849 
850  double *destX = srcX;
851  double *destY = srcY;
852  double *destM = srcM;
853  double *destZ = srcZ;
854 
855  int filteredPoints = 0;
856  for ( int i = 0; i < size; ++i )
857  {
858  double x = *srcX++;
859  double y = *srcY++;
860  double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
861  double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
862 
863  if ( filter( QgsPoint( x, y, z, m ) ) )
864  {
865  filteredPoints++;
866  *destX++ = x;
867  *destY++ = y;
868  if ( hasM )
869  *destM++ = m;
870  if ( hasZ )
871  *destZ++ = z;
872  }
873  }
874 
875  mX.resize( filteredPoints );
876  mY.resize( filteredPoints );
877  if ( hasZ )
878  mZ.resize( filteredPoints );
879  if ( hasM )
880  mM.resize( filteredPoints );
881 
882  clearCache();
883 }
884 
885 void QgsCircularString::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
886 {
887  bool hasZ = is3D();
888  bool hasM = isMeasure();
889  int size = mX.size();
890 
891  double *srcX = mX.data();
892  double *srcY = mY.data();
893  double *srcM = hasM ? mM.data() : nullptr;
894  double *srcZ = hasZ ? mZ.data() : nullptr;
895 
896  for ( int i = 0; i < size; ++i )
897  {
898  double x = *srcX;
899  double y = *srcY;
900  double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
901  double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
902  QgsPoint res = transform( QgsPoint( x, y, z, m ) );
903  *srcX++ = res.x();
904  *srcY++ = res.y();
905  if ( hasM )
906  *srcM++ = res.m();
907  if ( hasZ )
908  *srcZ++ = res.z();
909  }
910  clearCache();
911 }
912 
913 std::tuple<std::unique_ptr<QgsCurve>, std::unique_ptr<QgsCurve> > QgsCircularString::splitCurveAtVertex( int index ) const
914 {
915  const bool useZ = is3D();
916  const bool useM = isMeasure();
917 
918  const int size = mX.size();
919  if ( size == 0 )
920  return std::make_tuple( std::make_unique< QgsCircularString >(), std::make_unique< QgsCircularString >() );
921 
922  index = std::clamp( index, 0, size - 1 );
923 
924  const int part1Size = index + 1;
925  QVector< double > x1( part1Size );
926  QVector< double > y1( part1Size );
927  QVector< double > z1( useZ ? part1Size : 0 );
928  QVector< double > m1( useM ? part1Size : 0 );
929 
930  const double *sourceX = mX.constData();
931  const double *sourceY = mY.constData();
932  const double *sourceZ = useZ ? mZ.constData() : nullptr;
933  const double *sourceM = useM ? mM.constData() : nullptr;
934 
935  double *destX = x1.data();
936  double *destY = y1.data();
937  double *destZ = useZ ? z1.data() : nullptr;
938  double *destM = useM ? m1.data() : nullptr;
939 
940  std::copy( sourceX, sourceX + part1Size, destX );
941  std::copy( sourceY, sourceY + part1Size, destY );
942  if ( useZ )
943  std::copy( sourceZ, sourceZ + part1Size, destZ );
944  if ( useM )
945  std::copy( sourceM, sourceM + part1Size, destM );
946 
947  const int part2Size = size - index;
948  if ( part2Size < 2 )
949  return std::make_tuple( std::make_unique< QgsCircularString >( x1, y1, z1, m1 ), std::make_unique< QgsCircularString >() );
950 
951  QVector< double > x2( part2Size );
952  QVector< double > y2( part2Size );
953  QVector< double > z2( useZ ? part2Size : 0 );
954  QVector< double > m2( useM ? part2Size : 0 );
955  destX = x2.data();
956  destY = y2.data();
957  destZ = useZ ? z2.data() : nullptr;
958  destM = useM ? m2.data() : nullptr;
959  std::copy( sourceX + index, sourceX + size, destX );
960  std::copy( sourceY + index, sourceY + size, destY );
961  if ( useZ )
962  std::copy( sourceZ + index, sourceZ + size, destZ );
963  if ( useM )
964  std::copy( sourceM + index, sourceM + size, destM );
965 
966  if ( part1Size < 2 )
967  return std::make_tuple( std::make_unique< QgsCircularString >(), std::make_unique< QgsCircularString >( x2, y2, z2, m2 ) );
968  else
969  return std::make_tuple( std::make_unique< QgsCircularString >( x1, y1, z1, m1 ), std::make_unique< QgsCircularString >( x2, y2, z2, m2 ) );
970 }
971 
973 {
974  pts.clear();
975  int nPts = numPoints();
976  for ( int i = 0; i < nPts; ++i )
977  {
978  pts.push_back( pointN( i ) );
979  }
980 }
981 
983 {
984  clearCache();
985 
986  if ( points.empty() )
987  {
989  mX.clear();
990  mY.clear();
991  mZ.clear();
992  mM.clear();
993  return;
994  }
995 
996  //get wkb type from first point
997  const QgsPoint &firstPt = points.at( 0 );
998  bool hasZ = firstPt.is3D();
999  bool hasM = firstPt.isMeasure();
1000 
1002 
1003  mX.resize( points.size() );
1004  mY.resize( points.size() );
1005  if ( hasZ )
1006  {
1007  mZ.resize( points.size() );
1008  }
1009  else
1010  {
1011  mZ.clear();
1012  }
1013  if ( hasM )
1014  {
1015  mM.resize( points.size() );
1016  }
1017  else
1018  {
1019  mM.clear();
1020  }
1021 
1022  for ( int i = 0; i < points.size(); ++i )
1023  {
1024  mX[i] = points[i].x();
1025  mY[i] = points[i].y();
1026  if ( hasZ )
1027  {
1028  double z = points.at( i ).z();
1029  mZ[i] = std::isnan( z ) ? 0 : z;
1030  }
1031  if ( hasM )
1032  {
1033  double m = points.at( i ).m();
1034  mM[i] = std::isnan( m ) ? 0 : m;
1035  }
1036  }
1037 }
1038 
1040 {
1041  if ( !line || line->isEmpty() )
1042  {
1043  return;
1044  }
1045 
1046  if ( numPoints() < 1 )
1047  {
1049  }
1050 
1051  // do not store duplicate points
1052  if ( numPoints() > 0 &&
1053  line->numPoints() > 0 &&
1054  qgsDoubleNear( endPoint().x(), line->startPoint().x() ) &&
1055  qgsDoubleNear( endPoint().y(), line->startPoint().y() ) &&
1056  ( !is3D() || !line->is3D() || qgsDoubleNear( endPoint().z(), line->startPoint().z() ) ) &&
1057  ( !isMeasure() || !line->isMeasure() || qgsDoubleNear( endPoint().m(), line->startPoint().m() ) ) )
1058  {
1059  mX.pop_back();
1060  mY.pop_back();
1061 
1062  if ( is3D() && line->is3D() )
1063  {
1064  mZ.pop_back();
1065  }
1066  if ( isMeasure() && line->isMeasure() )
1067  {
1068  mM.pop_back();
1069  }
1070  }
1071 
1072  mX += line->mX;
1073  mY += line->mY;
1074 
1075  if ( is3D() )
1076  {
1077  if ( line->is3D() )
1078  {
1079  mZ += line->mZ;
1080  }
1081  else
1082  {
1083  // if append line does not have z coordinates, fill with NaN to match number of points in final line
1084  mZ.insert( mZ.count(), mX.size() - mZ.size(), std::numeric_limits<double>::quiet_NaN() );
1085  }
1086  }
1087 
1088  if ( isMeasure() )
1089  {
1090  if ( line->isMeasure() )
1091  {
1092  mM += line->mM;
1093  }
1094  else
1095  {
1096  // if append line does not have m values, fill with NaN to match number of points in final line
1097  mM.insert( mM.count(), mX.size() - mM.size(), std::numeric_limits<double>::quiet_NaN() );
1098  }
1099  }
1100 
1101  clearCache(); //set bounding box invalid
1102 }
1103 
1104 void QgsCircularString::draw( QPainter &p ) const
1105 {
1106  QPainterPath path;
1107  addToPainterPath( path );
1108  p.drawPath( path );
1109 }
1110 
1112 {
1113  clearCache();
1114 
1115  double *zArray = mZ.data();
1116 
1117  bool hasZ = is3D();
1118  int nPoints = numPoints();
1119  bool useDummyZ = !hasZ || !transformZ;
1120  if ( useDummyZ )
1121  {
1122  zArray = new double[nPoints];
1123  for ( int i = 0; i < nPoints; ++i )
1124  {
1125  zArray[i] = 0;
1126  }
1127  }
1128  ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
1129  if ( useDummyZ )
1130  {
1131  delete[] zArray;
1132  }
1133 }
1134 
1135 void QgsCircularString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
1136 {
1137  clearCache();
1138 
1139  int nPoints = numPoints();
1140  bool hasZ = is3D();
1141  bool hasM = isMeasure();
1142  for ( int i = 0; i < nPoints; ++i )
1143  {
1144  qreal x, y;
1145  t.map( mX.at( i ), mY.at( i ), &x, &y );
1146  mX[i] = x;
1147  mY[i] = y;
1148  if ( hasZ )
1149  {
1150  mZ[i] = mZ.at( i ) * zScale + zTranslate;
1151  }
1152  if ( hasM )
1153  {
1154  mM[i] = mM.at( i ) * mScale + mTranslate;
1155  }
1156  }
1157 }
1158 
1159 void arcTo( QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3 )
1160 {
1161  double centerX, centerY, radius;
1162  QgsGeometryUtils::circleCenterRadius( QgsPoint( pt1.x(), pt1.y() ), QgsPoint( pt2.x(), pt2.y() ), QgsPoint( pt3.x(), pt3.y() ),
1163  radius, centerX, centerY );
1164 
1165  double p1Angle = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
1166  double sweepAngle = QgsGeometryUtils::sweepAngle( centerX, centerY, pt1.x(), pt1.y(), pt2.x(), pt2.y(), pt3.x(), pt3.y() );
1167 
1168  double diameter = 2 * radius;
1169  path.arcTo( centerX - radius, centerY - radius, diameter, diameter, -p1Angle, -sweepAngle );
1170 }
1171 
1172 void QgsCircularString::addToPainterPath( QPainterPath &path ) const
1173 {
1174  int nPoints = numPoints();
1175  if ( nPoints < 1 )
1176  {
1177  return;
1178  }
1179 
1180  if ( path.isEmpty() || path.currentPosition() != QPointF( mX[0], mY[0] ) )
1181  {
1182  path.moveTo( QPointF( mX[0], mY[0] ) );
1183  }
1184 
1185  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
1186  {
1187  arcTo( path, QPointF( mX[i], mY[i] ), QPointF( mX[i + 1], mY[i + 1] ), QPointF( mX[i + 2], mY[i + 2] ) );
1188  }
1189 
1190  //if number of points is even, connect to last point with straight line (even though the circular string is not valid)
1191  if ( nPoints % 2 == 0 )
1192  {
1193  path.lineTo( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
1194  }
1195 }
1196 
1197 void QgsCircularString::drawAsPolygon( QPainter &p ) const
1198 {
1199  draw( p );
1200 }
1201 
1203 {
1204  if ( position.vertex >= mX.size() || position.vertex < 1 )
1205  {
1206  return false;
1207  }
1208 
1209  mX.insert( position.vertex, vertex.x() );
1210  mY.insert( position.vertex, vertex.y() );
1211  if ( is3D() )
1212  {
1213  mZ.insert( position.vertex, vertex.z() );
1214  }
1215  if ( isMeasure() )
1216  {
1217  mM.insert( position.vertex, vertex.m() );
1218  }
1219 
1220  bool vertexNrEven = ( position.vertex % 2 == 0 );
1221  if ( vertexNrEven )
1222  {
1223  insertVertexBetween( position.vertex - 2, position.vertex - 1, position.vertex );
1224  }
1225  else
1226  {
1227  insertVertexBetween( position.vertex, position.vertex + 1, position.vertex - 1 );
1228  }
1229  clearCache(); //set bounding box invalid
1230  return true;
1231 }
1232 
1233 bool QgsCircularString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
1234 {
1235  if ( position.vertex < 0 || position.vertex >= mX.size() )
1236  {
1237  return false;
1238  }
1239 
1240  mX[position.vertex] = newPos.x();
1241  mY[position.vertex] = newPos.y();
1242  if ( is3D() && newPos.is3D() )
1243  {
1244  mZ[position.vertex] = newPos.z();
1245  }
1246  if ( isMeasure() && newPos.isMeasure() )
1247  {
1248  mM[position.vertex] = newPos.m();
1249  }
1250  clearCache(); //set bounding box invalid
1251  return true;
1252 }
1253 
1255 {
1256  int nVertices = this->numPoints();
1257  if ( nVertices < 4 ) //circular string must have at least 3 vertices
1258  {
1259  clear();
1260  return true;
1261  }
1262  if ( position.vertex < 0 || position.vertex > ( nVertices - 1 ) )
1263  {
1264  return false;
1265  }
1266 
1267  if ( position.vertex < ( nVertices - 2 ) )
1268  {
1269  //remove this and the following vertex
1270  deleteVertex( position.vertex + 1 );
1271  deleteVertex( position.vertex );
1272  }
1273  else //remove this and the preceding vertex
1274  {
1275  deleteVertex( position.vertex );
1276  deleteVertex( position.vertex - 1 );
1277  }
1278 
1279  clearCache(); //set bounding box invalid
1280  return true;
1281 }
1282 
1283 void QgsCircularString::deleteVertex( int i )
1284 {
1285  mX.remove( i );
1286  mY.remove( i );
1287  if ( is3D() )
1288  {
1289  mZ.remove( i );
1290  }
1291  if ( isMeasure() )
1292  {
1293  mM.remove( i );
1294  }
1295  clearCache();
1296 }
1297 
1298 double QgsCircularString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1299 {
1300  double minDist = std::numeric_limits<double>::max();
1301  QgsPoint minDistSegmentPoint;
1302  QgsVertexId minDistVertexAfter;
1303  int minDistLeftOf = 0;
1304 
1305  double currentDist = 0.0;
1306 
1307  int nPoints = numPoints();
1308  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
1309  {
1310  currentDist = closestPointOnArc( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2], pt, segmentPt, vertexAfter, leftOf, epsilon );
1311  if ( currentDist < minDist )
1312  {
1313  minDist = currentDist;
1314  minDistSegmentPoint = segmentPt;
1315  minDistVertexAfter.vertex = vertexAfter.vertex + i;
1316  if ( leftOf )
1317  {
1318  minDistLeftOf = *leftOf;
1319  }
1320  }
1321  }
1322 
1323  if ( minDist == std::numeric_limits<double>::max() )
1324  return -1; // error: no segments
1325 
1326  segmentPt = minDistSegmentPoint;
1327  vertexAfter = minDistVertexAfter;
1328  vertexAfter.part = 0;
1329  vertexAfter.ring = 0;
1330  if ( leftOf )
1331  {
1332  *leftOf = qgsDoubleNear( minDist, 0.0 ) ? 0 : minDistLeftOf;
1333  }
1334  return minDist;
1335 }
1336 
1337 bool QgsCircularString::pointAt( int node, QgsPoint &point, Qgis::VertexType &type ) const
1338 {
1339  if ( node < 0 || node >= numPoints() )
1340  {
1341  return false;
1342  }
1343  point = pointN( node );
1344  type = ( node % 2 == 0 ) ? Qgis::VertexType::Segment : Qgis::VertexType::Curve;
1345  return true;
1346 }
1347 
1348 void QgsCircularString::sumUpArea( double &sum ) const
1349 {
1350  int maxIndex = numPoints() - 2;
1351 
1352  for ( int i = 0; i < maxIndex; i += 2 )
1353  {
1354  QgsPoint p1( mX[i], mY[i] );
1355  QgsPoint p2( mX[i + 1], mY[i + 1] );
1356  QgsPoint p3( mX[i + 2], mY[i + 2] );
1357 
1358  //segment is a full circle, p2 is the center point
1359  if ( p1 == p3 )
1360  {
1361  double r2 = QgsGeometryUtils::sqrDistance2D( p1, p2 ) / 4.0;
1362  sum += M_PI * r2;
1363  continue;
1364  }
1365 
1366  sum += 0.5 * ( mX[i] * mY[i + 2] - mY[i] * mX[i + 2] );
1367 
1368  //calculate area between circle and chord, then sum / subtract from total area
1369  double midPointX = ( p1.x() + p3.x() ) / 2.0;
1370  double midPointY = ( p1.y() + p3.y() ) / 2.0;
1371 
1372  double radius, centerX, centerY;
1373  QgsGeometryUtils::circleCenterRadius( p1, p2, p3, radius, centerX, centerY );
1374 
1375  double d = std::sqrt( QgsGeometryUtils::sqrDistance2D( QgsPoint( centerX, centerY ), QgsPoint( midPointX, midPointY ) ) );
1376  double r2 = radius * radius;
1377 
1378  if ( d > radius )
1379  {
1380  //d cannot be greater than radius, something must be wrong...
1381  continue;
1382  }
1383 
1384  bool circlePointLeftOfLine = QgsGeometryUtils::leftOfLine( p2.x(), p2.y(), p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
1385  bool centerPointLeftOfLine = QgsGeometryUtils::leftOfLine( centerX, centerY, p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
1386 
1387  double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - M_1_PI * std::asin( d / radius );
1388  double circleChordArea = 0;
1389  if ( circlePointLeftOfLine == centerPointLeftOfLine )
1390  {
1391  circleChordArea = M_PI * r2 * ( 1 - cov );
1392  }
1393  else
1394  {
1395  circleChordArea = M_PI * r2 * cov;
1396  }
1397 
1398  if ( !circlePointLeftOfLine )
1399  {
1400  sum += circleChordArea;
1401  }
1402  else
1403  {
1404  sum -= circleChordArea;
1405  }
1406  }
1407 }
1408 
1410 {
1411  return true;
1412 }
1413 
1414 double QgsCircularString::closestPointOnArc( double x1, double y1, double x2, double y2, double x3, double y3,
1415  const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon )
1416 {
1417  double radius, centerX, centerY;
1418  QgsPoint pt1( x1, y1 );
1419  QgsPoint pt2( x2, y2 );
1420  QgsPoint pt3( x3, y3 );
1421 
1422  QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
1423  double angle = QgsGeometryUtils::ccwAngle( pt.y() - centerY, pt.x() - centerX );
1424  double angle1 = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
1425  double angle2 = QgsGeometryUtils::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
1426  double angle3 = QgsGeometryUtils::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
1427 
1428  bool clockwise = QgsGeometryUtils::circleClockwise( angle1, angle2, angle3 );
1429 
1430  if ( QgsGeometryUtils::angleOnCircle( angle, angle1, angle2, angle3 ) )
1431  {
1432  //get point on line center -> pt with distance radius
1433  segmentPt = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), pt, radius );
1434 
1435  //vertexAfter
1436  vertexAfter.vertex = QgsGeometryUtils::circleAngleBetween( angle, angle1, angle2, clockwise ) ? 1 : 2;
1437  }
1438  else
1439  {
1440  double distPtPt1 = QgsGeometryUtils::sqrDistance2D( pt, pt1 );
1441  double distPtPt3 = QgsGeometryUtils::sqrDistance2D( pt, pt3 );
1442  segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
1443  vertexAfter.vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
1444  }
1445 
1446  double sqrDistance = QgsGeometryUtils::sqrDistance2D( segmentPt, pt );
1447  //prevent rounding errors if the point is directly on the segment
1448  if ( qgsDoubleNear( sqrDistance, 0.0, epsilon ) )
1449  {
1450  segmentPt.setX( pt.x() );
1451  segmentPt.setY( pt.y() );
1452  sqrDistance = 0.0;
1453  }
1454 
1455  if ( leftOf )
1456  {
1457  double sqrDistancePointToCenter = ( pt.x() - centerX ) * ( pt.x() - centerX ) + ( pt.y() - centerY ) * ( pt.y() - centerY );
1458  *leftOf = clockwise ? ( sqrDistancePointToCenter > radius * radius ? -1 : 1 )
1459  : ( sqrDistancePointToCenter < radius * radius ? -1 : 1 );
1460  }
1461 
1462  return sqrDistance;
1463 }
1464 
1465 void QgsCircularString::insertVertexBetween( int after, int before, int pointOnCircle )
1466 {
1467  double xAfter = mX.at( after );
1468  double yAfter = mY.at( after );
1469  double xBefore = mX.at( before );
1470  double yBefore = mY.at( before );
1471  double xOnCircle = mX.at( pointOnCircle );
1472  double yOnCircle = mY.at( pointOnCircle );
1473 
1474  double radius, centerX, centerY;
1475  QgsGeometryUtils::circleCenterRadius( QgsPoint( xAfter, yAfter ), QgsPoint( xBefore, yBefore ), QgsPoint( xOnCircle, yOnCircle ), radius, centerX, centerY );
1476 
1477  double x = ( xAfter + xBefore ) / 2.0;
1478  double y = ( yAfter + yBefore ) / 2.0;
1479 
1480  QgsPoint newVertex = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), QgsPoint( x, y ), radius );
1481  mX.insert( before, newVertex.x() );
1482  mY.insert( before, newVertex.y() );
1483 
1484  if ( is3D() )
1485  {
1486  mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
1487  }
1488  if ( isMeasure() )
1489  {
1490  mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
1491  }
1492  clearCache();
1493 }
1494 
1496 {
1497  if ( numPoints() < 3 )
1498  {
1499  //undefined
1500  return 0.0;
1501  }
1502 
1503  int before = vId.vertex - 1;
1504  int vertex = vId.vertex;
1505  int after = vId.vertex + 1;
1506 
1507  if ( vId.vertex % 2 != 0 ) // a curve vertex
1508  {
1509  if ( vId.vertex >= 1 && vId.vertex < numPoints() - 1 )
1510  {
1511  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[before], mY[before] ),
1512  QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[after], mY[after] ) );
1513  }
1514  }
1515  else //a point vertex
1516  {
1517  if ( vId.vertex == 0 )
1518  {
1519  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[0], mY[0] ), QgsPoint( mX[0], mY[0] ),
1520  QgsPoint( mX[1], mY[1] ), QgsPoint( mX[2], mY[2] ) );
1521  }
1522  if ( vId.vertex >= numPoints() - 1 )
1523  {
1524  int a = numPoints() - 3;
1525  int b = numPoints() - 2;
1526  int c = numPoints() - 1;
1527  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[c], mY[c] ), QgsPoint( mX[a], mY[a] ),
1528  QgsPoint( mX[b], mY[b] ), QgsPoint( mX[c], mY[c] ) );
1529  }
1530  else
1531  {
1532  if ( vId.vertex + 2 > numPoints() - 1 )
1533  {
1534  return 0.0;
1535  }
1536 
1537  int vertex1 = vId.vertex - 2;
1538  int vertex2 = vId.vertex - 1;
1539  int vertex3 = vId.vertex;
1540  double angle1 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1541  QgsPoint( mX[vertex1], mY[vertex1] ), QgsPoint( mX[vertex2], mY[vertex2] ), QgsPoint( mX[vertex3], mY[vertex3] ) );
1542  int vertex4 = vId.vertex + 1;
1543  int vertex5 = vId.vertex + 2;
1544  double angle2 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1545  QgsPoint( mX[vertex3], mY[vertex3] ), QgsPoint( mX[vertex4], mY[vertex4] ), QgsPoint( mX[vertex5], mY[vertex5] ) );
1546  return QgsGeometryUtils::averageAngle( angle1, angle2 );
1547  }
1548  }
1549  return 0.0;
1550 }
1551 
1553 {
1554  if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 2 )
1555  return 0.0;
1556 
1557  if ( startVertex.vertex % 2 == 1 )
1558  return 0.0; // curve point?
1559 
1560  double x1 = mX.at( startVertex.vertex );
1561  double y1 = mY.at( startVertex.vertex );
1562  double x2 = mX.at( startVertex.vertex + 1 );
1563  double y2 = mY.at( startVertex.vertex + 1 );
1564  double x3 = mX.at( startVertex.vertex + 2 );
1565  double y3 = mY.at( startVertex.vertex + 2 );
1566  return QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1567 }
1568 
1570 {
1571  QgsCircularString *copy = clone();
1572  std::reverse( copy->mX.begin(), copy->mX.end() );
1573  std::reverse( copy->mY.begin(), copy->mY.end() );
1574  if ( is3D() )
1575  {
1576  std::reverse( copy->mZ.begin(), copy->mZ.end() );
1577  }
1578  if ( isMeasure() )
1579  {
1580  std::reverse( copy->mM.begin(), copy->mM.end() );
1581  }
1582  return copy;
1583 }
1584 
1585 QgsPoint *QgsCircularString::interpolatePoint( const double distance ) const
1586 {
1587  if ( distance < 0 )
1588  return nullptr;
1589 
1590  double distanceTraversed = 0;
1591  const int totalPoints = numPoints();
1592  if ( totalPoints == 0 )
1593  return nullptr;
1594 
1596  if ( is3D() )
1597  pointType = QgsWkbTypes::PointZ;
1598  if ( isMeasure() )
1599  pointType = QgsWkbTypes::addM( pointType );
1600 
1601  const double *x = mX.constData();
1602  const double *y = mY.constData();
1603  const double *z = is3D() ? mZ.constData() : nullptr;
1604  const double *m = isMeasure() ? mM.constData() : nullptr;
1605 
1606  double prevX = *x++;
1607  double prevY = *y++;
1608  double prevZ = z ? *z++ : 0.0;
1609  double prevM = m ? *m++ : 0.0;
1610 
1611  if ( qgsDoubleNear( distance, 0.0 ) )
1612  {
1613  return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1614  }
1615 
1616  for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1617  {
1618  double x1 = prevX;
1619  double y1 = prevY;
1620  double z1 = prevZ;
1621  double m1 = prevM;
1622 
1623  double x2 = *x++;
1624  double y2 = *y++;
1625  double z2 = z ? *z++ : 0.0;
1626  double m2 = m ? *m++ : 0.0;
1627 
1628  double x3 = *x++;
1629  double y3 = *y++;
1630  double z3 = z ? *z++ : 0.0;
1631  double m3 = m ? *m++ : 0.0;
1632 
1633  const double segmentLength = QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1634  if ( distance < distanceTraversed + segmentLength || qgsDoubleNear( distance, distanceTraversed + segmentLength ) )
1635  {
1636  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1637  const double distanceToPoint = std::min( distance - distanceTraversed, segmentLength );
1638  return new QgsPoint( QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1639  QgsPoint( pointType, x2, y2, z2, m2 ),
1640  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToPoint ) );
1641  }
1642 
1643  distanceTraversed += segmentLength;
1644 
1645  prevX = x3;
1646  prevY = y3;
1647  prevZ = z3;
1648  prevM = m3;
1649  }
1650 
1651  return nullptr;
1652 }
1653 
1654 QgsCircularString *QgsCircularString::curveSubstring( double startDistance, double endDistance ) const
1655 {
1656  if ( startDistance < 0 && endDistance < 0 )
1657  return createEmptyWithSameType();
1658 
1659  endDistance = std::max( startDistance, endDistance );
1660 
1661  const int totalPoints = numPoints();
1662  if ( totalPoints == 0 )
1663  return clone();
1664 
1665  QVector< QgsPoint > substringPoints;
1666  substringPoints.reserve( totalPoints );
1667 
1669  if ( is3D() )
1670  pointType = QgsWkbTypes::PointZ;
1671  if ( isMeasure() )
1672  pointType = QgsWkbTypes::addM( pointType );
1673 
1674  const double *x = mX.constData();
1675  const double *y = mY.constData();
1676  const double *z = is3D() ? mZ.constData() : nullptr;
1677  const double *m = isMeasure() ? mM.constData() : nullptr;
1678 
1679  double distanceTraversed = 0;
1680  double prevX = *x++;
1681  double prevY = *y++;
1682  double prevZ = z ? *z++ : 0.0;
1683  double prevM = m ? *m++ : 0.0;
1684  bool foundStart = false;
1685 
1686  if ( startDistance < 0 )
1687  startDistance = 0;
1688 
1689  for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1690  {
1691  double x1 = prevX;
1692  double y1 = prevY;
1693  double z1 = prevZ;
1694  double m1 = prevM;
1695 
1696  double x2 = *x++;
1697  double y2 = *y++;
1698  double z2 = z ? *z++ : 0.0;
1699  double m2 = m ? *m++ : 0.0;
1700 
1701  double x3 = *x++;
1702  double y3 = *y++;
1703  double z3 = z ? *z++ : 0.0;
1704  double m3 = m ? *m++ : 0.0;
1705 
1706  bool addedSegmentEnd = false;
1707  const double segmentLength = QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1708  if ( distanceTraversed <= startDistance && startDistance < distanceTraversed + segmentLength )
1709  {
1710  // start point falls on this segment
1711  const double distanceToStart = startDistance - distanceTraversed;
1712  const QgsPoint startPoint = QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1713  QgsPoint( pointType, x2, y2, z2, m2 ),
1714  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToStart );
1715 
1716  // does end point also fall on this segment?
1717  const bool endPointOnSegment = distanceTraversed + segmentLength > endDistance;
1718  if ( endPointOnSegment )
1719  {
1720  const double distanceToEnd = endDistance - distanceTraversed;
1721  const double midPointDistance = ( distanceToEnd - distanceToStart ) * 0.5 + distanceToStart;
1722  substringPoints << startPoint
1723  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1724  QgsPoint( pointType, x2, y2, z2, m2 ),
1725  QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1726  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1727  QgsPoint( pointType, x2, y2, z2, m2 ),
1728  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1729  addedSegmentEnd = true;
1730  }
1731  else
1732  {
1733  const double midPointDistance = ( segmentLength - distanceToStart ) * 0.5 + distanceToStart;
1734  substringPoints << startPoint
1735  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1736  QgsPoint( pointType, x2, y2, z2, m2 ),
1737  QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1738  << QgsPoint( pointType, x3, y3, z3, m3 );
1739  addedSegmentEnd = true;
1740  }
1741  foundStart = true;
1742  }
1743  if ( !addedSegmentEnd && foundStart && ( distanceTraversed + segmentLength > endDistance ) )
1744  {
1745  // end point falls on this segment
1746  const double distanceToEnd = endDistance - distanceTraversed;
1747  // add mid point, at half way along this arc, then add the interpolated end point
1748  substringPoints << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1749  QgsPoint( pointType, x2, y2, z2, m2 ),
1750  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd / 2.0 )
1751 
1752  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1753  QgsPoint( pointType, x2, y2, z2, m2 ),
1754  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1755  }
1756  else if ( !addedSegmentEnd && foundStart )
1757  {
1758  substringPoints << QgsPoint( pointType, x2, y2, z2, m2 )
1759  << QgsPoint( pointType, x3, y3, z3, m3 );
1760  }
1761 
1762  prevX = x3;
1763  prevY = y3;
1764  prevZ = z3;
1765  prevM = m3;
1766  distanceTraversed += segmentLength;
1767  if ( distanceTraversed >= endDistance )
1768  break;
1769  }
1770 
1771  // start point is the last node
1772  if ( !foundStart && qgsDoubleNear( distanceTraversed, startDistance ) )
1773  {
1774  substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM )
1775  << QgsPoint( pointType, prevX, prevY, prevZ, prevM )
1776  << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1777  }
1778 
1779  std::unique_ptr< QgsCircularString > result = std::make_unique< QgsCircularString >();
1780  result->setPoints( substringPoints );
1781  return result.release();
1782 }
1783 
1784 bool QgsCircularString::addZValue( double zValue )
1785 {
1786  if ( QgsWkbTypes::hasZ( mWkbType ) )
1787  return false;
1788 
1789  clearCache();
1791 
1792  int nPoints = numPoints();
1793  mZ.clear();
1794  mZ.reserve( nPoints );
1795  for ( int i = 0; i < nPoints; ++i )
1796  {
1797  mZ << zValue;
1798  }
1799  return true;
1800 }
1801 
1802 bool QgsCircularString::addMValue( double mValue )
1803 {
1804  if ( QgsWkbTypes::hasM( mWkbType ) )
1805  return false;
1806 
1807  clearCache();
1809 
1810  int nPoints = numPoints();
1811  mM.clear();
1812  mM.reserve( nPoints );
1813  for ( int i = 0; i < nPoints; ++i )
1814  {
1815  mM << mValue;
1816  }
1817  return true;
1818 }
1819 
1821 {
1822  if ( !QgsWkbTypes::hasZ( mWkbType ) )
1823  return false;
1824 
1825  clearCache();
1826 
1828  mZ.clear();
1829  return true;
1830 }
1831 
1833 {
1834  if ( !QgsWkbTypes::hasM( mWkbType ) )
1835  return false;
1836 
1837  clearCache();
1838 
1840  mM.clear();
1841  return true;
1842 }
1843 
1845 {
1846  std::swap( mX, mY );
1847  clearCache();
1848 }
QgsCurve
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
qgsgeometrytransformer.h
QgsCircularString::asWkt
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
Definition: qgscircularstring.cpp:524
QgsVertexId::part
int part
Part number.
Definition: qgsvertexid.h:89
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:885
QgsCoordinateTransform::transformCoords
void transformCoords(int numPoint, double *x, double *y, double *z, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const SIP_THROW(QgsCsException)
Transform an array of coordinates to the destination CRS.
Definition: qgscoordinatetransform.cpp:705
QgsCircularString::transform
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
Definition: qgscircularstring.cpp:1111
QgsVertexId::vertex
int vertex
Vertex number.
Definition: qgsvertexid.h:95
QgsCircularString::addToPainterPath
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
Definition: qgscircularstring.cpp:1172
QgsCircularString::geometryType
QString geometryType() const override SIP_HOLDGIL
Returns a unique string representing the geometry type.
Definition: qgscircularstring.cpp:247
QgsCircularString::fromWkt
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
Definition: qgscircularstring.cpp:479
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:1255
QgsWkbTypes::Point
@ Point
Definition: qgswkbtypes.h:72
QgsCircularString::isValid
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
Definition: qgscircularstring.cpp:578
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:391
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:839
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:692
QgsCircularString::pointN
QgsPoint pointN(int i) const SIP_HOLDGIL
Returns the point at index i within the circular string.
Definition: qgscircularstring.cpp:742
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:48
qgswkbptr.h
QgsCircularString::dropMValue
bool dropMValue() override
Drops any measure values which exist in the geometry.
Definition: qgscircularstring.cpp:1832
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:779
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:970
QgsWkbTypes::flatType
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:732
QgsAbstractGeometry::parts
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
Definition: qgsabstractgeometry.cpp:308
QgsCircularString::hasCurvedSegments
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
Definition: qgscircularstring.cpp:1409
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:1176
QgsCircularString::moveVertex
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
Definition: qgscircularstring.cpp:1233
QgsAbstractGeometry::wktTypeStr
QString wktTypeStr() const
Returns the WKT type string of the geometry.
Definition: qgsabstractgeometry.cpp:181
QgsFeedback::isCanceled
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:67
QgsPoint::z
double z
Definition: qgspoint.h:71
QgsCircularString::equals
bool equals(const QgsCurve &other) const override
Checks whether this curve exactly equals another curve.
Definition: qgscircularstring.cpp:126
QgsCircularString::segmentLength
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
Definition: qgscircularstring.cpp:1552
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:69
QgsAbstractGeometry::SegmentationToleranceType
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
Definition: qgsabstractgeometry.h:120
qgspoint.h
QgsAbstractGeometry::mWkbType
QgsWkbTypes::Type mWkbType
Definition: qgsabstractgeometry.h:1119
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:605
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:1104
QgsCircularString::clear
void clear() override
Clears the geometry, ie reset it to a null geometry.
Definition: qgscircularstring.cpp:262
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:1132
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:41
arcTo
void arcTo(QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3)
Definition: qgscircularstring.cpp:1159
QgsCurve::clearCache
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgscurve.cpp:293
QgsCircularString::splitCurveAtVertex
std::tuple< std::unique_ptr< QgsCurve >, std::unique_ptr< QgsCurve > > splitCurveAtVertex(int index) const final
Splits the curve at the specified vertex index, returning two curves which represent the portion of t...
Definition: qgscircularstring.cpp:913
QgsCircularString::insertVertex
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
Definition: qgscircularstring.cpp:1202
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:706
qgsapplication.h
QgsAbstractGeometry::isMeasure
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
Definition: qgsabstractgeometry.h:228
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:272
QgsCircularString::vertexAngle
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
Definition: qgscircularstring.cpp:1495
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
QgsCircularString::indexOf
int indexOf(const QgsPoint &point) const final
Returns the index of the first vertex matching the given point, or -1 if a matching vertex is not fou...
Definition: qgscircularstring.cpp:702
QgsPoint::y
double y
Definition: qgspoint.h:70
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:1201
precision
int precision
Definition: qgswfsgetfeature.cpp:103
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:744
QgsCircularString::deleteVertex
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
Definition: qgscircularstring.cpp:1254
QgsWkbTypes::PointZM
@ PointZM
Definition: qgswkbtypes.h:112
QgsCircularString
Circular string geometry type.
Definition: qgscircularstring.h:34
QgsAbstractGeometry::wkbType
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
Definition: qgsabstractgeometry.h:206
QgsCircularString::pointAt
bool pointAt(int node, QgsPoint &point, Qgis::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
Definition: qgscircularstring.cpp:1337
QgsAbstractGeometry::AxisOrder
AxisOrder
Axis order for GML generation.
Definition: qgsabstractgeometry.h:138
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:539
QgsCircularString::startPoint
QgsPoint startPoint() const override SIP_HOLDGIL
Returns the starting point of the curve.
Definition: qgscircularstring.cpp:600
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:1219
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:2265
QgsConstWkbPtr
A const WKB pointer.
Definition: qgswkbptr.h:137
QgsFeedback
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:44
QgsLineString::setPoints
void setPoints(size_t size, const double *x, const double *y, const double *z=nullptr, const double *m=nullptr)
Resets the line string to match the specified point data.
Definition: qgslinestring.cpp:1092
QgsCircularString::points
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
Definition: qgscircularstring.cpp:972
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:787
QgsCircularString::interpolatePoint
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
Definition: qgscircularstring.cpp:1585
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:1662
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:616
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:788
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:1348
QgsPoint::setX
void setX(double x) SIP_HOLDGIL
Sets the point's x-coordinate.
Definition: qgspoint.h:297
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:877
QgsWkbTypes::hasM
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1130
QgsWkbTypes::PointZ
@ PointZ
Definition: qgswkbtypes.h:86
QgsApplication::endian
static endian_t endian()
Returns whether this machine uses big or little endian.
Definition: qgsapplication.cpp:1404
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:782
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:154
QgsPoint::m
double m
Definition: qgspoint.h:72
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:1569
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:1237
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:886
QgsCircularString::addZValue
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition: qgscircularstring.cpp:1784
QgsWkbPtr
WKB pointer handler.
Definition: qgswkbptr.h:43
QgsCircularString::length
double length() const override
Returns the planar, 2-dimensional length of the geometry.
Definition: qgscircularstring.cpp:589
QgsCurve::isValid
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
Definition: qgscurve.cpp:247
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:316
qgscircularstring.h
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:633
QgsAbstractGeometry
Abstract base class for all geometries.
Definition: qgsabstractgeometry.h:79
QgsWkbTypes::CircularStringZ
@ CircularStringZ
Definition: qgswkbtypes.h:94
qgsgeometryutils.h
QgsAbstractGeometry::is3D
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
Definition: qgsabstractgeometry.h:219
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:1282
Qgis::VertexType
VertexType
Types of vertex.
Definition: qgis.h:1356
QgsWkbTypes::CircularString
@ CircularString
Definition: qgswkbtypes.h:80
QgsCircularString::append
void append(const QgsCircularString *string)
Appends the contents of another circular string to the end of this circular string.
Definition: qgscircularstring.cpp:1039
QgsCircularString::swapXy
void swapXy() override
Swaps the x and y coordinates from the geometry.
Definition: qgscircularstring.cpp:1844
QgsCircularString::numPoints
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
Definition: qgscircularstring.cpp:697
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:800
QgsCircularString::fromWkb
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
Definition: qgscircularstring.cpp:440
QgsCircularString::wkbSize
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb()
Definition: qgscircularstring.cpp:504
QgsPointSequence
QVector< QgsPoint > QgsPointSequence
Definition: qgsabstractgeometry.h:52
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:1197
QgsGeometryUtils::pointsToWKT
static QString pointsToWKT(const QgsPointSequence &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
Definition: qgsgeometryutils.cpp:1236
QgsCircularString::isEmpty
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgscircularstring.cpp:573
QgsCircularString::scroll
void scroll(int firstVertexIndex) final
Scrolls the curve vertices so that they start with the vertex at the given index.
Definition: qgscircularstring.cpp:300
QgsVertexId
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:30
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:120
QgsAbstractGeometryTransformer
An abstract base class for classes which transform geometries by transforming input points to output ...
Definition: qgsgeometrytransformer.h:32
QgsCircularString::endPoint
QgsPoint endPoint() const override SIP_HOLDGIL
Returns the end point of the curve.
Definition: qgscircularstring.cpp:609
QgsVertexId::ring
int ring
Ring number.
Definition: qgsvertexid.h:92
QgsCircularString::addMValue
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition: qgscircularstring.cpp:1802
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:1298
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:618
QgsCircularString::curveSubstring
QgsCircularString * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
Definition: qgscircularstring.cpp:1654
QgsWkbTypes::hasZ
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1080
QgsCircularString::asWkb
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Definition: qgscircularstring.cpp:511
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:756
Qgis::TransformDirection
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition: qgis.h:1235
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:308
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:1351
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:77
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:646
QgsCoordinateTransform
Class for doing transforms between two map coordinate systems.
Definition: qgscoordinatetransform.h:57
QgsCircularString::QgsCircularString
QgsCircularString() SIP_HOLDGIL
Constructs an empty circular string.
Definition: qgscircularstring.cpp:36
qgsfeedback.h
QgsCircularString::clone
QgsCircularString * clone() const override
Clones the geometry by performing a deep copy.
Definition: qgscircularstring.cpp:257
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::compareToSameClass
int compareToSameClass(const QgsAbstractGeometry *other) const final
Compares to an other geometry of the same class, and returns a integer for sorting of the two geometr...
Definition: qgscircularstring.cpp:161
QgsCircularString::setPoints
void setPoints(const QgsPointSequence &points)
Sets the circular string's points.
Definition: qgscircularstring.cpp:982
QgsPoint::x
double x
Definition: qgspoint.h:69
QgsCircularString::dimension
int dimension() const override SIP_HOLDGIL
Returns the inherent dimension of the geometry.
Definition: qgscircularstring.cpp:252
QgsCircularString::asJsonObject
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
Definition: qgscircularstring.cpp:566
QgsAbstractGeometryTransformer::transformPoint
virtual bool transformPoint(double &x, double &y, double &z, double &m)=0
Transforms the point defined by the coordinates (x, y, z) and the specified m value.
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:547
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:667
QgsCircularString::dropZValue
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Definition: qgscircularstring.cpp:1820