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