QGIS API Documentation  3.20.0-Odense (decaadbb31)
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 "qgscompoundcurve.h"
19 #include "qgsapplication.h"
20 #include "qgscircularstring.h"
21 #include "qgsgeometryutils.h"
22 #include "qgslinestring.h"
23 #include "qgswkbptr.h"
24 #include "qgsfeedback.h"
25 
26 #include <QJsonObject>
27 #include <QPainter>
28 #include <QPainterPath>
29 #include <memory>
30 #include <nlohmann/json.hpp>
31 
33 {
35 }
36 
38 {
39  clear();
40 }
41 
42 bool QgsCompoundCurve::equals( const QgsCurve &other ) const
43 {
44  const QgsCompoundCurve *otherCurve = qgsgeometry_cast< const QgsCompoundCurve * >( &other );
45  if ( !otherCurve )
46  return false;
47 
48  if ( mWkbType != otherCurve->mWkbType )
49  return false;
50 
51  if ( mCurves.size() != otherCurve->mCurves.size() )
52  return false;
53 
54  for ( int i = 0; i < mCurves.size(); ++i )
55  {
56  if ( *mCurves.at( i ) != *otherCurve->mCurves.at( i ) )
57  return false;
58  }
59 
60  return true;
61 }
62 
64 {
65  auto result = std::make_unique< QgsCompoundCurve >();
66  result->mWkbType = mWkbType;
67  return result.release();
68 }
69 
71 {
72  const QgsCompoundCurve *otherCurve = qgsgeometry_cast<const QgsCompoundCurve *>( other );
73  if ( !otherCurve )
74  return -1;
75 
76  int i = 0;
77  int j = 0;
78  while ( i < mCurves.size() && j < otherCurve->mCurves.size() )
79  {
80  const QgsAbstractGeometry *aGeom = mCurves[i];
81  const QgsAbstractGeometry *bGeom = otherCurve->mCurves[j];
82  const int comparison = aGeom->compareTo( bGeom );
83  if ( comparison != 0 )
84  {
85  return comparison;
86  }
87  i++;
88  j++;
89  }
90  if ( i < mCurves.size() )
91  {
92  return 1;
93  }
94  if ( j < otherCurve->mCurves.size() )
95  {
96  return -1;
97  }
98  return 0;
99 }
100 
102 {
103  return QStringLiteral( "CompoundCurve" );
104 }
105 
107 {
108  return 1;
109 }
110 
112 {
113  mWkbType = curve.wkbType();
114  mCurves.reserve( curve.mCurves.size() );
115  for ( const QgsCurve *c : curve.mCurves )
116  {
117  mCurves.append( c->clone() );
118  }
119 }
120 
122 {
123  if ( &curve != this )
124  {
125  clearCache();
126  QgsCurve::operator=( curve );
127  for ( const QgsCurve *c : curve.mCurves )
128  {
129  mCurves.append( c->clone() );
130  }
131  }
132  return *this;
133 }
134 
136 {
137  return new QgsCompoundCurve( *this );
138 }
139 
141 {
143  qDeleteAll( mCurves );
144  mCurves.clear();
145  clearCache();
146 }
147 
149 {
150  if ( mCurves.empty() )
151  {
152  return QgsRectangle();
153  }
154 
155  QgsRectangle bbox = mCurves.at( 0 )->boundingBox();
156  for ( int i = 1; i < mCurves.size(); ++i )
157  {
158  QgsRectangle curveBox = mCurves.at( i )->boundingBox();
159  bbox.combineExtentWith( curveBox );
160  }
161  return bbox;
162 }
163 
164 void QgsCompoundCurve::scroll( int index )
165 {
166  const int size = numPoints();
167  if ( index < 1 || index >= size - 1 )
168  return;
169 
170  auto [p1, p2 ] = splitCurveAtVertex( index );
171 
172  mCurves.clear();
173  if ( QgsCompoundCurve *curve2 = qgsgeometry_cast< QgsCompoundCurve *>( p2.get() ) )
174  {
175  // take the curves from the second part and make them our first lot of curves
176  mCurves = std::move( curve2->mCurves );
177  }
178  if ( QgsCompoundCurve *curve1 = qgsgeometry_cast< QgsCompoundCurve *>( p1.get() ) )
179  {
180  // take the curves from the first part and append them to our curves
181  mCurves.append( curve1->mCurves );
182  curve1->mCurves.clear();
183  }
184 }
185 
187 {
188  clear();
189  if ( !wkbPtr )
190  {
191  return false;
192  }
193 
194  QgsWkbTypes::Type type = wkbPtr.readHeader();
196  {
197  return false;
198  }
199  mWkbType = type;
200 
201  int nCurves;
202  wkbPtr >> nCurves;
203  QgsCurve *currentCurve = nullptr;
204  for ( int i = 0; i < nCurves; ++i )
205  {
206  QgsWkbTypes::Type curveType = wkbPtr.readHeader();
207  wkbPtr -= 1 + sizeof( int );
208  if ( QgsWkbTypes::flatType( curveType ) == QgsWkbTypes::LineString )
209  {
210  currentCurve = new QgsLineString();
211  }
212  else if ( QgsWkbTypes::flatType( curveType ) == QgsWkbTypes::CircularString )
213  {
214  currentCurve = new QgsCircularString();
215  }
216  else
217  {
218  return false;
219  }
220  currentCurve->fromWkb( wkbPtr ); // also updates wkbPtr
221  mCurves.append( currentCurve );
222  }
223  return true;
224 }
225 
226 bool QgsCompoundCurve::fromWkt( const QString &wkt )
227 {
228  clear();
229 
230  QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
231 
233  return false;
234  mWkbType = parts.first;
235 
236  QString secondWithoutParentheses = parts.second;
237  secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
238  if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
239  secondWithoutParentheses.isEmpty() )
240  return true;
241 
242  QString defaultChildWkbType = QStringLiteral( "LineString%1%2" ).arg( is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
243 
244  const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
245  for ( const QString &childWkt : blocks )
246  {
247  QPair<QgsWkbTypes::Type, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
248 
249  if ( QgsWkbTypes::flatType( childParts.first ) == QgsWkbTypes::LineString )
250  mCurves.append( new QgsLineString() );
251  else if ( QgsWkbTypes::flatType( childParts.first ) == QgsWkbTypes::CircularString )
252  mCurves.append( new QgsCircularString() );
253  else
254  {
255  clear();
256  return false;
257  }
258  if ( !mCurves.back()->fromWkt( childWkt ) )
259  {
260  clear();
261  return false;
262  }
263  }
264 
265  //scan through curves and check if dimensionality of curves is different to compound curve.
266  //if so, update the type dimensionality of the compound curve to match
267  bool hasZ = false;
268  bool hasM = false;
269  for ( const QgsCurve *curve : std::as_const( mCurves ) )
270  {
271  hasZ = hasZ || curve->is3D();
272  hasM = hasM || curve->isMeasure();
273  if ( hasZ && hasM )
274  break;
275  }
276  if ( hasZ )
277  addZValue( 0 );
278  if ( hasM )
279  addMValue( 0 );
280 
281  return true;
282 }
283 
284 int QgsCompoundCurve::wkbSize( QgsAbstractGeometry::WkbFlags flags ) const
285 {
286  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
287  for ( const QgsCurve *curve : mCurves )
288  {
289  binarySize += curve->wkbSize( flags );
290  }
291  return binarySize;
292 }
293 
294 QByteArray QgsCompoundCurve::asWkb( WkbFlags flags ) const
295 {
296  QByteArray wkbArray;
297  wkbArray.resize( QgsCompoundCurve::wkbSize( flags ) );
298  QgsWkbPtr wkb( wkbArray );
299  wkb << static_cast<char>( QgsApplication::endian() );
300  wkb << static_cast<quint32>( wkbType() );
301  wkb << static_cast<quint32>( mCurves.size() );
302  for ( const QgsCurve *curve : mCurves )
303  {
304  wkb << curve->asWkb( flags );
305  }
306  return wkbArray;
307 }
308 
309 QString QgsCompoundCurve::asWkt( int precision ) const
310 {
311  QString wkt = wktTypeStr();
312  if ( isEmpty() )
313  wkt += QLatin1String( " EMPTY" );
314  else
315  {
316  wkt += QLatin1String( " (" );
317  for ( const QgsCurve *curve : mCurves )
318  {
319  QString childWkt = curve->asWkt( precision );
320  if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
321  {
322  // Type names of linear geometries are omitted
323  childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
324  }
325  wkt += childWkt + ',';
326  }
327  if ( wkt.endsWith( ',' ) )
328  {
329  wkt.chop( 1 );
330  }
331  wkt += ')';
332  }
333  return wkt;
334 }
335 
336 QDomElement QgsCompoundCurve::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
337 {
338  // GML2 does not support curves
339  std::unique_ptr< QgsLineString > line( curveToLine() );
340  QDomElement gml = line->asGml2( doc, precision, ns, axisOrder );
341  return gml;
342 }
343 
344 QDomElement QgsCompoundCurve::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
345 {
346  QDomElement compoundCurveElem = doc.createElementNS( ns, QStringLiteral( "CompositeCurve" ) );
347 
348  if ( isEmpty() )
349  return compoundCurveElem;
350 
351  for ( const QgsCurve *curve : mCurves )
352  {
353  QDomElement curveMemberElem = doc.createElementNS( ns, QStringLiteral( "curveMember" ) );
354  QDomElement curveElem = curve->asGml3( doc, precision, ns, axisOrder );
355  curveMemberElem.appendChild( curveElem );
356  compoundCurveElem.appendChild( curveMemberElem );
357  }
358 
359  return compoundCurveElem;
360 }
361 
363 {
364  // GeoJSON does not support curves
365  std::unique_ptr< QgsLineString > line( curveToLine() );
366  return line->asJsonObject( precision );
367 }
368 
370 {
371  double length = 0;
372  for ( const QgsCurve *curve : mCurves )
373  {
374  length += curve->length();
375  }
376  return length;
377 }
378 
380 {
381  if ( mCurves.empty() )
382  {
383  return QgsPoint();
384  }
385  return mCurves.at( 0 )->startPoint();
386 }
387 
389 {
390  if ( mCurves.empty() )
391  {
392  return QgsPoint();
393  }
394  return mCurves.at( mCurves.size() - 1 )->endPoint();
395 }
396 
398 {
399  pts.clear();
400  if ( mCurves.empty() )
401  {
402  return;
403  }
404 
405  mCurves[0]->points( pts );
406  for ( int i = 1; i < mCurves.size(); ++i )
407  {
408  QgsPointSequence pList;
409  mCurves[i]->points( pList );
410  pList.removeFirst(); //first vertex already added in previous line
411  pts.append( pList );
412  }
413 }
414 
416 {
417  int nPoints = 0;
418  int nCurves = mCurves.size();
419  if ( nCurves < 1 )
420  {
421  return 0;
422  }
423 
424  for ( int i = 0; i < nCurves; ++i )
425  {
426  nPoints += mCurves.at( i )->numPoints() - 1; //last vertex is equal to first of next section
427  }
428  nPoints += 1; //last vertex was removed above
429  return nPoints;
430 }
431 
433 {
434  if ( mCurves.isEmpty() )
435  return true;
436 
437  for ( QgsCurve *curve : mCurves )
438  {
439  if ( !curve->isEmpty() )
440  return false;
441  }
442  return true;
443 }
444 
445 bool QgsCompoundCurve::isValid( QString &error, int flags ) const
446 {
447  if ( mCurves.isEmpty() )
448  return true;
449 
450  for ( int i = 0; i < mCurves.size() ; ++i )
451  {
452  if ( !mCurves[i]->isValid( error, flags ) )
453  {
454  error = QObject::tr( "Curve[%1]: %2" ).arg( i + 1 ).arg( error );
455  return false;
456  }
457  }
458  return QgsCurve::isValid( error, flags );
459 }
460 
461 int QgsCompoundCurve::indexOf( const QgsPoint &point ) const
462 {
463  int curveStart = 0;
464  for ( const QgsCurve *curve : mCurves )
465  {
466  const int curveIndex = curve->indexOf( point );
467  if ( curveIndex >= 0 )
468  return curveStart + curveIndex;
469  // subtract 1 here, because the next curve will start with the same
470  // vertex as this curve ended at
471  curveStart += curve->numPoints() - 1;
472  }
473  return -1;
474 }
475 
477 {
478  QgsLineString *line = new QgsLineString();
479  std::unique_ptr< QgsLineString > currentLine;
480  for ( const QgsCurve *curve : mCurves )
481  {
482  currentLine.reset( curve->curveToLine( tolerance, toleranceType ) );
483  line->append( currentLine.get() );
484  }
485  return line;
486 }
487 
488 QgsCompoundCurve *QgsCompoundCurve::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
489 {
490  std::unique_ptr<QgsCompoundCurve> result( createEmptyWithSameType() );
491 
492  for ( QgsCurve *curve : mCurves )
493  {
494  std::unique_ptr<QgsCurve> gridified( static_cast< QgsCurve * >( curve->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) ) );
495  if ( gridified )
496  {
497  result->mCurves.append( gridified.release() );
498  }
499  }
500 
501  if ( result->mCurves.empty() )
502  return nullptr;
503  else
504  return result.release();
505 }
506 
507 bool QgsCompoundCurve::removeDuplicateNodes( double epsilon, bool useZValues )
508 {
509  bool result = false;
510  const QVector< QgsCurve * > curves = mCurves;
511  int i = 0;
512  QgsPoint lastEnd;
513  for ( QgsCurve *curve : curves )
514  {
515  result = curve->removeDuplicateNodes( epsilon, useZValues ) || result;
516  if ( curve->numPoints() == 0 || qgsDoubleNear( curve->length(), 0.0, epsilon ) )
517  {
518  // empty curve, remove it
519  delete mCurves.takeAt( i );
520  result = true;
521  }
522  else
523  {
524  // ensure this line starts exactly where previous line ended
525  if ( i > 0 )
526  {
527  curve->moveVertex( QgsVertexId( -1, -1, 0 ), lastEnd );
528  }
529  lastEnd = curve->vertexAt( QgsVertexId( -1, -1, curve->numPoints() - 1 ) );
530  }
531  i++;
532  }
533  return result;
534 }
535 
537 {
538  if ( mCurves.empty() )
539  return false;
540 
541  // if we already have the bounding box calculated, then this check is trivial!
542  if ( !mBoundingBox.isNull() )
543  {
544  return mBoundingBox.intersects( rectangle );
545  }
546 
547  // otherwise loop through each member curve and test the bounding box intersection.
548  // This gives us a chance to use optimisations which may be present on the individual
549  // curve subclasses, and at worst it will cause a calculation of the bounding box
550  // of each individual member curve which we would have to do anyway... (and these
551  // bounding boxes are cached, so would be reused without additional expense)
552  for ( const QgsCurve *curve : mCurves )
553  {
554  if ( curve->boundingBoxIntersects( rectangle ) )
555  return true;
556  }
557 
558  // even if we don't intersect the bounding box of any member curves, we may still intersect the
559  // bounding box of the overall compound curve.
560  // so here we fall back to the non-optimised base class check which has to first calculate
561  // the overall bounding box of the compound curve..
562  return QgsAbstractGeometry::boundingBoxIntersects( rectangle );
563 }
564 
566 {
567  if ( mCurves.size() == 1 )
568  return mCurves.at( 0 );
569  else
570  return this;
571 }
572 
573 const QgsCurve *QgsCompoundCurve::curveAt( int i ) const
574 {
575  if ( i < 0 || i >= mCurves.size() )
576  {
577  return nullptr;
578  }
579  return mCurves.at( i );
580 }
581 
582 void QgsCompoundCurve::addCurve( QgsCurve *c, const bool extendPrevious )
583 {
584  if ( !c )
585  return;
586 
587  if ( mCurves.empty() )
588  {
590  }
591 
592  if ( QgsWkbTypes::hasZ( mWkbType ) && !QgsWkbTypes::hasZ( c->wkbType() ) )
593  {
594  c->addZValue();
595  }
596  else if ( !QgsWkbTypes::hasZ( mWkbType ) && QgsWkbTypes::hasZ( c->wkbType() ) )
597  {
598  c->dropZValue();
599  }
600  if ( QgsWkbTypes::hasM( mWkbType ) && !QgsWkbTypes::hasM( c->wkbType() ) )
601  {
602  c->addMValue();
603  }
604  else if ( !QgsWkbTypes::hasM( mWkbType ) && QgsWkbTypes::hasM( c->wkbType() ) )
605  {
606  c->dropMValue();
607  }
608 
609  QgsLineString *previousLineString = !mCurves.empty() ? qgsgeometry_cast< QgsLineString * >( mCurves.constLast() ) : nullptr;
610  const QgsLineString *newLineString = qgsgeometry_cast< const QgsLineString * >( c );
611  const bool canExtendPrevious = extendPrevious && previousLineString && newLineString;
612  if ( canExtendPrevious )
613  {
614  previousLineString->append( newLineString );
615  // we are taking ownership, so delete the input curve
616  delete c;
617  c = nullptr;
618  }
619  else
620  {
621  mCurves.append( c );
622  }
623 
624  clearCache();
625 }
626 
628 {
629  if ( i < 0 || i >= mCurves.size() )
630  {
631  return;
632  }
633 
634  delete mCurves.takeAt( i );
635  clearCache();
636 }
637 
639 {
640  if ( mCurves.isEmpty() || mWkbType == QgsWkbTypes::Unknown )
641  {
643  }
644 
645  //is last curve QgsLineString
646  QgsCurve *lastCurve = nullptr;
647  if ( !mCurves.isEmpty() )
648  {
649  lastCurve = mCurves.at( mCurves.size() - 1 );
650  }
651 
652  QgsLineString *line = nullptr;
653  if ( !lastCurve || QgsWkbTypes::flatType( lastCurve->wkbType() ) != QgsWkbTypes::LineString )
654  {
655  line = new QgsLineString();
656  mCurves.append( line );
657  if ( lastCurve )
658  {
659  line->addVertex( lastCurve->endPoint() );
660  }
661  lastCurve = line;
662  }
663  else //create new QgsLineString* with point in it
664  {
665  line = static_cast<QgsLineString *>( lastCurve );
666  }
667  line->addVertex( pt );
668  clearCache();
669 }
670 
672 {
673  QgsCurve *lastCurve = nullptr;
674  QVector< QgsCurve * > newCurves;
675  newCurves.reserve( mCurves.size() );
676  for ( QgsCurve *curve : std::as_const( mCurves ) )
677  {
678  if ( lastCurve && lastCurve->wkbType() == curve->wkbType() )
679  {
680  if ( QgsLineString *ls = qgsgeometry_cast< QgsLineString * >( lastCurve ) )
681  {
682  ls->append( qgsgeometry_cast< QgsLineString * >( curve ) );
683  delete curve;
684  }
685  else if ( QgsCircularString *cs = qgsgeometry_cast< QgsCircularString * >( lastCurve ) )
686  {
687  cs->append( qgsgeometry_cast< QgsCircularString * >( curve ) );
688  delete curve;
689  }
690  }
691  else
692  {
693  lastCurve = curve;
694  newCurves << curve;
695  }
696  }
697  mCurves = newCurves;
698 }
699 
700 void QgsCompoundCurve::draw( QPainter &p ) const
701 {
702  for ( const QgsCurve *curve : mCurves )
703  {
704  curve->draw( p );
705  }
706 }
707 
709 {
710  for ( QgsCurve *curve : std::as_const( mCurves ) )
711  {
712  curve->transform( ct, d, transformZ );
713  }
714  clearCache();
715 }
716 
717 void QgsCompoundCurve::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
718 {
719  for ( QgsCurve *curve : std::as_const( mCurves ) )
720  {
721  curve->transform( t, zTranslate, zScale, mTranslate, mScale );
722  }
723  clearCache();
724 }
725 
726 void QgsCompoundCurve::addToPainterPath( QPainterPath &path ) const
727 {
728  QPainterPath pp;
729  for ( const QgsCurve *curve : mCurves )
730  {
731  curve->addToPainterPath( pp );
732  }
733  path.addPath( pp );
734 }
735 
736 void QgsCompoundCurve::drawAsPolygon( QPainter &p ) const
737 {
738  QPainterPath pp;
739  for ( const QgsCurve *curve : mCurves )
740  {
741  curve->addToPainterPath( pp );
742  }
743  p.drawPath( pp );
744 }
745 
746 bool QgsCompoundCurve::insertVertex( QgsVertexId position, const QgsPoint &vertex )
747 {
748  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
749  if ( curveIds.empty() )
750  {
751  return false;
752  }
753  int curveId = curveIds.at( 0 ).first;
754  if ( curveId >= mCurves.size() )
755  {
756  return false;
757  }
758 
759  bool success = mCurves.at( curveId )->insertVertex( curveIds.at( 0 ).second, vertex );
760  if ( success )
761  {
762  clearCache(); //bbox changed
763  }
764  return success;
765 }
766 
767 bool QgsCompoundCurve::moveVertex( QgsVertexId position, const QgsPoint &newPos )
768 {
769  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
770  QVector< QPair<int, QgsVertexId> >::const_iterator idIt = curveIds.constBegin();
771  for ( ; idIt != curveIds.constEnd(); ++idIt )
772  {
773  mCurves.at( idIt->first )->moveVertex( idIt->second, newPos );
774  }
775 
776  bool success = !curveIds.isEmpty();
777  if ( success )
778  {
779  clearCache(); //bbox changed
780  }
781  return success;
782 }
783 
785 {
786  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
787  if ( curveIds.size() == 1 )
788  {
789  if ( !mCurves.at( curveIds.at( 0 ).first )->deleteVertex( curveIds.at( 0 ).second ) )
790  {
791  clearCache(); //bbox may have changed
792  return false;
793  }
794  if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() == 0 )
795  {
796  removeCurve( curveIds.at( 0 ).first );
797  }
798  }
799  else if ( curveIds.size() == 2 )
800  {
801  Q_ASSERT( curveIds.at( 1 ).first == curveIds.at( 0 ).first + 1 );
802  Q_ASSERT( curveIds.at( 0 ).second.vertex == mCurves.at( curveIds.at( 0 ).first )->numPoints() - 1 );
803  Q_ASSERT( curveIds.at( 1 ).second.vertex == 0 );
804  QgsPoint startPoint = mCurves.at( curveIds.at( 0 ).first ) ->startPoint();
805  QgsPoint endPoint = mCurves.at( curveIds.at( 1 ).first ) ->endPoint();
806  if ( QgsWkbTypes::flatType( mCurves.at( curveIds.at( 0 ).first )->wkbType() ) == QgsWkbTypes::LineString &&
807  QgsWkbTypes::flatType( mCurves.at( curveIds.at( 1 ).first )->wkbType() ) == QgsWkbTypes::CircularString &&
808  mCurves.at( curveIds.at( 1 ).first )->numPoints() > 3 )
809  {
810  QgsPoint intermediatePoint;
812  mCurves.at( curveIds.at( 1 ).first ) ->pointAt( 2, intermediatePoint, type );
813  mCurves.at( curveIds.at( 0 ).first )->moveVertex(
814  QgsVertexId( 0, 0, mCurves.at( curveIds.at( 0 ).first )->numPoints() - 1 ), intermediatePoint );
815  }
816  else if ( !mCurves.at( curveIds.at( 0 ).first )->deleteVertex( curveIds.at( 0 ).second ) )
817  {
818  clearCache(); //bbox may have changed
819  return false;
820  }
821  if ( QgsWkbTypes::flatType( mCurves.at( curveIds.at( 0 ).first )->wkbType() ) == QgsWkbTypes::CircularString &&
822  mCurves.at( curveIds.at( 0 ).first )->numPoints() > 0 &&
823  QgsWkbTypes::flatType( mCurves.at( curveIds.at( 1 ).first )->wkbType() ) == QgsWkbTypes::LineString )
824  {
825  QgsPoint intermediatePoint = mCurves.at( curveIds.at( 0 ).first ) ->endPoint();
826  mCurves.at( curveIds.at( 1 ).first )->moveVertex( QgsVertexId( 0, 0, 0 ), intermediatePoint );
827  }
828  else if ( !mCurves.at( curveIds.at( 1 ).first )->deleteVertex( curveIds.at( 1 ).second ) )
829  {
830  clearCache(); //bbox may have changed
831  return false;
832  }
833  if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() == 0 &&
834  mCurves.at( curveIds.at( 1 ).first )->numPoints() != 0 )
835  {
836  mCurves.at( curveIds.at( 1 ).first )->moveVertex( QgsVertexId( 0, 0, 0 ), startPoint );
837  removeCurve( curveIds.at( 0 ).first );
838  }
839  else if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() != 0 &&
840  mCurves.at( curveIds.at( 1 ).first )->numPoints() == 0 )
841  {
842  mCurves.at( curveIds.at( 0 ).first )->moveVertex(
843  QgsVertexId( 0, 0, mCurves.at( curveIds.at( 0 ).first )->numPoints() - 1 ), endPoint );
844  removeCurve( curveIds.at( 1 ).first );
845  }
846  else if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() == 0 &&
847  mCurves.at( curveIds.at( 1 ).first )->numPoints() == 0 )
848  {
849  removeCurve( curveIds.at( 1 ).first );
850  removeCurve( curveIds.at( 0 ).first );
851  QgsLineString *line = new QgsLineString();
852  line->insertVertex( QgsVertexId( 0, 0, 0 ), startPoint );
853  line->insertVertex( QgsVertexId( 0, 0, 1 ), endPoint );
854  mCurves.insert( curveIds.at( 0 ).first, line );
855  }
856  else
857  {
858  QgsPoint endPointOfFirst = mCurves.at( curveIds.at( 0 ).first ) ->endPoint();
859  QgsPoint startPointOfSecond = mCurves.at( curveIds.at( 1 ).first ) ->startPoint();
860  if ( endPointOfFirst != startPointOfSecond )
861  {
862  QgsLineString *line = new QgsLineString();
863  line->insertVertex( QgsVertexId( 0, 0, 0 ), endPointOfFirst );
864  line->insertVertex( QgsVertexId( 0, 0, 1 ), startPointOfSecond );
865  mCurves.insert( curveIds.at( 1 ).first, line );
866  }
867  }
868  }
869 
870  bool success = !curveIds.isEmpty();
871  if ( success )
872  {
873  clearCache(); //bbox changed
874  }
875  return success;
876 }
877 
878 QVector< QPair<int, QgsVertexId> > QgsCompoundCurve::curveVertexId( QgsVertexId id ) const
879 {
880  QVector< QPair<int, QgsVertexId> > curveIds;
881 
882  int currentVertexIndex = 0;
883  for ( int i = 0; i < mCurves.size(); ++i )
884  {
885  int increment = mCurves.at( i )->numPoints() - 1;
886  if ( id.vertex >= currentVertexIndex && id.vertex <= currentVertexIndex + increment )
887  {
888  int curveVertexId = id.vertex - currentVertexIndex;
889  QgsVertexId vid;
890  vid.part = 0;
891  vid.ring = 0;
892  vid.vertex = curveVertexId;
893  curveIds.append( qMakePair( i, vid ) );
894  if ( curveVertexId == increment && i < ( mCurves.size() - 1 ) ) //add first vertex of next curve
895  {
896  vid.vertex = 0;
897  curveIds.append( qMakePair( i + 1, vid ) );
898  }
899  break;
900  }
901  else if ( id.vertex >= currentVertexIndex && id.vertex == currentVertexIndex + increment + 1 && i == ( mCurves.size() - 1 ) )
902  {
903  int curveVertexId = id.vertex - currentVertexIndex;
904  QgsVertexId vid;
905  vid.part = 0;
906  vid.ring = 0;
907  vid.vertex = curveVertexId;
908  curveIds.append( qMakePair( i, vid ) );
909  break;
910  }
911  currentVertexIndex += increment;
912  }
913 
914  return curveIds;
915 }
916 
917 double QgsCompoundCurve::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
918 {
919  return QgsGeometryUtils::closestSegmentFromComponents( mCurves, QgsGeometryUtils::Vertex, pt, segmentPt, vertexAfter, leftOf, epsilon );
920 }
921 
922 bool QgsCompoundCurve::pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const
923 {
924  int currentVertexId = 0;
925  for ( int j = 0; j < mCurves.size(); ++j )
926  {
927  int nCurvePoints = mCurves.at( j )->numPoints();
928  if ( ( node - currentVertexId ) < nCurvePoints )
929  {
930  return ( mCurves.at( j )->pointAt( node - currentVertexId, point, type ) );
931  }
932  currentVertexId += ( nCurvePoints - 1 );
933  }
934  return false;
935 }
936 
937 double QgsCompoundCurve::xAt( int index ) const
938 {
939  int currentVertexId = 0;
940  for ( int j = 0; j < mCurves.size(); ++j )
941  {
942  int nCurvePoints = mCurves.at( j )->numPoints();
943  if ( ( index - currentVertexId ) < nCurvePoints )
944  {
945  return mCurves.at( j )->xAt( index - currentVertexId );
946  }
947  currentVertexId += ( nCurvePoints - 1 );
948  }
949  return 0.0;
950 }
951 
952 double QgsCompoundCurve::yAt( int index ) const
953 {
954  int currentVertexId = 0;
955  for ( int j = 0; j < mCurves.size(); ++j )
956  {
957  int nCurvePoints = mCurves.at( j )->numPoints();
958  if ( ( index - currentVertexId ) < nCurvePoints )
959  {
960  return mCurves.at( j )->yAt( index - currentVertexId );
961  }
962  currentVertexId += ( nCurvePoints - 1 );
963  }
964  return 0.0;
965 }
966 
968 {
969  bool res = true;
970  for ( QgsCurve *curve : std::as_const( mCurves ) )
971  {
972  if ( !curve->transform( transformer ) )
973  {
974  res = false;
975  break;
976  }
977 
978  if ( feedback && feedback->isCanceled() )
979  {
980  res = false;
981  break;
982  }
983  }
984  clearCache();
985  return res;
986 }
987 
988 void QgsCompoundCurve::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
989 {
990  for ( QgsCurve *curve : std::as_const( mCurves ) )
991  {
992  curve->filterVertices( filter );
993  }
994  clearCache();
995 }
996 
997 void QgsCompoundCurve::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
998 {
999  for ( QgsCurve *curve : std::as_const( mCurves ) )
1000  {
1001  curve->transformVertices( transform );
1002  }
1003  clearCache();
1004 }
1005 
1006 std::tuple<std::unique_ptr<QgsCurve>, std::unique_ptr<QgsCurve> > QgsCompoundCurve::splitCurveAtVertex( int index ) const
1007 {
1008  if ( mCurves.empty() )
1009  return std::make_tuple( std::make_unique< QgsCompoundCurve >(), std::make_unique< QgsCompoundCurve >() );
1010 
1011  int curveStart = 0;
1012 
1013  std::unique_ptr< QgsCompoundCurve > curve1 = std::make_unique< QgsCompoundCurve >();
1014  std::unique_ptr< QgsCompoundCurve > curve2;
1015 
1016  for ( const QgsCurve *curve : mCurves )
1017  {
1018  const int curveSize = curve->numPoints();
1019  if ( !curve2 && index < curveStart + curveSize )
1020  {
1021  // split the curve
1022  auto [ p1, p2 ] = curve->splitCurveAtVertex( index - curveStart );
1023  if ( !p1->isEmpty() )
1024  curve1->addCurve( p1.release() );
1025 
1026  curve2 = std::make_unique< QgsCompoundCurve >();
1027  if ( !p2->isEmpty() )
1028  curve2->addCurve( p2.release() );
1029  }
1030  else
1031  {
1032  if ( curve2 )
1033  curve2->addCurve( curve->clone() );
1034  else
1035  curve1->addCurve( curve->clone() );
1036  }
1037 
1038  // subtract 1 here, because the next curve will start with the same
1039  // vertex as this curve ended at
1040  curveStart += curve->numPoints() - 1;
1041  }
1042 
1043  return std::make_tuple( std::move( curve1 ), curve2 ? std::move( curve2 ) : std::make_unique< QgsCompoundCurve >() );
1044 }
1045 
1046 void QgsCompoundCurve::sumUpArea( double &sum ) const
1047 {
1048  for ( const QgsCurve *curve : mCurves )
1049  {
1050  curve->sumUpArea( sum );
1051  }
1052 }
1053 
1055 {
1056  if ( numPoints() < 1 || isClosed() )
1057  {
1058  return;
1059  }
1060  addVertex( startPoint() );
1061 }
1062 
1064 {
1065  for ( const QgsCurve *curve : mCurves )
1066  {
1067  if ( curve->hasCurvedSegments() )
1068  {
1069  return true;
1070  }
1071  }
1072  return false;
1073 }
1074 
1076 {
1077  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( vertex );
1078  if ( curveIds.size() == 1 )
1079  {
1080  QgsCurve *curve = mCurves[curveIds.at( 0 ).first];
1081  return curve->vertexAngle( curveIds.at( 0 ).second );
1082  }
1083  else if ( curveIds.size() > 1 )
1084  {
1085  QgsCurve *curve1 = mCurves[curveIds.at( 0 ).first];
1086  QgsCurve *curve2 = mCurves[curveIds.at( 1 ).first];
1087  double angle1 = curve1->vertexAngle( curveIds.at( 0 ).second );
1088  double angle2 = curve2->vertexAngle( curveIds.at( 1 ).second );
1089  return QgsGeometryUtils::averageAngle( angle1, angle2 );
1090  }
1091  else
1092  {
1093  return 0.0;
1094  }
1095 }
1096 
1098 {
1099  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( startVertex );
1100  double length = 0.0;
1101  for ( auto it = curveIds.constBegin(); it != curveIds.constEnd(); ++it )
1102  {
1103  length += mCurves.at( it->first )->segmentLength( it->second );
1104  }
1105  return length;
1106 }
1107 
1109 {
1111  for ( int i = mCurves.count() - 1; i >= 0; --i )
1112  {
1113  QgsCurve *reversedCurve = mCurves.at( i )->reversed();
1114  clone->addCurve( reversedCurve );
1115  }
1116  return clone;
1117 }
1118 
1119 QgsPoint *QgsCompoundCurve::interpolatePoint( const double distance ) const
1120 {
1121  if ( distance < 0 )
1122  return nullptr;
1123 
1124  double distanceTraversed = 0;
1125  for ( const QgsCurve *curve : mCurves )
1126  {
1127  const double thisCurveLength = curve->length();
1128  if ( distanceTraversed + thisCurveLength > distance || qgsDoubleNear( distanceTraversed + thisCurveLength, distance ) )
1129  {
1130  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
1131  const double distanceToPoint = std::min( distance - distanceTraversed, thisCurveLength );
1132 
1133  // point falls on this curve
1134  return curve->interpolatePoint( distanceToPoint );
1135  }
1136 
1137  distanceTraversed += thisCurveLength;
1138  }
1139 
1140  return nullptr;
1141 }
1142 
1143 QgsCompoundCurve *QgsCompoundCurve::curveSubstring( double startDistance, double endDistance ) const
1144 {
1145  if ( startDistance < 0 && endDistance < 0 )
1146  return createEmptyWithSameType();
1147 
1148  endDistance = std::max( startDistance, endDistance );
1149  std::unique_ptr< QgsCompoundCurve > substring = std::make_unique< QgsCompoundCurve >();
1150 
1151  double distanceTraversed = 0;
1152  for ( const QgsCurve *curve : mCurves )
1153  {
1154  const double thisCurveLength = curve->length();
1155  if ( distanceTraversed + thisCurveLength < startDistance )
1156  {
1157  // keep going - haven't found start yet, so no need to include this curve at all
1158  }
1159  else
1160  {
1161  std::unique_ptr< QgsCurve > part( curve->curveSubstring( startDistance - distanceTraversed, endDistance - distanceTraversed ) );
1162  if ( part )
1163  substring->addCurve( part.release() );
1164  }
1165 
1166  distanceTraversed += thisCurveLength;
1167  if ( distanceTraversed > endDistance )
1168  break;
1169  }
1170 
1171  return substring.release();
1172 }
1173 
1174 bool QgsCompoundCurve::addZValue( double zValue )
1175 {
1176  if ( QgsWkbTypes::hasZ( mWkbType ) )
1177  return false;
1178 
1180 
1181  for ( QgsCurve *curve : std::as_const( mCurves ) )
1182  {
1183  curve->addZValue( zValue );
1184  }
1185  clearCache();
1186  return true;
1187 }
1188 
1189 bool QgsCompoundCurve::addMValue( double mValue )
1190 {
1191  if ( QgsWkbTypes::hasM( mWkbType ) )
1192  return false;
1193 
1195 
1196  for ( QgsCurve *curve : std::as_const( mCurves ) )
1197  {
1198  curve->addMValue( mValue );
1199  }
1200  clearCache();
1201  return true;
1202 }
1203 
1205 {
1206  if ( !QgsWkbTypes::hasZ( mWkbType ) )
1207  return false;
1208 
1210  for ( QgsCurve *curve : std::as_const( mCurves ) )
1211  {
1212  curve->dropZValue();
1213  }
1214  clearCache();
1215  return true;
1216 }
1217 
1219 {
1220  if ( !QgsWkbTypes::hasM( mWkbType ) )
1221  return false;
1222 
1224  for ( QgsCurve *curve : std::as_const( mCurves ) )
1225  {
1226  curve->dropMValue();
1227  }
1228  clearCache();
1229  return true;
1230 }
1231 
1233 {
1234  for ( QgsCurve *curve : std::as_const( mCurves ) )
1235  {
1236  curve->swapXy();
1237  }
1238  clearCache();
1239 }
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.
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.
bool isValid(QString &error, int flags=0) const override
Checks validity of the geometry, and returns true if the geometry is valid.
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.
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.
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
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, int flags=0) 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 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:598
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.