QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 QgsCircularString::addToPainterPath( QPainterPath &path ) const
1160 {
1161  int nPoints = numPoints();
1162  if ( nPoints < 1 )
1163  {
1164  return;
1165  }
1166 
1167  if ( path.isEmpty() || path.currentPosition() != QPointF( mX[0], mY[0] ) )
1168  {
1169  path.moveTo( QPointF( mX[0], mY[0] ) );
1170  }
1171 
1172  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
1173  {
1174  QgsPointSequence pt;
1175  QgsGeometryUtils::segmentizeArc( QgsPoint( mX[i], mY[i] ), QgsPoint( mX[i + 1], mY[i + 1] ), QgsPoint( mX[i + 2], mY[i + 2] ), pt );
1176  for ( int j = 1; j < pt.size(); ++j )
1177  {
1178  path.lineTo( pt.at( j ).x(), pt.at( j ).y() );
1179  }
1180 #if 0
1181  //arcTo( path, QPointF( mX[i], mY[i] ), QPointF( mX[i + 1], mY[i + 1] ), QPointF( mX[i + 2], mY[i + 2] ) );
1182 #endif
1183  }
1184 
1185  //if number of points is even, connect to last point with straight line (even though the circular string is not valid)
1186  if ( nPoints % 2 == 0 )
1187  {
1188  path.lineTo( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
1189  }
1190 }
1191 
1192 #if 0
1193 void QgsCircularString::arcTo( QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3 )
1194 {
1195  double centerX, centerY, radius;
1196  QgsGeometryUtils::circleCenterRadius( QgsPoint( pt1.x(), pt1.y() ), QgsPoint( pt2.x(), pt2.y() ), QgsPoint( pt3.x(), pt3.y() ),
1197  radius, centerX, centerY );
1198 
1199  double p1Angle = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
1200  double sweepAngle = QgsGeometryUtils::sweepAngle( centerX, centerY, pt1.x(), pt1.y(), pt2.x(), pt2.y(), pt3.x(), pt3.y() );
1201 
1202  double diameter = 2 * radius;
1203  path.arcTo( centerX - radius, centerY - radius, diameter, diameter, p1Angle, sweepAngle );
1204 }
1205 #endif
1206 
1207 void QgsCircularString::drawAsPolygon( QPainter &p ) const
1208 {
1209  draw( p );
1210 }
1211 
1213 {
1214  if ( position.vertex >= mX.size() || position.vertex < 1 )
1215  {
1216  return false;
1217  }
1218 
1219  mX.insert( position.vertex, vertex.x() );
1220  mY.insert( position.vertex, vertex.y() );
1221  if ( is3D() )
1222  {
1223  mZ.insert( position.vertex, vertex.z() );
1224  }
1225  if ( isMeasure() )
1226  {
1227  mM.insert( position.vertex, vertex.m() );
1228  }
1229 
1230  bool vertexNrEven = ( position.vertex % 2 == 0 );
1231  if ( vertexNrEven )
1232  {
1233  insertVertexBetween( position.vertex - 2, position.vertex - 1, position.vertex );
1234  }
1235  else
1236  {
1237  insertVertexBetween( position.vertex, position.vertex + 1, position.vertex - 1 );
1238  }
1239  clearCache(); //set bounding box invalid
1240  return true;
1241 }
1242 
1243 bool QgsCircularString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
1244 {
1245  if ( position.vertex < 0 || position.vertex >= mX.size() )
1246  {
1247  return false;
1248  }
1249 
1250  mX[position.vertex] = newPos.x();
1251  mY[position.vertex] = newPos.y();
1252  if ( is3D() && newPos.is3D() )
1253  {
1254  mZ[position.vertex] = newPos.z();
1255  }
1256  if ( isMeasure() && newPos.isMeasure() )
1257  {
1258  mM[position.vertex] = newPos.m();
1259  }
1260  clearCache(); //set bounding box invalid
1261  return true;
1262 }
1263 
1265 {
1266  int nVertices = this->numPoints();
1267  if ( nVertices < 4 ) //circular string must have at least 3 vertices
1268  {
1269  clear();
1270  return true;
1271  }
1272  if ( position.vertex < 0 || position.vertex > ( nVertices - 1 ) )
1273  {
1274  return false;
1275  }
1276 
1277  if ( position.vertex < ( nVertices - 2 ) )
1278  {
1279  //remove this and the following vertex
1280  deleteVertex( position.vertex + 1 );
1281  deleteVertex( position.vertex );
1282  }
1283  else //remove this and the preceding vertex
1284  {
1285  deleteVertex( position.vertex );
1286  deleteVertex( position.vertex - 1 );
1287  }
1288 
1289  clearCache(); //set bounding box invalid
1290  return true;
1291 }
1292 
1293 void QgsCircularString::deleteVertex( int i )
1294 {
1295  mX.remove( i );
1296  mY.remove( i );
1297  if ( is3D() )
1298  {
1299  mZ.remove( i );
1300  }
1301  if ( isMeasure() )
1302  {
1303  mM.remove( i );
1304  }
1305  clearCache();
1306 }
1307 
1308 double QgsCircularString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1309 {
1310  double minDist = std::numeric_limits<double>::max();
1311  QgsPoint minDistSegmentPoint;
1312  QgsVertexId minDistVertexAfter;
1313  int minDistLeftOf = 0;
1314 
1315  double currentDist = 0.0;
1316 
1317  int nPoints = numPoints();
1318  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
1319  {
1320  currentDist = closestPointOnArc( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2], pt, segmentPt, vertexAfter, leftOf, epsilon );
1321  if ( currentDist < minDist )
1322  {
1323  minDist = currentDist;
1324  minDistSegmentPoint = segmentPt;
1325  minDistVertexAfter.vertex = vertexAfter.vertex + i;
1326  if ( leftOf )
1327  {
1328  minDistLeftOf = *leftOf;
1329  }
1330  }
1331  }
1332 
1333  if ( minDist == std::numeric_limits<double>::max() )
1334  return -1; // error: no segments
1335 
1336  segmentPt = minDistSegmentPoint;
1337  vertexAfter = minDistVertexAfter;
1338  vertexAfter.part = 0;
1339  vertexAfter.ring = 0;
1340  if ( leftOf )
1341  {
1342  *leftOf = qgsDoubleNear( minDist, 0.0 ) ? 0 : minDistLeftOf;
1343  }
1344  return minDist;
1345 }
1346 
1347 bool QgsCircularString::pointAt( int node, QgsPoint &point, Qgis::VertexType &type ) const
1348 {
1349  if ( node < 0 || node >= numPoints() )
1350  {
1351  return false;
1352  }
1353  point = pointN( node );
1354  type = ( node % 2 == 0 ) ? Qgis::VertexType::Segment : Qgis::VertexType::Curve;
1355  return true;
1356 }
1357 
1358 void QgsCircularString::sumUpArea( double &sum ) const
1359 {
1360  int maxIndex = numPoints() - 2;
1361 
1362  for ( int i = 0; i < maxIndex; i += 2 )
1363  {
1364  QgsPoint p1( mX[i], mY[i] );
1365  QgsPoint p2( mX[i + 1], mY[i + 1] );
1366  QgsPoint p3( mX[i + 2], mY[i + 2] );
1367 
1368  //segment is a full circle, p2 is the center point
1369  if ( p1 == p3 )
1370  {
1371  double r2 = QgsGeometryUtils::sqrDistance2D( p1, p2 ) / 4.0;
1372  sum += M_PI * r2;
1373  continue;
1374  }
1375 
1376  sum += 0.5 * ( mX[i] * mY[i + 2] - mY[i] * mX[i + 2] );
1377 
1378  //calculate area between circle and chord, then sum / subtract from total area
1379  double midPointX = ( p1.x() + p3.x() ) / 2.0;
1380  double midPointY = ( p1.y() + p3.y() ) / 2.0;
1381 
1382  double radius, centerX, centerY;
1383  QgsGeometryUtils::circleCenterRadius( p1, p2, p3, radius, centerX, centerY );
1384 
1385  double d = std::sqrt( QgsGeometryUtils::sqrDistance2D( QgsPoint( centerX, centerY ), QgsPoint( midPointX, midPointY ) ) );
1386  double r2 = radius * radius;
1387 
1388  if ( d > radius )
1389  {
1390  //d cannot be greater than radius, something must be wrong...
1391  continue;
1392  }
1393 
1394  bool circlePointLeftOfLine = QgsGeometryUtils::leftOfLine( p2.x(), p2.y(), p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
1395  bool centerPointLeftOfLine = QgsGeometryUtils::leftOfLine( centerX, centerY, p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
1396 
1397  double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - M_1_PI * std::asin( d / radius );
1398  double circleChordArea = 0;
1399  if ( circlePointLeftOfLine == centerPointLeftOfLine )
1400  {
1401  circleChordArea = M_PI * r2 * ( 1 - cov );
1402  }
1403  else
1404  {
1405  circleChordArea = M_PI * r2 * cov;
1406  }
1407 
1408  if ( !circlePointLeftOfLine )
1409  {
1410  sum += circleChordArea;
1411  }
1412  else
1413  {
1414  sum -= circleChordArea;
1415  }
1416  }
1417 }
1418 
1420 {
1421  return true;
1422 }
1423 
1424 double QgsCircularString::closestPointOnArc( double x1, double y1, double x2, double y2, double x3, double y3,
1425  const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon )
1426 {
1427  double radius, centerX, centerY;
1428  QgsPoint pt1( x1, y1 );
1429  QgsPoint pt2( x2, y2 );
1430  QgsPoint pt3( x3, y3 );
1431 
1432  QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
1433  double angle = QgsGeometryUtils::ccwAngle( pt.y() - centerY, pt.x() - centerX );
1434  double angle1 = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
1435  double angle2 = QgsGeometryUtils::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
1436  double angle3 = QgsGeometryUtils::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
1437 
1438  bool clockwise = QgsGeometryUtils::circleClockwise( angle1, angle2, angle3 );
1439 
1440  if ( QgsGeometryUtils::angleOnCircle( angle, angle1, angle2, angle3 ) )
1441  {
1442  //get point on line center -> pt with distance radius
1443  segmentPt = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), pt, radius );
1444 
1445  //vertexAfter
1446  vertexAfter.vertex = QgsGeometryUtils::circleAngleBetween( angle, angle1, angle2, clockwise ) ? 1 : 2;
1447  }
1448  else
1449  {
1450  double distPtPt1 = QgsGeometryUtils::sqrDistance2D( pt, pt1 );
1451  double distPtPt3 = QgsGeometryUtils::sqrDistance2D( pt, pt3 );
1452  segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
1453  vertexAfter.vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
1454  }
1455 
1456  double sqrDistance = QgsGeometryUtils::sqrDistance2D( segmentPt, pt );
1457  //prevent rounding errors if the point is directly on the segment
1458  if ( qgsDoubleNear( sqrDistance, 0.0, epsilon ) )
1459  {
1460  segmentPt.setX( pt.x() );
1461  segmentPt.setY( pt.y() );
1462  sqrDistance = 0.0;
1463  }
1464 
1465  if ( leftOf )
1466  {
1467  double sqrDistancePointToCenter = ( pt.x() - centerX ) * ( pt.x() - centerX ) + ( pt.y() - centerY ) * ( pt.y() - centerY );
1468  *leftOf = clockwise ? ( sqrDistancePointToCenter > radius * radius ? -1 : 1 )
1469  : ( sqrDistancePointToCenter < radius * radius ? -1 : 1 );
1470  }
1471 
1472  return sqrDistance;
1473 }
1474 
1475 void QgsCircularString::insertVertexBetween( int after, int before, int pointOnCircle )
1476 {
1477  double xAfter = mX.at( after );
1478  double yAfter = mY.at( after );
1479  double xBefore = mX.at( before );
1480  double yBefore = mY.at( before );
1481  double xOnCircle = mX.at( pointOnCircle );
1482  double yOnCircle = mY.at( pointOnCircle );
1483 
1484  double radius, centerX, centerY;
1485  QgsGeometryUtils::circleCenterRadius( QgsPoint( xAfter, yAfter ), QgsPoint( xBefore, yBefore ), QgsPoint( xOnCircle, yOnCircle ), radius, centerX, centerY );
1486 
1487  double x = ( xAfter + xBefore ) / 2.0;
1488  double y = ( yAfter + yBefore ) / 2.0;
1489 
1490  QgsPoint newVertex = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), QgsPoint( x, y ), radius );
1491  mX.insert( before, newVertex.x() );
1492  mY.insert( before, newVertex.y() );
1493 
1494  if ( is3D() )
1495  {
1496  mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
1497  }
1498  if ( isMeasure() )
1499  {
1500  mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
1501  }
1502  clearCache();
1503 }
1504 
1506 {
1507  if ( numPoints() < 3 )
1508  {
1509  //undefined
1510  return 0.0;
1511  }
1512 
1513  int before = vId.vertex - 1;
1514  int vertex = vId.vertex;
1515  int after = vId.vertex + 1;
1516 
1517  if ( vId.vertex % 2 != 0 ) // a curve vertex
1518  {
1519  if ( vId.vertex >= 1 && vId.vertex < numPoints() - 1 )
1520  {
1521  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[before], mY[before] ),
1522  QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[after], mY[after] ) );
1523  }
1524  }
1525  else //a point vertex
1526  {
1527  if ( vId.vertex == 0 )
1528  {
1529  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[0], mY[0] ), QgsPoint( mX[0], mY[0] ),
1530  QgsPoint( mX[1], mY[1] ), QgsPoint( mX[2], mY[2] ) );
1531  }
1532  if ( vId.vertex >= numPoints() - 1 )
1533  {
1534  int a = numPoints() - 3;
1535  int b = numPoints() - 2;
1536  int c = numPoints() - 1;
1537  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[c], mY[c] ), QgsPoint( mX[a], mY[a] ),
1538  QgsPoint( mX[b], mY[b] ), QgsPoint( mX[c], mY[c] ) );
1539  }
1540  else
1541  {
1542  if ( vId.vertex + 2 > numPoints() - 1 )
1543  {
1544  return 0.0;
1545  }
1546 
1547  int vertex1 = vId.vertex - 2;
1548  int vertex2 = vId.vertex - 1;
1549  int vertex3 = vId.vertex;
1550  double angle1 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1551  QgsPoint( mX[vertex1], mY[vertex1] ), QgsPoint( mX[vertex2], mY[vertex2] ), QgsPoint( mX[vertex3], mY[vertex3] ) );
1552  int vertex4 = vId.vertex + 1;
1553  int vertex5 = vId.vertex + 2;
1554  double angle2 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1555  QgsPoint( mX[vertex3], mY[vertex3] ), QgsPoint( mX[vertex4], mY[vertex4] ), QgsPoint( mX[vertex5], mY[vertex5] ) );
1556  return QgsGeometryUtils::averageAngle( angle1, angle2 );
1557  }
1558  }
1559  return 0.0;
1560 }
1561 
1563 {
1564  if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 2 )
1565  return 0.0;
1566 
1567  if ( startVertex.vertex % 2 == 1 )
1568  return 0.0; // curve point?
1569 
1570  double x1 = mX.at( startVertex.vertex );
1571  double y1 = mY.at( startVertex.vertex );
1572  double x2 = mX.at( startVertex.vertex + 1 );
1573  double y2 = mY.at( startVertex.vertex + 1 );
1574  double x3 = mX.at( startVertex.vertex + 2 );
1575  double y3 = mY.at( startVertex.vertex + 2 );
1576  return QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1577 }
1578 
1580 {
1581  QgsCircularString *copy = clone();
1582  std::reverse( copy->mX.begin(), copy->mX.end() );
1583  std::reverse( copy->mY.begin(), copy->mY.end() );
1584  if ( is3D() )
1585  {
1586  std::reverse( copy->mZ.begin(), copy->mZ.end() );
1587  }
1588  if ( isMeasure() )
1589  {
1590  std::reverse( copy->mM.begin(), copy->mM.end() );
1591  }
1592  return copy;
1593 }
1594 
1595 QgsPoint *QgsCircularString::interpolatePoint( const double distance ) const
1596 {
1597  if ( distance < 0 )
1598  return nullptr;
1599 
1600  double distanceTraversed = 0;
1601  const int totalPoints = numPoints();
1602  if ( totalPoints == 0 )
1603  return nullptr;
1604 
1606  if ( is3D() )
1607  pointType = QgsWkbTypes::PointZ;
1608  if ( isMeasure() )
1609  pointType = QgsWkbTypes::addM( pointType );
1610 
1611  const double *x = mX.constData();
1612  const double *y = mY.constData();
1613  const double *z = is3D() ? mZ.constData() : nullptr;
1614  const double *m = isMeasure() ? mM.constData() : nullptr;
1615 
1616  double prevX = *x++;
1617  double prevY = *y++;
1618  double prevZ = z ? *z++ : 0.0;
1619  double prevM = m ? *m++ : 0.0;
1620 
1621  if ( qgsDoubleNear( distance, 0.0 ) )
1622  {
1623  return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1624  }
1625 
1626  for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1627  {
1628  double x1 = prevX;
1629  double y1 = prevY;
1630  double z1 = prevZ;
1631  double m1 = prevM;
1632 
1633  double x2 = *x++;
1634  double y2 = *y++;
1635  double z2 = z ? *z++ : 0.0;
1636  double m2 = m ? *m++ : 0.0;
1637 
1638  double x3 = *x++;
1639  double y3 = *y++;
1640  double z3 = z ? *z++ : 0.0;
1641  double m3 = m ? *m++ : 0.0;
1642 
1643  const double segmentLength = QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1644  if ( distance < distanceTraversed + segmentLength || qgsDoubleNear( distance, distanceTraversed + segmentLength ) )
1645  {
1646  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1647  const double distanceToPoint = std::min( distance - distanceTraversed, segmentLength );
1648  return new QgsPoint( QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1649  QgsPoint( pointType, x2, y2, z2, m2 ),
1650  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToPoint ) );
1651  }
1652 
1653  distanceTraversed += segmentLength;
1654 
1655  prevX = x3;
1656  prevY = y3;
1657  prevZ = z3;
1658  prevM = m3;
1659  }
1660 
1661  return nullptr;
1662 }
1663 
1664 QgsCircularString *QgsCircularString::curveSubstring( double startDistance, double endDistance ) const
1665 {
1666  if ( startDistance < 0 && endDistance < 0 )
1667  return createEmptyWithSameType();
1668 
1669  endDistance = std::max( startDistance, endDistance );
1670 
1671  const int totalPoints = numPoints();
1672  if ( totalPoints == 0 )
1673  return clone();
1674 
1675  QVector< QgsPoint > substringPoints;
1676  substringPoints.reserve( totalPoints );
1677 
1679  if ( is3D() )
1680  pointType = QgsWkbTypes::PointZ;
1681  if ( isMeasure() )
1682  pointType = QgsWkbTypes::addM( pointType );
1683 
1684  const double *x = mX.constData();
1685  const double *y = mY.constData();
1686  const double *z = is3D() ? mZ.constData() : nullptr;
1687  const double *m = isMeasure() ? mM.constData() : nullptr;
1688 
1689  double distanceTraversed = 0;
1690  double prevX = *x++;
1691  double prevY = *y++;
1692  double prevZ = z ? *z++ : 0.0;
1693  double prevM = m ? *m++ : 0.0;
1694  bool foundStart = false;
1695 
1696  if ( startDistance < 0 )
1697  startDistance = 0;
1698 
1699  for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1700  {
1701  double x1 = prevX;
1702  double y1 = prevY;
1703  double z1 = prevZ;
1704  double m1 = prevM;
1705 
1706  double x2 = *x++;
1707  double y2 = *y++;
1708  double z2 = z ? *z++ : 0.0;
1709  double m2 = m ? *m++ : 0.0;
1710 
1711  double x3 = *x++;
1712  double y3 = *y++;
1713  double z3 = z ? *z++ : 0.0;
1714  double m3 = m ? *m++ : 0.0;
1715 
1716  bool addedSegmentEnd = false;
1717  const double segmentLength = QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1718  if ( distanceTraversed <= startDistance && startDistance < distanceTraversed + segmentLength )
1719  {
1720  // start point falls on this segment
1721  const double distanceToStart = startDistance - distanceTraversed;
1722  const QgsPoint startPoint = QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1723  QgsPoint( pointType, x2, y2, z2, m2 ),
1724  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToStart );
1725 
1726  // does end point also fall on this segment?
1727  const bool endPointOnSegment = distanceTraversed + segmentLength > endDistance;
1728  if ( endPointOnSegment )
1729  {
1730  const double distanceToEnd = endDistance - distanceTraversed;
1731  const double midPointDistance = ( distanceToEnd - distanceToStart ) * 0.5 + distanceToStart;
1732  substringPoints << startPoint
1733  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1734  QgsPoint( pointType, x2, y2, z2, m2 ),
1735  QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1736  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1737  QgsPoint( pointType, x2, y2, z2, m2 ),
1738  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1739  addedSegmentEnd = true;
1740  }
1741  else
1742  {
1743  const double midPointDistance = ( segmentLength - distanceToStart ) * 0.5 + distanceToStart;
1744  substringPoints << startPoint
1745  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1746  QgsPoint( pointType, x2, y2, z2, m2 ),
1747  QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1748  << QgsPoint( pointType, x3, y3, z3, m3 );
1749  addedSegmentEnd = true;
1750  }
1751  foundStart = true;
1752  }
1753  if ( !addedSegmentEnd && foundStart && ( distanceTraversed + segmentLength > endDistance ) )
1754  {
1755  // end point falls on this segment
1756  const double distanceToEnd = endDistance - distanceTraversed;
1757  // add mid point, at half way along this arc, then add the interpolated end point
1758  substringPoints << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1759  QgsPoint( pointType, x2, y2, z2, m2 ),
1760  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd / 2.0 )
1761 
1762  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1763  QgsPoint( pointType, x2, y2, z2, m2 ),
1764  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1765  }
1766  else if ( !addedSegmentEnd && foundStart )
1767  {
1768  substringPoints << QgsPoint( pointType, x2, y2, z2, m2 )
1769  << QgsPoint( pointType, x3, y3, z3, m3 );
1770  }
1771 
1772  prevX = x3;
1773  prevY = y3;
1774  prevZ = z3;
1775  prevM = m3;
1776  distanceTraversed += segmentLength;
1777  if ( distanceTraversed >= endDistance )
1778  break;
1779  }
1780 
1781  // start point is the last node
1782  if ( !foundStart && qgsDoubleNear( distanceTraversed, startDistance ) )
1783  {
1784  substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM )
1785  << QgsPoint( pointType, prevX, prevY, prevZ, prevM )
1786  << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1787  }
1788 
1789  std::unique_ptr< QgsCircularString > result = std::make_unique< QgsCircularString >();
1790  result->setPoints( substringPoints );
1791  return result.release();
1792 }
1793 
1794 bool QgsCircularString::addZValue( double zValue )
1795 {
1796  if ( QgsWkbTypes::hasZ( mWkbType ) )
1797  return false;
1798 
1799  clearCache();
1801 
1802  int nPoints = numPoints();
1803  mZ.clear();
1804  mZ.reserve( nPoints );
1805  for ( int i = 0; i < nPoints; ++i )
1806  {
1807  mZ << zValue;
1808  }
1809  return true;
1810 }
1811 
1812 bool QgsCircularString::addMValue( double mValue )
1813 {
1814  if ( QgsWkbTypes::hasM( mWkbType ) )
1815  return false;
1816 
1817  clearCache();
1819 
1820  int nPoints = numPoints();
1821  mM.clear();
1822  mM.reserve( nPoints );
1823  for ( int i = 0; i < nPoints; ++i )
1824  {
1825  mM << mValue;
1826  }
1827  return true;
1828 }
1829 
1831 {
1832  if ( !QgsWkbTypes::hasZ( mWkbType ) )
1833  return false;
1834 
1835  clearCache();
1836 
1838  mZ.clear();
1839  return true;
1840 }
1841 
1843 {
1844  if ( !QgsWkbTypes::hasM( mWkbType ) )
1845  return false;
1846 
1847  clearCache();
1848 
1850  mM.clear();
1851  return true;
1852 }
1853 
1855 {
1856  std::swap( mX, mY );
1857  clearCache();
1858 }
VertexType
Types of vertex.
Definition: qgis.h:990
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition: qgis.h:896
An abstract base class for classes which transform geometries by transforming input points to output ...
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.
Abstract base class for all geometries.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
AxisOrder
Axis order for GML generation.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
QgsWkbTypes::Type mWkbType
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
static endian_t endian()
Returns whether this machine uses big or little endian.
Circular string geometry type.
bool equals(const QgsCurve &other) const override
Checks whether this curve exactly equals another curve.
double length() const override
Returns the planar, 2-dimensional length of the geometry.
QgsPoint pointN(int i) const SIP_HOLDGIL
Returns the point at index i within the circular string.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
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.
QgsCircularString * clone() const override
Clones the geometry by performing a deep copy.
QString geometryType() const override SIP_HOLDGIL
Returns a unique string representing the geometry type.
void append(const QgsCircularString *string)
Appends the contents of another circular string to the end of this circular string.
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
int dimension() const override SIP_HOLDGIL
Returns the inherent dimension of the geometry.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
QgsCircularString * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
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.
void swapXy() override
Swaps the x and y coordinates from the geometry.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
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...
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...
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
double xAt(int index) const override SIP_HOLDGIL
Returns the x-coordinate of the specified node in the line string.
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...
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML2 representation of the geometry.
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
QgsCircularString * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
QgsRectangle calculateBoundingBox() const override
Default calculator for the minimal bounding box for the geometry.
bool pointAt(int node, QgsPoint &point, Qgis::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
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...
bool dropMValue() override
Drops any measure values which exist in the geometry.
double yAt(int index) const override SIP_HOLDGIL
Returns the y-coordinate of the specified node in the line string.
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.
QgsPoint endPoint() const override SIP_HOLDGIL
Returns the end point of the curve.
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb()
QgsCircularString * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
void setPoints(const QgsPointSequence &points)
Sets the circular string's points.
QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a new line string geometry corresponding to a segmentized approximation of the curve.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
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.
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
void clear() override
Clears the geometry, ie reset it to a null geometry.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
void scroll(int firstVertexIndex) final
Scrolls the curve vertices so that they start with the vertex at the given index.
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
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...
QgsPoint startPoint() const override SIP_HOLDGIL
Returns the starting point of the curve.
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...
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
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.
QgsCircularString() SIP_HOLDGIL
Constructs an empty circular string.
A const WKB pointer.
Definition: qgswkbptr.h:138
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
Class for doing transforms between two map coordinate systems.
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.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgscurve.cpp:293
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
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
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
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 (...
static double circleTangentDirection(const QgsPoint &tangentPoint, const QgsPoint &cp1, const QgsPoint &cp2, const QgsPoint &cp3) SIP_HOLDGIL
Calculates the direction angle of a circle tangent (clockwise from north in radians)
static double sqrDistance2D(const QgsPoint &pt1, const QgsPoint &pt2) SIP_HOLDGIL
Returns the squared 2D distance between two points.
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...
static void pointsToWKB(QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
static QgsPoint segmentMidPointFromCenter(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, bool useShortestArc=true) SIP_HOLDGIL
Calculates the midpoint on the circle passing through p1 and p2, with the specified center coordinate...
static 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.
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,...
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,...
static bool circleClockwise(double angle1, double angle2, double angle3) SIP_HOLDGIL
Returns true if the circle defined by three angles is ordered clockwise.
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.
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.
static QgsPoint pointOnLineWithDistance(const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance) SIP_HOLDGIL
Returns a point a specified distance toward a second point.
static QgsPointSequence pointsFromWKT(const QString &wktCoordinateList, bool is3D, bool isMeasure)
Returns a list of points contained in a WKT string.
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.
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.
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.
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.
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).
static QString pointsToWKT(const QgsPointSequence &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
void setPoints(const QgsPointSequence &points)
Resets the line string to match the specified list of points.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
void setX(double x) SIP_HOLDGIL
Sets the point's x-coordinate.
Definition: qgspoint.h:280
Q_GADGET double x
Definition: qgspoint.h:52
void setY(double y) SIP_HOLDGIL
Sets the point's y-coordinate.
Definition: qgspoint.h:291
double z
Definition: qgspoint.h:54
double m
Definition: qgspoint.h:55
double y
Definition: qgspoint.h:53
A rectangle specified with double values.
Definition: qgsrectangle.h:42
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:391
WKB pointer handler.
Definition: qgswkbptr.h:44
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1130
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
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
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:732
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1176
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1080
static Type addM(Type type) SIP_HOLDGIL
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1201
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
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
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
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
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1246
QVector< QgsPoint > QgsPointSequence
int precision
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:31
int vertex
Vertex number.
Definition: qgsvertexid.h:95
int part
Part number.
Definition: qgsvertexid.h:89
int ring
Ring number.
Definition: qgsvertexid.h:92