QGIS API Documentation  3.2.0-Bonn (bc43194)
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 
334 QString QgsCircularString::asWkt( int precision ) const
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 
369 QString QgsCircularString::asJson( int precision ) const
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 
596 {
597  pts.clear();
598  int nPts = numPoints();
599  for ( int i = 0; i < nPts; ++i )
600  {
601  pts.push_back( pointN( i ) );
602  }
603 }
604 
606 {
607  clearCache();
608 
609  if ( points.empty() )
610  {
612  mX.clear();
613  mY.clear();
614  mZ.clear();
615  mM.clear();
616  return;
617  }
618 
619  //get wkb type from first point
620  const QgsPoint &firstPt = points.at( 0 );
621  bool hasZ = firstPt.is3D();
622  bool hasM = firstPt.isMeasure();
623 
625 
626  mX.resize( points.size() );
627  mY.resize( points.size() );
628  if ( hasZ )
629  {
630  mZ.resize( points.size() );
631  }
632  else
633  {
634  mZ.clear();
635  }
636  if ( hasM )
637  {
638  mM.resize( points.size() );
639  }
640  else
641  {
642  mM.clear();
643  }
644 
645  for ( int i = 0; i < points.size(); ++i )
646  {
647  mX[i] = points[i].x();
648  mY[i] = points[i].y();
649  if ( hasZ )
650  {
651  double z = points.at( i ).z();
652  mZ[i] = std::isnan( z ) ? 0 : z;
653  }
654  if ( hasM )
655  {
656  double m = points.at( i ).m();
657  mM[i] = std::isnan( m ) ? 0 : m;
658  }
659  }
660 }
661 
662 void QgsCircularString::draw( QPainter &p ) const
663 {
664  QPainterPath path;
665  addToPainterPath( path );
666  p.drawPath( path );
667 }
668 
670 {
671  clearCache();
672 
673  double *zArray = mZ.data();
674 
675  bool hasZ = is3D();
676  int nPoints = numPoints();
677  bool useDummyZ = !hasZ || !transformZ;
678  if ( useDummyZ )
679  {
680  zArray = new double[nPoints];
681  for ( int i = 0; i < nPoints; ++i )
682  {
683  zArray[i] = 0;
684  }
685  }
686  ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
687  if ( useDummyZ )
688  {
689  delete[] zArray;
690  }
691 }
692 
693 void QgsCircularString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
694 {
695  clearCache();
696 
697  int nPoints = numPoints();
698  bool hasZ = is3D();
699  bool hasM = isMeasure();
700  for ( int i = 0; i < nPoints; ++i )
701  {
702  qreal x, y;
703  t.map( mX.at( i ), mY.at( i ), &x, &y );
704  mX[i] = x;
705  mY[i] = y;
706  if ( hasZ )
707  {
708  mZ[i] = mZ.at( i ) * zScale + zTranslate;
709  }
710  if ( hasM )
711  {
712  mM[i] = mM.at( i ) * mScale + mTranslate;
713  }
714  }
715 }
716 
717 void QgsCircularString::addToPainterPath( QPainterPath &path ) const
718 {
719  int nPoints = numPoints();
720  if ( nPoints < 1 )
721  {
722  return;
723  }
724 
725  if ( path.isEmpty() || path.currentPosition() != QPointF( mX[0], mY[0] ) )
726  {
727  path.moveTo( QPointF( mX[0], mY[0] ) );
728  }
729 
730  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
731  {
732  QgsPointSequence pt;
733  QgsGeometryUtils::segmentizeArc( QgsPoint( mX[i], mY[i] ), QgsPoint( mX[i + 1], mY[i + 1] ), QgsPoint( mX[i + 2], mY[i + 2] ), pt );
734  for ( int j = 1; j < pt.size(); ++j )
735  {
736  path.lineTo( pt.at( j ).x(), pt.at( j ).y() );
737  }
738 #if 0
739  //arcTo( path, QPointF( mX[i], mY[i] ), QPointF( mX[i + 1], mY[i + 1] ), QPointF( mX[i + 2], mY[i + 2] ) );
740 #endif
741  }
742 
743  //if number of points is even, connect to last point with straight line (even though the circular string is not valid)
744  if ( nPoints % 2 == 0 )
745  {
746  path.lineTo( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
747  }
748 }
749 
750 #if 0
751 void QgsCircularString::arcTo( QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3 )
752 {
753  double centerX, centerY, radius;
754  QgsGeometryUtils::circleCenterRadius( QgsPoint( pt1.x(), pt1.y() ), QgsPoint( pt2.x(), pt2.y() ), QgsPoint( pt3.x(), pt3.y() ),
755  radius, centerX, centerY );
756 
757  double p1Angle = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
758  double sweepAngle = QgsGeometryUtils::sweepAngle( centerX, centerY, pt1.x(), pt1.y(), pt2.x(), pt2.y(), pt3.x(), pt3.y() );
759 
760  double diameter = 2 * radius;
761  path.arcTo( centerX - radius, centerY - radius, diameter, diameter, p1Angle, sweepAngle );
762 }
763 #endif
764 
765 void QgsCircularString::drawAsPolygon( QPainter &p ) const
766 {
767  draw( p );
768 }
769 
770 bool QgsCircularString::insertVertex( QgsVertexId position, const QgsPoint &vertex )
771 {
772  if ( position.vertex >= mX.size() || position.vertex < 1 )
773  {
774  return false;
775  }
776 
777  mX.insert( position.vertex, vertex.x() );
778  mY.insert( position.vertex, vertex.y() );
779  if ( is3D() )
780  {
781  mZ.insert( position.vertex, vertex.z() );
782  }
783  if ( isMeasure() )
784  {
785  mM.insert( position.vertex, vertex.m() );
786  }
787 
788  bool vertexNrEven = ( position.vertex % 2 == 0 );
789  if ( vertexNrEven )
790  {
791  insertVertexBetween( position.vertex - 2, position.vertex - 1, position.vertex );
792  }
793  else
794  {
795  insertVertexBetween( position.vertex, position.vertex + 1, position.vertex - 1 );
796  }
797  clearCache(); //set bounding box invalid
798  return true;
799 }
800 
801 bool QgsCircularString::moveVertex( QgsVertexId position, const QgsPoint &newPos )
802 {
803  if ( position.vertex < 0 || position.vertex >= mX.size() )
804  {
805  return false;
806  }
807 
808  mX[position.vertex] = newPos.x();
809  mY[position.vertex] = newPos.y();
810  if ( is3D() && newPos.is3D() )
811  {
812  mZ[position.vertex] = newPos.z();
813  }
814  if ( isMeasure() && newPos.isMeasure() )
815  {
816  mM[position.vertex] = newPos.m();
817  }
818  clearCache(); //set bounding box invalid
819  return true;
820 }
821 
823 {
824  int nVertices = this->numPoints();
825  if ( nVertices < 4 ) //circular string must have at least 3 vertices
826  {
827  clear();
828  return true;
829  }
830  if ( position.vertex < 0 || position.vertex > ( nVertices - 1 ) )
831  {
832  return false;
833  }
834 
835  if ( position.vertex < ( nVertices - 2 ) )
836  {
837  //remove this and the following vertex
838  deleteVertex( position.vertex + 1 );
839  deleteVertex( position.vertex );
840  }
841  else //remove this and the preceding vertex
842  {
843  deleteVertex( position.vertex );
844  deleteVertex( position.vertex - 1 );
845  }
846 
847  clearCache(); //set bounding box invalid
848  return true;
849 }
850 
852 {
853  mX.remove( i );
854  mY.remove( i );
855  if ( is3D() )
856  {
857  mZ.remove( i );
858  }
859  if ( isMeasure() )
860  {
861  mM.remove( i );
862  }
863  clearCache();
864 }
865 
866 double QgsCircularString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
867 {
868  double minDist = std::numeric_limits<double>::max();
869  QgsPoint minDistSegmentPoint;
870  QgsVertexId minDistVertexAfter;
871  int minDistLeftOf = 0;
872 
873  double currentDist = 0.0;
874 
875  int nPoints = numPoints();
876  for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
877  {
878  currentDist = closestPointOnArc( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2], pt, segmentPt, vertexAfter, leftOf, epsilon );
879  if ( currentDist < minDist )
880  {
881  minDist = currentDist;
882  minDistSegmentPoint = segmentPt;
883  minDistVertexAfter.vertex = vertexAfter.vertex + i;
884  if ( leftOf )
885  {
886  minDistLeftOf = *leftOf;
887  }
888  }
889  }
890 
891  if ( minDist == std::numeric_limits<double>::max() )
892  return -1; // error: no segments
893 
894  segmentPt = minDistSegmentPoint;
895  vertexAfter = minDistVertexAfter;
896  vertexAfter.part = 0;
897  vertexAfter.ring = 0;
898  if ( leftOf )
899  {
900  *leftOf = qgsDoubleNear( minDist, 0.0 ) ? 0 : minDistLeftOf;
901  }
902  return minDist;
903 }
904 
905 bool QgsCircularString::pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const
906 {
907  if ( node < 0 || node >= numPoints() )
908  {
909  return false;
910  }
911  point = pointN( node );
912  type = ( node % 2 == 0 ) ? QgsVertexId::SegmentVertex : QgsVertexId::CurveVertex;
913  return true;
914 }
915 
916 void QgsCircularString::sumUpArea( double &sum ) const
917 {
918  int maxIndex = numPoints() - 2;
919 
920  for ( int i = 0; i < maxIndex; i += 2 )
921  {
922  QgsPoint p1( mX[i], mY[i] );
923  QgsPoint p2( mX[i + 1], mY[i + 1] );
924  QgsPoint p3( mX[i + 2], mY[i + 2] );
925 
926  //segment is a full circle, p2 is the center point
927  if ( p1 == p3 )
928  {
929  double r2 = QgsGeometryUtils::sqrDistance2D( p1, p2 ) / 4.0;
930  sum += M_PI * r2;
931  continue;
932  }
933 
934  sum += 0.5 * ( mX[i] * mY[i + 2] - mY[i] * mX[i + 2] );
935 
936  //calculate area between circle and chord, then sum / subtract from total area
937  double midPointX = ( p1.x() + p3.x() ) / 2.0;
938  double midPointY = ( p1.y() + p3.y() ) / 2.0;
939 
940  double radius, centerX, centerY;
941  QgsGeometryUtils::circleCenterRadius( p1, p2, p3, radius, centerX, centerY );
942 
943  double d = std::sqrt( QgsGeometryUtils::sqrDistance2D( QgsPoint( centerX, centerY ), QgsPoint( midPointX, midPointY ) ) );
944  double r2 = radius * radius;
945 
946  if ( d > radius )
947  {
948  //d cannot be greater than radius, something must be wrong...
949  continue;
950  }
951 
952  bool circlePointLeftOfLine = QgsGeometryUtils::leftOfLine( p2.x(), p2.y(), p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
953  bool centerPointLeftOfLine = QgsGeometryUtils::leftOfLine( centerX, centerY, p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
954 
955  double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - M_1_PI * std::asin( d / radius );
956  double circleChordArea = 0;
957  if ( circlePointLeftOfLine == centerPointLeftOfLine )
958  {
959  circleChordArea = M_PI * r2 * ( 1 - cov );
960  }
961  else
962  {
963  circleChordArea = M_PI * r2 * cov;
964  }
965 
966  if ( !circlePointLeftOfLine )
967  {
968  sum += circleChordArea;
969  }
970  else
971  {
972  sum -= circleChordArea;
973  }
974  }
975 }
976 
978 {
979  return true;
980 }
981 
982 double QgsCircularString::closestPointOnArc( double x1, double y1, double x2, double y2, double x3, double y3,
983  const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon )
984 {
985  double radius, centerX, centerY;
986  QgsPoint pt1( x1, y1 );
987  QgsPoint pt2( x2, y2 );
988  QgsPoint pt3( x3, y3 );
989 
990  QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
991  double angle = QgsGeometryUtils::ccwAngle( pt.y() - centerY, pt.x() - centerX );
992  double angle1 = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
993  double angle2 = QgsGeometryUtils::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
994  double angle3 = QgsGeometryUtils::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
995 
996  bool clockwise = QgsGeometryUtils::circleClockwise( angle1, angle2, angle3 );
997 
998  if ( QgsGeometryUtils::angleOnCircle( angle, angle1, angle2, angle3 ) )
999  {
1000  //get point on line center -> pt with distance radius
1001  segmentPt = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), pt, radius );
1002 
1003  //vertexAfter
1004  vertexAfter.vertex = QgsGeometryUtils::circleAngleBetween( angle, angle1, angle2, clockwise ) ? 1 : 2;
1005  }
1006  else
1007  {
1008  double distPtPt1 = QgsGeometryUtils::sqrDistance2D( pt, pt1 );
1009  double distPtPt3 = QgsGeometryUtils::sqrDistance2D( pt, pt3 );
1010  segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
1011  vertexAfter.vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
1012  }
1013 
1014  double sqrDistance = QgsGeometryUtils::sqrDistance2D( segmentPt, pt );
1015  //prevent rounding errors if the point is directly on the segment
1016  if ( qgsDoubleNear( sqrDistance, 0.0, epsilon ) )
1017  {
1018  segmentPt.setX( pt.x() );
1019  segmentPt.setY( pt.y() );
1020  sqrDistance = 0.0;
1021  }
1022 
1023  if ( leftOf )
1024  {
1025  double sqrDistancePointToCenter = ( pt.x() - centerX ) * ( pt.x() - centerX ) + ( pt.y() - centerY ) * ( pt.y() - centerY );
1026  *leftOf = clockwise ? ( sqrDistancePointToCenter > radius * radius ? -1 : 1 )
1027  : ( sqrDistancePointToCenter < radius * radius ? -1 : 1 );
1028  }
1029 
1030  return sqrDistance;
1031 }
1032 
1033 void QgsCircularString::insertVertexBetween( int after, int before, int pointOnCircle )
1034 {
1035  double xAfter = mX.at( after );
1036  double yAfter = mY.at( after );
1037  double xBefore = mX.at( before );
1038  double yBefore = mY.at( before );
1039  double xOnCircle = mX.at( pointOnCircle );
1040  double yOnCircle = mY.at( pointOnCircle );
1041 
1042  double radius, centerX, centerY;
1043  QgsGeometryUtils::circleCenterRadius( QgsPoint( xAfter, yAfter ), QgsPoint( xBefore, yBefore ), QgsPoint( xOnCircle, yOnCircle ), radius, centerX, centerY );
1044 
1045  double x = ( xAfter + xBefore ) / 2.0;
1046  double y = ( yAfter + yBefore ) / 2.0;
1047 
1048  QgsPoint newVertex = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), QgsPoint( x, y ), radius );
1049  mX.insert( before, newVertex.x() );
1050  mY.insert( before, newVertex.y() );
1051 
1052  if ( is3D() )
1053  {
1054  mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
1055  }
1056  if ( isMeasure() )
1057  {
1058  mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
1059  }
1060  clearCache();
1061 }
1062 
1064 {
1065  if ( numPoints() < 3 )
1066  {
1067  //undefined
1068  return 0.0;
1069  }
1070 
1071  int before = vId.vertex - 1;
1072  int vertex = vId.vertex;
1073  int after = vId.vertex + 1;
1074 
1075  if ( vId.vertex % 2 != 0 ) // a curve vertex
1076  {
1077  if ( vId.vertex >= 1 && vId.vertex < numPoints() - 1 )
1078  {
1079  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[before], mY[before] ),
1080  QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[after], mY[after] ) );
1081  }
1082  }
1083  else //a point vertex
1084  {
1085  if ( vId.vertex == 0 )
1086  {
1087  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[0], mY[0] ), QgsPoint( mX[0], mY[0] ),
1088  QgsPoint( mX[1], mY[1] ), QgsPoint( mX[2], mY[2] ) );
1089  }
1090  if ( vId.vertex >= numPoints() - 1 )
1091  {
1092  int a = numPoints() - 3;
1093  int b = numPoints() - 2;
1094  int c = numPoints() - 1;
1095  return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[c], mY[c] ), QgsPoint( mX[a], mY[a] ),
1096  QgsPoint( mX[b], mY[b] ), QgsPoint( mX[c], mY[c] ) );
1097  }
1098  else
1099  {
1100  if ( vId.vertex + 2 > numPoints() - 1 )
1101  {
1102  return 0.0;
1103  }
1104 
1105  int vertex1 = vId.vertex - 2;
1106  int vertex2 = vId.vertex - 1;
1107  int vertex3 = vId.vertex;
1108  double angle1 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1109  QgsPoint( mX[vertex1], mY[vertex1] ), QgsPoint( mX[vertex2], mY[vertex2] ), QgsPoint( mX[vertex3], mY[vertex3] ) );
1110  int vertex4 = vId.vertex + 1;
1111  int vertex5 = vId.vertex + 2;
1112  double angle2 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1113  QgsPoint( mX[vertex3], mY[vertex3] ), QgsPoint( mX[vertex4], mY[vertex4] ), QgsPoint( mX[vertex5], mY[vertex5] ) );
1114  return QgsGeometryUtils::averageAngle( angle1, angle2 );
1115  }
1116  }
1117  return 0.0;
1118 }
1119 
1121 {
1122  if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 2 )
1123  return 0.0;
1124 
1125  if ( startVertex.vertex % 2 == 1 )
1126  return 0.0; // curve point?
1127 
1128  double x1 = mX.at( startVertex.vertex );
1129  double y1 = mY.at( startVertex.vertex );
1130  double x2 = mX.at( startVertex.vertex + 1 );
1131  double y2 = mY.at( startVertex.vertex + 1 );
1132  double x3 = mX.at( startVertex.vertex + 2 );
1133  double y3 = mY.at( startVertex.vertex + 2 );
1134  return QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1135 }
1136 
1138 {
1139  QgsCircularString *copy = clone();
1140  std::reverse( copy->mX.begin(), copy->mX.end() );
1141  std::reverse( copy->mY.begin(), copy->mY.end() );
1142  if ( is3D() )
1143  {
1144  std::reverse( copy->mZ.begin(), copy->mZ.end() );
1145  }
1146  if ( isMeasure() )
1147  {
1148  std::reverse( copy->mM.begin(), copy->mM.end() );
1149  }
1150  return copy;
1151 }
1152 
1153 bool QgsCircularString::addZValue( double zValue )
1154 {
1155  if ( QgsWkbTypes::hasZ( mWkbType ) )
1156  return false;
1157 
1158  clearCache();
1160 
1161  int nPoints = numPoints();
1162  mZ.clear();
1163  mZ.reserve( nPoints );
1164  for ( int i = 0; i < nPoints; ++i )
1165  {
1166  mZ << zValue;
1167  }
1168  return true;
1169 }
1170 
1171 bool QgsCircularString::addMValue( double mValue )
1172 {
1173  if ( QgsWkbTypes::hasM( mWkbType ) )
1174  return false;
1175 
1176  clearCache();
1178 
1179  int nPoints = numPoints();
1180  mM.clear();
1181  mM.reserve( nPoints );
1182  for ( int i = 0; i < nPoints; ++i )
1183  {
1184  mM << mValue;
1185  }
1186  return true;
1187 }
1188 
1190 {
1191  if ( !QgsWkbTypes::hasZ( mWkbType ) )
1192  return false;
1193 
1194  clearCache();
1195 
1197  mZ.clear();
1198  return true;
1199 }
1200 
1202 {
1203  if ( !QgsWkbTypes::hasM( mWkbType ) )
1204  return false;
1205 
1206  clearCache();
1207 
1209  mM.clear();
1210  return true;
1211 }
1212 
1214 {
1215  std::swap( mX, mY );
1216  clearCache();
1217 }
bool isMeasure() const
Returns true if the geometry contains m values.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
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 (...
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:251
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...
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:217
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:768
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:938
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 circle 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:67
static Type addM(Type type)
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:889
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.
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 QDomElement pointsToGML3(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D, const QgsAbstractGeometry::AxisOrder &axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::posList DOM element.
static Type addZ(Type type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:864
static double circleTangentDirection(const QgsPoint &tangentPoint, const QgsPoint &cp1, const QgsPoint &cp2, const QgsPoint &cp3)
Calculates the direction angle of a circle tangent (clockwise from north in radians) ...
QgsCircularString * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
static bool angleOnCircle(double angle, double angle1, double angle2, double angle3)
Returns true if an angle is between angle1 and angle3 on a circle described by angle1, angle2 and angle3.
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
int dimension() const override
Returns the inherent dimension of the geometry.
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
void transformCoords(int numPoint, double *x, double *y, double *z, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transform an array of coordinates to the destination CRS.
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
QgsPoint endPoint() const override
Returns the end point of the curve.
AxisOrder
Axis order for GML generation.
static QgsCircularString fromTwoPointsAndCenter(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, bool useShortestArc=true)
Creates a circular string with a single arc representing the curve from p1 to p2 with the specified c...
void setX(double x)
Sets the point&#39;s x-coordinate.
Definition: qgspoint.h: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:238
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:352
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:920
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
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.
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:818
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
Circular string geometry type.
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
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:427
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:53
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