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