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