QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
qgscircularstring.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscircularstring.cpp
3  -----------------------
4  begin : September 2014
5  copyright : (C) 2014 by Marco Hugentobler
6  email : marco at sourcepole dot ch
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgscircularstring.h"
19 #include "qgsapplication.h"
20 #include "qgscoordinatetransform.h"
21 #include "qgsgeometryutils.h"
22 #include "qgslinestring.h"
23 #include "qgsmaptopixel.h"
24 #include "qgspoint.h"
25 #include "qgswkbptr.h"
26 #include "qgslogger.h"
27 
28 #include <QJsonObject>
29 #include <QPainter>
30 #include <QPainterPath>
31 #include <memory>
32 #include <nlohmann/json.hpp>
33 
35 {
37 }
38 
40 {
41  //get wkb type from first point
42  bool hasZ = p1.is3D();
43  bool hasM = p1.isMeasure();
45 
46  mX.resize( 3 );
47  mX[ 0 ] = p1.x();
48  mX[ 1 ] = p2.x();
49  mX[ 2 ] = p3.x();
50  mY.resize( 3 );
51  mY[ 0 ] = p1.y();
52  mY[ 1 ] = p2.y();
53  mY[ 2 ] = p3.y();
54  if ( hasZ )
55  {
57  mZ.resize( 3 );
58  mZ[ 0 ] = p1.z();
59  mZ[ 1 ] = p2.z();
60  mZ[ 2 ] = p3.z();
61  }
62  if ( hasM )
63  {
65  mM.resize( 3 );
66  mM[ 0 ] = p1.m();
67  mM[ 1 ] = p2.m();
68  mM[ 2 ] = p3.m();
69  }
70 }
71 
72 QgsCircularString QgsCircularString::fromTwoPointsAndCenter( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, const bool useShortestArc )
73 {
74  const QgsPoint midPoint = QgsGeometryUtils::segmentMidPointFromCenter( p1, p2, center, useShortestArc );
75  return QgsCircularString( p1, midPoint, p2 );
76 }
77 
78 bool QgsCircularString::equals( const QgsCurve &other ) const
79 {
80  const QgsCircularString *otherLine = dynamic_cast< const QgsCircularString * >( &other );
81  if ( !otherLine )
82  return false;
83 
84  if ( mWkbType != otherLine->mWkbType )
85  return false;
86 
87  if ( mX.count() != otherLine->mX.count() )
88  return false;
89 
90  for ( int i = 0; i < mX.count(); ++i )
91  {
92  if ( !qgsDoubleNear( mX.at( i ), otherLine->mX.at( i ) )
93  || !qgsDoubleNear( mY.at( i ), otherLine->mY.at( i ) ) )
94  return false;
95 
96  if ( is3D() && !qgsDoubleNear( mZ.at( i ), otherLine->mZ.at( i ) ) )
97  return false;
98 
99  if ( isMeasure() && !qgsDoubleNear( mM.at( i ), otherLine->mM.at( i ) ) )
100  return false;
101  }
102 
103  return true;
104 }
105 
107 {
108  auto result = qgis::make_unique< QgsCircularString >();
109  result->mWkbType = mWkbType;
110  return result.release();
111 }
112 
114 {
115  return QStringLiteral( "CircularString" );
116 }
117 
119 {
120  return 1;
121 }
122 
124 {
125  return new QgsCircularString( *this );
126 }
127 
129 {
131  mX.clear();
132  mY.clear();
133  mZ.clear();
134  mM.clear();
135  clearCache();
136 }
137 
139 {
140  QgsRectangle bbox;
141  int nPoints = numPoints();
142  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
143  {
144  if ( i == 0 )
145  {
146  bbox = segmentBoundingBox( QgsPoint( mX[i], mY[i] ), QgsPoint( mX[i + 1], mY[i + 1] ), QgsPoint( mX[i + 2], mY[i + 2] ) );
147  }
148  else
149  {
150  QgsRectangle segmentBox = segmentBoundingBox( QgsPoint( mX[i], mY[i] ), QgsPoint( mX[i + 1], mY[i + 1] ), QgsPoint( mX[i + 2], mY[i + 2] ) );
151  bbox.combineExtentWith( segmentBox );
152  }
153  }
154 
155  if ( nPoints > 0 && nPoints % 2 == 0 )
156  {
157  if ( nPoints == 2 )
158  {
159  bbox.combineExtentWith( mX[ 0 ], mY[ 0 ] );
160  }
161  bbox.combineExtentWith( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
162  }
163  return bbox;
164 }
165 
166 QgsRectangle QgsCircularString::segmentBoundingBox( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3 )
167 {
168  double centerX, centerY, radius;
169  QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
170 
171  double p1Angle = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
172  double p2Angle = QgsGeometryUtils::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
173  double p3Angle = QgsGeometryUtils::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
174 
175  //start point, end point and compass points in between can be on bounding box
176  QgsRectangle bbox( pt1.x(), pt1.y(), pt1.x(), pt1.y() );
177  bbox.combineExtentWith( pt3.x(), pt3.y() );
178 
179  QgsPointSequence compassPoints = compassPointsOnSegment( p1Angle, p2Angle, p3Angle, centerX, centerY, radius );
180  QgsPointSequence::const_iterator cpIt = compassPoints.constBegin();
181  for ( ; cpIt != compassPoints.constEnd(); ++cpIt )
182  {
183  bbox.combineExtentWith( cpIt->x(), cpIt->y() );
184  }
185  return bbox;
186 }
187 
188 QgsPointSequence QgsCircularString::compassPointsOnSegment( double p1Angle, double p2Angle, double p3Angle, double centerX, double centerY, double radius )
189 {
190  QgsPointSequence pointList;
191 
192  QgsPoint nPoint( centerX, centerY + radius );
193  QgsPoint ePoint( centerX + radius, centerY );
194  QgsPoint sPoint( centerX, centerY - radius );
195  QgsPoint wPoint( centerX - radius, centerY );
196 
197  if ( p3Angle >= p1Angle )
198  {
199  if ( p2Angle > p1Angle && p2Angle < p3Angle )
200  {
201  if ( p1Angle <= 90 && p3Angle >= 90 )
202  {
203  pointList.append( nPoint );
204  }
205  if ( p1Angle <= 180 && p3Angle >= 180 )
206  {
207  pointList.append( wPoint );
208  }
209  if ( p1Angle <= 270 && p3Angle >= 270 )
210  {
211  pointList.append( sPoint );
212  }
213  }
214  else
215  {
216  pointList.append( ePoint );
217  if ( p1Angle >= 90 || p3Angle <= 90 )
218  {
219  pointList.append( nPoint );
220  }
221  if ( p1Angle >= 180 || p3Angle <= 180 )
222  {
223  pointList.append( wPoint );
224  }
225  if ( p1Angle >= 270 || p3Angle <= 270 )
226  {
227  pointList.append( sPoint );
228  }
229  }
230  }
231  else
232  {
233  if ( p2Angle < p1Angle && p2Angle > p3Angle )
234  {
235  if ( p1Angle >= 270 && p3Angle <= 270 )
236  {
237  pointList.append( sPoint );
238  }
239  if ( p1Angle >= 180 && p3Angle <= 180 )
240  {
241  pointList.append( wPoint );
242  }
243  if ( p1Angle >= 90 && p3Angle <= 90 )
244  {
245  pointList.append( nPoint );
246  }
247  }
248  else
249  {
250  pointList.append( ePoint );
251  if ( p1Angle <= 270 || p3Angle >= 270 )
252  {
253  pointList.append( sPoint );
254  }
255  if ( p1Angle <= 180 || p3Angle >= 180 )
256  {
257  pointList.append( wPoint );
258  }
259  if ( p1Angle <= 90 || p3Angle >= 90 )
260  {
261  pointList.append( nPoint );
262  }
263  }
264  }
265  return pointList;
266 }
267 
269 {
270  if ( !wkbPtr )
271  return false;
272 
273  QgsWkbTypes::Type type = wkbPtr.readHeader();
275  {
276  return false;
277  }
278  clearCache();
279  mWkbType = type;
280 
281  //type
282  bool hasZ = is3D();
283  bool hasM = isMeasure();
284  int nVertices = 0;
285  wkbPtr >> nVertices;
286  mX.resize( nVertices );
287  mY.resize( nVertices );
288  hasZ ? mZ.resize( nVertices ) : mZ.clear();
289  hasM ? mM.resize( nVertices ) : mM.clear();
290  for ( int i = 0; i < nVertices; ++i )
291  {
292  wkbPtr >> mX[i];
293  wkbPtr >> mY[i];
294  if ( hasZ )
295  {
296  wkbPtr >> mZ[i];
297  }
298  if ( hasM )
299  {
300  wkbPtr >> mM[i];
301  }
302  }
303 
304  return true;
305 }
306 
307 bool QgsCircularString::fromWkt( const QString &wkt )
308 {
309  clear();
310 
311  QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
312 
313  if ( QgsWkbTypes::flatType( parts.first ) != QgsWkbTypes::CircularString )
314  return false;
315  mWkbType = parts.first;
316 
317  if ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 )
318  return true;
319 
320  setPoints( QgsGeometryUtils::pointsFromWKT( parts.second, is3D(), isMeasure() ) );
321  return true;
322 }
323 
324 QByteArray QgsCircularString::asWkb() const
325 {
326  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
327  binarySize += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
328 
329  QByteArray wkbArray;
330  wkbArray.resize( binarySize );
331  QgsWkbPtr wkb( wkbArray );
332  wkb << static_cast<char>( QgsApplication::endian() );
333  wkb << static_cast<quint32>( wkbType() );
334  QgsPointSequence pts;
335  points( pts );
336  QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure() );
337  return wkbArray;
338 }
339 
341 {
342  QString wkt = wktTypeStr() + ' ';
343 
344  if ( isEmpty() )
345  wkt += QStringLiteral( "EMPTY" );
346  else
347  {
348  QgsPointSequence pts;
349  points( pts );
350  wkt += QgsGeometryUtils::pointsToWKT( pts, precision, is3D(), isMeasure() );
351  }
352  return wkt;
353 }
354 
355 QDomElement QgsCircularString::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
356 {
357  // GML2 does not support curves
358  std::unique_ptr< QgsLineString > line( curveToLine() );
359  QDomElement gml = line->asGml2( doc, precision, ns, axisOrder );
360  return gml;
361 }
362 
363 QDomElement QgsCircularString::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
364 {
365  QgsPointSequence pts;
366  points( pts );
367 
368  QDomElement elemCurve = doc.createElementNS( ns, QStringLiteral( "Curve" ) );
369 
370  if ( isEmpty() )
371  return elemCurve;
372 
373  QDomElement elemSegments = doc.createElementNS( ns, QStringLiteral( "segments" ) );
374  QDomElement elemArcString = doc.createElementNS( ns, QStringLiteral( "ArcString" ) );
375  elemArcString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D(), axisOrder ) );
376  elemSegments.appendChild( elemArcString );
377  elemCurve.appendChild( elemSegments );
378  return elemCurve;
379 }
380 
381 
383 {
384  // GeoJSON does not support curves
385  std::unique_ptr< QgsLineString > line( curveToLine() );
386  return line->asJsonObject( precision );
387 }
388 
390 {
391  return mX.isEmpty();
392 }
393 
394 //curve interface
396 {
397  int nPoints = numPoints();
398  double length = 0;
399  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
400  {
401  length += QgsGeometryUtils::circleLength( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2] );
402  }
403  return length;
404 }
405 
407 {
408  if ( numPoints() < 1 )
409  {
410  return QgsPoint();
411  }
412  return pointN( 0 );
413 }
414 
416 {
417  if ( numPoints() < 1 )
418  {
419  return QgsPoint();
420  }
421  return pointN( numPoints() - 1 );
422 }
423 
425 {
426  QgsLineString *line = new QgsLineString();
428  int nPoints = numPoints();
429 
430  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
431  {
432  QgsGeometryUtils::segmentizeArc( pointN( i ), pointN( i + 1 ), pointN( i + 2 ), points, tolerance, toleranceType, is3D(), isMeasure() );
433  }
434 
435  line->setPoints( points );
436  return line;
437 }
438 
439 QgsCircularString *QgsCircularString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
440 {
441  // prepare result
442  std::unique_ptr<QgsCircularString> result { createEmptyWithSameType() };
443 
444  bool res = snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
445  result->mX, result->mY, result->mZ, result->mM );
446  if ( res )
447  return result.release();
448  else
449  return nullptr;
450 }
451 
452 bool QgsCircularString::removeDuplicateNodes( double epsilon, bool useZValues )
453 {
454  if ( mX.count() <= 3 )
455  return false; // don't create degenerate lines
456  bool result = false;
457  double prevX = mX.at( 0 );
458  double prevY = mY.at( 0 );
459  bool hasZ = is3D();
460  bool useZ = hasZ && useZValues;
461  double prevZ = useZ ? mZ.at( 0 ) : 0;
462  int i = 1;
463  int remaining = mX.count();
464  // we have to consider points in pairs, since a segment can validly have the same start and
465  // end if it has a different curve point
466  while ( i + 1 < remaining )
467  {
468  double currentCurveX = mX.at( i );
469  double currentCurveY = mY.at( i );
470  double currentX = mX.at( i + 1 );
471  double currentY = mY.at( i + 1 );
472  double currentZ = useZ ? mZ.at( i + 1 ) : 0;
473  if ( qgsDoubleNear( currentCurveX, prevX, epsilon ) &&
474  qgsDoubleNear( currentCurveY, prevY, epsilon ) &&
475  qgsDoubleNear( currentX, prevX, epsilon ) &&
476  qgsDoubleNear( currentY, prevY, epsilon ) &&
477  ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
478  {
479  result = true;
480  // remove point
481  mX.removeAt( i );
482  mX.removeAt( i );
483  mY.removeAt( i );
484  mY.removeAt( i );
485  if ( hasZ )
486  {
487  mZ.removeAt( i );
488  mZ.removeAt( i );
489  }
490  remaining -= 2;
491  }
492  else
493  {
494  prevX = currentX;
495  prevY = currentY;
496  prevZ = currentZ;
497  i += 2;
498  }
499  }
500  return result;
501 }
502 
504 {
505  return std::min( mX.size(), mY.size() );
506 }
507 
509 {
510  if ( i < 0 || std::min( mX.size(), mY.size() ) <= i )
511  {
512  return QgsPoint();
513  }
514 
515  double x = mX.at( i );
516  double y = mY.at( i );
517  double z = 0;
518  double m = 0;
519 
520  if ( is3D() )
521  {
522  z = mZ.at( i );
523  }
524  if ( isMeasure() )
525  {
526  m = mM.at( i );
527  }
528 
530  if ( is3D() && isMeasure() )
531  {
533  }
534  else if ( is3D() )
535  {
537  }
538  else if ( isMeasure() )
539  {
541  }
542  return QgsPoint( t, x, y, z, m );
543 }
544 
545 double QgsCircularString::xAt( int index ) const
546 {
547  if ( index >= 0 && index < mX.size() )
548  return mX.at( index );
549  else
550  return 0.0;
551 }
552 
553 double QgsCircularString::yAt( int index ) const
554 {
555  if ( index >= 0 && index < mY.size() )
556  return mY.at( index );
557  else
558  return 0.0;
559 }
560 
561 void QgsCircularString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
562 {
563  bool hasZ = is3D();
564  bool hasM = isMeasure();
565  int size = mX.size();
566 
567  double *srcX = mX.data(); // clazy:exclude=detaching-member
568  double *srcY = mY.data(); // clazy:exclude=detaching-member
569  double *srcM = hasM ? mM.data() : nullptr; // clazy:exclude=detaching-member
570  double *srcZ = hasZ ? mZ.data() : nullptr; // clazy:exclude=detaching-member
571 
572  double *destX = srcX;
573  double *destY = srcY;
574  double *destM = srcM;
575  double *destZ = srcZ;
576 
577  int filteredPoints = 0;
578  for ( int i = 0; i < size; ++i )
579  {
580  double x = *srcX++;
581  double y = *srcY++;
582  double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
583  double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
584 
585  if ( filter( QgsPoint( x, y, z, m ) ) )
586  {
587  filteredPoints++;
588  *destX++ = x;
589  *destY++ = y;
590  if ( hasM )
591  *destM++ = m;
592  if ( hasZ )
593  *destZ++ = z;
594  }
595  }
596 
597  mX.resize( filteredPoints );
598  mY.resize( filteredPoints );
599  if ( hasZ )
600  mZ.resize( filteredPoints );
601  if ( hasM )
602  mM.resize( filteredPoints );
603 
604  clearCache();
605 }
606 
607 void QgsCircularString::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
608 {
609  bool hasZ = is3D();
610  bool hasM = isMeasure();
611  int size = mX.size();
612 
613  double *srcX = mX.data();
614  double *srcY = mY.data();
615  double *srcM = hasM ? mM.data() : nullptr;
616  double *srcZ = hasZ ? mZ.data() : nullptr;
617 
618  for ( int i = 0; i < size; ++i )
619  {
620  double x = *srcX;
621  double y = *srcY;
622  double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
623  double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
624  QgsPoint res = transform( QgsPoint( x, y, z, m ) );
625  *srcX++ = res.x();
626  *srcY++ = res.y();
627  if ( hasM )
628  *srcM++ = res.m();
629  if ( hasZ )
630  *srcZ++ = res.z();
631  }
632  clearCache();
633 }
634 
636 {
637  pts.clear();
638  int nPts = numPoints();
639  for ( int i = 0; i < nPts; ++i )
640  {
641  pts.push_back( pointN( i ) );
642  }
643 }
644 
646 {
647  clearCache();
648 
649  if ( points.empty() )
650  {
652  mX.clear();
653  mY.clear();
654  mZ.clear();
655  mM.clear();
656  return;
657  }
658 
659  //get wkb type from first point
660  const QgsPoint &firstPt = points.at( 0 );
661  bool hasZ = firstPt.is3D();
662  bool hasM = firstPt.isMeasure();
663 
665 
666  mX.resize( points.size() );
667  mY.resize( points.size() );
668  if ( hasZ )
669  {
670  mZ.resize( points.size() );
671  }
672  else
673  {
674  mZ.clear();
675  }
676  if ( hasM )
677  {
678  mM.resize( points.size() );
679  }
680  else
681  {
682  mM.clear();
683  }
684 
685  for ( int i = 0; i < points.size(); ++i )
686  {
687  mX[i] = points[i].x();
688  mY[i] = points[i].y();
689  if ( hasZ )
690  {
691  double z = points.at( i ).z();
692  mZ[i] = std::isnan( z ) ? 0 : z;
693  }
694  if ( hasM )
695  {
696  double m = points.at( i ).m();
697  mM[i] = std::isnan( m ) ? 0 : m;
698  }
699  }
700 }
701 
702 void QgsCircularString::draw( QPainter &p ) const
703 {
704  QPainterPath path;
705  addToPainterPath( path );
706  p.drawPath( path );
707 }
708 
710 {
711  clearCache();
712 
713  double *zArray = mZ.data();
714 
715  bool hasZ = is3D();
716  int nPoints = numPoints();
717  bool useDummyZ = !hasZ || !transformZ;
718  if ( useDummyZ )
719  {
720  zArray = new double[nPoints];
721  for ( int i = 0; i < nPoints; ++i )
722  {
723  zArray[i] = 0;
724  }
725  }
726  ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
727  if ( useDummyZ )
728  {
729  delete[] zArray;
730  }
731 }
732 
733 void QgsCircularString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
734 {
735  clearCache();
736 
737  int nPoints = numPoints();
738  bool hasZ = is3D();
739  bool hasM = isMeasure();
740  for ( int i = 0; i < nPoints; ++i )
741  {
742  qreal x, y;
743  t.map( mX.at( i ), mY.at( i ), &x, &y );
744  mX[i] = x;
745  mY[i] = y;
746  if ( hasZ )
747  {
748  mZ[i] = mZ.at( i ) * zScale + zTranslate;
749  }
750  if ( hasM )
751  {
752  mM[i] = mM.at( i ) * mScale + mTranslate;
753  }
754  }
755 }
756 
757 void QgsCircularString::addToPainterPath( QPainterPath &path ) const
758 {
759  int nPoints = numPoints();
760  if ( nPoints < 1 )
761  {
762  return;
763  }
764 
765  if ( path.isEmpty() || path.currentPosition() != QPointF( mX[0], mY[0] ) )
766  {
767  path.moveTo( QPointF( mX[0], mY[0] ) );
768  }
769 
770  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
771  {
772  QgsPointSequence pt;
773  QgsGeometryUtils::segmentizeArc( QgsPoint( mX[i], mY[i] ), QgsPoint( mX[i + 1], mY[i + 1] ), QgsPoint( mX[i + 2], mY[i + 2] ), pt );
774  for ( int j = 1; j < pt.size(); ++j )
775  {
776  path.lineTo( pt.at( j ).x(), pt.at( j ).y() );
777  }
778 #if 0
779  //arcTo( path, QPointF( mX[i], mY[i] ), QPointF( mX[i + 1], mY[i + 1] ), QPointF( mX[i + 2], mY[i + 2] ) );
780 #endif
781  }
782 
783  //if number of points is even, connect to last point with straight line (even though the circular string is not valid)
784  if ( nPoints % 2 == 0 )
785  {
786  path.lineTo( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
787  }
788 }
789 
790 #if 0
791 void QgsCircularString::arcTo( QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3 )
792 {
793  double centerX, centerY, radius;
794  QgsGeometryUtils::circleCenterRadius( QgsPoint( pt1.x(), pt1.y() ), QgsPoint( pt2.x(), pt2.y() ), QgsPoint( pt3.x(), pt3.y() ),
795  radius, centerX, centerY );
796 
797  double p1Angle = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
798  double sweepAngle = QgsGeometryUtils::sweepAngle( centerX, centerY, pt1.x(), pt1.y(), pt2.x(), pt2.y(), pt3.x(), pt3.y() );
799 
800  double diameter = 2 * radius;
801  path.arcTo( centerX - radius, centerY - radius, diameter, diameter, p1Angle, sweepAngle );
802 }
803 #endif
804 
805 void QgsCircularString::drawAsPolygon( QPainter &p ) const
806 {
807  draw( p );
808 }
809 
810 bool QgsCircularString::insertVertex( QgsVertexId position, const QgsPoint &vertex )
811 {
812  if ( position.vertex >= mX.size() || position.vertex < 1 )
813  {
814  return false;
815  }
816 
817  mX.insert( position.vertex, vertex.x() );
818  mY.insert( position.vertex, vertex.y() );
819  if ( is3D() )
820  {
821  mZ.insert( position.vertex, vertex.z() );
822  }
823  if ( isMeasure() )
824  {
825  mM.insert( position.vertex, vertex.m() );
826  }
827 
828  bool vertexNrEven = ( position.vertex % 2 == 0 );
829  if ( vertexNrEven )
830  {
831  insertVertexBetween( position.vertex - 2, position.vertex - 1, position.vertex );
832  }
833  else
834  {
835  insertVertexBetween( position.vertex, position.vertex + 1, position.vertex - 1 );
836  }
837  clearCache(); //set bounding box invalid
838  return true;
839 }
840 
841 bool QgsCircularString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
842 {
843  if ( position.vertex < 0 || position.vertex >= mX.size() )
844  {
845  return false;
846  }
847 
848  mX[position.vertex] = newPos.x();
849  mY[position.vertex] = newPos.y();
850  if ( is3D() && newPos.is3D() )
851  {
852  mZ[position.vertex] = newPos.z();
853  }
854  if ( isMeasure() && newPos.isMeasure() )
855  {
856  mM[position.vertex] = newPos.m();
857  }
858  clearCache(); //set bounding box invalid
859  return true;
860 }
861 
863 {
864  int nVertices = this->numPoints();
865  if ( nVertices < 4 ) //circular string must have at least 3 vertices
866  {
867  clear();
868  return true;
869  }
870  if ( position.vertex < 0 || position.vertex > ( nVertices - 1 ) )
871  {
872  return false;
873  }
874 
875  if ( position.vertex < ( nVertices - 2 ) )
876  {
877  //remove this and the following vertex
878  deleteVertex( position.vertex + 1 );
879  deleteVertex( position.vertex );
880  }
881  else //remove this and the preceding vertex
882  {
883  deleteVertex( position.vertex );
884  deleteVertex( position.vertex - 1 );
885  }
886 
887  clearCache(); //set bounding box invalid
888  return true;
889 }
890 
892 {
893  mX.remove( i );
894  mY.remove( i );
895  if ( is3D() )
896  {
897  mZ.remove( i );
898  }
899  if ( isMeasure() )
900  {
901  mM.remove( i );
902  }
903  clearCache();
904 }
905 
906 double QgsCircularString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
907 {
908  double minDist = std::numeric_limits<double>::max();
909  QgsPoint minDistSegmentPoint;
910  QgsVertexId minDistVertexAfter;
911  int minDistLeftOf = 0;
912 
913  double currentDist = 0.0;
914 
915  int nPoints = numPoints();
916  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
917  {
918  currentDist = closestPointOnArc( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2], pt, segmentPt, vertexAfter, leftOf, epsilon );
919  if ( currentDist < minDist )
920  {
921  minDist = currentDist;
922  minDistSegmentPoint = segmentPt;
923  minDistVertexAfter.vertex = vertexAfter.vertex + i;
924  if ( leftOf )
925  {
926  minDistLeftOf = *leftOf;
927  }
928  }
929  }
930 
931  if ( minDist == std::numeric_limits<double>::max() )
932  return -1; // error: no segments
933 
934  segmentPt = minDistSegmentPoint;
935  vertexAfter = minDistVertexAfter;
936  vertexAfter.part = 0;
937  vertexAfter.ring = 0;
938  if ( leftOf )
939  {
940  *leftOf = qgsDoubleNear( minDist, 0.0 ) ? 0 : minDistLeftOf;
941  }
942  return minDist;
943 }
944 
945 bool QgsCircularString::pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const
946 {
947  if ( node < 0 || node >= numPoints() )
948  {
949  return false;
950  }
951  point = pointN( node );
952  type = ( node % 2 == 0 ) ? QgsVertexId::SegmentVertex : QgsVertexId::CurveVertex;
953  return true;
954 }
955 
956 void QgsCircularString::sumUpArea( double &sum ) const
957 {
958  int maxIndex = numPoints() - 2;
959 
960  for ( int i = 0; i < maxIndex; i += 2 )
961  {
962  QgsPoint p1( mX[i], mY[i] );
963  QgsPoint p2( mX[i + 1], mY[i + 1] );
964  QgsPoint p3( mX[i + 2], mY[i + 2] );
965 
966  //segment is a full circle, p2 is the center point
967  if ( p1 == p3 )
968  {
969  double r2 = QgsGeometryUtils::sqrDistance2D( p1, p2 ) / 4.0;
970  sum += M_PI * r2;
971  continue;
972  }
973 
974  sum += 0.5 * ( mX[i] * mY[i + 2] - mY[i] * mX[i + 2] );
975 
976  //calculate area between circle and chord, then sum / subtract from total area
977  double midPointX = ( p1.x() + p3.x() ) / 2.0;
978  double midPointY = ( p1.y() + p3.y() ) / 2.0;
979 
980  double radius, centerX, centerY;
981  QgsGeometryUtils::circleCenterRadius( p1, p2, p3, radius, centerX, centerY );
982 
983  double d = std::sqrt( QgsGeometryUtils::sqrDistance2D( QgsPoint( centerX, centerY ), QgsPoint( midPointX, midPointY ) ) );
984  double r2 = radius * radius;
985 
986  if ( d > radius )
987  {
988  //d cannot be greater than radius, something must be wrong...
989  continue;
990  }
991 
992  bool circlePointLeftOfLine = QgsGeometryUtils::leftOfLine( p2.x(), p2.y(), p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
993  bool centerPointLeftOfLine = QgsGeometryUtils::leftOfLine( centerX, centerY, p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
994 
995  double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - M_1_PI * std::asin( d / radius );
996  double circleChordArea = 0;
997  if ( circlePointLeftOfLine == centerPointLeftOfLine )
998  {
999  circleChordArea = M_PI * r2 * ( 1 - cov );
1000  }
1001  else
1002  {
1003  circleChordArea = M_PI * r2 * cov;
1004  }
1005 
1006  if ( !circlePointLeftOfLine )
1007  {
1008  sum += circleChordArea;
1009  }
1010  else
1011  {
1012  sum -= circleChordArea;
1013  }
1014  }
1015 }
1016 
1018 {
1019  return true;
1020 }
1021 
1022 double QgsCircularString::closestPointOnArc( double x1, double y1, double x2, double y2, double x3, double y3,
1023  const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon )
1024 {
1025  double radius, centerX, centerY;
1026  QgsPoint pt1( x1, y1 );
1027  QgsPoint pt2( x2, y2 );
1028  QgsPoint pt3( x3, y3 );
1029 
1030  QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
1031  double angle = QgsGeometryUtils::ccwAngle( pt.y() - centerY, pt.x() - centerX );
1032  double angle1 = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
1033  double angle2 = QgsGeometryUtils::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
1034  double angle3 = QgsGeometryUtils::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
1035 
1036  bool clockwise = QgsGeometryUtils::circleClockwise( angle1, angle2, angle3 );
1037 
1038  if ( QgsGeometryUtils::angleOnCircle( angle, angle1, angle2, angle3 ) )
1039  {
1040  //get point on line center -> pt with distance radius
1041  segmentPt = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), pt, radius );
1042 
1043  //vertexAfter
1044  vertexAfter.vertex = QgsGeometryUtils::circleAngleBetween( angle, angle1, angle2, clockwise ) ? 1 : 2;
1045  }
1046  else
1047  {
1048  double distPtPt1 = QgsGeometryUtils::sqrDistance2D( pt, pt1 );
1049  double distPtPt3 = QgsGeometryUtils::sqrDistance2D( pt, pt3 );
1050  segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
1051  vertexAfter.vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
1052  }
1053 
1054  double sqrDistance = QgsGeometryUtils::sqrDistance2D( segmentPt, pt );
1055  //prevent rounding errors if the point is directly on the segment
1056  if ( qgsDoubleNear( sqrDistance, 0.0, epsilon ) )
1057  {
1058  segmentPt.setX( pt.x() );
1059  segmentPt.setY( pt.y() );
1060  sqrDistance = 0.0;
1061  }
1062 
1063  if ( leftOf )
1064  {
1065  double sqrDistancePointToCenter = ( pt.x() - centerX ) * ( pt.x() - centerX ) + ( pt.y() - centerY ) * ( pt.y() - centerY );
1066  *leftOf = clockwise ? ( sqrDistancePointToCenter > radius * radius ? -1 : 1 )
1067  : ( sqrDistancePointToCenter < radius * radius ? -1 : 1 );
1068  }
1069 
1070  return sqrDistance;
1071 }
1072 
1073 void QgsCircularString::insertVertexBetween( int after, int before, int pointOnCircle )
1074 {
1075  double xAfter = mX.at( after );
1076  double yAfter = mY.at( after );
1077  double xBefore = mX.at( before );
1078  double yBefore = mY.at( before );
1079  double xOnCircle = mX.at( pointOnCircle );
1080  double yOnCircle = mY.at( pointOnCircle );
1081 
1082  double radius, centerX, centerY;
1083  QgsGeometryUtils::circleCenterRadius( QgsPoint( xAfter, yAfter ), QgsPoint( xBefore, yBefore ), QgsPoint( xOnCircle, yOnCircle ), radius, centerX, centerY );
1084 
1085  double x = ( xAfter + xBefore ) / 2.0;
1086  double y = ( yAfter + yBefore ) / 2.0;
1087 
1088  QgsPoint newVertex = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), QgsPoint( x, y ), radius );
1089  mX.insert( before, newVertex.x() );
1090  mY.insert( before, newVertex.y() );
1091 
1092  if ( is3D() )
1093  {
1094  mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
1095  }
1096  if ( isMeasure() )
1097  {
1098  mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
1099  }
1100  clearCache();
1101 }
1102 
1104 {
1105  if ( numPoints() < 3 )
1106  {
1107  //undefined
1108  return 0.0;
1109  }
1110 
1111  int before = vId.vertex - 1;
1112  int vertex = vId.vertex;
1113  int after = vId.vertex + 1;
1114 
1115  if ( vId.vertex % 2 != 0 ) // a curve vertex
1116  {
1117  if ( vId.vertex >= 1 && vId.vertex < numPoints() - 1 )
1118  {
1119  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[before], mY[before] ),
1120  QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[after], mY[after] ) );
1121  }
1122  }
1123  else //a point vertex
1124  {
1125  if ( vId.vertex == 0 )
1126  {
1127  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[0], mY[0] ), QgsPoint( mX[0], mY[0] ),
1128  QgsPoint( mX[1], mY[1] ), QgsPoint( mX[2], mY[2] ) );
1129  }
1130  if ( vId.vertex >= numPoints() - 1 )
1131  {
1132  int a = numPoints() - 3;
1133  int b = numPoints() - 2;
1134  int c = numPoints() - 1;
1135  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[c], mY[c] ), QgsPoint( mX[a], mY[a] ),
1136  QgsPoint( mX[b], mY[b] ), QgsPoint( mX[c], mY[c] ) );
1137  }
1138  else
1139  {
1140  if ( vId.vertex + 2 > numPoints() - 1 )
1141  {
1142  return 0.0;
1143  }
1144 
1145  int vertex1 = vId.vertex - 2;
1146  int vertex2 = vId.vertex - 1;
1147  int vertex3 = vId.vertex;
1148  double angle1 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1149  QgsPoint( mX[vertex1], mY[vertex1] ), QgsPoint( mX[vertex2], mY[vertex2] ), QgsPoint( mX[vertex3], mY[vertex3] ) );
1150  int vertex4 = vId.vertex + 1;
1151  int vertex5 = vId.vertex + 2;
1152  double angle2 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1153  QgsPoint( mX[vertex3], mY[vertex3] ), QgsPoint( mX[vertex4], mY[vertex4] ), QgsPoint( mX[vertex5], mY[vertex5] ) );
1154  return QgsGeometryUtils::averageAngle( angle1, angle2 );
1155  }
1156  }
1157  return 0.0;
1158 }
1159 
1161 {
1162  if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 2 )
1163  return 0.0;
1164 
1165  if ( startVertex.vertex % 2 == 1 )
1166  return 0.0; // curve point?
1167 
1168  double x1 = mX.at( startVertex.vertex );
1169  double y1 = mY.at( startVertex.vertex );
1170  double x2 = mX.at( startVertex.vertex + 1 );
1171  double y2 = mY.at( startVertex.vertex + 1 );
1172  double x3 = mX.at( startVertex.vertex + 2 );
1173  double y3 = mY.at( startVertex.vertex + 2 );
1174  return QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1175 }
1176 
1178 {
1179  QgsCircularString *copy = clone();
1180  std::reverse( copy->mX.begin(), copy->mX.end() );
1181  std::reverse( copy->mY.begin(), copy->mY.end() );
1182  if ( is3D() )
1183  {
1184  std::reverse( copy->mZ.begin(), copy->mZ.end() );
1185  }
1186  if ( isMeasure() )
1187  {
1188  std::reverse( copy->mM.begin(), copy->mM.end() );
1189  }
1190  return copy;
1191 }
1192 
1193 QgsPoint *QgsCircularString::interpolatePoint( const double distance ) const
1194 {
1195  if ( distance < 0 )
1196  return nullptr;
1197 
1198  double distanceTraversed = 0;
1199  const int totalPoints = numPoints();
1200  if ( totalPoints == 0 )
1201  return nullptr;
1202 
1204  if ( is3D() )
1205  pointType = QgsWkbTypes::PointZ;
1206  if ( isMeasure() )
1207  pointType = QgsWkbTypes::addM( pointType );
1208 
1209  const double *x = mX.constData();
1210  const double *y = mY.constData();
1211  const double *z = is3D() ? mZ.constData() : nullptr;
1212  const double *m = isMeasure() ? mM.constData() : nullptr;
1213 
1214  double prevX = *x++;
1215  double prevY = *y++;
1216  double prevZ = z ? *z++ : 0.0;
1217  double prevM = m ? *m++ : 0.0;
1218 
1219  if ( qgsDoubleNear( distance, 0.0 ) )
1220  {
1221  return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1222  }
1223 
1224  for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1225  {
1226  double x1 = prevX;
1227  double y1 = prevY;
1228  double z1 = prevZ;
1229  double m1 = prevM;
1230 
1231  double x2 = *x++;
1232  double y2 = *y++;
1233  double z2 = z ? *z++ : 0.0;
1234  double m2 = m ? *m++ : 0.0;
1235 
1236  double x3 = *x++;
1237  double y3 = *y++;
1238  double z3 = z ? *z++ : 0.0;
1239  double m3 = m ? *m++ : 0.0;
1240 
1241  const double segmentLength = QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1242  if ( distance < distanceTraversed + segmentLength || qgsDoubleNear( distance, distanceTraversed + segmentLength ) )
1243  {
1244  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1245  const double distanceToPoint = std::min( distance - distanceTraversed, segmentLength );
1246  return new QgsPoint( QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1247  QgsPoint( pointType, x2, y2, z2, m2 ),
1248  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToPoint ) );
1249  }
1250 
1251  distanceTraversed += segmentLength;
1252 
1253  prevX = x3;
1254  prevY = y3;
1255  prevZ = z3;
1256  prevM = m3;
1257  }
1258 
1259  return nullptr;
1260 }
1261 
1262 QgsCircularString *QgsCircularString::curveSubstring( double startDistance, double endDistance ) const
1263 {
1264  if ( startDistance < 0 && endDistance < 0 )
1265  return createEmptyWithSameType();
1266 
1267  endDistance = std::max( startDistance, endDistance );
1268 
1269  double distanceTraversed = 0;
1270  const int totalPoints = numPoints();
1271  if ( totalPoints == 0 )
1272  return clone();
1273 
1274  QVector< QgsPoint > substringPoints;
1275 
1277  if ( is3D() )
1278  pointType = QgsWkbTypes::PointZ;
1279  if ( isMeasure() )
1280  pointType = QgsWkbTypes::addM( pointType );
1281 
1282  const double *x = mX.constData();
1283  const double *y = mY.constData();
1284  const double *z = is3D() ? mZ.constData() : nullptr;
1285  const double *m = isMeasure() ? mM.constData() : nullptr;
1286 
1287  double prevX = *x++;
1288  double prevY = *y++;
1289  double prevZ = z ? *z++ : 0.0;
1290  double prevM = m ? *m++ : 0.0;
1291  bool foundStart = false;
1292 
1293  if ( qgsDoubleNear( startDistance, 0.0 ) || startDistance < 0 )
1294  {
1295  substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1296  foundStart = true;
1297  }
1298 
1299  substringPoints.reserve( totalPoints );
1300 
1301  for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1302  {
1303  double x1 = prevX;
1304  double y1 = prevY;
1305  double z1 = prevZ;
1306  double m1 = prevM;
1307 
1308  double x2 = *x++;
1309  double y2 = *y++;
1310  double z2 = z ? *z++ : 0.0;
1311  double m2 = m ? *m++ : 0.0;
1312 
1313  double x3 = *x++;
1314  double y3 = *y++;
1315  double z3 = z ? *z++ : 0.0;
1316  double m3 = m ? *m++ : 0.0;
1317 
1318  bool addedSegmentEnd = false;
1319  const double segmentLength = QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1320  if ( distanceTraversed < startDistance && distanceTraversed + segmentLength > startDistance )
1321  {
1322  // start point falls on this segment
1323  const double distanceToStart = startDistance - distanceTraversed;
1324  const QgsPoint startPoint = QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1325  QgsPoint( pointType, x2, y2, z2, m2 ),
1326  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToStart );
1327 
1328  // does end point also fall on this segment?
1329  const bool endPointOnSegment = distanceTraversed + segmentLength > endDistance;
1330  if ( endPointOnSegment )
1331  {
1332  const double distanceToEnd = endDistance - distanceTraversed;
1333  const double midPointDistance = ( distanceToEnd - distanceToStart ) * 0.5 + distanceToStart;
1334  substringPoints << startPoint
1335  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1336  QgsPoint( pointType, x2, y2, z2, m2 ),
1337  QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1338  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1339  QgsPoint( pointType, x2, y2, z2, m2 ),
1340  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1341  addedSegmentEnd = true;
1342  }
1343  else
1344  {
1345  const double midPointDistance = ( segmentLength - distanceToStart ) * 0.5 + distanceToStart;
1346  substringPoints << startPoint
1347  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1348  QgsPoint( pointType, x2, y2, z2, m2 ),
1349  QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1350  << QgsPoint( pointType, x3, y3, z3, m3 );
1351  addedSegmentEnd = true;
1352  }
1353  foundStart = true;
1354  }
1355  if ( !addedSegmentEnd && foundStart && ( distanceTraversed + segmentLength > endDistance ) )
1356  {
1357  // end point falls on this segment
1358  const double distanceToEnd = endDistance - distanceTraversed;
1359  // add mid point, at half way along this arc, then add the interpolated end point
1360  substringPoints << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1361  QgsPoint( pointType, x2, y2, z2, m2 ),
1362  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd / 2.0 )
1363 
1364  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1365  QgsPoint( pointType, x2, y2, z2, m2 ),
1366  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1367  }
1368  else if ( !addedSegmentEnd && foundStart )
1369  {
1370  substringPoints << QgsPoint( pointType, x2, y2, z2, m2 )
1371  << QgsPoint( pointType, x3, y3, z3, m3 );
1372  }
1373 
1374  distanceTraversed += segmentLength;
1375  if ( distanceTraversed > endDistance )
1376  break;
1377 
1378  prevX = x3;
1379  prevY = y3;
1380  prevZ = z3;
1381  prevM = m3;
1382  }
1383 
1384  std::unique_ptr< QgsCircularString > result = qgis::make_unique< QgsCircularString >();
1385  result->setPoints( substringPoints );
1386  return result.release();
1387 }
1388 
1389 bool QgsCircularString::addZValue( double zValue )
1390 {
1391  if ( QgsWkbTypes::hasZ( mWkbType ) )
1392  return false;
1393 
1394  clearCache();
1396 
1397  int nPoints = numPoints();
1398  mZ.clear();
1399  mZ.reserve( nPoints );
1400  for ( int i = 0; i < nPoints; ++i )
1401  {
1402  mZ << zValue;
1403  }
1404  return true;
1405 }
1406 
1407 bool QgsCircularString::addMValue( double mValue )
1408 {
1409  if ( QgsWkbTypes::hasM( mWkbType ) )
1410  return false;
1411 
1412  clearCache();
1414 
1415  int nPoints = numPoints();
1416  mM.clear();
1417  mM.reserve( nPoints );
1418  for ( int i = 0; i < nPoints; ++i )
1419  {
1420  mM << mValue;
1421  }
1422  return true;
1423 }
1424 
1426 {
1427  if ( !QgsWkbTypes::hasZ( mWkbType ) )
1428  return false;
1429 
1430  clearCache();
1431 
1433  mZ.clear();
1434  return true;
1435 }
1436 
1438 {
1439  if ( !QgsWkbTypes::hasM( mWkbType ) )
1440  return false;
1441 
1442  clearCache();
1443 
1445  mM.clear();
1446  return true;
1447 }
1448 
1450 {
1451  std::swap( mX, mY );
1452  clearCache();
1453 }
bool isMeasure() const
Returns true if the geometry contains m values.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
int precision
A rectangle specified with double values.
Definition: qgsrectangle.h:41
double y
Definition: qgspoint.h:42
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...
void setPoints(const QgsPointSequence &points)
Resets the line string to match the specified list of points.
int numPoints() const override
Returns the number of points in the curve.
static bool circleAngleBetween(double angle, double angle1, double angle2, bool clockwise)
Returns true if, in a circle, angle is between angle1 and angle2.
static double ccwAngle(double dy, double dx)
Returns the counter clockwise angle between a line with components dx, dy and the line with dx > 0 an...
QgsCircularString()
Constructs an empty circular string.
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 (...
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the average angle (in radians) between the two linear segments from (x1, y1) to (x2, y2) and (x2, y2) to (x3, y3).
bool pointAt(int node, QgsPoint &point, QgsVertexId::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:280
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
static QgsPoint segmentMidPointFromCenter(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, bool useShortestArc=true)
Calculates the midpoint on the circle passing through p1 and p2, with the specified center coordinate...
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.
QgsRectangle calculateBoundingBox() const override
Default calculator for the minimal bounding box for the geometry.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgscurve.cpp:245
static double circleLength(double x1, double y1, double x2, double y2, double x3, double y3)
Length of a circular string segment defined by pt1, pt2, pt3.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
static endian_t endian()
Returns whether this machine uses big or little endian.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:917
QgsPoint pointN(int i) const
Returns the point at index i within the circular string.
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
static Type dropM(Type type)
Drops the m dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1087
QgsWkbTypes::Type mWkbType
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
void clear() override
Clears the geometry, ie reset it to a null geometry.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
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
QString wktTypeStr() const
Returns the WKT type string of the geometry.
static bool circleClockwise(double angle1, double angle2, double angle3)
Returns true if the circle defined by three angles is ordered clockwise.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
bool equals(const QgsCurve &other) const override
Checks whether this curve exactly equals another curve.
static QString pointsToWKT(const QgsPointSequence &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
QgsCircularString * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership...
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
static int leftOfLine(const double x, const double y, const double x1, const double y1, const double x2, const double y2)
Returns a value < 0 if the point (x, y) is left of the line from (x1, y1) -> ( x2, y2).
static Type addM(Type type)
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1038
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
Utility class for identifying a unique vertex within a geometry.
QString geometryType() const override
Returns a unique string representing the geometry type.
double length() const override
Returns the planar, 2-dimensional length of the geometry.
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
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 setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
static Type addZ(Type type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1013
static double circleTangentDirection(const QgsPoint &tangentPoint, const QgsPoint &cp1, const QgsPoint &cp2, const QgsPoint &cp3)
Calculates the direction angle of a circle tangent (clockwise from north in radians) ...
QgsCircularString * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
static bool angleOnCircle(double angle, double angle1, double angle2, double angle3)
Returns true if an angle is between angle1 and angle3 on a circle described by angle1, angle2 and angle3.
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
int dimension() const override
Returns the inherent dimension of the geometry.
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
void transformCoords(int numPoint, double *x, double *y, double *z, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transform an array of coordinates to the destination CRS.
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
QgsPoint endPoint() const override
Returns the end point of the curve.
AxisOrder
Axis order for GML generation.
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 setX(double x)
Sets the point&#39;s x-coordinate.
Definition: qgspoint.h:262
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:268
void setY(double y)
Sets the point&#39;s y-coordinate.
Definition: qgspoint.h:273
QVector< QgsPoint > QgsPointSequence
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle...
Definition: qgsrectangle.h:359
static QgsPoint pointOnLineWithDistance(const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance)
Returns a point a specified distance toward a second point.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
static double sqrDistance2D(const QgsPoint &pt1, const QgsPoint &pt2)
Returns the squared 2D distance between two points.
static Type dropZ(Type type)
Drops the z dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1069
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
static double sweepAngle(double centerX, double centerY, double x1, double y1, double x2, double y2, double x3, double y3)
Calculates angle of a circular string part defined by pt1, pt2, pt3.
static QgsPointSequence pointsFromWKT(const QString &wktCoordinateList, bool is3D, bool isMeasure)
Returns a list of points contained in a WKT string.
QgsCircularString * clone() const override
Clones the geometry by performing a deep copy.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
static QgsPoint interpolatePointOnArc(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double distance)
Interpolates a point on an arc defined by three points, pt1, pt2 and pt3.
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...
void setPoints(const QgsPointSequence &points)
Sets the circular string&#39;s points.
QByteArray asWkb() const override
Returns a WKB representation of the geometry.
static void circleCenterRadius(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double &radius, double &centerX, double &centerY)
Returns radius and center of the circle through pt1, pt2, pt3.
Class for doing transforms between two map coordinate systems.
static void pointsToWKB(QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
double z
Definition: qgspoint.h:43
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:967
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
Circular string geometry type.
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...
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
QgsCircularString * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
void swapXy() override
Swaps the x and y coordinates from the geometry.
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:576
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
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.
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 is3D() const
Returns true if the geometry is 3D and contains a z-value.
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether &#39;thepoint&#39; is left or right of the line from &#39;p1&#39; to &#39;p2&#39;. Negativ values mean left a...
Definition: MathUtils.cpp:292
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
bool isEmpty() const override
Returns true if the geometry is empty.
QgsPoint startPoint() const override
Returns the starting point of the curve.
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.
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 m
Definition: qgspoint.h:44
bool dropMValue() override
Drops any measure values which exist in the geometry.
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.
double x
Definition: qgspoint.h:41