QGIS API Documentation  3.21.0-Master (909859188c)
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  for ( const QgsCurve *curve : mCurves )
731  {
732  curve->addToPainterPath( pp );
733  }
734  path.addPath( pp );
735 }
736 
737 void QgsCompoundCurve::drawAsPolygon( QPainter &p ) const
738 {
739  QPainterPath pp;
740  for ( const QgsCurve *curve : mCurves )
741  {
742  curve->addToPainterPath( pp );
743  }
744  p.drawPath( pp );
745 }
746 
747 bool QgsCompoundCurve::insertVertex( QgsVertexId position, const QgsPoint &vertex )
748 {
749  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
750  if ( curveIds.empty() )
751  {
752  return false;
753  }
754  int curveId = curveIds.at( 0 ).first;
755  if ( curveId >= mCurves.size() )
756  {
757  return false;
758  }
759 
760  bool success = mCurves.at( curveId )->insertVertex( curveIds.at( 0 ).second, vertex );
761  if ( success )
762  {
763  clearCache(); //bbox changed
764  }
765  return success;
766 }
767 
768 bool QgsCompoundCurve::moveVertex( QgsVertexId position, const QgsPoint &newPos )
769 {
770  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
771  QVector< QPair<int, QgsVertexId> >::const_iterator idIt = curveIds.constBegin();
772  for ( ; idIt != curveIds.constEnd(); ++idIt )
773  {
774  mCurves.at( idIt->first )->moveVertex( idIt->second, newPos );
775  }
776 
777  bool success = !curveIds.isEmpty();
778  if ( success )
779  {
780  clearCache(); //bbox changed
781  }
782  return success;
783 }
784 
786 {
787  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
788  if ( curveIds.size() == 1 )
789  {
790  if ( !mCurves.at( curveIds.at( 0 ).first )->deleteVertex( curveIds.at( 0 ).second ) )
791  {
792  clearCache(); //bbox may have changed
793  return false;
794  }
795  if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() == 0 )
796  {
797  removeCurve( curveIds.at( 0 ).first );
798  }
799  }
800  else if ( curveIds.size() == 2 )
801  {
802  Q_ASSERT( curveIds.at( 1 ).first == curveIds.at( 0 ).first + 1 );
803  Q_ASSERT( curveIds.at( 0 ).second.vertex == mCurves.at( curveIds.at( 0 ).first )->numPoints() - 1 );
804  Q_ASSERT( curveIds.at( 1 ).second.vertex == 0 );
805  QgsPoint startPoint = mCurves.at( curveIds.at( 0 ).first ) ->startPoint();
806  QgsPoint endPoint = mCurves.at( curveIds.at( 1 ).first ) ->endPoint();
807  if ( QgsWkbTypes::flatType( mCurves.at( curveIds.at( 0 ).first )->wkbType() ) == QgsWkbTypes::LineString &&
808  QgsWkbTypes::flatType( mCurves.at( curveIds.at( 1 ).first )->wkbType() ) == QgsWkbTypes::CircularString &&
809  mCurves.at( curveIds.at( 1 ).first )->numPoints() > 3 )
810  {
811  QgsPoint intermediatePoint;
813  mCurves.at( curveIds.at( 1 ).first ) ->pointAt( 2, intermediatePoint, type );
814  mCurves.at( curveIds.at( 0 ).first )->moveVertex(
815  QgsVertexId( 0, 0, mCurves.at( curveIds.at( 0 ).first )->numPoints() - 1 ), intermediatePoint );
816  }
817  else if ( !mCurves.at( curveIds.at( 0 ).first )->deleteVertex( curveIds.at( 0 ).second ) )
818  {
819  clearCache(); //bbox may have changed
820  return false;
821  }
822  if ( QgsWkbTypes::flatType( mCurves.at( curveIds.at( 0 ).first )->wkbType() ) == QgsWkbTypes::CircularString &&
823  mCurves.at( curveIds.at( 0 ).first )->numPoints() > 0 &&
824  QgsWkbTypes::flatType( mCurves.at( curveIds.at( 1 ).first )->wkbType() ) == QgsWkbTypes::LineString )
825  {
826  QgsPoint intermediatePoint = mCurves.at( curveIds.at( 0 ).first ) ->endPoint();
827  mCurves.at( curveIds.at( 1 ).first )->moveVertex( QgsVertexId( 0, 0, 0 ), intermediatePoint );
828  }
829  else if ( !mCurves.at( curveIds.at( 1 ).first )->deleteVertex( curveIds.at( 1 ).second ) )
830  {
831  clearCache(); //bbox may have changed
832  return false;
833  }
834  if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() == 0 &&
835  mCurves.at( curveIds.at( 1 ).first )->numPoints() != 0 )
836  {
837  mCurves.at( curveIds.at( 1 ).first )->moveVertex( QgsVertexId( 0, 0, 0 ), startPoint );
838  removeCurve( curveIds.at( 0 ).first );
839  }
840  else if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() != 0 &&
841  mCurves.at( curveIds.at( 1 ).first )->numPoints() == 0 )
842  {
843  mCurves.at( curveIds.at( 0 ).first )->moveVertex(
844  QgsVertexId( 0, 0, mCurves.at( curveIds.at( 0 ).first )->numPoints() - 1 ), endPoint );
845  removeCurve( curveIds.at( 1 ).first );
846  }
847  else if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() == 0 &&
848  mCurves.at( curveIds.at( 1 ).first )->numPoints() == 0 )
849  {
850  removeCurve( curveIds.at( 1 ).first );
851  removeCurve( curveIds.at( 0 ).first );
852  QgsLineString *line = new QgsLineString();
853  line->insertVertex( QgsVertexId( 0, 0, 0 ), startPoint );
854  line->insertVertex( QgsVertexId( 0, 0, 1 ), endPoint );
855  mCurves.insert( curveIds.at( 0 ).first, line );
856  }
857  else
858  {
859  QgsPoint endPointOfFirst = mCurves.at( curveIds.at( 0 ).first ) ->endPoint();
860  QgsPoint startPointOfSecond = mCurves.at( curveIds.at( 1 ).first ) ->startPoint();
861  if ( endPointOfFirst != startPointOfSecond )
862  {
863  QgsLineString *line = new QgsLineString();
864  line->insertVertex( QgsVertexId( 0, 0, 0 ), endPointOfFirst );
865  line->insertVertex( QgsVertexId( 0, 0, 1 ), startPointOfSecond );
866  mCurves.insert( curveIds.at( 1 ).first, line );
867  }
868  }
869  }
870 
871  bool success = !curveIds.isEmpty();
872  if ( success )
873  {
874  clearCache(); //bbox changed
875  }
876  return success;
877 }
878 
879 QVector< QPair<int, QgsVertexId> > QgsCompoundCurve::curveVertexId( QgsVertexId id ) const
880 {
881  QVector< QPair<int, QgsVertexId> > curveIds;
882 
883  int currentVertexIndex = 0;
884  for ( int i = 0; i < mCurves.size(); ++i )
885  {
886  int increment = mCurves.at( i )->numPoints() - 1;
887  if ( id.vertex >= currentVertexIndex && id.vertex <= currentVertexIndex + increment )
888  {
889  int curveVertexId = id.vertex - currentVertexIndex;
890  QgsVertexId vid;
891  vid.part = 0;
892  vid.ring = 0;
893  vid.vertex = curveVertexId;
894  curveIds.append( qMakePair( i, vid ) );
895  if ( curveVertexId == increment && i < ( mCurves.size() - 1 ) ) //add first vertex of next curve
896  {
897  vid.vertex = 0;
898  curveIds.append( qMakePair( i + 1, vid ) );
899  }
900  break;
901  }
902  else if ( id.vertex >= currentVertexIndex && id.vertex == currentVertexIndex + increment + 1 && i == ( mCurves.size() - 1 ) )
903  {
904  int curveVertexId = id.vertex - currentVertexIndex;
905  QgsVertexId vid;
906  vid.part = 0;
907  vid.ring = 0;
908  vid.vertex = curveVertexId;
909  curveIds.append( qMakePair( i, vid ) );
910  break;
911  }
912  currentVertexIndex += increment;
913  }
914 
915  return curveIds;
916 }
917 
919 {
920 
921  // First we find out the sub-curves that are contain that vertex.
922 
923  // If there is more than one, it means the vertex was at the beginning or end
924  // of an arc, which we don't support.
925 
926  // If there is exactly one, we may either be on a LineString, or on a CircularString.
927 
928  // If on CircularString, we need to check if the vertex is a CurveVertex (odd index).
929  // If so, we split the subcurve at vertex -1 and +1, , drop the middle part and insert a LineString/CircularString
930  // instead with the same points.
931 
932  // At the end, we call condenseCurves() to merge successible line/circular strings
933 
934  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
935 
936  // We cannot convert points at start/end of subcurves
937  if ( curveIds.length() != 1 )
938  return false;
939 
940  int curveId = curveIds[0].first;
941  QgsVertexId subVertexId = curveIds[0].second;
942  QgsCurve *curve = mCurves[curveId];
943 
944  // We cannot convert first/last point of curve
945  if ( subVertexId.vertex == 0 || subVertexId.vertex == curve->numPoints() - 1 )
946  return false;
947 
948  if ( const QgsCircularString *circularString = qgsgeometry_cast<const QgsCircularString *>( curve ) )
949  {
950  // If it's a circular string, we convert to LineString
951 
952  // We cannot convert start/end points of arcs
953  if ( subVertexId.vertex % 2 == 0 ) // for some reason, subVertexId.type is always SegmentVertex...
954  return false;
955 
957  circularString->points( points );
958 
959  const QgsPointSequence partA = points.mid( 0, subVertexId.vertex );
960  const QgsPointSequence partB = QgsPointSequence() << points[subVertexId.vertex - 1] << points[subVertexId.vertex] << points[subVertexId.vertex + 1];
961  const QgsPointSequence partC = points.mid( subVertexId.vertex + 1 );
962 
963  std::unique_ptr<QgsCircularString> curveA = std::make_unique<QgsCircularString>();
964  curveA->setPoints( partA );
965  std::unique_ptr<QgsLineString> curveB = std::make_unique<QgsLineString>();
966  curveB->setPoints( partB );
967  std::unique_ptr<QgsCircularString> curveC = std::make_unique<QgsCircularString>();
968  curveC->setPoints( partC );
969 
970  removeCurve( curveId );
971  if ( subVertexId.vertex < points.length() - 2 )
972  mCurves.insert( curveId, curveC.release() );
973  mCurves.insert( curveId, curveB.release() );
974  if ( subVertexId.vertex > 1 )
975  mCurves.insert( curveId, curveA.release() );
976  }
977  else if ( const QgsLineString *lineString = dynamic_cast<const QgsLineString *>( curve ) )
978  {
979  // If it's a linestring, we split and insert a curve
980 
982  lineString->points( points );
983 
984  const QgsPointSequence partA = points.mid( 0, subVertexId.vertex );
985  const QgsPointSequence partB = QgsPointSequence() << points[subVertexId.vertex - 1] << points[subVertexId.vertex] << points[subVertexId.vertex + 1];
986  const QgsPointSequence partC = points.mid( subVertexId.vertex + 1 );
987 
988  QgsLineString *curveA = new QgsLineString();
989  curveA->setPoints( partA );
990  QgsCircularString *curveB = new QgsCircularString();
991  curveB->setPoints( partB );
992  QgsLineString *curveC = new QgsLineString();
993  curveC->setPoints( partC );
994 
995  removeCurve( curveId );
996  if ( subVertexId.vertex < points.length() - 2 )
997  mCurves.insert( curveId, curveC );
998  mCurves.insert( curveId, curveB );
999  if ( subVertexId.vertex > 1 )
1000  mCurves.insert( curveId, curveA );
1001  }
1002 
1003  // We merge consecutive LineStrings
1004  condenseCurves();
1005 
1006  clearCache();
1007  return true;
1008 }
1009 
1010 
1011 double QgsCompoundCurve::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1012 {
1013  return QgsGeometryUtils::closestSegmentFromComponents( mCurves, QgsGeometryUtils::Vertex, pt, segmentPt, vertexAfter, leftOf, epsilon );
1014 }
1015 
1016 bool QgsCompoundCurve::pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const
1017 {
1018  int currentVertexId = 0;
1019  for ( int j = 0; j < mCurves.size(); ++j )
1020  {
1021  int nCurvePoints = mCurves.at( j )->numPoints();
1022  if ( ( node - currentVertexId ) < nCurvePoints )
1023  {
1024  return ( mCurves.at( j )->pointAt( node - currentVertexId, point, type ) );
1025  }
1026  currentVertexId += ( nCurvePoints - 1 );
1027  }
1028  return false;
1029 }
1030 
1031 double QgsCompoundCurve::xAt( int index ) const
1032 {
1033  int currentVertexId = 0;
1034  for ( int j = 0; j < mCurves.size(); ++j )
1035  {
1036  int nCurvePoints = mCurves.at( j )->numPoints();
1037  if ( ( index - currentVertexId ) < nCurvePoints )
1038  {
1039  return mCurves.at( j )->xAt( index - currentVertexId );
1040  }
1041  currentVertexId += ( nCurvePoints - 1 );
1042  }
1043  return 0.0;
1044 }
1045 
1046 double QgsCompoundCurve::yAt( int index ) const
1047 {
1048  int currentVertexId = 0;
1049  for ( int j = 0; j < mCurves.size(); ++j )
1050  {
1051  int nCurvePoints = mCurves.at( j )->numPoints();
1052  if ( ( index - currentVertexId ) < nCurvePoints )
1053  {
1054  return mCurves.at( j )->yAt( index - currentVertexId );
1055  }
1056  currentVertexId += ( nCurvePoints - 1 );
1057  }
1058  return 0.0;
1059 }
1060 
1062 {
1063  bool res = true;
1064  for ( QgsCurve *curve : std::as_const( mCurves ) )
1065  {
1066  if ( !curve->transform( transformer ) )
1067  {
1068  res = false;
1069  break;
1070  }
1071 
1072  if ( feedback && feedback->isCanceled() )
1073  {
1074  res = false;
1075  break;
1076  }
1077  }
1078  clearCache();
1079  return res;
1080 }
1081 
1082 void QgsCompoundCurve::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1083 {
1084  for ( QgsCurve *curve : std::as_const( mCurves ) )
1085  {
1086  curve->filterVertices( filter );
1087  }
1088  clearCache();
1089 }
1090 
1091 void QgsCompoundCurve::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1092 {
1093  for ( QgsCurve *curve : std::as_const( mCurves ) )
1094  {
1095  curve->transformVertices( transform );
1096  }
1097  clearCache();
1098 }
1099 
1100 std::tuple<std::unique_ptr<QgsCurve>, std::unique_ptr<QgsCurve> > QgsCompoundCurve::splitCurveAtVertex( int index ) const
1101 {
1102  if ( mCurves.empty() )
1103  return std::make_tuple( std::make_unique< QgsCompoundCurve >(), std::make_unique< QgsCompoundCurve >() );
1104 
1105  int curveStart = 0;
1106 
1107  std::unique_ptr< QgsCompoundCurve > curve1 = std::make_unique< QgsCompoundCurve >();
1108  std::unique_ptr< QgsCompoundCurve > curve2;
1109 
1110  for ( const QgsCurve *curve : mCurves )
1111  {
1112  const int curveSize = curve->numPoints();
1113  if ( !curve2 && index < curveStart + curveSize )
1114  {
1115  // split the curve
1116  auto [ p1, p2 ] = curve->splitCurveAtVertex( index - curveStart );
1117  if ( !p1->isEmpty() )
1118  curve1->addCurve( p1.release() );
1119 
1120  curve2 = std::make_unique< QgsCompoundCurve >();
1121  if ( !p2->isEmpty() )
1122  curve2->addCurve( p2.release() );
1123  }
1124  else
1125  {
1126  if ( curve2 )
1127  curve2->addCurve( curve->clone() );
1128  else
1129  curve1->addCurve( curve->clone() );
1130  }
1131 
1132  // subtract 1 here, because the next curve will start with the same
1133  // vertex as this curve ended at
1134  curveStart += curve->numPoints() - 1;
1135  }
1136 
1137  return std::make_tuple( std::move( curve1 ), curve2 ? std::move( curve2 ) : std::make_unique< QgsCompoundCurve >() );
1138 }
1139 
1140 void QgsCompoundCurve::sumUpArea( double &sum ) const
1141 {
1142  for ( const QgsCurve *curve : mCurves )
1143  {
1144  curve->sumUpArea( sum );
1145  }
1146 }
1147 
1149 {
1150  if ( numPoints() < 1 || isClosed() )
1151  {
1152  return;
1153  }
1154  addVertex( startPoint() );
1155 }
1156 
1158 {
1159  for ( const QgsCurve *curve : mCurves )
1160  {
1161  if ( curve->hasCurvedSegments() )
1162  {
1163  return true;
1164  }
1165  }
1166  return false;
1167 }
1168 
1170 {
1171  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( vertex );
1172  if ( curveIds.size() == 1 )
1173  {
1174  QgsCurve *curve = mCurves[curveIds.at( 0 ).first];
1175  return curve->vertexAngle( curveIds.at( 0 ).second );
1176  }
1177  else if ( curveIds.size() > 1 )
1178  {
1179  QgsCurve *curve1 = mCurves[curveIds.at( 0 ).first];
1180  QgsCurve *curve2 = mCurves[curveIds.at( 1 ).first];
1181  double angle1 = curve1->vertexAngle( curveIds.at( 0 ).second );
1182  double angle2 = curve2->vertexAngle( curveIds.at( 1 ).second );
1183  return QgsGeometryUtils::averageAngle( angle1, angle2 );
1184  }
1185  else
1186  {
1187  return 0.0;
1188  }
1189 }
1190 
1192 {
1193  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( startVertex );
1194  double length = 0.0;
1195  for ( auto it = curveIds.constBegin(); it != curveIds.constEnd(); ++it )
1196  {
1197  length += mCurves.at( it->first )->segmentLength( it->second );
1198  }
1199  return length;
1200 }
1201 
1203 {
1205  for ( int i = mCurves.count() - 1; i >= 0; --i )
1206  {
1207  QgsCurve *reversedCurve = mCurves.at( i )->reversed();
1208  clone->addCurve( reversedCurve );
1209  }
1210  return clone;
1211 }
1212 
1213 QgsPoint *QgsCompoundCurve::interpolatePoint( const double distance ) const
1214 {
1215  if ( distance < 0 )
1216  return nullptr;
1217 
1218  double distanceTraversed = 0;
1219  for ( const QgsCurve *curve : mCurves )
1220  {
1221  const double thisCurveLength = curve->length();
1222  if ( distanceTraversed + thisCurveLength > distance || qgsDoubleNear( distanceTraversed + thisCurveLength, distance ) )
1223  {
1224  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1225  const double distanceToPoint = std::min( distance - distanceTraversed, thisCurveLength );
1226 
1227  // point falls on this curve
1228  return curve->interpolatePoint( distanceToPoint );
1229  }
1230 
1231  distanceTraversed += thisCurveLength;
1232  }
1233 
1234  return nullptr;
1235 }
1236 
1237 QgsCompoundCurve *QgsCompoundCurve::curveSubstring( double startDistance, double endDistance ) const
1238 {
1239  if ( startDistance < 0 && endDistance < 0 )
1240  return createEmptyWithSameType();
1241 
1242  endDistance = std::max( startDistance, endDistance );
1243  std::unique_ptr< QgsCompoundCurve > substring = std::make_unique< QgsCompoundCurve >();
1244 
1245  double distanceTraversed = 0;
1246  for ( const QgsCurve *curve : mCurves )
1247  {
1248  const double thisCurveLength = curve->length();
1249  if ( distanceTraversed + thisCurveLength < startDistance )
1250  {
1251  // keep going - haven't found start yet, so no need to include this curve at all
1252  }
1253  else
1254  {
1255  std::unique_ptr< QgsCurve > part( curve->curveSubstring( startDistance - distanceTraversed, endDistance - distanceTraversed ) );
1256  if ( part )
1257  substring->addCurve( part.release() );
1258  }
1259 
1260  distanceTraversed += thisCurveLength;
1261  if ( distanceTraversed > endDistance )
1262  break;
1263  }
1264 
1265  return substring.release();
1266 }
1267 
1268 bool QgsCompoundCurve::addZValue( double zValue )
1269 {
1270  if ( QgsWkbTypes::hasZ( mWkbType ) )
1271  return false;
1272 
1274 
1275  for ( QgsCurve *curve : std::as_const( mCurves ) )
1276  {
1277  curve->addZValue( zValue );
1278  }
1279  clearCache();
1280  return true;
1281 }
1282 
1283 bool QgsCompoundCurve::addMValue( double mValue )
1284 {
1285  if ( QgsWkbTypes::hasM( mWkbType ) )
1286  return false;
1287 
1289 
1290  for ( QgsCurve *curve : std::as_const( mCurves ) )
1291  {
1292  curve->addMValue( mValue );
1293  }
1294  clearCache();
1295  return true;
1296 }
1297 
1299 {
1300  if ( !QgsWkbTypes::hasZ( mWkbType ) )
1301  return false;
1302 
1304  for ( QgsCurve *curve : std::as_const( mCurves ) )
1305  {
1306  curve->dropZValue();
1307  }
1308  clearCache();
1309  return true;
1310 }
1311 
1313 {
1314  if ( !QgsWkbTypes::hasM( mWkbType ) )
1315  return false;
1316 
1318  for ( QgsCurve *curve : std::as_const( mCurves ) )
1319  {
1320  curve->dropMValue();
1321  }
1322  clearCache();
1323  return true;
1324 }
1325 
1327 {
1328  for ( QgsCurve *curve : std::as_const( mCurves ) )
1329  {
1330  curve->swapXy();
1331  }
1332  clearCache();
1333 }
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.
bool pointAt(int node, QgsPoint &point, QgsVertexId::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
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.
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
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 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.
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.
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
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:292
virtual bool isClosed() const SIP_HOLDGIL
Returns true if the curve is closed.
Definition: qgscurve.cpp:52
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:246
QgsRectangle mBoundingBox
Cached bounding box.
Definition: qgscurve.h:349
virtual QgsPoint endPoint() const =0
Returns the end point of the curve.
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:44
void setPoints(const QgsPointSequence &points)
Resets the line string to match the specified list of points.
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:1100
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:1207
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:702
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1146
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1050
static Type addM(Type type) SIP_HOLDGIL
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1171
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:1225
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:1108
QVector< QgsPoint > QgsPointSequence
int precision
Utility class for identifying a unique vertex within a geometry.
int vertex
Vertex number.
int part
Part number.
int ring
Ring number.
VertexType
Type of vertex.