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