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