QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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  currentVertexIndex += increment;
734  }
735 
736  return curveIds;
737 }
738 
739 double QgsCompoundCurve::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
740 {
741  return QgsGeometryUtils::closestSegmentFromComponents( mCurves, QgsGeometryUtils::Vertex, pt, segmentPt, vertexAfter, leftOf, epsilon );
742 }
743 
744 bool QgsCompoundCurve::pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const
745 {
746  int currentVertexId = 0;
747  for ( int j = 0; j < mCurves.size(); ++j )
748  {
749  int nCurvePoints = mCurves.at( j )->numPoints();
750  if ( ( node - currentVertexId ) < nCurvePoints )
751  {
752  return ( mCurves.at( j )->pointAt( node - currentVertexId, point, type ) );
753  }
754  currentVertexId += ( nCurvePoints - 1 );
755  }
756  return false;
757 }
758 
759 double QgsCompoundCurve::xAt( int index ) const
760 {
761  int currentVertexId = 0;
762  for ( int j = 0; j < mCurves.size(); ++j )
763  {
764  int nCurvePoints = mCurves.at( j )->numPoints();
765  if ( ( index - currentVertexId ) < nCurvePoints )
766  {
767  return mCurves.at( j )->xAt( index - currentVertexId );
768  }
769  currentVertexId += ( nCurvePoints - 1 );
770  }
771  return 0.0;
772 }
773 
774 double QgsCompoundCurve::yAt( int index ) const
775 {
776  int currentVertexId = 0;
777  for ( int j = 0; j < mCurves.size(); ++j )
778  {
779  int nCurvePoints = mCurves.at( j )->numPoints();
780  if ( ( index - currentVertexId ) < nCurvePoints )
781  {
782  return mCurves.at( j )->yAt( index - currentVertexId );
783  }
784  currentVertexId += ( nCurvePoints - 1 );
785  }
786  return 0.0;
787 }
788 
789 void QgsCompoundCurve::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
790 {
791  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
792  {
793  curve->filterVertices( filter );
794  }
795  clearCache();
796 }
797 
798 void QgsCompoundCurve::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
799 {
800  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
801  {
802  curve->transformVertices( transform );
803  }
804  clearCache();
805 }
806 
807 void QgsCompoundCurve::sumUpArea( double &sum ) const
808 {
809  for ( const QgsCurve *curve : mCurves )
810  {
811  curve->sumUpArea( sum );
812  }
813 }
814 
816 {
817  if ( numPoints() < 1 || isClosed() )
818  {
819  return;
820  }
821  addVertex( startPoint() );
822 }
823 
825 {
826  for ( const QgsCurve *curve : mCurves )
827  {
828  if ( curve->hasCurvedSegments() )
829  {
830  return true;
831  }
832  }
833  return false;
834 }
835 
837 {
838  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( vertex );
839  if ( curveIds.size() == 1 )
840  {
841  QgsCurve *curve = mCurves[curveIds.at( 0 ).first];
842  return curve->vertexAngle( curveIds.at( 0 ).second );
843  }
844  else if ( curveIds.size() > 1 )
845  {
846  QgsCurve *curve1 = mCurves[curveIds.at( 0 ).first];
847  QgsCurve *curve2 = mCurves[curveIds.at( 1 ).first];
848  double angle1 = curve1->vertexAngle( curveIds.at( 0 ).second );
849  double angle2 = curve2->vertexAngle( curveIds.at( 1 ).second );
850  return QgsGeometryUtils::averageAngle( angle1, angle2 );
851  }
852  else
853  {
854  return 0.0;
855  }
856 }
857 
858 double QgsCompoundCurve::segmentLength( QgsVertexId startVertex ) const
859 {
860  QVector< QPair<int, QgsVertexId> > curveIds = curveVertexId( startVertex );
861  double length = 0.0;
862  for ( auto it = curveIds.constBegin(); it != curveIds.constEnd(); ++it )
863  {
864  length += mCurves.at( it->first )->segmentLength( it->second );
865  }
866  return length;
867 }
868 
870 {
872  for ( int i = mCurves.count() - 1; i >= 0; --i )
873  {
874  QgsCurve *reversedCurve = mCurves.at( i )->reversed();
875  clone->addCurve( reversedCurve );
876  }
877  return clone;
878 }
879 
880 QgsPoint *QgsCompoundCurve::interpolatePoint( const double distance ) const
881 {
882  if ( distance < 0 )
883  return nullptr;
884 
885  double distanceTraversed = 0;
886  for ( const QgsCurve *curve : mCurves )
887  {
888  const double thisCurveLength = curve->length();
889  if ( distanceTraversed + thisCurveLength > distance || qgsDoubleNear( distanceTraversed + thisCurveLength, distance ) )
890  {
891  // point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
892  const double distanceToPoint = std::min( distance - distanceTraversed, thisCurveLength );
893 
894  // point falls on this curve
895  return curve->interpolatePoint( distanceToPoint );
896  }
897 
898  distanceTraversed += thisCurveLength;
899  }
900 
901  return nullptr;
902 }
903 
904 QgsCompoundCurve *QgsCompoundCurve::curveSubstring( double startDistance, double endDistance ) const
905 {
906  if ( startDistance < 0 && endDistance < 0 )
907  return createEmptyWithSameType();
908 
909  endDistance = std::max( startDistance, endDistance );
910  std::unique_ptr< QgsCompoundCurve > substring = qgis::make_unique< QgsCompoundCurve >();
911 
912  double distanceTraversed = 0;
913  for ( const QgsCurve *curve : mCurves )
914  {
915  const double thisCurveLength = curve->length();
916  if ( distanceTraversed + thisCurveLength < startDistance )
917  {
918  // keep going - haven't found start yet, so no need to include this curve at all
919  }
920  else
921  {
922  std::unique_ptr< QgsCurve > part( curve->curveSubstring( startDistance - distanceTraversed, endDistance - distanceTraversed ) );
923  if ( part )
924  substring->addCurve( part.release() );
925  }
926 
927  distanceTraversed += thisCurveLength;
928  if ( distanceTraversed > endDistance )
929  break;
930  }
931 
932  return substring.release();
933 }
934 
935 bool QgsCompoundCurve::addZValue( double zValue )
936 {
937  if ( QgsWkbTypes::hasZ( mWkbType ) )
938  return false;
939 
941 
942  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
943  {
944  curve->addZValue( zValue );
945  }
946  clearCache();
947  return true;
948 }
949 
950 bool QgsCompoundCurve::addMValue( double mValue )
951 {
952  if ( QgsWkbTypes::hasM( mWkbType ) )
953  return false;
954 
956 
957  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
958  {
959  curve->addMValue( mValue );
960  }
961  clearCache();
962  return true;
963 }
964 
966 {
967  if ( !QgsWkbTypes::hasZ( mWkbType ) )
968  return false;
969 
971  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
972  {
973  curve->dropZValue();
974  }
975  clearCache();
976  return true;
977 }
978 
980 {
981  if ( !QgsWkbTypes::hasM( mWkbType ) )
982  return false;
983 
985  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
986  {
987  curve->dropMValue();
988  }
989  clearCache();
990  return true;
991 }
992 
994 {
995  for ( QgsCurve *curve : qgis::as_const( mCurves ) )
996  {
997  curve->swapXy();
998  }
999  clearCache();
1000 }
1001 
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:280
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:245
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:480
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;. 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.