QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
qgscompoundcurve.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgscompoundcurve.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 "qgsmessagelog.h"
19#include "qgscompoundcurve.h"
20#include "qgsapplication.h"
21#include "qgscircularstring.h"
22#include "qgsgeometryutils.h"
23#include "qgslinestring.h"
24#include "qgswkbptr.h"
25#include "qgsfeedback.h"
26
27#include <QJsonObject>
28#include <QPainter>
29#include <QPainterPath>
30#include <memory>
31#include <nlohmann/json.hpp>
32
37
42
44{
45 auto result = std::make_unique< QgsCompoundCurve >();
46 result->mWkbType = mWkbType;
47 return result.release();
48}
49
51{
52 const QgsCompoundCurve *otherCurve = qgsgeometry_cast<const QgsCompoundCurve *>( other );
53 if ( !otherCurve )
54 return -1;
55
56 int i = 0;
57 int j = 0;
58 while ( i < mCurves.size() && j < otherCurve->mCurves.size() )
59 {
60 const QgsAbstractGeometry *aGeom = mCurves[i];
61 const QgsAbstractGeometry *bGeom = otherCurve->mCurves[j];
62 const int comparison = aGeom->compareTo( bGeom );
63 if ( comparison != 0 )
64 {
65 return comparison;
66 }
67 i++;
68 j++;
69 }
70 if ( i < mCurves.size() )
71 {
72 return 1;
73 }
74 if ( j < otherCurve->mCurves.size() )
75 {
76 return -1;
77 }
78 return 0;
79}
80
82{
83 return QStringLiteral( "CompoundCurve" );
84}
85
87{
88 return 1;
89}
90
92{
93 mWkbType = curve.wkbType();
94 mCurves.reserve( curve.mCurves.size() );
95 for ( const QgsCurve *c : curve.mCurves )
96 {
97 mCurves.append( c->clone() );
98 }
99}
100
101// cppcheck-suppress operatorEqVarError
103{
104 if ( &curve != this )
105 {
106 QgsCurve::operator=( curve );
107 for ( const QgsCurve *c : curve.mCurves )
108 {
109 mCurves.append( c->clone() );
110 }
111 }
112 return *this;
113}
114
116{
117 return new QgsCompoundCurve( *this );
118}
119
121{
123 qDeleteAll( mCurves );
124 mCurves.clear();
125 clearCache();
126}
127
129{
130 if ( mCurves.empty() )
131 {
132 return QgsBox3D();
133 }
134
135 QgsBox3D bbox = mCurves.at( 0 )->boundingBox3D();
136 for ( int i = 1; i < mCurves.size(); ++i )
137 {
138 QgsBox3D curveBox = mCurves.at( i )->boundingBox3D();
139 bbox.combineWith( curveBox );
140 }
141 return bbox;
142}
143
145{
146 const int size = numPoints();
147 if ( index < 1 || index >= size - 1 )
148 return;
149
150 auto [p1, p2 ] = splitCurveAtVertex( index );
151
152 mCurves.clear();
153 if ( QgsCompoundCurve *curve2 = qgsgeometry_cast< QgsCompoundCurve *>( p2.get() ) )
154 {
155 // take the curves from the second part and make them our first lot of curves
156 mCurves = std::move( curve2->mCurves );
157 }
158 if ( QgsCompoundCurve *curve1 = qgsgeometry_cast< QgsCompoundCurve *>( p1.get() ) )
159 {
160 // take the curves from the first part and append them to our curves
161 mCurves.append( curve1->mCurves );
162 curve1->mCurves.clear();
163 }
164}
165
167{
168 clear();
169 if ( !wkbPtr )
170 {
171 return false;
172 }
173
174 Qgis::WkbType type = wkbPtr.readHeader();
176 {
177 return false;
178 }
179 mWkbType = type;
180
181 int nCurves;
182 wkbPtr >> nCurves;
183 QgsCurve *currentCurve = nullptr;
184 for ( int i = 0; i < nCurves; ++i )
185 {
186 Qgis::WkbType curveType = wkbPtr.readHeader();
187 wkbPtr -= 1 + sizeof( int );
189 {
190 currentCurve = new QgsLineString();
191 }
192 else if ( QgsWkbTypes::flatType( curveType ) == Qgis::WkbType::CircularString )
193 {
194 currentCurve = new QgsCircularString();
195 }
196 else
197 {
198 return false;
199 }
200 currentCurve->fromWkb( wkbPtr ); // also updates wkbPtr
201 mCurves.append( currentCurve );
202 }
203 return true;
204}
205
206bool QgsCompoundCurve::fromWkt( const QString &wkt )
207{
208 clear();
209
210 QPair<Qgis::WkbType, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
211
213 return false;
214 mWkbType = parts.first;
215
216 QString secondWithoutParentheses = parts.second;
217 secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
218 if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
219 secondWithoutParentheses.isEmpty() )
220 return true;
221
222 QString defaultChildWkbType = QStringLiteral( "LineString%1%2" ).arg( is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
223
224 const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
225 for ( const QString &childWkt : blocks )
226 {
227 QPair<Qgis::WkbType, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
228
229 if ( QgsWkbTypes::flatType( childParts.first ) == Qgis::WkbType::LineString )
230 mCurves.append( new QgsLineString() );
231 else if ( QgsWkbTypes::flatType( childParts.first ) == Qgis::WkbType::CircularString )
232 mCurves.append( new QgsCircularString() );
233 else
234 {
235 clear();
236 return false;
237 }
238 if ( !mCurves.back()->fromWkt( childWkt ) )
239 {
240 clear();
241 return false;
242 }
243 }
244
245 //scan through curves and check if dimensionality of curves is different to compound curve.
246 //if so, update the type dimensionality of the compound curve to match
247 bool hasZ = false;
248 bool hasM = false;
249 for ( const QgsCurve *curve : std::as_const( mCurves ) )
250 {
251 hasZ = hasZ || curve->is3D();
252 hasM = hasM || curve->isMeasure();
253 if ( hasZ && hasM )
254 break;
255 }
256 if ( hasZ )
257 addZValue( 0 );
258 if ( hasM )
259 addMValue( 0 );
260
261 return true;
262}
263
265{
266 int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
267 for ( const QgsCurve *curve : mCurves )
268 {
269 binarySize += curve->wkbSize( flags );
270 }
271 return binarySize;
272}
273
274QByteArray QgsCompoundCurve::asWkb( WkbFlags flags ) const
275{
276 QByteArray wkbArray;
277 wkbArray.resize( QgsCompoundCurve::wkbSize( flags ) );
278 QgsWkbPtr wkb( wkbArray );
279 wkb << static_cast<char>( QgsApplication::endian() );
280 wkb << static_cast<quint32>( wkbType() );
281 wkb << static_cast<quint32>( mCurves.size() );
282 for ( const QgsCurve *curve : mCurves )
283 {
284 wkb << curve->asWkb( flags );
285 }
286 return wkbArray;
287}
288
290{
291 QString wkt = wktTypeStr();
292 if ( isEmpty() )
293 wkt += QLatin1String( " EMPTY" );
294 else
295 {
296 wkt += QLatin1String( " (" );
297 for ( const QgsCurve *curve : mCurves )
298 {
299 QString childWkt = curve->asWkt( precision );
300 if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
301 {
302 // Type names of linear geometries are omitted
303 childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
304 }
305 wkt += childWkt + ',';
306 }
307 if ( wkt.endsWith( ',' ) )
308 {
309 wkt.chop( 1 );
310 }
311 wkt += ')';
312 }
313 return wkt;
314}
315
316QDomElement QgsCompoundCurve::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
317{
318 // GML2 does not support curves
319 std::unique_ptr< QgsLineString > line( curveToLine() );
320 QDomElement gml = line->asGml2( doc, precision, ns, axisOrder );
321 return gml;
322}
323
324QDomElement QgsCompoundCurve::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
325{
326 QDomElement compoundCurveElem = doc.createElementNS( ns, QStringLiteral( "CompositeCurve" ) );
327
328 if ( isEmpty() )
329 return compoundCurveElem;
330
331 for ( const QgsCurve *curve : mCurves )
332 {
333 QDomElement curveMemberElem = doc.createElementNS( ns, QStringLiteral( "curveMember" ) );
334 QDomElement curveElem = curve->asGml3( doc, precision, ns, axisOrder );
335 curveMemberElem.appendChild( curveElem );
336 compoundCurveElem.appendChild( curveMemberElem );
337 }
338
339 return compoundCurveElem;
340}
341
343{
344 // GeoJSON does not support curves
345 std::unique_ptr< QgsLineString > line( curveToLine() );
346 return line->asJsonObject( precision );
347}
348
350{
351 double length = 0;
352 for ( const QgsCurve *curve : mCurves )
353 {
354 length += curve->length();
355 }
356 return length;
357}
358
360{
361 if ( mCurves.empty() )
362 {
363 return QgsPoint();
364 }
365 return mCurves.at( 0 )->startPoint();
366}
367
369{
370 if ( mCurves.empty() )
371 {
372 return QgsPoint();
373 }
374 return mCurves.at( mCurves.size() - 1 )->endPoint();
375}
376
378{
379 pts.clear();
380 if ( mCurves.empty() )
381 {
382 return;
383 }
384
385 mCurves[0]->points( pts );
386 for ( int i = 1; i < mCurves.size(); ++i )
387 {
388 QgsPointSequence pList;
389 mCurves[i]->points( pList );
390 pList.removeFirst(); //first vertex already added in previous line
391 pts.append( pList );
392 }
393}
394
396{
397 int nPoints = 0;
398 int nCurves = mCurves.size();
399 if ( nCurves < 1 )
400 {
401 return 0;
402 }
403
404 for ( int i = 0; i < nCurves; ++i )
405 {
406 nPoints += mCurves.at( i )->numPoints() - 1; //last vertex is equal to first of next section
407 }
408 nPoints += 1; //last vertex was removed above
409 return nPoints;
410}
411
413{
414 if ( mCurves.isEmpty() )
415 return true;
416
417 for ( QgsCurve *curve : mCurves )
418 {
419 if ( !curve->isEmpty() )
420 return false;
421 }
422 return true;
423}
424
426{
427 if ( mCurves.isEmpty() )
428 return true;
429
430 for ( int i = 0; i < mCurves.size() ; ++i )
431 {
432 if ( !mCurves[i]->isValid( error, flags ) )
433 {
434 error = QObject::tr( "Curve[%1]: %2" ).arg( i + 1 ).arg( error );
435 return false;
436 }
437 }
438 return QgsCurve::isValid( error, flags );
439}
440
441int QgsCompoundCurve::indexOf( const QgsPoint &point ) const
442{
443 int curveStart = 0;
444 for ( const QgsCurve *curve : mCurves )
445 {
446 const int curveIndex = curve->indexOf( point );
447 if ( curveIndex >= 0 )
448 return curveStart + curveIndex;
449 // subtract 1 here, because the next curve will start with the same
450 // vertex as this curve ended at
451 curveStart += curve->numPoints() - 1;
452 }
453 return -1;
454}
455
457{
458 QgsLineString *line = new QgsLineString();
459 std::unique_ptr< QgsLineString > currentLine;
460 for ( const QgsCurve *curve : mCurves )
461 {
462 currentLine.reset( curve->curveToLine( tolerance, toleranceType ) );
463 line->append( currentLine.get() );
464 }
465 return line;
466}
467
468QgsCompoundCurve *QgsCompoundCurve::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool removeRedundantPoints ) const
469{
470 std::unique_ptr<QgsCompoundCurve> result( createEmptyWithSameType() );
471
472 for ( QgsCurve *curve : mCurves )
473 {
474 std::unique_ptr<QgsCurve> gridified( static_cast< QgsCurve * >( curve->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) ) );
475 if ( gridified )
476 {
477 result->mCurves.append( gridified.release() );
478 }
479 }
480
481 if ( result->mCurves.empty() )
482 return nullptr;
483 else
484 return result.release();
485}
486
488{
489 std::unique_ptr< QgsLineString > line( curveToLine() );
490 return line->simplifyByDistance( tolerance );
491}
492
493bool QgsCompoundCurve::removeDuplicateNodes( double epsilon, bool useZValues )
494{
495 bool result = false;
496 const QVector< QgsCurve * > curves = mCurves;
497 int i = 0;
498 QgsPoint lastEnd;
499 for ( QgsCurve *curve : curves )
500 {
501 result = curve->removeDuplicateNodes( epsilon, useZValues ) || result;
502 if ( curve->numPoints() == 0 || qgsDoubleNear( curve->length(), 0.0, epsilon ) )
503 {
504 // empty curve, remove it
505 delete mCurves.takeAt( i );
506 result = true;
507 }
508 else
509 {
510 // ensure this line starts exactly where previous line ended
511 if ( i > 0 )
512 {
513 curve->moveVertex( QgsVertexId( -1, -1, 0 ), lastEnd );
514 }
515 lastEnd = curve->vertexAt( QgsVertexId( -1, -1, curve->numPoints() - 1 ) );
516 }
517 i++;
518 }
519 return result;
520}
521
523{
524 if ( mCurves.empty() )
525 return false;
526
527 // if we already have the bounding box calculated, then this check is trivial!
528 if ( !mBoundingBox.isNull() )
529 {
530 return mBoundingBox.intersects( box3d );
531 }
532
533 // otherwise loop through each member curve and test the bounding box intersection.
534 // This gives us a chance to use optimisations which may be present on the individual
535 // curve subclasses, and at worst it will cause a calculation of the bounding box
536 // of each individual member curve which we would have to do anyway... (and these
537 // bounding boxes are cached, so would be reused without additional expense)
538 for ( const QgsCurve *curve : mCurves )
539 {
540 if ( curve->boundingBoxIntersects( box3d ) )
541 return true;
542 }
543
544 // even if we don't intersect the bounding box of any member curves, we may still intersect the
545 // bounding box of the overall compound curve.
546 // so here we fall back to the non-optimised base class check which has to first calculate
547 // the overall bounding box of the compound curve..
549}
550
552{
553 if ( mCurves.size() == 1 )
554 return mCurves.at( 0 );
555 else
556 return this;
557}
558
560{
561 if ( i < 0 || i >= mCurves.size() )
562 {
563 return nullptr;
564 }
565 return mCurves.at( i );
566}
567
568void QgsCompoundCurve::addCurve( QgsCurve *c, const bool extendPrevious )
569{
570 if ( !c )
571 return;
572
573 if ( mCurves.empty() )
574 {
576 }
577
578 if ( QgsWkbTypes::hasZ( mWkbType ) && !QgsWkbTypes::hasZ( c->wkbType() ) )
579 {
580 c->addZValue();
581 }
582 else if ( !QgsWkbTypes::hasZ( mWkbType ) && QgsWkbTypes::hasZ( c->wkbType() ) )
583 {
584 c->dropZValue();
585 }
586 if ( QgsWkbTypes::hasM( mWkbType ) && !QgsWkbTypes::hasM( c->wkbType() ) )
587 {
588 c->addMValue();
589 }
590 else if ( !QgsWkbTypes::hasM( mWkbType ) && QgsWkbTypes::hasM( c->wkbType() ) )
591 {
592 c->dropMValue();
593 }
594
595 QgsLineString *previousLineString = !mCurves.empty() ? qgsgeometry_cast< QgsLineString * >( mCurves.constLast() ) : nullptr;
596 const QgsLineString *newLineString = qgsgeometry_cast< const QgsLineString * >( c );
597 const bool canExtendPrevious = extendPrevious && previousLineString && newLineString;
598 if ( canExtendPrevious )
599 {
600 previousLineString->append( newLineString );
601 // we are taking ownership, so delete the input curve
602 delete c;
603 c = nullptr;
604 }
605 else
606 {
607 mCurves.append( c );
608 }
609
610 clearCache();
611}
612
614{
615 if ( i < 0 || i >= mCurves.size() )
616 {
617 return;
618 }
619
620 delete mCurves.takeAt( i );
621 clearCache();
622}
623
625{
626 if ( mCurves.isEmpty() || mWkbType == Qgis::WkbType::Unknown )
627 {
629 }
630
631 //is last curve QgsLineString
632 QgsCurve *lastCurve = nullptr;
633 if ( !mCurves.isEmpty() )
634 {
635 lastCurve = mCurves.at( mCurves.size() - 1 );
636 }
637
638 QgsLineString *line = nullptr;
639 if ( !lastCurve || QgsWkbTypes::flatType( lastCurve->wkbType() ) != Qgis::WkbType::LineString )
640 {
641 line = new QgsLineString();
642 mCurves.append( line );
643 if ( lastCurve )
644 {
645 line->addVertex( lastCurve->endPoint() );
646 }
647 lastCurve = line;
648 }
649 else //create new QgsLineString* with point in it
650 {
651 line = static_cast<QgsLineString *>( lastCurve );
652 }
653 line->addVertex( pt );
654 clearCache();
655}
656
658{
659 QgsCurve *lastCurve = nullptr;
660 QVector< QgsCurve * > newCurves;
661 newCurves.reserve( mCurves.size() );
662 for ( QgsCurve *curve : std::as_const( mCurves ) )
663 {
664 if ( lastCurve && lastCurve->wkbType() == curve->wkbType() )
665 {
666 if ( QgsLineString *ls = qgsgeometry_cast< QgsLineString * >( lastCurve ) )
667 {
668 ls->append( qgsgeometry_cast< QgsLineString * >( curve ) );
669 delete curve;
670 }
671 else if ( QgsCircularString *cs = qgsgeometry_cast< QgsCircularString * >( lastCurve ) )
672 {
673 cs->append( qgsgeometry_cast< QgsCircularString * >( curve ) );
674 delete curve;
675 }
676 }
677 else
678 {
679 lastCurve = curve;
680 newCurves << curve;
681 }
682 }
683 mCurves = newCurves;
684}
685
686void QgsCompoundCurve::draw( QPainter &p ) const
687{
688 for ( const QgsCurve *curve : mCurves )
689 {
690 curve->draw( p );
691 }
692}
693
695{
696 for ( QgsCurve *curve : std::as_const( mCurves ) )
697 {
698 curve->transform( ct, d, transformZ );
699 }
700 clearCache();
701}
702
703void QgsCompoundCurve::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
704{
705 for ( QgsCurve *curve : std::as_const( mCurves ) )
706 {
707 curve->transform( t, zTranslate, zScale, mTranslate, mScale );
708 }
709 clearCache();
710}
711
712void QgsCompoundCurve::addToPainterPath( QPainterPath &path ) const
713{
714 QPainterPath pp;
715
716 for ( const QgsCurve *curve : mCurves )
717 {
718 if ( curve != mCurves.at( 0 ) && pp.currentPosition() != curve->startPoint().toQPointF() )
719 {
720 pp.lineTo( curve->startPoint().toQPointF() );
721 }
722 curve->addToPainterPath( pp );
723 }
724 path.addPath( pp );
725}
726
727void QgsCompoundCurve::drawAsPolygon( QPainter &p ) const
728{
729 QPainterPath pp;
730 for ( const QgsCurve *curve : mCurves )
731 {
732 if ( curve != mCurves.at( 0 ) && pp.currentPosition() != curve->startPoint().toQPointF() )
733 {
734 pp.lineTo( curve->startPoint().toQPointF() );
735 }
736 curve->addToPainterPath( pp );
737 }
738 p.drawPath( pp );
739}
740
742{
743 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
744 if ( curveIds.empty() )
745 {
746 return false;
747 }
748 int curveId = curveIds.at( 0 ).first;
749 if ( curveId >= mCurves.size() )
750 {
751 return false;
752 }
753
754 bool success = mCurves.at( curveId )->insertVertex( curveIds.at( 0 ).second, vertex );
755 if ( success )
756 {
757 clearCache(); //bbox changed
758 }
759 return success;
760}
761
762bool QgsCompoundCurve::moveVertex( QgsVertexId position, const QgsPoint &newPos )
763{
764 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
765 QVector< QPair<int, QgsVertexId> >::const_iterator idIt = curveIds.constBegin();
766 for ( ; idIt != curveIds.constEnd(); ++idIt )
767 {
768 mCurves.at( idIt->first )->moveVertex( idIt->second, newPos );
769 }
770
771 bool success = !curveIds.isEmpty();
772 if ( success )
773 {
774 clearCache(); //bbox changed
775 }
776 return success;
777}
778
780{
781 const QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
782 if ( curveIds.isEmpty() )
783 return false;
784
785 const int curveId = curveIds.at( 0 ).first;
786 QgsCurve *curve = mCurves.at( curveId );
787 const QgsVertexId subVertexId = curveIds.at( 0 ).second;
788
789 // We are on a vertex that belongs to one curve only
790 if ( curveIds.size() == 1 )
791 {
792 const QgsCircularString *circularString = qgsgeometry_cast<const QgsCircularString *>( curve );
793 // If the vertex to delete is the middle vertex of a CircularString, we transform
794 // this CircularString into a LineString without the middle vertex
795 if ( circularString && subVertexId.vertex % 2 == 1 )
796 {
797 {
799 circularString->points( points );
800
801 removeCurve( curveId );
802
803 if ( subVertexId.vertex < points.length() - 2 )
804 {
805 std::unique_ptr<QgsCircularString> curveC = std::make_unique<QgsCircularString>();
806 curveC->setPoints( points.mid( subVertexId.vertex + 1 ) );
807 mCurves.insert( curveId, curveC.release() );
808 }
809
810 const QgsPointSequence partB = QgsPointSequence() << points[subVertexId.vertex - 1] << points[subVertexId.vertex + 1];
811 std::unique_ptr<QgsLineString> curveB = std::make_unique<QgsLineString>();
812 curveB->setPoints( partB );
813 mCurves.insert( curveId, curveB.release() );
814 curve = mCurves.at( curveId );
815
816 if ( subVertexId.vertex > 1 )
817 {
818 std::unique_ptr<QgsCircularString> curveA = std::make_unique<QgsCircularString>();
819 curveA->setPoints( points.mid( 0, subVertexId.vertex ) );
820 mCurves.insert( curveId, curveA.release() );
821 }
822 }
823 }
824 else if ( !curve->deleteVertex( subVertexId ) )
825 {
826 clearCache(); //bbox may have changed
827 return false;
828 }
829 if ( curve->numPoints() == 0 )
830 {
831 removeCurve( curveId );
832 }
833 }
834 // We are on a vertex that belongs to two curves
835 else if ( curveIds.size() == 2 )
836 {
837 const int nextCurveId = curveIds.at( 1 ).first;
838 QgsCurve *nextCurve = mCurves.at( nextCurveId );
839 const QgsVertexId nextSubVertexId = curveIds.at( 1 ).second;
840
841 Q_ASSERT( nextCurveId == curveId + 1 );
842 Q_ASSERT( subVertexId.vertex == curve->numPoints() - 1 );
843 Q_ASSERT( nextSubVertexId.vertex == 0 );
844
845 // globals start and end points
846 const QgsPoint startPoint = curve->startPoint();
847 const QgsPoint endPoint = nextCurve->endPoint();
848
849 // delete the vertex on first curve
850 if ( !curve->deleteVertex( subVertexId ) )
851 {
852 clearCache(); //bbox may have changed
853 return false;
854 }
855
856 // delete the vertex on second curve
857 if ( !nextCurve->deleteVertex( nextSubVertexId ) )
858 {
859 clearCache(); //bbox may have changed
860 return false;
861 }
862
863 // if first curve is now empty and second is not then
864 // create a LineString to link from the global start point to the
865 // new start of the second curve and delete the first curve
866 if ( curve->numPoints() == 0 && nextCurve->numPoints() != 0 )
867 {
868 QgsPoint startPointOfSecond = nextCurve->startPoint();
869 removeCurve( curveId );
870 QgsLineString *line = new QgsLineString();
871 line->insertVertex( QgsVertexId( 0, 0, 0 ), startPoint );
872 line->insertVertex( QgsVertexId( 0, 0, 1 ), startPointOfSecond );
873 mCurves.insert( curveId, line );
874 }
875 // else, if the first curve is not empty and the second is
876 // then create a LineString to link from the new end of the first curve to the
877 // global end point and delete the first curve
878 else if ( curve->numPoints() != 0 && nextCurve->numPoints() == 0 )
879 {
880 QgsPoint endPointOfFirst = curve->endPoint();
881 removeCurve( nextCurveId );
882 QgsLineString *line = new QgsLineString();
883 line->insertVertex( QgsVertexId( 0, 0, 0 ), endPointOfFirst );
884 line->insertVertex( QgsVertexId( 0, 0, 1 ), endPoint );
885 mCurves.insert( nextCurveId, line );
886 }
887 // else, if both curves are empty then
888 // remove both curves and create a LineString to link
889 // the curves before and the curves after the whole geometry
890 else if ( curve->numPoints() == 0 &&
891 nextCurve->numPoints() == 0 )
892 {
893 removeCurve( nextCurveId );
894 removeCurve( curveId );
895 QgsLineString *line = new QgsLineString();
896 line->insertVertex( QgsVertexId( 0, 0, 0 ), startPoint );
897 line->insertVertex( QgsVertexId( 0, 0, 1 ), endPoint );
898 mCurves.insert( curveId, line );
899 }
900 // else, both curves still have vertices, create a LineString to link
901 // the curves if needed
902 else
903 {
904 QgsPoint endPointOfFirst = curve->endPoint();
905 QgsPoint startPointOfSecond = nextCurve->startPoint();
906 if ( endPointOfFirst != startPointOfSecond )
907 {
908 QgsLineString *line = new QgsLineString();
909 line->insertVertex( QgsVertexId( 0, 0, 0 ), endPointOfFirst );
910 line->insertVertex( QgsVertexId( 0, 0, 1 ), startPointOfSecond );
911 mCurves.insert( nextCurveId, line );
912 }
913 }
914 condenseCurves(); // We merge consecutive LineStrings and CircularStrings
915 }
916
917 bool success = !curveIds.isEmpty();
918 if ( success )
919 clearCache(); //bbox changed
920 return success;
921}
922
923QVector< QPair<int, QgsVertexId> > QgsCompoundCurve::curveVertexId( QgsVertexId id ) const
924{
925 QVector< QPair<int, QgsVertexId> > curveIds;
926
927 int currentVertexIndex = 0;
928 for ( int i = 0; i < mCurves.size(); ++i )
929 {
930 int increment = mCurves.at( i )->numPoints() - 1;
931 if ( id.vertex >= currentVertexIndex && id.vertex <= currentVertexIndex + increment )
932 {
933 int curveVertexId = id.vertex - currentVertexIndex;
934 QgsVertexId vid;
935 vid.part = 0;
936 vid.ring = 0;
937 vid.vertex = curveVertexId;
938 curveIds.append( qMakePair( i, vid ) );
939 if ( curveVertexId == increment && i < ( mCurves.size() - 1 ) ) //add first vertex of next curve
940 {
941 vid.vertex = 0;
942 curveIds.append( qMakePair( i + 1, vid ) );
943 }
944 break;
945 }
946 else if ( id.vertex >= currentVertexIndex && id.vertex == currentVertexIndex + increment + 1 && i == ( mCurves.size() - 1 ) )
947 {
948 int curveVertexId = id.vertex - currentVertexIndex;
949 QgsVertexId vid;
950 vid.part = 0;
951 vid.ring = 0;
952 vid.vertex = curveVertexId;
953 curveIds.append( qMakePair( i, vid ) );
954 break;
955 }
956 currentVertexIndex += increment;
957 }
958
959 return curveIds;
960}
961
963{
964
965 // First we find out the sub-curves that are contain that vertex.
966
967 // If there is more than one, it means the vertex was at the beginning or end
968 // of an arc, which we don't support.
969
970 // If there is exactly one, we may either be on a LineString, or on a CircularString.
971
972 // If on CircularString, we need to check if the vertex is a CurveVertex (odd index).
973 // If so, we split the subcurve at vertex -1 and +1, , drop the middle part and insert a LineString/CircularString
974 // instead with the same points.
975
976 // At the end, we call condenseCurves() to merge successible line/circular strings
977
978 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
979
980 // We cannot convert points at start/end of subcurves
981 if ( curveIds.length() != 1 )
982 return false;
983
984 int curveId = curveIds[0].first;
985 QgsVertexId subVertexId = curveIds[0].second;
986 QgsCurve *curve = mCurves[curveId];
987
988 // We cannot convert first/last point of curve
989 if ( subVertexId.vertex == 0 || subVertexId.vertex == curve->numPoints() - 1 )
990 return false;
991
992 if ( const QgsCircularString *circularString = qgsgeometry_cast<const QgsCircularString *>( curve ) )
993 {
994 // If it's a circular string, we convert to LineString
995
996 // We cannot convert start/end points of arcs
997 if ( subVertexId.vertex % 2 == 0 ) // for some reason, subVertexId.type is always SegmentVertex...
998 return false;
999
1001 circularString->points( points );
1002
1003 const QgsPointSequence partA = points.mid( 0, subVertexId.vertex );
1004 const QgsPointSequence partB = QgsPointSequence() << points[subVertexId.vertex - 1] << points[subVertexId.vertex] << points[subVertexId.vertex + 1];
1005 const QgsPointSequence partC = points.mid( subVertexId.vertex + 1 );
1006
1007 std::unique_ptr<QgsCircularString> curveA = std::make_unique<QgsCircularString>();
1008 curveA->setPoints( partA );
1009 std::unique_ptr<QgsLineString> curveB = std::make_unique<QgsLineString>();
1010 curveB->setPoints( partB );
1011 std::unique_ptr<QgsCircularString> curveC = std::make_unique<QgsCircularString>();
1012 curveC->setPoints( partC );
1013
1014 removeCurve( curveId );
1015 if ( subVertexId.vertex < points.length() - 2 )
1016 mCurves.insert( curveId, curveC.release() );
1017 mCurves.insert( curveId, curveB.release() );
1018 if ( subVertexId.vertex > 1 )
1019 mCurves.insert( curveId, curveA.release() );
1020 }
1021 else if ( const QgsLineString *lineString = dynamic_cast<const QgsLineString *>( curve ) )
1022 {
1023 // If it's a linestring, we split and insert a curve
1024
1026 lineString->points( points );
1027
1028 const QgsPointSequence partA = points.mid( 0, subVertexId.vertex );
1029 const QgsPointSequence partB = QgsPointSequence() << points[subVertexId.vertex - 1] << points[subVertexId.vertex] << points[subVertexId.vertex + 1];
1030 const QgsPointSequence partC = points.mid( subVertexId.vertex + 1 );
1031
1032 std::unique_ptr<QgsLineString> curveA = std::make_unique<QgsLineString>();
1033 curveA->setPoints( partA );
1034 std::unique_ptr<QgsCircularString> curveB = std::make_unique<QgsCircularString>();
1035 curveB->setPoints( partB );
1036 std::unique_ptr<QgsLineString> curveC = std::make_unique<QgsLineString>();
1037 curveC->setPoints( partC );
1038
1039 removeCurve( curveId );
1040 if ( subVertexId.vertex < points.length() - 2 )
1041 mCurves.insert( curveId, curveC.release() );
1042 mCurves.insert( curveId, curveB.release() );
1043 if ( subVertexId.vertex > 1 )
1044 mCurves.insert( curveId, curveA.release() );
1045 }
1046
1047 // We merge consecutive LineStrings
1049
1050 clearCache();
1051 return true;
1052}
1053
1054
1055double QgsCompoundCurve::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1056{
1057 return QgsGeometryUtils::closestSegmentFromComponents( mCurves, QgsGeometryUtils::Vertex, pt, segmentPt, vertexAfter, leftOf, epsilon );
1058}
1059
1060bool QgsCompoundCurve::pointAt( int node, QgsPoint &point, Qgis::VertexType &type ) const
1061{
1062 int currentVertexId = 0;
1063 for ( int j = 0; j < mCurves.size(); ++j )
1064 {
1065 int nCurvePoints = mCurves.at( j )->numPoints();
1066 if ( ( node - currentVertexId ) < nCurvePoints )
1067 {
1068 return ( mCurves.at( j )->pointAt( node - currentVertexId, point, type ) );
1069 }
1070 currentVertexId += ( nCurvePoints - 1 );
1071 }
1072 return false;
1073}
1074
1075double QgsCompoundCurve::xAt( int index ) const
1076{
1077 int currentVertexId = 0;
1078 for ( int j = 0; j < mCurves.size(); ++j )
1079 {
1080 int nCurvePoints = mCurves.at( j )->numPoints();
1081 if ( ( index - currentVertexId ) < nCurvePoints )
1082 {
1083 return mCurves.at( j )->xAt( index - currentVertexId );
1084 }
1085 currentVertexId += ( nCurvePoints - 1 );
1086 }
1087 return 0.0;
1088}
1089
1090double QgsCompoundCurve::yAt( int index ) const
1091{
1092 int currentVertexId = 0;
1093 for ( int j = 0; j < mCurves.size(); ++j )
1094 {
1095 int nCurvePoints = mCurves.at( j )->numPoints();
1096 if ( ( index - currentVertexId ) < nCurvePoints )
1097 {
1098 return mCurves.at( j )->yAt( index - currentVertexId );
1099 }
1100 currentVertexId += ( nCurvePoints - 1 );
1101 }
1102 return 0.0;
1103}
1104
1105double QgsCompoundCurve::zAt( int index ) const
1106{
1107 int currentVertexId = 0;
1108 for ( int j = 0; j < mCurves.size(); ++j )
1109 {
1110 int nCurvePoints = mCurves.at( j )->numPoints();
1111 if ( ( index - currentVertexId ) < nCurvePoints )
1112 {
1113 return mCurves.at( j )->zAt( index - currentVertexId );
1114 }
1115 currentVertexId += ( nCurvePoints - 1 );
1116 }
1117 return 0.0;
1118}
1119
1120double QgsCompoundCurve::mAt( int index ) const
1121{
1122 int currentVertexId = 0;
1123 for ( int j = 0; j < mCurves.size(); ++j )
1124 {
1125 int nCurvePoints = mCurves.at( j )->numPoints();
1126 if ( ( index - currentVertexId ) < nCurvePoints )
1127 {
1128 return mCurves.at( j )->mAt( index - currentVertexId );
1129 }
1130 currentVertexId += ( nCurvePoints - 1 );
1131 }
1132 return 0.0;
1133}
1134
1136{
1137 bool res = true;
1138 for ( QgsCurve *curve : std::as_const( mCurves ) )
1139 {
1140 if ( !curve->transform( transformer ) )
1141 {
1142 res = false;
1143 break;
1144 }
1145
1146 if ( feedback && feedback->isCanceled() )
1147 {
1148 res = false;
1149 break;
1150 }
1151 }
1152 clearCache();
1153 return res;
1154}
1155
1156void QgsCompoundCurve::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1157{
1158 for ( QgsCurve *curve : std::as_const( mCurves ) )
1159 {
1160 curve->filterVertices( filter );
1161 }
1162 clearCache();
1163}
1164
1165void QgsCompoundCurve::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1166{
1167 for ( QgsCurve *curve : std::as_const( mCurves ) )
1168 {
1169 curve->transformVertices( transform );
1170 }
1171 clearCache();
1172}
1173
1174std::tuple<std::unique_ptr<QgsCurve>, std::unique_ptr<QgsCurve> > QgsCompoundCurve::splitCurveAtVertex( int index ) const
1175{
1176 if ( mCurves.empty() )
1177 return std::make_tuple( std::make_unique< QgsCompoundCurve >(), std::make_unique< QgsCompoundCurve >() );
1178
1179 int curveStart = 0;
1180
1181 std::unique_ptr< QgsCompoundCurve > curve1 = std::make_unique< QgsCompoundCurve >();
1182 std::unique_ptr< QgsCompoundCurve > curve2;
1183
1184 for ( const QgsCurve *curve : mCurves )
1185 {
1186 const int curveSize = curve->numPoints();
1187 if ( !curve2 && index < curveStart + curveSize )
1188 {
1189 // split the curve
1190 auto [ p1, p2 ] = curve->splitCurveAtVertex( index - curveStart );
1191 if ( !p1->isEmpty() )
1192 curve1->addCurve( p1.release() );
1193
1194 curve2 = std::make_unique< QgsCompoundCurve >();
1195 if ( !p2->isEmpty() )
1196 curve2->addCurve( p2.release() );
1197 }
1198 else
1199 {
1200 if ( curve2 )
1201 curve2->addCurve( curve->clone() );
1202 else
1203 curve1->addCurve( curve->clone() );
1204 }
1205
1206 // subtract 1 here, because the next curve will start with the same
1207 // vertex as this curve ended at
1208 curveStart += curve->numPoints() - 1;
1209 }
1210
1211 return std::make_tuple( std::move( curve1 ), curve2 ? std::move( curve2 ) : std::make_unique< QgsCompoundCurve >() );
1212}
1213
1214void QgsCompoundCurve::sumUpArea( double &sum ) const
1215{
1217 {
1218 sum += mSummedUpArea;
1219 return;
1220 }
1221
1222 mSummedUpArea = 0;
1223 for ( const QgsCurve *curve : mCurves )
1224 {
1225 curve->sumUpArea( mSummedUpArea );
1226 }
1228 sum += mSummedUpArea;
1229}
1230
1232{
1233 if ( numPoints() < 1 || isClosed() )
1234 {
1235 return;
1236 }
1237 addVertex( startPoint() );
1238}
1239
1241{
1242 for ( const QgsCurve *curve : mCurves )
1243 {
1244 if ( curve->hasCurvedSegments() )
1245 {
1246 return true;
1247 }
1248 }
1249 return false;
1250}
1251
1253{
1254 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( vertex );
1255 if ( curveIds.size() == 1 )
1256 {
1257 QgsCurve *curve = mCurves[curveIds.at( 0 ).first];
1258 return curve->vertexAngle( curveIds.at( 0 ).second );
1259 }
1260 else if ( curveIds.size() > 1 )
1261 {
1262 QgsCurve *curve1 = mCurves[curveIds.at( 0 ).first];
1263 QgsCurve *curve2 = mCurves[curveIds.at( 1 ).first];
1264 double angle1 = curve1->vertexAngle( curveIds.at( 0 ).second );
1265 double angle2 = curve2->vertexAngle( curveIds.at( 1 ).second );
1266 return QgsGeometryUtilsBase::averageAngle( angle1, angle2 );
1267 }
1268 else
1269 {
1270 return 0.0;
1271 }
1272}
1273
1275{
1276 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( startVertex );
1277 double length = 0.0;
1278 for ( auto it = curveIds.constBegin(); it != curveIds.constEnd(); ++it )
1279 {
1280 length += mCurves.at( it->first )->segmentLength( it->second );
1281 }
1282 return length;
1283}
1284
1286{
1288 for ( int i = mCurves.count() - 1; i >= 0; --i )
1289 {
1290 QgsCurve *reversedCurve = mCurves.at( i )->reversed();
1291 clone->addCurve( reversedCurve );
1292 }
1293 return clone;
1294}
1295
1296QgsPoint *QgsCompoundCurve::interpolatePoint( const double distance ) const
1297{
1298 if ( distance < 0 )
1299 return nullptr;
1300
1301 double distanceTraversed = 0;
1302 for ( const QgsCurve *curve : mCurves )
1303 {
1304 const double thisCurveLength = curve->length();
1305 if ( distanceTraversed + thisCurveLength > distance || qgsDoubleNear( distanceTraversed + thisCurveLength, distance ) )
1306 {
1307 // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1308 const double distanceToPoint = std::min( distance - distanceTraversed, thisCurveLength );
1309
1310 // point falls on this curve
1311 return curve->interpolatePoint( distanceToPoint );
1312 }
1313
1314 distanceTraversed += thisCurveLength;
1315 }
1316
1317 return nullptr;
1318}
1319
1320QgsCompoundCurve *QgsCompoundCurve::curveSubstring( double startDistance, double endDistance ) const
1321{
1322 if ( startDistance < 0 && endDistance < 0 )
1323 return createEmptyWithSameType();
1324
1325 endDistance = std::max( startDistance, endDistance );
1326 std::unique_ptr< QgsCompoundCurve > substring = std::make_unique< QgsCompoundCurve >();
1327
1328 double distanceTraversed = 0;
1329 for ( const QgsCurve *curve : mCurves )
1330 {
1331 const double thisCurveLength = curve->length();
1332 if ( distanceTraversed + thisCurveLength < startDistance )
1333 {
1334 // keep going - haven't found start yet, so no need to include this curve at all
1335 }
1336 else
1337 {
1338 std::unique_ptr< QgsCurve > part( curve->curveSubstring( startDistance - distanceTraversed, endDistance - distanceTraversed ) );
1339 if ( part )
1340 substring->addCurve( part.release() );
1341 }
1342
1343 distanceTraversed += thisCurveLength;
1344 if ( distanceTraversed > endDistance )
1345 break;
1346 }
1347
1348 return substring.release();
1349}
1350
1351bool QgsCompoundCurve::addZValue( double zValue )
1352{
1353 if ( QgsWkbTypes::hasZ( mWkbType ) )
1354 return false;
1355
1357
1358 for ( QgsCurve *curve : std::as_const( mCurves ) )
1359 {
1360 curve->addZValue( zValue );
1361 }
1362 clearCache();
1363 return true;
1364}
1365
1366bool QgsCompoundCurve::addMValue( double mValue )
1367{
1368 if ( QgsWkbTypes::hasM( mWkbType ) )
1369 return false;
1370
1372
1373 for ( QgsCurve *curve : std::as_const( mCurves ) )
1374 {
1375 curve->addMValue( mValue );
1376 }
1377 clearCache();
1378 return true;
1379}
1380
1382{
1383 if ( !QgsWkbTypes::hasZ( mWkbType ) )
1384 return false;
1385
1387 for ( QgsCurve *curve : std::as_const( mCurves ) )
1388 {
1389 curve->dropZValue();
1390 }
1391 clearCache();
1392 return true;
1393}
1394
1396{
1397 if ( !QgsWkbTypes::hasM( mWkbType ) )
1398 return false;
1399
1401 for ( QgsCurve *curve : std::as_const( mCurves ) )
1402 {
1403 curve->dropMValue();
1404 }
1405 clearCache();
1406 return true;
1407}
1408
1410{
1411 for ( QgsCurve *curve : std::as_const( mCurves ) )
1412 {
1413 curve->swapXy();
1414 }
1415 clearCache();
1416}
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
Definition qgis.h:1985
VertexType
Types of vertex.
Definition qgis.h:2906
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:256
@ CompoundCurve
CompoundCurve.
@ LineString
LineString.
@ Unknown
Unknown.
@ CircularString
CircularString.
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2565
An abstract base class for classes which transform geometries by transforming input points to output ...
Abstract base class for all geometries.
virtual bool fromWkb(QgsConstWkbPtr &wkb)=0
Sets the geometry from a WKB string.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
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.
QgsAbstractGeometry & operator=(const QgsAbstractGeometry &geom)
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.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
virtual bool boundingBoxIntersects(const QgsRectangle &rectangle) const
Returns true if the bounding box of this geometry intersects with a rectangle.
virtual bool deleteVertex(QgsVertexId position)=0
Deletes a vertex within the geometry.
virtual int compareTo(const QgsAbstractGeometry *other) const
Comparator for sorting of geometry.
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
bool intersects(const QgsBox3D &other) const
Returns true if box intersects with another box.
Definition qgsbox3d.cpp:144
void combineWith(const QgsBox3D &box)
Expands the bbox so that it covers both the original rectangle and the given rectangle.
Definition qgsbox3d.cpp:208
bool isNull() const
Test if the box is null (holding no spatial information).
Definition qgsbox3d.cpp:310
Circular string geometry type.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
Compound curve geometry type.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
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.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
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...
QgsAbstractGeometry * simplifyByDistance(double tolerance) const override
Simplifies the geometry by applying the Douglas Peucker simplification by distance algorithm.
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...
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
QgsCompoundCurve * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
void condenseCurves()
Condenses the curves in this geometry by combining adjacent linestrings a to a single continuous line...
void close()
Appends first point if not already closed.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
int dimension() const override
Returns the inherent dimension of the geometry.
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...
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...
bool boundingBoxIntersects(const QgsBox3D &box3d) const override
Returns true if the bounding box of this geometry intersects with a box3d.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
QString geometryType() const override
Returns a unique string representing the geometry type.
double mAt(int index) const override
Returns the m-coordinate of the specified node in the line string.
bool isEmpty() const override
Returns true if the geometry is empty.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
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.
int nCurves() const
Returns the number of curves in the geometry.
bool toggleCircularAtVertex(QgsVertexId position)
Converts the vertex at the given position from/to circular.
QgsBox3D calculateBoundingBox3D() const override
Calculates the minimal 3D bounding box for the geometry.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
const QgsAbstractGeometry * simplifiedTypeRef() const override
Returns a reference to the simplest lossless representation of this geometry, e.g.
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb()
void swapXy() override
Swaps the x and y coordinates from the geometry.
void removeCurve(int i)
Removes a curve from the geometry.
void addCurve(QgsCurve *c, bool extendPrevious=false)
Adds a curve to the geometry (takes ownership).
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.
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.
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.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
bool dropMValue() override
Drops any measure values which exist in the geometry.
QgsCompoundCurve & operator=(const QgsCompoundCurve &curve)
QgsCompoundCurve * clone() const override
Clones the geometry by performing a deep copy.
const QgsCurve * curveAt(int i) const
Returns the curve at the specified index.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
~QgsCompoundCurve() override
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.
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...
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
QgsCompoundCurve * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
void scroll(int firstVertexIndex) final
Scrolls the curve vertices so that they start with the vertex at the given index.
double length() const override
Returns the planar, 2-dimensional length of the geometry.
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
QgsCompoundCurve * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
bool pointAt(int node, QgsPoint &point, Qgis::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
QgsPoint startPoint() const override
Returns the starting point of the curve.
QgsCompoundCurve * 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.
QgsPoint endPoint() const override
Returns the end point of the curve.
int numPoints() const override
Returns the number of points in the curve.
double zAt(int index) const override
Returns the z-coordinate of the specified node in the line string.
void addVertex(const QgsPoint &pt)
Adds a vertex to the end of the geometry.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
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.
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.
Abstract base class for curved geometry type.
Definition qgscurve.h:35
virtual int numPoints() const =0
Returns the number of points in the curve.
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:354
virtual bool isClosed() const
Returns true if the curve is closed.
Definition qgscurve.cpp:53
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
QgsBox3D mBoundingBox
Cached bounding box.
Definition qgscurve.h:352
virtual QgsPoint startPoint() const =0
Returns the starting point of the curve.
virtual QgsPoint endPoint() const =0
Returns the end point of the curve.
double mSummedUpArea
Definition qgscurve.h:355
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 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 QStringList wktGetChildBlocks(const QString &wkt, const QString &defaultType=QString())
Parses a WKT string and returns of list of blocks contained in the WKT.
static QPair< Qgis::WkbType, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
static double closestSegmentFromComponents(T &container, ComponentType ctype, const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon)
Line string geometry type, with support for z-dimension and m-values.
void append(const QgsLineString *line)
Appends the contents of another line string to the end of this line string.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
QgsPoint vertexAt(QgsVertexId) const override
Returns the point corresponding to a specified vertex id.
Definition qgspoint.cpp:527
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...
Definition qgspoint.cpp:136
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.
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:6066
QVector< QgsPoint > QgsPointSequence
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