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