QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 }
VertexType
Types of vertex.
Definition: qgis.h:1028
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition: qgis.h:930
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:1578
QVector< QgsPoint > QgsPointSequence
void arcTo(QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3)
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