QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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  setPoints( QgsGeometryUtils::pointsFromWKT( parts.second, is3D(), isMeasure() ) );
318  return true;
319 }
320 
321 QByteArray QgsCircularString::asWkb() const
322 {
323  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
324  binarySize += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
325 
326  QByteArray wkbArray;
327  wkbArray.resize( binarySize );
328  QgsWkbPtr wkb( wkbArray );
329  wkb << static_cast<char>( QgsApplication::endian() );
330  wkb << static_cast<quint32>( wkbType() );
331  QgsPointSequence pts;
332  points( pts );
333  QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure() );
334  return wkbArray;
335 }
336 
338 {
339  QString wkt = wktTypeStr() + ' ';
340  QgsPointSequence pts;
341  points( pts );
342  wkt += QgsGeometryUtils::pointsToWKT( pts, precision, is3D(), isMeasure() );
343  return wkt;
344 }
345 
346 QDomElement QgsCircularString::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
347 {
348  // GML2 does not support curves
349  std::unique_ptr< QgsLineString > line( curveToLine() );
350  QDomElement gml = line->asGml2( doc, precision, ns, axisOrder );
351  return gml;
352 }
353 
354 QDomElement QgsCircularString::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
355 {
356  QgsPointSequence pts;
357  points( pts );
358 
359  QDomElement elemCurve = doc.createElementNS( ns, QStringLiteral( "Curve" ) );
360 
361  if ( isEmpty() )
362  return elemCurve;
363 
364  QDomElement elemSegments = doc.createElementNS( ns, QStringLiteral( "segments" ) );
365  QDomElement elemArcString = doc.createElementNS( ns, QStringLiteral( "ArcString" ) );
366  elemArcString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D(), axisOrder ) );
367  elemSegments.appendChild( elemArcString );
368  elemCurve.appendChild( elemSegments );
369  return elemCurve;
370 }
371 
372 
374 {
375  // GeoJSON does not support curves
376  std::unique_ptr< QgsLineString > line( curveToLine() );
377  return line->asJsonObject( precision );
378 }
379 
381 {
382  return mX.isEmpty();
383 }
384 
385 //curve interface
387 {
388  int nPoints = numPoints();
389  double length = 0;
390  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
391  {
392  length += QgsGeometryUtils::circleLength( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2] );
393  }
394  return length;
395 }
396 
398 {
399  if ( numPoints() < 1 )
400  {
401  return QgsPoint();
402  }
403  return pointN( 0 );
404 }
405 
407 {
408  if ( numPoints() < 1 )
409  {
410  return QgsPoint();
411  }
412  return pointN( numPoints() - 1 );
413 }
414 
416 {
417  QgsLineString *line = new QgsLineString();
419  int nPoints = numPoints();
420 
421  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
422  {
423  QgsGeometryUtils::segmentizeArc( pointN( i ), pointN( i + 1 ), pointN( i + 2 ), points, tolerance, toleranceType, is3D(), isMeasure() );
424  }
425 
426  line->setPoints( points );
427  return line;
428 }
429 
430 QgsCircularString *QgsCircularString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
431 {
432  // prepare result
433  std::unique_ptr<QgsCircularString> result { createEmptyWithSameType() };
434 
435  bool res = snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
436  result->mX, result->mY, result->mZ, result->mM );
437  if ( res )
438  return result.release();
439  else
440  return nullptr;
441 }
442 
443 bool QgsCircularString::removeDuplicateNodes( double epsilon, bool useZValues )
444 {
445  if ( mX.count() <= 3 )
446  return false; // don't create degenerate lines
447  bool result = false;
448  double prevX = mX.at( 0 );
449  double prevY = mY.at( 0 );
450  bool hasZ = is3D();
451  bool useZ = hasZ && useZValues;
452  double prevZ = useZ ? mZ.at( 0 ) : 0;
453  int i = 1;
454  int remaining = mX.count();
455  // we have to consider points in pairs, since a segment can validly have the same start and
456  // end if it has a different curve point
457  while ( i + 1 < remaining )
458  {
459  double currentCurveX = mX.at( i );
460  double currentCurveY = mY.at( i );
461  double currentX = mX.at( i + 1 );
462  double currentY = mY.at( i + 1 );
463  double currentZ = useZ ? mZ.at( i + 1 ) : 0;
464  if ( qgsDoubleNear( currentCurveX, prevX, epsilon ) &&
465  qgsDoubleNear( currentCurveY, prevY, epsilon ) &&
466  qgsDoubleNear( currentX, prevX, epsilon ) &&
467  qgsDoubleNear( currentY, prevY, epsilon ) &&
468  ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
469  {
470  result = true;
471  // remove point
472  mX.removeAt( i );
473  mX.removeAt( i );
474  mY.removeAt( i );
475  mY.removeAt( i );
476  if ( hasZ )
477  {
478  mZ.removeAt( i );
479  mZ.removeAt( i );
480  }
481  remaining -= 2;
482  }
483  else
484  {
485  prevX = currentX;
486  prevY = currentY;
487  prevZ = currentZ;
488  i += 2;
489  }
490  }
491  return result;
492 }
493 
495 {
496  return std::min( mX.size(), mY.size() );
497 }
498 
500 {
501  if ( i < 0 || std::min( mX.size(), mY.size() ) <= i )
502  {
503  return QgsPoint();
504  }
505 
506  double x = mX.at( i );
507  double y = mY.at( i );
508  double z = 0;
509  double m = 0;
510 
511  if ( is3D() )
512  {
513  z = mZ.at( i );
514  }
515  if ( isMeasure() )
516  {
517  m = mM.at( i );
518  }
519 
521  if ( is3D() && isMeasure() )
522  {
524  }
525  else if ( is3D() )
526  {
528  }
529  else if ( isMeasure() )
530  {
532  }
533  return QgsPoint( t, x, y, z, m );
534 }
535 
536 double QgsCircularString::xAt( int index ) const
537 {
538  if ( index >= 0 && index < mX.size() )
539  return mX.at( index );
540  else
541  return 0.0;
542 }
543 
544 double QgsCircularString::yAt( int index ) const
545 {
546  if ( index >= 0 && index < mY.size() )
547  return mY.at( index );
548  else
549  return 0.0;
550 }
551 
552 void QgsCircularString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
553 {
554  bool hasZ = is3D();
555  bool hasM = isMeasure();
556  int size = mX.size();
557 
558  double *srcX = mX.data(); // clazy:exclude=detaching-member
559  double *srcY = mY.data(); // clazy:exclude=detaching-member
560  double *srcM = hasM ? mM.data() : nullptr; // clazy:exclude=detaching-member
561  double *srcZ = hasZ ? mZ.data() : nullptr; // clazy:exclude=detaching-member
562 
563  double *destX = srcX;
564  double *destY = srcY;
565  double *destM = srcM;
566  double *destZ = srcZ;
567 
568  int filteredPoints = 0;
569  for ( int i = 0; i < size; ++i )
570  {
571  double x = *srcX++;
572  double y = *srcY++;
573  double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
574  double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
575 
576  if ( filter( QgsPoint( x, y, z, m ) ) )
577  {
578  filteredPoints++;
579  *destX++ = x;
580  *destY++ = y;
581  if ( hasM )
582  *destM++ = m;
583  if ( hasZ )
584  *destZ++ = z;
585  }
586  }
587 
588  mX.resize( filteredPoints );
589  mY.resize( filteredPoints );
590  if ( hasZ )
591  mZ.resize( filteredPoints );
592  if ( hasM )
593  mM.resize( filteredPoints );
594 
595  clearCache();
596 }
597 
598 void QgsCircularString::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
599 {
600  bool hasZ = is3D();
601  bool hasM = isMeasure();
602  int size = mX.size();
603 
604  double *srcX = mX.data();
605  double *srcY = mY.data();
606  double *srcM = hasM ? mM.data() : nullptr;
607  double *srcZ = hasZ ? mZ.data() : nullptr;
608 
609  for ( int i = 0; i < size; ++i )
610  {
611  double x = *srcX;
612  double y = *srcY;
613  double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
614  double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
615  QgsPoint res = transform( QgsPoint( x, y, z, m ) );
616  *srcX++ = res.x();
617  *srcY++ = res.y();
618  if ( hasM )
619  *srcM++ = res.m();
620  if ( hasZ )
621  *srcZ++ = res.z();
622  }
623  clearCache();
624 }
625 
627 {
628  pts.clear();
629  int nPts = numPoints();
630  for ( int i = 0; i < nPts; ++i )
631  {
632  pts.push_back( pointN( i ) );
633  }
634 }
635 
637 {
638  clearCache();
639 
640  if ( points.empty() )
641  {
643  mX.clear();
644  mY.clear();
645  mZ.clear();
646  mM.clear();
647  return;
648  }
649 
650  //get wkb type from first point
651  const QgsPoint &firstPt = points.at( 0 );
652  bool hasZ = firstPt.is3D();
653  bool hasM = firstPt.isMeasure();
654 
656 
657  mX.resize( points.size() );
658  mY.resize( points.size() );
659  if ( hasZ )
660  {
661  mZ.resize( points.size() );
662  }
663  else
664  {
665  mZ.clear();
666  }
667  if ( hasM )
668  {
669  mM.resize( points.size() );
670  }
671  else
672  {
673  mM.clear();
674  }
675 
676  for ( int i = 0; i < points.size(); ++i )
677  {
678  mX[i] = points[i].x();
679  mY[i] = points[i].y();
680  if ( hasZ )
681  {
682  double z = points.at( i ).z();
683  mZ[i] = std::isnan( z ) ? 0 : z;
684  }
685  if ( hasM )
686  {
687  double m = points.at( i ).m();
688  mM[i] = std::isnan( m ) ? 0 : m;
689  }
690  }
691 }
692 
693 void QgsCircularString::draw( QPainter &p ) const
694 {
695  QPainterPath path;
696  addToPainterPath( path );
697  p.drawPath( path );
698 }
699 
701 {
702  clearCache();
703 
704  double *zArray = mZ.data();
705 
706  bool hasZ = is3D();
707  int nPoints = numPoints();
708  bool useDummyZ = !hasZ || !transformZ;
709  if ( useDummyZ )
710  {
711  zArray = new double[nPoints];
712  for ( int i = 0; i < nPoints; ++i )
713  {
714  zArray[i] = 0;
715  }
716  }
717  ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
718  if ( useDummyZ )
719  {
720  delete[] zArray;
721  }
722 }
723 
724 void QgsCircularString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
725 {
726  clearCache();
727 
728  int nPoints = numPoints();
729  bool hasZ = is3D();
730  bool hasM = isMeasure();
731  for ( int i = 0; i < nPoints; ++i )
732  {
733  qreal x, y;
734  t.map( mX.at( i ), mY.at( i ), &x, &y );
735  mX[i] = x;
736  mY[i] = y;
737  if ( hasZ )
738  {
739  mZ[i] = mZ.at( i ) * zScale + zTranslate;
740  }
741  if ( hasM )
742  {
743  mM[i] = mM.at( i ) * mScale + mTranslate;
744  }
745  }
746 }
747 
748 void QgsCircularString::addToPainterPath( QPainterPath &path ) const
749 {
750  int nPoints = numPoints();
751  if ( nPoints < 1 )
752  {
753  return;
754  }
755 
756  if ( path.isEmpty() || path.currentPosition() != QPointF( mX[0], mY[0] ) )
757  {
758  path.moveTo( QPointF( mX[0], mY[0] ) );
759  }
760 
761  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
762  {
763  QgsPointSequence pt;
764  QgsGeometryUtils::segmentizeArc( QgsPoint( mX[i], mY[i] ), QgsPoint( mX[i + 1], mY[i + 1] ), QgsPoint( mX[i + 2], mY[i + 2] ), pt );
765  for ( int j = 1; j < pt.size(); ++j )
766  {
767  path.lineTo( pt.at( j ).x(), pt.at( j ).y() );
768  }
769 #if 0
770  //arcTo( path, QPointF( mX[i], mY[i] ), QPointF( mX[i + 1], mY[i + 1] ), QPointF( mX[i + 2], mY[i + 2] ) );
771 #endif
772  }
773 
774  //if number of points is even, connect to last point with straight line (even though the circular string is not valid)
775  if ( nPoints % 2 == 0 )
776  {
777  path.lineTo( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
778  }
779 }
780 
781 #if 0
782 void QgsCircularString::arcTo( QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3 )
783 {
784  double centerX, centerY, radius;
785  QgsGeometryUtils::circleCenterRadius( QgsPoint( pt1.x(), pt1.y() ), QgsPoint( pt2.x(), pt2.y() ), QgsPoint( pt3.x(), pt3.y() ),
786  radius, centerX, centerY );
787 
788  double p1Angle = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
789  double sweepAngle = QgsGeometryUtils::sweepAngle( centerX, centerY, pt1.x(), pt1.y(), pt2.x(), pt2.y(), pt3.x(), pt3.y() );
790 
791  double diameter = 2 * radius;
792  path.arcTo( centerX - radius, centerY - radius, diameter, diameter, p1Angle, sweepAngle );
793 }
794 #endif
795 
796 void QgsCircularString::drawAsPolygon( QPainter &p ) const
797 {
798  draw( p );
799 }
800 
801 bool QgsCircularString::insertVertex( QgsVertexId position, const QgsPoint &vertex )
802 {
803  if ( position.vertex >= mX.size() || position.vertex < 1 )
804  {
805  return false;
806  }
807 
808  mX.insert( position.vertex, vertex.x() );
809  mY.insert( position.vertex, vertex.y() );
810  if ( is3D() )
811  {
812  mZ.insert( position.vertex, vertex.z() );
813  }
814  if ( isMeasure() )
815  {
816  mM.insert( position.vertex, vertex.m() );
817  }
818 
819  bool vertexNrEven = ( position.vertex % 2 == 0 );
820  if ( vertexNrEven )
821  {
822  insertVertexBetween( position.vertex - 2, position.vertex - 1, position.vertex );
823  }
824  else
825  {
826  insertVertexBetween( position.vertex, position.vertex + 1, position.vertex - 1 );
827  }
828  clearCache(); //set bounding box invalid
829  return true;
830 }
831 
832 bool QgsCircularString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
833 {
834  if ( position.vertex < 0 || position.vertex >= mX.size() )
835  {
836  return false;
837  }
838 
839  mX[position.vertex] = newPos.x();
840  mY[position.vertex] = newPos.y();
841  if ( is3D() && newPos.is3D() )
842  {
843  mZ[position.vertex] = newPos.z();
844  }
845  if ( isMeasure() && newPos.isMeasure() )
846  {
847  mM[position.vertex] = newPos.m();
848  }
849  clearCache(); //set bounding box invalid
850  return true;
851 }
852 
854 {
855  int nVertices = this->numPoints();
856  if ( nVertices < 4 ) //circular string must have at least 3 vertices
857  {
858  clear();
859  return true;
860  }
861  if ( position.vertex < 0 || position.vertex > ( nVertices - 1 ) )
862  {
863  return false;
864  }
865 
866  if ( position.vertex < ( nVertices - 2 ) )
867  {
868  //remove this and the following vertex
869  deleteVertex( position.vertex + 1 );
870  deleteVertex( position.vertex );
871  }
872  else //remove this and the preceding vertex
873  {
874  deleteVertex( position.vertex );
875  deleteVertex( position.vertex - 1 );
876  }
877 
878  clearCache(); //set bounding box invalid
879  return true;
880 }
881 
883 {
884  mX.remove( i );
885  mY.remove( i );
886  if ( is3D() )
887  {
888  mZ.remove( i );
889  }
890  if ( isMeasure() )
891  {
892  mM.remove( i );
893  }
894  clearCache();
895 }
896 
897 double QgsCircularString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
898 {
899  double minDist = std::numeric_limits<double>::max();
900  QgsPoint minDistSegmentPoint;
901  QgsVertexId minDistVertexAfter;
902  int minDistLeftOf = 0;
903 
904  double currentDist = 0.0;
905 
906  int nPoints = numPoints();
907  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
908  {
909  currentDist = closestPointOnArc( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2], pt, segmentPt, vertexAfter, leftOf, epsilon );
910  if ( currentDist < minDist )
911  {
912  minDist = currentDist;
913  minDistSegmentPoint = segmentPt;
914  minDistVertexAfter.vertex = vertexAfter.vertex + i;
915  if ( leftOf )
916  {
917  minDistLeftOf = *leftOf;
918  }
919  }
920  }
921 
922  if ( minDist == std::numeric_limits<double>::max() )
923  return -1; // error: no segments
924 
925  segmentPt = minDistSegmentPoint;
926  vertexAfter = minDistVertexAfter;
927  vertexAfter.part = 0;
928  vertexAfter.ring = 0;
929  if ( leftOf )
930  {
931  *leftOf = qgsDoubleNear( minDist, 0.0 ) ? 0 : minDistLeftOf;
932  }
933  return minDist;
934 }
935 
936 bool QgsCircularString::pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const
937 {
938  if ( node < 0 || node >= numPoints() )
939  {
940  return false;
941  }
942  point = pointN( node );
943  type = ( node % 2 == 0 ) ? QgsVertexId::SegmentVertex : QgsVertexId::CurveVertex;
944  return true;
945 }
946 
947 void QgsCircularString::sumUpArea( double &sum ) const
948 {
949  int maxIndex = numPoints() - 2;
950 
951  for ( int i = 0; i < maxIndex; i += 2 )
952  {
953  QgsPoint p1( mX[i], mY[i] );
954  QgsPoint p2( mX[i + 1], mY[i + 1] );
955  QgsPoint p3( mX[i + 2], mY[i + 2] );
956 
957  //segment is a full circle, p2 is the center point
958  if ( p1 == p3 )
959  {
960  double r2 = QgsGeometryUtils::sqrDistance2D( p1, p2 ) / 4.0;
961  sum += M_PI * r2;
962  continue;
963  }
964 
965  sum += 0.5 * ( mX[i] * mY[i + 2] - mY[i] * mX[i + 2] );
966 
967  //calculate area between circle and chord, then sum / subtract from total area
968  double midPointX = ( p1.x() + p3.x() ) / 2.0;
969  double midPointY = ( p1.y() + p3.y() ) / 2.0;
970 
971  double radius, centerX, centerY;
972  QgsGeometryUtils::circleCenterRadius( p1, p2, p3, radius, centerX, centerY );
973 
974  double d = std::sqrt( QgsGeometryUtils::sqrDistance2D( QgsPoint( centerX, centerY ), QgsPoint( midPointX, midPointY ) ) );
975  double r2 = radius * radius;
976 
977  if ( d > radius )
978  {
979  //d cannot be greater than radius, something must be wrong...
980  continue;
981  }
982 
983  bool circlePointLeftOfLine = QgsGeometryUtils::leftOfLine( p2.x(), p2.y(), p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
984  bool centerPointLeftOfLine = QgsGeometryUtils::leftOfLine( centerX, centerY, p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
985 
986  double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - M_1_PI * std::asin( d / radius );
987  double circleChordArea = 0;
988  if ( circlePointLeftOfLine == centerPointLeftOfLine )
989  {
990  circleChordArea = M_PI * r2 * ( 1 - cov );
991  }
992  else
993  {
994  circleChordArea = M_PI * r2 * cov;
995  }
996 
997  if ( !circlePointLeftOfLine )
998  {
999  sum += circleChordArea;
1000  }
1001  else
1002  {
1003  sum -= circleChordArea;
1004  }
1005  }
1006 }
1007 
1009 {
1010  return true;
1011 }
1012 
1013 double QgsCircularString::closestPointOnArc( double x1, double y1, double x2, double y2, double x3, double y3,
1014  const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon )
1015 {
1016  double radius, centerX, centerY;
1017  QgsPoint pt1( x1, y1 );
1018  QgsPoint pt2( x2, y2 );
1019  QgsPoint pt3( x3, y3 );
1020 
1021  QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
1022  double angle = QgsGeometryUtils::ccwAngle( pt.y() - centerY, pt.x() - centerX );
1023  double angle1 = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
1024  double angle2 = QgsGeometryUtils::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
1025  double angle3 = QgsGeometryUtils::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
1026 
1027  bool clockwise = QgsGeometryUtils::circleClockwise( angle1, angle2, angle3 );
1028 
1029  if ( QgsGeometryUtils::angleOnCircle( angle, angle1, angle2, angle3 ) )
1030  {
1031  //get point on line center -> pt with distance radius
1032  segmentPt = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), pt, radius );
1033 
1034  //vertexAfter
1035  vertexAfter.vertex = QgsGeometryUtils::circleAngleBetween( angle, angle1, angle2, clockwise ) ? 1 : 2;
1036  }
1037  else
1038  {
1039  double distPtPt1 = QgsGeometryUtils::sqrDistance2D( pt, pt1 );
1040  double distPtPt3 = QgsGeometryUtils::sqrDistance2D( pt, pt3 );
1041  segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
1042  vertexAfter.vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
1043  }
1044 
1045  double sqrDistance = QgsGeometryUtils::sqrDistance2D( segmentPt, pt );
1046  //prevent rounding errors if the point is directly on the segment
1047  if ( qgsDoubleNear( sqrDistance, 0.0, epsilon ) )
1048  {
1049  segmentPt.setX( pt.x() );
1050  segmentPt.setY( pt.y() );
1051  sqrDistance = 0.0;
1052  }
1053 
1054  if ( leftOf )
1055  {
1056  double sqrDistancePointToCenter = ( pt.x() - centerX ) * ( pt.x() - centerX ) + ( pt.y() - centerY ) * ( pt.y() - centerY );
1057  *leftOf = clockwise ? ( sqrDistancePointToCenter > radius * radius ? -1 : 1 )
1058  : ( sqrDistancePointToCenter < radius * radius ? -1 : 1 );
1059  }
1060 
1061  return sqrDistance;
1062 }
1063 
1064 void QgsCircularString::insertVertexBetween( int after, int before, int pointOnCircle )
1065 {
1066  double xAfter = mX.at( after );
1067  double yAfter = mY.at( after );
1068  double xBefore = mX.at( before );
1069  double yBefore = mY.at( before );
1070  double xOnCircle = mX.at( pointOnCircle );
1071  double yOnCircle = mY.at( pointOnCircle );
1072 
1073  double radius, centerX, centerY;
1074  QgsGeometryUtils::circleCenterRadius( QgsPoint( xAfter, yAfter ), QgsPoint( xBefore, yBefore ), QgsPoint( xOnCircle, yOnCircle ), radius, centerX, centerY );
1075 
1076  double x = ( xAfter + xBefore ) / 2.0;
1077  double y = ( yAfter + yBefore ) / 2.0;
1078 
1079  QgsPoint newVertex = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), QgsPoint( x, y ), radius );
1080  mX.insert( before, newVertex.x() );
1081  mY.insert( before, newVertex.y() );
1082 
1083  if ( is3D() )
1084  {
1085  mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
1086  }
1087  if ( isMeasure() )
1088  {
1089  mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
1090  }
1091  clearCache();
1092 }
1093 
1095 {
1096  if ( numPoints() < 3 )
1097  {
1098  //undefined
1099  return 0.0;
1100  }
1101 
1102  int before = vId.vertex - 1;
1103  int vertex = vId.vertex;
1104  int after = vId.vertex + 1;
1105 
1106  if ( vId.vertex % 2 != 0 ) // a curve vertex
1107  {
1108  if ( vId.vertex >= 1 && vId.vertex < numPoints() - 1 )
1109  {
1110  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[before], mY[before] ),
1111  QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[after], mY[after] ) );
1112  }
1113  }
1114  else //a point vertex
1115  {
1116  if ( vId.vertex == 0 )
1117  {
1118  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[0], mY[0] ), QgsPoint( mX[0], mY[0] ),
1119  QgsPoint( mX[1], mY[1] ), QgsPoint( mX[2], mY[2] ) );
1120  }
1121  if ( vId.vertex >= numPoints() - 1 )
1122  {
1123  int a = numPoints() - 3;
1124  int b = numPoints() - 2;
1125  int c = numPoints() - 1;
1126  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[c], mY[c] ), QgsPoint( mX[a], mY[a] ),
1127  QgsPoint( mX[b], mY[b] ), QgsPoint( mX[c], mY[c] ) );
1128  }
1129  else
1130  {
1131  if ( vId.vertex + 2 > numPoints() - 1 )
1132  {
1133  return 0.0;
1134  }
1135 
1136  int vertex1 = vId.vertex - 2;
1137  int vertex2 = vId.vertex - 1;
1138  int vertex3 = vId.vertex;
1139  double angle1 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1140  QgsPoint( mX[vertex1], mY[vertex1] ), QgsPoint( mX[vertex2], mY[vertex2] ), QgsPoint( mX[vertex3], mY[vertex3] ) );
1141  int vertex4 = vId.vertex + 1;
1142  int vertex5 = vId.vertex + 2;
1143  double angle2 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1144  QgsPoint( mX[vertex3], mY[vertex3] ), QgsPoint( mX[vertex4], mY[vertex4] ), QgsPoint( mX[vertex5], mY[vertex5] ) );
1145  return QgsGeometryUtils::averageAngle( angle1, angle2 );
1146  }
1147  }
1148  return 0.0;
1149 }
1150 
1152 {
1153  if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 2 )
1154  return 0.0;
1155 
1156  if ( startVertex.vertex % 2 == 1 )
1157  return 0.0; // curve point?
1158 
1159  double x1 = mX.at( startVertex.vertex );
1160  double y1 = mY.at( startVertex.vertex );
1161  double x2 = mX.at( startVertex.vertex + 1 );
1162  double y2 = mY.at( startVertex.vertex + 1 );
1163  double x3 = mX.at( startVertex.vertex + 2 );
1164  double y3 = mY.at( startVertex.vertex + 2 );
1165  return QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1166 }
1167 
1169 {
1170  QgsCircularString *copy = clone();
1171  std::reverse( copy->mX.begin(), copy->mX.end() );
1172  std::reverse( copy->mY.begin(), copy->mY.end() );
1173  if ( is3D() )
1174  {
1175  std::reverse( copy->mZ.begin(), copy->mZ.end() );
1176  }
1177  if ( isMeasure() )
1178  {
1179  std::reverse( copy->mM.begin(), copy->mM.end() );
1180  }
1181  return copy;
1182 }
1183 
1184 QgsPoint *QgsCircularString::interpolatePoint( const double distance ) const
1185 {
1186  if ( distance < 0 )
1187  return nullptr;
1188 
1189  double distanceTraversed = 0;
1190  const int totalPoints = numPoints();
1191  if ( totalPoints == 0 )
1192  return nullptr;
1193 
1195  if ( is3D() )
1196  pointType = QgsWkbTypes::PointZ;
1197  if ( isMeasure() )
1198  pointType = QgsWkbTypes::addM( pointType );
1199 
1200  const double *x = mX.constData();
1201  const double *y = mY.constData();
1202  const double *z = is3D() ? mZ.constData() : nullptr;
1203  const double *m = isMeasure() ? mM.constData() : nullptr;
1204 
1205  double prevX = *x++;
1206  double prevY = *y++;
1207  double prevZ = z ? *z++ : 0.0;
1208  double prevM = m ? *m++ : 0.0;
1209 
1210  if ( qgsDoubleNear( distance, 0.0 ) )
1211  {
1212  return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1213  }
1214 
1215  for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1216  {
1217  double x1 = prevX;
1218  double y1 = prevY;
1219  double z1 = prevZ;
1220  double m1 = prevM;
1221 
1222  double x2 = *x++;
1223  double y2 = *y++;
1224  double z2 = z ? *z++ : 0.0;
1225  double m2 = m ? *m++ : 0.0;
1226 
1227  double x3 = *x++;
1228  double y3 = *y++;
1229  double z3 = z ? *z++ : 0.0;
1230  double m3 = m ? *m++ : 0.0;
1231 
1232  const double segmentLength = QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1233  if ( distance < distanceTraversed + segmentLength || qgsDoubleNear( distance, distanceTraversed + segmentLength ) )
1234  {
1235  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1236  const double distanceToPoint = std::min( distance - distanceTraversed, segmentLength );
1237  return new QgsPoint( QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1238  QgsPoint( pointType, x2, y2, z2, m2 ),
1239  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToPoint ) );
1240  }
1241 
1242  distanceTraversed += segmentLength;
1243 
1244  prevX = x3;
1245  prevY = y3;
1246  prevZ = z3;
1247  prevM = m3;
1248  }
1249 
1250  return nullptr;
1251 }
1252 
1253 QgsCircularString *QgsCircularString::curveSubstring( double startDistance, double endDistance ) const
1254 {
1255  if ( startDistance < 0 && endDistance < 0 )
1256  return createEmptyWithSameType();
1257 
1258  endDistance = std::max( startDistance, endDistance );
1259 
1260  double distanceTraversed = 0;
1261  const int totalPoints = numPoints();
1262  if ( totalPoints == 0 )
1263  return clone();
1264 
1265  QVector< QgsPoint > substringPoints;
1266 
1268  if ( is3D() )
1269  pointType = QgsWkbTypes::PointZ;
1270  if ( isMeasure() )
1271  pointType = QgsWkbTypes::addM( pointType );
1272 
1273  const double *x = mX.constData();
1274  const double *y = mY.constData();
1275  const double *z = is3D() ? mZ.constData() : nullptr;
1276  const double *m = isMeasure() ? mM.constData() : nullptr;
1277 
1278  double prevX = *x++;
1279  double prevY = *y++;
1280  double prevZ = z ? *z++ : 0.0;
1281  double prevM = m ? *m++ : 0.0;
1282  bool foundStart = false;
1283 
1284  if ( qgsDoubleNear( startDistance, 0.0 ) || startDistance < 0 )
1285  {
1286  substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1287  foundStart = true;
1288  }
1289 
1290  substringPoints.reserve( totalPoints );
1291 
1292  for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1293  {
1294  double x1 = prevX;
1295  double y1 = prevY;
1296  double z1 = prevZ;
1297  double m1 = prevM;
1298 
1299  double x2 = *x++;
1300  double y2 = *y++;
1301  double z2 = z ? *z++ : 0.0;
1302  double m2 = m ? *m++ : 0.0;
1303 
1304  double x3 = *x++;
1305  double y3 = *y++;
1306  double z3 = z ? *z++ : 0.0;
1307  double m3 = m ? *m++ : 0.0;
1308 
1309  bool addedSegmentEnd = false;
1310  const double segmentLength = QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1311  if ( distanceTraversed < startDistance && distanceTraversed + segmentLength > startDistance )
1312  {
1313  // start point falls on this segment
1314  const double distanceToStart = startDistance - distanceTraversed;
1315  const QgsPoint startPoint = QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1316  QgsPoint( pointType, x2, y2, z2, m2 ),
1317  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToStart );
1318 
1319  // does end point also fall on this segment?
1320  const bool endPointOnSegment = distanceTraversed + segmentLength > endDistance;
1321  if ( endPointOnSegment )
1322  {
1323  const double distanceToEnd = endDistance - distanceTraversed;
1324  const double midPointDistance = ( distanceToEnd - distanceToStart ) * 0.5 + distanceToStart;
1325  substringPoints << startPoint
1326  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1327  QgsPoint( pointType, x2, y2, z2, m2 ),
1328  QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1329  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1330  QgsPoint( pointType, x2, y2, z2, m2 ),
1331  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1332  addedSegmentEnd = true;
1333  }
1334  else
1335  {
1336  const double midPointDistance = ( segmentLength - distanceToStart ) * 0.5 + distanceToStart;
1337  substringPoints << startPoint
1338  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1339  QgsPoint( pointType, x2, y2, z2, m2 ),
1340  QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1341  << QgsPoint( pointType, x3, y3, z3, m3 );
1342  addedSegmentEnd = true;
1343  }
1344  foundStart = true;
1345  }
1346  if ( !addedSegmentEnd && foundStart && ( distanceTraversed + segmentLength > endDistance ) )
1347  {
1348  // end point falls on this segment
1349  const double distanceToEnd = endDistance - distanceTraversed;
1350  // add mid point, at half way along this arc, then add the interpolated end point
1351  substringPoints << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1352  QgsPoint( pointType, x2, y2, z2, m2 ),
1353  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd / 2.0 )
1354 
1355  << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1356  QgsPoint( pointType, x2, y2, z2, m2 ),
1357  QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1358  }
1359  else if ( !addedSegmentEnd && foundStart )
1360  {
1361  substringPoints << QgsPoint( pointType, x2, y2, z2, m2 )
1362  << QgsPoint( pointType, x3, y3, z3, m3 );
1363  }
1364 
1365  distanceTraversed += segmentLength;
1366  if ( distanceTraversed > endDistance )
1367  break;
1368 
1369  prevX = x3;
1370  prevY = y3;
1371  prevZ = z3;
1372  prevM = m3;
1373  }
1374 
1375  std::unique_ptr< QgsCircularString > result = qgis::make_unique< QgsCircularString >();
1376  result->setPoints( substringPoints );
1377  return result.release();
1378 }
1379 
1380 bool QgsCircularString::addZValue( double zValue )
1381 {
1382  if ( QgsWkbTypes::hasZ( mWkbType ) )
1383  return false;
1384 
1385  clearCache();
1387 
1388  int nPoints = numPoints();
1389  mZ.clear();
1390  mZ.reserve( nPoints );
1391  for ( int i = 0; i < nPoints; ++i )
1392  {
1393  mZ << zValue;
1394  }
1395  return true;
1396 }
1397 
1398 bool QgsCircularString::addMValue( double mValue )
1399 {
1400  if ( QgsWkbTypes::hasM( mWkbType ) )
1401  return false;
1402 
1403  clearCache();
1405 
1406  int nPoints = numPoints();
1407  mM.clear();
1408  mM.reserve( nPoints );
1409  for ( int i = 0; i < nPoints; ++i )
1410  {
1411  mM << mValue;
1412  }
1413  return true;
1414 }
1415 
1417 {
1418  if ( !QgsWkbTypes::hasZ( mWkbType ) )
1419  return false;
1420 
1421  clearCache();
1422 
1424  mZ.clear();
1425  return true;
1426 }
1427 
1429 {
1430  if ( !QgsWkbTypes::hasM( mWkbType ) )
1431  return false;
1432 
1433  clearCache();
1434 
1436  mM.clear();
1437  return true;
1438 }
1439 
1441 {
1442  std::swap( mX, mY );
1443  clearCache();
1444 }
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:265
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:244
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:771
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:941
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:892
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 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:867
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.
nlohmann::json json
Definition: qgsjsonutils.h:27
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
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:267
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: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:923
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:821
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:430
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