QGIS API Documentation  3.27.0-Master (11ef3e5184)
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  if ( mHasCachedSummedUpArea )
1152  {
1153  sum += mSummedUpArea;
1154  return;
1155  }
1156 
1157  mSummedUpArea = 0;
1158  for ( const QgsCurve *curve : mCurves )
1159  {
1160  curve->sumUpArea( mSummedUpArea );
1161  }
1162  mHasCachedSummedUpArea = true;
1163  sum += mSummedUpArea;
1164 }
1165 
1167 {
1168  if ( numPoints() < 1 || isClosed() )
1169  {
1170  return;
1171  }
1172  addVertex( startPoint() );
1173 }
1174 
1176 {
1177  for ( const QgsCurve *curve : mCurves )
1178  {
1179  if ( curve->hasCurvedSegments() )
1180  {
1181  return true;
1182  }
1183  }
1184  return false;
1185 }
1186 
1188 {
1189  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( vertex );
1190  if ( curveIds.size() == 1 )
1191  {
1192  QgsCurve *curve = mCurves[curveIds.at( 0 ).first];
1193  return curve->vertexAngle( curveIds.at( 0 ).second );
1194  }
1195  else if ( curveIds.size() > 1 )
1196  {
1197  QgsCurve *curve1 = mCurves[curveIds.at( 0 ).first];
1198  QgsCurve *curve2 = mCurves[curveIds.at( 1 ).first];
1199  double angle1 = curve1->vertexAngle( curveIds.at( 0 ).second );
1200  double angle2 = curve2->vertexAngle( curveIds.at( 1 ).second );
1201  return QgsGeometryUtils::averageAngle( angle1, angle2 );
1202  }
1203  else
1204  {
1205  return 0.0;
1206  }
1207 }
1208 
1210 {
1211  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( startVertex );
1212  double length = 0.0;
1213  for ( auto it = curveIds.constBegin(); it != curveIds.constEnd(); ++it )
1214  {
1215  length += mCurves.at( it->first )->segmentLength( it->second );
1216  }
1217  return length;
1218 }
1219 
1221 {
1223  for ( int i = mCurves.count() - 1; i >= 0; --i )
1224  {
1225  QgsCurve *reversedCurve = mCurves.at( i )->reversed();
1226  clone->addCurve( reversedCurve );
1227  }
1228  return clone;
1229 }
1230 
1231 QgsPoint *QgsCompoundCurve::interpolatePoint( const double distance ) const
1232 {
1233  if ( distance < 0 )
1234  return nullptr;
1235 
1236  double distanceTraversed = 0;
1237  for ( const QgsCurve *curve : mCurves )
1238  {
1239  const double thisCurveLength = curve->length();
1240  if ( distanceTraversed + thisCurveLength > distance || qgsDoubleNear( distanceTraversed + thisCurveLength, distance ) )
1241  {
1242  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1243  const double distanceToPoint = std::min( distance - distanceTraversed, thisCurveLength );
1244 
1245  // point falls on this curve
1246  return curve->interpolatePoint( distanceToPoint );
1247  }
1248 
1249  distanceTraversed += thisCurveLength;
1250  }
1251 
1252  return nullptr;
1253 }
1254 
1255 QgsCompoundCurve *QgsCompoundCurve::curveSubstring( double startDistance, double endDistance ) const
1256 {
1257  if ( startDistance < 0 && endDistance < 0 )
1258  return createEmptyWithSameType();
1259 
1260  endDistance = std::max( startDistance, endDistance );
1261  std::unique_ptr< QgsCompoundCurve > substring = std::make_unique< QgsCompoundCurve >();
1262 
1263  double distanceTraversed = 0;
1264  for ( const QgsCurve *curve : mCurves )
1265  {
1266  const double thisCurveLength = curve->length();
1267  if ( distanceTraversed + thisCurveLength < startDistance )
1268  {
1269  // keep going - haven't found start yet, so no need to include this curve at all
1270  }
1271  else
1272  {
1273  std::unique_ptr< QgsCurve > part( curve->curveSubstring( startDistance - distanceTraversed, endDistance - distanceTraversed ) );
1274  if ( part )
1275  substring->addCurve( part.release() );
1276  }
1277 
1278  distanceTraversed += thisCurveLength;
1279  if ( distanceTraversed > endDistance )
1280  break;
1281  }
1282 
1283  return substring.release();
1284 }
1285 
1286 bool QgsCompoundCurve::addZValue( double zValue )
1287 {
1288  if ( QgsWkbTypes::hasZ( mWkbType ) )
1289  return false;
1290 
1292 
1293  for ( QgsCurve *curve : std::as_const( mCurves ) )
1294  {
1295  curve->addZValue( zValue );
1296  }
1297  clearCache();
1298  return true;
1299 }
1300 
1301 bool QgsCompoundCurve::addMValue( double mValue )
1302 {
1303  if ( QgsWkbTypes::hasM( mWkbType ) )
1304  return false;
1305 
1307 
1308  for ( QgsCurve *curve : std::as_const( mCurves ) )
1309  {
1310  curve->addMValue( mValue );
1311  }
1312  clearCache();
1313  return true;
1314 }
1315 
1317 {
1318  if ( !QgsWkbTypes::hasZ( mWkbType ) )
1319  return false;
1320 
1322  for ( QgsCurve *curve : std::as_const( mCurves ) )
1323  {
1324  curve->dropZValue();
1325  }
1326  clearCache();
1327  return true;
1328 }
1329 
1331 {
1332  if ( !QgsWkbTypes::hasM( mWkbType ) )
1333  return false;
1334 
1336  for ( QgsCurve *curve : std::as_const( mCurves ) )
1337  {
1338  curve->dropMValue();
1339  }
1340  clearCache();
1341  return true;
1342 }
1343 
1345 {
1346  for ( QgsCurve *curve : std::as_const( mCurves ) )
1347  {
1348  curve->swapXy();
1349  }
1350  clearCache();
1351 }
VertexType
Types of vertex.
Definition: qgis.h:1518
TransformDirection
Flags for raster layer temporal capabilities.
Definition: qgis.h:1320
An abstract base class for classes which transform geometries by transforming input points to output ...
Abstract base class for all geometries.
virtual bool fromWkb(QgsConstWkbPtr &wkb)=0
Sets the geometry from a WKB string.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
virtual bool boundingBoxIntersects(const QgsRectangle &rectangle) const SIP_HOLDGIL
Returns true if the bounding box of this geometry intersects with a rectangle.
AxisOrder
Axis order for GML generation.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
QgsAbstractGeometry & operator=(const QgsAbstractGeometry &geom)
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
virtual int compareTo(const QgsAbstractGeometry *other) const
Comparator for sorting of geometry.
QgsWkbTypes::Type mWkbType
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
static endian_t endian()
Returns whether this machine uses big or little endian.
Circular string geometry type.
void setPoints(const QgsPointSequence &points)
Sets the circular string's points.
Compound curve geometry type.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a new line string geometry corresponding to a segmentized approximation of the curve.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
const QgsAbstractGeometry * simplifiedTypeRef() const override SIP_HOLDGIL
Returns a reference to the simplest lossless representation of this geometry, e.g.
int compareToSameClass(const QgsAbstractGeometry *other) const final
Compares to an other geometry of the same class, and returns a integer for sorting of the two geometr...
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
QgsCompoundCurve * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
void condenseCurves()
Condenses the curves in this geometry by combining adjacent linestrings a to a single continuous line...
void close()
Appends first point if not already closed.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
QgsPoint endPoint() const override SIP_HOLDGIL
Returns the end point of the curve.
void filterVertices(const std::function< bool(const QgsPoint &) > &filter) override
Filters the vertices from the geometry in place, removing any which do not return true for the filter...
std::tuple< std::unique_ptr< QgsCurve >, std::unique_ptr< QgsCurve > > splitCurveAtVertex(int index) const final
Splits the curve at the specified vertex index, returning two curves which represent the portion of t...
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
const QgsCurve * curveAt(int i) const SIP_HOLDGIL
Returns the curve at the specified index.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
void transformVertices(const std::function< QgsPoint(const QgsPoint &) > &transform) override
Transforms the vertices from the geometry in place, applying the transform function to every vertex.
int dimension() const override SIP_HOLDGIL
Returns the inherent dimension of the geometry.
bool toggleCircularAtVertex(QgsVertexId position)
Converts the vertex at the given position from/to circular.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb()
QString geometryType() const override SIP_HOLDGIL
Returns a unique string representing the geometry type.
void swapXy() override
Swaps the x and y coordinates from the geometry.
void removeCurve(int i)
Removes a curve from the geometry.
void addCurve(QgsCurve *c, bool extendPrevious=false)
Adds a curve to the geometry (takes ownership).
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *std::numeric_limits< double >::epsilon()) const override
Searches for the closest segment of the geometry to a given point.
QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML2 representation of the geometry.
bool boundingBoxIntersects(const QgsRectangle &rectangle) const override SIP_HOLDGIL
Returns true if the bounding box of this geometry intersects with a rectangle.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
QgsRectangle calculateBoundingBox() const override
Default calculator for the minimal bounding box for the geometry.
bool dropMValue() override
Drops any measure values which exist in the geometry.
QgsCompoundCurve & operator=(const QgsCompoundCurve &curve)
QgsCompoundCurve * clone() const override
Clones the geometry by performing a deep copy.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
double length() const override SIP_HOLDGIL
Returns the planar, 2-dimensional length of the geometry.
~QgsCompoundCurve() override
double xAt(int index) const override SIP_HOLDGIL
Returns the x-coordinate of the specified node in the line string.
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
void clear() override
Clears the geometry, ie reset it to a null geometry.
int indexOf(const QgsPoint &point) const final
Returns the index of the first vertex matching the given point, or -1 if a matching vertex is not fou...
bool equals(const QgsCurve &other) const override
Checks whether this curve exactly equals another curve.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
double yAt(int index) const override SIP_HOLDGIL
Returns the y-coordinate of the specified node in the line string.
QgsCompoundCurve * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
QgsPoint startPoint() const override SIP_HOLDGIL
Returns the starting point of the curve.
void scroll(int firstVertexIndex) final
Scrolls the curve vertices so that they start with the vertex at the given index.
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
QgsCompoundCurve * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
bool pointAt(int node, QgsPoint &point, Qgis::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
QgsCompoundCurve * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid.
void addVertex(const QgsPoint &pt)
Adds a vertex to the end of the geometry.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
int nCurves() const SIP_HOLDGIL
Returns the number of curves in the geometry.
QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML3 representation of the geometry.
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
A const WKB pointer.
Definition: qgswkbptr.h:138
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
Class for doing transforms between two map coordinate systems.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
virtual int numPoints() const =0
Returns the number of points in the curve.
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgscurve.cpp:293
bool mHasCachedSummedUpArea
Definition: qgscurve.h:344
virtual bool isClosed() const SIP_HOLDGIL
Returns true if the curve is closed.
Definition: qgscurve.cpp:53
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
Definition: qgscurve.cpp:247
QgsRectangle mBoundingBox
Cached bounding box.
Definition: qgscurve.h:342
virtual QgsPoint endPoint() const =0
Returns the end point of the curve.
double mSummedUpArea
Definition: qgscurve.h:345
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
static QPair< QgsWkbTypes::Type, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
static QStringList wktGetChildBlocks(const QString &wkt, const QString &defaultType=QString())
Parses a WKT string and returns of list of blocks contained in the WKT.
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3) SIP_HOLDGIL
Calculates the average angle (in radians) between the two linear segments from (x1,...
static double closestSegmentFromComponents(T &container, ComponentType ctype, const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon)
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
void setPoints(size_t size, const double *x, const double *y, const double *z=nullptr, const double *m=nullptr)
Resets the line string to match the specified point data.
void append(const QgsLineString *line)
Appends the contents of another line string to the end of this line string.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
QgsPoint vertexAt(QgsVertexId) const override
Returns the point corresponding to a specified vertex id.
Definition: qgspoint.cpp:525
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
Definition: qgspoint.cpp:130
A rectangle specified with double values.
Definition: qgsrectangle.h:42
bool intersects(const QgsRectangle &rect) const SIP_HOLDGIL
Returns true when rectangle intersects with other rectangle.
Definition: qgsrectangle.h:349
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:479
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:391
WKB pointer handler.
Definition: qgswkbptr.h:44
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1130
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
static Type dropZ(Type type) SIP_HOLDGIL
Drops the z dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1237
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:732
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1176
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1080
static Type addM(Type type) SIP_HOLDGIL
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1201
static Type dropM(Type type) SIP_HOLDGIL
Drops the m dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1255
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether 'thepoint' is left or right of the line from 'p1' to 'p2'. Negative values mean left ...
Definition: MathUtils.cpp:292
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2527
QVector< QgsPoint > QgsPointSequence
int precision
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:31
int vertex
Vertex number.
Definition: qgsvertexid.h:95
int part
Part number.
Definition: qgsvertexid.h:89
int ring
Ring number.
Definition: qgsvertexid.h:92