QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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 "qgsbox3d.h"
22#include "qgsgeometryutils.h"
23#include "qgslinestring.h"
24#include "qgspoint.h"
25#include "qgsrectangle.h"
26#include "qgswkbptr.h"
28#include "qgsfeedback.h"
29
30#include <QJsonObject>
31#include <QPainter>
32#include <QPainterPath>
33#include <memory>
34#include <nlohmann/json.hpp>
35
37{
39}
40
42{
43 //get wkb type from first point
44 bool hasZ = p1.is3D();
45 bool hasM = p1.isMeasure();
47
48 mX.resize( 3 );
49 mX[ 0 ] = p1.x();
50 mX[ 1 ] = p2.x();
51 mX[ 2 ] = p3.x();
52 mY.resize( 3 );
53 mY[ 0 ] = p1.y();
54 mY[ 1 ] = p2.y();
55 mY[ 2 ] = p3.y();
56 if ( hasZ )
57 {
59 mZ.resize( 3 );
60 mZ[ 0 ] = p1.z();
61 mZ[ 1 ] = p2.z();
62 mZ[ 2 ] = p3.z();
63 }
64 if ( hasM )
65 {
67 mM.resize( 3 );
68 mM[ 0 ] = p1.m();
69 mM[ 1 ] = p2.m();
70 mM[ 2 ] = p3.m();
71 }
72}
73
74QgsCircularString::QgsCircularString( const QVector<double> &x, const QVector<double> &y, const QVector<double> &z, const QVector<double> &m )
75{
77 int pointCount = std::min( x.size(), y.size() );
78 if ( x.size() == pointCount )
79 {
80 mX = x;
81 }
82 else
83 {
84 mX = x.mid( 0, pointCount );
85 }
86 if ( y.size() == pointCount )
87 {
88 mY = y;
89 }
90 else
91 {
92 mY = y.mid( 0, pointCount );
93 }
94 if ( !z.isEmpty() && z.count() >= pointCount )
95 {
97 if ( z.size() == pointCount )
98 {
99 mZ = z;
100 }
101 else
102 {
103 mZ = z.mid( 0, pointCount );
104 }
105 }
106 if ( !m.isEmpty() && m.count() >= pointCount )
107 {
109 if ( m.size() == pointCount )
110 {
111 mM = m;
112 }
113 else
114 {
115 mM = m.mid( 0, pointCount );
116 }
117 }
118}
119
120QgsCircularString QgsCircularString::fromTwoPointsAndCenter( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, const bool useShortestArc )
121{
122 const QgsPoint midPoint = QgsGeometryUtils::segmentMidPointFromCenter( p1, p2, center, useShortestArc );
123 return QgsCircularString( p1, midPoint, p2 );
124}
125
127{
128 auto result = std::make_unique< QgsCircularString >();
129 result->mWkbType = mWkbType;
130 return result.release();
131}
132
134{
135 const QgsCircularString *otherLine = qgsgeometry_cast<const QgsCircularString *>( other );
136 if ( !otherLine )
137 return -1;
138
139 const int size = mX.size();
140 const int otherSize = otherLine->mX.size();
141 if ( size > otherSize )
142 {
143 return 1;
144 }
145 else if ( size < otherSize )
146 {
147 return -1;
148 }
149
150 if ( is3D() && !otherLine->is3D() )
151 return 1;
152 else if ( !is3D() && otherLine->is3D() )
153 return -1;
154 const bool considerZ = is3D();
155
156 if ( isMeasure() && !otherLine->isMeasure() )
157 return 1;
158 else if ( !isMeasure() && otherLine->isMeasure() )
159 return -1;
160 const bool considerM = isMeasure();
161
162 for ( int i = 0; i < size; i++ )
163 {
164 const double x = mX[i];
165 const double otherX = otherLine->mX[i];
166 if ( x < otherX )
167 {
168 return -1;
169 }
170 else if ( x > otherX )
171 {
172 return 1;
173 }
174
175 const double y = mY[i];
176 const double otherY = otherLine->mY[i];
177 if ( y < otherY )
178 {
179 return -1;
180 }
181 else if ( y > otherY )
182 {
183 return 1;
184 }
185
186 if ( considerZ )
187 {
188 const double z = mZ[i];
189 const double otherZ = otherLine->mZ[i];
190
191 if ( z < otherZ )
192 {
193 return -1;
194 }
195 else if ( z > otherZ )
196 {
197 return 1;
198 }
199 }
200
201 if ( considerM )
202 {
203 const double m = mM[i];
204 const double otherM = otherLine->mM[i];
205
206 if ( m < otherM )
207 {
208 return -1;
209 }
210 else if ( m > otherM )
211 {
212 return 1;
213 }
214 }
215 }
216 return 0;
217}
218
220{
221 return QStringLiteral( "CircularString" );
222}
223
225{
226 return 1;
227}
228
230{
231 return new QgsCircularString( *this );
232}
233
235{
237 mX.clear();
238 mY.clear();
239 mZ.clear();
240 mM.clear();
241 clearCache();
242}
243
245{
246 QgsBox3D bbox;
247 int nPoints = numPoints();
248 for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
249 {
250 QgsRectangle box2d = segmentBoundingBox( QgsPoint( mX[i], mY[i] ), QgsPoint( mX[i + 1], mY[i + 1] ), QgsPoint( mX[i + 2], mY[i + 2] ) );
251 double zMin = std::numeric_limits<double>::quiet_NaN();
252 double zMax = std::numeric_limits<double>::quiet_NaN();
253 if ( is3D() )
254 {
255 zMin = *std::min_element( mZ.begin() + i, mZ.begin() + i + 3 );
256 zMax = *std::max_element( mZ.begin() + i, mZ.begin() + i + 3 );
257 }
258 if ( i == 0 )
259 {
260 bbox = QgsBox3D( box2d, zMin, zMax );
261 }
262 else
263 {
264 bbox.combineWith( QgsBox3D( box2d, zMin, zMax ) );
265 }
266 }
267
268 if ( nPoints > 0 && nPoints % 2 == 0 )
269 {
270 double z = std::numeric_limits<double>::quiet_NaN();
271 if ( nPoints == 2 )
272 {
273 if ( is3D() )
274 {
275 z = mZ[ 0 ];
276 }
277 bbox.combineWith( mX[ 0 ], mY[ 0 ], z );
278 }
279 if ( is3D() )
280 {
281 z = mZ[ nPoints - 1 ];
282 }
283 bbox.combineWith( mX[ nPoints - 1 ], mY[ nPoints - 1 ], z );
284 }
285 return bbox;
286}
287
289{
290 const int size = mX.size();
291 if ( index < 1 || index >= size - 1 )
292 return;
293
294 const bool useZ = is3D();
295 const bool useM = isMeasure();
296
297 QVector<double> newX( size );
298 QVector<double> newY( size );
299 QVector<double> newZ( useZ ? size : 0 );
300 QVector<double> newM( useM ? size : 0 );
301 auto it = std::copy( mX.constBegin() + index, mX.constEnd() - 1, newX.begin() );
302 it = std::copy( mX.constBegin(), mX.constBegin() + index, it );
303 *it = *newX.constBegin();
304 mX = std::move( newX );
305
306 it = std::copy( mY.constBegin() + index, mY.constEnd() - 1, newY.begin() );
307 it = std::copy( mY.constBegin(), mY.constBegin() + index, it );
308 *it = *newY.constBegin();
309 mY = std::move( newY );
310 if ( useZ )
311 {
312 it = std::copy( mZ.constBegin() + index, mZ.constEnd() - 1, newZ.begin() );
313 it = std::copy( mZ.constBegin(), mZ.constBegin() + index, it );
314 *it = *newZ.constBegin();
315 mZ = std::move( newZ );
316 }
317 if ( useM )
318 {
319 it = std::copy( mM.constBegin() + index, mM.constEnd() - 1, newM.begin() );
320 it = std::copy( mM.constBegin(), mM.constBegin() + index, it );
321 *it = *newM.constBegin();
322 mM = std::move( newM );
323 }
324}
325
326QgsRectangle QgsCircularString::segmentBoundingBox( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3 )
327{
328 double centerX, centerY, radius;
329 QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
330
331 double p1Angle = QgsGeometryUtilsBase::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
332 double p2Angle = QgsGeometryUtilsBase::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
333 double p3Angle = QgsGeometryUtilsBase::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
334
335 //start point, end point and compass points in between can be on bounding box
336 QgsRectangle bbox( pt1.x(), pt1.y(), pt1.x(), pt1.y() );
337 bbox.combineExtentWith( pt3.x(), pt3.y() );
338
339 QgsPointSequence compassPoints = compassPointsOnSegment( p1Angle, p2Angle, p3Angle, centerX, centerY, radius );
340 QgsPointSequence::const_iterator cpIt = compassPoints.constBegin();
341 for ( ; cpIt != compassPoints.constEnd(); ++cpIt )
342 {
343 bbox.combineExtentWith( cpIt->x(), cpIt->y() );
344 }
345 return bbox;
346}
347
348QgsPointSequence QgsCircularString::compassPointsOnSegment( double p1Angle, double p2Angle, double p3Angle, double centerX, double centerY, double radius )
349{
350 QgsPointSequence pointList;
351
352 QgsPoint nPoint( centerX, centerY + radius );
353 QgsPoint ePoint( centerX + radius, centerY );
354 QgsPoint sPoint( centerX, centerY - radius );
355 QgsPoint wPoint( centerX - radius, centerY );
356
357 if ( p3Angle >= p1Angle )
358 {
359 if ( p2Angle > p1Angle && p2Angle < p3Angle )
360 {
361 if ( p1Angle <= 90 && p3Angle >= 90 )
362 {
363 pointList.append( nPoint );
364 }
365 if ( p1Angle <= 180 && p3Angle >= 180 )
366 {
367 pointList.append( wPoint );
368 }
369 if ( p1Angle <= 270 && p3Angle >= 270 )
370 {
371 pointList.append( sPoint );
372 }
373 }
374 else
375 {
376 pointList.append( ePoint );
377 if ( p1Angle >= 90 || p3Angle <= 90 )
378 {
379 pointList.append( nPoint );
380 }
381 if ( p1Angle >= 180 || p3Angle <= 180 )
382 {
383 pointList.append( wPoint );
384 }
385 if ( p1Angle >= 270 || p3Angle <= 270 )
386 {
387 pointList.append( sPoint );
388 }
389 }
390 }
391 else
392 {
393 if ( p2Angle < p1Angle && p2Angle > p3Angle )
394 {
395 if ( p1Angle >= 270 && p3Angle <= 270 )
396 {
397 pointList.append( sPoint );
398 }
399 if ( p1Angle >= 180 && p3Angle <= 180 )
400 {
401 pointList.append( wPoint );
402 }
403 if ( p1Angle >= 90 && p3Angle <= 90 )
404 {
405 pointList.append( nPoint );
406 }
407 }
408 else
409 {
410 pointList.append( ePoint );
411 if ( p1Angle <= 270 || p3Angle >= 270 )
412 {
413 pointList.append( sPoint );
414 }
415 if ( p1Angle <= 180 || p3Angle >= 180 )
416 {
417 pointList.append( wPoint );
418 }
419 if ( p1Angle <= 90 || p3Angle >= 90 )
420 {
421 pointList.append( nPoint );
422 }
423 }
424 }
425 return pointList;
426}
427
429{
430 if ( !wkbPtr )
431 return false;
432
433 Qgis::WkbType type = wkbPtr.readHeader();
435 {
436 return false;
437 }
438 clearCache();
439 mWkbType = type;
440
441 //type
442 const bool hasZ = is3D();
443 const bool hasM = isMeasure();
444 int nVertices = 0;
445 wkbPtr >> nVertices;
446 mX.resize( nVertices );
447 mY.resize( nVertices );
448 if ( hasZ )
449 mZ.resize( nVertices );
450 else
451 mZ.clear();
452 if ( hasM )
453 mM.resize( nVertices );
454 else
455 mM.clear();
456 for ( int i = 0; i < nVertices; ++i )
457 {
458 wkbPtr >> mX[i];
459 wkbPtr >> mY[i];
460 if ( hasZ )
461 {
462 wkbPtr >> mZ[i];
463 }
464 if ( hasM )
465 {
466 wkbPtr >> mM[i];
467 }
468 }
469
470 return true;
471}
472
473bool QgsCircularString::fromWkt( const QString &wkt )
474{
475 clear();
476
477 QPair<Qgis::WkbType, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
478
480 return false;
481 mWkbType = parts.first;
482
483 parts.second = parts.second.remove( '(' ).remove( ')' );
484 QString secondWithoutParentheses = parts.second;
485 secondWithoutParentheses = secondWithoutParentheses.simplified().remove( ' ' );
486 if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
487 secondWithoutParentheses.isEmpty() )
488 return true;
489
491 if ( points.isEmpty() )
492 return false;
493
494 setPoints( points );
495 return true;
496}
497
499{
500 int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
501 binarySize += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
502 return binarySize;
503}
504
505QByteArray QgsCircularString::asWkb( WkbFlags flags ) const
506{
507 QByteArray wkbArray;
508 wkbArray.resize( QgsCircularString::wkbSize( flags ) );
509 QgsWkbPtr wkb( wkbArray );
510 wkb << static_cast<char>( QgsApplication::endian() );
511 wkb << static_cast<quint32>( wkbType() );
513 points( pts );
514 QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure(), flags );
515 return wkbArray;
516}
517
519{
520 QString wkt = wktTypeStr() + ' ';
521
522 if ( isEmpty() )
523 wkt += QLatin1String( "EMPTY" );
524 else
525 {
527 points( pts );
529 }
530 return wkt;
531}
532
533QDomElement QgsCircularString::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
534{
535 // GML2 does not support curves
536 std::unique_ptr< QgsLineString > line( curveToLine() );
537 QDomElement gml = line->asGml2( doc, precision, ns, axisOrder );
538 return gml;
539}
540
541QDomElement QgsCircularString::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
542{
544 points( pts );
545
546 QDomElement elemCurve = doc.createElementNS( ns, QStringLiteral( "Curve" ) );
547
548 if ( isEmpty() )
549 return elemCurve;
550
551 QDomElement elemSegments = doc.createElementNS( ns, QStringLiteral( "segments" ) );
552 QDomElement elemArcString = doc.createElementNS( ns, QStringLiteral( "ArcString" ) );
553 elemArcString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D(), axisOrder ) );
554 elemSegments.appendChild( elemArcString );
555 elemCurve.appendChild( elemSegments );
556 return elemCurve;
557}
558
559
561{
562 // GeoJSON does not support curves
563 std::unique_ptr< QgsLineString > line( curveToLine() );
564 return line->asJsonObject( precision );
565}
566
568{
569 return mX.isEmpty();
570}
571
573{
574 if ( !isEmpty() && ( numPoints() < 3 ) )
575 {
576 error = QObject::tr( "CircularString has less than 3 points and is not empty." );
577 return false;
578 }
579 return QgsCurve::isValid( error, flags );
580}
581
582//curve interface
584{
585 int nPoints = numPoints();
586 double length = 0;
587 for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
588 {
589 length += QgsGeometryUtilsBase::circleLength( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2] );
590 }
591 return length;
592}
593
595{
596 if ( numPoints() < 1 )
597 {
598 return QgsPoint();
599 }
600 return pointN( 0 );
601}
602
604{
605 if ( numPoints() < 1 )
606 {
607 return QgsPoint();
608 }
609 return pointN( numPoints() - 1 );
610}
611
613{
614 QgsLineString *line = new QgsLineString();
616 int nPoints = numPoints();
617
618 for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
619 {
620 QgsGeometryUtils::segmentizeArc( pointN( i ), pointN( i + 1 ), pointN( i + 2 ), points, tolerance, toleranceType, is3D(), isMeasure() );
621 }
622
623 line->setPoints( points );
624 return line;
625}
626
627QgsCircularString *QgsCircularString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
628{
629 // prepare result
630 std::unique_ptr<QgsCircularString> result { createEmptyWithSameType() };
631
632 bool res = snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
633 result->mX, result->mY, result->mZ, result->mM );
634 if ( res )
635 return result.release();
636 else
637 return nullptr;
638}
639
640bool QgsCircularString::removeDuplicateNodes( double epsilon, bool useZValues )
641{
642 if ( mX.count() <= 3 )
643 return false; // don't create degenerate lines
644 bool result = false;
645 double prevX = mX.at( 0 );
646 double prevY = mY.at( 0 );
647 bool hasZ = is3D();
648 bool useZ = hasZ && useZValues;
649 double prevZ = useZ ? mZ.at( 0 ) : 0;
650 int i = 1;
651 int remaining = mX.count();
652 // we have to consider points in pairs, since a segment can validly have the same start and
653 // end if it has a different curve point
654 while ( i + 1 < remaining )
655 {
656 double currentCurveX = mX.at( i );
657 double currentCurveY = mY.at( i );
658 double currentX = mX.at( i + 1 );
659 double currentY = mY.at( i + 1 );
660 double currentZ = useZ ? mZ.at( i + 1 ) : 0;
661 if ( qgsDoubleNear( currentCurveX, prevX, epsilon ) &&
662 qgsDoubleNear( currentCurveY, prevY, epsilon ) &&
663 qgsDoubleNear( currentX, prevX, epsilon ) &&
664 qgsDoubleNear( currentY, prevY, epsilon ) &&
665 ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
666 {
667 result = true;
668 // remove point
669 mX.removeAt( i );
670 mX.removeAt( i );
671 mY.removeAt( i );
672 mY.removeAt( i );
673 if ( hasZ )
674 {
675 mZ.removeAt( i );
676 mZ.removeAt( i );
677 }
678 remaining -= 2;
679 }
680 else
681 {
682 prevX = currentX;
683 prevY = currentY;
684 prevZ = currentZ;
685 i += 2;
686 }
687 }
688 return result;
689}
690
692{
693 return std::min( mX.size(), mY.size() );
694}
695
696int QgsCircularString::indexOf( const QgsPoint &point ) const
697{
698 const int size = mX.size();
699 if ( size == 0 )
700 return -1;
701
702 const double *x = mX.constData();
703 const double *y = mY.constData();
704 const bool useZ = is3D();
705 const bool useM = isMeasure();
706 const double *z = useZ ? mZ.constData() : nullptr;
707 const double *m = useM ? mM.constData() : nullptr;
708
709 for ( int i = 0; i < size; i += 2 )
710 {
711 if ( qgsDoubleNear( *x, point.x() )
712 && qgsDoubleNear( *y, point.y() )
713 && ( !useZ || qgsDoubleNear( *z, point.z() ) )
714 && ( !useM || qgsDoubleNear( *m, point.m() ) ) )
715 return i;
716
717 // we skip over curve points!
718 x++;
719 x++;
720 y++;
721 y++;
722 if ( useZ )
723 {
724 z++;
725 z++;
726 }
727 if ( useM )
728 {
729 m++;
730 m++;
731 }
732 }
733 return -1;
734}
735
737{
738 if ( i < 0 || std::min( mX.size(), mY.size() ) <= i )
739 {
740 return QgsPoint();
741 }
742
743 double x = mX.at( i );
744 double y = mY.at( i );
745 double z = 0;
746 double m = 0;
747
748 if ( is3D() )
749 {
750 z = mZ.at( i );
751 }
752 if ( isMeasure() )
753 {
754 m = mM.at( i );
755 }
756
758 if ( is3D() && isMeasure() )
759 {
761 }
762 else if ( is3D() )
763 {
765 }
766 else if ( isMeasure() )
767 {
769 }
770 return QgsPoint( t, x, y, z, m );
771}
772
773double QgsCircularString::xAt( int index ) const
774{
775 if ( index >= 0 && index < mX.size() )
776 return mX.at( index );
777 else
778 return 0.0;
779}
780
781double QgsCircularString::yAt( int index ) const
782{
783 if ( index >= 0 && index < mY.size() )
784 return mY.at( index );
785 else
786 return 0.0;
787}
788
789double QgsCircularString::zAt( int index ) const
790{
791 if ( index >= 0 && index < mZ.size() )
792 return mZ.at( index );
793 else
794 return 0.0;
795}
796
797double QgsCircularString::mAt( int index ) const
798{
799 if ( index >= 0 && index < mM.size() )
800 return mM.at( index );
801 else
802 return 0.0;
803}
804
806{
807 if ( !transformer )
808 return false;
809
810 bool hasZ = is3D();
811 bool hasM = isMeasure();
812 int size = mX.size();
813
814 double *srcX = mX.data();
815 double *srcY = mY.data();
816 double *srcM = hasM ? mM.data() : nullptr;
817 double *srcZ = hasZ ? mZ.data() : nullptr;
818
819 bool res = true;
820 for ( int i = 0; i < size; ++i )
821 {
822 double x = *srcX;
823 double y = *srcY;
824 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
825 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
826 if ( !transformer->transformPoint( x, y, z, m ) )
827 {
828 res = false;
829 break;
830 }
831
832 *srcX++ = x;
833 *srcY++ = y;
834 if ( hasM )
835 *srcM++ = m;
836 if ( hasZ )
837 *srcZ++ = z;
838
839 if ( feedback && feedback->isCanceled() )
840 {
841 res = false;
842 break;
843 }
844 }
845 clearCache();
846 return res;
847}
848
849void QgsCircularString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
850{
851 bool hasZ = is3D();
852 bool hasM = isMeasure();
853 int size = mX.size();
854
855 double *srcX = mX.data(); // clazy:exclude=detaching-member
856 double *srcY = mY.data(); // clazy:exclude=detaching-member
857 double *srcM = hasM ? mM.data() : nullptr; // clazy:exclude=detaching-member
858 double *srcZ = hasZ ? mZ.data() : nullptr; // clazy:exclude=detaching-member
859
860 double *destX = srcX;
861 double *destY = srcY;
862 double *destM = srcM;
863 double *destZ = srcZ;
864
865 int filteredPoints = 0;
866 for ( int i = 0; i < size; ++i )
867 {
868 double x = *srcX++;
869 double y = *srcY++;
870 double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
871 double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
872
873 if ( filter( QgsPoint( x, y, z, m ) ) )
874 {
875 filteredPoints++;
876 *destX++ = x;
877 *destY++ = y;
878 if ( hasM )
879 *destM++ = m;
880 if ( hasZ )
881 *destZ++ = z;
882 }
883 }
884
885 mX.resize( filteredPoints );
886 mY.resize( filteredPoints );
887 if ( hasZ )
888 mZ.resize( filteredPoints );
889 if ( hasM )
890 mM.resize( filteredPoints );
891
892 clearCache();
893}
894
895void QgsCircularString::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
896{
897 bool hasZ = is3D();
898 bool hasM = isMeasure();
899 int size = mX.size();
900
901 double *srcX = mX.data();
902 double *srcY = mY.data();
903 double *srcM = hasM ? mM.data() : nullptr;
904 double *srcZ = hasZ ? mZ.data() : nullptr;
905
906 for ( int i = 0; i < size; ++i )
907 {
908 double x = *srcX;
909 double y = *srcY;
910 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
911 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
912 QgsPoint res = transform( QgsPoint( x, y, z, m ) );
913 *srcX++ = res.x();
914 *srcY++ = res.y();
915 if ( hasM )
916 *srcM++ = res.m();
917 if ( hasZ )
918 *srcZ++ = res.z();
919 }
920 clearCache();
921}
922
923std::tuple<std::unique_ptr<QgsCurve>, std::unique_ptr<QgsCurve> > QgsCircularString::splitCurveAtVertex( int index ) const
924{
925 const bool useZ = is3D();
926 const bool useM = isMeasure();
927
928 const int size = mX.size();
929 if ( size == 0 )
930 return std::make_tuple( std::make_unique< QgsCircularString >(), std::make_unique< QgsCircularString >() );
931
932 index = std::clamp( index, 0, size - 1 );
933
934 const int part1Size = index + 1;
935 QVector< double > x1( part1Size );
936 QVector< double > y1( part1Size );
937 QVector< double > z1( useZ ? part1Size : 0 );
938 QVector< double > m1( useM ? part1Size : 0 );
939
940 const double *sourceX = mX.constData();
941 const double *sourceY = mY.constData();
942 const double *sourceZ = useZ ? mZ.constData() : nullptr;
943 const double *sourceM = useM ? mM.constData() : nullptr;
944
945 double *destX = x1.data();
946 double *destY = y1.data();
947 double *destZ = useZ ? z1.data() : nullptr;
948 double *destM = useM ? m1.data() : nullptr;
949
950 std::copy( sourceX, sourceX + part1Size, destX );
951 std::copy( sourceY, sourceY + part1Size, destY );
952 if ( useZ )
953 std::copy( sourceZ, sourceZ + part1Size, destZ );
954 if ( useM )
955 std::copy( sourceM, sourceM + part1Size, destM );
956
957 const int part2Size = size - index;
958 if ( part2Size < 2 )
959 return std::make_tuple( std::make_unique< QgsCircularString >( x1, y1, z1, m1 ), std::make_unique< QgsCircularString >() );
960
961 QVector< double > x2( part2Size );
962 QVector< double > y2( part2Size );
963 QVector< double > z2( useZ ? part2Size : 0 );
964 QVector< double > m2( useM ? part2Size : 0 );
965 destX = x2.data();
966 destY = y2.data();
967 destZ = useZ ? z2.data() : nullptr;
968 destM = useM ? m2.data() : nullptr;
969 std::copy( sourceX + index, sourceX + size, destX );
970 std::copy( sourceY + index, sourceY + size, destY );
971 if ( useZ )
972 std::copy( sourceZ + index, sourceZ + size, destZ );
973 if ( useM )
974 std::copy( sourceM + index, sourceM + size, destM );
975
976 if ( part1Size < 2 )
977 return std::make_tuple( std::make_unique< QgsCircularString >(), std::make_unique< QgsCircularString >( x2, y2, z2, m2 ) );
978 else
979 return std::make_tuple( std::make_unique< QgsCircularString >( x1, y1, z1, m1 ), std::make_unique< QgsCircularString >( x2, y2, z2, m2 ) );
980}
981
983{
984 pts.clear();
985 int nPts = numPoints();
986 for ( int i = 0; i < nPts; ++i )
987 {
988 pts.push_back( pointN( i ) );
989 }
990}
991
993{
994 clearCache();
995
996 if ( points.empty() )
997 {
999 mX.clear();
1000 mY.clear();
1001 mZ.clear();
1002 mM.clear();
1003 return;
1004 }
1005
1006 //get wkb type from first point
1007 const QgsPoint &firstPt = points.at( 0 );
1008 bool hasZ = firstPt.is3D();
1009 bool hasM = firstPt.isMeasure();
1010
1012
1013 mX.resize( points.size() );
1014 mY.resize( points.size() );
1015 if ( hasZ )
1016 {
1017 mZ.resize( points.size() );
1018 }
1019 else
1020 {
1021 mZ.clear();
1022 }
1023 if ( hasM )
1024 {
1025 mM.resize( points.size() );
1026 }
1027 else
1028 {
1029 mM.clear();
1030 }
1031
1032 for ( int i = 0; i < points.size(); ++i )
1033 {
1034 mX[i] = points[i].x();
1035 mY[i] = points[i].y();
1036 if ( hasZ )
1037 {
1038 double z = points.at( i ).z();
1039 mZ[i] = std::isnan( z ) ? 0 : z;
1040 }
1041 if ( hasM )
1042 {
1043 double m = points.at( i ).m();
1044 mM[i] = std::isnan( m ) ? 0 : m;
1045 }
1046 }
1047}
1048
1050{
1051 if ( !line || line->isEmpty() )
1052 {
1053 return;
1054 }
1055
1056 if ( numPoints() < 1 )
1057 {
1059 }
1060
1061 // do not store duplicate points
1062 if ( numPoints() > 0 &&
1063 line->numPoints() > 0 &&
1064 qgsDoubleNear( endPoint().x(), line->startPoint().x() ) &&
1065 qgsDoubleNear( endPoint().y(), line->startPoint().y() ) &&
1066 ( !is3D() || !line->is3D() || qgsDoubleNear( endPoint().z(), line->startPoint().z() ) ) &&
1067 ( !isMeasure() || !line->isMeasure() || qgsDoubleNear( endPoint().m(), line->startPoint().m() ) ) )
1068 {
1069 mX.pop_back();
1070 mY.pop_back();
1071
1072 if ( is3D() && line->is3D() )
1073 {
1074 mZ.pop_back();
1075 }
1076 if ( isMeasure() && line->isMeasure() )
1077 {
1078 mM.pop_back();
1079 }
1080 }
1081
1082 mX += line->mX;
1083 mY += line->mY;
1084
1085 if ( is3D() )
1086 {
1087 if ( line->is3D() )
1088 {
1089 mZ += line->mZ;
1090 }
1091 else
1092 {
1093 // if append line does not have z coordinates, fill with NaN to match number of points in final line
1094 mZ.insert( mZ.count(), mX.size() - mZ.size(), std::numeric_limits<double>::quiet_NaN() );
1095 }
1096 }
1097
1098 if ( isMeasure() )
1099 {
1100 if ( line->isMeasure() )
1101 {
1102 mM += line->mM;
1103 }
1104 else
1105 {
1106 // if append line does not have m values, fill with NaN to match number of points in final line
1107 mM.insert( mM.count(), mX.size() - mM.size(), std::numeric_limits<double>::quiet_NaN() );
1108 }
1109 }
1110
1111 clearCache(); //set bounding box invalid
1112}
1113
1114void QgsCircularString::draw( QPainter &p ) const
1115{
1116 QPainterPath path;
1117 addToPainterPath( path );
1118 p.drawPath( path );
1119}
1120
1122{
1123 clearCache();
1124
1125 double *zArray = mZ.data();
1126
1127 bool hasZ = is3D();
1128 int nPoints = numPoints();
1129 bool useDummyZ = !hasZ || !transformZ;
1130 if ( useDummyZ )
1131 {
1132 zArray = new double[nPoints];
1133 for ( int i = 0; i < nPoints; ++i )
1134 {
1135 zArray[i] = 0;
1136 }
1137 }
1138 ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
1139 if ( useDummyZ )
1140 {
1141 delete[] zArray;
1142 }
1143}
1144
1145void QgsCircularString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
1146{
1147 clearCache();
1148
1149 int nPoints = numPoints();
1150 bool hasZ = is3D();
1151 bool hasM = isMeasure();
1152 for ( int i = 0; i < nPoints; ++i )
1153 {
1154 qreal x, y;
1155 t.map( mX.at( i ), mY.at( i ), &x, &y );
1156 mX[i] = x;
1157 mY[i] = y;
1158 if ( hasZ )
1159 {
1160 mZ[i] = mZ.at( i ) * zScale + zTranslate;
1161 }
1162 if ( hasM )
1163 {
1164 mM[i] = mM.at( i ) * mScale + mTranslate;
1165 }
1166 }
1167}
1168
1169void arcTo( QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3 )
1170{
1171 double centerX, centerY, radius;
1172 QgsGeometryUtils::circleCenterRadius( QgsPoint( pt1.x(), pt1.y() ), QgsPoint( pt2.x(), pt2.y() ), QgsPoint( pt3.x(), pt3.y() ),
1173 radius, centerX, centerY );
1174
1175 double p1Angle = QgsGeometryUtilsBase::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
1176 double sweepAngle = QgsGeometryUtilsBase::sweepAngle( centerX, centerY, pt1.x(), pt1.y(), pt2.x(), pt2.y(), pt3.x(), pt3.y() );
1177
1178 double diameter = 2 * radius;
1179 path.arcTo( centerX - radius, centerY - radius, diameter, diameter, -p1Angle, -sweepAngle );
1180}
1181
1182void QgsCircularString::addToPainterPath( QPainterPath &path ) const
1183{
1184 int nPoints = numPoints();
1185 if ( nPoints < 1 )
1186 {
1187 return;
1188 }
1189
1190 if ( path.isEmpty() || path.currentPosition() != QPointF( mX[0], mY[0] ) )
1191 {
1192 path.moveTo( QPointF( mX[0], mY[0] ) );
1193 }
1194
1195 for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
1196 {
1197 arcTo( path, QPointF( mX[i], mY[i] ), QPointF( mX[i + 1], mY[i + 1] ), QPointF( mX[i + 2], mY[i + 2] ) );
1198 }
1199
1200 //if number of points is even, connect to last point with straight line (even though the circular string is not valid)
1201 if ( nPoints % 2 == 0 )
1202 {
1203 path.lineTo( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
1204 }
1205}
1206
1207void QgsCircularString::drawAsPolygon( QPainter &p ) const
1208{
1209 draw( p );
1210}
1211
1213{
1214 if ( position.vertex >= mX.size() || position.vertex < 1 )
1215 {
1216 return false;
1217 }
1218
1219 mX.insert( position.vertex, vertex.x() );
1220 mY.insert( position.vertex, vertex.y() );
1221 if ( is3D() )
1222 {
1223 mZ.insert( position.vertex, vertex.z() );
1224 }
1225 if ( isMeasure() )
1226 {
1227 mM.insert( position.vertex, vertex.m() );
1228 }
1229
1230 bool vertexNrEven = ( position.vertex % 2 == 0 );
1231 if ( vertexNrEven )
1232 {
1233 insertVertexBetween( position.vertex - 2, position.vertex - 1, position.vertex );
1234 }
1235 else
1236 {
1237 insertVertexBetween( position.vertex, position.vertex + 1, position.vertex - 1 );
1238 }
1239 clearCache(); //set bounding box invalid
1240 return true;
1241}
1242
1244{
1245 if ( position.vertex < 0 || position.vertex >= mX.size() )
1246 {
1247 return false;
1248 }
1249
1250 mX[position.vertex] = newPos.x();
1251 mY[position.vertex] = newPos.y();
1252 if ( is3D() && newPos.is3D() )
1253 {
1254 mZ[position.vertex] = newPos.z();
1255 }
1256 if ( isMeasure() && newPos.isMeasure() )
1257 {
1258 mM[position.vertex] = newPos.m();
1259 }
1260 clearCache(); //set bounding box invalid
1261 return true;
1262}
1263
1265{
1266 int nVertices = this->numPoints();
1267 if ( nVertices < 4 ) //circular string must have at least 3 vertices
1268 {
1269 clear();
1270 return true;
1271 }
1272 if ( position.vertex < 0 || position.vertex > ( nVertices - 1 ) )
1273 {
1274 return false;
1275 }
1276
1277 if ( position.vertex < ( nVertices - 2 ) )
1278 {
1279 //remove this and the following vertex
1280 deleteVertex( position.vertex + 1 );
1281 deleteVertex( position.vertex );
1282 }
1283 else //remove this and the preceding vertex
1284 {
1285 deleteVertex( position.vertex );
1286 deleteVertex( position.vertex - 1 );
1287 }
1288
1289 clearCache(); //set bounding box invalid
1290 return true;
1291}
1292
1294{
1295 mX.remove( i );
1296 mY.remove( i );
1297 if ( is3D() )
1298 {
1299 mZ.remove( i );
1300 }
1301 if ( isMeasure() )
1302 {
1303 mM.remove( i );
1304 }
1305 clearCache();
1306}
1307
1308double QgsCircularString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1309{
1310 double minDist = std::numeric_limits<double>::max();
1311 QgsPoint minDistSegmentPoint;
1312 QgsVertexId minDistVertexAfter;
1313 int minDistLeftOf = 0;
1314
1315 double currentDist = 0.0;
1316
1317 int nPoints = numPoints();
1318 for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
1319 {
1320 currentDist = closestPointOnArc( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2], pt, segmentPt, vertexAfter, leftOf, epsilon );
1321 if ( currentDist < minDist )
1322 {
1323 minDist = currentDist;
1324 minDistSegmentPoint = segmentPt;
1325 minDistVertexAfter.vertex = vertexAfter.vertex + i;
1326 if ( leftOf )
1327 {
1328 minDistLeftOf = *leftOf;
1329 }
1330 }
1331 }
1332
1333 if ( minDist == std::numeric_limits<double>::max() )
1334 return -1; // error: no segments
1335
1336 segmentPt = minDistSegmentPoint;
1337 vertexAfter = minDistVertexAfter;
1338 vertexAfter.part = 0;
1339 vertexAfter.ring = 0;
1340 if ( leftOf )
1341 {
1342 *leftOf = qgsDoubleNear( minDist, 0.0 ) ? 0 : minDistLeftOf;
1343 }
1344 return minDist;
1345}
1346
1347bool QgsCircularString::pointAt( int node, QgsPoint &point, Qgis::VertexType &type ) const
1348{
1349 if ( node < 0 || node >= numPoints() )
1350 {
1351 return false;
1352 }
1353 point = pointN( node );
1354 type = ( node % 2 == 0 ) ? Qgis::VertexType::Segment : Qgis::VertexType::Curve;
1355 return true;
1356}
1357
1358void QgsCircularString::sumUpArea( double &sum ) const
1359{
1361 {
1362 sum += mSummedUpArea;
1363 return;
1364 }
1365
1366 int maxIndex = numPoints() - 2;
1367 mSummedUpArea = 0;
1368 for ( int i = 0; i < maxIndex; i += 2 )
1369 {
1370 QgsPoint p1( mX[i], mY[i] );
1371 QgsPoint p2( mX[i + 1], mY[i + 1] );
1372 QgsPoint p3( mX[i + 2], mY[i + 2] );
1373
1374 //segment is a full circle, p2 is the center point
1375 if ( p1 == p3 )
1376 {
1377 double r2 = QgsGeometryUtils::sqrDistance2D( p1, p2 ) / 4.0;
1378 mSummedUpArea += M_PI * r2;
1379 continue;
1380 }
1381
1382 mSummedUpArea += 0.5 * ( mX[i] * mY[i + 2] - mY[i] * mX[i + 2] );
1383
1384 //calculate area between circle and chord, then sum / subtract from total area
1385 double midPointX = ( p1.x() + p3.x() ) / 2.0;
1386 double midPointY = ( p1.y() + p3.y() ) / 2.0;
1387
1388 double radius, centerX, centerY;
1389 QgsGeometryUtils::circleCenterRadius( p1, p2, p3, radius, centerX, centerY );
1390
1391 double d = std::sqrt( QgsGeometryUtils::sqrDistance2D( QgsPoint( centerX, centerY ), QgsPoint( midPointX, midPointY ) ) );
1392 double r2 = radius * radius;
1393
1394 if ( d > radius )
1395 {
1396 //d cannot be greater than radius, something must be wrong...
1397 continue;
1398 }
1399
1400 bool circlePointLeftOfLine = QgsGeometryUtilsBase::leftOfLine( p2.x(), p2.y(), p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
1401 bool centerPointLeftOfLine = QgsGeometryUtilsBase::leftOfLine( centerX, centerY, p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
1402
1403 double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - M_1_PI * std::asin( d / radius );
1404 double circleChordArea = 0;
1405 if ( circlePointLeftOfLine == centerPointLeftOfLine )
1406 {
1407 circleChordArea = M_PI * r2 * ( 1 - cov );
1408 }
1409 else
1410 {
1411 circleChordArea = M_PI * r2 * cov;
1412 }
1413
1414 if ( !circlePointLeftOfLine )
1415 {
1416 mSummedUpArea += circleChordArea;
1417 }
1418 else
1419 {
1420 mSummedUpArea -= circleChordArea;
1421 }
1422 }
1423
1425 sum += mSummedUpArea;
1426}
1427
1429{
1430 return true;
1431}
1432
1433double QgsCircularString::closestPointOnArc( double x1, double y1, double x2, double y2, double x3, double y3,
1434 const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon )
1435{
1436 double radius, centerX, centerY;
1437 QgsPoint pt1( x1, y1 );
1438 QgsPoint pt2( x2, y2 );
1439 QgsPoint pt3( x3, y3 );
1440
1441 QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
1442 double angle = QgsGeometryUtilsBase::ccwAngle( pt.y() - centerY, pt.x() - centerX );
1443 double angle1 = QgsGeometryUtilsBase::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
1444 double angle2 = QgsGeometryUtilsBase::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
1445 double angle3 = QgsGeometryUtilsBase::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
1446
1447 bool clockwise = QgsGeometryUtilsBase::circleClockwise( angle1, angle2, angle3 );
1448
1449 if ( QgsGeometryUtilsBase::angleOnCircle( angle, angle1, angle2, angle3 ) )
1450 {
1451 //get point on line center -> pt with distance radius
1452 segmentPt = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), pt, radius );
1453
1454 //vertexAfter
1455 vertexAfter.vertex = QgsGeometryUtilsBase::circleAngleBetween( angle, angle1, angle2, clockwise ) ? 1 : 2;
1456 }
1457 else
1458 {
1459 double distPtPt1 = QgsGeometryUtils::sqrDistance2D( pt, pt1 );
1460 double distPtPt3 = QgsGeometryUtils::sqrDistance2D( pt, pt3 );
1461 segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
1462 vertexAfter.vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
1463 }
1464
1465 double sqrDistance = QgsGeometryUtils::sqrDistance2D( segmentPt, pt );
1466 //prevent rounding errors if the point is directly on the segment
1467 if ( qgsDoubleNear( sqrDistance, 0.0, epsilon ) )
1468 {
1469 segmentPt.setX( pt.x() );
1470 segmentPt.setY( pt.y() );
1471 sqrDistance = 0.0;
1472 }
1473
1474 if ( leftOf )
1475 {
1476 double sqrDistancePointToCenter = pt.distanceSquared( centerX, centerY );
1477 *leftOf = clockwise ? ( sqrDistancePointToCenter > radius * radius ? -1 : 1 )
1478 : ( sqrDistancePointToCenter < radius * radius ? -1 : 1 );
1479 }
1480
1481 return sqrDistance;
1482}
1483
1484void QgsCircularString::insertVertexBetween( int after, int before, int pointOnCircle )
1485{
1486 double xAfter = mX.at( after );
1487 double yAfter = mY.at( after );
1488 double xBefore = mX.at( before );
1489 double yBefore = mY.at( before );
1490 double xOnCircle = mX.at( pointOnCircle );
1491 double yOnCircle = mY.at( pointOnCircle );
1492
1493 double radius, centerX, centerY;
1494 QgsGeometryUtils::circleCenterRadius( QgsPoint( xAfter, yAfter ), QgsPoint( xBefore, yBefore ), QgsPoint( xOnCircle, yOnCircle ), radius, centerX, centerY );
1495
1496 double x = ( xAfter + xBefore ) / 2.0;
1497 double y = ( yAfter + yBefore ) / 2.0;
1498
1499 QgsPoint newVertex = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), QgsPoint( x, y ), radius );
1500 mX.insert( before, newVertex.x() );
1501 mY.insert( before, newVertex.y() );
1502
1503 if ( is3D() )
1504 {
1505 mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
1506 }
1507 if ( isMeasure() )
1508 {
1509 mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
1510 }
1511 clearCache();
1512}
1513
1515{
1516 if ( numPoints() < 3 )
1517 {
1518 //undefined
1519 return 0.0;
1520 }
1521
1522 int before = vId.vertex - 1;
1523 int vertex = vId.vertex;
1524 int after = vId.vertex + 1;
1525
1526 if ( vId.vertex % 2 != 0 ) // a curve vertex
1527 {
1528 if ( vId.vertex >= 1 && vId.vertex < numPoints() - 1 )
1529 {
1530 return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[before], mY[before] ),
1531 QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[after], mY[after] ) );
1532 }
1533 }
1534 else //a point vertex
1535 {
1536 if ( vId.vertex == 0 )
1537 {
1538 return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[0], mY[0] ), QgsPoint( mX[0], mY[0] ),
1539 QgsPoint( mX[1], mY[1] ), QgsPoint( mX[2], mY[2] ) );
1540 }
1541 if ( vId.vertex >= numPoints() - 1 )
1542 {
1543 int a = numPoints() - 3;
1544 int b = numPoints() - 2;
1545 int c = numPoints() - 1;
1546 return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[c], mY[c] ), QgsPoint( mX[a], mY[a] ),
1547 QgsPoint( mX[b], mY[b] ), QgsPoint( mX[c], mY[c] ) );
1548 }
1549 else
1550 {
1551 if ( vId.vertex + 2 > numPoints() - 1 )
1552 {
1553 return 0.0;
1554 }
1555
1556 int vertex1 = vId.vertex - 2;
1557 int vertex2 = vId.vertex - 1;
1558 int vertex3 = vId.vertex;
1559 double angle1 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1560 QgsPoint( mX[vertex1], mY[vertex1] ), QgsPoint( mX[vertex2], mY[vertex2] ), QgsPoint( mX[vertex3], mY[vertex3] ) );
1561 int vertex4 = vId.vertex + 1;
1562 int vertex5 = vId.vertex + 2;
1563 double angle2 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1564 QgsPoint( mX[vertex3], mY[vertex3] ), QgsPoint( mX[vertex4], mY[vertex4] ), QgsPoint( mX[vertex5], mY[vertex5] ) );
1565 return QgsGeometryUtilsBase::averageAngle( angle1, angle2 );
1566 }
1567 }
1568 return 0.0;
1569}
1570
1572{
1573 if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 2 )
1574 return 0.0;
1575
1576 if ( startVertex.vertex % 2 == 1 )
1577 return 0.0; // curve point?
1578
1579 double x1 = mX.at( startVertex.vertex );
1580 double y1 = mY.at( startVertex.vertex );
1581 double x2 = mX.at( startVertex.vertex + 1 );
1582 double y2 = mY.at( startVertex.vertex + 1 );
1583 double x3 = mX.at( startVertex.vertex + 2 );
1584 double y3 = mY.at( startVertex.vertex + 2 );
1585 return QgsGeometryUtilsBase::circleLength( x1, y1, x2, y2, x3, y3 );
1586}
1587
1589{
1590 QgsCircularString *copy = clone();
1591 std::reverse( copy->mX.begin(), copy->mX.end() );
1592 std::reverse( copy->mY.begin(), copy->mY.end() );
1593 if ( is3D() )
1594 {
1595 std::reverse( copy->mZ.begin(), copy->mZ.end() );
1596 }
1597 if ( isMeasure() )
1598 {
1599 std::reverse( copy->mM.begin(), copy->mM.end() );
1600 }
1601 return copy;
1602}
1603
1604QgsPoint *QgsCircularString::interpolatePoint( const double distance ) const
1605{
1606 if ( distance < 0 )
1607 return nullptr;
1608
1609 double distanceTraversed = 0;
1610 const int totalPoints = numPoints();
1611 if ( totalPoints == 0 )
1612 return nullptr;
1613
1615 if ( is3D() )
1616 pointType = Qgis::WkbType::PointZ;
1617 if ( isMeasure() )
1618 pointType = QgsWkbTypes::addM( pointType );
1619
1620 const double *x = mX.constData();
1621 const double *y = mY.constData();
1622 const double *z = is3D() ? mZ.constData() : nullptr;
1623 const double *m = isMeasure() ? mM.constData() : nullptr;
1624
1625 double prevX = *x++;
1626 double prevY = *y++;
1627 double prevZ = z ? *z++ : 0.0;
1628 double prevM = m ? *m++ : 0.0;
1629
1630 if ( qgsDoubleNear( distance, 0.0 ) )
1631 {
1632 return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1633 }
1634
1635 for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1636 {
1637 double x1 = prevX;
1638 double y1 = prevY;
1639 double z1 = prevZ;
1640 double m1 = prevM;
1641
1642 double x2 = *x++;
1643 double y2 = *y++;
1644 double z2 = z ? *z++ : 0.0;
1645 double m2 = m ? *m++ : 0.0;
1646
1647 double x3 = *x++;
1648 double y3 = *y++;
1649 double z3 = z ? *z++ : 0.0;
1650 double m3 = m ? *m++ : 0.0;
1651
1652 const double segmentLength = QgsGeometryUtilsBase::circleLength( x1, y1, x2, y2, x3, y3 );
1653 if ( distance < distanceTraversed + segmentLength || qgsDoubleNear( distance, distanceTraversed + segmentLength ) )
1654 {
1655 // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1656 const double distanceToPoint = std::min( distance - distanceTraversed, segmentLength );
1657 return new QgsPoint( QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1658 QgsPoint( pointType, x2, y2, z2, m2 ),
1659 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToPoint ) );
1660 }
1661
1662 distanceTraversed += segmentLength;
1663
1664 prevX = x3;
1665 prevY = y3;
1666 prevZ = z3;
1667 prevM = m3;
1668 }
1669
1670 return nullptr;
1671}
1672
1673QgsCircularString *QgsCircularString::curveSubstring( double startDistance, double endDistance ) const
1674{
1675 if ( startDistance < 0 && endDistance < 0 )
1676 return createEmptyWithSameType();
1677
1678 endDistance = std::max( startDistance, endDistance );
1679
1680 const int totalPoints = numPoints();
1681 if ( totalPoints == 0 )
1682 return clone();
1683
1684 QVector< QgsPoint > substringPoints;
1685 substringPoints.reserve( totalPoints );
1686
1688 if ( is3D() )
1689 pointType = Qgis::WkbType::PointZ;
1690 if ( isMeasure() )
1691 pointType = QgsWkbTypes::addM( pointType );
1692
1693 const double *x = mX.constData();
1694 const double *y = mY.constData();
1695 const double *z = is3D() ? mZ.constData() : nullptr;
1696 const double *m = isMeasure() ? mM.constData() : nullptr;
1697
1698 double distanceTraversed = 0;
1699 double prevX = *x++;
1700 double prevY = *y++;
1701 double prevZ = z ? *z++ : 0.0;
1702 double prevM = m ? *m++ : 0.0;
1703 bool foundStart = false;
1704
1705 if ( startDistance < 0 )
1706 startDistance = 0;
1707
1708 for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1709 {
1710 double x1 = prevX;
1711 double y1 = prevY;
1712 double z1 = prevZ;
1713 double m1 = prevM;
1714
1715 double x2 = *x++;
1716 double y2 = *y++;
1717 double z2 = z ? *z++ : 0.0;
1718 double m2 = m ? *m++ : 0.0;
1719
1720 double x3 = *x++;
1721 double y3 = *y++;
1722 double z3 = z ? *z++ : 0.0;
1723 double m3 = m ? *m++ : 0.0;
1724
1725 bool addedSegmentEnd = false;
1726 const double segmentLength = QgsGeometryUtilsBase::circleLength( x1, y1, x2, y2, x3, y3 );
1727 if ( distanceTraversed <= startDistance && startDistance < distanceTraversed + segmentLength )
1728 {
1729 // start point falls on this segment
1730 const double distanceToStart = startDistance - distanceTraversed;
1731 const QgsPoint startPoint = QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1732 QgsPoint( pointType, x2, y2, z2, m2 ),
1733 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToStart );
1734
1735 // does end point also fall on this segment?
1736 const bool endPointOnSegment = distanceTraversed + segmentLength > endDistance;
1737 if ( endPointOnSegment )
1738 {
1739 const double distanceToEnd = endDistance - distanceTraversed;
1740 const double midPointDistance = ( distanceToEnd - distanceToStart ) * 0.5 + distanceToStart;
1741 substringPoints << startPoint
1742 << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1743 QgsPoint( pointType, x2, y2, z2, m2 ),
1744 QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1745 << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1746 QgsPoint( pointType, x2, y2, z2, m2 ),
1747 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1748 addedSegmentEnd = true;
1749 }
1750 else
1751 {
1752 const double midPointDistance = ( segmentLength - distanceToStart ) * 0.5 + distanceToStart;
1753 substringPoints << startPoint
1754 << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1755 QgsPoint( pointType, x2, y2, z2, m2 ),
1756 QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1757 << QgsPoint( pointType, x3, y3, z3, m3 );
1758 addedSegmentEnd = true;
1759 }
1760 foundStart = true;
1761 }
1762 if ( !addedSegmentEnd && foundStart && ( distanceTraversed + segmentLength > endDistance ) )
1763 {
1764 // end point falls on this segment
1765 const double distanceToEnd = endDistance - distanceTraversed;
1766 // add mid point, at half way along this arc, then add the interpolated end point
1767 substringPoints << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1768 QgsPoint( pointType, x2, y2, z2, m2 ),
1769 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd / 2.0 )
1770
1771 << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1772 QgsPoint( pointType, x2, y2, z2, m2 ),
1773 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1774 }
1775 else if ( !addedSegmentEnd && foundStart )
1776 {
1777 substringPoints << QgsPoint( pointType, x2, y2, z2, m2 )
1778 << QgsPoint( pointType, x3, y3, z3, m3 );
1779 }
1780
1781 prevX = x3;
1782 prevY = y3;
1783 prevZ = z3;
1784 prevM = m3;
1785 distanceTraversed += segmentLength;
1786 if ( distanceTraversed >= endDistance )
1787 break;
1788 }
1789
1790 // start point is the last node
1791 if ( !foundStart && qgsDoubleNear( distanceTraversed, startDistance ) )
1792 {
1793 substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM )
1794 << QgsPoint( pointType, prevX, prevY, prevZ, prevM )
1795 << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1796 }
1797
1798 std::unique_ptr< QgsCircularString > result = std::make_unique< QgsCircularString >();
1799 result->setPoints( substringPoints );
1800 return result.release();
1801}
1802
1803bool QgsCircularString::addZValue( double zValue )
1804{
1805 if ( QgsWkbTypes::hasZ( mWkbType ) )
1806 return false;
1807
1808 clearCache();
1810
1811 int nPoints = numPoints();
1812 mZ.clear();
1813 mZ.reserve( nPoints );
1814 for ( int i = 0; i < nPoints; ++i )
1815 {
1816 mZ << zValue;
1817 }
1818 return true;
1819}
1820
1821bool QgsCircularString::addMValue( double mValue )
1822{
1823 if ( QgsWkbTypes::hasM( mWkbType ) )
1824 return false;
1825
1826 clearCache();
1828
1829 int nPoints = numPoints();
1830 mM.clear();
1831 mM.reserve( nPoints );
1832 for ( int i = 0; i < nPoints; ++i )
1833 {
1834 mM << mValue;
1835 }
1836 return true;
1837}
1838
1840{
1841 if ( !QgsWkbTypes::hasZ( mWkbType ) )
1842 return false;
1843
1844 clearCache();
1845
1847 mZ.clear();
1848 return true;
1849}
1850
1852{
1853 if ( !QgsWkbTypes::hasM( mWkbType ) )
1854 return false;
1855
1856 clearCache();
1857
1859 mM.clear();
1860 return true;
1861}
1862
1864{
1865 std::swap( mX, mY );
1866 clearCache();
1867}
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
Definition: qgis.h:1648
VertexType
Types of vertex.
Definition: qgis.h:2477
@ Curve
An intermediate point on a segment defining the curvature of the segment.
@ Segment
The actual start or end point of a segment.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition: qgis.h:182
@ PointM
PointM.
@ CircularString
CircularString.
@ PointZ
PointZ.
@ PointZM
PointZM.
@ CircularStringZ
CircularStringZ.
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition: qgis.h:2191
An abstract base class for classes which transform geometries by transforming input points to output ...
virtual bool transformPoint(double &x, double &y, double &z, double &m)=0
Transforms the point defined by the coordinates (x, y, z) and the specified m value.
Abstract base class for all geometries.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
bool isMeasure() const
Returns true if the geometry contains m values.
QFlags< WkbFlag > WkbFlags
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
AxisOrder
Axis order for GML generation.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, Qgis::WkbType baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
static endian_t endian()
Returns whether this machine uses big or little endian.
A 3-dimensional box composed of x, y, z coordinates.
Definition: qgsbox3d.h:43
void combineWith(const QgsBox3D &box)
Expands the bbox so that it covers both the original rectangle and the given rectangle.
Definition: qgsbox3d.cpp:196
Circular string geometry type.
double length() const override
Returns the planar, 2-dimensional length of the geometry.
QString geometryType() const override
Returns a unique string representing the geometry type.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
QgsCircularString * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid.
QgsCircularString * clone() const override
Clones the geometry by performing a deep copy.
QgsPoint endPoint() const override
Returns the end point of the curve.
void append(const QgsCircularString *string)
Appends the contents of another circular string to the end of this circular string.
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
QgsCircularString * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *std::numeric_limits< double >::epsilon()) const override
Searches for the closest segment of the geometry to a given point.
void swapXy() override
Swaps the x and y coordinates from the geometry.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
static QgsCircularString fromTwoPointsAndCenter(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, bool useShortestArc=true)
Creates a circular string with a single arc representing the curve from p1 to p2 with the specified c...
void filterVertices(const std::function< bool(const QgsPoint &) > &filter) override
Filters the vertices from the geometry in place, removing any which do not return true for the filter...
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML2 representation of the geometry.
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
QgsCircularString * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
QgsBox3D calculateBoundingBox3D() const override
Calculates the minimal 3D bounding box for the geometry.
QgsPoint startPoint() const override
Returns the starting point of the curve.
bool pointAt(int node, QgsPoint &point, Qgis::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
bool isEmpty() const override
Returns true if the geometry is empty.
int indexOf(const QgsPoint &point) const final
Returns the index of the first vertex matching the given point, or -1 if a matching vertex is not fou...
bool dropMValue() override
Drops any measure values which exist in the geometry.
int numPoints() const override
Returns the number of points in the curve.
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb()
QgsCircularString * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
int dimension() const override
Returns the inherent dimension of the geometry.
void setPoints(const QgsPointSequence &points)
Sets the circular string's points.
QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a new line string geometry corresponding to a segmentized approximation of the curve.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML3 representation of the geometry.
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns a WKB representation of the geometry.
void clear() override
Clears the geometry, ie reset it to a null geometry.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
void scroll(int firstVertexIndex) final
Scrolls the curve vertices so that they start with the vertex at the given index.
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
double zAt(int index) const override
Returns the z-coordinate of the specified node in the line string.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
QgsCircularString()
Constructs an empty circular string.
QgsPoint pointN(int i) const
Returns the point at index i within the circular string.
std::tuple< std::unique_ptr< QgsCurve >, std::unique_ptr< QgsCurve > > splitCurveAtVertex(int index) const final
Splits the curve at the specified vertex index, returning two curves which represent the portion of t...
double mAt(int index) const override
Returns the m-coordinate of the specified node in the line string.
int compareToSameClass(const QgsAbstractGeometry *other) const final
Compares to an other geometry of the same class, and returns a integer for sorting of the two geometr...
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
void transformVertices(const std::function< QgsPoint(const QgsPoint &) > &transform) override
Transforms the vertices from the geometry in place, applying the transform function to every vertex.
A const WKB pointer.
Definition: qgswkbptr.h:138
Qgis::WkbType readHeader() const
readHeader
Definition: qgswkbptr.cpp:55
Class for doing transforms between two map coordinate systems.
void transformCoords(int numPoint, double *x, double *y, double *z, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform an array of coordinates to the destination CRS.
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgscurve.cpp:293
bool mHasCachedSummedUpArea
Definition: qgscurve.h:356
bool snapToGridPrivate(double hSpacing, double vSpacing, double dSpacing, double mSpacing, const QVector< double > &srcX, const QVector< double > &srcY, const QVector< double > &srcZ, const QVector< double > &srcM, QVector< double > &outX, QVector< double > &outY, QVector< double > &outZ, QVector< double > &outM) const
Helper function for QgsCurve subclasses to snap to grids.
Definition: qgscurve.cpp:317
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
Definition: qgscurve.cpp:247
double mSummedUpArea
Definition: qgscurve.h:357
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:53
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.
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...
static bool circleClockwise(double angle1, double angle2, double angle3)
Returns true if the circle defined by three angles is ordered clockwise.
static double sweepAngle(double centerX, double centerY, double x1, double y1, double x2, double y2, double x3, double y3)
Calculates angle of a circular string part defined by pt1, pt2, pt3.
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,...
static bool circleAngleBetween(double angle, double angle1, double angle2, bool clockwise)
Returns true if, in a circle, angle is between angle1 and angle2.
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,...
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 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)
static QgsPoint pointOnLineWithDistance(const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance)
Returns a point a specified distance toward a second point.
static void pointsToWKB(QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure, QgsAbstractGeometry::WkbFlags flags)
Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }.
static QPair< Qgis::WkbType, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
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.
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.
static QgsPointSequence pointsFromWKT(const QString &wktCoordinateList, bool is3D, bool isMeasure)
Returns a list of points contained in a WKT string.
static void segmentizeArc(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, QgsPointSequence &points, double tolerance=M_PI_2/90, QgsAbstractGeometry::SegmentationToleranceType toleranceType=QgsAbstractGeometry::MaximumAngle, bool hasZ=false, bool hasM=false)
Convert circular arc defined by p1, p2, p3 (p1/p3 being start resp.
static Q_DECL_DEPRECATED double sqrDistance2D(double x1, double y1, double x2, double y2)
Returns the squared 2D distance between (x1, y1) and (x2, y2).
static QDomElement pointsToGML3(const QgsPointSequence &points, QDomDocument &doc, int precision, const QString &ns, bool is3D, QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY)
Returns a gml::posList DOM element.
static QString pointsToWKT(const QgsPointSequence &points, int precision, bool is3D, bool isMeasure)
Returns a WKT coordinate list.
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...
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
void setPoints(size_t size, const double *x, const double *y, const double *z=nullptr, const double *m=nullptr)
Resets the line string to match the specified point data.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
void setY(double y)
Sets the point's y-coordinate.
Definition: qgspoint.h:343
void setX(double x)
Sets the point's x-coordinate.
Definition: qgspoint.h:332
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
double distanceSquared(double x, double y) const
Returns the Cartesian 2D squared distance between this point a specified x, y coordinate.
Definition: qgspoint.h:415
double m
Definition: qgspoint.h:55
double y
Definition: qgspoint.h:53
A rectangle specified with double values.
Definition: qgsrectangle.h:42
WKB pointer handler.
Definition: qgswkbptr.h:44
static Qgis::WkbType dropM(Qgis::WkbType type)
Drops the m dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1144
static Qgis::WkbType dropZ(Qgis::WkbType type)
Drops the z dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1127
static Qgis::WkbType addM(Qgis::WkbType type)
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1092
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1068
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:973
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1023
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:628
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:716
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether 'thepoint' is left or right of the line from 'p1' to 'p2'. Negative values mean left ...
Definition: MathUtils.cpp:222
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5207
QVector< QgsPoint > QgsPointSequence
void arcTo(QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3)
int precision
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:30
int vertex
Vertex number.
Definition: qgsvertexid.h:94
int part
Part number.
Definition: qgsvertexid.h:88
int ring
Ring number.
Definition: qgsvertexid.h:91