QGIS API Documentation  3.27.0-Master (11ef3e5184)
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  if ( mHasCachedSummedUpArea )
1351  {
1352  sum += mSummedUpArea;
1353  return;
1354  }
1355 
1356  int maxIndex = numPoints() - 2;
1357  mSummedUpArea = 0;
1358  for ( int i = 0; i < maxIndex; i += 2 )
1359  {
1360  QgsPoint p1( mX[i], mY[i] );
1361  QgsPoint p2( mX[i + 1], mY[i + 1] );
1362  QgsPoint p3( mX[i + 2], mY[i + 2] );
1363 
1364  //segment is a full circle, p2 is the center point
1365  if ( p1 == p3 )
1366  {
1367  double r2 = QgsGeometryUtils::sqrDistance2D( p1, p2 ) / 4.0;
1368  mSummedUpArea += M_PI * r2;
1369  continue;
1370  }
1371 
1372  mSummedUpArea += 0.5 * ( mX[i] * mY[i + 2] - mY[i] * mX[i + 2] );
1373 
1374  //calculate area between circle and chord, then sum / subtract from total area
1375  double midPointX = ( p1.x() + p3.x() ) / 2.0;
1376  double midPointY = ( p1.y() + p3.y() ) / 2.0;
1377 
1378  double radius, centerX, centerY;
1379  QgsGeometryUtils::circleCenterRadius( p1, p2, p3, radius, centerX, centerY );
1380 
1381  double d = std::sqrt( QgsGeometryUtils::sqrDistance2D( QgsPoint( centerX, centerY ), QgsPoint( midPointX, midPointY ) ) );
1382  double r2 = radius * radius;
1383 
1384  if ( d > radius )
1385  {
1386  //d cannot be greater than radius, something must be wrong...
1387  continue;
1388  }
1389 
1390  bool circlePointLeftOfLine = QgsGeometryUtils::leftOfLine( p2.x(), p2.y(), p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
1391  bool centerPointLeftOfLine = QgsGeometryUtils::leftOfLine( centerX, centerY, p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
1392 
1393  double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - M_1_PI * std::asin( d / radius );
1394  double circleChordArea = 0;
1395  if ( circlePointLeftOfLine == centerPointLeftOfLine )
1396  {
1397  circleChordArea = M_PI * r2 * ( 1 - cov );
1398  }
1399  else
1400  {
1401  circleChordArea = M_PI * r2 * cov;
1402  }
1403 
1404  if ( !circlePointLeftOfLine )
1405  {
1406  mSummedUpArea += circleChordArea;
1407  }
1408  else
1409  {
1410  mSummedUpArea -= circleChordArea;
1411  }
1412  }
1413 
1414  mHasCachedSummedUpArea = true;
1415  sum += mSummedUpArea;
1416 }
1417 
1419 {
1420  return true;
1421 }
1422 
1423 double QgsCircularString::closestPointOnArc( double x1, double y1, double x2, double y2, double x3, double y3,
1424  const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon )
1425 {
1426  double radius, centerX, centerY;
1427  QgsPoint pt1( x1, y1 );
1428  QgsPoint pt2( x2, y2 );
1429  QgsPoint pt3( x3, y3 );
1430 
1431  QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
1432  double angle = QgsGeometryUtils::ccwAngle( pt.y() - centerY, pt.x() - centerX );
1433  double angle1 = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
1434  double angle2 = QgsGeometryUtils::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
1435  double angle3 = QgsGeometryUtils::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
1436 
1437  bool clockwise = QgsGeometryUtils::circleClockwise( angle1, angle2, angle3 );
1438 
1439  if ( QgsGeometryUtils::angleOnCircle( angle, angle1, angle2, angle3 ) )
1440  {
1441  //get point on line center -> pt with distance radius
1442  segmentPt = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), pt, radius );
1443 
1444  //vertexAfter
1445  vertexAfter.vertex = QgsGeometryUtils::circleAngleBetween( angle, angle1, angle2, clockwise ) ? 1 : 2;
1446  }
1447  else
1448  {
1449  double distPtPt1 = QgsGeometryUtils::sqrDistance2D( pt, pt1 );
1450  double distPtPt3 = QgsGeometryUtils::sqrDistance2D( pt, pt3 );
1451  segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
1452  vertexAfter.vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
1453  }
1454 
1455  double sqrDistance = QgsGeometryUtils::sqrDistance2D( segmentPt, pt );
1456  //prevent rounding errors if the point is directly on the segment
1457  if ( qgsDoubleNear( sqrDistance, 0.0, epsilon ) )
1458  {
1459  segmentPt.setX( pt.x() );
1460  segmentPt.setY( pt.y() );
1461  sqrDistance = 0.0;
1462  }
1463 
1464  if ( leftOf )
1465  {
1466  double sqrDistancePointToCenter = ( pt.x() - centerX ) * ( pt.x() - centerX ) + ( pt.y() - centerY ) * ( pt.y() - centerY );
1467  *leftOf = clockwise ? ( sqrDistancePointToCenter > radius * radius ? -1 : 1 )
1468  : ( sqrDistancePointToCenter < radius * radius ? -1 : 1 );
1469  }
1470 
1471  return sqrDistance;
1472 }
1473 
1474 void QgsCircularString::insertVertexBetween( int after, int before, int pointOnCircle )
1475 {
1476  double xAfter = mX.at( after );
1477  double yAfter = mY.at( after );
1478  double xBefore = mX.at( before );
1479  double yBefore = mY.at( before );
1480  double xOnCircle = mX.at( pointOnCircle );
1481  double yOnCircle = mY.at( pointOnCircle );
1482 
1483  double radius, centerX, centerY;
1484  QgsGeometryUtils::circleCenterRadius( QgsPoint( xAfter, yAfter ), QgsPoint( xBefore, yBefore ), QgsPoint( xOnCircle, yOnCircle ), radius, centerX, centerY );
1485 
1486  double x = ( xAfter + xBefore ) / 2.0;
1487  double y = ( yAfter + yBefore ) / 2.0;
1488 
1489  QgsPoint newVertex = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), QgsPoint( x, y ), radius );
1490  mX.insert( before, newVertex.x() );
1491  mY.insert( before, newVertex.y() );
1492 
1493  if ( is3D() )
1494  {
1495  mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
1496  }
1497  if ( isMeasure() )
1498  {
1499  mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
1500  }
1501  clearCache();
1502 }
1503 
1505 {
1506  if ( numPoints() < 3 )
1507  {
1508  //undefined
1509  return 0.0;
1510  }
1511 
1512  int before = vId.vertex - 1;
1513  int vertex = vId.vertex;
1514  int after = vId.vertex + 1;
1515 
1516  if ( vId.vertex % 2 != 0 ) // a curve vertex
1517  {
1518  if ( vId.vertex >= 1 && vId.vertex < numPoints() - 1 )
1519  {
1520  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[before], mY[before] ),
1521  QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[after], mY[after] ) );
1522  }
1523  }
1524  else //a point vertex
1525  {
1526  if ( vId.vertex == 0 )
1527  {
1528  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[0], mY[0] ), QgsPoint( mX[0], mY[0] ),
1529  QgsPoint( mX[1], mY[1] ), QgsPoint( mX[2], mY[2] ) );
1530  }
1531  if ( vId.vertex >= numPoints() - 1 )
1532  {
1533  int a = numPoints() - 3;
1534  int b = numPoints() - 2;
1535  int c = numPoints() - 1;
1536  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[c], mY[c] ), QgsPoint( mX[a], mY[a] ),
1537  QgsPoint( mX[b], mY[b] ), QgsPoint( mX[c], mY[c] ) );
1538  }
1539  else
1540  {
1541  if ( vId.vertex + 2 > numPoints() - 1 )
1542  {
1543  return 0.0;
1544  }
1545 
1546  int vertex1 = vId.vertex - 2;
1547  int vertex2 = vId.vertex - 1;
1548  int vertex3 = vId.vertex;
1549  double angle1 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1550  QgsPoint( mX[vertex1], mY[vertex1] ), QgsPoint( mX[vertex2], mY[vertex2] ), QgsPoint( mX[vertex3], mY[vertex3] ) );
1551  int vertex4 = vId.vertex + 1;
1552  int vertex5 = vId.vertex + 2;
1553  double angle2 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1554  QgsPoint( mX[vertex3], mY[vertex3] ), QgsPoint( mX[vertex4], mY[vertex4] ), QgsPoint( mX[vertex5], mY[vertex5] ) );
1555  return QgsGeometryUtils::averageAngle( angle1, angle2 );
1556  }
1557  }
1558  return 0.0;
1559 }
1560 
1562 {
1563  if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 2 )
1564  return 0.0;
1565 
1566  if ( startVertex.vertex % 2 == 1 )
1567  return 0.0; // curve point?
1568 
1569  double x1 = mX.at( startVertex.vertex );
1570  double y1 = mY.at( startVertex.vertex );
1571  double x2 = mX.at( startVertex.vertex + 1 );
1572  double y2 = mY.at( startVertex.vertex + 1 );
1573  double x3 = mX.at( startVertex.vertex + 2 );
1574  double y3 = mY.at( startVertex.vertex + 2 );
1575  return QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1576 }
1577 
1579 {
1580  QgsCircularString *copy = clone();
1581  std::reverse( copy->mX.begin(), copy->mX.end() );
1582  std::reverse( copy->mY.begin(), copy->mY.end() );
1583  if ( is3D() )
1584  {
1585  std::reverse( copy->mZ.begin(), copy->mZ.end() );
1586  }
1587  if ( isMeasure() )
1588  {
1589  std::reverse( copy->mM.begin(), copy->mM.end() );
1590  }
1591  return copy;
1592 }
1593 
1594 QgsPoint *QgsCircularString::interpolatePoint( const double distance ) const
1595 {
1596  if ( distance < 0 )
1597  return nullptr;
1598 
1599  double distanceTraversed = 0;
1600  const int totalPoints = numPoints();
1601  if ( totalPoints == 0 )
1602  return nullptr;
1603 
1605  if ( is3D() )
1606  pointType = QgsWkbTypes::PointZ;
1607  if ( isMeasure() )
1608  pointType = QgsWkbTypes::addM( pointType );
1609 
1610  const double *x = mX.constData();
1611  const double *y = mY.constData();
1612  const double *z = is3D() ? mZ.constData() : nullptr;
1613  const double *m = isMeasure() ? mM.constData() : nullptr;
1614 
1615  double prevX = *x++;
1616  double prevY = *y++;
1617  double prevZ = z ? *z++ : 0.0;
1618  double prevM = m ? *m++ : 0.0;
1619 
1620  if ( qgsDoubleNear( distance, 0.0 ) )
1621  {
1622  return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1623  }
1624 
1625  for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1626  {
1627  double x1 = prevX;
1628  double y1 = prevY;
1629  double z1 = prevZ;
1630  double m1 = prevM;
1631 
1632  double x2 = *x++;
1633  double y2 = *y++;
1634  double z2 = z ? *z++ : 0.0;
1635  double m2 = m ? *m++ : 0.0;
1636 
1637  double x3 = *x++;
1638  double y3 = *y++;
1639  double z3 = z ? *z++ : 0.0;
1640  double m3 = m ? *m++ : 0.0;
1641 
1642  const double segmentLength = QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1643  if ( distance < distanceTraversed + segmentLength || qgsDoubleNear( distance, distanceTraversed + segmentLength ) )
1644  {
1645  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1646  const double distanceToPoint = std::min( distance - distanceTraversed, segmentLength );
1647  return new QgsPoint( QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1648  QgsPoint( pointType, x2, y2, z2, m2 ),
1649  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToPoint ) );
1650  }
1651 
1652  distanceTraversed += segmentLength;
1653 
1654  prevX = x3;
1655  prevY = y3;
1656  prevZ = z3;
1657  prevM = m3;
1658  }
1659 
1660  return nullptr;
1661 }
1662 
1663 QgsCircularString *QgsCircularString::curveSubstring( double startDistance, double endDistance ) const
1664 {
1665  if ( startDistance < 0 && endDistance < 0 )
1666  return createEmptyWithSameType();
1667 
1668  endDistance = std::max( startDistance, endDistance );
1669 
1670  const int totalPoints = numPoints();
1671  if ( totalPoints == 0 )
1672  return clone();
1673 
1674  QVector< QgsPoint > substringPoints;
1675  substringPoints.reserve( totalPoints );
1676 
1678  if ( is3D() )
1679  pointType = QgsWkbTypes::PointZ;
1680  if ( isMeasure() )
1681  pointType = QgsWkbTypes::addM( pointType );
1682 
1683  const double *x = mX.constData();
1684  const double *y = mY.constData();
1685  const double *z = is3D() ? mZ.constData() : nullptr;
1686  const double *m = isMeasure() ? mM.constData() : nullptr;
1687 
1688  double distanceTraversed = 0;
1689  double prevX = *x++;
1690  double prevY = *y++;
1691  double prevZ = z ? *z++ : 0.0;
1692  double prevM = m ? *m++ : 0.0;
1693  bool foundStart = false;
1694 
1695  if ( startDistance < 0 )
1696  startDistance = 0;
1697 
1698  for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1699  {
1700  double x1 = prevX;
1701  double y1 = prevY;
1702  double z1 = prevZ;
1703  double m1 = prevM;
1704 
1705  double x2 = *x++;
1706  double y2 = *y++;
1707  double z2 = z ? *z++ : 0.0;
1708  double m2 = m ? *m++ : 0.0;
1709 
1710  double x3 = *x++;
1711  double y3 = *y++;
1712  double z3 = z ? *z++ : 0.0;
1713  double m3 = m ? *m++ : 0.0;
1714 
1715  bool addedSegmentEnd = false;
1716  const double segmentLength = QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1717  if ( distanceTraversed <= startDistance && startDistance < distanceTraversed + segmentLength )
1718  {
1719  // start point falls on this segment
1720  const double distanceToStart = startDistance - distanceTraversed;
1721  const QgsPoint startPoint = QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1722  QgsPoint( pointType, x2, y2, z2, m2 ),
1723  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToStart );
1724 
1725  // does end point also fall on this segment?
1726  const bool endPointOnSegment = distanceTraversed + segmentLength > endDistance;
1727  if ( endPointOnSegment )
1728  {
1729  const double distanceToEnd = endDistance - distanceTraversed;
1730  const double midPointDistance = ( distanceToEnd - distanceToStart ) * 0.5 + distanceToStart;
1731  substringPoints << startPoint
1732  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1733  QgsPoint( pointType, x2, y2, z2, m2 ),
1734  QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1735  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1736  QgsPoint( pointType, x2, y2, z2, m2 ),
1737  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1738  addedSegmentEnd = true;
1739  }
1740  else
1741  {
1742  const double midPointDistance = ( segmentLength - distanceToStart ) * 0.5 + distanceToStart;
1743  substringPoints << startPoint
1744  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1745  QgsPoint( pointType, x2, y2, z2, m2 ),
1746  QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1747  << QgsPoint( pointType, x3, y3, z3, m3 );
1748  addedSegmentEnd = true;
1749  }
1750  foundStart = true;
1751  }
1752  if ( !addedSegmentEnd && foundStart && ( distanceTraversed + segmentLength > endDistance ) )
1753  {
1754  // end point falls on this segment
1755  const double distanceToEnd = endDistance - distanceTraversed;
1756  // add mid point, at half way along this arc, then add the interpolated end point
1757  substringPoints << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1758  QgsPoint( pointType, x2, y2, z2, m2 ),
1759  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd / 2.0 )
1760 
1761  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1762  QgsPoint( pointType, x2, y2, z2, m2 ),
1763  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1764  }
1765  else if ( !addedSegmentEnd && foundStart )
1766  {
1767  substringPoints << QgsPoint( pointType, x2, y2, z2, m2 )
1768  << QgsPoint( pointType, x3, y3, z3, m3 );
1769  }
1770 
1771  prevX = x3;
1772  prevY = y3;
1773  prevZ = z3;
1774  prevM = m3;
1775  distanceTraversed += segmentLength;
1776  if ( distanceTraversed >= endDistance )
1777  break;
1778  }
1779 
1780  // start point is the last node
1781  if ( !foundStart && qgsDoubleNear( distanceTraversed, startDistance ) )
1782  {
1783  substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM )
1784  << QgsPoint( pointType, prevX, prevY, prevZ, prevM )
1785  << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1786  }
1787 
1788  std::unique_ptr< QgsCircularString > result = std::make_unique< QgsCircularString >();
1789  result->setPoints( substringPoints );
1790  return result.release();
1791 }
1792 
1793 bool QgsCircularString::addZValue( double zValue )
1794 {
1795  if ( QgsWkbTypes::hasZ( mWkbType ) )
1796  return false;
1797 
1798  clearCache();
1800 
1801  int nPoints = numPoints();
1802  mZ.clear();
1803  mZ.reserve( nPoints );
1804  for ( int i = 0; i < nPoints; ++i )
1805  {
1806  mZ << zValue;
1807  }
1808  return true;
1809 }
1810 
1811 bool QgsCircularString::addMValue( double mValue )
1812 {
1813  if ( QgsWkbTypes::hasM( mWkbType ) )
1814  return false;
1815 
1816  clearCache();
1818 
1819  int nPoints = numPoints();
1820  mM.clear();
1821  mM.reserve( nPoints );
1822  for ( int i = 0; i < nPoints; ++i )
1823  {
1824  mM << mValue;
1825  }
1826  return true;
1827 }
1828 
1830 {
1831  if ( !QgsWkbTypes::hasZ( mWkbType ) )
1832  return false;
1833 
1834  clearCache();
1835 
1837  mZ.clear();
1838  return true;
1839 }
1840 
1842 {
1843  if ( !QgsWkbTypes::hasM( mWkbType ) )
1844  return false;
1845 
1846  clearCache();
1847 
1849  mM.clear();
1850  return true;
1851 }
1852 
1854 {
1855  std::swap( mX, mY );
1856  clearCache();
1857 }
VertexType
Types of vertex.
Definition: qgis.h:1518
TransformDirection
Flags for raster layer temporal capabilities.
Definition: qgis.h:1320
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 mHasCachedSummedUpArea
Definition: qgscurve.h:344
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:317
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
double mSummedUpArea
Definition: qgscurve.h:345
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:45
void setPoints(size_t size, const double *x, const double *y, const double *z=nullptr, const double *m=nullptr)
Resets the line string to match the specified point data.
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:2527
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