QGIS API Documentation  3.0.2-Girona (307d082)
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  for ( const QgsCurve *c : curve.mCurves )
80  {
81  mCurves.append( static_cast<QgsCurve *>( c->clone() ) );
82  }
83 }
84 
86 {
87  if ( &curve != this )
88  {
89  clearCache();
90  QgsCurve::operator=( curve );
91  for ( const QgsCurve *c : curve.mCurves )
92  {
93  mCurves.append( static_cast<QgsCurve *>( c->clone() ) );
94  }
95  }
96  return *this;
97 }
98 
100 {
101  return new QgsCompoundCurve( *this );
102 }
103 
105 {
107  qDeleteAll( mCurves );
108  mCurves.clear();
109  clearCache();
110 }
111 
113 {
114  if ( mCurves.empty() )
115  {
116  return QgsRectangle();
117  }
118 
119  QgsRectangle bbox = mCurves.at( 0 )->boundingBox();
120  for ( int i = 1; i < mCurves.size(); ++i )
121  {
122  QgsRectangle curveBox = mCurves.at( i )->boundingBox();
123  bbox.combineExtentWith( curveBox );
124  }
125  return bbox;
126 }
127 
129 {
130  clear();
131  if ( !wkbPtr )
132  {
133  return false;
134  }
135 
136  QgsWkbTypes::Type type = wkbPtr.readHeader();
138  {
139  return false;
140  }
141  mWkbType = type;
142 
143  int nCurves;
144  wkbPtr >> nCurves;
145  QgsCurve *currentCurve = nullptr;
146  for ( int i = 0; i < nCurves; ++i )
147  {
148  QgsWkbTypes::Type curveType = wkbPtr.readHeader();
149  wkbPtr -= 1 + sizeof( int );
150  if ( QgsWkbTypes::flatType( curveType ) == QgsWkbTypes::LineString )
151  {
152  currentCurve = new QgsLineString();
153  }
154  else if ( QgsWkbTypes::flatType( curveType ) == QgsWkbTypes::CircularString )
155  {
156  currentCurve = new QgsCircularString();
157  }
158  else
159  {
160  return false;
161  }
162  currentCurve->fromWkb( wkbPtr ); // also updates wkbPtr
163  mCurves.append( currentCurve );
164  }
165  return true;
166 }
167 
168 bool QgsCompoundCurve::fromWkt( const QString &wkt )
169 {
170  clear();
171 
172  QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
173 
174  if ( QgsWkbTypes::flatType( parts.first ) != QgsWkbTypes::CompoundCurve )
175  return false;
176  mWkbType = parts.first;
177 
178  QString defaultChildWkbType = QStringLiteral( "LineString%1%2" ).arg( is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
179 
180  const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
181  for ( const QString &childWkt : blocks )
182  {
183  QPair<QgsWkbTypes::Type, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
184 
185  if ( QgsWkbTypes::flatType( childParts.first ) == QgsWkbTypes::LineString )
186  mCurves.append( new QgsLineString() );
187  else if ( QgsWkbTypes::flatType( childParts.first ) == QgsWkbTypes::CircularString )
188  mCurves.append( new QgsCircularString() );
189  else
190  {
191  clear();
192  return false;
193  }
194  if ( !mCurves.back()->fromWkt( childWkt ) )
195  {
196  clear();
197  return false;
198  }
199  }
200 
201  //scan through curves and check if dimensionality of curves is different to compound curve.
202  //if so, update the type dimensionality of the compound curve to match
203  bool hasZ = false;
204  bool hasM = false;
205  for ( const QgsCurve *curve : qgis::as_const( mCurves ) )
206  {
207  hasZ = hasZ || curve->is3D();
208  hasM = hasM || curve->isMeasure();
209  if ( hasZ && hasM )
210  break;
211  }
212  if ( hasZ )
213  addZValue( 0 );
214  if ( hasM )
215  addMValue( 0 );
216 
217  return true;
218 }
219 
220 QByteArray QgsCompoundCurve::asWkb() const
221 {
222  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
223  QVector<QByteArray> wkbForCurves;
224  for ( const QgsCurve *curve : mCurves )
225  {
226  QByteArray wkbForCurve = curve->asWkb();
227  binarySize += wkbForCurve.length();
228  wkbForCurves << wkbForCurve;
229  }
230 
231  QByteArray wkbArray;
232  wkbArray.resize( binarySize );
233  QgsWkbPtr wkb( wkbArray );
234  wkb << static_cast<char>( QgsApplication::endian() );
235  wkb << static_cast<quint32>( wkbType() );
236  wkb << static_cast<quint32>( mCurves.size() );
237  for ( const QByteArray &wkbForCurve : qgis::as_const( wkbForCurves ) )
238  {
239  wkb << wkbForCurve;
240  }
241  return wkbArray;
242 }
243 
244 QString QgsCompoundCurve::asWkt( int precision ) const
245 {
246  QString wkt = wktTypeStr() + " (";
247  for ( const QgsCurve *curve : mCurves )
248  {
249  QString childWkt = curve->asWkt( precision );
250  if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
251  {
252  // Type names of linear geometries are omitted
253  childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
254  }
255  wkt += childWkt + ',';
256  }
257  if ( wkt.endsWith( ',' ) )
258  {
259  wkt.chop( 1 );
260  }
261  wkt += ')';
262  return wkt;
263 }
264 
265 QDomElement QgsCompoundCurve::asGml2( QDomDocument &doc, int precision, const QString &ns ) const
266 {
267  // GML2 does not support curves
268  std::unique_ptr< QgsLineString > line( curveToLine() );
269  QDomElement gml = line->asGml2( doc, precision, ns );
270  return gml;
271 }
272 
273 QDomElement QgsCompoundCurve::asGml3( QDomDocument &doc, int precision, const QString &ns ) const
274 {
275  QDomElement compoundCurveElem = doc.createElementNS( ns, QStringLiteral( "CompositeCurve" ) );
276 
277  if ( isEmpty() )
278  return compoundCurveElem;
279 
280  for ( const QgsCurve *curve : mCurves )
281  {
282  QDomElement curveMemberElem = doc.createElementNS( ns, QStringLiteral( "curveMember" ) );
283  QDomElement curveElem = curve->asGml3( doc, precision, ns );
284  curveMemberElem.appendChild( curveElem );
285  compoundCurveElem.appendChild( curveMemberElem );
286  }
287 
288  return compoundCurveElem;
289 }
290 
291 QString QgsCompoundCurve::asJson( int precision ) const
292 {
293  // GeoJSON does not support curves
294  std::unique_ptr< QgsLineString > line( curveToLine() );
295  QString json = line->asJson( precision );
296  return json;
297 }
298 
300 {
301  double length = 0;
302  for ( const QgsCurve *curve : mCurves )
303  {
304  length += curve->length();
305  }
306  return length;
307 }
308 
310 {
311  if ( mCurves.empty() )
312  {
313  return QgsPoint();
314  }
315  return mCurves.at( 0 )->startPoint();
316 }
317 
319 {
320  if ( mCurves.empty() )
321  {
322  return QgsPoint();
323  }
324  return mCurves.at( mCurves.size() - 1 )->endPoint();
325 }
326 
328 {
329  pts.clear();
330  if ( mCurves.empty() )
331  {
332  return;
333  }
334 
335  mCurves[0]->points( pts );
336  for ( int i = 1; i < mCurves.size(); ++i )
337  {
338  QgsPointSequence pList;
339  mCurves[i]->points( pList );
340  pList.removeFirst(); //first vertex already added in previous line
341  pts.append( pList );
342  }
343 }
344 
346 {
347  int nPoints = 0;
348  int nCurves = mCurves.size();
349  if ( nCurves < 1 )
350  {
351  return 0;
352  }
353 
354  for ( int i = 0; i < nCurves; ++i )
355  {
356  nPoints += mCurves.at( i )->numPoints() - 1; //last vertex is equal to first of next section
357  }
358  nPoints += 1; //last vertex was removed above
359  return nPoints;
360 }
361 
363 {
364  if ( mCurves.isEmpty() )
365  return true;
366 
367  for ( QgsCurve *curve : mCurves )
368  {
369  if ( !curve->isEmpty() )
370  return false;
371  }
372  return true;
373 }
374 
376 {
377  QgsLineString *line = new QgsLineString();
378  std::unique_ptr< QgsLineString > currentLine;
379  for ( const QgsCurve *curve : mCurves )
380  {
381  currentLine.reset( curve->curveToLine( tolerance, toleranceType ) );
382  line->append( currentLine.get() );
383  }
384  return line;
385 }
386 
387 QgsCompoundCurve *QgsCompoundCurve::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
388 {
389  std::unique_ptr<QgsCompoundCurve> result( createEmptyWithSameType() );
390 
391  for ( QgsCurve *curve : mCurves )
392  {
393  std::unique_ptr<QgsCurve> gridified( static_cast< QgsCurve * >( curve->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) ) );
394  if ( gridified )
395  {
396  result->mCurves.append( gridified.release() );
397  }
398  }
399 
400  if ( result->mCurves.empty() )
401  return nullptr;
402  else
403  return result.release();
404 }
405 
406 bool QgsCompoundCurve::removeDuplicateNodes( double epsilon, bool useZValues )
407 {
408  bool result = false;
409  const QVector< QgsCurve * > curves = mCurves;
410  int i = 0;
411  QgsPoint lastEnd;
412  for ( QgsCurve *curve : curves )
413  {
414  result = result || curve->removeDuplicateNodes( epsilon, useZValues );
415  if ( curve->numPoints() == 0 || qgsDoubleNear( curve->length(), 0.0, epsilon ) )
416  {
417  // empty curve, remove it
418  delete mCurves.takeAt( i );
419  result = true;
420  }
421  else
422  {
423  // ensure this line starts exactly where previous line ended
424  if ( i > 0 )
425  {
426  curve->moveVertex( QgsVertexId( -1, -1, 0 ), lastEnd );
427  }
428  lastEnd = curve->vertexAt( QgsVertexId( -1, -1, curve->numPoints() - 1 ) );
429  }
430  i++;
431  }
432  return result;
433 }
434 
435 const QgsCurve *QgsCompoundCurve::curveAt( int i ) const
436 {
437  if ( i < 0 || i >= mCurves.size() )
438  {
439  return nullptr;
440  }
441  return mCurves.at( i );
442 }
443 
445 {
446  if ( c )
447  {
448  if ( mCurves.empty() )
449  {
451  }
452 
453  mCurves.append( c );
454 
456  {
457  c->addZValue();
458  }
459  else if ( !QgsWkbTypes::hasZ( mWkbType ) && QgsWkbTypes::hasZ( c->wkbType() ) )
460  {
461  c->dropZValue();
462  }
464  {
465  c->addMValue();
466  }
467  else if ( !QgsWkbTypes::hasM( mWkbType ) && QgsWkbTypes::hasM( c->wkbType() ) )
468  {
469  c->dropMValue();
470  }
471  clearCache();
472  }
473 }
474 
476 {
477  if ( i < 0 || i >= mCurves.size() )
478  {
479  return;
480  }
481 
482  delete mCurves.takeAt( i );
483  clearCache();
484 }
485 
487 {
488  if ( mCurves.isEmpty() || mWkbType == QgsWkbTypes::Unknown )
489  {
491  }
492 
493  //is last curve QgsLineString
494  QgsCurve *lastCurve = nullptr;
495  if ( !mCurves.isEmpty() )
496  {
497  lastCurve = mCurves.at( mCurves.size() - 1 );
498  }
499 
500  QgsLineString *line = nullptr;
501  if ( !lastCurve || QgsWkbTypes::flatType( lastCurve->wkbType() ) != QgsWkbTypes::LineString )
502  {
503  line = new QgsLineString();
504  mCurves.append( line );
505  if ( lastCurve )
506  {
507  line->addVertex( lastCurve->endPoint() );
508  }
509  lastCurve = line;
510  }
511  else //create new QgsLineString* with point in it
512  {
513  line = static_cast<QgsLineString *>( lastCurve );
514  }
515  line->addVertex( pt );
516  clearCache();
517 }
518 
519 void QgsCompoundCurve::draw( QPainter &p ) const
520 {
521  for ( const QgsCurve *curve : mCurves )
522  {
523  curve->draw( p );
524  }
525 }
526 
528 {
529  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
530  {
531  curve->transform( ct, d, transformZ );
532  }
533  clearCache();
534 }
535 
536 void QgsCompoundCurve::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
537 {
538  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
539  {
540  curve->transform( t, zTranslate, zScale, mTranslate, mScale );
541  }
542  clearCache();
543 }
544 
545 void QgsCompoundCurve::addToPainterPath( QPainterPath &path ) const
546 {
547  QPainterPath pp;
548  for ( const QgsCurve *curve : mCurves )
549  {
550  curve->addToPainterPath( pp );
551  }
552  path.addPath( pp );
553 }
554 
555 void QgsCompoundCurve::drawAsPolygon( QPainter &p ) const
556 {
557  QPainterPath pp;
558  for ( const QgsCurve *curve : mCurves )
559  {
560  curve->addToPainterPath( pp );
561  }
562  p.drawPath( pp );
563 }
564 
565 bool QgsCompoundCurve::insertVertex( QgsVertexId position, const QgsPoint &vertex )
566 {
567  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
568  if ( curveIds.empty() )
569  {
570  return false;
571  }
572  int curveId = curveIds.at( 0 ).first;
573  if ( curveId >= mCurves.size() )
574  {
575  return false;
576  }
577 
578  bool success = mCurves.at( curveId )->insertVertex( curveIds.at( 0 ).second, vertex );
579  if ( success )
580  {
581  clearCache(); //bbox changed
582  }
583  return success;
584 }
585 
586 bool QgsCompoundCurve::moveVertex( QgsVertexId position, const QgsPoint &newPos )
587 {
588  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
589  QVector< QPair<int, QgsVertexId> >::const_iterator idIt = curveIds.constBegin();
590  for ( ; idIt != curveIds.constEnd(); ++idIt )
591  {
592  mCurves.at( idIt->first )->moveVertex( idIt->second, newPos );
593  }
594 
595  bool success = !curveIds.isEmpty();
596  if ( success )
597  {
598  clearCache(); //bbox changed
599  }
600  return success;
601 }
602 
604 {
605  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( position );
606  if ( curveIds.size() == 1 )
607  {
608  if ( !mCurves.at( curveIds.at( 0 ).first )->deleteVertex( curveIds.at( 0 ).second ) )
609  {
610  clearCache(); //bbox may have changed
611  return false;
612  }
613  if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() == 0 )
614  {
615  removeCurve( curveIds.at( 0 ).first );
616  }
617  }
618  else if ( curveIds.size() == 2 )
619  {
620  Q_ASSERT( curveIds.at( 1 ).first == curveIds.at( 0 ).first + 1 );
621  Q_ASSERT( curveIds.at( 0 ).second.vertex == mCurves.at( curveIds.at( 0 ).first )->numPoints() - 1 );
622  Q_ASSERT( curveIds.at( 1 ).second.vertex == 0 );
623  QgsPoint startPoint = mCurves.at( curveIds.at( 0 ).first ) ->startPoint();
624  QgsPoint endPoint = mCurves.at( curveIds.at( 1 ).first ) ->endPoint();
625  if ( QgsWkbTypes::flatType( mCurves.at( curveIds.at( 0 ).first )->wkbType() ) == QgsWkbTypes::LineString &&
626  QgsWkbTypes::flatType( mCurves.at( curveIds.at( 1 ).first )->wkbType() ) == QgsWkbTypes::CircularString &&
627  mCurves.at( curveIds.at( 1 ).first )->numPoints() > 3 )
628  {
629  QgsPoint intermediatePoint;
631  mCurves.at( curveIds.at( 1 ).first ) ->pointAt( 2, intermediatePoint, type );
632  mCurves.at( curveIds.at( 0 ).first )->moveVertex(
633  QgsVertexId( 0, 0, mCurves.at( curveIds.at( 0 ).first )->numPoints() - 1 ), intermediatePoint );
634  }
635  else if ( !mCurves.at( curveIds.at( 0 ).first )->deleteVertex( curveIds.at( 0 ).second ) )
636  {
637  clearCache(); //bbox may have changed
638  return false;
639  }
640  if ( QgsWkbTypes::flatType( mCurves.at( curveIds.at( 0 ).first )->wkbType() ) == QgsWkbTypes::CircularString &&
641  mCurves.at( curveIds.at( 0 ).first )->numPoints() > 0 &&
642  QgsWkbTypes::flatType( mCurves.at( curveIds.at( 1 ).first )->wkbType() ) == QgsWkbTypes::LineString )
643  {
644  QgsPoint intermediatePoint = mCurves.at( curveIds.at( 0 ).first ) ->endPoint();
645  mCurves.at( curveIds.at( 1 ).first )->moveVertex( QgsVertexId( 0, 0, 0 ), intermediatePoint );
646  }
647  else if ( !mCurves.at( curveIds.at( 1 ).first )->deleteVertex( curveIds.at( 1 ).second ) )
648  {
649  clearCache(); //bbox may have changed
650  return false;
651  }
652  if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() == 0 &&
653  mCurves.at( curveIds.at( 1 ).first )->numPoints() != 0 )
654  {
655  mCurves.at( curveIds.at( 1 ).first )->moveVertex( QgsVertexId( 0, 0, 0 ), startPoint );
656  removeCurve( curveIds.at( 0 ).first );
657  }
658  else if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() != 0 &&
659  mCurves.at( curveIds.at( 1 ).first )->numPoints() == 0 )
660  {
661  mCurves.at( curveIds.at( 0 ).first )->moveVertex(
662  QgsVertexId( 0, 0, mCurves.at( curveIds.at( 0 ).first )->numPoints() - 1 ), endPoint );
663  removeCurve( curveIds.at( 1 ).first );
664  }
665  else if ( mCurves.at( curveIds.at( 0 ).first )->numPoints() == 0 &&
666  mCurves.at( curveIds.at( 1 ).first )->numPoints() == 0 )
667  {
668  removeCurve( curveIds.at( 1 ).first );
669  removeCurve( curveIds.at( 0 ).first );
670  QgsLineString *line = new QgsLineString();
671  line->insertVertex( QgsVertexId( 0, 0, 0 ), startPoint );
672  line->insertVertex( QgsVertexId( 0, 0, 1 ), endPoint );
673  mCurves.insert( curveIds.at( 0 ).first, line );
674  }
675  else
676  {
677  QgsPoint endPointOfFirst = mCurves.at( curveIds.at( 0 ).first ) ->endPoint();
678  QgsPoint startPointOfSecond = mCurves.at( curveIds.at( 1 ).first ) ->startPoint();
679  if ( endPointOfFirst != startPointOfSecond )
680  {
681  QgsLineString *line = new QgsLineString();
682  line->insertVertex( QgsVertexId( 0, 0, 0 ), endPointOfFirst );
683  line->insertVertex( QgsVertexId( 0, 0, 1 ), startPointOfSecond );
684  mCurves.insert( curveIds.at( 1 ).first, line );
685  }
686  }
687  }
688 
689  bool success = !curveIds.isEmpty();
690  if ( success )
691  {
692  clearCache(); //bbox changed
693  }
694  return success;
695 }
696 
697 QVector< QPair<int, QgsVertexId> > QgsCompoundCurve::curveVertexId( QgsVertexId id ) const
698 {
699  QVector< QPair<int, QgsVertexId> > curveIds;
700 
701  int currentVertexIndex = 0;
702  for ( int i = 0; i < mCurves.size(); ++i )
703  {
704  int increment = mCurves.at( i )->numPoints() - 1;
705  if ( id.vertex >= currentVertexIndex && id.vertex <= currentVertexIndex + increment )
706  {
707  int curveVertexId = id.vertex - currentVertexIndex;
708  QgsVertexId vid;
709  vid.part = 0;
710  vid.ring = 0;
711  vid.vertex = curveVertexId;
712  curveIds.append( qMakePair( i, vid ) );
713  if ( curveVertexId == increment && i < ( mCurves.size() - 1 ) ) //add first vertex of next curve
714  {
715  vid.vertex = 0;
716  curveIds.append( qMakePair( i + 1, vid ) );
717  }
718  break;
719  }
720  currentVertexIndex += increment;
721  }
722 
723  return curveIds;
724 }
725 
726 double QgsCompoundCurve::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
727 {
728  return QgsGeometryUtils::closestSegmentFromComponents( mCurves, QgsGeometryUtils::Vertex, pt, segmentPt, vertexAfter, leftOf, epsilon );
729 }
730 
731 bool QgsCompoundCurve::pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const
732 {
733  int currentVertexId = 0;
734  for ( int j = 0; j < mCurves.size(); ++j )
735  {
736  int nCurvePoints = mCurves.at( j )->numPoints();
737  if ( ( node - currentVertexId ) < nCurvePoints )
738  {
739  return ( mCurves.at( j )->pointAt( node - currentVertexId, point, type ) );
740  }
741  currentVertexId += ( nCurvePoints - 1 );
742  }
743  return false;
744 }
745 
746 double QgsCompoundCurve::xAt( int index ) const
747 {
748  int currentVertexId = 0;
749  for ( int j = 0; j < mCurves.size(); ++j )
750  {
751  int nCurvePoints = mCurves.at( j )->numPoints();
752  if ( ( index - currentVertexId ) < nCurvePoints )
753  {
754  return mCurves.at( j )->xAt( index - currentVertexId );
755  }
756  currentVertexId += ( nCurvePoints - 1 );
757  }
758  return 0.0;
759 }
760 
761 double QgsCompoundCurve::yAt( int index ) const
762 {
763  int currentVertexId = 0;
764  for ( int j = 0; j < mCurves.size(); ++j )
765  {
766  int nCurvePoints = mCurves.at( j )->numPoints();
767  if ( ( index - currentVertexId ) < nCurvePoints )
768  {
769  return mCurves.at( j )->yAt( index - currentVertexId );
770  }
771  currentVertexId += ( nCurvePoints - 1 );
772  }
773  return 0.0;
774 }
775 
776 void QgsCompoundCurve::sumUpArea( double &sum ) const
777 {
778  for ( const QgsCurve *curve : mCurves )
779  {
780  curve->sumUpArea( sum );
781  }
782 }
783 
785 {
786  if ( numPoints() < 1 || isClosed() )
787  {
788  return;
789  }
790  addVertex( startPoint() );
791 }
792 
794 {
795  for ( const QgsCurve *curve : mCurves )
796  {
797  if ( curve->hasCurvedSegments() )
798  {
799  return true;
800  }
801  }
802  return false;
803 }
804 
806 {
807  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( vertex );
808  if ( curveIds.size() == 1 )
809  {
810  QgsCurve *curve = mCurves[curveIds.at( 0 ).first];
811  return curve->vertexAngle( curveIds.at( 0 ).second );
812  }
813  else if ( curveIds.size() > 1 )
814  {
815  QgsCurve *curve1 = mCurves[curveIds.at( 0 ).first];
816  QgsCurve *curve2 = mCurves[curveIds.at( 1 ).first];
817  double angle1 = curve1->vertexAngle( curveIds.at( 0 ).second );
818  double angle2 = curve2->vertexAngle( curveIds.at( 1 ).second );
819  return QgsGeometryUtils::averageAngle( angle1, angle2 );
820  }
821  else
822  {
823  return 0.0;
824  }
825 }
826 
827 double QgsCompoundCurve::segmentLength( QgsVertexId startVertex ) const
828 {
829  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( startVertex );
830  double length = 0.0;
831  for ( auto it = curveIds.constBegin(); it != curveIds.constEnd(); ++it )
832  {
833  length += mCurves.at( it->first )->segmentLength( it->second );
834  }
835  return length;
836 }
837 
839 {
841  for ( int i = mCurves.count() - 1; i >= 0; --i )
842  {
843  QgsCurve *reversedCurve = mCurves.at( i )->reversed();
844  clone->addCurve( reversedCurve );
845  }
846  return clone;
847 }
848 
849 bool QgsCompoundCurve::addZValue( double zValue )
850 {
851  if ( QgsWkbTypes::hasZ( mWkbType ) )
852  return false;
853 
855 
856  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
857  {
858  curve->addZValue( zValue );
859  }
860  clearCache();
861  return true;
862 }
863 
864 bool QgsCompoundCurve::addMValue( double mValue )
865 {
866  if ( QgsWkbTypes::hasM( mWkbType ) )
867  return false;
868 
870 
871  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
872  {
873  curve->addMValue( mValue );
874  }
875  clearCache();
876  return true;
877 }
878 
880 {
881  if ( !QgsWkbTypes::hasZ( mWkbType ) )
882  return false;
883 
885  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
886  {
887  curve->dropZValue();
888  }
889  clearCache();
890  return true;
891 }
892 
894 {
895  if ( !QgsWkbTypes::hasM( mWkbType ) )
896  return false;
897 
899  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
900  {
901  curve->dropMValue();
902  }
903  clearCache();
904  return true;
905 }
906 
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.
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:39
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.
QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml") const override
Returns a GML2 representation of the geometry.
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)
Angle between two linear segments.
void sumUpArea(double &sum) const override
Sums up the area of the curve by iterating over the vertices (shoelace formula).
bool removeDuplicateNodes(double epsilon=4 *DBL_EPSILON, bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
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.
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:203
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.
bool removeDuplicateNodes(double epsilon=4 *DBL_EPSILON, bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
Definition: qgspoint.cpp:144
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:768
QgsCompoundCurve & operator=(const QgsCompoundCurve &curve)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:251
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:938
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:464
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.
virtual QgsPoint endPoint() const =0
Returns the end point of the curve.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:67
static Type addM(Type type)
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:889
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.
Utility class for identifying a unique vertex within a geometry.
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:864
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.
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.
virtual bool isClosed() const
Returns true if the curve is closed.
Definition: qgscurve.cpp:39
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)
Expand the rectangle so that covers both the original rectangle and the given rectangle.
static Type dropZ(Type type)
Drops the z dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:920
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.
QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml") const override
Returns a GML3 representation 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:41
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.
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:818
Compound curve geometry type.
Circular string geometry type.
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *DBL_EPSILON) const override
Searches for the closest segment of the geometry to a given point.
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
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:427
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:53
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
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)
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.