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