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