QGIS API Documentation  3.8.0-Zanzibar (11aff65)
qgscurvepolygon.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscurvepolygon.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 "qgscurvepolygon.h"
19 #include "qgsapplication.h"
20 #include "qgscircularstring.h"
21 #include "qgscompoundcurve.h"
22 #include "qgsgeometryutils.h"
23 #include "qgslinestring.h"
24 #include "qgspolygon.h"
25 #include "qgswkbptr.h"
26 #include "qgsmulticurve.h"
27 
28 #include <QJsonArray>
29 #include <QJsonObject>
30 #include <QPainter>
31 #include <QPainterPath>
32 #include <memory>
33 #include <nlohmann/json.hpp>
34 
36 {
38 }
39 
41 {
42  clear();
43 }
44 
46 {
47  auto result = qgis::make_unique< QgsCurvePolygon >();
48  result->mWkbType = mWkbType;
49  return result.release();
50 }
51 
53 {
54  return QStringLiteral( "CurvePolygon" );
55 }
56 
58 {
59  return 2;
60 }
61 
63  : QgsSurface( p )
64 
65 {
66  mWkbType = p.mWkbType;
67  if ( p.mExteriorRing )
68  {
69  mExteriorRing.reset( p.mExteriorRing->clone() );
70  }
71 
72  for ( const QgsCurve *ring : p.mInteriorRings )
73  {
74  mInteriorRings.push_back( ring->clone() );
75  }
76 }
77 
79 {
80  if ( &p != this )
81  {
82  clearCache();
84  if ( p.mExteriorRing )
85  {
86  mExteriorRing.reset( p.mExteriorRing->clone() );
87  }
88 
89  for ( const QgsCurve *ring : p.mInteriorRings )
90  {
91  mInteriorRings.push_back( ring->clone() );
92  }
93  }
94  return *this;
95 }
96 
98 {
99  const QgsCurvePolygon *otherPolygon = qgsgeometry_cast< const QgsCurvePolygon * >( &other );
100  if ( !otherPolygon )
101  return false;
102 
103  //run cheap checks first
104  if ( mWkbType != otherPolygon->mWkbType )
105  return false;
106 
107  if ( ( !mExteriorRing && otherPolygon->mExteriorRing ) || ( mExteriorRing && !otherPolygon->mExteriorRing ) )
108  return false;
109 
110  if ( mInteriorRings.count() != otherPolygon->mInteriorRings.count() )
111  return false;
112 
113  // compare rings
114  if ( mExteriorRing && otherPolygon->mExteriorRing )
115  {
116  if ( *mExteriorRing != *otherPolygon->mExteriorRing )
117  return false;
118  }
119 
120  for ( int i = 0; i < mInteriorRings.count(); ++i )
121  {
122  if ( ( !mInteriorRings.at( i ) && otherPolygon->mInteriorRings.at( i ) ) ||
123  ( mInteriorRings.at( i ) && !otherPolygon->mInteriorRings.at( i ) ) )
124  return false;
125 
126  if ( mInteriorRings.at( i ) && otherPolygon->mInteriorRings.at( i ) &&
127  *mInteriorRings.at( i ) != *otherPolygon->mInteriorRings.at( i ) )
128  return false;
129  }
130 
131  return true;
132 }
133 
135 {
136  return !operator==( other );
137 }
138 
140 {
141  return new QgsCurvePolygon( *this );
142 }
143 
145 {
147  mExteriorRing.reset();
148  qDeleteAll( mInteriorRings );
149  mInteriorRings.clear();
150  clearCache();
151 }
152 
153 
155 {
156  clear();
157  if ( !wkbPtr )
158  {
159  return false;
160  }
161 
162  QgsWkbTypes::Type type = wkbPtr.readHeader();
164  {
165  return false;
166  }
167  mWkbType = type;
168 
169  int nRings;
170  wkbPtr >> nRings;
171  std::unique_ptr< QgsCurve > currentCurve;
172  for ( int i = 0; i < nRings; ++i )
173  {
174  QgsWkbTypes::Type curveType = wkbPtr.readHeader();
175  wkbPtr -= 1 + sizeof( int );
176  QgsWkbTypes::Type flatCurveType = QgsWkbTypes::flatType( curveType );
177  if ( flatCurveType == QgsWkbTypes::LineString )
178  {
179  currentCurve.reset( new QgsLineString() );
180  }
181  else if ( flatCurveType == QgsWkbTypes::CircularString )
182  {
183  currentCurve.reset( new QgsCircularString() );
184  }
185  else if ( flatCurveType == QgsWkbTypes::CompoundCurve )
186  {
187  currentCurve.reset( new QgsCompoundCurve() );
188  }
189  else
190  {
191  return false;
192  }
193  currentCurve->fromWkb( wkbPtr ); // also updates wkbPtr
194  if ( i == 0 )
195  {
196  mExteriorRing = std::move( currentCurve );
197  }
198  else
199  {
200  mInteriorRings.append( currentCurve.release() );
201  }
202  }
203 
204  return true;
205 }
206 
207 bool QgsCurvePolygon::fromWkt( const QString &wkt )
208 {
209  clear();
210 
211  QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
212 
214  return false;
215 
216  mWkbType = parts.first;
217 
218  QString defaultChildWkbType = QStringLiteral( "LineString%1%2" ).arg( is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
219 
220  const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
221  for ( const QString &childWkt : blocks )
222  {
223  QPair<QgsWkbTypes::Type, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
224 
225  QgsWkbTypes::Type flatCurveType = QgsWkbTypes::flatType( childParts.first );
226  if ( flatCurveType == QgsWkbTypes::LineString )
227  mInteriorRings.append( new QgsLineString() );
228  else if ( flatCurveType == QgsWkbTypes::CircularString )
229  mInteriorRings.append( new QgsCircularString() );
230  else if ( flatCurveType == QgsWkbTypes::CompoundCurve )
231  mInteriorRings.append( new QgsCompoundCurve() );
232  else
233  {
234  clear();
235  return false;
236  }
237  if ( !mInteriorRings.back()->fromWkt( childWkt ) )
238  {
239  clear();
240  return false;
241  }
242  }
243 
244  if ( mInteriorRings.isEmpty() )
245  {
246  clear();
247  return false;
248  }
249 
250  mExteriorRing.reset( mInteriorRings.takeFirst() );
251 
252  //scan through rings and check if dimensionality of rings is different to CurvePolygon.
253  //if so, update the type dimensionality of the CurvePolygon to match
254  bool hasZ = false;
255  bool hasM = false;
256  if ( mExteriorRing )
257  {
258  hasZ = hasZ || mExteriorRing->is3D();
259  hasM = hasM || mExteriorRing->isMeasure();
260  }
261  for ( const QgsCurve *curve : qgis::as_const( mInteriorRings ) )
262  {
263  hasZ = hasZ || curve->is3D();
264  hasM = hasM || curve->isMeasure();
265  if ( hasZ && hasM )
266  break;
267  }
268  if ( hasZ )
269  addZValue( 0 );
270  if ( hasM )
271  addMValue( 0 );
272 
273  return true;
274 }
275 
277 {
278  if ( mExteriorRing )
279  {
280  return mExteriorRing->boundingBox();
281  }
282  return QgsRectangle();
283 }
284 
285 QByteArray QgsCurvePolygon::asWkb() const
286 {
287  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
288  QVector<QByteArray> wkbForRings;
289  wkbForRings.reserve( 1 + mInteriorRings.size() );
290  if ( mExteriorRing )
291  {
292  QByteArray wkb( mExteriorRing->asWkb() );
293  binarySize += wkb.length();
294  wkbForRings << wkb;
295  }
296  for ( const QgsCurve *curve : mInteriorRings )
297  {
298  QByteArray wkb( curve->asWkb() );
299  binarySize += wkb.length();
300  wkbForRings << wkb;
301  }
302 
303  QByteArray wkbArray;
304  wkbArray.resize( binarySize );
305  QgsWkbPtr wkbPtr( wkbArray );
306  wkbPtr << static_cast<char>( QgsApplication::endian() );
307  wkbPtr << static_cast<quint32>( wkbType() );
308  wkbPtr << static_cast<quint32>( wkbForRings.count() );
309  for ( const QByteArray &wkb : qgis::as_const( wkbForRings ) )
310  {
311  wkbPtr << wkb;
312  }
313  return wkbArray;
314 }
315 
316 QString QgsCurvePolygon::asWkt( int precision ) const
317 {
318  QString wkt = wktTypeStr() + QLatin1String( " (" );
319  if ( mExteriorRing )
320  {
321  QString childWkt = mExteriorRing->asWkt( precision );
322  if ( qgsgeometry_cast<QgsLineString *>( mExteriorRing.get() ) )
323  {
324  // Type names of linear geometries are omitted
325  childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
326  }
327  wkt += childWkt + ',';
328  }
329  for ( const QgsCurve *curve : mInteriorRings )
330  {
331  QString childWkt = curve->asWkt( precision );
332  if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
333  {
334  // Type names of linear geometries are omitted
335  childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
336  }
337  wkt += childWkt + ',';
338  }
339  if ( wkt.endsWith( ',' ) )
340  {
341  wkt.chop( 1 ); // Remove last ','
342  }
343  wkt += ')';
344  return wkt;
345 }
346 
347 QDomElement QgsCurvePolygon::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
348 {
349  // GML2 does not support curves
350  QDomElement elemPolygon = doc.createElementNS( ns, QStringLiteral( "Polygon" ) );
351 
352  if ( isEmpty() )
353  return elemPolygon;
354 
355  QDomElement elemOuterBoundaryIs = doc.createElementNS( ns, QStringLiteral( "outerBoundaryIs" ) );
356  std::unique_ptr< QgsLineString > exteriorLineString( exteriorRing()->curveToLine() );
357  QDomElement outerRing = exteriorLineString->asGml2( doc, precision, ns, axisOrder );
358  outerRing.toElement().setTagName( QStringLiteral( "LinearRing" ) );
359  elemOuterBoundaryIs.appendChild( outerRing );
360  elemPolygon.appendChild( elemOuterBoundaryIs );
361  std::unique_ptr< QgsLineString > interiorLineString;
362  for ( int i = 0, n = numInteriorRings(); i < n; ++i )
363  {
364  QDomElement elemInnerBoundaryIs = doc.createElementNS( ns, QStringLiteral( "innerBoundaryIs" ) );
365  interiorLineString.reset( interiorRing( i )->curveToLine() );
366  QDomElement innerRing = interiorLineString->asGml2( doc, precision, ns, axisOrder );
367  innerRing.toElement().setTagName( QStringLiteral( "LinearRing" ) );
368  elemInnerBoundaryIs.appendChild( innerRing );
369  elemPolygon.appendChild( elemInnerBoundaryIs );
370  }
371  return elemPolygon;
372 }
373 
374 QDomElement QgsCurvePolygon::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
375 {
376  QDomElement elemCurvePolygon = doc.createElementNS( ns, QStringLiteral( "Polygon" ) );
377 
378  if ( isEmpty() )
379  return elemCurvePolygon;
380 
381  QDomElement elemExterior = doc.createElementNS( ns, QStringLiteral( "exterior" ) );
382  QDomElement curveElem = exteriorRing()->asGml3( doc, precision, ns, axisOrder );
383  if ( curveElem.tagName() == QLatin1String( "LineString" ) )
384  {
385  curveElem.setTagName( QStringLiteral( "LinearRing" ) );
386  }
387  elemExterior.appendChild( curveElem );
388  elemCurvePolygon.appendChild( elemExterior );
389 
390  for ( int i = 0, n = numInteriorRings(); i < n; ++i )
391  {
392  QDomElement elemInterior = doc.createElementNS( ns, QStringLiteral( "interior" ) );
393  QDomElement innerRing = interiorRing( i )->asGml3( doc, precision, ns, axisOrder );
394  if ( innerRing.tagName() == QLatin1String( "LineString" ) )
395  {
396  innerRing.setTagName( QStringLiteral( "LinearRing" ) );
397  }
398  elemInterior.appendChild( innerRing );
399  elemCurvePolygon.appendChild( elemInterior );
400  }
401  return elemCurvePolygon;
402 }
403 
405 {
406  json coordinates( json::array( ) );
407  if ( exteriorRing() )
408  {
409  std::unique_ptr< QgsLineString > exteriorLineString( exteriorRing()->curveToLine() );
410  QgsPointSequence exteriorPts;
411  exteriorLineString->points( exteriorPts );
412  coordinates.push_back( QgsGeometryUtils::pointsToJson( exteriorPts, precision ) );
413 
414  std::unique_ptr< QgsLineString > interiorLineString;
415  for ( int i = 0, n = numInteriorRings(); i < n; ++i )
416  {
417  interiorLineString.reset( interiorRing( i )->curveToLine() );
418  QgsPointSequence interiorPts;
419  interiorLineString->points( interiorPts );
420  coordinates.push_back( QgsGeometryUtils::pointsToJson( interiorPts, precision ) );
421  }
422  }
423  return
424  {
425  { "type", "Polygon" },
426  { "coordinates", coordinates }
427  };
428 }
429 
430 double QgsCurvePolygon::area() const
431 {
432  if ( !mExteriorRing )
433  {
434  return 0.0;
435  }
436 
437  double totalArea = 0.0;
438 
439  if ( mExteriorRing->isRing() )
440  {
441  double area = 0.0;
442  mExteriorRing->sumUpArea( area );
443  totalArea += std::fabs( area );
444  }
445 
446  for ( const QgsCurve *ring : mInteriorRings )
447  {
448  double area = 0.0;
449  if ( ring->isRing() )
450  {
451  ring->sumUpArea( area );
452  totalArea -= std::fabs( area );
453  }
454  }
455  return totalArea;
456 }
457 
459 {
460  if ( !mExteriorRing )
461  return 0.0;
462 
463  //sum perimeter of rings
464  double perimeter = mExteriorRing->length();
465  for ( const QgsCurve *ring : mInteriorRings )
466  {
467  perimeter += ring->length();
468  }
469  return perimeter;
470 }
471 
473 {
474  std::unique_ptr< QgsPolygon > polygon( new QgsPolygon() );
475  if ( !mExteriorRing )
476  return polygon.release();
477 
478  polygon->setExteriorRing( exteriorRing()->curveToLine() );
479  QVector<QgsCurve *> interiors;
480  int n = numInteriorRings();
481  interiors.reserve( n );
482  for ( int i = 0; i < n; ++i )
483  {
484  interiors.append( interiorRing( i )->curveToLine() );
485  }
486  polygon->setInteriorRings( interiors );
487  return polygon.release();
488 }
489 
491 {
492  if ( !mExteriorRing )
493  return nullptr;
494 
495  if ( mInteriorRings.isEmpty() )
496  {
497  return mExteriorRing->clone();
498  }
499  else
500  {
501  QgsMultiCurve *multiCurve = new QgsMultiCurve();
502  multiCurve->addGeometry( mExteriorRing->clone() );
503  int nInteriorRings = mInteriorRings.size();
504  for ( int i = 0; i < nInteriorRings; ++i )
505  {
506  multiCurve->addGeometry( mInteriorRings.at( i )->clone() );
507  }
508  return multiCurve;
509  }
510 }
511 
512 QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
513 {
514  if ( !mExteriorRing )
515  return nullptr;
516 
517 
518  std::unique_ptr< QgsCurvePolygon > polygon( createEmptyWithSameType() );
519 
520  // exterior ring
521  auto exterior = std::unique_ptr<QgsCurve> { static_cast< QgsCurve *>( mExteriorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) ) };
522 
523  if ( !exterior )
524  return nullptr;
525 
526  polygon->mExteriorRing = std::move( exterior );
527 
528  //interior rings
529  for ( auto interior : mInteriorRings )
530  {
531  if ( !interior )
532  continue;
533 
534  QgsCurve *gridifiedInterior = static_cast< QgsCurve * >( interior->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
535 
536  if ( !gridifiedInterior )
537  continue;
538 
539  polygon->mInteriorRings.append( gridifiedInterior );
540  }
541 
542  return polygon.release();
543 
544 }
545 
546 bool QgsCurvePolygon::removeDuplicateNodes( double epsilon, bool useZValues )
547 {
548  bool result = false;
549  auto cleanRing = [epsilon, useZValues ]( QgsCurve * ring )->bool
550  {
551  if ( ring->numPoints() <= 4 )
552  return false;
553 
554  if ( ring->removeDuplicateNodes( epsilon, useZValues ) )
555  {
556  QgsPoint startPoint;
558  ring->pointAt( 0, startPoint, type );
559  // ensure ring is properly closed - if we removed the final node, it may no longer be properly closed
560  ring->moveVertex( QgsVertexId( -1, -1, ring->numPoints() - 1 ), startPoint );
561  return true;
562  }
563 
564  return false;
565  };
566  if ( mExteriorRing )
567  {
568  result = cleanRing( mExteriorRing.get() );
569  }
570  for ( QgsCurve *ring : qgis::as_const( mInteriorRings ) )
571  {
572  result = result || cleanRing( ring );
573  }
574  return result;
575 }
576 
577 QgsPolygon *QgsCurvePolygon::toPolygon( double tolerance, SegmentationToleranceType toleranceType ) const
578 {
579  std::unique_ptr< QgsPolygon > poly( new QgsPolygon() );
580  if ( !mExteriorRing )
581  {
582  return poly.release();
583  }
584 
585  poly->setExteriorRing( mExteriorRing->curveToLine( tolerance, toleranceType ) );
586 
587  QVector<QgsCurve *> rings;
588  rings.reserve( mInteriorRings.size() );
589  for ( const QgsCurve *ring : mInteriorRings )
590  {
591  rings.push_back( ring->curveToLine( tolerance, toleranceType ) );
592  }
593  poly->setInteriorRings( rings );
594  return poly.release();
595 }
596 
598 {
599  if ( !ring )
600  {
601  return;
602  }
603  mExteriorRing.reset( ring );
604 
605  //set proper wkb type
607  {
609  }
611  {
613  }
614 
615  //match dimensionality for rings
616  for ( QgsCurve *ring : qgis::as_const( mInteriorRings ) )
617  {
618  if ( is3D() )
619  ring->addZValue();
620  else
621  ring->dropZValue();
622 
623  if ( isMeasure() )
624  ring->addMValue();
625  else
626  ring->dropMValue();
627  }
628  clearCache();
629 }
630 
631 void QgsCurvePolygon::setInteriorRings( const QVector<QgsCurve *> &rings )
632 {
633  qDeleteAll( mInteriorRings );
634  mInteriorRings.clear();
635 
636  //add rings one-by-one, so that they can each be converted to the correct type for the CurvePolygon
637  for ( QgsCurve *ring : rings )
638  {
639  addInteriorRing( ring );
640  }
641  clearCache();
642 }
643 
645 {
646  if ( !ring )
647  return;
648 
649  //ensure dimensionality of ring matches curve polygon
650  if ( !is3D() )
651  ring->dropZValue();
652  else if ( !ring->is3D() )
653  ring->addZValue();
654 
655  if ( !isMeasure() )
656  ring->dropMValue();
657  else if ( !ring->isMeasure() )
658  ring->addMValue();
659 
660  mInteriorRings.append( ring );
661  clearCache();
662 }
663 
665 {
666  if ( nr < 0 || nr >= mInteriorRings.size() )
667  {
668  return false;
669  }
670  delete mInteriorRings.takeAt( nr );
671  clearCache();
672  return true;
673 }
674 
675 void QgsCurvePolygon::removeInteriorRings( double minimumAllowedArea )
676 {
677  for ( int ringIndex = mInteriorRings.size() - 1; ringIndex >= 0; --ringIndex )
678  {
679  if ( minimumAllowedArea < 0 )
680  delete mInteriorRings.takeAt( ringIndex );
681  else
682  {
683  double area = 0.0;
684  mInteriorRings.at( ringIndex )->sumUpArea( area );
685  if ( area < minimumAllowedArea )
686  delete mInteriorRings.takeAt( ringIndex );
687  }
688  }
689 
690  clearCache();
691 }
692 
694 {
695  QVector<QgsCurve *> validRings;
696  validRings.reserve( mInteriorRings.size() );
697  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
698  {
699  if ( !curve->isRing() )
700  {
701  // remove invalid rings
702  delete curve;
703  }
704  else
705  {
706  validRings << curve;
707  }
708  }
709  mInteriorRings = validRings;
710 }
711 
713 {
714  if ( mExteriorRing && mExteriorRing->orientation() != QgsCurve::Clockwise )
715  {
716  // flip exterior ring orientation
717  std::unique_ptr< QgsCurve > flipped( mExteriorRing->reversed() );
718  mExteriorRing = std::move( flipped );
719  }
720 
721  QVector<QgsCurve *> validRings;
722  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
723  {
724  if ( curve && curve->orientation() != QgsCurve::CounterClockwise )
725  {
726  // flip interior ring orientation
727  QgsCurve *flipped = curve->reversed();
728  validRings << flipped;
729  delete curve;
730  }
731  else
732  {
733  validRings << curve;
734  }
735  }
736  mInteriorRings = validRings;
737 }
738 
739 void QgsCurvePolygon::draw( QPainter &p ) const
740 {
741  if ( !mExteriorRing )
742  return;
743 
744  if ( mInteriorRings.empty() )
745  {
746  mExteriorRing->drawAsPolygon( p );
747  }
748  else
749  {
750  QPainterPath path;
751  mExteriorRing->addToPainterPath( path );
752 
753  for ( const QgsCurve *ring : mInteriorRings )
754  {
755  ring->addToPainterPath( path );
756  }
757  p.drawPath( path );
758  }
759 }
760 
762 {
763  if ( mExteriorRing )
764  {
765  mExteriorRing->transform( ct, d, transformZ );
766  }
767 
768  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
769  {
770  curve->transform( ct, d, transformZ );
771  }
772  clearCache();
773 }
774 
775 void QgsCurvePolygon::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
776 {
777  if ( mExteriorRing )
778  {
779  mExteriorRing->transform( t, zTranslate, zScale, mTranslate, mScale );
780  }
781 
782  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
783  {
784  curve->transform( t, zTranslate, zScale, mTranslate, mScale );
785  }
786  clearCache();
787 }
788 
790 {
791  QgsCoordinateSequence sequence;
792  sequence.append( QgsRingSequence() );
793 
794  if ( mExteriorRing )
795  {
796  sequence.back().append( QgsPointSequence() );
797  mExteriorRing->points( sequence.back().back() );
798  }
799 
800  for ( const QgsCurve *ring : mInteriorRings )
801  {
802  sequence.back().append( QgsPointSequence() );
803  ring->points( sequence.back().back() );
804  }
805 
806  return sequence;
807 }
808 
810 {
811  int count = 0;
812 
813  if ( mExteriorRing )
814  {
815  count += mExteriorRing->nCoordinates();
816  }
817 
818  for ( const QgsCurve *ring : mInteriorRings )
819  {
820  count += ring->nCoordinates();
821  }
822 
823  return count;
824 }
825 
827 {
828  if ( id.part != 0 )
829  return -1;
830 
831  if ( id.ring < 0 || id.ring >= ringCount() )
832  return -1;
833 
834  int number = 0;
835  if ( id.ring == 0 && mExteriorRing )
836  {
837  return mExteriorRing->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
838  }
839  else
840  {
841  number += mExteriorRing->numPoints();
842  }
843 
844  for ( int i = 0; i < mInteriorRings.count(); ++i )
845  {
846  if ( id.ring == i + 1 )
847  {
848  int partNumber = mInteriorRings.at( i )->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
849  if ( partNumber == -1 )
850  return -1;
851  return number + partNumber;
852  }
853  else
854  {
855  number += mInteriorRings.at( i )->numPoints();
856  }
857  }
858  return -1; // should not happen
859 }
860 
862 {
863  if ( !mExteriorRing )
864  return true;
865 
866  return mExteriorRing->isEmpty();
867 }
868 
869 double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
870 {
871  if ( !mExteriorRing )
872  {
873  return -1;
874  }
875  QVector<QgsCurve *> segmentList;
876  segmentList.append( mExteriorRing.get() );
877  segmentList.append( mInteriorRings );
878  return QgsGeometryUtils::closestSegmentFromComponents( segmentList, QgsGeometryUtils::Ring, pt, segmentPt, vertexAfter, leftOf, epsilon );
879 }
880 
882 {
883  if ( !mExteriorRing || vId.ring >= 1 + mInteriorRings.size() )
884  {
885  return false;
886  }
887 
888  if ( vId.ring < 0 )
889  {
890  vId.ring = 0;
891  vId.vertex = -1;
892  if ( vId.part < 0 )
893  {
894  vId.part = 0;
895  }
896  return mExteriorRing->nextVertex( vId, vertex );
897  }
898  else
899  {
900  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings[vId.ring - 1];
901 
902  if ( ring->nextVertex( vId, vertex ) )
903  {
904  return true;
905  }
906  ++vId.ring;
907  vId.vertex = -1;
908  if ( vId.ring >= 1 + mInteriorRings.size() )
909  {
910  return false;
911  }
912  ring = mInteriorRings[ vId.ring - 1 ];
913  return ring->nextVertex( vId, vertex );
914  }
915 }
916 
917 void ringAdjacentVertices( const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
918 {
919  int n = curve->numPoints();
920  if ( vertex.vertex < 0 || vertex.vertex >= n )
921  {
922  previousVertex = QgsVertexId();
923  nextVertex = QgsVertexId();
924  return;
925  }
926 
927  if ( vertex.vertex == 0 && n < 3 )
928  {
929  previousVertex = QgsVertexId();
930  }
931  else if ( vertex.vertex == 0 )
932  {
933  previousVertex = QgsVertexId( vertex.part, vertex.ring, n - 2 );
934  }
935  else
936  {
937  previousVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex - 1 );
938  }
939  if ( vertex.vertex == n - 1 && n < 3 )
940  {
941  nextVertex = QgsVertexId();
942  }
943  else if ( vertex.vertex == n - 1 )
944  {
945  nextVertex = QgsVertexId( vertex.part, vertex.ring, 1 );
946  }
947  else
948  {
949  nextVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex + 1 );
950  }
951 }
952 
954 {
955  if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
956  {
957  previousVertex = QgsVertexId();
958  nextVertex = QgsVertexId();
959  return;
960  }
961 
962  if ( vertex.ring == 0 )
963  {
964  ringAdjacentVertices( mExteriorRing.get(), vertex, previousVertex, nextVertex );
965  }
966  else
967  {
968  ringAdjacentVertices( mInteriorRings.at( vertex.ring - 1 ), vertex, previousVertex, nextVertex );
969  }
970 }
971 
973 {
974  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
975  {
976  return false;
977  }
978 
979  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
980  int n = ring->numPoints();
981  bool success = ring->insertVertex( QgsVertexId( 0, 0, vId.vertex ), vertex );
982  if ( !success )
983  {
984  return false;
985  }
986 
987  // If first or last vertex is inserted, re-sync the last/first vertex
988  if ( vId.vertex == 0 )
989  ring->moveVertex( QgsVertexId( 0, 0, n ), vertex );
990  else if ( vId.vertex == n )
991  ring->moveVertex( QgsVertexId( 0, 0, 0 ), vertex );
992 
993  clearCache();
994 
995  return true;
996 }
997 
999 {
1000  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1001  {
1002  return false;
1003  }
1004 
1005  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1006  int n = ring->numPoints();
1007  bool success = ring->moveVertex( vId, newPos );
1008  if ( success )
1009  {
1010  // If first or last vertex is moved, also move the last/first vertex
1011  if ( vId.vertex == 0 )
1012  ring->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos );
1013  else if ( vId.vertex == n - 1 )
1014  ring->moveVertex( QgsVertexId( vId.part, vId.ring, 0 ), newPos );
1015  clearCache();
1016  }
1017  return success;
1018 }
1019 
1021 {
1022  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1023  {
1024  return false;
1025  }
1026 
1027  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1028  int n = ring->numPoints();
1029  if ( n <= 4 )
1030  {
1031  //no points will be left in ring, so remove whole ring
1032  if ( vId.ring == 0 )
1033  {
1034  mExteriorRing.reset();
1035  if ( !mInteriorRings.isEmpty() )
1036  {
1037  mExteriorRing.reset( mInteriorRings.takeFirst() );
1038  }
1039  }
1040  else
1041  {
1042  removeInteriorRing( vId.ring - 1 );
1043  }
1044  clearCache();
1045  return true;
1046  }
1047 
1048  bool success = ring->deleteVertex( vId );
1049  if ( success )
1050  {
1051  // If first or last vertex is removed, re-sync the last/first vertex
1052  // Do not use "n - 2", but "ring->numPoints() - 1" as more than one vertex
1053  // may have been deleted (e.g. with CircularString)
1054  if ( vId.vertex == 0 )
1055  ring->moveVertex( QgsVertexId( 0, 0, ring->numPoints() - 1 ), ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) );
1056  else if ( vId.vertex == n - 1 )
1057  ring->moveVertex( QgsVertexId( 0, 0, 0 ), ring->vertexAt( QgsVertexId( 0, 0, ring->numPoints() - 1 ) ) );
1058  clearCache();
1059  }
1060  return success;
1061 }
1062 
1064 {
1065  if ( mExteriorRing && mExteriorRing->hasCurvedSegments() )
1066  {
1067  return true;
1068  }
1069 
1070  for ( const QgsCurve *ring : mInteriorRings )
1071  {
1072  if ( ring->hasCurvedSegments() )
1073  {
1074  return true;
1075  }
1076  }
1077  return false;
1078 }
1079 
1081 {
1082  return toPolygon( tolerance, toleranceType );
1083 }
1084 
1086 {
1087  if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1088  {
1089  //makes no sense - conversion of false to double!
1090  return false;
1091  }
1092 
1093  QgsCurve *ring = vertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[vertex.ring - 1];
1094  return ring->vertexAngle( vertex );
1095 }
1096 
1097 int QgsCurvePolygon::vertexCount( int /*part*/, int ring ) const
1098 {
1099  return ring == 0 ? mExteriorRing->vertexCount() : mInteriorRings[ring - 1]->vertexCount();
1100 }
1101 
1103 {
1104  return ( nullptr != mExteriorRing ) + mInteriorRings.size();
1105 }
1106 
1108 {
1109  return ringCount() > 0 ? 1 : 0;
1110 }
1111 
1113 {
1114  return id.ring == 0 ? mExteriorRing->vertexAt( id ) : mInteriorRings[id.ring - 1]->vertexAt( id );
1115 }
1116 
1117 double QgsCurvePolygon::segmentLength( QgsVertexId startVertex ) const
1118 {
1119  if ( !mExteriorRing || startVertex.ring < 0 || startVertex.ring >= 1 + mInteriorRings.size() )
1120  {
1121  return 0.0;
1122  }
1123 
1124  const QgsCurve *ring = startVertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[startVertex.ring - 1];
1125  return ring->segmentLength( startVertex );
1126 }
1127 
1128 bool QgsCurvePolygon::addZValue( double zValue )
1129 {
1130  if ( QgsWkbTypes::hasZ( mWkbType ) )
1131  return false;
1132 
1134 
1135  if ( mExteriorRing )
1136  mExteriorRing->addZValue( zValue );
1137  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1138  {
1139  curve->addZValue( zValue );
1140  }
1141  clearCache();
1142  return true;
1143 }
1144 
1145 bool QgsCurvePolygon::addMValue( double mValue )
1146 {
1147  if ( QgsWkbTypes::hasM( mWkbType ) )
1148  return false;
1149 
1151 
1152  if ( mExteriorRing )
1153  mExteriorRing->addMValue( mValue );
1154  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1155  {
1156  curve->addMValue( mValue );
1157  }
1158  clearCache();
1159  return true;
1160 }
1161 
1163 {
1164  if ( !is3D() )
1165  return false;
1166 
1168  if ( mExteriorRing )
1169  mExteriorRing->dropZValue();
1170  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1171  {
1172  curve->dropZValue();
1173  }
1174  clearCache();
1175  return true;
1176 }
1177 
1179 {
1180  if ( !isMeasure() )
1181  return false;
1182 
1184  if ( mExteriorRing )
1185  mExteriorRing->dropMValue();
1186  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1187  {
1188  curve->dropMValue();
1189  }
1190  clearCache();
1191  return true;
1192 }
1193 
1195 {
1196  if ( mExteriorRing )
1197  mExteriorRing->swapXy();
1198  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1199  {
1200  curve->swapXy();
1201  }
1202  clearCache();
1203 }
1204 
1206 {
1207  return clone();
1208 }
1209 
1210 void QgsCurvePolygon::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1211 {
1212  if ( mExteriorRing )
1213  mExteriorRing->filterVertices( filter );
1214 
1215  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1216  {
1217  curve->filterVertices( filter );
1218  }
1219  clearCache();
1220 }
1221 
1222 void QgsCurvePolygon::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1223 {
1224  if ( mExteriorRing )
1225  mExteriorRing->transformVertices( transform );
1226 
1227  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1228  {
1229  curve->transformVertices( transform );
1230  }
1231  clearCache();
1232 }
1233 
1235 {
1236  return 1 + mInteriorRings.count();
1237 }
1238 
1240 {
1241  if ( index == 0 )
1242  return mExteriorRing.get();
1243  else
1244  return mInteriorRings.at( index - 1 );
1245 }
bool isMeasure() const
Returns true if the geometry contains m values.
QByteArray asWkb() const override
Returns a WKB representation of the geometry.
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
virtual void setExteriorRing(QgsCurve *ring)
Sets the exterior ring of the polygon.
void ringAdjacentVertices(const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex)
bool dropMValue() override
Drops any measure values which exist in the geometry.
int precision
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
bool operator==(const QgsAbstractGeometry &other) const override
QgsCurvePolygon & operator=(const QgsCurvePolygon &p)
virtual bool deleteVertex(QgsVertexId position)=0
Deletes a vertex within the geometry.
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
QgsAbstractGeometry & operator=(const QgsAbstractGeometry &geom)
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgssurface.cpp:43
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 (...
virtual bool insertVertex(QgsVertexId position, const QgsPoint &vertex)=0
Inserts a vertex into the geometry.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
virtual void addInteriorRing(QgsCurve *ring)
Adds an interior ring to the geometry (takes ownership)
QVector< QgsRingSequence > QgsCoordinateSequence
void swapXy() override
Swaps the x and y coordinates from the geometry.
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
Definition: qgscurve.cpp:71
QgsCurvePolygon * toCurveType() const override
Returns the geometry converted to the more generic curve type.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
QgsAbstractGeometry * boundary() const override
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
Curve polygon geometry type.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
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.
void forceRHR()
Forces the geometry to respect the Right-Hand-Rule, in which the area that is bounded by the polygon ...
static endian_t endian()
Returns whether this machine uses big or little endian.
double area() const override
Returns the area of the geometry.
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:771
static QStringList wktGetChildBlocks(const QString &wkt, const QString &defaultType=QString())
Parses a WKT string and returns of list of blocks contained in the WKT.
static Type dropM(Type type)
Drops the m dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:941
QgsWkbTypes::Type mWkbType
bool removeInteriorRing(int ringIndex)
Removes an interior ring from the polygon.
QVector< QgsCurve * > mInteriorRings
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
void removeInvalidRings()
Removes any interior rings which are not valid from the polygon.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
Definition: qgspoint.cpp:382
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
~QgsCurvePolygon() override
int vertexNumberFromVertexId(QgsVertexId id) const override
Returns the vertex number corresponding to a vertex id.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
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...
int childCount() const override
Returns number of child geometries (for geometries with child geometries) or child points (for geomet...
int nCoordinates() const override
Returns the number of nodes contained in the geometry.
virtual double segmentLength(QgsVertexId startVertex) const =0
Returns the length of the segment of the geometry which begins at startVertex.
static Type addM(Type type)
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:892
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
Utility class for identifying a unique vertex within a geometry.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
int dimension() const override
Returns the inherent dimension of the geometry.
QgsCurvePolygon * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership...
QgsRectangle calculateBoundingBox() const override
Default calculator for the minimal bounding box for the geometry.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
QString geometryType() const override
Returns a unique string representing the geometry type.
QgsCurvePolygon * 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...
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:666
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
static Type addZ(Type type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:867
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Multi curve geometry collection.
Definition: qgsmulticurve.h:29
double perimeter() const override
Returns the perimeter of the geometry.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
virtual QgsPolygon * toPolygon(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a new polygon geometry corresponding to a segmentized approximation of the curve...
QgsAbstractGeometry * childGeometry(int index) const override
Returns pointer to child geometry (for geometries with child geometries - i.e.
Abstract base class for all geometries.
QgsCoordinateSequence coordinateSequence() const override
Retrieves the sequence of geometries, rings and nodes.
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
Definition: qgscurve.cpp:170
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
Counter-clockwise orientation.
Definition: qgscurve.h:237
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
AxisOrder
Axis order for GML generation.
bool operator!=(const QgsAbstractGeometry &other) const override
nlohmann::json json
Definition: qgsjsonutils.h:27
double vertexAngle(QgsVertexId vertex) const override
Returns approximate rotation angle for a vertex.
static json pointsToJson(const QgsPointSequence &points, int precision)
Returns coordinates as json object.
int partCount() const override
Returns count of parts contained in the geometry.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Clockwise orientation.
Definition: qgscurve.h:236
QVector< QgsPoint > QgsPointSequence
void adjacentVertices(QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex) const override
Returns the vertices adjacent to a specified vertex within a geometry.
QVector< QgsPointSequence > QgsRingSequence
static Type dropZ(Type type)
Drops the z dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:923
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
virtual bool moveVertex(QgsVertexId position, const QgsPoint &newPos)=0
Moves a vertex within the geometry.
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
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
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
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...
Class for doing transforms between two map coordinate systems.
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
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.
QgsAbstractGeometry * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:821
Compound curve geometry type.
Circular string geometry type.
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 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...
QgsCurvePolygon * clone() const override
Clones the geometry by performing a deep copy.
virtual bool dropMValue()=0
Drops any measure values which exist in 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.
Polygon geometry type.
Definition: qgspolygon.h:31
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
const QgsCurve * exteriorRing() const
Returns the curve polygon&#39;s exterior ring.
void setInteriorRings(const QVector< QgsCurve *> &rings)
Sets all interior rings (takes ownership)
std::unique_ptr< QgsCurve > mExteriorRing
QgsPolygon * surfaceToPolygon() const override
Gets a polygon representation of this surface.
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:430
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
void removeInteriorRings(double minimumAllowedArea=-1)
Removes the interior rings from the polygon.
bool isEmpty() const override
Returns true if the geometry is empty.
virtual int numPoints() const =0
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
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)
virtual QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const =0
Returns a GML3 representation of the geometry.