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