QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
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 = qgis::make_unique< QgsCompoundCurve >();
66  result->mWkbType = mWkbType;
67  return result.release();
68 }
69 
71 {
72  return QStringLiteral( "CompoundCurve" );
73 }
74 
76 {
77  return 1;
78 }
79 
81 {
82  mWkbType = curve.wkbType();
83  mCurves.reserve( curve.mCurves.size() );
84  for ( const QgsCurve *c : curve.mCurves )
85  {
86  mCurves.append( c->clone() );
87  }
88 }
89 
91 {
92  if ( &curve != this )
93  {
94  clearCache();
95  QgsCurve::operator=( curve );
96  for ( const QgsCurve *c : curve.mCurves )
97  {
98  mCurves.append( c->clone() );
99  }
100  }
101  return *this;
102 }
103 
105 {
106  return new QgsCompoundCurve( *this );
107 }
108 
110 {
112  qDeleteAll( mCurves );
113  mCurves.clear();
114  clearCache();
115 }
116 
118 {
119  if ( mCurves.empty() )
120  {
121  return QgsRectangle();
122  }
123 
124  QgsRectangle bbox = mCurves.at( 0 )->boundingBox();
125  for ( int i = 1; i < mCurves.size(); ++i )
126  {
127  QgsRectangle curveBox = mCurves.at( i )->boundingBox();
128  bbox.combineExtentWith( curveBox );
129  }
130  return bbox;
131 }
132 
134 {
135  clear();
136  if ( !wkbPtr )
137  {
138  return false;
139  }
140 
141  QgsWkbTypes::Type type = wkbPtr.readHeader();
143  {
144  return false;
145  }
146  mWkbType = type;
147 
148  int nCurves;
149  wkbPtr >> nCurves;
150  QgsCurve *currentCurve = nullptr;
151  for ( int i = 0; i < nCurves; ++i )
152  {
153  QgsWkbTypes::Type curveType = wkbPtr.readHeader();
154  wkbPtr -= 1 + sizeof( int );
155  if ( QgsWkbTypes::flatType( curveType ) == QgsWkbTypes::LineString )
156  {
157  currentCurve = new QgsLineString();
158  }
159  else if ( QgsWkbTypes::flatType( curveType ) == QgsWkbTypes::CircularString )
160  {
161  currentCurve = new QgsCircularString();
162  }
163  else
164  {
165  return false;
166  }
167  currentCurve->fromWkb( wkbPtr ); // also updates wkbPtr
168  mCurves.append( currentCurve );
169  }
170  return true;
171 }
172 
173 bool QgsCompoundCurve::fromWkt( const QString &wkt )
174 {
175  clear();
176 
177  QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
178 
180  return false;
181  mWkbType = parts.first;
182 
183  QString secondWithoutParentheses = parts.second;
184  secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
185  if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
186  secondWithoutParentheses.isEmpty() )
187  return true;
188 
189  QString defaultChildWkbType = QStringLiteral( "LineString%1%2" ).arg( is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
190 
191  const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
192  for ( const QString &childWkt : blocks )
193  {
194  QPair<QgsWkbTypes::Type, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
195 
196  if ( QgsWkbTypes::flatType( childParts.first ) == QgsWkbTypes::LineString )
197  mCurves.append( new QgsLineString() );
198  else if ( QgsWkbTypes::flatType( childParts.first ) == QgsWkbTypes::CircularString )
199  mCurves.append( new QgsCircularString() );
200  else
201  {
202  clear();
203  return false;
204  }
205  if ( !mCurves.back()->fromWkt( childWkt ) )
206  {
207  clear();
208  return false;
209  }
210  }
211 
212  //scan through curves and check if dimensionality of curves is different to compound curve.
213  //if so, update the type dimensionality of the compound curve to match
214  bool hasZ = false;
215  bool hasM = false;
216  for ( const QgsCurve *curve : qgis::as_const( mCurves ) )
217  {
218  hasZ = hasZ || curve->is3D();
219  hasM = hasM || curve->isMeasure();
220  if ( hasZ && hasM )
221  break;
222  }
223  if ( hasZ )
224  addZValue( 0 );
225  if ( hasM )
226  addMValue( 0 );
227 
228  return true;
229 }
230 
231 int QgsCompoundCurve::wkbSize( QgsAbstractGeometry::WkbFlags flags ) const
232 {
233  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
234  for ( const QgsCurve *curve : mCurves )
235  {
236  binarySize += curve->wkbSize( flags );
237  }
238  return binarySize;
239 }
240 
241 QByteArray QgsCompoundCurve::asWkb( WkbFlags flags ) const
242 {
243  QByteArray wkbArray;
244  wkbArray.resize( QgsCompoundCurve::wkbSize( flags ) );
245  QgsWkbPtr wkb( wkbArray );
246  wkb << static_cast<char>( QgsApplication::endian() );
247  wkb << static_cast<quint32>( wkbType() );
248  wkb << static_cast<quint32>( mCurves.size() );
249  for ( const QgsCurve *curve : mCurves )
250  {
251  wkb << curve->asWkb( flags );
252  }
253  return wkbArray;
254 }
255 
256 QString QgsCompoundCurve::asWkt( int precision ) const
257 {
258  QString wkt = wktTypeStr();
259  if ( isEmpty() )
260  wkt += QLatin1String( " EMPTY" );
261  else
262  {
263  wkt += QLatin1String( " (" );
264  for ( const QgsCurve *curve : mCurves )
265  {
266  QString childWkt = curve->asWkt( precision );
267  if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
268  {
269  // Type names of linear geometries are omitted
270  childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
271  }
272  wkt += childWkt + ',';
273  }
274  if ( wkt.endsWith( ',' ) )
275  {
276  wkt.chop( 1 );
277  }
278  wkt += ')';
279  }
280  return wkt;
281 }
282 
283 QDomElement QgsCompoundCurve::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
284 {
285  // GML2 does not support curves
286  std::unique_ptr< QgsLineString > line( curveToLine() );
287  QDomElement gml = line->asGml2( doc, precision, ns, axisOrder );
288  return gml;
289 }
290 
291 QDomElement QgsCompoundCurve::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
292 {
293  QDomElement compoundCurveElem = doc.createElementNS( ns, QStringLiteral( "CompositeCurve" ) );
294 
295  if ( isEmpty() )
296  return compoundCurveElem;
297 
298  for ( const QgsCurve *curve : mCurves )
299  {
300  QDomElement curveMemberElem = doc.createElementNS( ns, QStringLiteral( "curveMember" ) );
301  QDomElement curveElem = curve->asGml3( doc, precision, ns, axisOrder );
302  curveMemberElem.appendChild( curveElem );
303  compoundCurveElem.appendChild( curveMemberElem );
304  }
305 
306  return compoundCurveElem;
307 }
308 
310 {
311  // GeoJSON does not support curves
312  std::unique_ptr< QgsLineString > line( curveToLine() );
313  return line->asJsonObject( precision );
314 }
315 
317 {
318  double length = 0;
319  for ( const QgsCurve *curve : mCurves )
320  {
321  length += curve->length();
322  }
323  return length;
324 }
325 
327 {
328  if ( mCurves.empty() )
329  {
330  return QgsPoint();
331  }
332  return mCurves.at( 0 )->startPoint();
333 }
334 
336 {
337  if ( mCurves.empty() )
338  {
339  return QgsPoint();
340  }
341  return mCurves.at( mCurves.size() - 1 )->endPoint();
342 }
343 
345 {
346  pts.clear();
347  if ( mCurves.empty() )
348  {
349  return;
350  }
351 
352  mCurves[0]->points( pts );
353  for ( int i = 1; i < mCurves.size(); ++i )
354  {
355  QgsPointSequence pList;
356  mCurves[i]->points( pList );
357  pList.removeFirst(); //first vertex already added in previous line
358  pts.append( pList );
359  }
360 }
361 
363 {
364  int nPoints = 0;
365  int nCurves = mCurves.size();
366  if ( nCurves < 1 )
367  {
368  return 0;
369  }
370 
371  for ( int i = 0; i < nCurves; ++i )
372  {
373  nPoints += mCurves.at( i )->numPoints() - 1; //last vertex is equal to first of next section
374  }
375  nPoints += 1; //last vertex was removed above
376  return nPoints;
377 }
378 
380 {
381  if ( mCurves.isEmpty() )
382  return true;
383 
384  for ( QgsCurve *curve : mCurves )
385  {
386  if ( !curve->isEmpty() )
387  return false;
388  }
389  return true;
390 }
391 
392 bool QgsCompoundCurve::isValid( QString &error, int flags ) const
393 {
394  if ( mCurves.isEmpty() )
395  return true;
396 
397  for ( int i = 0; i < mCurves.size() ; ++i )
398  {
399  if ( !mCurves[i]->isValid( error, flags ) )
400  {
401  error = QObject::tr( "Curve[%1]: %2" ).arg( i + 1 ).arg( error );
402  return false;
403  }
404  }
405  return QgsCurve::isValid( error, flags );
406 }
407 
409 {
410  QgsLineString *line = new QgsLineString();
411  std::unique_ptr< QgsLineString > currentLine;
412  for ( const QgsCurve *curve : mCurves )
413  {
414  currentLine.reset( curve->curveToLine( tolerance, toleranceType ) );
415  line->append( currentLine.get() );
416  }
417  return line;
418 }
419 
420 QgsCompoundCurve *QgsCompoundCurve::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
421 {
422  std::unique_ptr<QgsCompoundCurve> result( createEmptyWithSameType() );
423 
424  for ( QgsCurve *curve : mCurves )
425  {
426  std::unique_ptr<QgsCurve> gridified( static_cast< QgsCurve * >( curve->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) ) );
427  if ( gridified )
428  {
429  result->mCurves.append( gridified.release() );
430  }
431  }
432 
433  if ( result->mCurves.empty() )
434  return nullptr;
435  else
436  return result.release();
437 }
438 
439 bool QgsCompoundCurve::removeDuplicateNodes( double epsilon, bool useZValues )
440 {
441  bool result = false;
442  const QVector< QgsCurve * > curves = mCurves;
443  int i = 0;
444  QgsPoint lastEnd;
445  for ( QgsCurve *curve : curves )
446  {
447  result = curve->removeDuplicateNodes( epsilon, useZValues ) || result;
448  if ( curve->numPoints() == 0 || qgsDoubleNear( curve->length(), 0.0, epsilon ) )
449  {
450  // empty curve, remove it
451  delete mCurves.takeAt( i );
452  result = true;
453  }
454  else
455  {
456  // ensure this line starts exactly where previous line ended
457  if ( i > 0 )
458  {
459  curve->moveVertex( QgsVertexId( -1, -1, 0 ), lastEnd );
460  }
461  lastEnd = curve->vertexAt( QgsVertexId( -1, -1, curve->numPoints() - 1 ) );
462  }
463  i++;
464  }
465  return result;
466 }
467 
468 const QgsCurve *QgsCompoundCurve::curveAt( int i ) const
469 {
470  if ( i < 0 || i >= mCurves.size() )
471  {
472  return nullptr;
473  }
474  return mCurves.at( i );
475 }
476 
478 {
479  if ( c )
480  {
481  if ( mCurves.empty() )
482  {
484  }
485 
486  mCurves.append( c );
487 
488  if ( QgsWkbTypes::hasZ( mWkbType ) && !QgsWkbTypes::hasZ( c->wkbType() ) )
489  {
490  c->addZValue();
491  }
492  else if ( !QgsWkbTypes::hasZ( mWkbType ) && QgsWkbTypes::hasZ( c->wkbType() ) )
493  {
494  c->dropZValue();
495  }
496  if ( QgsWkbTypes::hasM( mWkbType ) && !QgsWkbTypes::hasM( c->wkbType() ) )
497  {
498  c->addMValue();
499  }
500  else if ( !QgsWkbTypes::hasM( mWkbType ) && QgsWkbTypes::hasM( c->wkbType() ) )
501  {
502  c->dropMValue();
503  }
504  clearCache();
505  }
506 }
507 
509 {
510  if ( i < 0 || i >= mCurves.size() )
511  {
512  return;
513  }
514 
515  delete mCurves.takeAt( i );
516  clearCache();
517 }
518 
520 {
521  if ( mCurves.isEmpty() || mWkbType == QgsWkbTypes::Unknown )
522  {
524  }
525 
526  //is last curve QgsLineString
527  QgsCurve *lastCurve = nullptr;
528  if ( !mCurves.isEmpty() )
529  {
530  lastCurve = mCurves.at( mCurves.size() - 1 );
531  }
532 
533  QgsLineString *line = nullptr;
534  if ( !lastCurve || QgsWkbTypes::flatType( lastCurve->wkbType() ) != QgsWkbTypes::LineString )
535  {
536  line = new QgsLineString();
537  mCurves.append( line );
538  if ( lastCurve )
539  {
540  line->addVertex( lastCurve->endPoint() );
541  }
542  lastCurve = line;
543  }
544  else //create new QgsLineString* with point in it
545  {
546  line = static_cast<QgsLineString *>( lastCurve );
547  }
548  line->addVertex( pt );
549  clearCache();
550 }
551 
552 void QgsCompoundCurve::draw( QPainter &p ) const
553 {
554  for ( const QgsCurve *curve : mCurves )
555  {
556  curve->draw( p );
557  }
558 }
559 
561 {
562  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
563  {
564  curve->transform( ct, d, transformZ );
565  }
566  clearCache();
567 }
568 
569 void QgsCompoundCurve::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
570 {
571  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
572  {
573  curve->transform( t, zTranslate, zScale, mTranslate, mScale );
574  }
575  clearCache();
576 }
577 
578 void QgsCompoundCurve::addToPainterPath( QPainterPath &path ) const
579 {
580  QPainterPath pp;
581  for ( const QgsCurve *curve : mCurves )
582  {
583  curve->addToPainterPath( pp );
584  }
585  path.addPath( pp );
586 }
587 
588 void QgsCompoundCurve::drawAsPolygon( QPainter &p ) const
589 {
590  QPainterPath pp;
591  for ( const QgsCurve *curve : mCurves )
592  {
593  curve->addToPainterPath( pp );
594  }
595  p.drawPath( pp );
596 }
597 
598 bool QgsCompoundCurve::insertVertex( QgsVertexId position, const QgsPoint &vertex )
599 {
600  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
601  if ( curveIds.empty() )
602  {
603  return false;
604  }
605  int curveId = curveIds.at( 0 ).first;
606  if ( curveId >= mCurves.size() )
607  {
608  return false;
609  }
610 
611  bool success = mCurves.at( curveId )->insertVertex( curveIds.at( 0 ).second, vertex );
612  if ( success )
613  {
614  clearCache(); //bbox changed
615  }
616  return success;
617 }
618 
619 bool QgsCompoundCurve::moveVertex( QgsVertexId position, const QgsPoint &newPos )
620 {
621  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
622  QVector< QPair<int, QgsVertexId> >::const_iterator idIt = curveIds.constBegin();
623  for ( ; idIt != curveIds.constEnd(); ++idIt )
624  {
625  mCurves.at( idIt->first )->moveVertex( idIt->second, newPos );
626  }
627 
628  bool success = !curveIds.isEmpty();
629  if ( success )
630  {
631  clearCache(); //bbox changed
632  }
633  return success;
634 }
635 
637 {
638  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
639  if ( curveIds.size() == 1 )
640  {
641  if ( !mCurves.at( curveIds.at( 0 ).first )->deleteVertex( curveIds.at( 0 ).second ) )
642  {
643  clearCache(); //bbox may have changed
644  return false;
645  }
646  if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() == 0 )
647  {
648  removeCurve( curveIds.at( 0 ).first );
649  }
650  }
651  else if ( curveIds.size() == 2 )
652  {
653  Q_ASSERT( curveIds.at( 1 ).first == curveIds.at( 0 ).first + 1 );
654  Q_ASSERT( curveIds.at( 0 ).second.vertex == mCurves.at( curveIds.at( 0 ).first )->numPoints() - 1 );
655  Q_ASSERT( curveIds.at( 1 ).second.vertex == 0 );
656  QgsPoint startPoint = mCurves.at( curveIds.at( 0 ).first ) ->startPoint();
657  QgsPoint endPoint = mCurves.at( curveIds.at( 1 ).first ) ->endPoint();
658  if ( QgsWkbTypes::flatType( mCurves.at( curveIds.at( 0 ).first )->wkbType() ) == QgsWkbTypes::LineString &&
659  QgsWkbTypes::flatType( mCurves.at( curveIds.at( 1 ).first )->wkbType() ) == QgsWkbTypes::CircularString &&
660  mCurves.at( curveIds.at( 1 ).first )->numPoints() > 3 )
661  {
662  QgsPoint intermediatePoint;
664  mCurves.at( curveIds.at( 1 ).first ) ->pointAt( 2, intermediatePoint, type );
665  mCurves.at( curveIds.at( 0 ).first )->moveVertex(
666  QgsVertexId( 0, 0, mCurves.at( curveIds.at( 0 ).first )->numPoints() - 1 ), intermediatePoint );
667  }
668  else if ( !mCurves.at( curveIds.at( 0 ).first )->deleteVertex( curveIds.at( 0 ).second ) )
669  {
670  clearCache(); //bbox may have changed
671  return false;
672  }
673  if ( QgsWkbTypes::flatType( mCurves.at( curveIds.at( 0 ).first )->wkbType() ) == QgsWkbTypes::CircularString &&
674  mCurves.at( curveIds.at( 0 ).first )->numPoints() > 0 &&
675  QgsWkbTypes::flatType( mCurves.at( curveIds.at( 1 ).first )->wkbType() ) == QgsWkbTypes::LineString )
676  {
677  QgsPoint intermediatePoint = mCurves.at( curveIds.at( 0 ).first ) ->endPoint();
678  mCurves.at( curveIds.at( 1 ).first )->moveVertex( QgsVertexId( 0, 0, 0 ), intermediatePoint );
679  }
680  else if ( !mCurves.at( curveIds.at( 1 ).first )->deleteVertex( curveIds.at( 1 ).second ) )
681  {
682  clearCache(); //bbox may have changed
683  return false;
684  }
685  if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() == 0 &&
686  mCurves.at( curveIds.at( 1 ).first )->numPoints() != 0 )
687  {
688  mCurves.at( curveIds.at( 1 ).first )->moveVertex( QgsVertexId( 0, 0, 0 ), startPoint );
689  removeCurve( curveIds.at( 0 ).first );
690  }
691  else if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() != 0 &&
692  mCurves.at( curveIds.at( 1 ).first )->numPoints() == 0 )
693  {
694  mCurves.at( curveIds.at( 0 ).first )->moveVertex(
695  QgsVertexId( 0, 0, mCurves.at( curveIds.at( 0 ).first )->numPoints() - 1 ), endPoint );
696  removeCurve( curveIds.at( 1 ).first );
697  }
698  else if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() == 0 &&
699  mCurves.at( curveIds.at( 1 ).first )->numPoints() == 0 )
700  {
701  removeCurve( curveIds.at( 1 ).first );
702  removeCurve( curveIds.at( 0 ).first );
703  QgsLineString *line = new QgsLineString();
704  line->insertVertex( QgsVertexId( 0, 0, 0 ), startPoint );
705  line->insertVertex( QgsVertexId( 0, 0, 1 ), endPoint );
706  mCurves.insert( curveIds.at( 0 ).first, line );
707  }
708  else
709  {
710  QgsPoint endPointOfFirst = mCurves.at( curveIds.at( 0 ).first ) ->endPoint();
711  QgsPoint startPointOfSecond = mCurves.at( curveIds.at( 1 ).first ) ->startPoint();
712  if ( endPointOfFirst != startPointOfSecond )
713  {
714  QgsLineString *line = new QgsLineString();
715  line->insertVertex( QgsVertexId( 0, 0, 0 ), endPointOfFirst );
716  line->insertVertex( QgsVertexId( 0, 0, 1 ), startPointOfSecond );
717  mCurves.insert( curveIds.at( 1 ).first, line );
718  }
719  }
720  }
721 
722  bool success = !curveIds.isEmpty();
723  if ( success )
724  {
725  clearCache(); //bbox changed
726  }
727  return success;
728 }
729 
730 QVector< QPair<int, QgsVertexId> > QgsCompoundCurve::curveVertexId( QgsVertexId id ) const
731 {
732  QVector< QPair<int, QgsVertexId> > curveIds;
733 
734  int currentVertexIndex = 0;
735  for ( int i = 0; i < mCurves.size(); ++i )
736  {
737  int increment = mCurves.at( i )->numPoints() - 1;
738  if ( id.vertex >= currentVertexIndex && id.vertex <= currentVertexIndex + increment )
739  {
740  int curveVertexId = id.vertex - currentVertexIndex;
741  QgsVertexId vid;
742  vid.part = 0;
743  vid.ring = 0;
744  vid.vertex = curveVertexId;
745  curveIds.append( qMakePair( i, vid ) );
746  if ( curveVertexId == increment && i < ( mCurves.size() - 1 ) ) //add first vertex of next curve
747  {
748  vid.vertex = 0;
749  curveIds.append( qMakePair( i + 1, vid ) );
750  }
751  break;
752  }
753  else if ( id.vertex >= currentVertexIndex && id.vertex == currentVertexIndex + increment + 1 && i == ( mCurves.size() - 1 ) )
754  {
755  int curveVertexId = id.vertex - currentVertexIndex;
756  QgsVertexId vid;
757  vid.part = 0;
758  vid.ring = 0;
759  vid.vertex = curveVertexId;
760  curveIds.append( qMakePair( i, vid ) );
761  break;
762  }
763  currentVertexIndex += increment;
764  }
765 
766  return curveIds;
767 }
768 
769 double QgsCompoundCurve::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
770 {
771  return QgsGeometryUtils::closestSegmentFromComponents( mCurves, QgsGeometryUtils::Vertex, pt, segmentPt, vertexAfter, leftOf, epsilon );
772 }
773 
774 bool QgsCompoundCurve::pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const
775 {
776  int currentVertexId = 0;
777  for ( int j = 0; j < mCurves.size(); ++j )
778  {
779  int nCurvePoints = mCurves.at( j )->numPoints();
780  if ( ( node - currentVertexId ) < nCurvePoints )
781  {
782  return ( mCurves.at( j )->pointAt( node - currentVertexId, point, type ) );
783  }
784  currentVertexId += ( nCurvePoints - 1 );
785  }
786  return false;
787 }
788 
789 double QgsCompoundCurve::xAt( int index ) const
790 {
791  int currentVertexId = 0;
792  for ( int j = 0; j < mCurves.size(); ++j )
793  {
794  int nCurvePoints = mCurves.at( j )->numPoints();
795  if ( ( index - currentVertexId ) < nCurvePoints )
796  {
797  return mCurves.at( j )->xAt( index - currentVertexId );
798  }
799  currentVertexId += ( nCurvePoints - 1 );
800  }
801  return 0.0;
802 }
803 
804 double QgsCompoundCurve::yAt( int index ) const
805 {
806  int currentVertexId = 0;
807  for ( int j = 0; j < mCurves.size(); ++j )
808  {
809  int nCurvePoints = mCurves.at( j )->numPoints();
810  if ( ( index - currentVertexId ) < nCurvePoints )
811  {
812  return mCurves.at( j )->yAt( index - currentVertexId );
813  }
814  currentVertexId += ( nCurvePoints - 1 );
815  }
816  return 0.0;
817 }
818 
820 {
821  bool res = true;
822  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
823  {
824  if ( !curve->transform( transformer ) )
825  {
826  res = false;
827  break;
828  }
829 
830  if ( feedback && feedback->isCanceled() )
831  {
832  res = false;
833  break;
834  }
835  }
836  clearCache();
837  return res;
838 }
839 
840 void QgsCompoundCurve::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
841 {
842  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
843  {
844  curve->filterVertices( filter );
845  }
846  clearCache();
847 }
848 
849 void QgsCompoundCurve::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
850 {
851  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
852  {
853  curve->transformVertices( transform );
854  }
855  clearCache();
856 }
857 
858 void QgsCompoundCurve::sumUpArea( double &sum ) const
859 {
860  for ( const QgsCurve *curve : mCurves )
861  {
862  curve->sumUpArea( sum );
863  }
864 }
865 
867 {
868  if ( numPoints() < 1 || isClosed() )
869  {
870  return;
871  }
872  addVertex( startPoint() );
873 }
874 
876 {
877  for ( const QgsCurve *curve : mCurves )
878  {
879  if ( curve->hasCurvedSegments() )
880  {
881  return true;
882  }
883  }
884  return false;
885 }
886 
888 {
889  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( vertex );
890  if ( curveIds.size() == 1 )
891  {
892  QgsCurve *curve = mCurves[curveIds.at( 0 ).first];
893  return curve->vertexAngle( curveIds.at( 0 ).second );
894  }
895  else if ( curveIds.size() > 1 )
896  {
897  QgsCurve *curve1 = mCurves[curveIds.at( 0 ).first];
898  QgsCurve *curve2 = mCurves[curveIds.at( 1 ).first];
899  double angle1 = curve1->vertexAngle( curveIds.at( 0 ).second );
900  double angle2 = curve2->vertexAngle( curveIds.at( 1 ).second );
901  return QgsGeometryUtils::averageAngle( angle1, angle2 );
902  }
903  else
904  {
905  return 0.0;
906  }
907 }
908 
909 double QgsCompoundCurve::segmentLength( QgsVertexId startVertex ) const
910 {
911  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( startVertex );
912  double length = 0.0;
913  for ( auto it = curveIds.constBegin(); it != curveIds.constEnd(); ++it )
914  {
915  length += mCurves.at( it->first )->segmentLength( it->second );
916  }
917  return length;
918 }
919 
921 {
923  for ( int i = mCurves.count() - 1; i >= 0; --i )
924  {
925  QgsCurve *reversedCurve = mCurves.at( i )->reversed();
926  clone->addCurve( reversedCurve );
927  }
928  return clone;
929 }
930 
931 QgsPoint *QgsCompoundCurve::interpolatePoint( const double distance ) const
932 {
933  if ( distance < 0 )
934  return nullptr;
935 
936  double distanceTraversed = 0;
937  for ( const QgsCurve *curve : mCurves )
938  {
939  const double thisCurveLength = curve->length();
940  if ( distanceTraversed + thisCurveLength > distance || qgsDoubleNear( distanceTraversed + thisCurveLength, distance ) )
941  {
942  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
943  const double distanceToPoint = std::min( distance - distanceTraversed, thisCurveLength );
944 
945  // point falls on this curve
946  return curve->interpolatePoint( distanceToPoint );
947  }
948 
949  distanceTraversed += thisCurveLength;
950  }
951 
952  return nullptr;
953 }
954 
955 QgsCompoundCurve *QgsCompoundCurve::curveSubstring( double startDistance, double endDistance ) const
956 {
957  if ( startDistance < 0 && endDistance < 0 )
958  return createEmptyWithSameType();
959 
960  endDistance = std::max( startDistance, endDistance );
961  std::unique_ptr< QgsCompoundCurve > substring = qgis::make_unique< QgsCompoundCurve >();
962 
963  double distanceTraversed = 0;
964  for ( const QgsCurve *curve : mCurves )
965  {
966  const double thisCurveLength = curve->length();
967  if ( distanceTraversed + thisCurveLength < startDistance )
968  {
969  // keep going - haven't found start yet, so no need to include this curve at all
970  }
971  else
972  {
973  std::unique_ptr< QgsCurve > part( curve->curveSubstring( startDistance - distanceTraversed, endDistance - distanceTraversed ) );
974  if ( part )
975  substring->addCurve( part.release() );
976  }
977 
978  distanceTraversed += thisCurveLength;
979  if ( distanceTraversed > endDistance )
980  break;
981  }
982 
983  return substring.release();
984 }
985 
986 bool QgsCompoundCurve::addZValue( double zValue )
987 {
988  if ( QgsWkbTypes::hasZ( mWkbType ) )
989  return false;
990 
992 
993  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
994  {
995  curve->addZValue( zValue );
996  }
997  clearCache();
998  return true;
999 }
1000 
1001 bool QgsCompoundCurve::addMValue( double mValue )
1002 {
1003  if ( QgsWkbTypes::hasM( mWkbType ) )
1004  return false;
1005 
1007 
1008  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
1009  {
1010  curve->addMValue( mValue );
1011  }
1012  clearCache();
1013  return true;
1014 }
1015 
1017 {
1018  if ( !QgsWkbTypes::hasZ( mWkbType ) )
1019  return false;
1020 
1022  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
1023  {
1024  curve->dropZValue();
1025  }
1026  clearCache();
1027  return true;
1028 }
1029 
1031 {
1032  if ( !QgsWkbTypes::hasM( mWkbType ) )
1033  return false;
1034 
1036  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
1037  {
1038  curve->dropMValue();
1039  }
1040  clearCache();
1041  return true;
1042 }
1043 
1045 {
1046  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
1047  {
1048  curve->swapXy();
1049  }
1050  clearCache();
1051 }
An abstract base class for classes which transform geometries by transforming input points to output ...
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.
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.
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...
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 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.
void addCurve(QgsCurve *c)
Adds a curve to the geometry (takes ownership)
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...
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.
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 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.
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 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:130
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:257
virtual bool isClosed() const SIP_HOLDGIL
Returns true if the curve is closed.
Definition: qgscurve.cpp:40
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:211
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
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:38
QgsPoint vertexAt(QgsVertexId) const override
Returns the point corresponding to a specified vertex id.
Definition: qgspoint.cpp:521
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
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:359
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:316
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.