QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
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 const bool hasZ = is3D();
453 const bool hasM = isMeasure();
454 int nVertices = 0;
455 wkbPtr >> nVertices;
456 mX.resize( nVertices );
457 mY.resize( nVertices );
458 if ( hasZ )
459 mZ.resize( nVertices );
460 else
461 mZ.clear();
462 if ( hasM )
463 mM.resize( nVertices );
464 else
465 mM.clear();
466 for ( int i = 0; i < nVertices; ++i )
467 {
468 wkbPtr >> mX[i];
469 wkbPtr >> mY[i];
470 if ( hasZ )
471 {
472 wkbPtr >> mZ[i];
473 }
474 if ( hasM )
475 {
476 wkbPtr >> mM[i];
477 }
478 }
479
480 return true;
481}
482
483bool QgsCircularString::fromWkt( const QString &wkt )
484{
485 clear();
486
487 QPair<Qgis::WkbType, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
488
490 return false;
491 mWkbType = parts.first;
492
493 parts.second = parts.second.remove( '(' ).remove( ')' );
494 QString secondWithoutParentheses = parts.second;
495 secondWithoutParentheses = secondWithoutParentheses.simplified().remove( ' ' );
496 if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
497 secondWithoutParentheses.isEmpty() )
498 return true;
499
501 if ( points.isEmpty() )
502 return false;
503
504 setPoints( points );
505 return true;
506}
507
508int QgsCircularString::wkbSize( QgsAbstractGeometry::WkbFlags ) const
509{
510 int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
511 binarySize += numPoints() * ( 2 + is3D() + isMeasure() ) * sizeof( double );
512 return binarySize;
513}
514
515QByteArray QgsCircularString::asWkb( WkbFlags flags ) const
516{
517 QByteArray wkbArray;
518 wkbArray.resize( QgsCircularString::wkbSize( flags ) );
519 QgsWkbPtr wkb( wkbArray );
520 wkb << static_cast<char>( QgsApplication::endian() );
521 wkb << static_cast<quint32>( wkbType() );
523 points( pts );
524 QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure(), flags );
525 return wkbArray;
526}
527
529{
530 QString wkt = wktTypeStr() + ' ';
531
532 if ( isEmpty() )
533 wkt += QLatin1String( "EMPTY" );
534 else
535 {
537 points( pts );
539 }
540 return wkt;
541}
542
543QDomElement QgsCircularString::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
544{
545 // GML2 does not support curves
546 std::unique_ptr< QgsLineString > line( curveToLine() );
547 QDomElement gml = line->asGml2( doc, precision, ns, axisOrder );
548 return gml;
549}
550
551QDomElement QgsCircularString::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
552{
554 points( pts );
555
556 QDomElement elemCurve = doc.createElementNS( ns, QStringLiteral( "Curve" ) );
557
558 if ( isEmpty() )
559 return elemCurve;
560
561 QDomElement elemSegments = doc.createElementNS( ns, QStringLiteral( "segments" ) );
562 QDomElement elemArcString = doc.createElementNS( ns, QStringLiteral( "ArcString" ) );
563 elemArcString.appendChild( QgsGeometryUtils::pointsToGML3( pts, doc, precision, ns, is3D(), axisOrder ) );
564 elemSegments.appendChild( elemArcString );
565 elemCurve.appendChild( elemSegments );
566 return elemCurve;
567}
568
569
571{
572 // GeoJSON does not support curves
573 std::unique_ptr< QgsLineString > line( curveToLine() );
574 return line->asJsonObject( precision );
575}
576
578{
579 return mX.isEmpty();
580}
581
582bool QgsCircularString::isValid( QString &error, Qgis::GeometryValidityFlags flags ) const
583{
584 if ( !isEmpty() && ( numPoints() < 3 ) )
585 {
586 error = QObject::tr( "CircularString has less than 3 points and is not empty." );
587 return false;
588 }
589 return QgsCurve::isValid( error, flags );
590}
591
592//curve interface
594{
595 int nPoints = numPoints();
596 double length = 0;
597 for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
598 {
599 length += QgsGeometryUtils::circleLength( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2] );
600 }
601 return length;
602}
603
605{
606 if ( numPoints() < 1 )
607 {
608 return QgsPoint();
609 }
610 return pointN( 0 );
611}
612
614{
615 if ( numPoints() < 1 )
616 {
617 return QgsPoint();
618 }
619 return pointN( numPoints() - 1 );
620}
621
623{
624 QgsLineString *line = new QgsLineString();
626 int nPoints = numPoints();
627
628 for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
629 {
630 QgsGeometryUtils::segmentizeArc( pointN( i ), pointN( i + 1 ), pointN( i + 2 ), points, tolerance, toleranceType, is3D(), isMeasure() );
631 }
632
633 line->setPoints( points );
634 return line;
635}
636
637QgsCircularString *QgsCircularString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
638{
639 // prepare result
640 std::unique_ptr<QgsCircularString> result { createEmptyWithSameType() };
641
642 bool res = snapToGridPrivate( hSpacing, vSpacing, dSpacing, mSpacing, mX, mY, mZ, mM,
643 result->mX, result->mY, result->mZ, result->mM );
644 if ( res )
645 return result.release();
646 else
647 return nullptr;
648}
649
650bool QgsCircularString::removeDuplicateNodes( double epsilon, bool useZValues )
651{
652 if ( mX.count() <= 3 )
653 return false; // don't create degenerate lines
654 bool result = false;
655 double prevX = mX.at( 0 );
656 double prevY = mY.at( 0 );
657 bool hasZ = is3D();
658 bool useZ = hasZ && useZValues;
659 double prevZ = useZ ? mZ.at( 0 ) : 0;
660 int i = 1;
661 int remaining = mX.count();
662 // we have to consider points in pairs, since a segment can validly have the same start and
663 // end if it has a different curve point
664 while ( i + 1 < remaining )
665 {
666 double currentCurveX = mX.at( i );
667 double currentCurveY = mY.at( i );
668 double currentX = mX.at( i + 1 );
669 double currentY = mY.at( i + 1 );
670 double currentZ = useZ ? mZ.at( i + 1 ) : 0;
671 if ( qgsDoubleNear( currentCurveX, prevX, epsilon ) &&
672 qgsDoubleNear( currentCurveY, prevY, epsilon ) &&
673 qgsDoubleNear( currentX, prevX, epsilon ) &&
674 qgsDoubleNear( currentY, prevY, epsilon ) &&
675 ( !useZ || qgsDoubleNear( currentZ, prevZ, epsilon ) ) )
676 {
677 result = true;
678 // remove point
679 mX.removeAt( i );
680 mX.removeAt( i );
681 mY.removeAt( i );
682 mY.removeAt( i );
683 if ( hasZ )
684 {
685 mZ.removeAt( i );
686 mZ.removeAt( i );
687 }
688 remaining -= 2;
689 }
690 else
691 {
692 prevX = currentX;
693 prevY = currentY;
694 prevZ = currentZ;
695 i += 2;
696 }
697 }
698 return result;
699}
700
702{
703 return std::min( mX.size(), mY.size() );
704}
705
706int QgsCircularString::indexOf( const QgsPoint &point ) const
707{
708 const int size = mX.size();
709 if ( size == 0 )
710 return -1;
711
712 const double *x = mX.constData();
713 const double *y = mY.constData();
714 const bool useZ = is3D();
715 const bool useM = isMeasure();
716 const double *z = useZ ? mZ.constData() : nullptr;
717 const double *m = useM ? mM.constData() : nullptr;
718
719 for ( int i = 0; i < size; i += 2 )
720 {
721 if ( qgsDoubleNear( *x, point.x() )
722 && qgsDoubleNear( *y, point.y() )
723 && ( !useZ || qgsDoubleNear( *z, point.z() ) )
724 && ( !useM || qgsDoubleNear( *m, point.m() ) ) )
725 return i;
726
727 // we skip over curve points!
728 x++;
729 x++;
730 y++;
731 y++;
732 if ( useZ )
733 {
734 z++;
735 z++;
736 }
737 if ( useM )
738 {
739 m++;
740 m++;
741 }
742 }
743 return -1;
744}
745
747{
748 if ( i < 0 || std::min( mX.size(), mY.size() ) <= i )
749 {
750 return QgsPoint();
751 }
752
753 double x = mX.at( i );
754 double y = mY.at( i );
755 double z = 0;
756 double m = 0;
757
758 if ( is3D() )
759 {
760 z = mZ.at( i );
761 }
762 if ( isMeasure() )
763 {
764 m = mM.at( i );
765 }
766
768 if ( is3D() && isMeasure() )
769 {
771 }
772 else if ( is3D() )
773 {
775 }
776 else if ( isMeasure() )
777 {
779 }
780 return QgsPoint( t, x, y, z, m );
781}
782
783double QgsCircularString::xAt( int index ) const
784{
785 if ( index >= 0 && index < mX.size() )
786 return mX.at( index );
787 else
788 return 0.0;
789}
790
791double QgsCircularString::yAt( int index ) const
792{
793 if ( index >= 0 && index < mY.size() )
794 return mY.at( index );
795 else
796 return 0.0;
797}
798
799double QgsCircularString::zAt( int index ) const
800{
801 if ( index >= 0 && index < mZ.size() )
802 return mZ.at( index );
803 else
804 return 0.0;
805}
806
807double QgsCircularString::mAt( int index ) const
808{
809 if ( index >= 0 && index < mM.size() )
810 return mM.at( index );
811 else
812 return 0.0;
813}
814
816{
817 if ( !transformer )
818 return false;
819
820 bool hasZ = is3D();
821 bool hasM = isMeasure();
822 int size = mX.size();
823
824 double *srcX = mX.data();
825 double *srcY = mY.data();
826 double *srcM = hasM ? mM.data() : nullptr;
827 double *srcZ = hasZ ? mZ.data() : nullptr;
828
829 bool res = true;
830 for ( int i = 0; i < size; ++i )
831 {
832 double x = *srcX;
833 double y = *srcY;
834 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
835 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
836 if ( !transformer->transformPoint( x, y, z, m ) )
837 {
838 res = false;
839 break;
840 }
841
842 *srcX++ = x;
843 *srcY++ = y;
844 if ( hasM )
845 *srcM++ = m;
846 if ( hasZ )
847 *srcZ++ = z;
848
849 if ( feedback && feedback->isCanceled() )
850 {
851 res = false;
852 break;
853 }
854 }
855 clearCache();
856 return res;
857}
858
859void QgsCircularString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
860{
861 bool hasZ = is3D();
862 bool hasM = isMeasure();
863 int size = mX.size();
864
865 double *srcX = mX.data(); // clazy:exclude=detaching-member
866 double *srcY = mY.data(); // clazy:exclude=detaching-member
867 double *srcM = hasM ? mM.data() : nullptr; // clazy:exclude=detaching-member
868 double *srcZ = hasZ ? mZ.data() : nullptr; // clazy:exclude=detaching-member
869
870 double *destX = srcX;
871 double *destY = srcY;
872 double *destM = srcM;
873 double *destZ = srcZ;
874
875 int filteredPoints = 0;
876 for ( int i = 0; i < size; ++i )
877 {
878 double x = *srcX++;
879 double y = *srcY++;
880 double z = hasZ ? *srcZ++ : std::numeric_limits<double>::quiet_NaN();
881 double m = hasM ? *srcM++ : std::numeric_limits<double>::quiet_NaN();
882
883 if ( filter( QgsPoint( x, y, z, m ) ) )
884 {
885 filteredPoints++;
886 *destX++ = x;
887 *destY++ = y;
888 if ( hasM )
889 *destM++ = m;
890 if ( hasZ )
891 *destZ++ = z;
892 }
893 }
894
895 mX.resize( filteredPoints );
896 mY.resize( filteredPoints );
897 if ( hasZ )
898 mZ.resize( filteredPoints );
899 if ( hasM )
900 mM.resize( filteredPoints );
901
902 clearCache();
903}
904
905void QgsCircularString::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
906{
907 bool hasZ = is3D();
908 bool hasM = isMeasure();
909 int size = mX.size();
910
911 double *srcX = mX.data();
912 double *srcY = mY.data();
913 double *srcM = hasM ? mM.data() : nullptr;
914 double *srcZ = hasZ ? mZ.data() : nullptr;
915
916 for ( int i = 0; i < size; ++i )
917 {
918 double x = *srcX;
919 double y = *srcY;
920 double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
921 double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
922 QgsPoint res = transform( QgsPoint( x, y, z, m ) );
923 *srcX++ = res.x();
924 *srcY++ = res.y();
925 if ( hasM )
926 *srcM++ = res.m();
927 if ( hasZ )
928 *srcZ++ = res.z();
929 }
930 clearCache();
931}
932
933std::tuple<std::unique_ptr<QgsCurve>, std::unique_ptr<QgsCurve> > QgsCircularString::splitCurveAtVertex( int index ) const
934{
935 const bool useZ = is3D();
936 const bool useM = isMeasure();
937
938 const int size = mX.size();
939 if ( size == 0 )
940 return std::make_tuple( std::make_unique< QgsCircularString >(), std::make_unique< QgsCircularString >() );
941
942 index = std::clamp( index, 0, size - 1 );
943
944 const int part1Size = index + 1;
945 QVector< double > x1( part1Size );
946 QVector< double > y1( part1Size );
947 QVector< double > z1( useZ ? part1Size : 0 );
948 QVector< double > m1( useM ? part1Size : 0 );
949
950 const double *sourceX = mX.constData();
951 const double *sourceY = mY.constData();
952 const double *sourceZ = useZ ? mZ.constData() : nullptr;
953 const double *sourceM = useM ? mM.constData() : nullptr;
954
955 double *destX = x1.data();
956 double *destY = y1.data();
957 double *destZ = useZ ? z1.data() : nullptr;
958 double *destM = useM ? m1.data() : nullptr;
959
960 std::copy( sourceX, sourceX + part1Size, destX );
961 std::copy( sourceY, sourceY + part1Size, destY );
962 if ( useZ )
963 std::copy( sourceZ, sourceZ + part1Size, destZ );
964 if ( useM )
965 std::copy( sourceM, sourceM + part1Size, destM );
966
967 const int part2Size = size - index;
968 if ( part2Size < 2 )
969 return std::make_tuple( std::make_unique< QgsCircularString >( x1, y1, z1, m1 ), std::make_unique< QgsCircularString >() );
970
971 QVector< double > x2( part2Size );
972 QVector< double > y2( part2Size );
973 QVector< double > z2( useZ ? part2Size : 0 );
974 QVector< double > m2( useM ? part2Size : 0 );
975 destX = x2.data();
976 destY = y2.data();
977 destZ = useZ ? z2.data() : nullptr;
978 destM = useM ? m2.data() : nullptr;
979 std::copy( sourceX + index, sourceX + size, destX );
980 std::copy( sourceY + index, sourceY + size, destY );
981 if ( useZ )
982 std::copy( sourceZ + index, sourceZ + size, destZ );
983 if ( useM )
984 std::copy( sourceM + index, sourceM + size, destM );
985
986 if ( part1Size < 2 )
987 return std::make_tuple( std::make_unique< QgsCircularString >(), std::make_unique< QgsCircularString >( x2, y2, z2, m2 ) );
988 else
989 return std::make_tuple( std::make_unique< QgsCircularString >( x1, y1, z1, m1 ), std::make_unique< QgsCircularString >( x2, y2, z2, m2 ) );
990}
991
993{
994 pts.clear();
995 int nPts = numPoints();
996 for ( int i = 0; i < nPts; ++i )
997 {
998 pts.push_back( pointN( i ) );
999 }
1000}
1001
1003{
1004 clearCache();
1005
1006 if ( points.empty() )
1007 {
1009 mX.clear();
1010 mY.clear();
1011 mZ.clear();
1012 mM.clear();
1013 return;
1014 }
1015
1016 //get wkb type from first point
1017 const QgsPoint &firstPt = points.at( 0 );
1018 bool hasZ = firstPt.is3D();
1019 bool hasM = firstPt.isMeasure();
1020
1022
1023 mX.resize( points.size() );
1024 mY.resize( points.size() );
1025 if ( hasZ )
1026 {
1027 mZ.resize( points.size() );
1028 }
1029 else
1030 {
1031 mZ.clear();
1032 }
1033 if ( hasM )
1034 {
1035 mM.resize( points.size() );
1036 }
1037 else
1038 {
1039 mM.clear();
1040 }
1041
1042 for ( int i = 0; i < points.size(); ++i )
1043 {
1044 mX[i] = points[i].x();
1045 mY[i] = points[i].y();
1046 if ( hasZ )
1047 {
1048 double z = points.at( i ).z();
1049 mZ[i] = std::isnan( z ) ? 0 : z;
1050 }
1051 if ( hasM )
1052 {
1053 double m = points.at( i ).m();
1054 mM[i] = std::isnan( m ) ? 0 : m;
1055 }
1056 }
1057}
1058
1060{
1061 if ( !line || line->isEmpty() )
1062 {
1063 return;
1064 }
1065
1066 if ( numPoints() < 1 )
1067 {
1069 }
1070
1071 // do not store duplicate points
1072 if ( numPoints() > 0 &&
1073 line->numPoints() > 0 &&
1074 qgsDoubleNear( endPoint().x(), line->startPoint().x() ) &&
1075 qgsDoubleNear( endPoint().y(), line->startPoint().y() ) &&
1076 ( !is3D() || !line->is3D() || qgsDoubleNear( endPoint().z(), line->startPoint().z() ) ) &&
1077 ( !isMeasure() || !line->isMeasure() || qgsDoubleNear( endPoint().m(), line->startPoint().m() ) ) )
1078 {
1079 mX.pop_back();
1080 mY.pop_back();
1081
1082 if ( is3D() && line->is3D() )
1083 {
1084 mZ.pop_back();
1085 }
1086 if ( isMeasure() && line->isMeasure() )
1087 {
1088 mM.pop_back();
1089 }
1090 }
1091
1092 mX += line->mX;
1093 mY += line->mY;
1094
1095 if ( is3D() )
1096 {
1097 if ( line->is3D() )
1098 {
1099 mZ += line->mZ;
1100 }
1101 else
1102 {
1103 // if append line does not have z coordinates, fill with NaN to match number of points in final line
1104 mZ.insert( mZ.count(), mX.size() - mZ.size(), std::numeric_limits<double>::quiet_NaN() );
1105 }
1106 }
1107
1108 if ( isMeasure() )
1109 {
1110 if ( line->isMeasure() )
1111 {
1112 mM += line->mM;
1113 }
1114 else
1115 {
1116 // if append line does not have m values, fill with NaN to match number of points in final line
1117 mM.insert( mM.count(), mX.size() - mM.size(), std::numeric_limits<double>::quiet_NaN() );
1118 }
1119 }
1120
1121 clearCache(); //set bounding box invalid
1122}
1123
1124void QgsCircularString::draw( QPainter &p ) const
1125{
1126 QPainterPath path;
1127 addToPainterPath( path );
1128 p.drawPath( path );
1129}
1130
1132{
1133 clearCache();
1134
1135 double *zArray = mZ.data();
1136
1137 bool hasZ = is3D();
1138 int nPoints = numPoints();
1139 bool useDummyZ = !hasZ || !transformZ;
1140 if ( useDummyZ )
1141 {
1142 zArray = new double[nPoints];
1143 for ( int i = 0; i < nPoints; ++i )
1144 {
1145 zArray[i] = 0;
1146 }
1147 }
1148 ct.transformCoords( nPoints, mX.data(), mY.data(), zArray, d );
1149 if ( useDummyZ )
1150 {
1151 delete[] zArray;
1152 }
1153}
1154
1155void QgsCircularString::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
1156{
1157 clearCache();
1158
1159 int nPoints = numPoints();
1160 bool hasZ = is3D();
1161 bool hasM = isMeasure();
1162 for ( int i = 0; i < nPoints; ++i )
1163 {
1164 qreal x, y;
1165 t.map( mX.at( i ), mY.at( i ), &x, &y );
1166 mX[i] = x;
1167 mY[i] = y;
1168 if ( hasZ )
1169 {
1170 mZ[i] = mZ.at( i ) * zScale + zTranslate;
1171 }
1172 if ( hasM )
1173 {
1174 mM[i] = mM.at( i ) * mScale + mTranslate;
1175 }
1176 }
1177}
1178
1179void arcTo( QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3 )
1180{
1181 double centerX, centerY, radius;
1182 QgsGeometryUtils::circleCenterRadius( QgsPoint( pt1.x(), pt1.y() ), QgsPoint( pt2.x(), pt2.y() ), QgsPoint( pt3.x(), pt3.y() ),
1183 radius, centerX, centerY );
1184
1185 double p1Angle = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
1186 double sweepAngle = QgsGeometryUtils::sweepAngle( centerX, centerY, pt1.x(), pt1.y(), pt2.x(), pt2.y(), pt3.x(), pt3.y() );
1187
1188 double diameter = 2 * radius;
1189 path.arcTo( centerX - radius, centerY - radius, diameter, diameter, -p1Angle, -sweepAngle );
1190}
1191
1192void QgsCircularString::addToPainterPath( QPainterPath &path ) const
1193{
1194 int nPoints = numPoints();
1195 if ( nPoints < 1 )
1196 {
1197 return;
1198 }
1199
1200 if ( path.isEmpty() || path.currentPosition() != QPointF( mX[0], mY[0] ) )
1201 {
1202 path.moveTo( QPointF( mX[0], mY[0] ) );
1203 }
1204
1205 for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
1206 {
1207 arcTo( path, QPointF( mX[i], mY[i] ), QPointF( mX[i + 1], mY[i + 1] ), QPointF( mX[i + 2], mY[i + 2] ) );
1208 }
1209
1210 //if number of points is even, connect to last point with straight line (even though the circular string is not valid)
1211 if ( nPoints % 2 == 0 )
1212 {
1213 path.lineTo( mX[ nPoints - 1 ], mY[ nPoints - 1 ] );
1214 }
1215}
1216
1217void QgsCircularString::drawAsPolygon( QPainter &p ) const
1218{
1219 draw( p );
1220}
1221
1223{
1224 if ( position.vertex >= mX.size() || position.vertex < 1 )
1225 {
1226 return false;
1227 }
1228
1229 mX.insert( position.vertex, vertex.x() );
1230 mY.insert( position.vertex, vertex.y() );
1231 if ( is3D() )
1232 {
1233 mZ.insert( position.vertex, vertex.z() );
1234 }
1235 if ( isMeasure() )
1236 {
1237 mM.insert( position.vertex, vertex.m() );
1238 }
1239
1240 bool vertexNrEven = ( position.vertex % 2 == 0 );
1241 if ( vertexNrEven )
1242 {
1243 insertVertexBetween( position.vertex - 2, position.vertex - 1, position.vertex );
1244 }
1245 else
1246 {
1247 insertVertexBetween( position.vertex, position.vertex + 1, position.vertex - 1 );
1248 }
1249 clearCache(); //set bounding box invalid
1250 return true;
1251}
1252
1254{
1255 if ( position.vertex < 0 || position.vertex >= mX.size() )
1256 {
1257 return false;
1258 }
1259
1260 mX[position.vertex] = newPos.x();
1261 mY[position.vertex] = newPos.y();
1262 if ( is3D() && newPos.is3D() )
1263 {
1264 mZ[position.vertex] = newPos.z();
1265 }
1266 if ( isMeasure() && newPos.isMeasure() )
1267 {
1268 mM[position.vertex] = newPos.m();
1269 }
1270 clearCache(); //set bounding box invalid
1271 return true;
1272}
1273
1275{
1276 int nVertices = this->numPoints();
1277 if ( nVertices < 4 ) //circular string must have at least 3 vertices
1278 {
1279 clear();
1280 return true;
1281 }
1282 if ( position.vertex < 0 || position.vertex > ( nVertices - 1 ) )
1283 {
1284 return false;
1285 }
1286
1287 if ( position.vertex < ( nVertices - 2 ) )
1288 {
1289 //remove this and the following vertex
1290 deleteVertex( position.vertex + 1 );
1291 deleteVertex( position.vertex );
1292 }
1293 else //remove this and the preceding vertex
1294 {
1295 deleteVertex( position.vertex );
1296 deleteVertex( position.vertex - 1 );
1297 }
1298
1299 clearCache(); //set bounding box invalid
1300 return true;
1301}
1302
1304{
1305 mX.remove( i );
1306 mY.remove( i );
1307 if ( is3D() )
1308 {
1309 mZ.remove( i );
1310 }
1311 if ( isMeasure() )
1312 {
1313 mM.remove( i );
1314 }
1315 clearCache();
1316}
1317
1318double QgsCircularString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1319{
1320 double minDist = std::numeric_limits<double>::max();
1321 QgsPoint minDistSegmentPoint;
1322 QgsVertexId minDistVertexAfter;
1323 int minDistLeftOf = 0;
1324
1325 double currentDist = 0.0;
1326
1327 int nPoints = numPoints();
1328 for ( int i = 0; i < ( nPoints - 2 ) ; i += 2 )
1329 {
1330 currentDist = closestPointOnArc( mX[i], mY[i], mX[i + 1], mY[i + 1], mX[i + 2], mY[i + 2], pt, segmentPt, vertexAfter, leftOf, epsilon );
1331 if ( currentDist < minDist )
1332 {
1333 minDist = currentDist;
1334 minDistSegmentPoint = segmentPt;
1335 minDistVertexAfter.vertex = vertexAfter.vertex + i;
1336 if ( leftOf )
1337 {
1338 minDistLeftOf = *leftOf;
1339 }
1340 }
1341 }
1342
1343 if ( minDist == std::numeric_limits<double>::max() )
1344 return -1; // error: no segments
1345
1346 segmentPt = minDistSegmentPoint;
1347 vertexAfter = minDistVertexAfter;
1348 vertexAfter.part = 0;
1349 vertexAfter.ring = 0;
1350 if ( leftOf )
1351 {
1352 *leftOf = qgsDoubleNear( minDist, 0.0 ) ? 0 : minDistLeftOf;
1353 }
1354 return minDist;
1355}
1356
1357bool QgsCircularString::pointAt( int node, QgsPoint &point, Qgis::VertexType &type ) const
1358{
1359 if ( node < 0 || node >= numPoints() )
1360 {
1361 return false;
1362 }
1363 point = pointN( node );
1364 type = ( node % 2 == 0 ) ? Qgis::VertexType::Segment : Qgis::VertexType::Curve;
1365 return true;
1366}
1367
1368void QgsCircularString::sumUpArea( double &sum ) const
1369{
1371 {
1372 sum += mSummedUpArea;
1373 return;
1374 }
1375
1376 int maxIndex = numPoints() - 2;
1377 mSummedUpArea = 0;
1378 for ( int i = 0; i < maxIndex; i += 2 )
1379 {
1380 QgsPoint p1( mX[i], mY[i] );
1381 QgsPoint p2( mX[i + 1], mY[i + 1] );
1382 QgsPoint p3( mX[i + 2], mY[i + 2] );
1383
1384 //segment is a full circle, p2 is the center point
1385 if ( p1 == p3 )
1386 {
1387 double r2 = QgsGeometryUtils::sqrDistance2D( p1, p2 ) / 4.0;
1388 mSummedUpArea += M_PI * r2;
1389 continue;
1390 }
1391
1392 mSummedUpArea += 0.5 * ( mX[i] * mY[i + 2] - mY[i] * mX[i + 2] );
1393
1394 //calculate area between circle and chord, then sum / subtract from total area
1395 double midPointX = ( p1.x() + p3.x() ) / 2.0;
1396 double midPointY = ( p1.y() + p3.y() ) / 2.0;
1397
1398 double radius, centerX, centerY;
1399 QgsGeometryUtils::circleCenterRadius( p1, p2, p3, radius, centerX, centerY );
1400
1401 double d = std::sqrt( QgsGeometryUtils::sqrDistance2D( QgsPoint( centerX, centerY ), QgsPoint( midPointX, midPointY ) ) );
1402 double r2 = radius * radius;
1403
1404 if ( d > radius )
1405 {
1406 //d cannot be greater than radius, something must be wrong...
1407 continue;
1408 }
1409
1410 bool circlePointLeftOfLine = QgsGeometryUtils::leftOfLine( p2.x(), p2.y(), p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
1411 bool centerPointLeftOfLine = QgsGeometryUtils::leftOfLine( centerX, centerY, p1.x(), p1.y(), p3.x(), p3.y() ) < 0;
1412
1413 double cov = 0.5 - d * std::sqrt( r2 - d * d ) / ( M_PI * r2 ) - M_1_PI * std::asin( d / radius );
1414 double circleChordArea = 0;
1415 if ( circlePointLeftOfLine == centerPointLeftOfLine )
1416 {
1417 circleChordArea = M_PI * r2 * ( 1 - cov );
1418 }
1419 else
1420 {
1421 circleChordArea = M_PI * r2 * cov;
1422 }
1423
1424 if ( !circlePointLeftOfLine )
1425 {
1426 mSummedUpArea += circleChordArea;
1427 }
1428 else
1429 {
1430 mSummedUpArea -= circleChordArea;
1431 }
1432 }
1433
1435 sum += mSummedUpArea;
1436}
1437
1439{
1440 return true;
1441}
1442
1443double QgsCircularString::closestPointOnArc( double x1, double y1, double x2, double y2, double x3, double y3,
1444 const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon )
1445{
1446 double radius, centerX, centerY;
1447 QgsPoint pt1( x1, y1 );
1448 QgsPoint pt2( x2, y2 );
1449 QgsPoint pt3( x3, y3 );
1450
1451 QgsGeometryUtils::circleCenterRadius( pt1, pt2, pt3, radius, centerX, centerY );
1452 double angle = QgsGeometryUtils::ccwAngle( pt.y() - centerY, pt.x() - centerX );
1453 double angle1 = QgsGeometryUtils::ccwAngle( pt1.y() - centerY, pt1.x() - centerX );
1454 double angle2 = QgsGeometryUtils::ccwAngle( pt2.y() - centerY, pt2.x() - centerX );
1455 double angle3 = QgsGeometryUtils::ccwAngle( pt3.y() - centerY, pt3.x() - centerX );
1456
1457 bool clockwise = QgsGeometryUtils::circleClockwise( angle1, angle2, angle3 );
1458
1459 if ( QgsGeometryUtils::angleOnCircle( angle, angle1, angle2, angle3 ) )
1460 {
1461 //get point on line center -> pt with distance radius
1462 segmentPt = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), pt, radius );
1463
1464 //vertexAfter
1465 vertexAfter.vertex = QgsGeometryUtils::circleAngleBetween( angle, angle1, angle2, clockwise ) ? 1 : 2;
1466 }
1467 else
1468 {
1469 double distPtPt1 = QgsGeometryUtils::sqrDistance2D( pt, pt1 );
1470 double distPtPt3 = QgsGeometryUtils::sqrDistance2D( pt, pt3 );
1471 segmentPt = ( distPtPt1 <= distPtPt3 ) ? pt1 : pt3;
1472 vertexAfter.vertex = ( distPtPt1 <= distPtPt3 ) ? 1 : 2;
1473 }
1474
1475 double sqrDistance = QgsGeometryUtils::sqrDistance2D( segmentPt, pt );
1476 //prevent rounding errors if the point is directly on the segment
1477 if ( qgsDoubleNear( sqrDistance, 0.0, epsilon ) )
1478 {
1479 segmentPt.setX( pt.x() );
1480 segmentPt.setY( pt.y() );
1481 sqrDistance = 0.0;
1482 }
1483
1484 if ( leftOf )
1485 {
1486 double sqrDistancePointToCenter = ( pt.x() - centerX ) * ( pt.x() - centerX ) + ( pt.y() - centerY ) * ( pt.y() - centerY );
1487 *leftOf = clockwise ? ( sqrDistancePointToCenter > radius * radius ? -1 : 1 )
1488 : ( sqrDistancePointToCenter < radius * radius ? -1 : 1 );
1489 }
1490
1491 return sqrDistance;
1492}
1493
1494void QgsCircularString::insertVertexBetween( int after, int before, int pointOnCircle )
1495{
1496 double xAfter = mX.at( after );
1497 double yAfter = mY.at( after );
1498 double xBefore = mX.at( before );
1499 double yBefore = mY.at( before );
1500 double xOnCircle = mX.at( pointOnCircle );
1501 double yOnCircle = mY.at( pointOnCircle );
1502
1503 double radius, centerX, centerY;
1504 QgsGeometryUtils::circleCenterRadius( QgsPoint( xAfter, yAfter ), QgsPoint( xBefore, yBefore ), QgsPoint( xOnCircle, yOnCircle ), radius, centerX, centerY );
1505
1506 double x = ( xAfter + xBefore ) / 2.0;
1507 double y = ( yAfter + yBefore ) / 2.0;
1508
1509 QgsPoint newVertex = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( centerX, centerY ), QgsPoint( x, y ), radius );
1510 mX.insert( before, newVertex.x() );
1511 mY.insert( before, newVertex.y() );
1512
1513 if ( is3D() )
1514 {
1515 mZ.insert( before, ( mZ[after] + mZ[before] ) / 2.0 );
1516 }
1517 if ( isMeasure() )
1518 {
1519 mM.insert( before, ( mM[after] + mM[before] ) / 2.0 );
1520 }
1521 clearCache();
1522}
1523
1525{
1526 if ( numPoints() < 3 )
1527 {
1528 //undefined
1529 return 0.0;
1530 }
1531
1532 int before = vId.vertex - 1;
1533 int vertex = vId.vertex;
1534 int after = vId.vertex + 1;
1535
1536 if ( vId.vertex % 2 != 0 ) // a curve vertex
1537 {
1538 if ( vId.vertex >= 1 && vId.vertex < numPoints() - 1 )
1539 {
1540 return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[before], mY[before] ),
1541 QgsPoint( mX[vertex], mY[vertex] ), QgsPoint( mX[after], mY[after] ) );
1542 }
1543 }
1544 else //a point vertex
1545 {
1546 if ( vId.vertex == 0 )
1547 {
1548 return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[0], mY[0] ), QgsPoint( mX[0], mY[0] ),
1549 QgsPoint( mX[1], mY[1] ), QgsPoint( mX[2], mY[2] ) );
1550 }
1551 if ( vId.vertex >= numPoints() - 1 )
1552 {
1553 int a = numPoints() - 3;
1554 int b = numPoints() - 2;
1555 int c = numPoints() - 1;
1556 return QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[c], mY[c] ), QgsPoint( mX[a], mY[a] ),
1557 QgsPoint( mX[b], mY[b] ), QgsPoint( mX[c], mY[c] ) );
1558 }
1559 else
1560 {
1561 if ( vId.vertex + 2 > numPoints() - 1 )
1562 {
1563 return 0.0;
1564 }
1565
1566 int vertex1 = vId.vertex - 2;
1567 int vertex2 = vId.vertex - 1;
1568 int vertex3 = vId.vertex;
1569 double angle1 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1570 QgsPoint( mX[vertex1], mY[vertex1] ), QgsPoint( mX[vertex2], mY[vertex2] ), QgsPoint( mX[vertex3], mY[vertex3] ) );
1571 int vertex4 = vId.vertex + 1;
1572 int vertex5 = vId.vertex + 2;
1573 double angle2 = QgsGeometryUtils::circleTangentDirection( QgsPoint( mX[vertex3], mY[vertex3] ),
1574 QgsPoint( mX[vertex3], mY[vertex3] ), QgsPoint( mX[vertex4], mY[vertex4] ), QgsPoint( mX[vertex5], mY[vertex5] ) );
1575 return QgsGeometryUtils::averageAngle( angle1, angle2 );
1576 }
1577 }
1578 return 0.0;
1579}
1580
1582{
1583 if ( startVertex.vertex < 0 || startVertex.vertex >= mX.count() - 2 )
1584 return 0.0;
1585
1586 if ( startVertex.vertex % 2 == 1 )
1587 return 0.0; // curve point?
1588
1589 double x1 = mX.at( startVertex.vertex );
1590 double y1 = mY.at( startVertex.vertex );
1591 double x2 = mX.at( startVertex.vertex + 1 );
1592 double y2 = mY.at( startVertex.vertex + 1 );
1593 double x3 = mX.at( startVertex.vertex + 2 );
1594 double y3 = mY.at( startVertex.vertex + 2 );
1595 return QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1596}
1597
1599{
1600 QgsCircularString *copy = clone();
1601 std::reverse( copy->mX.begin(), copy->mX.end() );
1602 std::reverse( copy->mY.begin(), copy->mY.end() );
1603 if ( is3D() )
1604 {
1605 std::reverse( copy->mZ.begin(), copy->mZ.end() );
1606 }
1607 if ( isMeasure() )
1608 {
1609 std::reverse( copy->mM.begin(), copy->mM.end() );
1610 }
1611 return copy;
1612}
1613
1614QgsPoint *QgsCircularString::interpolatePoint( const double distance ) const
1615{
1616 if ( distance < 0 )
1617 return nullptr;
1618
1619 double distanceTraversed = 0;
1620 const int totalPoints = numPoints();
1621 if ( totalPoints == 0 )
1622 return nullptr;
1623
1625 if ( is3D() )
1626 pointType = Qgis::WkbType::PointZ;
1627 if ( isMeasure() )
1628 pointType = QgsWkbTypes::addM( pointType );
1629
1630 const double *x = mX.constData();
1631 const double *y = mY.constData();
1632 const double *z = is3D() ? mZ.constData() : nullptr;
1633 const double *m = isMeasure() ? mM.constData() : nullptr;
1634
1635 double prevX = *x++;
1636 double prevY = *y++;
1637 double prevZ = z ? *z++ : 0.0;
1638 double prevM = m ? *m++ : 0.0;
1639
1640 if ( qgsDoubleNear( distance, 0.0 ) )
1641 {
1642 return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1643 }
1644
1645 for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1646 {
1647 double x1 = prevX;
1648 double y1 = prevY;
1649 double z1 = prevZ;
1650 double m1 = prevM;
1651
1652 double x2 = *x++;
1653 double y2 = *y++;
1654 double z2 = z ? *z++ : 0.0;
1655 double m2 = m ? *m++ : 0.0;
1656
1657 double x3 = *x++;
1658 double y3 = *y++;
1659 double z3 = z ? *z++ : 0.0;
1660 double m3 = m ? *m++ : 0.0;
1661
1662 const double segmentLength = QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1663 if ( distance < distanceTraversed + segmentLength || qgsDoubleNear( distance, distanceTraversed + segmentLength ) )
1664 {
1665 // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1666 const double distanceToPoint = std::min( distance - distanceTraversed, segmentLength );
1667 return new QgsPoint( QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1668 QgsPoint( pointType, x2, y2, z2, m2 ),
1669 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToPoint ) );
1670 }
1671
1672 distanceTraversed += segmentLength;
1673
1674 prevX = x3;
1675 prevY = y3;
1676 prevZ = z3;
1677 prevM = m3;
1678 }
1679
1680 return nullptr;
1681}
1682
1683QgsCircularString *QgsCircularString::curveSubstring( double startDistance, double endDistance ) const
1684{
1685 if ( startDistance < 0 && endDistance < 0 )
1686 return createEmptyWithSameType();
1687
1688 endDistance = std::max( startDistance, endDistance );
1689
1690 const int totalPoints = numPoints();
1691 if ( totalPoints == 0 )
1692 return clone();
1693
1694 QVector< QgsPoint > substringPoints;
1695 substringPoints.reserve( totalPoints );
1696
1698 if ( is3D() )
1699 pointType = Qgis::WkbType::PointZ;
1700 if ( isMeasure() )
1701 pointType = QgsWkbTypes::addM( pointType );
1702
1703 const double *x = mX.constData();
1704 const double *y = mY.constData();
1705 const double *z = is3D() ? mZ.constData() : nullptr;
1706 const double *m = isMeasure() ? mM.constData() : nullptr;
1707
1708 double distanceTraversed = 0;
1709 double prevX = *x++;
1710 double prevY = *y++;
1711 double prevZ = z ? *z++ : 0.0;
1712 double prevM = m ? *m++ : 0.0;
1713 bool foundStart = false;
1714
1715 if ( startDistance < 0 )
1716 startDistance = 0;
1717
1718 for ( int i = 0; i < ( totalPoints - 2 ) ; i += 2 )
1719 {
1720 double x1 = prevX;
1721 double y1 = prevY;
1722 double z1 = prevZ;
1723 double m1 = prevM;
1724
1725 double x2 = *x++;
1726 double y2 = *y++;
1727 double z2 = z ? *z++ : 0.0;
1728 double m2 = m ? *m++ : 0.0;
1729
1730 double x3 = *x++;
1731 double y3 = *y++;
1732 double z3 = z ? *z++ : 0.0;
1733 double m3 = m ? *m++ : 0.0;
1734
1735 bool addedSegmentEnd = false;
1736 const double segmentLength = QgsGeometryUtils::circleLength( x1, y1, x2, y2, x3, y3 );
1737 if ( distanceTraversed <= startDistance && startDistance < distanceTraversed + segmentLength )
1738 {
1739 // start point falls on this segment
1740 const double distanceToStart = startDistance - distanceTraversed;
1741 const QgsPoint startPoint = QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1742 QgsPoint( pointType, x2, y2, z2, m2 ),
1743 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToStart );
1744
1745 // does end point also fall on this segment?
1746 const bool endPointOnSegment = distanceTraversed + segmentLength > endDistance;
1747 if ( endPointOnSegment )
1748 {
1749 const double distanceToEnd = endDistance - distanceTraversed;
1750 const double midPointDistance = ( distanceToEnd - distanceToStart ) * 0.5 + distanceToStart;
1751 substringPoints << startPoint
1752 << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1753 QgsPoint( pointType, x2, y2, z2, m2 ),
1754 QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1755 << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1756 QgsPoint( pointType, x2, y2, z2, m2 ),
1757 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1758 addedSegmentEnd = true;
1759 }
1760 else
1761 {
1762 const double midPointDistance = ( segmentLength - distanceToStart ) * 0.5 + distanceToStart;
1763 substringPoints << startPoint
1764 << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1765 QgsPoint( pointType, x2, y2, z2, m2 ),
1766 QgsPoint( pointType, x3, y3, z3, m3 ), midPointDistance )
1767 << QgsPoint( pointType, x3, y3, z3, m3 );
1768 addedSegmentEnd = true;
1769 }
1770 foundStart = true;
1771 }
1772 if ( !addedSegmentEnd && foundStart && ( distanceTraversed + segmentLength > endDistance ) )
1773 {
1774 // end point falls on this segment
1775 const double distanceToEnd = endDistance - distanceTraversed;
1776 // add mid point, at half way along this arc, then add the interpolated end point
1777 substringPoints << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1778 QgsPoint( pointType, x2, y2, z2, m2 ),
1779 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd / 2.0 )
1780
1781 << QgsGeometryUtils::interpolatePointOnArc( QgsPoint( pointType, x1, y1, z1, m1 ),
1782 QgsPoint( pointType, x2, y2, z2, m2 ),
1783 QgsPoint( pointType, x3, y3, z3, m3 ), distanceToEnd );
1784 }
1785 else if ( !addedSegmentEnd && foundStart )
1786 {
1787 substringPoints << QgsPoint( pointType, x2, y2, z2, m2 )
1788 << QgsPoint( pointType, x3, y3, z3, m3 );
1789 }
1790
1791 prevX = x3;
1792 prevY = y3;
1793 prevZ = z3;
1794 prevM = m3;
1795 distanceTraversed += segmentLength;
1796 if ( distanceTraversed >= endDistance )
1797 break;
1798 }
1799
1800 // start point is the last node
1801 if ( !foundStart && qgsDoubleNear( distanceTraversed, startDistance ) )
1802 {
1803 substringPoints << QgsPoint( pointType, prevX, prevY, prevZ, prevM )
1804 << QgsPoint( pointType, prevX, prevY, prevZ, prevM )
1805 << QgsPoint( pointType, prevX, prevY, prevZ, prevM );
1806 }
1807
1808 std::unique_ptr< QgsCircularString > result = std::make_unique< QgsCircularString >();
1809 result->setPoints( substringPoints );
1810 return result.release();
1811}
1812
1813bool QgsCircularString::addZValue( double zValue )
1814{
1815 if ( QgsWkbTypes::hasZ( mWkbType ) )
1816 return false;
1817
1818 clearCache();
1820
1821 int nPoints = numPoints();
1822 mZ.clear();
1823 mZ.reserve( nPoints );
1824 for ( int i = 0; i < nPoints; ++i )
1825 {
1826 mZ << zValue;
1827 }
1828 return true;
1829}
1830
1831bool QgsCircularString::addMValue( double mValue )
1832{
1833 if ( QgsWkbTypes::hasM( mWkbType ) )
1834 return false;
1835
1836 clearCache();
1838
1839 int nPoints = numPoints();
1840 mM.clear();
1841 mM.reserve( nPoints );
1842 for ( int i = 0; i < nPoints; ++i )
1843 {
1844 mM << mValue;
1845 }
1846 return true;
1847}
1848
1850{
1851 if ( !QgsWkbTypes::hasZ( mWkbType ) )
1852 return false;
1853
1854 clearCache();
1855
1857 mZ.clear();
1858 return true;
1859}
1860
1862{
1863 if ( !QgsWkbTypes::hasM( mWkbType ) )
1864 return false;
1865
1866 clearCache();
1867
1869 mM.clear();
1870 return true;
1871}
1872
1874{
1875 std::swap( mX, mY );
1876 clearCache();
1877}
VertexType
Types of vertex.
Definition: qgis.h:2053
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition: qgis.h:154
@ PointM
PointM.
@ CircularString
CircularString.
@ PointZ
PointZ.
@ PointZM
PointZM.
@ CircularStringZ
CircularStringZ.
TransformDirection
Flags for raster layer temporal capabilities.
Definition: qgis.h:1789
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: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 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:3988
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