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