QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
34{
36}
37
39{
40 clear();
41}
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
102{
103 if ( &curve != this )
104 {
105 clearCache();
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 ) 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 ) ) );
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
487bool QgsCompoundCurve::removeDuplicateNodes( double epsilon, bool useZValues )
488{
489 bool result = false;
490 const QVector< QgsCurve * > curves = mCurves;
491 int i = 0;
492 QgsPoint lastEnd;
493 for ( QgsCurve *curve : curves )
494 {
495 result = curve->removeDuplicateNodes( epsilon, useZValues ) || result;
496 if ( curve->numPoints() == 0 || qgsDoubleNear( curve->length(), 0.0, epsilon ) )
497 {
498 // empty curve, remove it
499 delete mCurves.takeAt( i );
500 result = true;
501 }
502 else
503 {
504 // ensure this line starts exactly where previous line ended
505 if ( i > 0 )
506 {
507 curve->moveVertex( QgsVertexId( -1, -1, 0 ), lastEnd );
508 }
509 lastEnd = curve->vertexAt( QgsVertexId( -1, -1, curve->numPoints() - 1 ) );
510 }
511 i++;
512 }
513 return result;
514}
515
517{
518 if ( mCurves.empty() )
519 return false;
520
521 // if we already have the bounding box calculated, then this check is trivial!
522 if ( !mBoundingBox.isNull() )
523 {
524 return mBoundingBox.intersects( box3d );
525 }
526
527 // otherwise loop through each member curve and test the bounding box intersection.
528 // This gives us a chance to use optimisations which may be present on the individual
529 // curve subclasses, and at worst it will cause a calculation of the bounding box
530 // of each individual member curve which we would have to do anyway... (and these
531 // bounding boxes are cached, so would be reused without additional expense)
532 for ( const QgsCurve *curve : mCurves )
533 {
534 if ( curve->boundingBoxIntersects( box3d ) )
535 return true;
536 }
537
538 // even if we don't intersect the bounding box of any member curves, we may still intersect the
539 // bounding box of the overall compound curve.
540 // so here we fall back to the non-optimised base class check which has to first calculate
541 // the overall bounding box of the compound curve..
543}
544
546{
547 if ( mCurves.size() == 1 )
548 return mCurves.at( 0 );
549 else
550 return this;
551}
552
554{
555 if ( i < 0 || i >= mCurves.size() )
556 {
557 return nullptr;
558 }
559 return mCurves.at( i );
560}
561
562void QgsCompoundCurve::addCurve( QgsCurve *c, const bool extendPrevious )
563{
564 if ( !c )
565 return;
566
567 if ( mCurves.empty() )
568 {
570 }
571
572 if ( QgsWkbTypes::hasZ( mWkbType ) && !QgsWkbTypes::hasZ( c->wkbType() ) )
573 {
574 c->addZValue();
575 }
576 else if ( !QgsWkbTypes::hasZ( mWkbType ) && QgsWkbTypes::hasZ( c->wkbType() ) )
577 {
578 c->dropZValue();
579 }
580 if ( QgsWkbTypes::hasM( mWkbType ) && !QgsWkbTypes::hasM( c->wkbType() ) )
581 {
582 c->addMValue();
583 }
584 else if ( !QgsWkbTypes::hasM( mWkbType ) && QgsWkbTypes::hasM( c->wkbType() ) )
585 {
586 c->dropMValue();
587 }
588
589 QgsLineString *previousLineString = !mCurves.empty() ? qgsgeometry_cast< QgsLineString * >( mCurves.constLast() ) : nullptr;
590 const QgsLineString *newLineString = qgsgeometry_cast< const QgsLineString * >( c );
591 const bool canExtendPrevious = extendPrevious && previousLineString && newLineString;
592 if ( canExtendPrevious )
593 {
594 previousLineString->append( newLineString );
595 // we are taking ownership, so delete the input curve
596 delete c;
597 c = nullptr;
598 }
599 else
600 {
601 mCurves.append( c );
602 }
603
604 clearCache();
605}
606
608{
609 if ( i < 0 || i >= mCurves.size() )
610 {
611 return;
612 }
613
614 delete mCurves.takeAt( i );
615 clearCache();
616}
617
619{
620 if ( mCurves.isEmpty() || mWkbType == Qgis::WkbType::Unknown )
621 {
623 }
624
625 //is last curve QgsLineString
626 QgsCurve *lastCurve = nullptr;
627 if ( !mCurves.isEmpty() )
628 {
629 lastCurve = mCurves.at( mCurves.size() - 1 );
630 }
631
632 QgsLineString *line = nullptr;
633 if ( !lastCurve || QgsWkbTypes::flatType( lastCurve->wkbType() ) != Qgis::WkbType::LineString )
634 {
635 line = new QgsLineString();
636 mCurves.append( line );
637 if ( lastCurve )
638 {
639 line->addVertex( lastCurve->endPoint() );
640 }
641 lastCurve = line;
642 }
643 else //create new QgsLineString* with point in it
644 {
645 line = static_cast<QgsLineString *>( lastCurve );
646 }
647 line->addVertex( pt );
648 clearCache();
649}
650
652{
653 QgsCurve *lastCurve = nullptr;
654 QVector< QgsCurve * > newCurves;
655 newCurves.reserve( mCurves.size() );
656 for ( QgsCurve *curve : std::as_const( mCurves ) )
657 {
658 if ( lastCurve && lastCurve->wkbType() == curve->wkbType() )
659 {
660 if ( QgsLineString *ls = qgsgeometry_cast< QgsLineString * >( lastCurve ) )
661 {
662 ls->append( qgsgeometry_cast< QgsLineString * >( curve ) );
663 delete curve;
664 }
665 else if ( QgsCircularString *cs = qgsgeometry_cast< QgsCircularString * >( lastCurve ) )
666 {
667 cs->append( qgsgeometry_cast< QgsCircularString * >( curve ) );
668 delete curve;
669 }
670 }
671 else
672 {
673 lastCurve = curve;
674 newCurves << curve;
675 }
676 }
677 mCurves = newCurves;
678}
679
680void QgsCompoundCurve::draw( QPainter &p ) const
681{
682 for ( const QgsCurve *curve : mCurves )
683 {
684 curve->draw( p );
685 }
686}
687
689{
690 for ( QgsCurve *curve : std::as_const( mCurves ) )
691 {
692 curve->transform( ct, d, transformZ );
693 }
694 clearCache();
695}
696
697void QgsCompoundCurve::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
698{
699 for ( QgsCurve *curve : std::as_const( mCurves ) )
700 {
701 curve->transform( t, zTranslate, zScale, mTranslate, mScale );
702 }
703 clearCache();
704}
705
706void QgsCompoundCurve::addToPainterPath( QPainterPath &path ) const
707{
708 QPainterPath pp;
709
710 for ( const QgsCurve *curve : mCurves )
711 {
712 if ( curve != mCurves.at( 0 ) && pp.currentPosition() != curve->startPoint().toQPointF() )
713 {
714 pp.lineTo( curve->startPoint().toQPointF() );
715 }
716 curve->addToPainterPath( pp );
717 }
718 path.addPath( pp );
719}
720
721void QgsCompoundCurve::drawAsPolygon( QPainter &p ) const
722{
723 QPainterPath pp;
724 for ( const QgsCurve *curve : mCurves )
725 {
726 if ( curve != mCurves.at( 0 ) && pp.currentPosition() != curve->startPoint().toQPointF() )
727 {
728 pp.lineTo( curve->startPoint().toQPointF() );
729 }
730 curve->addToPainterPath( pp );
731 }
732 p.drawPath( pp );
733}
734
736{
737 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
738 if ( curveIds.empty() )
739 {
740 return false;
741 }
742 int curveId = curveIds.at( 0 ).first;
743 if ( curveId >= mCurves.size() )
744 {
745 return false;
746 }
747
748 bool success = mCurves.at( curveId )->insertVertex( curveIds.at( 0 ).second, vertex );
749 if ( success )
750 {
751 clearCache(); //bbox changed
752 }
753 return success;
754}
755
756bool QgsCompoundCurve::moveVertex( QgsVertexId position, const QgsPoint &newPos )
757{
758 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
759 QVector< QPair<int, QgsVertexId> >::const_iterator idIt = curveIds.constBegin();
760 for ( ; idIt != curveIds.constEnd(); ++idIt )
761 {
762 mCurves.at( idIt->first )->moveVertex( idIt->second, newPos );
763 }
764
765 bool success = !curveIds.isEmpty();
766 if ( success )
767 {
768 clearCache(); //bbox changed
769 }
770 return success;
771}
772
774{
775 const QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
776 if ( curveIds.isEmpty() )
777 return false;
778
779 const int curveId = curveIds.at( 0 ).first;
780 QgsCurve *curve = mCurves.at( curveId );
781 const QgsVertexId subVertexId = curveIds.at( 0 ).second;
782
783 // We are on a vertex that belongs to one curve only
784 if ( curveIds.size() == 1 )
785 {
786 const QgsCircularString *circularString = qgsgeometry_cast<const QgsCircularString *>( curve );
787 // If the vertex to delete is the middle vertex of a CircularString, we transform
788 // this CircularString into a LineString without the middle vertex
789 if ( circularString && subVertexId.vertex % 2 == 1 )
790 {
791 {
793 circularString->points( points );
794
795 removeCurve( curveId );
796
797 if ( subVertexId.vertex < points.length() - 2 )
798 {
799 std::unique_ptr<QgsCircularString> curveC = std::make_unique<QgsCircularString>();
800 curveC->setPoints( points.mid( subVertexId.vertex + 1 ) );
801 mCurves.insert( curveId, curveC.release() );
802 }
803
804 const QgsPointSequence partB = QgsPointSequence() << points[subVertexId.vertex - 1] << points[subVertexId.vertex + 1];
805 std::unique_ptr<QgsLineString> curveB = std::make_unique<QgsLineString>();
806 curveB->setPoints( partB );
807 mCurves.insert( curveId, curveB.release() );
808 curve = mCurves.at( curveId );
809
810 if ( subVertexId.vertex > 1 )
811 {
812 std::unique_ptr<QgsCircularString> curveA = std::make_unique<QgsCircularString>();
813 curveA->setPoints( points.mid( 0, subVertexId.vertex ) );
814 mCurves.insert( curveId, curveA.release() );
815 }
816 }
817 }
818 else if ( !curve->deleteVertex( subVertexId ) )
819 {
820 clearCache(); //bbox may have changed
821 return false;
822 }
823 if ( curve->numPoints() == 0 )
824 {
825 removeCurve( curveId );
826 }
827 }
828 else if ( curveIds.size() == 2 )
829 {
830 const int nextCurveId = curveIds.at( 1 ).first;
831 QgsCurve *nextCurve = mCurves.at( nextCurveId );
832 const QgsVertexId nextSubVertexId = curveIds.at( 1 ).second;
833
834 Q_ASSERT( nextCurveId == curveId + 1 );
835 Q_ASSERT( subVertexId.vertex == curve->numPoints() - 1 );
836 Q_ASSERT( nextSubVertexId.vertex == 0 );
837
838 const QgsPoint startPoint = curve->startPoint();
839 const QgsPoint endPoint = nextCurve->endPoint();
840
843 nextCurve->numPoints() > 3 )
844 {
845 QgsPoint intermediatePoint;
846 Qgis::VertexType type;
847 nextCurve->pointAt( 2, intermediatePoint, type );
848 curve->moveVertex( QgsVertexId( 0, 0, curve->numPoints() - 1 ), intermediatePoint );
849 }
850 else if ( !curve->deleteVertex( subVertexId ) )
851 {
852 clearCache(); //bbox may have changed
853 return false;
854 }
856 curve->numPoints() > 0 &&
858 {
859 QgsPoint intermediatePoint = curve->endPoint();
860 nextCurve->moveVertex( QgsVertexId( 0, 0, 0 ), intermediatePoint );
861 }
862 else if ( !nextCurve->deleteVertex( nextSubVertexId ) )
863 {
864 clearCache(); //bbox may have changed
865 return false;
866 }
867 if ( curve->numPoints() == 0 &&
868 nextCurve->numPoints() != 0 )
869 {
870 nextCurve->moveVertex( QgsVertexId( 0, 0, 0 ), startPoint );
871 removeCurve( curveId );
872 }
873 else if ( curve->numPoints() != 0 && nextCurve->numPoints() == 0 )
874 {
875 curve->moveVertex( QgsVertexId( 0, 0, curve->numPoints() - 1 ), endPoint );
876 removeCurve( nextCurveId );
877 }
878 else if ( curve->numPoints() == 0 &&
879 nextCurve->numPoints() == 0 )
880 {
881 removeCurve( nextCurveId );
882 removeCurve( curveId );
883 QgsLineString *line = new QgsLineString();
884 line->insertVertex( QgsVertexId( 0, 0, 0 ), startPoint );
885 line->insertVertex( QgsVertexId( 0, 0, 1 ), endPoint );
886 mCurves.insert( curveId, line );
887 }
888 else
889 {
890 QgsPoint endPointOfFirst = curve->endPoint();
891 QgsPoint startPointOfSecond = nextCurve->startPoint();
892 if ( endPointOfFirst != startPointOfSecond )
893 {
894 QgsLineString *line = new QgsLineString();
895 line->insertVertex( QgsVertexId( 0, 0, 0 ), endPointOfFirst );
896 line->insertVertex( QgsVertexId( 0, 0, 1 ), startPointOfSecond );
897 mCurves.insert( nextCurveId, line );
898 }
899 }
900 }
901
902 bool success = !curveIds.isEmpty();
903 if ( success )
904 clearCache(); //bbox changed
905 return success;
906}
907
908QVector< QPair<int, QgsVertexId> > QgsCompoundCurve::curveVertexId( QgsVertexId id ) const
909{
910 QVector< QPair<int, QgsVertexId> > curveIds;
911
912 int currentVertexIndex = 0;
913 for ( int i = 0; i < mCurves.size(); ++i )
914 {
915 int increment = mCurves.at( i )->numPoints() - 1;
916 if ( id.vertex >= currentVertexIndex && id.vertex <= currentVertexIndex + increment )
917 {
918 int curveVertexId = id.vertex - currentVertexIndex;
919 QgsVertexId vid;
920 vid.part = 0;
921 vid.ring = 0;
922 vid.vertex = curveVertexId;
923 curveIds.append( qMakePair( i, vid ) );
924 if ( curveVertexId == increment && i < ( mCurves.size() - 1 ) ) //add first vertex of next curve
925 {
926 vid.vertex = 0;
927 curveIds.append( qMakePair( i + 1, vid ) );
928 }
929 break;
930 }
931 else if ( id.vertex >= currentVertexIndex && id.vertex == currentVertexIndex + increment + 1 && i == ( mCurves.size() - 1 ) )
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 break;
940 }
941 currentVertexIndex += increment;
942 }
943
944 return curveIds;
945}
946
948{
949
950 // First we find out the sub-curves that are contain that vertex.
951
952 // If there is more than one, it means the vertex was at the beginning or end
953 // of an arc, which we don't support.
954
955 // If there is exactly one, we may either be on a LineString, or on a CircularString.
956
957 // If on CircularString, we need to check if the vertex is a CurveVertex (odd index).
958 // If so, we split the subcurve at vertex -1 and +1, , drop the middle part and insert a LineString/CircularString
959 // instead with the same points.
960
961 // At the end, we call condenseCurves() to merge successible line/circular strings
962
963 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
964
965 // We cannot convert points at start/end of subcurves
966 if ( curveIds.length() != 1 )
967 return false;
968
969 int curveId = curveIds[0].first;
970 QgsVertexId subVertexId = curveIds[0].second;
971 QgsCurve *curve = mCurves[curveId];
972
973 // We cannot convert first/last point of curve
974 if ( subVertexId.vertex == 0 || subVertexId.vertex == curve->numPoints() - 1 )
975 return false;
976
977 if ( const QgsCircularString *circularString = qgsgeometry_cast<const QgsCircularString *>( curve ) )
978 {
979 // If it's a circular string, we convert to LineString
980
981 // We cannot convert start/end points of arcs
982 if ( subVertexId.vertex % 2 == 0 ) // for some reason, subVertexId.type is always SegmentVertex...
983 return false;
984
986 circularString->points( points );
987
988 const QgsPointSequence partA = points.mid( 0, subVertexId.vertex );
989 const QgsPointSequence partB = QgsPointSequence() << points[subVertexId.vertex - 1] << points[subVertexId.vertex] << points[subVertexId.vertex + 1];
990 const QgsPointSequence partC = points.mid( subVertexId.vertex + 1 );
991
992 std::unique_ptr<QgsCircularString> curveA = std::make_unique<QgsCircularString>();
993 curveA->setPoints( partA );
994 std::unique_ptr<QgsLineString> curveB = std::make_unique<QgsLineString>();
995 curveB->setPoints( partB );
996 std::unique_ptr<QgsCircularString> curveC = std::make_unique<QgsCircularString>();
997 curveC->setPoints( partC );
998
999 removeCurve( curveId );
1000 if ( subVertexId.vertex < points.length() - 2 )
1001 mCurves.insert( curveId, curveC.release() );
1002 mCurves.insert( curveId, curveB.release() );
1003 if ( subVertexId.vertex > 1 )
1004 mCurves.insert( curveId, curveA.release() );
1005 }
1006 else if ( const QgsLineString *lineString = dynamic_cast<const QgsLineString *>( curve ) )
1007 {
1008 // If it's a linestring, we split and insert a curve
1009
1011 lineString->points( points );
1012
1013 const QgsPointSequence partA = points.mid( 0, subVertexId.vertex );
1014 const QgsPointSequence partB = QgsPointSequence() << points[subVertexId.vertex - 1] << points[subVertexId.vertex] << points[subVertexId.vertex + 1];
1015 const QgsPointSequence partC = points.mid( subVertexId.vertex + 1 );
1016
1017 std::unique_ptr<QgsLineString> curveA = std::make_unique<QgsLineString>();
1018 curveA->setPoints( partA );
1019 std::unique_ptr<QgsCircularString> curveB = std::make_unique<QgsCircularString>();
1020 curveB->setPoints( partB );
1021 std::unique_ptr<QgsLineString> curveC = std::make_unique<QgsLineString>();
1022 curveC->setPoints( partC );
1023
1024 removeCurve( curveId );
1025 if ( subVertexId.vertex < points.length() - 2 )
1026 mCurves.insert( curveId, curveC.release() );
1027 mCurves.insert( curveId, curveB.release() );
1028 if ( subVertexId.vertex > 1 )
1029 mCurves.insert( curveId, curveA.release() );
1030 }
1031
1032 // We merge consecutive LineStrings
1034
1035 clearCache();
1036 return true;
1037}
1038
1039
1040double QgsCompoundCurve::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1041{
1042 return QgsGeometryUtils::closestSegmentFromComponents( mCurves, QgsGeometryUtils::Vertex, pt, segmentPt, vertexAfter, leftOf, epsilon );
1043}
1044
1045bool QgsCompoundCurve::pointAt( int node, QgsPoint &point, Qgis::VertexType &type ) const
1046{
1047 int currentVertexId = 0;
1048 for ( int j = 0; j < mCurves.size(); ++j )
1049 {
1050 int nCurvePoints = mCurves.at( j )->numPoints();
1051 if ( ( node - currentVertexId ) < nCurvePoints )
1052 {
1053 return ( mCurves.at( j )->pointAt( node - currentVertexId, point, type ) );
1054 }
1055 currentVertexId += ( nCurvePoints - 1 );
1056 }
1057 return false;
1058}
1059
1060double QgsCompoundCurve::xAt( int index ) 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 ( ( index - currentVertexId ) < nCurvePoints )
1067 {
1068 return mCurves.at( j )->xAt( index - currentVertexId );
1069 }
1070 currentVertexId += ( nCurvePoints - 1 );
1071 }
1072 return 0.0;
1073}
1074
1075double QgsCompoundCurve::yAt( 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 )->yAt( index - currentVertexId );
1084 }
1085 currentVertexId += ( nCurvePoints - 1 );
1086 }
1087 return 0.0;
1088}
1089
1090double QgsCompoundCurve::zAt( 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 )->zAt( index - currentVertexId );
1099 }
1100 currentVertexId += ( nCurvePoints - 1 );
1101 }
1102 return 0.0;
1103}
1104
1105double QgsCompoundCurve::mAt( 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 )->mAt( index - currentVertexId );
1114 }
1115 currentVertexId += ( nCurvePoints - 1 );
1116 }
1117 return 0.0;
1118}
1119
1121{
1122 bool res = true;
1123 for ( QgsCurve *curve : std::as_const( mCurves ) )
1124 {
1125 if ( !curve->transform( transformer ) )
1126 {
1127 res = false;
1128 break;
1129 }
1130
1131 if ( feedback && feedback->isCanceled() )
1132 {
1133 res = false;
1134 break;
1135 }
1136 }
1137 clearCache();
1138 return res;
1139}
1140
1141void QgsCompoundCurve::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1142{
1143 for ( QgsCurve *curve : std::as_const( mCurves ) )
1144 {
1145 curve->filterVertices( filter );
1146 }
1147 clearCache();
1148}
1149
1150void QgsCompoundCurve::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1151{
1152 for ( QgsCurve *curve : std::as_const( mCurves ) )
1153 {
1154 curve->transformVertices( transform );
1155 }
1156 clearCache();
1157}
1158
1159std::tuple<std::unique_ptr<QgsCurve>, std::unique_ptr<QgsCurve> > QgsCompoundCurve::splitCurveAtVertex( int index ) const
1160{
1161 if ( mCurves.empty() )
1162 return std::make_tuple( std::make_unique< QgsCompoundCurve >(), std::make_unique< QgsCompoundCurve >() );
1163
1164 int curveStart = 0;
1165
1166 std::unique_ptr< QgsCompoundCurve > curve1 = std::make_unique< QgsCompoundCurve >();
1167 std::unique_ptr< QgsCompoundCurve > curve2;
1168
1169 for ( const QgsCurve *curve : mCurves )
1170 {
1171 const int curveSize = curve->numPoints();
1172 if ( !curve2 && index < curveStart + curveSize )
1173 {
1174 // split the curve
1175 auto [ p1, p2 ] = curve->splitCurveAtVertex( index - curveStart );
1176 if ( !p1->isEmpty() )
1177 curve1->addCurve( p1.release() );
1178
1179 curve2 = std::make_unique< QgsCompoundCurve >();
1180 if ( !p2->isEmpty() )
1181 curve2->addCurve( p2.release() );
1182 }
1183 else
1184 {
1185 if ( curve2 )
1186 curve2->addCurve( curve->clone() );
1187 else
1188 curve1->addCurve( curve->clone() );
1189 }
1190
1191 // subtract 1 here, because the next curve will start with the same
1192 // vertex as this curve ended at
1193 curveStart += curve->numPoints() - 1;
1194 }
1195
1196 return std::make_tuple( std::move( curve1 ), curve2 ? std::move( curve2 ) : std::make_unique< QgsCompoundCurve >() );
1197}
1198
1199void QgsCompoundCurve::sumUpArea( double &sum ) const
1200{
1202 {
1203 sum += mSummedUpArea;
1204 return;
1205 }
1206
1207 mSummedUpArea = 0;
1208 for ( const QgsCurve *curve : mCurves )
1209 {
1210 curve->sumUpArea( mSummedUpArea );
1211 }
1213 sum += mSummedUpArea;
1214}
1215
1217{
1218 if ( numPoints() < 1 || isClosed() )
1219 {
1220 return;
1221 }
1222 addVertex( startPoint() );
1223}
1224
1226{
1227 for ( const QgsCurve *curve : mCurves )
1228 {
1229 if ( curve->hasCurvedSegments() )
1230 {
1231 return true;
1232 }
1233 }
1234 return false;
1235}
1236
1238{
1239 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( vertex );
1240 if ( curveIds.size() == 1 )
1241 {
1242 QgsCurve *curve = mCurves[curveIds.at( 0 ).first];
1243 return curve->vertexAngle( curveIds.at( 0 ).second );
1244 }
1245 else if ( curveIds.size() > 1 )
1246 {
1247 QgsCurve *curve1 = mCurves[curveIds.at( 0 ).first];
1248 QgsCurve *curve2 = mCurves[curveIds.at( 1 ).first];
1249 double angle1 = curve1->vertexAngle( curveIds.at( 0 ).second );
1250 double angle2 = curve2->vertexAngle( curveIds.at( 1 ).second );
1251 return QgsGeometryUtilsBase::averageAngle( angle1, angle2 );
1252 }
1253 else
1254 {
1255 return 0.0;
1256 }
1257}
1258
1260{
1261 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( startVertex );
1262 double length = 0.0;
1263 for ( auto it = curveIds.constBegin(); it != curveIds.constEnd(); ++it )
1264 {
1265 length += mCurves.at( it->first )->segmentLength( it->second );
1266 }
1267 return length;
1268}
1269
1271{
1273 for ( int i = mCurves.count() - 1; i >= 0; --i )
1274 {
1275 QgsCurve *reversedCurve = mCurves.at( i )->reversed();
1276 clone->addCurve( reversedCurve );
1277 }
1278 return clone;
1279}
1280
1281QgsPoint *QgsCompoundCurve::interpolatePoint( const double distance ) const
1282{
1283 if ( distance < 0 )
1284 return nullptr;
1285
1286 double distanceTraversed = 0;
1287 for ( const QgsCurve *curve : mCurves )
1288 {
1289 const double thisCurveLength = curve->length();
1290 if ( distanceTraversed + thisCurveLength > distance || qgsDoubleNear( distanceTraversed + thisCurveLength, distance ) )
1291 {
1292 // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1293 const double distanceToPoint = std::min( distance - distanceTraversed, thisCurveLength );
1294
1295 // point falls on this curve
1296 return curve->interpolatePoint( distanceToPoint );
1297 }
1298
1299 distanceTraversed += thisCurveLength;
1300 }
1301
1302 return nullptr;
1303}
1304
1305QgsCompoundCurve *QgsCompoundCurve::curveSubstring( double startDistance, double endDistance ) const
1306{
1307 if ( startDistance < 0 && endDistance < 0 )
1308 return createEmptyWithSameType();
1309
1310 endDistance = std::max( startDistance, endDistance );
1311 std::unique_ptr< QgsCompoundCurve > substring = std::make_unique< QgsCompoundCurve >();
1312
1313 double distanceTraversed = 0;
1314 for ( const QgsCurve *curve : mCurves )
1315 {
1316 const double thisCurveLength = curve->length();
1317 if ( distanceTraversed + thisCurveLength < startDistance )
1318 {
1319 // keep going - haven't found start yet, so no need to include this curve at all
1320 }
1321 else
1322 {
1323 std::unique_ptr< QgsCurve > part( curve->curveSubstring( startDistance - distanceTraversed, endDistance - distanceTraversed ) );
1324 if ( part )
1325 substring->addCurve( part.release() );
1326 }
1327
1328 distanceTraversed += thisCurveLength;
1329 if ( distanceTraversed > endDistance )
1330 break;
1331 }
1332
1333 return substring.release();
1334}
1335
1336bool QgsCompoundCurve::addZValue( double zValue )
1337{
1338 if ( QgsWkbTypes::hasZ( mWkbType ) )
1339 return false;
1340
1342
1343 for ( QgsCurve *curve : std::as_const( mCurves ) )
1344 {
1345 curve->addZValue( zValue );
1346 }
1347 clearCache();
1348 return true;
1349}
1350
1351bool QgsCompoundCurve::addMValue( double mValue )
1352{
1353 if ( QgsWkbTypes::hasM( mWkbType ) )
1354 return false;
1355
1357
1358 for ( QgsCurve *curve : std::as_const( mCurves ) )
1359 {
1360 curve->addMValue( mValue );
1361 }
1362 clearCache();
1363 return true;
1364}
1365
1367{
1368 if ( !QgsWkbTypes::hasZ( mWkbType ) )
1369 return false;
1370
1372 for ( QgsCurve *curve : std::as_const( mCurves ) )
1373 {
1374 curve->dropZValue();
1375 }
1376 clearCache();
1377 return true;
1378}
1379
1381{
1382 if ( !QgsWkbTypes::hasM( mWkbType ) )
1383 return false;
1384
1386 for ( QgsCurve *curve : std::as_const( mCurves ) )
1387 {
1388 curve->dropMValue();
1389 }
1390 clearCache();
1391 return true;
1392}
1393
1395{
1396 for ( QgsCurve *curve : std::as_const( mCurves ) )
1397 {
1398 curve->swapXy();
1399 }
1400 clearCache();
1401}
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
Definition: qgis.h:1648
VertexType
Types of vertex.
Definition: qgis.h:2477
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition: qgis.h:182
@ CompoundCurve
CompoundCurve.
@ LineString
LineString.
@ Unknown
Unknown.
@ CircularString
CircularString.
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition: qgis.h:2191
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.
virtual bool moveVertex(QgsVertexId position, const QgsPoint &newPos)=0
Moves a vertex within the geometry.
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:132
void combineWith(const QgsBox3D &box)
Expands the bbox so that it covers both the original rectangle and the given rectangle.
Definition: qgsbox3d.cpp:196
bool isNull() const
Test if the box is null (holding no spatial information).
Definition: qgsbox3d.cpp:289
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...
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) 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:356
virtual bool pointAt(int node, QgsPoint &point, Qgis::VertexType &type) const =0
Returns the point and vertex id of a point within the curve.
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:354
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:357
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.
Definition: qgslinestring.h:45
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:522
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:131
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.
Definition: qgswkbtypes.h:1144
static Qgis::WkbType dropZ(Qgis::WkbType type)
Drops the z dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1127
static Qgis::WkbType addM(Qgis::WkbType type)
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1092
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1068
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:973
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1023
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:628
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether 'thepoint' is left or right of the line from 'p1' to 'p2'. Negative values mean left ...
Definition: MathUtils.cpp:222
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:5207
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