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