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