QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 <QPainter>
25 #include <QPainterPath>
26 #include <memory>
27 
29 {
31 }
32 
34 {
35  clear();
36 }
37 
38 bool QgsCompoundCurve::equals( const QgsCurve &other ) const
39 {
40  const QgsCompoundCurve *otherCurve = qgsgeometry_cast< const QgsCompoundCurve * >( &other );
41  if ( !otherCurve )
42  return false;
43 
44  if ( mWkbType != otherCurve->mWkbType )
45  return false;
46 
47  if ( mCurves.size() != otherCurve->mCurves.size() )
48  return false;
49 
50  for ( int i = 0; i < mCurves.size(); ++i )
51  {
52  if ( *mCurves.at( i ) != *otherCurve->mCurves.at( i ) )
53  return false;
54  }
55 
56  return true;
57 }
58 
60 {
61  auto result = qgis::make_unique< QgsCompoundCurve >();
62  result->mWkbType = mWkbType;
63  return result.release();
64 }
65 
67 {
68  return QStringLiteral( "CompoundCurve" );
69 }
70 
72 {
73  return 1;
74 }
75 
77 {
78  mWkbType = curve.wkbType();
79  mCurves.reserve( curve.mCurves.size() );
80  for ( const QgsCurve *c : curve.mCurves )
81  {
82  mCurves.append( c->clone() );
83  }
84 }
85 
87 {
88  if ( &curve != this )
89  {
90  clearCache();
91  QgsCurve::operator=( curve );
92  for ( const QgsCurve *c : curve.mCurves )
93  {
94  mCurves.append( c->clone() );
95  }
96  }
97  return *this;
98 }
99 
101 {
102  return new QgsCompoundCurve( *this );
103 }
104 
106 {
108  qDeleteAll( mCurves );
109  mCurves.clear();
110  clearCache();
111 }
112 
114 {
115  if ( mCurves.empty() )
116  {
117  return QgsRectangle();
118  }
119 
120  QgsRectangle bbox = mCurves.at( 0 )->boundingBox();
121  for ( int i = 1; i < mCurves.size(); ++i )
122  {
123  QgsRectangle curveBox = mCurves.at( i )->boundingBox();
124  bbox.combineExtentWith( curveBox );
125  }
126  return bbox;
127 }
128 
130 {
131  clear();
132  if ( !wkbPtr )
133  {
134  return false;
135  }
136 
137  QgsWkbTypes::Type type = wkbPtr.readHeader();
139  {
140  return false;
141  }
142  mWkbType = type;
143 
144  int nCurves;
145  wkbPtr >> nCurves;
146  QgsCurve *currentCurve = nullptr;
147  for ( int i = 0; i < nCurves; ++i )
148  {
149  QgsWkbTypes::Type curveType = wkbPtr.readHeader();
150  wkbPtr -= 1 + sizeof( int );
151  if ( QgsWkbTypes::flatType( curveType ) == QgsWkbTypes::LineString )
152  {
153  currentCurve = new QgsLineString();
154  }
155  else if ( QgsWkbTypes::flatType( curveType ) == QgsWkbTypes::CircularString )
156  {
157  currentCurve = new QgsCircularString();
158  }
159  else
160  {
161  return false;
162  }
163  currentCurve->fromWkb( wkbPtr ); // also updates wkbPtr
164  mCurves.append( currentCurve );
165  }
166  return true;
167 }
168 
169 bool QgsCompoundCurve::fromWkt( const QString &wkt )
170 {
171  clear();
172 
173  QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
174 
175  if ( QgsWkbTypes::flatType( parts.first ) != QgsWkbTypes::CompoundCurve )
176  return false;
177  mWkbType = parts.first;
178 
179  QString defaultChildWkbType = QStringLiteral( "LineString%1%2" ).arg( is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
180 
181  const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
182  for ( const QString &childWkt : blocks )
183  {
184  QPair<QgsWkbTypes::Type, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
185 
186  if ( QgsWkbTypes::flatType( childParts.first ) == QgsWkbTypes::LineString )
187  mCurves.append( new QgsLineString() );
188  else if ( QgsWkbTypes::flatType( childParts.first ) == QgsWkbTypes::CircularString )
189  mCurves.append( new QgsCircularString() );
190  else
191  {
192  clear();
193  return false;
194  }
195  if ( !mCurves.back()->fromWkt( childWkt ) )
196  {
197  clear();
198  return false;
199  }
200  }
201 
202  //scan through curves and check if dimensionality of curves is different to compound curve.
203  //if so, update the type dimensionality of the compound curve to match
204  bool hasZ = false;
205  bool hasM = false;
206  for ( const QgsCurve *curve : qgis::as_const( mCurves ) )
207  {
208  hasZ = hasZ || curve->is3D();
209  hasM = hasM || curve->isMeasure();
210  if ( hasZ && hasM )
211  break;
212  }
213  if ( hasZ )
214  addZValue( 0 );
215  if ( hasM )
216  addMValue( 0 );
217 
218  return true;
219 }
220 
221 QByteArray QgsCompoundCurve::asWkb() const
222 {
223  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
224  QVector<QByteArray> wkbForCurves;
225  wkbForCurves.reserve( mCurves.size() );
226  for ( const QgsCurve *curve : mCurves )
227  {
228  QByteArray wkbForCurve = curve->asWkb();
229  binarySize += wkbForCurve.length();
230  wkbForCurves << wkbForCurve;
231  }
232 
233  QByteArray wkbArray;
234  wkbArray.resize( binarySize );
235  QgsWkbPtr wkb( wkbArray );
236  wkb << static_cast<char>( QgsApplication::endian() );
237  wkb << static_cast<quint32>( wkbType() );
238  wkb << static_cast<quint32>( mCurves.size() );
239  for ( const QByteArray &wkbForCurve : qgis::as_const( wkbForCurves ) )
240  {
241  wkb << wkbForCurve;
242  }
243  return wkbArray;
244 }
245 
246 QString QgsCompoundCurve::asWkt( int precision ) const
247 {
248  QString wkt = wktTypeStr() + " (";
249  for ( const QgsCurve *curve : mCurves )
250  {
251  QString childWkt = curve->asWkt( precision );
252  if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
253  {
254  // Type names of linear geometries are omitted
255  childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
256  }
257  wkt += childWkt + ',';
258  }
259  if ( wkt.endsWith( ',' ) )
260  {
261  wkt.chop( 1 );
262  }
263  wkt += ')';
264  return wkt;
265 }
266 
267 QDomElement QgsCompoundCurve::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
268 {
269  // GML2 does not support curves
270  std::unique_ptr< QgsLineString > line( curveToLine() );
271  QDomElement gml = line->asGml2( doc, precision, ns, axisOrder );
272  return gml;
273 }
274 
275 QDomElement QgsCompoundCurve::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
276 {
277  QDomElement compoundCurveElem = doc.createElementNS( ns, QStringLiteral( "CompositeCurve" ) );
278 
279  if ( isEmpty() )
280  return compoundCurveElem;
281 
282  for ( const QgsCurve *curve : mCurves )
283  {
284  QDomElement curveMemberElem = doc.createElementNS( ns, QStringLiteral( "curveMember" ) );
285  QDomElement curveElem = curve->asGml3( doc, precision, ns, axisOrder );
286  curveMemberElem.appendChild( curveElem );
287  compoundCurveElem.appendChild( curveMemberElem );
288  }
289 
290  return compoundCurveElem;
291 }
292 
294 {
295  // GeoJSON does not support curves
296  std::unique_ptr< QgsLineString > line( curveToLine() );
297  QString json = line->asJson( precision );
298  return json;
299 }
300 
302 {
303  double length = 0;
304  for ( const QgsCurve *curve : mCurves )
305  {
306  length += curve->length();
307  }
308  return length;
309 }
310 
312 {
313  if ( mCurves.empty() )
314  {
315  return QgsPoint();
316  }
317  return mCurves.at( 0 )->startPoint();
318 }
319 
321 {
322  if ( mCurves.empty() )
323  {
324  return QgsPoint();
325  }
326  return mCurves.at( mCurves.size() - 1 )->endPoint();
327 }
328 
330 {
331  pts.clear();
332  if ( mCurves.empty() )
333  {
334  return;
335  }
336 
337  mCurves[0]->points( pts );
338  for ( int i = 1; i < mCurves.size(); ++i )
339  {
340  QgsPointSequence pList;
341  mCurves[i]->points( pList );
342  pList.removeFirst(); //first vertex already added in previous line
343  pts.append( pList );
344  }
345 }
346 
348 {
349  int nPoints = 0;
350  int nCurves = mCurves.size();
351  if ( nCurves < 1 )
352  {
353  return 0;
354  }
355 
356  for ( int i = 0; i < nCurves; ++i )
357  {
358  nPoints += mCurves.at( i )->numPoints() - 1; //last vertex is equal to first of next section
359  }
360  nPoints += 1; //last vertex was removed above
361  return nPoints;
362 }
363 
365 {
366  if ( mCurves.isEmpty() )
367  return true;
368 
369  for ( QgsCurve *curve : mCurves )
370  {
371  if ( !curve->isEmpty() )
372  return false;
373  }
374  return true;
375 }
376 
378 {
379  QgsLineString *line = new QgsLineString();
380  std::unique_ptr< QgsLineString > currentLine;
381  for ( const QgsCurve *curve : mCurves )
382  {
383  currentLine.reset( curve->curveToLine( tolerance, toleranceType ) );
384  line->append( currentLine.get() );
385  }
386  return line;
387 }
388 
389 QgsCompoundCurve *QgsCompoundCurve::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
390 {
391  std::unique_ptr<QgsCompoundCurve> result( createEmptyWithSameType() );
392 
393  for ( QgsCurve *curve : mCurves )
394  {
395  std::unique_ptr<QgsCurve> gridified( static_cast< QgsCurve * >( curve->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) ) );
396  if ( gridified )
397  {
398  result->mCurves.append( gridified.release() );
399  }
400  }
401 
402  if ( result->mCurves.empty() )
403  return nullptr;
404  else
405  return result.release();
406 }
407 
408 bool QgsCompoundCurve::removeDuplicateNodes( double epsilon, bool useZValues )
409 {
410  bool result = false;
411  const QVector< QgsCurve * > curves = mCurves;
412  int i = 0;
413  QgsPoint lastEnd;
414  for ( QgsCurve *curve : curves )
415  {
416  result = result || curve->removeDuplicateNodes( epsilon, useZValues );
417  if ( curve->numPoints() == 0 || qgsDoubleNear( curve->length(), 0.0, epsilon ) )
418  {
419  // empty curve, remove it
420  delete mCurves.takeAt( i );
421  result = true;
422  }
423  else
424  {
425  // ensure this line starts exactly where previous line ended
426  if ( i > 0 )
427  {
428  curve->moveVertex( QgsVertexId( -1, -1, 0 ), lastEnd );
429  }
430  lastEnd = curve->vertexAt( QgsVertexId( -1, -1, curve->numPoints() - 1 ) );
431  }
432  i++;
433  }
434  return result;
435 }
436 
437 const QgsCurve *QgsCompoundCurve::curveAt( int i ) const
438 {
439  if ( i < 0 || i >= mCurves.size() )
440  {
441  return nullptr;
442  }
443  return mCurves.at( i );
444 }
445 
447 {
448  if ( c )
449  {
450  if ( mCurves.empty() )
451  {
453  }
454 
455  mCurves.append( c );
456 
458  {
459  c->addZValue();
460  }
461  else if ( !QgsWkbTypes::hasZ( mWkbType ) && QgsWkbTypes::hasZ( c->wkbType() ) )
462  {
463  c->dropZValue();
464  }
466  {
467  c->addMValue();
468  }
469  else if ( !QgsWkbTypes::hasM( mWkbType ) && QgsWkbTypes::hasM( c->wkbType() ) )
470  {
471  c->dropMValue();
472  }
473  clearCache();
474  }
475 }
476 
478 {
479  if ( i < 0 || i >= mCurves.size() )
480  {
481  return;
482  }
483 
484  delete mCurves.takeAt( i );
485  clearCache();
486 }
487 
489 {
490  if ( mCurves.isEmpty() || mWkbType == QgsWkbTypes::Unknown )
491  {
493  }
494 
495  //is last curve QgsLineString
496  QgsCurve *lastCurve = nullptr;
497  if ( !mCurves.isEmpty() )
498  {
499  lastCurve = mCurves.at( mCurves.size() - 1 );
500  }
501 
502  QgsLineString *line = nullptr;
503  if ( !lastCurve || QgsWkbTypes::flatType( lastCurve->wkbType() ) != QgsWkbTypes::LineString )
504  {
505  line = new QgsLineString();
506  mCurves.append( line );
507  if ( lastCurve )
508  {
509  line->addVertex( lastCurve->endPoint() );
510  }
511  lastCurve = line;
512  }
513  else //create new QgsLineString* with point in it
514  {
515  line = static_cast<QgsLineString *>( lastCurve );
516  }
517  line->addVertex( pt );
518  clearCache();
519 }
520 
521 void QgsCompoundCurve::draw( QPainter &p ) const
522 {
523  for ( const QgsCurve *curve : mCurves )
524  {
525  curve->draw( p );
526  }
527 }
528 
530 {
531  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
532  {
533  curve->transform( ct, d, transformZ );
534  }
535  clearCache();
536 }
537 
538 void QgsCompoundCurve::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
539 {
540  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
541  {
542  curve->transform( t, zTranslate, zScale, mTranslate, mScale );
543  }
544  clearCache();
545 }
546 
547 void QgsCompoundCurve::addToPainterPath( QPainterPath &path ) const
548 {
549  QPainterPath pp;
550  for ( const QgsCurve *curve : mCurves )
551  {
552  curve->addToPainterPath( pp );
553  }
554  path.addPath( pp );
555 }
556 
557 void QgsCompoundCurve::drawAsPolygon( QPainter &p ) const
558 {
559  QPainterPath pp;
560  for ( const QgsCurve *curve : mCurves )
561  {
562  curve->addToPainterPath( pp );
563  }
564  p.drawPath( pp );
565 }
566 
567 bool QgsCompoundCurve::insertVertex( QgsVertexId position, const QgsPoint &vertex )
568 {
569  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
570  if ( curveIds.empty() )
571  {
572  return false;
573  }
574  int curveId = curveIds.at( 0 ).first;
575  if ( curveId >= mCurves.size() )
576  {
577  return false;
578  }
579 
580  bool success = mCurves.at( curveId )->insertVertex( curveIds.at( 0 ).second, vertex );
581  if ( success )
582  {
583  clearCache(); //bbox changed
584  }
585  return success;
586 }
587 
588 bool QgsCompoundCurve::moveVertex( QgsVertexId position, const QgsPoint &newPos )
589 {
590  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
591  QVector< QPair<int, QgsVertexId> >::const_iterator idIt = curveIds.constBegin();
592  for ( ; idIt != curveIds.constEnd(); ++idIt )
593  {
594  mCurves.at( idIt->first )->moveVertex( idIt->second, newPos );
595  }
596 
597  bool success = !curveIds.isEmpty();
598  if ( success )
599  {
600  clearCache(); //bbox changed
601  }
602  return success;
603 }
604 
606 {
607  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
608  if ( curveIds.size() == 1 )
609  {
610  if ( !mCurves.at( curveIds.at( 0 ).first )->deleteVertex( curveIds.at( 0 ).second ) )
611  {
612  clearCache(); //bbox may have changed
613  return false;
614  }
615  if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() == 0 )
616  {
617  removeCurve( curveIds.at( 0 ).first );
618  }
619  }
620  else if ( curveIds.size() == 2 )
621  {
622  Q_ASSERT( curveIds.at( 1 ).first == curveIds.at( 0 ).first + 1 );
623  Q_ASSERT( curveIds.at( 0 ).second.vertex == mCurves.at( curveIds.at( 0 ).first )->numPoints() - 1 );
624  Q_ASSERT( curveIds.at( 1 ).second.vertex == 0 );
625  QgsPoint startPoint = mCurves.at( curveIds.at( 0 ).first ) ->startPoint();
626  QgsPoint endPoint = mCurves.at( curveIds.at( 1 ).first ) ->endPoint();
627  if ( QgsWkbTypes::flatType( mCurves.at( curveIds.at( 0 ).first )->wkbType() ) == QgsWkbTypes::LineString &&
628  QgsWkbTypes::flatType( mCurves.at( curveIds.at( 1 ).first )->wkbType() ) == QgsWkbTypes::CircularString &&
629  mCurves.at( curveIds.at( 1 ).first )->numPoints() > 3 )
630  {
631  QgsPoint intermediatePoint;
633  mCurves.at( curveIds.at( 1 ).first ) ->pointAt( 2, intermediatePoint, type );
634  mCurves.at( curveIds.at( 0 ).first )->moveVertex(
635  QgsVertexId( 0, 0, mCurves.at( curveIds.at( 0 ).first )->numPoints() - 1 ), intermediatePoint );
636  }
637  else if ( !mCurves.at( curveIds.at( 0 ).first )->deleteVertex( curveIds.at( 0 ).second ) )
638  {
639  clearCache(); //bbox may have changed
640  return false;
641  }
642  if ( QgsWkbTypes::flatType( mCurves.at( curveIds.at( 0 ).first )->wkbType() ) == QgsWkbTypes::CircularString &&
643  mCurves.at( curveIds.at( 0 ).first )->numPoints() > 0 &&
644  QgsWkbTypes::flatType( mCurves.at( curveIds.at( 1 ).first )->wkbType() ) == QgsWkbTypes::LineString )
645  {
646  QgsPoint intermediatePoint = mCurves.at( curveIds.at( 0 ).first ) ->endPoint();
647  mCurves.at( curveIds.at( 1 ).first )->moveVertex( QgsVertexId( 0, 0, 0 ), intermediatePoint );
648  }
649  else if ( !mCurves.at( curveIds.at( 1 ).first )->deleteVertex( curveIds.at( 1 ).second ) )
650  {
651  clearCache(); //bbox may have changed
652  return false;
653  }
654  if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() == 0 &&
655  mCurves.at( curveIds.at( 1 ).first )->numPoints() != 0 )
656  {
657  mCurves.at( curveIds.at( 1 ).first )->moveVertex( QgsVertexId( 0, 0, 0 ), startPoint );
658  removeCurve( curveIds.at( 0 ).first );
659  }
660  else if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() != 0 &&
661  mCurves.at( curveIds.at( 1 ).first )->numPoints() == 0 )
662  {
663  mCurves.at( curveIds.at( 0 ).first )->moveVertex(
664  QgsVertexId( 0, 0, mCurves.at( curveIds.at( 0 ).first )->numPoints() - 1 ), endPoint );
665  removeCurve( curveIds.at( 1 ).first );
666  }
667  else if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() == 0 &&
668  mCurves.at( curveIds.at( 1 ).first )->numPoints() == 0 )
669  {
670  removeCurve( curveIds.at( 1 ).first );
671  removeCurve( curveIds.at( 0 ).first );
672  QgsLineString *line = new QgsLineString();
673  line->insertVertex( QgsVertexId( 0, 0, 0 ), startPoint );
674  line->insertVertex( QgsVertexId( 0, 0, 1 ), endPoint );
675  mCurves.insert( curveIds.at( 0 ).first, line );
676  }
677  else
678  {
679  QgsPoint endPointOfFirst = mCurves.at( curveIds.at( 0 ).first ) ->endPoint();
680  QgsPoint startPointOfSecond = mCurves.at( curveIds.at( 1 ).first ) ->startPoint();
681  if ( endPointOfFirst != startPointOfSecond )
682  {
683  QgsLineString *line = new QgsLineString();
684  line->insertVertex( QgsVertexId( 0, 0, 0 ), endPointOfFirst );
685  line->insertVertex( QgsVertexId( 0, 0, 1 ), startPointOfSecond );
686  mCurves.insert( curveIds.at( 1 ).first, line );
687  }
688  }
689  }
690 
691  bool success = !curveIds.isEmpty();
692  if ( success )
693  {
694  clearCache(); //bbox changed
695  }
696  return success;
697 }
698 
699 QVector< QPair<int, QgsVertexId> > QgsCompoundCurve::curveVertexId( QgsVertexId id ) const
700 {
701  QVector< QPair<int, QgsVertexId> > curveIds;
702 
703  int currentVertexIndex = 0;
704  for ( int i = 0; i < mCurves.size(); ++i )
705  {
706  int increment = mCurves.at( i )->numPoints() - 1;
707  if ( id.vertex >= currentVertexIndex && id.vertex <= currentVertexIndex + increment )
708  {
709  int curveVertexId = id.vertex - currentVertexIndex;
710  QgsVertexId vid;
711  vid.part = 0;
712  vid.ring = 0;
713  vid.vertex = curveVertexId;
714  curveIds.append( qMakePair( i, vid ) );
715  if ( curveVertexId == increment && i < ( mCurves.size() - 1 ) ) //add first vertex of next curve
716  {
717  vid.vertex = 0;
718  curveIds.append( qMakePair( i + 1, vid ) );
719  }
720  break;
721  }
722  currentVertexIndex += increment;
723  }
724 
725  return curveIds;
726 }
727 
728 double QgsCompoundCurve::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
729 {
730  return QgsGeometryUtils::closestSegmentFromComponents( mCurves, QgsGeometryUtils::Vertex, pt, segmentPt, vertexAfter, leftOf, epsilon );
731 }
732 
733 bool QgsCompoundCurve::pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const
734 {
735  int currentVertexId = 0;
736  for ( int j = 0; j < mCurves.size(); ++j )
737  {
738  int nCurvePoints = mCurves.at( j )->numPoints();
739  if ( ( node - currentVertexId ) < nCurvePoints )
740  {
741  return ( mCurves.at( j )->pointAt( node - currentVertexId, point, type ) );
742  }
743  currentVertexId += ( nCurvePoints - 1 );
744  }
745  return false;
746 }
747 
748 double QgsCompoundCurve::xAt( int index ) const
749 {
750  int currentVertexId = 0;
751  for ( int j = 0; j < mCurves.size(); ++j )
752  {
753  int nCurvePoints = mCurves.at( j )->numPoints();
754  if ( ( index - currentVertexId ) < nCurvePoints )
755  {
756  return mCurves.at( j )->xAt( index - currentVertexId );
757  }
758  currentVertexId += ( nCurvePoints - 1 );
759  }
760  return 0.0;
761 }
762 
763 double QgsCompoundCurve::yAt( int index ) const
764 {
765  int currentVertexId = 0;
766  for ( int j = 0; j < mCurves.size(); ++j )
767  {
768  int nCurvePoints = mCurves.at( j )->numPoints();
769  if ( ( index - currentVertexId ) < nCurvePoints )
770  {
771  return mCurves.at( j )->yAt( index - currentVertexId );
772  }
773  currentVertexId += ( nCurvePoints - 1 );
774  }
775  return 0.0;
776 }
777 
778 void QgsCompoundCurve::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
779 {
780  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
781  {
782  curve->filterVertices( filter );
783  }
784  clearCache();
785 }
786 
787 void QgsCompoundCurve::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
788 {
789  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
790  {
791  curve->transformVertices( transform );
792  }
793  clearCache();
794 }
795 
796 void QgsCompoundCurve::sumUpArea( double &sum ) const
797 {
798  for ( const QgsCurve *curve : mCurves )
799  {
800  curve->sumUpArea( sum );
801  }
802 }
803 
805 {
806  if ( numPoints() < 1 || isClosed() )
807  {
808  return;
809  }
810  addVertex( startPoint() );
811 }
812 
814 {
815  for ( const QgsCurve *curve : mCurves )
816  {
817  if ( curve->hasCurvedSegments() )
818  {
819  return true;
820  }
821  }
822  return false;
823 }
824 
826 {
827  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( vertex );
828  if ( curveIds.size() == 1 )
829  {
830  QgsCurve *curve = mCurves[curveIds.at( 0 ).first];
831  return curve->vertexAngle( curveIds.at( 0 ).second );
832  }
833  else if ( curveIds.size() > 1 )
834  {
835  QgsCurve *curve1 = mCurves[curveIds.at( 0 ).first];
836  QgsCurve *curve2 = mCurves[curveIds.at( 1 ).first];
837  double angle1 = curve1->vertexAngle( curveIds.at( 0 ).second );
838  double angle2 = curve2->vertexAngle( curveIds.at( 1 ).second );
839  return QgsGeometryUtils::averageAngle( angle1, angle2 );
840  }
841  else
842  {
843  return 0.0;
844  }
845 }
846 
847 double QgsCompoundCurve::segmentLength( QgsVertexId startVertex ) const
848 {
849  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( startVertex );
850  double length = 0.0;
851  for ( auto it = curveIds.constBegin(); it != curveIds.constEnd(); ++it )
852  {
853  length += mCurves.at( it->first )->segmentLength( it->second );
854  }
855  return length;
856 }
857 
859 {
861  for ( int i = mCurves.count() - 1; i >= 0; --i )
862  {
863  QgsCurve *reversedCurve = mCurves.at( i )->reversed();
864  clone->addCurve( reversedCurve );
865  }
866  return clone;
867 }
868 
869 QgsPoint *QgsCompoundCurve::interpolatePoint( const double distance ) const
870 {
871  if ( distance < 0 )
872  return nullptr;
873 
874  double distanceTraversed = 0;
875  for ( const QgsCurve *curve : mCurves )
876  {
877  const double thisCurveLength = curve->length();
878  if ( distanceTraversed + thisCurveLength > distance || qgsDoubleNear( distanceTraversed + thisCurveLength, distance ) )
879  {
880  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
881  const double distanceToPoint = std::min( distance - distanceTraversed, thisCurveLength );
882 
883  // point falls on this curve
884  return curve->interpolatePoint( distanceToPoint );
885  }
886 
887  distanceTraversed += thisCurveLength;
888  }
889 
890  return nullptr;
891 }
892 
893 QgsCompoundCurve *QgsCompoundCurve::curveSubstring( double startDistance, double endDistance ) const
894 {
895  if ( startDistance < 0 && endDistance < 0 )
896  return createEmptyWithSameType();
897 
898  endDistance = std::max( startDistance, endDistance );
899  std::unique_ptr< QgsCompoundCurve > substring = qgis::make_unique< QgsCompoundCurve >();
900 
901  double distanceTraversed = 0;
902  for ( const QgsCurve *curve : mCurves )
903  {
904  const double thisCurveLength = curve->length();
905  if ( distanceTraversed + thisCurveLength < startDistance )
906  {
907  // keep going - haven't found start yet, so no need to include this curve at all
908  }
909  else
910  {
911  std::unique_ptr< QgsCurve > part( curve->curveSubstring( startDistance - distanceTraversed, endDistance - distanceTraversed ) );
912  if ( part )
913  substring->addCurve( part.release() );
914  }
915 
916  distanceTraversed += thisCurveLength;
917  if ( distanceTraversed > endDistance )
918  break;
919  }
920 
921  return substring.release();
922 }
923 
924 bool QgsCompoundCurve::addZValue( double zValue )
925 {
926  if ( QgsWkbTypes::hasZ( mWkbType ) )
927  return false;
928 
930 
931  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
932  {
933  curve->addZValue( zValue );
934  }
935  clearCache();
936  return true;
937 }
938 
939 bool QgsCompoundCurve::addMValue( double mValue )
940 {
941  if ( QgsWkbTypes::hasM( mWkbType ) )
942  return false;
943 
945 
946  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
947  {
948  curve->addMValue( mValue );
949  }
950  clearCache();
951  return true;
952 }
953 
955 {
956  if ( !QgsWkbTypes::hasZ( mWkbType ) )
957  return false;
958 
960  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
961  {
962  curve->dropZValue();
963  }
964  clearCache();
965  return true;
966 }
967 
969 {
970  if ( !QgsWkbTypes::hasM( mWkbType ) )
971  return false;
972 
974  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
975  {
976  curve->dropMValue();
977  }
978  clearCache();
979  return true;
980 }
981 
983 {
984  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
985  {
986  curve->swapXy();
987  }
988  clearCache();
989 }
990 
QgsPoint startPoint() const override
Returns the starting point of the curve.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
int precision
void append(const QgsLineString *line)
Appends the contents of another line string to the end of this line string.
QgsCompoundCurve * clone() const override
Clones the geometry by performing a deep copy.
A rectangle specified with double values.
Definition: qgsrectangle.h:40
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
void addToPainterPath(QPainterPath &path) const override
Adds a curve to a painter path.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
QgsAbstractGeometry & operator=(const QgsAbstractGeometry &geom)
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
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...
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 (...
int dimension() const override
Returns the inherent dimension of the geometry.
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the average angle (in radians) between the two linear segments from (x1, y1) to (x2, y2) and (x2, y2) to (x3, y3).
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:278
void swapXy() override
Swaps the x and y coordinates from 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.
QString asJson(int precision=17) const override
Returns a GeoJSON representation of the geometry.
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate angle at a vertex.
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgscurve.cpp:224
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
void clear() override
Clears the geometry, ie reset it to a null geometry.
static endian_t endian()
Returns whether this machine uses big or little endian.
QgsCompoundCurve * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership...
const QgsCurve * curveAt(int i) const
Returns the curve at the specified index.
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:906
QgsCompoundCurve & operator=(const QgsCompoundCurve &curve)
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 Type dropM(Type type)
Drops the m dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1076
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...
void addCurve(QgsCurve *c)
Adds a curve to the geometry (takes ownership)
QgsPoint vertexAt(QgsVertexId) const override
Returns the point corresponding to a specified vertex id.
Definition: qgspoint.cpp:448
QgsWkbTypes::Type mWkbType
bool pointAt(int node, QgsPoint &point, QgsVertexId::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
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
virtual QgsPoint endPoint() const =0
Returns the end point of the curve.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
bool isMeasure() const
Returns true if the geometry contains m values.
static Type addM(Type type)
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1027
double length() const override
Returns the length of the geometry.
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
Utility class for identifying a unique vertex within a 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...
QString geometryType() const override
Returns a unique string representing the geometry type.
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
int nCurves() const
Returns the number of curves in the geometry.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
static Type addZ(Type type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1002
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
virtual bool isClosed() const
Returns true if the curve is closed.
Definition: qgscurve.cpp:39
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
QgsPoint * interpolatePoint(double distance) const override
Returns an interpolated point on the curve at the specified distance.
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:53
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
AxisOrder
Axis order for GML generation.
void removeCurve(int i)
Removes a curve from the geometry.
virtual double length() const
Returns the length of the geometry.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
bool isEmpty() const override
Returns true if the geometry is empty.
QVector< QgsPoint > QgsPointSequence
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle...
Definition: qgsrectangle.h:358
static Type dropZ(Type type)
Drops the z dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1058
void drawAsPolygon(QPainter &p) const override
Draws the curve as a polygon on the specified QPainter.
void addVertex(const QgsPoint &pt)
Adds a vertex to the end of the geometry.
bool equals(const QgsCurve &other) const override
Checks whether this curve exactly equals another curve.
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
QgsCompoundCurve * reversed() const override
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
QgsCompoundCurve * curveSubstring(double startDistance, double endDistance) const override
Returns a new curve representing a substring of this curve.
QgsRectangle calculateBoundingBox() const override
Default calculator for the minimal bounding box for the geometry.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
Class for doing transforms between two map coordinate systems.
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
QByteArray asWkb() const override
Returns a WKB representation of 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.
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:120
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:956
Compound curve geometry type.
Circular string geometry type.
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...
void close()
Appends first point if not already closed.
virtual bool dropMValue()=0
Drops any measure values which exist in the geometry.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
~QgsCompoundCurve() override
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...
bool dropMValue() override
Drops any measure values which exist in the geometry.
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:565
int numPoints() const override
Returns the number of points in the curve.
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether &#39;thepoint&#39; is left or right of the line from &#39;p1&#39; to &#39;p2&#39;. Negativ values mean left a...
Definition: MathUtils.cpp:292
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 insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
virtual bool dropZValue()=0
Drops any z-dimensions which exist in the geometry.
static double closestSegmentFromComponents(T &container, ComponentType ctype, const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon)
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
QgsPoint endPoint() const override
Returns the end point of the curve.
virtual bool fromWkb(QgsConstWkbPtr &wkb)=0
Sets the geometry from a WKB string.