QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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  if ( exteriorRing() )
406  {
407  std::unique_ptr< QgsLineString > exteriorLineString( exteriorRing()->curveToLine() );
408  QgsPointSequence exteriorPts;
409  exteriorLineString->points( exteriorPts );
410  json += QgsGeometryUtils::pointsToJSON( exteriorPts, precision ) + QLatin1String( ", " );
411 
412  std::unique_ptr< QgsLineString > interiorLineString;
413  for ( int i = 0, n = numInteriorRings(); i < n; ++i )
414  {
415  interiorLineString.reset( interiorRing( i )->curveToLine() );
416  QgsPointSequence interiorPts;
417  interiorLineString->points( interiorPts );
418  json += QgsGeometryUtils::pointsToJSON( interiorPts, precision ) + QLatin1String( ", " );
419  }
420  if ( json.endsWith( QLatin1String( ", " ) ) )
421  {
422  json.chop( 2 ); // Remove last ", "
423  }
424  }
425  json += QLatin1String( "] }" );
426  return json;
427 }
428 
429 double QgsCurvePolygon::area() const
430 {
431  if ( !mExteriorRing )
432  {
433  return 0.0;
434  }
435 
436  double totalArea = 0.0;
437 
438  if ( mExteriorRing->isRing() )
439  {
440  double area = 0.0;
441  mExteriorRing->sumUpArea( area );
442  totalArea += std::fabs( area );
443  }
444 
445  for ( const QgsCurve *ring : mInteriorRings )
446  {
447  double area = 0.0;
448  if ( ring->isRing() )
449  {
450  ring->sumUpArea( area );
451  totalArea -= std::fabs( area );
452  }
453  }
454  return totalArea;
455 }
456 
458 {
459  if ( !mExteriorRing )
460  return 0.0;
461 
462  //sum perimeter of rings
463  double perimeter = mExteriorRing->length();
464  for ( const QgsCurve *ring : mInteriorRings )
465  {
466  perimeter += ring->length();
467  }
468  return perimeter;
469 }
470 
472 {
473  std::unique_ptr< QgsPolygon > polygon( new QgsPolygon() );
474  if ( !mExteriorRing )
475  return polygon.release();
476 
477  polygon->setExteriorRing( exteriorRing()->curveToLine() );
478  QVector<QgsCurve *> interiors;
479  int n = numInteriorRings();
480  interiors.reserve( n );
481  for ( int i = 0; i < n; ++i )
482  {
483  interiors.append( interiorRing( i )->curveToLine() );
484  }
485  polygon->setInteriorRings( interiors );
486  return polygon.release();
487 }
488 
490 {
491  if ( !mExteriorRing )
492  return nullptr;
493 
494  if ( mInteriorRings.isEmpty() )
495  {
496  return mExteriorRing->clone();
497  }
498  else
499  {
500  QgsMultiCurve *multiCurve = new QgsMultiCurve();
501  multiCurve->addGeometry( mExteriorRing->clone() );
502  int nInteriorRings = mInteriorRings.size();
503  for ( int i = 0; i < nInteriorRings; ++i )
504  {
505  multiCurve->addGeometry( mInteriorRings.at( i )->clone() );
506  }
507  return multiCurve;
508  }
509 }
510 
511 QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
512 {
513  if ( !mExteriorRing )
514  return nullptr;
515 
516 
517  std::unique_ptr< QgsCurvePolygon > polygon( createEmptyWithSameType() );
518 
519  // exterior ring
520  auto exterior = std::unique_ptr<QgsCurve> { static_cast< QgsCurve *>( mExteriorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) ) };
521 
522  if ( !exterior )
523  return nullptr;
524 
525  polygon->mExteriorRing = std::move( exterior );
526 
527  //interior rings
528  for ( auto interior : mInteriorRings )
529  {
530  if ( !interior )
531  continue;
532 
533  QgsCurve *gridifiedInterior = static_cast< QgsCurve * >( interior->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
534 
535  if ( !gridifiedInterior )
536  continue;
537 
538  polygon->mInteriorRings.append( gridifiedInterior );
539  }
540 
541  return polygon.release();
542 
543 }
544 
545 bool QgsCurvePolygon::removeDuplicateNodes( double epsilon, bool useZValues )
546 {
547  bool result = false;
548  auto cleanRing = [epsilon, useZValues ]( QgsCurve * ring )->bool
549  {
550  if ( ring->numPoints() <= 4 )
551  return false;
552 
553  if ( ring->removeDuplicateNodes( epsilon, useZValues ) )
554  {
555  QgsPoint startPoint;
557  ring->pointAt( 0, startPoint, type );
558  // ensure ring is properly closed - if we removed the final node, it may no longer be properly closed
559  ring->moveVertex( QgsVertexId( -1, -1, ring->numPoints() - 1 ), startPoint );
560  return true;
561  }
562 
563  return false;
564  };
565  if ( mExteriorRing )
566  {
567  result = cleanRing( mExteriorRing.get() );
568  }
569  for ( QgsCurve *ring : qgis::as_const( mInteriorRings ) )
570  {
571  result = result || cleanRing( ring );
572  }
573  return result;
574 }
575 
576 QgsPolygon *QgsCurvePolygon::toPolygon( double tolerance, SegmentationToleranceType toleranceType ) const
577 {
578  std::unique_ptr< QgsPolygon > poly( new QgsPolygon() );
579  if ( !mExteriorRing )
580  {
581  return poly.release();
582  }
583 
584  poly->setExteriorRing( mExteriorRing->curveToLine( tolerance, toleranceType ) );
585 
586  QVector<QgsCurve *> rings;
587  rings.reserve( mInteriorRings.size() );
588  for ( const QgsCurve *ring : mInteriorRings )
589  {
590  rings.push_back( ring->curveToLine( tolerance, toleranceType ) );
591  }
592  poly->setInteriorRings( rings );
593  return poly.release();
594 }
595 
597 {
598  if ( !ring )
599  {
600  return;
601  }
602  mExteriorRing.reset( ring );
603 
604  //set proper wkb type
606  {
608  }
610  {
612  }
613 
614  //match dimensionality for rings
615  for ( QgsCurve *ring : qgis::as_const( mInteriorRings ) )
616  {
617  if ( is3D() )
618  ring->addZValue();
619  else
620  ring->dropZValue();
621 
622  if ( isMeasure() )
623  ring->addMValue();
624  else
625  ring->dropMValue();
626  }
627  clearCache();
628 }
629 
630 void QgsCurvePolygon::setInteriorRings( const QVector<QgsCurve *> &rings )
631 {
632  qDeleteAll( mInteriorRings );
633  mInteriorRings.clear();
634 
635  //add rings one-by-one, so that they can each be converted to the correct type for the CurvePolygon
636  for ( QgsCurve *ring : rings )
637  {
638  addInteriorRing( ring );
639  }
640  clearCache();
641 }
642 
644 {
645  if ( !ring )
646  return;
647 
648  //ensure dimensionality of ring matches curve polygon
649  if ( !is3D() )
650  ring->dropZValue();
651  else if ( !ring->is3D() )
652  ring->addZValue();
653 
654  if ( !isMeasure() )
655  ring->dropMValue();
656  else if ( !ring->isMeasure() )
657  ring->addMValue();
658 
659  mInteriorRings.append( ring );
660  clearCache();
661 }
662 
664 {
665  if ( nr < 0 || nr >= mInteriorRings.size() )
666  {
667  return false;
668  }
669  delete mInteriorRings.takeAt( nr );
670  clearCache();
671  return true;
672 }
673 
674 void QgsCurvePolygon::removeInteriorRings( double minimumAllowedArea )
675 {
676  for ( int ringIndex = mInteriorRings.size() - 1; ringIndex >= 0; --ringIndex )
677  {
678  if ( minimumAllowedArea < 0 )
679  delete mInteriorRings.takeAt( ringIndex );
680  else
681  {
682  double area = 0.0;
683  mInteriorRings.at( ringIndex )->sumUpArea( area );
684  if ( area < minimumAllowedArea )
685  delete mInteriorRings.takeAt( ringIndex );
686  }
687  }
688 
689  clearCache();
690 }
691 
693 {
694  QVector<QgsCurve *> validRings;
695  validRings.reserve( mInteriorRings.size() );
696  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
697  {
698  if ( !curve->isRing() )
699  {
700  // remove invalid rings
701  delete curve;
702  }
703  else
704  {
705  validRings << curve;
706  }
707  }
708  mInteriorRings = validRings;
709 }
710 
712 {
713  if ( mExteriorRing && mExteriorRing->orientation() != QgsCurve::Clockwise )
714  {
715  // flip exterior ring orientation
716  std::unique_ptr< QgsCurve > flipped( mExteriorRing->reversed() );
717  mExteriorRing = std::move( flipped );
718  }
719 
720  QVector<QgsCurve *> validRings;
721  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
722  {
723  if ( curve && curve->orientation() != QgsCurve::CounterClockwise )
724  {
725  // flip interior ring orientation
726  QgsCurve *flipped = curve->reversed();
727  validRings << flipped;
728  delete curve;
729  }
730  else
731  {
732  validRings << curve;
733  }
734  }
735  mInteriorRings = validRings;
736 }
737 
738 void QgsCurvePolygon::draw( QPainter &p ) const
739 {
740  if ( !mExteriorRing )
741  return;
742 
743  if ( mInteriorRings.empty() )
744  {
745  mExteriorRing->drawAsPolygon( p );
746  }
747  else
748  {
749  QPainterPath path;
750  mExteriorRing->addToPainterPath( path );
751 
752  for ( const QgsCurve *ring : mInteriorRings )
753  {
754  ring->addToPainterPath( path );
755  }
756  p.drawPath( path );
757  }
758 }
759 
761 {
762  if ( mExteriorRing )
763  {
764  mExteriorRing->transform( ct, d, transformZ );
765  }
766 
767  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
768  {
769  curve->transform( ct, d, transformZ );
770  }
771  clearCache();
772 }
773 
774 void QgsCurvePolygon::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
775 {
776  if ( mExteriorRing )
777  {
778  mExteriorRing->transform( t, zTranslate, zScale, mTranslate, mScale );
779  }
780 
781  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
782  {
783  curve->transform( t, zTranslate, zScale, mTranslate, mScale );
784  }
785  clearCache();
786 }
787 
789 {
790  QgsCoordinateSequence sequence;
791  sequence.append( QgsRingSequence() );
792 
793  if ( mExteriorRing )
794  {
795  sequence.back().append( QgsPointSequence() );
796  mExteriorRing->points( sequence.back().back() );
797  }
798 
799  for ( const QgsCurve *ring : mInteriorRings )
800  {
801  sequence.back().append( QgsPointSequence() );
802  ring->points( sequence.back().back() );
803  }
804 
805  return sequence;
806 }
807 
809 {
810  int count = 0;
811 
812  if ( mExteriorRing )
813  {
814  count += mExteriorRing->nCoordinates();
815  }
816 
817  for ( const QgsCurve *ring : mInteriorRings )
818  {
819  count += ring->nCoordinates();
820  }
821 
822  return count;
823 }
824 
826 {
827  if ( id.part != 0 )
828  return -1;
829 
830  if ( id.ring < 0 || id.ring >= ringCount() )
831  return -1;
832 
833  int number = 0;
834  if ( id.ring == 0 && mExteriorRing )
835  {
836  return mExteriorRing->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
837  }
838  else
839  {
840  number += mExteriorRing->numPoints();
841  }
842 
843  for ( int i = 0; i < mInteriorRings.count(); ++i )
844  {
845  if ( id.ring == i + 1 )
846  {
847  int partNumber = mInteriorRings.at( i )->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
848  if ( partNumber == -1 )
849  return -1;
850  return number + partNumber;
851  }
852  else
853  {
854  number += mInteriorRings.at( i )->numPoints();
855  }
856  }
857  return -1; // should not happen
858 }
859 
861 {
862  if ( !mExteriorRing )
863  return true;
864 
865  return mExteriorRing->isEmpty();
866 }
867 
868 double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
869 {
870  if ( !mExteriorRing )
871  {
872  return -1;
873  }
874  QVector<QgsCurve *> segmentList;
875  segmentList.append( mExteriorRing.get() );
876  segmentList.append( mInteriorRings );
877  return QgsGeometryUtils::closestSegmentFromComponents( segmentList, QgsGeometryUtils::Ring, pt, segmentPt, vertexAfter, leftOf, epsilon );
878 }
879 
881 {
882  if ( !mExteriorRing || vId.ring >= 1 + mInteriorRings.size() )
883  {
884  return false;
885  }
886 
887  if ( vId.ring < 0 )
888  {
889  vId.ring = 0;
890  vId.vertex = -1;
891  if ( vId.part < 0 )
892  {
893  vId.part = 0;
894  }
895  return mExteriorRing->nextVertex( vId, vertex );
896  }
897  else
898  {
899  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings[vId.ring - 1];
900 
901  if ( ring->nextVertex( vId, vertex ) )
902  {
903  return true;
904  }
905  ++vId.ring;
906  vId.vertex = -1;
907  if ( vId.ring >= 1 + mInteriorRings.size() )
908  {
909  return false;
910  }
911  ring = mInteriorRings[ vId.ring - 1 ];
912  return ring->nextVertex( vId, vertex );
913  }
914 }
915 
916 void ringAdjacentVertices( const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
917 {
918  int n = curve->numPoints();
919  if ( vertex.vertex < 0 || vertex.vertex >= n )
920  {
921  previousVertex = QgsVertexId();
922  nextVertex = QgsVertexId();
923  return;
924  }
925 
926  if ( vertex.vertex == 0 && n < 3 )
927  {
928  previousVertex = QgsVertexId();
929  }
930  else if ( vertex.vertex == 0 )
931  {
932  previousVertex = QgsVertexId( vertex.part, vertex.ring, n - 2 );
933  }
934  else
935  {
936  previousVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex - 1 );
937  }
938  if ( vertex.vertex == n - 1 && n < 3 )
939  {
940  nextVertex = QgsVertexId();
941  }
942  else if ( vertex.vertex == n - 1 )
943  {
944  nextVertex = QgsVertexId( vertex.part, vertex.ring, 1 );
945  }
946  else
947  {
948  nextVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex + 1 );
949  }
950 }
951 
953 {
954  if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
955  {
956  previousVertex = QgsVertexId();
957  nextVertex = QgsVertexId();
958  return;
959  }
960 
961  if ( vertex.ring == 0 )
962  {
963  ringAdjacentVertices( mExteriorRing.get(), vertex, previousVertex, nextVertex );
964  }
965  else
966  {
967  ringAdjacentVertices( mInteriorRings.at( vertex.ring - 1 ), vertex, previousVertex, nextVertex );
968  }
969 }
970 
972 {
973  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
974  {
975  return false;
976  }
977 
978  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
979  int n = ring->numPoints();
980  bool success = ring->insertVertex( QgsVertexId( 0, 0, vId.vertex ), vertex );
981  if ( !success )
982  {
983  return false;
984  }
985 
986  // If first or last vertex is inserted, re-sync the last/first vertex
987  if ( vId.vertex == 0 )
988  ring->moveVertex( QgsVertexId( 0, 0, n ), vertex );
989  else if ( vId.vertex == n )
990  ring->moveVertex( QgsVertexId( 0, 0, 0 ), vertex );
991 
992  clearCache();
993 
994  return true;
995 }
996 
998 {
999  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1000  {
1001  return false;
1002  }
1003 
1004  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1005  int n = ring->numPoints();
1006  bool success = ring->moveVertex( vId, newPos );
1007  if ( success )
1008  {
1009  // If first or last vertex is moved, also move the last/first vertex
1010  if ( vId.vertex == 0 )
1011  ring->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos );
1012  else if ( vId.vertex == n - 1 )
1013  ring->moveVertex( QgsVertexId( vId.part, vId.ring, 0 ), newPos );
1014  clearCache();
1015  }
1016  return success;
1017 }
1018 
1020 {
1021  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1022  {
1023  return false;
1024  }
1025 
1026  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1027  int n = ring->numPoints();
1028  if ( n <= 4 )
1029  {
1030  //no points will be left in ring, so remove whole ring
1031  if ( vId.ring == 0 )
1032  {
1033  mExteriorRing.reset();
1034  if ( !mInteriorRings.isEmpty() )
1035  {
1036  mExteriorRing.reset( mInteriorRings.takeFirst() );
1037  }
1038  }
1039  else
1040  {
1041  removeInteriorRing( vId.ring - 1 );
1042  }
1043  clearCache();
1044  return true;
1045  }
1046 
1047  bool success = ring->deleteVertex( vId );
1048  if ( success )
1049  {
1050  // If first or last vertex is removed, re-sync the last/first vertex
1051  // Do not use "n - 2", but "ring->numPoints() - 1" as more than one vertex
1052  // may have been deleted (e.g. with CircularString)
1053  if ( vId.vertex == 0 )
1054  ring->moveVertex( QgsVertexId( 0, 0, ring->numPoints() - 1 ), ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) );
1055  else if ( vId.vertex == n - 1 )
1056  ring->moveVertex( QgsVertexId( 0, 0, 0 ), ring->vertexAt( QgsVertexId( 0, 0, ring->numPoints() - 1 ) ) );
1057  clearCache();
1058  }
1059  return success;
1060 }
1061 
1063 {
1064  if ( mExteriorRing && mExteriorRing->hasCurvedSegments() )
1065  {
1066  return true;
1067  }
1068 
1069  for ( const QgsCurve *ring : mInteriorRings )
1070  {
1071  if ( ring->hasCurvedSegments() )
1072  {
1073  return true;
1074  }
1075  }
1076  return false;
1077 }
1078 
1080 {
1081  return toPolygon( tolerance, toleranceType );
1082 }
1083 
1085 {
1086  if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1087  {
1088  //makes no sense - conversion of false to double!
1089  return false;
1090  }
1091 
1092  QgsCurve *ring = vertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[vertex.ring - 1];
1093  return ring->vertexAngle( vertex );
1094 }
1095 
1096 int QgsCurvePolygon::vertexCount( int /*part*/, int ring ) const
1097 {
1098  return ring == 0 ? mExteriorRing->vertexCount() : mInteriorRings[ring - 1]->vertexCount();
1099 }
1100 
1102 {
1103  return ( nullptr != mExteriorRing ) + mInteriorRings.size();
1104 }
1105 
1107 {
1108  return ringCount() > 0 ? 1 : 0;
1109 }
1110 
1112 {
1113  return id.ring == 0 ? mExteriorRing->vertexAt( id ) : mInteriorRings[id.ring - 1]->vertexAt( id );
1114 }
1115 
1116 double QgsCurvePolygon::segmentLength( QgsVertexId startVertex ) const
1117 {
1118  if ( !mExteriorRing || startVertex.ring < 0 || startVertex.ring >= 1 + mInteriorRings.size() )
1119  {
1120  return 0.0;
1121  }
1122 
1123  const QgsCurve *ring = startVertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[startVertex.ring - 1];
1124  return ring->segmentLength( startVertex );
1125 }
1126 
1127 bool QgsCurvePolygon::addZValue( double zValue )
1128 {
1129  if ( QgsWkbTypes::hasZ( mWkbType ) )
1130  return false;
1131 
1133 
1134  if ( mExteriorRing )
1135  mExteriorRing->addZValue( zValue );
1136  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1137  {
1138  curve->addZValue( zValue );
1139  }
1140  clearCache();
1141  return true;
1142 }
1143 
1144 bool QgsCurvePolygon::addMValue( double mValue )
1145 {
1146  if ( QgsWkbTypes::hasM( mWkbType ) )
1147  return false;
1148 
1150 
1151  if ( mExteriorRing )
1152  mExteriorRing->addMValue( mValue );
1153  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1154  {
1155  curve->addMValue( mValue );
1156  }
1157  clearCache();
1158  return true;
1159 }
1160 
1162 {
1163  if ( !is3D() )
1164  return false;
1165 
1167  if ( mExteriorRing )
1168  mExteriorRing->dropZValue();
1169  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1170  {
1171  curve->dropZValue();
1172  }
1173  clearCache();
1174  return true;
1175 }
1176 
1178 {
1179  if ( !isMeasure() )
1180  return false;
1181 
1183  if ( mExteriorRing )
1184  mExteriorRing->dropMValue();
1185  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1186  {
1187  curve->dropMValue();
1188  }
1189  clearCache();
1190  return true;
1191 }
1192 
1194 {
1195  if ( mExteriorRing )
1196  mExteriorRing->swapXy();
1197  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1198  {
1199  curve->swapXy();
1200  }
1201  clearCache();
1202 }
1203 
1205 {
1206  return clone();
1207 }
1208 
1209 void QgsCurvePolygon::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1210 {
1211  if ( mExteriorRing )
1212  mExteriorRing->filterVertices( filter );
1213 
1214  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1215  {
1216  curve->filterVertices( filter );
1217  }
1218  clearCache();
1219 }
1220 
1221 void QgsCurvePolygon::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1222 {
1223  if ( mExteriorRing )
1224  mExteriorRing->transformVertices( transform );
1225 
1226  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1227  {
1228  curve->transformVertices( transform );
1229  }
1230  clearCache();
1231 }
1232 
1234 {
1235  return 1 + mInteriorRings.count();
1236 }
1237 
1239 {
1240  if ( index == 0 )
1241  return mExteriorRing.get();
1242  else
1243  return mInteriorRings.at( index - 1 );
1244 }
QByteArray asWkb() const override
Returns a WKB representation of the geometry.
const QgsCurve * exteriorRing() const
Returns the curve polygon&#39;s exterior ring.
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:40
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.
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
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.
void setInteriorRings(const QVector< QgsCurve * > &rings)
Sets all interior rings (takes ownership)
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:906
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:1076
QgsWkbTypes::Type mWkbType
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
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.
~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.
bool isMeasure() const
Returns true if the geometry contains m values.
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:1027
QString asJson(int precision=17) const override
Returns a GeoJSON representation of the geometry.
QString wktTypeStr() const
Returns the WKT type string 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:801
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:1002
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
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
Counter-clockwise orientation.
Definition: qgscurve.h:236
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:53
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:1058
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...
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
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
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...
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
Class for doing transforms between two map coordinate systems.
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *std::numeric_limits< double >::epsilon()) const override
Searches for the closest segment of the geometry to a given point.
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:956
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.
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:565
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.
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.