QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 
43 bool 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 
165 void QgsCompoundCurve::scroll( int index )
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 );
209  if ( QgsWkbTypes::flatType( curveType ) == QgsWkbTypes::LineString )
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 
227 bool 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 
285 int 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 
295 QByteArray 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 
310 QString QgsCompoundCurve::asWkt( int precision ) const
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 
337 QDomElement 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 
345 QDomElement 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 
446 bool 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 
462 int 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 
489 QgsCompoundCurve *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 
508 bool 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..
563  return QgsAbstractGeometry::boundingBoxIntersects( rectangle );
564 }
565 
567 {
568  if ( mCurves.size() == 1 )
569  return mCurves.at( 0 );
570  else
571  return this;
572 }
573 
574 const QgsCurve *QgsCompoundCurve::curveAt( int i ) const
575 {
576  if ( i < 0 || i >= mCurves.size() )
577  {
578  return nullptr;
579  }
580  return mCurves.at( i );
581 }
582 
583 void 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 
701 void 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 
718 void 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 
727 void 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 
742 void 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 
756 bool QgsCompoundCurve::insertVertex( QgsVertexId position, const QgsPoint &vertex )
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 
777 bool 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 
888 QVector< 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
1013  condenseCurves();
1014 
1015  clearCache();
1016  return true;
1017 }
1018 
1019 
1020 double 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 
1025 bool 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 
1040 double 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 
1055 double 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 
1091 void 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 
1100 void 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 
1109 std::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 
1149 void QgsCompoundCurve::sumUpArea( double &sum ) const
1150 {
1151  for ( const QgsCurve *curve : mCurves )
1152  {
1153  curve->sumUpArea( sum );
1154  }
1155 }
1156 
1158 {
1159  if ( numPoints() < 1 || isClosed() )
1160  {
1161  return;
1162  }
1163  addVertex( startPoint() );
1164 }
1165 
1167 {
1168  for ( const QgsCurve *curve : mCurves )
1169  {
1170  if ( curve->hasCurvedSegments() )
1171  {
1172  return true;
1173  }
1174  }
1175  return false;
1176 }
1177 
1179 {
1180  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( vertex );
1181  if ( curveIds.size() == 1 )
1182  {
1183  QgsCurve *curve = mCurves[curveIds.at( 0 ).first];
1184  return curve->vertexAngle( curveIds.at( 0 ).second );
1185  }
1186  else if ( curveIds.size() > 1 )
1187  {
1188  QgsCurve *curve1 = mCurves[curveIds.at( 0 ).first];
1189  QgsCurve *curve2 = mCurves[curveIds.at( 1 ).first];
1190  double angle1 = curve1->vertexAngle( curveIds.at( 0 ).second );
1191  double angle2 = curve2->vertexAngle( curveIds.at( 1 ).second );
1192  return QgsGeometryUtils::averageAngle( angle1, angle2 );
1193  }
1194  else
1195  {
1196  return 0.0;
1197  }
1198 }
1199 
1201 {
1202  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( startVertex );
1203  double length = 0.0;
1204  for ( auto it = curveIds.constBegin(); it != curveIds.constEnd(); ++it )
1205  {
1206  length += mCurves.at( it->first )->segmentLength( it->second );
1207  }
1208  return length;
1209 }
1210 
1212 {
1214  for ( int i = mCurves.count() - 1; i >= 0; --i )
1215  {
1216  QgsCurve *reversedCurve = mCurves.at( i )->reversed();
1217  clone->addCurve( reversedCurve );
1218  }
1219  return clone;
1220 }
1221 
1222 QgsPoint *QgsCompoundCurve::interpolatePoint( const double distance ) const
1223 {
1224  if ( distance < 0 )
1225  return nullptr;
1226 
1227  double distanceTraversed = 0;
1228  for ( const QgsCurve *curve : mCurves )
1229  {
1230  const double thisCurveLength = curve->length();
1231  if ( distanceTraversed + thisCurveLength > distance || qgsDoubleNear( distanceTraversed + thisCurveLength, distance ) )
1232  {
1233  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1234  const double distanceToPoint = std::min( distance - distanceTraversed, thisCurveLength );
1235 
1236  // point falls on this curve
1237  return curve->interpolatePoint( distanceToPoint );
1238  }
1239 
1240  distanceTraversed += thisCurveLength;
1241  }
1242 
1243  return nullptr;
1244 }
1245 
1246 QgsCompoundCurve *QgsCompoundCurve::curveSubstring( double startDistance, double endDistance ) const
1247 {
1248  if ( startDistance < 0 && endDistance < 0 )
1249  return createEmptyWithSameType();
1250 
1251  endDistance = std::max( startDistance, endDistance );
1252  std::unique_ptr< QgsCompoundCurve > substring = std::make_unique< QgsCompoundCurve >();
1253 
1254  double distanceTraversed = 0;
1255  for ( const QgsCurve *curve : mCurves )
1256  {
1257  const double thisCurveLength = curve->length();
1258  if ( distanceTraversed + thisCurveLength < startDistance )
1259  {
1260  // keep going - haven't found start yet, so no need to include this curve at all
1261  }
1262  else
1263  {
1264  std::unique_ptr< QgsCurve > part( curve->curveSubstring( startDistance - distanceTraversed, endDistance - distanceTraversed ) );
1265  if ( part )
1266  substring->addCurve( part.release() );
1267  }
1268 
1269  distanceTraversed += thisCurveLength;
1270  if ( distanceTraversed > endDistance )
1271  break;
1272  }
1273 
1274  return substring.release();
1275 }
1276 
1277 bool QgsCompoundCurve::addZValue( double zValue )
1278 {
1279  if ( QgsWkbTypes::hasZ( mWkbType ) )
1280  return false;
1281 
1283 
1284  for ( QgsCurve *curve : std::as_const( mCurves ) )
1285  {
1286  curve->addZValue( zValue );
1287  }
1288  clearCache();
1289  return true;
1290 }
1291 
1292 bool QgsCompoundCurve::addMValue( double mValue )
1293 {
1294  if ( QgsWkbTypes::hasM( mWkbType ) )
1295  return false;
1296 
1298 
1299  for ( QgsCurve *curve : std::as_const( mCurves ) )
1300  {
1301  curve->addMValue( mValue );
1302  }
1303  clearCache();
1304  return true;
1305 }
1306 
1308 {
1309  if ( !QgsWkbTypes::hasZ( mWkbType ) )
1310  return false;
1311 
1313  for ( QgsCurve *curve : std::as_const( mCurves ) )
1314  {
1315  curve->dropZValue();
1316  }
1317  clearCache();
1318  return true;
1319 }
1320 
1322 {
1323  if ( !QgsWkbTypes::hasM( mWkbType ) )
1324  return false;
1325 
1327  for ( QgsCurve *curve : std::as_const( mCurves ) )
1328  {
1329  curve->dropMValue();
1330  }
1331  clearCache();
1332  return true;
1333 }
1334 
1336 {
1337  for ( QgsCurve *curve : std::as_const( mCurves ) )
1338  {
1339  curve->swapXy();
1340  }
1341  clearCache();
1342 }
QgsRectangle::intersects
bool intersects(const QgsRectangle &rect) const SIP_HOLDGIL
Returns true when rectangle intersects with other rectangle.
Definition: qgsrectangle.h:349
QgsCurve
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
QgsCompoundCurve::toggleCircularAtVertex
bool toggleCircularAtVertex(QgsVertexId position)
Converts the vertex at the given position from/to circular.
Definition: qgscompoundcurve.cpp:927
QgsVertexId::part
int part
Part number.
Definition: qgsvertexid.h:89
QgsVertexId::vertex
int vertex
Vertex number.
Definition: qgsvertexid.h:95
QgsCompoundCurve::length
double length() const override SIP_HOLDGIL
Returns the planar, 2-dimensional length of the geometry.
Definition: qgscompoundcurve.cpp:370
QgsCompoundCurve::addMValue
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition: qgscompoundcurve.cpp:1292
QgsWkbTypes::dropM
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
QgsCompoundCurve::splitCurveAtVertex
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...
Definition: qgscompoundcurve.cpp:1109
QgsCompoundCurve::addZValue
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition: qgscompoundcurve.cpp:1277
QgsRectangle::combineExtentWith
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:391
QgsCompoundCurve::wkbSize
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb()
Definition: qgscompoundcurve.cpp:285
QgsCompoundCurve::geometryType
QString geometryType() const override SIP_HOLDGIL
Returns a unique string representing the geometry type.
Definition: qgscompoundcurve.cpp:102
qgslinestring.h
QgsGeometryUtils::Vertex
@ Vertex
Definition: qgsgeometryutils.h:999
QgsPoint::vertexAt
QgsPoint vertexAt(QgsVertexId) const override
Returns the point corresponding to a specified vertex id.
Definition: qgspoint.cpp:525
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:48
qgswkbptr.h
QgsCompoundCurve::hasCurvedSegments
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
Definition: qgscompoundcurve.cpp:1166
QgsCompoundCurve::calculateBoundingBox
QgsRectangle calculateBoundingBox() const override
Default calculator for the minimal bounding box for the geometry.
Definition: qgscompoundcurve.cpp:149
QgsCompoundCurve::scroll
void scroll(int firstVertexIndex) final
Scrolls the curve vertices so that they start with the vertex at the given index.
Definition: qgscompoundcurve.cpp:165
QgsCompoundCurve::isEmpty
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgscompoundcurve.cpp:433
QgsCompoundCurve::nCurves
int nCurves() const SIP_HOLDGIL
Returns the number of curves in the geometry.
Definition: qgscompoundcurve.h:82
qgscompoundcurve.h
QgsCompoundCurve::dropZValue
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Definition: qgscompoundcurve.cpp:1307
QgsWkbTypes::flatType
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:732
QgsAbstractGeometry::parts
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
Definition: qgsabstractgeometry.cpp:308
QgsCompoundCurve::compareToSameClass
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...
Definition: qgscompoundcurve.cpp:71
QgsWkbTypes::LineString
@ LineString
Definition: qgswkbtypes.h:73
QgsCompoundCurve::dropMValue
bool dropMValue() override
Drops any measure values which exist in the geometry.
Definition: qgscompoundcurve.cpp:1321
QgsCompoundCurve::operator=
QgsCompoundCurve & operator=(const QgsCompoundCurve &curve)
Definition: qgscompoundcurve.cpp:122
QgsWkbTypes::addZ
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1176
QgsCompoundCurve::transformVertices
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.
Definition: qgscompoundcurve.cpp:1100
QgsAbstractGeometry::wktTypeStr
QString wktTypeStr() const
Returns the WKT type string of the geometry.
Definition: qgsabstractgeometry.cpp:181
QgsFeedback::isCanceled
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:67
QgsAbstractGeometry::length
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
Definition: qgsabstractgeometry.cpp:166
QgsCompoundCurve::yAt
double yAt(int index) const override SIP_HOLDGIL
Returns the y-coordinate of the specified node in the line string.
Definition: qgscompoundcurve.cpp:1055
QgsCompoundCurve::curveToLine
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.
Definition: qgscompoundcurve.cpp:477
QgsCompoundCurve::close
void close()
Appends first point if not already closed.
Definition: qgscompoundcurve.cpp:1157
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:69
QgsAbstractGeometry::SegmentationToleranceType
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
Definition: qgsabstractgeometry.h:120
QgsAbstractGeometry::mWkbType
QgsWkbTypes::Type mWkbType
Definition: qgsabstractgeometry.h:1119
QgsCompoundCurve::numPoints
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
Definition: qgscompoundcurve.cpp:416
QgsCompoundCurve::sumUpArea
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
Definition: qgscompoundcurve.cpp:1149
QgsCompoundCurve::createEmptyWithSameType
QgsCompoundCurve * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
Definition: qgscompoundcurve.cpp:64
QgsCompoundCurve::insertVertex
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
Definition: qgscompoundcurve.cpp:756
QgsCompoundCurve::asJsonObject
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
Definition: qgscompoundcurve.cpp:363
QgsLineString
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
QgsAbstractGeometry::compareTo
virtual int compareTo(const QgsAbstractGeometry *other) const
Comparator for sorting of geometry.
Definition: qgsabstractgeometry.cpp:44
QgsCompoundCurve::xAt
double xAt(int index) const override SIP_HOLDGIL
Returns the x-coordinate of the specified node in the line string.
Definition: qgscompoundcurve.cpp:1040
QgsCompoundCurve::fromWkt
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
Definition: qgscompoundcurve.cpp:227
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:41
QgsCompoundCurve::curveAt
const QgsCurve * curveAt(int i) const SIP_HOLDGIL
Returns the curve at the specified index.
Definition: qgscompoundcurve.cpp:574
QgsCompoundCurve::startPoint
QgsPoint startPoint() const override SIP_HOLDGIL
Returns the starting point of the curve.
Definition: qgscompoundcurve.cpp:380
QgsCurve::clearCache
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgscurve.cpp:293
QgsCompoundCurve::fromWkb
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
Definition: qgscompoundcurve.cpp:187
QgsCompoundCurve::simplifiedTypeRef
const QgsAbstractGeometry * simplifiedTypeRef() const override SIP_HOLDGIL
Returns a reference to the simplest lossless representation of this geometry, e.g.
Definition: qgscompoundcurve.cpp:566
qgsapplication.h
QgsAbstractGeometry::isMeasure
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
Definition: qgsabstractgeometry.h:228
QgsCompoundCurve::segmentLength
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
Definition: qgscompoundcurve.cpp:1200
MathUtils::leftOf
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
QgsAbstractGeometry::vertexAngle
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
QgsWkbTypes::addM
static Type addM(Type type) SIP_HOLDGIL
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1201
precision
int precision
Definition: qgswfsgetfeature.cpp:103
QgsGeometryUtils::closestSegmentFromComponents
static double closestSegmentFromComponents(T &container, ComponentType ctype, const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon)
Definition: qgsgeometryutils.h:1005
QgsCircularString
Circular string geometry type.
Definition: qgscircularstring.h:34
QgsAbstractGeometry::wkbType
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
Definition: qgsabstractgeometry.h:206
QgsCurve::endPoint
virtual QgsPoint endPoint() const =0
Returns the end point of the curve.
QgsWkbTypes::Unknown
@ Unknown
Definition: qgswkbtypes.h:71
QgsAbstractGeometry::AxisOrder
AxisOrder
Axis order for GML generation.
Definition: qgsabstractgeometry.h:138
QgsLineString::insertVertex
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
Definition: qgslinestring.cpp:1706
QgsCompoundCurve::endPoint
QgsPoint endPoint() const override SIP_HOLDGIL
Returns the end point of the curve.
Definition: qgscompoundcurve.cpp:389
QgsCompoundCurve::addVertex
void addVertex(const QgsPoint &pt)
Adds a vertex to the end of the geometry.
Definition: qgscompoundcurve.cpp:639
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2265
QgsConstWkbPtr
A const WKB pointer.
Definition: qgswkbptr.h:137
QgsFeedback
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:44
QgsCompoundCurve::isValid
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: qgscompoundcurve.cpp:446
QgsLineString::setPoints
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.
Definition: qgslinestring.cpp:1092
QgsGeometryUtils::averageAngle
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,...
Definition: qgsgeometryutils.cpp:1662
QgsCompoundCurve::points
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
Definition: qgscompoundcurve.cpp:398
QgsWkbTypes::hasM
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1130
QgsCompoundCurve::closestSegment
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.
Definition: qgscompoundcurve.cpp:1020
QgsApplication::endian
static endian_t endian()
Returns whether this machine uses big or little endian.
Definition: qgsapplication.cpp:1404
QgsCompoundCurve::asGml3
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.
Definition: qgscompoundcurve.cpp:345
QgsCompoundCurve::condenseCurves
void condenseCurves()
Condenses the curves in this geometry by combining adjacent linestrings a to a single continuous line...
Definition: qgscompoundcurve.cpp:672
QgsCompoundCurve::asWkt
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
Definition: qgscompoundcurve.cpp:310
QgsWkbTypes::dropZ
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
QgsWkbPtr
WKB pointer handler.
Definition: qgswkbptr.h:43
QgsCurve::isValid
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
qgscircularstring.h
QgsAbstractGeometry::fromWkb
virtual bool fromWkb(QgsConstWkbPtr &wkb)=0
Sets the geometry from a WKB string.
QgsAbstractGeometry
Abstract base class for all geometries.
Definition: qgsabstractgeometry.h:79
QgsCompoundCurve::clear
void clear() override
Clears the geometry, ie reset it to a null geometry.
Definition: qgscompoundcurve.cpp:141
qgsgeometryutils.h
QgsAbstractGeometry::is3D
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
Definition: qgsabstractgeometry.h:219
QgsPoint::removeDuplicateNodes
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
QgsCompoundCurve::~QgsCompoundCurve
~QgsCompoundCurve() override
Definition: qgscompoundcurve.cpp:38
QgsGeometryUtils::wktGetChildBlocks
static QStringList wktGetChildBlocks(const QString &wkt, const QString &defaultType=QString())
Parses a WKT string and returns of list of blocks contained in the WKT.
Definition: qgsgeometryutils.cpp:1383
Qgis::VertexType
VertexType
Types of vertex.
Definition: qgis.h:1356
QgsCompoundCurve::moveVertex
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
Definition: qgscompoundcurve.cpp:777
QgsWkbTypes::CircularString
@ CircularString
Definition: qgswkbtypes.h:80
QgsCompoundCurve::QgsCompoundCurve
QgsCompoundCurve()
Definition: qgscompoundcurve.cpp:33
QgsCompoundCurve::clone
QgsCompoundCurve * clone() const override
Clones the geometry by performing a deep copy.
Definition: qgscompoundcurve.cpp:136
QgsCompoundCurve::addToPainterPath
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
Definition: qgscompoundcurve.cpp:727
QgsLineString::append
void append(const QgsLineString *line)
Appends the contents of another line string to the end of this line string.
Definition: qgslinestring.cpp:1221
QgsCompoundCurve::equals
bool equals(const QgsCurve &other) const override
Checks whether this curve exactly equals another curve.
Definition: qgscompoundcurve.cpp:43
QgsAbstractGeometry::operator=
QgsAbstractGeometry & operator=(const QgsAbstractGeometry &geom)
Definition: qgsabstractgeometry.cpp:34
QgsPointSequence
QVector< QgsPoint > QgsPointSequence
Definition: qgsabstractgeometry.h:52
c
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
Definition: porting_processing.dox:1
QgsCompoundCurve::curveSubstring
QgsCompoundCurve * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
Definition: qgscompoundcurve.cpp:1246
QgsCompoundCurve::indexOf
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...
Definition: qgscompoundcurve.cpp:462
QgsLineString::addVertex
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
Definition: qgslinestring.cpp:1785
QgsCompoundCurve::addCurve
void addCurve(QgsCurve *c, bool extendPrevious=false)
Adds a curve to the geometry (takes ownership).
Definition: qgscompoundcurve.cpp:583
QgsVertexId
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:30
QgsAbstractGeometryTransformer
An abstract base class for classes which transform geometries by transforming input points to output ...
Definition: qgsgeometrytransformer.h:32
QgsVertexId::ring
int ring
Ring number.
Definition: qgsvertexid.h:92
QgsCompoundCurve::removeDuplicateNodes
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: qgscompoundcurve.cpp:508
QgsWkbTypes::hasZ
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1080
QgsCompoundCurve::vertexAngle
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
Definition: qgscompoundcurve.cpp:1178
QgsCompoundCurve::dimension
int dimension() const override SIP_HOLDGIL
Returns the inherent dimension of the geometry.
Definition: qgscompoundcurve.cpp:107
QgsCompoundCurve::asWkb
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Definition: qgscompoundcurve.cpp:295
QgsCurve::numPoints
virtual int numPoints() const =0
Returns the number of points in the curve.
Qgis::TransformDirection
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition: qgis.h:1235
QgsConstWkbPtr::readHeader
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
QgsCurve::mBoundingBox
QgsRectangle mBoundingBox
Cached bounding box.
Definition: qgscurve.h:342
QgsAbstractGeometry::boundingBoxIntersects
virtual bool boundingBoxIntersects(const QgsRectangle &rectangle) const SIP_HOLDGIL
Returns true if the bounding box of this geometry intersects with a rectangle.
Definition: qgsabstractgeometry.cpp:390
QgsGeometryUtils::wktReadBlock
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 (...
Definition: qgsgeometryutils.cpp:1351
QgsCompoundCurve::reversed
QgsCompoundCurve * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
Definition: qgscompoundcurve.cpp:1211
QgsAbstractGeometry::setZMTypeFromSubGeometry
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
Definition: qgsabstractgeometry.cpp:77
QgsCompoundCurve::snappedToGrid
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.
Definition: qgscompoundcurve.cpp:489
QgsCompoundCurve::deleteVertex
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
Definition: qgscompoundcurve.cpp:794
QgsCurve::isClosed
virtual bool isClosed() const SIP_HOLDGIL
Returns true if the curve is closed.
Definition: qgscurve.cpp:53
QgsCoordinateTransform
Class for doing transforms between two map coordinate systems.
Definition: qgscoordinatetransform.h:57
qgsfeedback.h
QgsCompoundCurve::swapXy
void swapXy() override
Swaps the x and y coordinates from the geometry.
Definition: qgscompoundcurve.cpp:1335
QgsCompoundCurve::interpolatePoint
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
Definition: qgscompoundcurve.cpp:1222
QgsCompoundCurve::draw
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
Definition: qgscompoundcurve.cpp:701
QgsCircularString::setPoints
void setPoints(const QgsPointSequence &points)
Sets the circular string's points.
Definition: qgscircularstring.cpp:982
QgsRectangle::isNull
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:479
QgsCompoundCurve::boundingBoxIntersects
bool boundingBoxIntersects(const QgsRectangle &rectangle) const override SIP_HOLDGIL
Returns true if the bounding box of this geometry intersects with a rectangle.
Definition: qgscompoundcurve.cpp:537
QgsCompoundCurve::drawAsPolygon
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
Definition: qgscompoundcurve.cpp:742
QgsCompoundCurve
Compound curve geometry type.
Definition: qgscompoundcurve.h:31
QgsCompoundCurve::pointAt
bool pointAt(int node, QgsPoint &point, Qgis::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
Definition: qgscompoundcurve.cpp:1025
QgsCompoundCurve::asGml2
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.
Definition: qgscompoundcurve.cpp:337
QgsCompoundCurve::filterVertices
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...
Definition: qgscompoundcurve.cpp:1091
QgsCompoundCurve::removeCurve
void removeCurve(int i)
Removes a curve from the geometry.
Definition: qgscompoundcurve.cpp:628
QgsWkbTypes::CompoundCurve
@ CompoundCurve
Definition: qgswkbtypes.h:81
qgsmessagelog.h
QgsCompoundCurve::transform
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.
Definition: qgscompoundcurve.cpp:709