QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 "qgsfeedback.h"
28 
29 #include <QJsonArray>
30 #include <QJsonObject>
31 #include <QPainter>
32 #include <QPainterPath>
33 #include <memory>
34 #include <nlohmann/json.hpp>
35 
37 {
39 }
40 
42 {
43  clear();
44 }
45 
47 {
48  auto result = std::make_unique< QgsCurvePolygon >();
49  result->mWkbType = mWkbType;
50  return result.release();
51 }
52 
54 {
55  return QStringLiteral( "CurvePolygon" );
56 }
57 
59 {
60  return 2;
61 }
62 
64  : QgsSurface( p )
65 
66 {
67  mWkbType = p.mWkbType;
68  if ( p.mExteriorRing )
69  {
70  mExteriorRing.reset( p.mExteriorRing->clone() );
71  }
72 
73  for ( const QgsCurve *ring : p.mInteriorRings )
74  {
75  mInteriorRings.push_back( ring->clone() );
76  }
77 }
78 
80 {
81  if ( &p != this )
82  {
83  clearCache();
85  if ( p.mExteriorRing )
86  {
87  mExteriorRing.reset( p.mExteriorRing->clone() );
88  }
89 
90  for ( const QgsCurve *ring : p.mInteriorRings )
91  {
92  mInteriorRings.push_back( ring->clone() );
93  }
94  }
95  return *this;
96 }
97 
99 {
100  const QgsCurvePolygon *otherPolygon = qgsgeometry_cast< const QgsCurvePolygon * >( &other );
101  if ( !otherPolygon )
102  return false;
103 
104  //run cheap checks first
105  if ( mWkbType != otherPolygon->mWkbType )
106  return false;
107 
108  if ( ( !mExteriorRing && otherPolygon->mExteriorRing ) || ( mExteriorRing && !otherPolygon->mExteriorRing ) )
109  return false;
110 
111  if ( mInteriorRings.count() != otherPolygon->mInteriorRings.count() )
112  return false;
113 
114  // compare rings
115  if ( mExteriorRing && otherPolygon->mExteriorRing )
116  {
117  if ( *mExteriorRing != *otherPolygon->mExteriorRing )
118  return false;
119  }
120 
121  for ( int i = 0; i < mInteriorRings.count(); ++i )
122  {
123  if ( ( !mInteriorRings.at( i ) && otherPolygon->mInteriorRings.at( i ) ) ||
124  ( mInteriorRings.at( i ) && !otherPolygon->mInteriorRings.at( i ) ) )
125  return false;
126 
127  if ( mInteriorRings.at( i ) && otherPolygon->mInteriorRings.at( i ) &&
128  *mInteriorRings.at( i ) != *otherPolygon->mInteriorRings.at( i ) )
129  return false;
130  }
131 
132  return true;
133 }
134 
136 {
137  return !operator==( other );
138 }
139 
141 {
142  return new QgsCurvePolygon( *this );
143 }
144 
146 {
148  mExteriorRing.reset();
149  qDeleteAll( mInteriorRings );
150  mInteriorRings.clear();
151  clearCache();
152 }
153 
154 
156 {
157  clear();
158  if ( !wkbPtr )
159  {
160  return false;
161  }
162 
163  QgsWkbTypes::Type type = wkbPtr.readHeader();
165  {
166  return false;
167  }
168  mWkbType = type;
169 
170  int nRings;
171  wkbPtr >> nRings;
172  std::unique_ptr< QgsCurve > currentCurve;
173  for ( int i = 0; i < nRings; ++i )
174  {
175  QgsWkbTypes::Type curveType = wkbPtr.readHeader();
176  wkbPtr -= 1 + sizeof( int );
177  QgsWkbTypes::Type flatCurveType = QgsWkbTypes::flatType( curveType );
178  if ( flatCurveType == QgsWkbTypes::LineString )
179  {
180  currentCurve.reset( new QgsLineString() );
181  }
182  else if ( flatCurveType == QgsWkbTypes::CircularString )
183  {
184  currentCurve.reset( new QgsCircularString() );
185  }
186  else if ( flatCurveType == QgsWkbTypes::CompoundCurve )
187  {
188  currentCurve.reset( new QgsCompoundCurve() );
189  }
190  else
191  {
192  return false;
193  }
194  currentCurve->fromWkb( wkbPtr ); // also updates wkbPtr
195  if ( i == 0 )
196  {
197  mExteriorRing = std::move( currentCurve );
198  }
199  else
200  {
201  mInteriorRings.append( currentCurve.release() );
202  }
203  }
204 
205  return true;
206 }
207 
208 bool QgsCurvePolygon::fromWkt( const QString &wkt )
209 {
210  clear();
211 
212  QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
213 
215  return false;
216 
217  mWkbType = parts.first;
218 
219  QString secondWithoutParentheses = parts.second;
220  secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
221  if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
222  secondWithoutParentheses.isEmpty() )
223  return true;
224 
225  QString defaultChildWkbType = QStringLiteral( "LineString%1%2" ).arg( is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
226 
227  const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
228  for ( const QString &childWkt : blocks )
229  {
230  QPair<QgsWkbTypes::Type, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
231 
232  QgsWkbTypes::Type flatCurveType = QgsWkbTypes::flatType( childParts.first );
233  if ( flatCurveType == QgsWkbTypes::LineString )
234  mInteriorRings.append( new QgsLineString() );
235  else if ( flatCurveType == QgsWkbTypes::CircularString )
236  mInteriorRings.append( new QgsCircularString() );
237  else if ( flatCurveType == QgsWkbTypes::CompoundCurve )
238  mInteriorRings.append( new QgsCompoundCurve() );
239  else
240  {
241  clear();
242  return false;
243  }
244  if ( !mInteriorRings.back()->fromWkt( childWkt ) )
245  {
246  clear();
247  return false;
248  }
249  }
250 
251  if ( mInteriorRings.isEmpty() )
252  {
253  clear();
254  return false;
255  }
256 
257  mExteriorRing.reset( mInteriorRings.takeFirst() );
258 
259  //scan through rings and check if dimensionality of rings is different to CurvePolygon.
260  //if so, update the type dimensionality of the CurvePolygon to match
261  bool hasZ = false;
262  bool hasM = false;
263  if ( mExteriorRing )
264  {
265  hasZ = hasZ || mExteriorRing->is3D();
266  hasM = hasM || mExteriorRing->isMeasure();
267  }
268  for ( const QgsCurve *curve : std::as_const( mInteriorRings ) )
269  {
270  hasZ = hasZ || curve->is3D();
271  hasM = hasM || curve->isMeasure();
272  if ( hasZ && hasM )
273  break;
274  }
275  if ( hasZ )
276  addZValue( 0 );
277  if ( hasM )
278  addMValue( 0 );
279 
280  return true;
281 }
282 
284 {
285  if ( mExteriorRing )
286  {
287  return mExteriorRing->boundingBox();
288  }
289  return QgsRectangle();
290 }
291 
292 int QgsCurvePolygon::wkbSize( QgsAbstractGeometry::WkbFlags flags ) const
293 {
294  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
295  if ( mExteriorRing )
296  {
297  binarySize += mExteriorRing->wkbSize( flags );
298  }
299  for ( const QgsCurve *curve : mInteriorRings )
300  {
301  binarySize += curve->wkbSize( flags );
302  }
303  return binarySize;
304 }
305 
306 QByteArray QgsCurvePolygon::asWkb( WkbFlags flags ) const
307 {
308  QByteArray wkbArray;
309  wkbArray.resize( QgsCurvePolygon::wkbSize( flags ) );
310  QgsWkbPtr wkbPtr( wkbArray );
311  wkbPtr << static_cast<char>( QgsApplication::endian() );
312  wkbPtr << static_cast<quint32>( wkbType() );
313  wkbPtr << static_cast<quint32>( ( mExteriorRing ? 1 : 0 ) + mInteriorRings.size() );
314  if ( mExteriorRing )
315  {
316  wkbPtr << mExteriorRing->asWkb( flags );
317  }
318  for ( const QgsCurve *curve : mInteriorRings )
319  {
320  wkbPtr << curve->asWkb( flags );
321  }
322  return wkbArray;
323 }
324 
325 QString QgsCurvePolygon::asWkt( int precision ) const
326 {
327  QString wkt = wktTypeStr();
328 
329  if ( isEmpty() )
330  wkt += QLatin1String( " EMPTY" );
331  else
332  {
333  wkt += QLatin1String( " (" );
334  if ( mExteriorRing )
335  {
336  QString childWkt = mExteriorRing->asWkt( precision );
337  if ( qgsgeometry_cast<QgsLineString *>( mExteriorRing.get() ) )
338  {
339  // Type names of linear geometries are omitted
340  childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
341  }
342  wkt += childWkt + ',';
343  }
344  for ( const QgsCurve *curve : mInteriorRings )
345  {
346  QString childWkt = curve->asWkt( precision );
347  if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
348  {
349  // Type names of linear geometries are omitted
350  childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
351  }
352  wkt += childWkt + ',';
353  }
354  if ( wkt.endsWith( ',' ) )
355  {
356  wkt.chop( 1 ); // Remove last ','
357  }
358  wkt += ')';
359  }
360  return wkt;
361 }
362 
363 QDomElement QgsCurvePolygon::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
364 {
365  // GML2 does not support curves
366  QDomElement elemPolygon = doc.createElementNS( ns, QStringLiteral( "Polygon" ) );
367 
368  if ( isEmpty() )
369  return elemPolygon;
370 
371  QDomElement elemOuterBoundaryIs = doc.createElementNS( ns, QStringLiteral( "outerBoundaryIs" ) );
372  std::unique_ptr< QgsLineString > exteriorLineString( exteriorRing()->curveToLine() );
373  QDomElement outerRing = exteriorLineString->asGml2( doc, precision, ns, axisOrder );
374  outerRing.toElement().setTagName( QStringLiteral( "LinearRing" ) );
375  elemOuterBoundaryIs.appendChild( outerRing );
376  elemPolygon.appendChild( elemOuterBoundaryIs );
377  std::unique_ptr< QgsLineString > interiorLineString;
378  for ( int i = 0, n = numInteriorRings(); i < n; ++i )
379  {
380  QDomElement elemInnerBoundaryIs = doc.createElementNS( ns, QStringLiteral( "innerBoundaryIs" ) );
381  interiorLineString.reset( interiorRing( i )->curveToLine() );
382  QDomElement innerRing = interiorLineString->asGml2( doc, precision, ns, axisOrder );
383  innerRing.toElement().setTagName( QStringLiteral( "LinearRing" ) );
384  elemInnerBoundaryIs.appendChild( innerRing );
385  elemPolygon.appendChild( elemInnerBoundaryIs );
386  }
387  return elemPolygon;
388 }
389 
390 QDomElement QgsCurvePolygon::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
391 {
392  QDomElement elemCurvePolygon = doc.createElementNS( ns, QStringLiteral( "Polygon" ) );
393 
394  if ( isEmpty() )
395  return elemCurvePolygon;
396 
397  QDomElement elemExterior = doc.createElementNS( ns, QStringLiteral( "exterior" ) );
398  QDomElement curveElem = exteriorRing()->asGml3( doc, precision, ns, axisOrder );
399  if ( curveElem.tagName() == QLatin1String( "LineString" ) )
400  {
401  curveElem.setTagName( QStringLiteral( "LinearRing" ) );
402  }
403  elemExterior.appendChild( curveElem );
404  elemCurvePolygon.appendChild( elemExterior );
405 
406  for ( int i = 0, n = numInteriorRings(); i < n; ++i )
407  {
408  QDomElement elemInterior = doc.createElementNS( ns, QStringLiteral( "interior" ) );
409  QDomElement innerRing = interiorRing( i )->asGml3( doc, precision, ns, axisOrder );
410  if ( innerRing.tagName() == QLatin1String( "LineString" ) )
411  {
412  innerRing.setTagName( QStringLiteral( "LinearRing" ) );
413  }
414  elemInterior.appendChild( innerRing );
415  elemCurvePolygon.appendChild( elemInterior );
416  }
417  return elemCurvePolygon;
418 }
419 
421 {
422  json coordinates( json::array( ) );
423  if ( auto *lExteriorRing = exteriorRing() )
424  {
425  std::unique_ptr< QgsLineString > exteriorLineString( lExteriorRing->curveToLine() );
426  QgsPointSequence exteriorPts;
427  exteriorLineString->points( exteriorPts );
428  coordinates.push_back( QgsGeometryUtils::pointsToJson( exteriorPts, precision ) );
429 
430  std::unique_ptr< QgsLineString > interiorLineString;
431  for ( int i = 0, n = numInteriorRings(); i < n; ++i )
432  {
433  interiorLineString.reset( interiorRing( i )->curveToLine() );
434  QgsPointSequence interiorPts;
435  interiorLineString->points( interiorPts );
436  coordinates.push_back( QgsGeometryUtils::pointsToJson( interiorPts, precision ) );
437  }
438  }
439  return
440  {
441  { "type", "Polygon" },
442  { "coordinates", coordinates }
443  };
444 }
445 
446 QString QgsCurvePolygon::asKml( int precision ) const
447 {
448  QString kml;
449  kml.append( QLatin1String( "<Polygon>" ) );
450  if ( mExteriorRing )
451  {
452  kml.append( QLatin1String( "<outerBoundaryIs>" ) );
453  kml.append( mExteriorRing->asKml( precision ) );
454  kml.append( QLatin1String( "</outerBoundaryIs>" ) );
455  }
456  const QVector<QgsCurve *> &interiorRings = mInteriorRings;
457  for ( const QgsCurve *ring : interiorRings )
458  {
459  kml.append( QLatin1String( "<innerBoundaryIs>" ) );
460  kml.append( ring->asKml( precision ) );
461  kml.append( QLatin1String( "</innerBoundaryIs>" ) );
462  }
463  kml.append( QLatin1String( "</Polygon>" ) );
464  return kml;
465 }
466 
468 {
469  // normalize rings
470  if ( mExteriorRing )
471  mExteriorRing->normalize();
472 
473  for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
474  {
475  ring->normalize();
476  }
477 
478  // sort rings
479  std::sort( mInteriorRings.begin(), mInteriorRings.end(), []( const QgsCurve * a, const QgsCurve * b )
480  {
481  return a->compareTo( b ) > 0;
482  } );
483 
484  // normalize ring orientation
485  forceRHR();
486 }
487 
488 double QgsCurvePolygon::area() const
489 {
490  if ( !mExteriorRing )
491  {
492  return 0.0;
493  }
494 
495  double totalArea = 0.0;
496 
497  if ( mExteriorRing->isRing() )
498  {
499  double area = 0.0;
500  mExteriorRing->sumUpArea( area );
501  totalArea += std::fabs( area );
502  }
503 
504  for ( const QgsCurve *ring : mInteriorRings )
505  {
506  double area = 0.0;
507  if ( ring->isRing() )
508  {
509  ring->sumUpArea( area );
510  totalArea -= std::fabs( area );
511  }
512  }
513  return totalArea;
514 }
515 
517 {
518  if ( !mExteriorRing )
519  return 0.0;
520 
521  //sum perimeter of rings
522  double perimeter = mExteriorRing->length();
523  for ( const QgsCurve *ring : mInteriorRings )
524  {
525  perimeter += ring->length();
526  }
527  return perimeter;
528 }
529 
531 {
532  std::unique_ptr< QgsPolygon > polygon( new QgsPolygon() );
533  if ( !mExteriorRing )
534  return polygon.release();
535 
536  polygon->setExteriorRing( exteriorRing()->curveToLine() );
537  QVector<QgsCurve *> interiors;
538  int n = numInteriorRings();
539  interiors.reserve( n );
540  for ( int i = 0; i < n; ++i )
541  {
542  interiors.append( interiorRing( i )->curveToLine() );
543  }
544  polygon->setInteriorRings( interiors );
545  return polygon.release();
546 }
547 
549 {
550  if ( !mExteriorRing )
551  return nullptr;
552 
553  if ( mInteriorRings.isEmpty() )
554  {
555  return mExteriorRing->clone();
556  }
557  else
558  {
559  QgsMultiCurve *multiCurve = new QgsMultiCurve();
560  int nInteriorRings = mInteriorRings.size();
561  multiCurve->reserve( nInteriorRings + 1 );
562  multiCurve->addGeometry( mExteriorRing->clone() );
563  for ( int i = 0; i < nInteriorRings; ++i )
564  {
565  multiCurve->addGeometry( mInteriorRings.at( i )->clone() );
566  }
567  return multiCurve;
568  }
569 }
570 
571 QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
572 {
573  if ( !mExteriorRing )
574  return nullptr;
575 
576 
577  std::unique_ptr< QgsCurvePolygon > polygon( createEmptyWithSameType() );
578 
579  // exterior ring
580  auto exterior = std::unique_ptr<QgsCurve> { static_cast< QgsCurve *>( mExteriorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) ) };
581 
582  if ( !exterior )
583  return nullptr;
584 
585  polygon->mExteriorRing = std::move( exterior );
586 
587  //interior rings
588  for ( auto interior : mInteriorRings )
589  {
590  if ( !interior )
591  continue;
592 
593  QgsCurve *gridifiedInterior = static_cast< QgsCurve * >( interior->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
594 
595  if ( !gridifiedInterior )
596  continue;
597 
598  polygon->mInteriorRings.append( gridifiedInterior );
599  }
600 
601  return polygon.release();
602 
603 }
604 
605 bool QgsCurvePolygon::removeDuplicateNodes( double epsilon, bool useZValues )
606 {
607  bool result = false;
608  auto cleanRing = [epsilon, useZValues ]( QgsCurve * ring )->bool
609  {
610  if ( ring->numPoints() <= 4 )
611  return false;
612 
613  if ( ring->removeDuplicateNodes( epsilon, useZValues ) )
614  {
615  QgsPoint startPoint;
616  Qgis::VertexType type;
617  ring->pointAt( 0, startPoint, type );
618  // ensure ring is properly closed - if we removed the final node, it may no longer be properly closed
619  ring->moveVertex( QgsVertexId( -1, -1, ring->numPoints() - 1 ), startPoint );
620  return true;
621  }
622 
623  return false;
624  };
625  if ( mExteriorRing )
626  {
627  result = cleanRing( mExteriorRing.get() );
628  }
629  for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
630  {
631  if ( cleanRing( ring ) ) result = true;
632  }
633  return result;
634 }
635 
637 {
638  if ( !mExteriorRing && mInteriorRings.empty() )
639  return false;
640 
641  // if we already have the bounding box calculated, then this check is trivial!
642  if ( !mBoundingBox.isNull() )
643  {
644  return mBoundingBox.intersects( rectangle );
645  }
646 
647  // loop through each ring and test the bounding box intersection.
648  // This gives us a chance to use optimisations which may be present on the individual
649  // ring geometry subclasses, and at worst it will cause a calculation of the bounding box
650  // of each individual ring geometry which we would have to do anyway... (and these
651  // bounding boxes are cached, so would be reused without additional expense)
652  if ( mExteriorRing && mExteriorRing->boundingBoxIntersects( rectangle ) )
653  return true;
654 
655  for ( const QgsCurve *ring : mInteriorRings )
656  {
657  if ( ring->boundingBoxIntersects( rectangle ) )
658  return true;
659  }
660 
661  // even if we don't intersect the bounding box of any rings, we may still intersect the
662  // bounding box of the overall polygon (we are considering worst case scenario here and
663  // the polygon is invalid, with rings outside the exterior ring!)
664  // so here we fall back to the non-optimised base class check which has to first calculate
665  // the overall bounding box of the polygon..
666  return QgsSurface::boundingBoxIntersects( rectangle );
667 }
668 
669 QgsPolygon *QgsCurvePolygon::toPolygon( double tolerance, SegmentationToleranceType toleranceType ) const
670 {
671  std::unique_ptr< QgsPolygon > poly( new QgsPolygon() );
672  if ( !mExteriorRing )
673  {
674  return poly.release();
675  }
676 
677  poly->setExteriorRing( mExteriorRing->curveToLine( tolerance, toleranceType ) );
678 
679  QVector<QgsCurve *> rings;
680  rings.reserve( mInteriorRings.size() );
681  for ( const QgsCurve *ring : mInteriorRings )
682  {
683  rings.push_back( ring->curveToLine( tolerance, toleranceType ) );
684  }
685  poly->setInteriorRings( rings );
686  return poly.release();
687 }
688 
690 {
691  if ( !ring )
692  {
693  return;
694  }
695  mExteriorRing.reset( ring );
696 
697  //set proper wkb type
699  {
701  }
703  {
705  }
706 
707  //match dimensionality for rings
708  for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
709  {
710  if ( is3D() )
711  ring->addZValue();
712  else
713  ring->dropZValue();
714 
715  if ( isMeasure() )
716  ring->addMValue();
717  else
718  ring->dropMValue();
719  }
720  clearCache();
721 }
722 
723 void QgsCurvePolygon::setInteriorRings( const QVector<QgsCurve *> &rings )
724 {
725  qDeleteAll( mInteriorRings );
726  mInteriorRings.clear();
727 
728  //add rings one-by-one, so that they can each be converted to the correct type for the CurvePolygon
729  for ( QgsCurve *ring : rings )
730  {
731  addInteriorRing( ring );
732  }
733  clearCache();
734 }
735 
737 {
738  if ( !ring )
739  return;
740 
741  //ensure dimensionality of ring matches curve polygon
742  if ( !is3D() )
743  ring->dropZValue();
744  else if ( !ring->is3D() )
745  ring->addZValue();
746 
747  if ( !isMeasure() )
748  ring->dropMValue();
749  else if ( !ring->isMeasure() )
750  ring->addMValue();
751 
752  mInteriorRings.append( ring );
753  clearCache();
754 }
755 
757 {
758  if ( nr < 0 || nr >= mInteriorRings.size() )
759  {
760  return false;
761  }
762  delete mInteriorRings.takeAt( nr );
763  clearCache();
764  return true;
765 }
766 
767 void QgsCurvePolygon::removeInteriorRings( double minimumAllowedArea )
768 {
769  for ( int ringIndex = mInteriorRings.size() - 1; ringIndex >= 0; --ringIndex )
770  {
771  if ( minimumAllowedArea < 0 )
772  delete mInteriorRings.takeAt( ringIndex );
773  else
774  {
775  double area = 0.0;
776  mInteriorRings.at( ringIndex )->sumUpArea( area );
777  if ( area < minimumAllowedArea )
778  delete mInteriorRings.takeAt( ringIndex );
779  }
780  }
781 
782  clearCache();
783 }
784 
786 {
787  QVector<QgsCurve *> validRings;
788  validRings.reserve( mInteriorRings.size() );
789  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
790  {
791  if ( !curve->isRing() )
792  {
793  // remove invalid rings
794  delete curve;
795  }
796  else
797  {
798  validRings << curve;
799  }
800  }
801  mInteriorRings = validRings;
802 }
803 
805 {
806  if ( mExteriorRing && mExteriorRing->orientation() != QgsCurve::Clockwise )
807  {
808  // flip exterior ring orientation
809  std::unique_ptr< QgsCurve > flipped( mExteriorRing->reversed() );
810  mExteriorRing = std::move( flipped );
811  }
812 
813  QVector<QgsCurve *> validRings;
814  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
815  {
816  if ( curve && curve->orientation() != QgsCurve::CounterClockwise )
817  {
818  // flip interior ring orientation
819  QgsCurve *flipped = curve->reversed();
820  validRings << flipped;
821  delete curve;
822  }
823  else
824  {
825  validRings << curve;
826  }
827  }
828  mInteriorRings = validRings;
829 }
830 
832 {
833  QPainterPath p;
834  if ( mExteriorRing )
835  {
836  QPainterPath ring = mExteriorRing->asQPainterPath();
837  ring.closeSubpath();
838  p.addPath( ring );
839  }
840 
841  for ( const QgsCurve *ring : mInteriorRings )
842  {
843  QPainterPath ringPath = ring->asQPainterPath();
844  ringPath.closeSubpath();
845  p.addPath( ringPath );
846  }
847 
848  return p;
849 }
850 
851 void QgsCurvePolygon::draw( QPainter &p ) const
852 {
853  if ( !mExteriorRing )
854  return;
855 
856  if ( mInteriorRings.empty() )
857  {
858  mExteriorRing->drawAsPolygon( p );
859  }
860  else
861  {
862  QPainterPath path;
863  mExteriorRing->addToPainterPath( path );
864 
865  for ( const QgsCurve *ring : mInteriorRings )
866  {
867  ring->addToPainterPath( path );
868  }
869  p.drawPath( path );
870  }
871 }
872 
874 {
875  if ( mExteriorRing )
876  {
877  mExteriorRing->transform( ct, d, transformZ );
878  }
879 
880  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
881  {
882  curve->transform( ct, d, transformZ );
883  }
884  clearCache();
885 }
886 
887 void QgsCurvePolygon::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
888 {
889  if ( mExteriorRing )
890  {
891  mExteriorRing->transform( t, zTranslate, zScale, mTranslate, mScale );
892  }
893 
894  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
895  {
896  curve->transform( t, zTranslate, zScale, mTranslate, mScale );
897  }
898  clearCache();
899 }
900 
902 {
903  QgsCoordinateSequence sequence;
904  sequence.append( QgsRingSequence() );
905 
906  if ( mExteriorRing )
907  {
908  sequence.back().append( QgsPointSequence() );
909  mExteriorRing->points( sequence.back().back() );
910  }
911 
912  for ( const QgsCurve *ring : mInteriorRings )
913  {
914  sequence.back().append( QgsPointSequence() );
915  ring->points( sequence.back().back() );
916  }
917 
918  return sequence;
919 }
920 
922 {
923  int count = 0;
924 
925  if ( mExteriorRing )
926  {
927  count += mExteriorRing->nCoordinates();
928  }
929 
930  for ( const QgsCurve *ring : mInteriorRings )
931  {
932  count += ring->nCoordinates();
933  }
934 
935  return count;
936 }
937 
939 {
940  if ( id.part != 0 )
941  return -1;
942 
943  if ( id.ring < 0 || id.ring >= ringCount() || !mExteriorRing )
944  return -1;
945 
946  int number = 0;
947  if ( id.ring == 0 )
948  {
949  return mExteriorRing->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
950  }
951  else
952  {
953  number += mExteriorRing->numPoints();
954  }
955 
956  for ( int i = 0; i < mInteriorRings.count(); ++i )
957  {
958  if ( id.ring == i + 1 )
959  {
960  int partNumber = mInteriorRings.at( i )->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
961  if ( partNumber == -1 )
962  return -1;
963  return number + partNumber;
964  }
965  else
966  {
967  number += mInteriorRings.at( i )->numPoints();
968  }
969  }
970  return -1; // should not happen
971 }
972 
974 {
975  if ( !mExteriorRing )
976  return true;
977 
978  return mExteriorRing->isEmpty();
979 }
980 
981 double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
982 {
983  if ( !mExteriorRing )
984  {
985  return -1;
986  }
987  QVector<QgsCurve *> segmentList;
988  segmentList.append( mExteriorRing.get() );
989  segmentList.append( mInteriorRings );
990  return QgsGeometryUtils::closestSegmentFromComponents( segmentList, QgsGeometryUtils::Ring, pt, segmentPt, vertexAfter, leftOf, epsilon );
991 }
992 
994 {
995  if ( !mExteriorRing || vId.ring >= 1 + mInteriorRings.size() )
996  {
997  return false;
998  }
999 
1000  if ( vId.ring < 0 )
1001  {
1002  vId.ring = 0;
1003  vId.vertex = -1;
1004  if ( vId.part < 0 )
1005  {
1006  vId.part = 0;
1007  }
1008  return mExteriorRing->nextVertex( vId, vertex );
1009  }
1010  else
1011  {
1012  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings[vId.ring - 1];
1013 
1014  if ( ring->nextVertex( vId, vertex ) )
1015  {
1016  return true;
1017  }
1018  ++vId.ring;
1019  vId.vertex = -1;
1020  if ( vId.ring >= 1 + mInteriorRings.size() )
1021  {
1022  return false;
1023  }
1024  ring = mInteriorRings[ vId.ring - 1 ];
1025  return ring->nextVertex( vId, vertex );
1026  }
1027 }
1028 
1029 void ringAdjacentVertices( const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
1030 {
1031  int n = curve->numPoints();
1032  if ( vertex.vertex < 0 || vertex.vertex >= n )
1033  {
1034  previousVertex = QgsVertexId();
1035  nextVertex = QgsVertexId();
1036  return;
1037  }
1038 
1039  if ( vertex.vertex == 0 && n < 3 )
1040  {
1041  previousVertex = QgsVertexId();
1042  }
1043  else if ( vertex.vertex == 0 )
1044  {
1045  previousVertex = QgsVertexId( vertex.part, vertex.ring, n - 2 );
1046  }
1047  else
1048  {
1049  previousVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex - 1 );
1050  }
1051  if ( vertex.vertex == n - 1 && n < 3 )
1052  {
1053  nextVertex = QgsVertexId();
1054  }
1055  else if ( vertex.vertex == n - 1 )
1056  {
1057  nextVertex = QgsVertexId( vertex.part, vertex.ring, 1 );
1058  }
1059  else
1060  {
1061  nextVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex + 1 );
1062  }
1063 }
1064 
1065 void QgsCurvePolygon::adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex ) const
1066 {
1067  if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1068  {
1069  previousVertex = QgsVertexId();
1070  nextVertex = QgsVertexId();
1071  return;
1072  }
1073 
1074  if ( vertex.ring == 0 )
1075  {
1076  ringAdjacentVertices( mExteriorRing.get(), vertex, previousVertex, nextVertex );
1077  }
1078  else
1079  {
1080  ringAdjacentVertices( mInteriorRings.at( vertex.ring - 1 ), vertex, previousVertex, nextVertex );
1081  }
1082 }
1083 
1085 {
1086  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1087  {
1088  return false;
1089  }
1090 
1091  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1092  int n = ring->numPoints();
1093  bool success = ring->insertVertex( QgsVertexId( 0, 0, vId.vertex ), vertex );
1094  if ( !success )
1095  {
1096  return false;
1097  }
1098 
1099  // If first or last vertex is inserted, re-sync the last/first vertex
1100  if ( vId.vertex == 0 )
1101  ring->moveVertex( QgsVertexId( 0, 0, n ), vertex );
1102  else if ( vId.vertex == n )
1103  ring->moveVertex( QgsVertexId( 0, 0, 0 ), vertex );
1104 
1105  clearCache();
1106 
1107  return true;
1108 }
1109 
1111 {
1112  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1113  {
1114  return false;
1115  }
1116 
1117  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1118  int n = ring->numPoints();
1119  bool success = ring->moveVertex( vId, newPos );
1120  if ( success )
1121  {
1122  // If first or last vertex is moved, also move the last/first vertex
1123  if ( vId.vertex == 0 )
1124  ring->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos );
1125  else if ( vId.vertex == n - 1 )
1126  ring->moveVertex( QgsVertexId( vId.part, vId.ring, 0 ), newPos );
1127  clearCache();
1128  }
1129  return success;
1130 }
1131 
1133 {
1134  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1135  {
1136  return false;
1137  }
1138 
1139  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1140  int n = ring->numPoints();
1141  if ( n <= 4 )
1142  {
1143  //no points will be left in ring, so remove whole ring
1144  if ( vId.ring == 0 )
1145  {
1146  mExteriorRing.reset();
1147  if ( !mInteriorRings.isEmpty() )
1148  {
1149  mExteriorRing.reset( mInteriorRings.takeFirst() );
1150  }
1151  }
1152  else
1153  {
1154  removeInteriorRing( vId.ring - 1 );
1155  }
1156  clearCache();
1157  return true;
1158  }
1159 
1160  bool success = ring->deleteVertex( vId );
1161  if ( success )
1162  {
1163  // If first or last vertex is removed, re-sync the last/first vertex
1164  // Do not use "n - 2", but "ring->numPoints() - 1" as more than one vertex
1165  // may have been deleted (e.g. with CircularString)
1166  if ( vId.vertex == 0 )
1167  ring->moveVertex( QgsVertexId( 0, 0, ring->numPoints() - 1 ), ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) );
1168  else if ( vId.vertex == n - 1 )
1169  ring->moveVertex( QgsVertexId( 0, 0, 0 ), ring->vertexAt( QgsVertexId( 0, 0, ring->numPoints() - 1 ) ) );
1170  clearCache();
1171  }
1172  return success;
1173 }
1174 
1176 {
1177  if ( mExteriorRing && mExteriorRing->hasCurvedSegments() )
1178  {
1179  return true;
1180  }
1181 
1182  for ( const QgsCurve *ring : mInteriorRings )
1183  {
1184  if ( ring->hasCurvedSegments() )
1185  {
1186  return true;
1187  }
1188  }
1189  return false;
1190 }
1191 
1193 {
1194  return toPolygon( tolerance, toleranceType );
1195 }
1196 
1198 {
1199  if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1200  {
1201  //makes no sense - conversion of false to double!
1202  return false;
1203  }
1204 
1205  QgsCurve *ring = vertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[vertex.ring - 1];
1206  return ring->vertexAngle( vertex );
1207 }
1208 
1209 int QgsCurvePolygon::vertexCount( int /*part*/, int ring ) const
1210 {
1211  return ring == 0 ? mExteriorRing->vertexCount() : mInteriorRings[ring - 1]->vertexCount();
1212 }
1213 
1215 {
1216  return ( nullptr != mExteriorRing ) + mInteriorRings.size();
1217 }
1218 
1220 {
1221  return ringCount() > 0 ? 1 : 0;
1222 }
1223 
1225 {
1226  return id.ring == 0 ? mExteriorRing->vertexAt( id ) : mInteriorRings[id.ring - 1]->vertexAt( id );
1227 }
1228 
1229 double QgsCurvePolygon::segmentLength( QgsVertexId startVertex ) const
1230 {
1231  if ( !mExteriorRing || startVertex.ring < 0 || startVertex.ring >= 1 + mInteriorRings.size() )
1232  {
1233  return 0.0;
1234  }
1235 
1236  const QgsCurve *ring = startVertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[startVertex.ring - 1];
1237  return ring->segmentLength( startVertex );
1238 }
1239 
1240 bool QgsCurvePolygon::addZValue( double zValue )
1241 {
1242  if ( QgsWkbTypes::hasZ( mWkbType ) )
1243  return false;
1244 
1246 
1247  if ( mExteriorRing )
1248  mExteriorRing->addZValue( zValue );
1249  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1250  {
1251  curve->addZValue( zValue );
1252  }
1253  clearCache();
1254  return true;
1255 }
1256 
1257 bool QgsCurvePolygon::addMValue( double mValue )
1258 {
1259  if ( QgsWkbTypes::hasM( mWkbType ) )
1260  return false;
1261 
1263 
1264  if ( mExteriorRing )
1265  mExteriorRing->addMValue( mValue );
1266  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1267  {
1268  curve->addMValue( mValue );
1269  }
1270  clearCache();
1271  return true;
1272 }
1273 
1275 {
1276  if ( !is3D() )
1277  return false;
1278 
1280  if ( mExteriorRing )
1281  mExteriorRing->dropZValue();
1282  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1283  {
1284  curve->dropZValue();
1285  }
1286  clearCache();
1287  return true;
1288 }
1289 
1291 {
1292  if ( !isMeasure() )
1293  return false;
1294 
1296  if ( mExteriorRing )
1297  mExteriorRing->dropMValue();
1298  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1299  {
1300  curve->dropMValue();
1301  }
1302  clearCache();
1303  return true;
1304 }
1305 
1307 {
1308  if ( mExteriorRing )
1309  mExteriorRing->swapXy();
1310  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1311  {
1312  curve->swapXy();
1313  }
1314  clearCache();
1315 }
1316 
1318 {
1319  return clone();
1320 }
1321 
1323 {
1324  if ( !transformer )
1325  return false;
1326 
1327  bool res = true;
1328  if ( mExteriorRing )
1329  res = mExteriorRing->transform( transformer, feedback );
1330 
1331  if ( !res || ( feedback && feedback->isCanceled() ) )
1332  {
1333  clearCache();
1334  return false;
1335  }
1336 
1337  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1338  {
1339  res = curve->transform( transformer );
1340 
1341  if ( feedback && feedback->isCanceled() )
1342  res = false;
1343 
1344  if ( !res )
1345  break;
1346  }
1347  clearCache();
1348  return res;
1349 }
1350 
1351 void QgsCurvePolygon::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1352 {
1353  if ( mExteriorRing )
1354  mExteriorRing->filterVertices( filter );
1355 
1356  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1357  {
1358  curve->filterVertices( filter );
1359  }
1360  clearCache();
1361 }
1362 
1363 void QgsCurvePolygon::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1364 {
1365  if ( mExteriorRing )
1366  mExteriorRing->transformVertices( transform );
1367 
1368  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1369  {
1370  curve->transformVertices( transform );
1371  }
1372  clearCache();
1373 }
1374 
1376 {
1377  return 1 + mInteriorRings.count();
1378 }
1379 
1381 {
1382  if ( index == 0 )
1383  return mExteriorRing.get();
1384  else
1385  return mInteriorRings.at( index - 1 );
1386 }
1387 
1389 {
1390  const QgsCurvePolygon *otherPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( other );
1391  if ( !otherPolygon )
1392  return -1;
1393 
1394  if ( mExteriorRing && !otherPolygon->mExteriorRing )
1395  return 1;
1396  else if ( !mExteriorRing && otherPolygon->mExteriorRing )
1397  return -1;
1398  else if ( mExteriorRing && otherPolygon->mExteriorRing )
1399  {
1400  int shellComp = mExteriorRing->compareTo( otherPolygon->mExteriorRing.get() );
1401  if ( shellComp != 0 )
1402  {
1403  return shellComp;
1404  }
1405  }
1406 
1407  const int nHole1 = mInteriorRings.size();
1408  const int nHole2 = otherPolygon->mInteriorRings.size();
1409  if ( nHole1 < nHole2 )
1410  {
1411  return -1;
1412  }
1413  if ( nHole1 > nHole2 )
1414  {
1415  return 1;
1416  }
1417 
1418  for ( int i = 0; i < nHole1; i++ )
1419  {
1420  const int holeComp = mInteriorRings.at( i )->compareTo( otherPolygon->mInteriorRings.at( i ) );
1421  if ( holeComp != 0 )
1422  {
1423  return holeComp;
1424  }
1425  }
1426 
1427  return 0;
1428 }
VertexType
Types of vertex.
Definition: qgis.h:990
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition: qgis.h:896
An abstract base class for classes which transform geometries by transforming input points to output ...
Abstract base class for all geometries.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual bool moveVertex(QgsVertexId position, const QgsPoint &newPos)=0
Moves a vertex within the geometry.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
virtual bool dropMValue()=0
Drops any measure values which exist in the geometry.
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
virtual bool boundingBoxIntersects(const QgsRectangle &rectangle) const SIP_HOLDGIL
Returns true if the bounding box of this geometry intersects with a rectangle.
AxisOrder
Axis order for GML generation.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
QgsAbstractGeometry & operator=(const QgsAbstractGeometry &geom)
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
virtual bool insertVertex(QgsVertexId position, const QgsPoint &vertex)=0
Inserts a vertex into the geometry.
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.
virtual bool deleteVertex(QgsVertexId position)=0
Deletes a vertex within the geometry.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
virtual bool dropZValue()=0
Drops any z-dimensions which exist in the geometry.
virtual double segmentLength(QgsVertexId startVertex) const =0
Returns the length of the segment of the geometry which begins at startVertex.
QgsWkbTypes::Type mWkbType
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
static endian_t endian()
Returns whether this machine uses big or little endian.
Circular string geometry type.
Compound curve geometry type.
A const WKB pointer.
Definition: qgswkbptr.h:138
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
Class for doing transforms between two map coordinate systems.
Curve polygon geometry type.
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
QgsCoordinateSequence coordinateSequence() const override
Retrieves the sequence of geometries, rings and nodes.
int ringCount(int part=0) const override SIP_HOLDGIL
Returns the number of rings of which this geometry is built.
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb()
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
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.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate rotation angle for a vertex.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
QgsAbstractGeometry * boundary() const override
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
QgsCurvePolygon * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
QPainterPath asQPainterPath() const override
Returns the geometry represented as a QPainterPath.
void swapXy() override
Swaps the x and y coordinates from the geometry.
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
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.
QVector< QgsCurve * > mInteriorRings
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
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.
QgsRectangle calculateBoundingBox() const override
Default calculator for the minimal bounding box for the geometry.
void normalize() final SIP_HOLDGIL
Reorganizes the geometry into a normalized form (or "canonical" form).
bool operator!=(const QgsAbstractGeometry &other) const override
void removeInteriorRings(double minimumAllowedArea=-1)
Removes the interior rings from the polygon.
QgsCurvePolygon * clone() const override
Clones the geometry by performing a deep copy.
QgsCurvePolygon * toCurveType() const override
Returns the geometry converted to the more generic curve type.
void forceRHR()
Forces the geometry to respect the Right-Hand-Rule, in which the area that is bounded by the polygon ...
QString asKml(int precision=17) const override
Returns a KML representation of the geometry.
void clear() override
Clears the geometry, ie reset it to a null geometry.
virtual void setExteriorRing(QgsCurve *ring)
Sets the exterior ring of the polygon.
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.
int nCoordinates() const override
Returns the number of nodes contained in the geometry.
int partCount() const override SIP_HOLDGIL
Returns count of parts contained in the geometry.
void filterVertices(const std::function< bool(const QgsPoint &) > &filter) override
Filters the vertices from the geometry in place, removing any which do not return true for the filter...
void adjacentVertices(QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex) const override
Returns the vertices adjacent to a specified vertex within a geometry.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
QgsCurvePolygon & operator=(const QgsCurvePolygon &p)
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
virtual void addInteriorRing(QgsCurve *ring)
Adds an interior ring to the geometry (takes ownership)
~QgsCurvePolygon() override
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
int dimension() const override SIP_HOLDGIL
Returns the inherent dimension of the geometry.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
void removeInvalidRings()
Removes any interior rings which are not valid from the polygon.
QgsAbstractGeometry * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
int vertexNumberFromVertexId(QgsVertexId id) const override
Returns the vertex number corresponding to a vertex id.
double area() const override SIP_HOLDGIL
Returns the planar, 2-dimensional area of the geometry.
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.
int childCount() const override
Returns number of child geometries (for geometries with child geometries) or child points (for geomet...
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
QString geometryType() const override SIP_HOLDGIL
Returns a unique string representing the geometry type.
QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML3 representation of the geometry.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
bool dropMValue() override
Drops any measure values which exist in the geometry.
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
bool boundingBoxIntersects(const QgsRectangle &rectangle) const override SIP_HOLDGIL
Returns true if the bounding box of this geometry intersects with a rectangle.
QgsAbstractGeometry * childGeometry(int index) const override
Returns pointer to child geometry (for geometries with child geometries - i.e.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
void setInteriorRings(const QVector< QgsCurve * > &rings)
Sets all interior rings (takes ownership)
double perimeter() const override SIP_HOLDGIL
Returns the planar, 2-dimensional perimeter of the geometry.
bool operator==(const QgsAbstractGeometry &other) const override
QgsPolygon * surfaceToPolygon() const override
Gets a polygon representation of this surface.
bool removeInteriorRing(int ringIndex)
Removes an interior ring from the polygon.
std::unique_ptr< QgsCurve > mExteriorRing
int compareToSameClass(const QgsAbstractGeometry *other) const final
Compares to an other geometry of the same class, and returns a integer for sorting of the two geometr...
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
virtual int numPoints() const =0
Returns the number of points in the curve.
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
Definition: qgscurve.cpp:198
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
@ Clockwise
Clockwise orientation.
Definition: qgscurve.h:266
@ CounterClockwise
Counter-clockwise orientation.
Definition: qgscurve.h:267
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
Definition: qgscurve.cpp:87
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
void reserve(int size) SIP_HOLDGIL
Attempts to allocate memory for at least size geometries.
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 (...
static json pointsToJson(const QgsPointSequence &points, int precision)
Returns coordinates as json object.
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 double closestSegmentFromComponents(T &container, ComponentType ctype, const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon)
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
Multi curve geometry collection.
Definition: qgsmulticurve.h:30
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
Definition: qgspoint.cpp:442
Polygon geometry type.
Definition: qgspolygon.h:34
A rectangle specified with double values.
Definition: qgsrectangle.h:42
bool intersects(const QgsRectangle &rect) const SIP_HOLDGIL
Returns true when rectangle intersects with other rectangle.
Definition: qgsrectangle.h:349
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:479
Surface geometry type.
Definition: qgssurface.h:34
QgsRectangle mBoundingBox
Definition: qgssurface.h:81
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgssurface.cpp:43
WKB pointer handler.
Definition: qgswkbptr.h:44
static GeometryType geometryType(Type type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:968
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1130
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
static Type dropZ(Type type) SIP_HOLDGIL
Drops the z dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1237
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:732
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1176
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1080
static Type addM(Type type) SIP_HOLDGIL
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1201
static Type dropM(Type type) SIP_HOLDGIL
Drops the m dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1255
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether 'thepoint' is left or right of the line from 'p1' to 'p2'. Negative values mean left ...
Definition: MathUtils.cpp:292
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
void ringAdjacentVertices(const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex)
int precision
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:31
int vertex
Vertex number.
Definition: qgsvertexid.h:95
int part
Part number.
Definition: qgsvertexid.h:89
int ring
Ring number.
Definition: qgsvertexid.h:92