QGIS API Documentation 3.33.0-Master (bf22a165b3)
Loading...
Searching...
No Matches
qgscompoundcurve.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgscompoundcurve.cpp
3 ----------------------
4 begin : September 2014
5 copyright : (C) 2014 by Marco Hugentobler
6 email : marco at sourcepole dot ch
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgsmessagelog.h"
19#include "qgscompoundcurve.h"
20#include "qgsapplication.h"
21#include "qgscircularstring.h"
22#include "qgsgeometryutils.h"
23#include "qgslinestring.h"
24#include "qgswkbptr.h"
25#include "qgsfeedback.h"
26
27#include <QJsonObject>
28#include <QPainter>
29#include <QPainterPath>
30#include <memory>
31#include <nlohmann/json.hpp>
32
37
42
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 QgsBox3D();
154 }
155
156 QgsBox3D bbox = mCurves.at( 0 )->boundingBox3D();
157 for ( int i = 1; i < mCurves.size(); ++i )
158 {
159 QgsBox3D curveBox = mCurves.at( i )->boundingBox3D();
160 bbox.combineWith( 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( box3d );
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( box3d ) )
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 const QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
797 if ( curveIds.isEmpty() )
798 return false;
799
800 const int curveId = curveIds.at( 0 ).first;
801 QgsCurve *curve = mCurves.at( curveId );
802 const QgsVertexId subVertexId = curveIds.at( 0 ).second;
803
804 // We are on a vertex that belongs to one curve only
805 if ( curveIds.size() == 1 )
806 {
807 const QgsCircularString *circularString = qgsgeometry_cast<const QgsCircularString *>( curve );
808 // If the vertex to delete is the middle vertex of a CircularString, we transform
809 // this CircularString into a LineString without the middle vertex
810 if ( circularString && subVertexId.vertex % 2 == 1 )
811 {
812 {
814 circularString->points( points );
815
816 removeCurve( curveId );
817
818 if ( subVertexId.vertex < points.length() - 2 )
819 {
820 std::unique_ptr<QgsCircularString> curveC = std::make_unique<QgsCircularString>();
821 curveC->setPoints( points.mid( subVertexId.vertex + 1 ) );
822 mCurves.insert( curveId, curveC.release() );
823 }
824
825 const QgsPointSequence partB = QgsPointSequence() << points[subVertexId.vertex - 1] << points[subVertexId.vertex + 1];
826 std::unique_ptr<QgsLineString> curveB = std::make_unique<QgsLineString>();
827 curveB->setPoints( partB );
828 mCurves.insert( curveId, curveB.release() );
829 curve = mCurves.at( curveId );
830
831 if ( subVertexId.vertex > 1 )
832 {
833 std::unique_ptr<QgsCircularString> curveA = std::make_unique<QgsCircularString>();
834 curveA->setPoints( points.mid( 0, subVertexId.vertex ) );
835 mCurves.insert( curveId, curveA.release() );
836 }
837 }
838 }
839 else if ( !curve->deleteVertex( subVertexId ) )
840 {
841 clearCache(); //bbox may have changed
842 return false;
843 }
844 if ( curve->numPoints() == 0 )
845 {
846 removeCurve( curveId );
847 }
848 }
849 else if ( curveIds.size() == 2 )
850 {
851 const int nextCurveId = curveIds.at( 1 ).first;
852 QgsCurve *nextCurve = mCurves.at( nextCurveId );
853 const QgsVertexId nextSubVertexId = curveIds.at( 1 ).second;
854
855 Q_ASSERT( nextCurveId == curveId + 1 );
856 Q_ASSERT( subVertexId.vertex == curve->numPoints() - 1 );
857 Q_ASSERT( nextSubVertexId.vertex == 0 );
858
859 const QgsPoint startPoint = curve->startPoint();
860 const QgsPoint endPoint = nextCurve->endPoint();
861
864 nextCurve->numPoints() > 3 )
865 {
866 QgsPoint intermediatePoint;
867 Qgis::VertexType type;
868 nextCurve->pointAt( 2, intermediatePoint, type );
869 curve->moveVertex( QgsVertexId( 0, 0, curve->numPoints() - 1 ), intermediatePoint );
870 }
871 else if ( !curve->deleteVertex( subVertexId ) )
872 {
873 clearCache(); //bbox may have changed
874 return false;
875 }
877 curve->numPoints() > 0 &&
879 {
880 QgsPoint intermediatePoint = curve->endPoint();
881 nextCurve->moveVertex( QgsVertexId( 0, 0, 0 ), intermediatePoint );
882 }
883 else if ( !nextCurve->deleteVertex( nextSubVertexId ) )
884 {
885 clearCache(); //bbox may have changed
886 return false;
887 }
888 if ( curve->numPoints() == 0 &&
889 nextCurve->numPoints() != 0 )
890 {
891 nextCurve->moveVertex( QgsVertexId( 0, 0, 0 ), startPoint );
892 removeCurve( curveId );
893 }
894 else if ( curve->numPoints() != 0 && nextCurve->numPoints() == 0 )
895 {
896 curve->moveVertex( QgsVertexId( 0, 0, curve->numPoints() - 1 ), endPoint );
897 removeCurve( nextCurveId );
898 }
899 else if ( curve->numPoints() == 0 &&
900 nextCurve->numPoints() == 0 )
901 {
902 removeCurve( nextCurveId );
903 removeCurve( curveId );
904 QgsLineString *line = new QgsLineString();
905 line->insertVertex( QgsVertexId( 0, 0, 0 ), startPoint );
906 line->insertVertex( QgsVertexId( 0, 0, 1 ), endPoint );
907 mCurves.insert( curveId, line );
908 }
909 else
910 {
911 QgsPoint endPointOfFirst = curve->endPoint();
912 QgsPoint startPointOfSecond = nextCurve->startPoint();
913 if ( endPointOfFirst != startPointOfSecond )
914 {
915 QgsLineString *line = new QgsLineString();
916 line->insertVertex( QgsVertexId( 0, 0, 0 ), endPointOfFirst );
917 line->insertVertex( QgsVertexId( 0, 0, 1 ), startPointOfSecond );
918 mCurves.insert( nextCurveId, line );
919 }
920 }
921 }
922
923 bool success = !curveIds.isEmpty();
924 if ( success )
925 clearCache(); //bbox changed
926 return success;
927}
928
929QVector< QPair<int, QgsVertexId> > QgsCompoundCurve::curveVertexId( QgsVertexId id ) const
930{
931 QVector< QPair<int, QgsVertexId> > curveIds;
932
933 int currentVertexIndex = 0;
934 for ( int i = 0; i < mCurves.size(); ++i )
935 {
936 int increment = mCurves.at( i )->numPoints() - 1;
937 if ( id.vertex >= currentVertexIndex && id.vertex <= currentVertexIndex + increment )
938 {
939 int curveVertexId = id.vertex - currentVertexIndex;
940 QgsVertexId vid;
941 vid.part = 0;
942 vid.ring = 0;
943 vid.vertex = curveVertexId;
944 curveIds.append( qMakePair( i, vid ) );
945 if ( curveVertexId == increment && i < ( mCurves.size() - 1 ) ) //add first vertex of next curve
946 {
947 vid.vertex = 0;
948 curveIds.append( qMakePair( i + 1, vid ) );
949 }
950 break;
951 }
952 else if ( id.vertex >= currentVertexIndex && id.vertex == currentVertexIndex + increment + 1 && i == ( mCurves.size() - 1 ) )
953 {
954 int curveVertexId = id.vertex - currentVertexIndex;
955 QgsVertexId vid;
956 vid.part = 0;
957 vid.ring = 0;
958 vid.vertex = curveVertexId;
959 curveIds.append( qMakePair( i, vid ) );
960 break;
961 }
962 currentVertexIndex += increment;
963 }
964
965 return curveIds;
966}
967
969{
970
971 // First we find out the sub-curves that are contain that vertex.
972
973 // If there is more than one, it means the vertex was at the beginning or end
974 // of an arc, which we don't support.
975
976 // If there is exactly one, we may either be on a LineString, or on a CircularString.
977
978 // If on CircularString, we need to check if the vertex is a CurveVertex (odd index).
979 // If so, we split the subcurve at vertex -1 and +1, , drop the middle part and insert a LineString/CircularString
980 // instead with the same points.
981
982 // At the end, we call condenseCurves() to merge successible line/circular strings
983
984 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
985
986 // We cannot convert points at start/end of subcurves
987 if ( curveIds.length() != 1 )
988 return false;
989
990 int curveId = curveIds[0].first;
991 QgsVertexId subVertexId = curveIds[0].second;
992 QgsCurve *curve = mCurves[curveId];
993
994 // We cannot convert first/last point of curve
995 if ( subVertexId.vertex == 0 || subVertexId.vertex == curve->numPoints() - 1 )
996 return false;
997
998 if ( const QgsCircularString *circularString = qgsgeometry_cast<const QgsCircularString *>( curve ) )
999 {
1000 // If it's a circular string, we convert to LineString
1001
1002 // We cannot convert start/end points of arcs
1003 if ( subVertexId.vertex % 2 == 0 ) // for some reason, subVertexId.type is always SegmentVertex...
1004 return false;
1005
1007 circularString->points( points );
1008
1009 const QgsPointSequence partA = points.mid( 0, subVertexId.vertex );
1010 const QgsPointSequence partB = QgsPointSequence() << points[subVertexId.vertex - 1] << points[subVertexId.vertex] << points[subVertexId.vertex + 1];
1011 const QgsPointSequence partC = points.mid( subVertexId.vertex + 1 );
1012
1013 std::unique_ptr<QgsCircularString> curveA = std::make_unique<QgsCircularString>();
1014 curveA->setPoints( partA );
1015 std::unique_ptr<QgsLineString> curveB = std::make_unique<QgsLineString>();
1016 curveB->setPoints( partB );
1017 std::unique_ptr<QgsCircularString> curveC = std::make_unique<QgsCircularString>();
1018 curveC->setPoints( partC );
1019
1020 removeCurve( curveId );
1021 if ( subVertexId.vertex < points.length() - 2 )
1022 mCurves.insert( curveId, curveC.release() );
1023 mCurves.insert( curveId, curveB.release() );
1024 if ( subVertexId.vertex > 1 )
1025 mCurves.insert( curveId, curveA.release() );
1026 }
1027 else if ( const QgsLineString *lineString = dynamic_cast<const QgsLineString *>( curve ) )
1028 {
1029 // If it's a linestring, we split and insert a curve
1030
1032 lineString->points( points );
1033
1034 const QgsPointSequence partA = points.mid( 0, subVertexId.vertex );
1035 const QgsPointSequence partB = QgsPointSequence() << points[subVertexId.vertex - 1] << points[subVertexId.vertex] << points[subVertexId.vertex + 1];
1036 const QgsPointSequence partC = points.mid( subVertexId.vertex + 1 );
1037
1038 std::unique_ptr<QgsLineString> curveA = std::make_unique<QgsLineString>();
1039 curveA->setPoints( partA );
1040 std::unique_ptr<QgsCircularString> curveB = std::make_unique<QgsCircularString>();
1041 curveB->setPoints( partB );
1042 std::unique_ptr<QgsLineString> curveC = std::make_unique<QgsLineString>();
1043 curveC->setPoints( partC );
1044
1045 removeCurve( curveId );
1046 if ( subVertexId.vertex < points.length() - 2 )
1047 mCurves.insert( curveId, curveC.release() );
1048 mCurves.insert( curveId, curveB.release() );
1049 if ( subVertexId.vertex > 1 )
1050 mCurves.insert( curveId, curveA.release() );
1051 }
1052
1053 // We merge consecutive LineStrings
1055
1056 clearCache();
1057 return true;
1058}
1059
1060
1061double QgsCompoundCurve::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1062{
1063 return QgsGeometryUtils::closestSegmentFromComponents( mCurves, QgsGeometryUtils::Vertex, pt, segmentPt, vertexAfter, leftOf, epsilon );
1064}
1065
1066bool QgsCompoundCurve::pointAt( int node, QgsPoint &point, Qgis::VertexType &type ) const
1067{
1068 int currentVertexId = 0;
1069 for ( int j = 0; j < mCurves.size(); ++j )
1070 {
1071 int nCurvePoints = mCurves.at( j )->numPoints();
1072 if ( ( node - currentVertexId ) < nCurvePoints )
1073 {
1074 return ( mCurves.at( j )->pointAt( node - currentVertexId, point, type ) );
1075 }
1076 currentVertexId += ( nCurvePoints - 1 );
1077 }
1078 return false;
1079}
1080
1081double QgsCompoundCurve::xAt( int index ) const
1082{
1083 int currentVertexId = 0;
1084 for ( int j = 0; j < mCurves.size(); ++j )
1085 {
1086 int nCurvePoints = mCurves.at( j )->numPoints();
1087 if ( ( index - currentVertexId ) < nCurvePoints )
1088 {
1089 return mCurves.at( j )->xAt( index - currentVertexId );
1090 }
1091 currentVertexId += ( nCurvePoints - 1 );
1092 }
1093 return 0.0;
1094}
1095
1096double QgsCompoundCurve::yAt( int index ) const
1097{
1098 int currentVertexId = 0;
1099 for ( int j = 0; j < mCurves.size(); ++j )
1100 {
1101 int nCurvePoints = mCurves.at( j )->numPoints();
1102 if ( ( index - currentVertexId ) < nCurvePoints )
1103 {
1104 return mCurves.at( j )->yAt( index - currentVertexId );
1105 }
1106 currentVertexId += ( nCurvePoints - 1 );
1107 }
1108 return 0.0;
1109}
1110
1111double QgsCompoundCurve::zAt( int index ) const
1112{
1113 int currentVertexId = 0;
1114 for ( int j = 0; j < mCurves.size(); ++j )
1115 {
1116 int nCurvePoints = mCurves.at( j )->numPoints();
1117 if ( ( index - currentVertexId ) < nCurvePoints )
1118 {
1119 return mCurves.at( j )->zAt( index - currentVertexId );
1120 }
1121 currentVertexId += ( nCurvePoints - 1 );
1122 }
1123 return 0.0;
1124}
1125
1126double QgsCompoundCurve::mAt( int index ) const
1127{
1128 int currentVertexId = 0;
1129 for ( int j = 0; j < mCurves.size(); ++j )
1130 {
1131 int nCurvePoints = mCurves.at( j )->numPoints();
1132 if ( ( index - currentVertexId ) < nCurvePoints )
1133 {
1134 return mCurves.at( j )->mAt( index - currentVertexId );
1135 }
1136 currentVertexId += ( nCurvePoints - 1 );
1137 }
1138 return 0.0;
1139}
1140
1142{
1143 bool res = true;
1144 for ( QgsCurve *curve : std::as_const( mCurves ) )
1145 {
1146 if ( !curve->transform( transformer ) )
1147 {
1148 res = false;
1149 break;
1150 }
1151
1152 if ( feedback && feedback->isCanceled() )
1153 {
1154 res = false;
1155 break;
1156 }
1157 }
1158 clearCache();
1159 return res;
1160}
1161
1162void QgsCompoundCurve::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1163{
1164 for ( QgsCurve *curve : std::as_const( mCurves ) )
1165 {
1166 curve->filterVertices( filter );
1167 }
1168 clearCache();
1169}
1170
1171void QgsCompoundCurve::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1172{
1173 for ( QgsCurve *curve : std::as_const( mCurves ) )
1174 {
1175 curve->transformVertices( transform );
1176 }
1177 clearCache();
1178}
1179
1180std::tuple<std::unique_ptr<QgsCurve>, std::unique_ptr<QgsCurve> > QgsCompoundCurve::splitCurveAtVertex( int index ) const
1181{
1182 if ( mCurves.empty() )
1183 return std::make_tuple( std::make_unique< QgsCompoundCurve >(), std::make_unique< QgsCompoundCurve >() );
1184
1185 int curveStart = 0;
1186
1187 std::unique_ptr< QgsCompoundCurve > curve1 = std::make_unique< QgsCompoundCurve >();
1188 std::unique_ptr< QgsCompoundCurve > curve2;
1189
1190 for ( const QgsCurve *curve : mCurves )
1191 {
1192 const int curveSize = curve->numPoints();
1193 if ( !curve2 && index < curveStart + curveSize )
1194 {
1195 // split the curve
1196 auto [ p1, p2 ] = curve->splitCurveAtVertex( index - curveStart );
1197 if ( !p1->isEmpty() )
1198 curve1->addCurve( p1.release() );
1199
1200 curve2 = std::make_unique< QgsCompoundCurve >();
1201 if ( !p2->isEmpty() )
1202 curve2->addCurve( p2.release() );
1203 }
1204 else
1205 {
1206 if ( curve2 )
1207 curve2->addCurve( curve->clone() );
1208 else
1209 curve1->addCurve( curve->clone() );
1210 }
1211
1212 // subtract 1 here, because the next curve will start with the same
1213 // vertex as this curve ended at
1214 curveStart += curve->numPoints() - 1;
1215 }
1216
1217 return std::make_tuple( std::move( curve1 ), curve2 ? std::move( curve2 ) : std::make_unique< QgsCompoundCurve >() );
1218}
1219
1220void QgsCompoundCurve::sumUpArea( double &sum ) const
1221{
1223 {
1224 sum += mSummedUpArea;
1225 return;
1226 }
1227
1228 mSummedUpArea = 0;
1229 for ( const QgsCurve *curve : mCurves )
1230 {
1231 curve->sumUpArea( mSummedUpArea );
1232 }
1234 sum += mSummedUpArea;
1235}
1236
1238{
1239 if ( numPoints() < 1 || isClosed() )
1240 {
1241 return;
1242 }
1243 addVertex( startPoint() );
1244}
1245
1247{
1248 for ( const QgsCurve *curve : mCurves )
1249 {
1250 if ( curve->hasCurvedSegments() )
1251 {
1252 return true;
1253 }
1254 }
1255 return false;
1256}
1257
1259{
1260 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( vertex );
1261 if ( curveIds.size() == 1 )
1262 {
1263 QgsCurve *curve = mCurves[curveIds.at( 0 ).first];
1264 return curve->vertexAngle( curveIds.at( 0 ).second );
1265 }
1266 else if ( curveIds.size() > 1 )
1267 {
1268 QgsCurve *curve1 = mCurves[curveIds.at( 0 ).first];
1269 QgsCurve *curve2 = mCurves[curveIds.at( 1 ).first];
1270 double angle1 = curve1->vertexAngle( curveIds.at( 0 ).second );
1271 double angle2 = curve2->vertexAngle( curveIds.at( 1 ).second );
1272 return QgsGeometryUtils::averageAngle( angle1, angle2 );
1273 }
1274 else
1275 {
1276 return 0.0;
1277 }
1278}
1279
1281{
1282 QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( startVertex );
1283 double length = 0.0;
1284 for ( auto it = curveIds.constBegin(); it != curveIds.constEnd(); ++it )
1285 {
1286 length += mCurves.at( it->first )->segmentLength( it->second );
1287 }
1288 return length;
1289}
1290
1292{
1294 for ( int i = mCurves.count() - 1; i >= 0; --i )
1295 {
1296 QgsCurve *reversedCurve = mCurves.at( i )->reversed();
1297 clone->addCurve( reversedCurve );
1298 }
1299 return clone;
1300}
1301
1302QgsPoint *QgsCompoundCurve::interpolatePoint( const double distance ) const
1303{
1304 if ( distance < 0 )
1305 return nullptr;
1306
1307 double distanceTraversed = 0;
1308 for ( const QgsCurve *curve : mCurves )
1309 {
1310 const double thisCurveLength = curve->length();
1311 if ( distanceTraversed + thisCurveLength > distance || qgsDoubleNear( distanceTraversed + thisCurveLength, distance ) )
1312 {
1313 // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1314 const double distanceToPoint = std::min( distance - distanceTraversed, thisCurveLength );
1315
1316 // point falls on this curve
1317 return curve->interpolatePoint( distanceToPoint );
1318 }
1319
1320 distanceTraversed += thisCurveLength;
1321 }
1322
1323 return nullptr;
1324}
1325
1326QgsCompoundCurve *QgsCompoundCurve::curveSubstring( double startDistance, double endDistance ) const
1327{
1328 if ( startDistance < 0 && endDistance < 0 )
1329 return createEmptyWithSameType();
1330
1331 endDistance = std::max( startDistance, endDistance );
1332 std::unique_ptr< QgsCompoundCurve > substring = std::make_unique< QgsCompoundCurve >();
1333
1334 double distanceTraversed = 0;
1335 for ( const QgsCurve *curve : mCurves )
1336 {
1337 const double thisCurveLength = curve->length();
1338 if ( distanceTraversed + thisCurveLength < startDistance )
1339 {
1340 // keep going - haven't found start yet, so no need to include this curve at all
1341 }
1342 else
1343 {
1344 std::unique_ptr< QgsCurve > part( curve->curveSubstring( startDistance - distanceTraversed, endDistance - distanceTraversed ) );
1345 if ( part )
1346 substring->addCurve( part.release() );
1347 }
1348
1349 distanceTraversed += thisCurveLength;
1350 if ( distanceTraversed > endDistance )
1351 break;
1352 }
1353
1354 return substring.release();
1355}
1356
1357bool QgsCompoundCurve::addZValue( double zValue )
1358{
1359 if ( QgsWkbTypes::hasZ( mWkbType ) )
1360 return false;
1361
1363
1364 for ( QgsCurve *curve : std::as_const( mCurves ) )
1365 {
1366 curve->addZValue( zValue );
1367 }
1368 clearCache();
1369 return true;
1370}
1371
1372bool QgsCompoundCurve::addMValue( double mValue )
1373{
1374 if ( QgsWkbTypes::hasM( mWkbType ) )
1375 return false;
1376
1378
1379 for ( QgsCurve *curve : std::as_const( mCurves ) )
1380 {
1381 curve->addMValue( mValue );
1382 }
1383 clearCache();
1384 return true;
1385}
1386
1388{
1389 if ( !QgsWkbTypes::hasZ( mWkbType ) )
1390 return false;
1391
1393 for ( QgsCurve *curve : std::as_const( mCurves ) )
1394 {
1395 curve->dropZValue();
1396 }
1397 clearCache();
1398 return true;
1399}
1400
1402{
1403 if ( !QgsWkbTypes::hasM( mWkbType ) )
1404 return false;
1405
1407 for ( QgsCurve *curve : std::as_const( mCurves ) )
1408 {
1409 curve->dropMValue();
1410 }
1411 clearCache();
1412 return true;
1413}
1414
1416{
1417 for ( QgsCurve *curve : std::as_const( mCurves ) )
1418 {
1419 curve->swapXy();
1420 }
1421 clearCache();
1422}
VertexType
Types of vertex.
Definition qgis.h:2169
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:1883
An abstract base class for classes which transform geometries by transforming input points to output ...
Abstract base class for all geometries.
virtual bool fromWkb(QgsConstWkbPtr &wkb)=0
Sets the geometry from a WKB string.
virtual bool moveVertex(QgsVertexId position, const QgsPoint &newPos)=0
Moves a vertex within the geometry.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
bool isMeasure() const
Returns true if the geometry contains m values.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
AxisOrder
Axis order for GML generation.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
QgsAbstractGeometry & operator=(const QgsAbstractGeometry &geom)
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, Qgis::WkbType baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
virtual bool boundingBoxIntersects(const QgsRectangle &rectangle) const
Returns true if the bounding box of this geometry intersects with a rectangle.
virtual bool deleteVertex(QgsVertexId position)=0
Deletes a vertex within the geometry.
virtual int compareTo(const QgsAbstractGeometry *other) const
Comparator for sorting of geometry.
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
static endian_t endian()
Returns whether this machine uses big or little endian.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:44
bool intersects(const QgsBox3D &other) const
Returns true if box intersects with another box.
Definition qgsbox3d.cpp:132
void combineWith(const QgsBox3D &box)
Expands the bbox so that it covers both the original rectangle and the given rectangle.
Definition qgsbox3d.cpp:196
bool isNull() const
Test if the box is null (all coordinates NaN or after call to setMinimal()).
Definition qgsbox3d.cpp:289
Circular string geometry type.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
Compound curve geometry type.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a new line string geometry corresponding to a segmentized approximation of the curve.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
int compareToSameClass(const QgsAbstractGeometry *other) const final
Compares to an other geometry of the same class, and returns a integer for sorting of the two geometr...
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
QgsCompoundCurve * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
void condenseCurves()
Condenses the curves in this geometry by combining adjacent linestrings a to a single continuous line...
void close()
Appends first point if not already closed.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
int dimension() const override
Returns the inherent dimension of the geometry.
void filterVertices(const std::function< bool(const QgsPoint &) > &filter) override
Filters the vertices from the geometry in place, removing any which do not return true for the filter...
std::tuple< std::unique_ptr< QgsCurve >, std::unique_ptr< QgsCurve > > splitCurveAtVertex(int index) const final
Splits the curve at the specified vertex index, returning two curves which represent the portion of t...
bool boundingBoxIntersects(const QgsBox3D &box3d) const override
Returns true if the bounding box of this geometry intersects with a box3d.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
QString geometryType() const override
Returns a unique string representing the geometry type.
double mAt(int index) const override
Returns the m-coordinate of the specified node in the line string.
bool isEmpty() const override
Returns true if the geometry is empty.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
void transformVertices(const std::function< QgsPoint(const QgsPoint &) > &transform) override
Transforms the vertices from the geometry in place, applying the transform function to every vertex.
int nCurves() const
Returns the number of curves in the geometry.
bool toggleCircularAtVertex(QgsVertexId position)
Converts the vertex at the given position from/to circular.
QgsBox3D calculateBoundingBox3D() const override
Calculates the minimal 3D bounding box for the geometry.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
const QgsAbstractGeometry * simplifiedTypeRef() const override
Returns a reference to the simplest lossless representation of this geometry, e.g.
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb()
void swapXy() override
Swaps the x and y coordinates from the geometry.
void removeCurve(int i)
Removes a curve from the geometry.
void addCurve(QgsCurve *c, bool extendPrevious=false)
Adds a curve to the geometry (takes ownership).
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *std::numeric_limits< double >::epsilon()) const override
Searches for the closest segment of the geometry to a given point.
QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML2 representation of the geometry.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
bool dropMValue() override
Drops any measure values which exist in the geometry.
QgsCompoundCurve & operator=(const QgsCompoundCurve &curve)
QgsCompoundCurve * clone() const override
Clones the geometry by performing a deep copy.
const QgsCurve * curveAt(int i) const
Returns the curve at the specified index.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
~QgsCompoundCurve() override
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
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 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.
QgsCompoundCurve * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
void scroll(int firstVertexIndex) final
Scrolls the curve vertices so that they start with the vertex at the given index.
double length() const override
Returns the planar, 2-dimensional length of the geometry.
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
QgsCompoundCurve * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
bool pointAt(int node, QgsPoint &point, Qgis::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
QgsPoint startPoint() const override
Returns the starting point of the curve.
QgsCompoundCurve * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid.
QgsPoint endPoint() const override
Returns the end point of the curve.
int numPoints() const override
Returns the number of points in the curve.
double zAt(int index) const override
Returns the z-coordinate of the specified node in the line string.
void addVertex(const QgsPoint &pt)
Adds a vertex to the end of the geometry.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML3 representation of the geometry.
A const WKB pointer.
Definition qgswkbptr.h:138
Qgis::WkbType readHeader() const
readHeader
Definition qgswkbptr.cpp:55
Class for doing transforms between two map coordinate systems.
Abstract base class for curved geometry type.
Definition qgscurve.h: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 pointAt(int node, QgsPoint &point, Qgis::VertexType &type) const =0
Returns the point and vertex id of a point within the curve.
virtual bool isClosed() const
Returns true if the curve is closed.
Definition qgscurve.cpp:53
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
Definition qgscurve.cpp:247
QgsBox3D mBoundingBox
Cached bounding box.
Definition qgscurve.h:358
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: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
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 closestSegmentFromComponents(T &container, ComponentType ctype, const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon)
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,...
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:526
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
Definition qgspoint.cpp:131
WKB pointer handler.
Definition qgswkbptr.h:44
static Qgis::WkbType dropM(Qgis::WkbType type)
Drops the m dimension (if present) for a WKB type and returns the new type.
static Qgis::WkbType dropZ(Qgis::WkbType type)
Drops the z dimension (if present) for a WKB type and returns the new type.
static Qgis::WkbType addM(Qgis::WkbType type)
Adds the m dimension to a WKB type and returns the new type.
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:4260
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