QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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  const double p = perimeter();
533  if ( qgsDoubleNear( p, 0.0 ) )
534  return 0.0;
535 
536  return 4.0 * M_PI * area() / pow( p, 2.0 );
537 }
538 
540 {
541  std::unique_ptr< QgsPolygon > polygon( new QgsPolygon() );
542  if ( !mExteriorRing )
543  return polygon.release();
544 
545  polygon->setExteriorRing( exteriorRing()->curveToLine() );
546  QVector<QgsCurve *> interiors;
547  int n = numInteriorRings();
548  interiors.reserve( n );
549  for ( int i = 0; i < n; ++i )
550  {
551  interiors.append( interiorRing( i )->curveToLine() );
552  }
553  polygon->setInteriorRings( interiors );
554  return polygon.release();
555 }
556 
558 {
559  if ( !mExteriorRing )
560  return nullptr;
561 
562  if ( mInteriorRings.isEmpty() )
563  {
564  return mExteriorRing->clone();
565  }
566  else
567  {
568  QgsMultiCurve *multiCurve = new QgsMultiCurve();
569  int nInteriorRings = mInteriorRings.size();
570  multiCurve->reserve( nInteriorRings + 1 );
571  multiCurve->addGeometry( mExteriorRing->clone() );
572  for ( int i = 0; i < nInteriorRings; ++i )
573  {
574  multiCurve->addGeometry( mInteriorRings.at( i )->clone() );
575  }
576  return multiCurve;
577  }
578 }
579 
580 QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
581 {
582  if ( !mExteriorRing )
583  return nullptr;
584 
585 
586  std::unique_ptr< QgsCurvePolygon > polygon( createEmptyWithSameType() );
587 
588  // exterior ring
589  auto exterior = std::unique_ptr<QgsCurve> { static_cast< QgsCurve *>( mExteriorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) ) };
590 
591  if ( !exterior )
592  return nullptr;
593 
594  polygon->mExteriorRing = std::move( exterior );
595 
596  //interior rings
597  for ( auto interior : mInteriorRings )
598  {
599  if ( !interior )
600  continue;
601 
602  QgsCurve *gridifiedInterior = static_cast< QgsCurve * >( interior->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
603 
604  if ( !gridifiedInterior )
605  continue;
606 
607  polygon->mInteriorRings.append( gridifiedInterior );
608  }
609 
610  return polygon.release();
611 
612 }
613 
614 bool QgsCurvePolygon::removeDuplicateNodes( double epsilon, bool useZValues )
615 {
616  bool result = false;
617  auto cleanRing = [epsilon, useZValues ]( QgsCurve * ring )->bool
618  {
619  if ( ring->numPoints() <= 4 )
620  return false;
621 
622  if ( ring->removeDuplicateNodes( epsilon, useZValues ) )
623  {
624  QgsPoint startPoint;
625  Qgis::VertexType type;
626  ring->pointAt( 0, startPoint, type );
627  // ensure ring is properly closed - if we removed the final node, it may no longer be properly closed
628  ring->moveVertex( QgsVertexId( -1, -1, ring->numPoints() - 1 ), startPoint );
629  return true;
630  }
631 
632  return false;
633  };
634  if ( mExteriorRing )
635  {
636  result = cleanRing( mExteriorRing.get() );
637  }
638  for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
639  {
640  if ( cleanRing( ring ) ) result = true;
641  }
642  return result;
643 }
644 
646 {
647  if ( !mExteriorRing && mInteriorRings.empty() )
648  return false;
649 
650  // if we already have the bounding box calculated, then this check is trivial!
651  if ( !mBoundingBox.isNull() )
652  {
653  return mBoundingBox.intersects( rectangle );
654  }
655 
656  // loop through each ring and test the bounding box intersection.
657  // This gives us a chance to use optimisations which may be present on the individual
658  // ring geometry subclasses, and at worst it will cause a calculation of the bounding box
659  // of each individual ring geometry which we would have to do anyway... (and these
660  // bounding boxes are cached, so would be reused without additional expense)
661  if ( mExteriorRing && mExteriorRing->boundingBoxIntersects( rectangle ) )
662  return true;
663 
664  for ( const QgsCurve *ring : mInteriorRings )
665  {
666  if ( ring->boundingBoxIntersects( rectangle ) )
667  return true;
668  }
669 
670  // even if we don't intersect the bounding box of any rings, we may still intersect the
671  // bounding box of the overall polygon (we are considering worst case scenario here and
672  // the polygon is invalid, with rings outside the exterior ring!)
673  // so here we fall back to the non-optimised base class check which has to first calculate
674  // the overall bounding box of the polygon..
675  return QgsSurface::boundingBoxIntersects( rectangle );
676 }
677 
678 QgsPolygon *QgsCurvePolygon::toPolygon( double tolerance, SegmentationToleranceType toleranceType ) const
679 {
680  std::unique_ptr< QgsPolygon > poly( new QgsPolygon() );
681  if ( !mExteriorRing )
682  {
683  return poly.release();
684  }
685 
686  poly->setExteriorRing( mExteriorRing->curveToLine( tolerance, toleranceType ) );
687 
688  QVector<QgsCurve *> rings;
689  rings.reserve( mInteriorRings.size() );
690  for ( const QgsCurve *ring : mInteriorRings )
691  {
692  rings.push_back( ring->curveToLine( tolerance, toleranceType ) );
693  }
694  poly->setInteriorRings( rings );
695  return poly.release();
696 }
697 
699 {
700  if ( !ring )
701  {
702  return;
703  }
704  mExteriorRing.reset( ring );
705 
706  //set proper wkb type
708  {
710  }
712  {
714  }
715 
716  //match dimensionality for rings
717  for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
718  {
719  if ( is3D() )
720  ring->addZValue();
721  else
722  ring->dropZValue();
723 
724  if ( isMeasure() )
725  ring->addMValue();
726  else
727  ring->dropMValue();
728  }
729  clearCache();
730 }
731 
732 void QgsCurvePolygon::setInteriorRings( const QVector<QgsCurve *> &rings )
733 {
734  qDeleteAll( mInteriorRings );
735  mInteriorRings.clear();
736 
737  //add rings one-by-one, so that they can each be converted to the correct type for the CurvePolygon
738  for ( QgsCurve *ring : rings )
739  {
740  addInteriorRing( ring );
741  }
742  clearCache();
743 }
744 
746 {
747  if ( !ring )
748  return;
749 
750  //ensure dimensionality of ring matches curve polygon
751  if ( !is3D() )
752  ring->dropZValue();
753  else if ( !ring->is3D() )
754  ring->addZValue();
755 
756  if ( !isMeasure() )
757  ring->dropMValue();
758  else if ( !ring->isMeasure() )
759  ring->addMValue();
760 
761  mInteriorRings.append( ring );
762  clearCache();
763 }
764 
766 {
767  if ( nr < 0 || nr >= mInteriorRings.size() )
768  {
769  return false;
770  }
771  delete mInteriorRings.takeAt( nr );
772  clearCache();
773  return true;
774 }
775 
776 void QgsCurvePolygon::removeInteriorRings( double minimumAllowedArea )
777 {
778  for ( int ringIndex = mInteriorRings.size() - 1; ringIndex >= 0; --ringIndex )
779  {
780  if ( minimumAllowedArea < 0 )
781  delete mInteriorRings.takeAt( ringIndex );
782  else
783  {
784  double area = 0.0;
785  mInteriorRings.at( ringIndex )->sumUpArea( area );
786  if ( area < minimumAllowedArea )
787  delete mInteriorRings.takeAt( ringIndex );
788  }
789  }
790 
791  clearCache();
792 }
793 
795 {
796  QVector<QgsCurve *> validRings;
797  validRings.reserve( mInteriorRings.size() );
798  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
799  {
800  if ( !curve->isRing() )
801  {
802  // remove invalid rings
803  delete curve;
804  }
805  else
806  {
807  validRings << curve;
808  }
809  }
810  mInteriorRings = validRings;
811 }
812 
814 {
815  forceClockwise();
816 }
817 
819 {
821  {
822  // flip exterior ring orientation
823  std::unique_ptr< QgsCurve > flipped( mExteriorRing->reversed() );
824  mExteriorRing = std::move( flipped );
825  }
826 
827  QVector<QgsCurve *> validRings;
828  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
829  {
830  if ( curve && curve->orientation() != Qgis::AngularDirection::CounterClockwise )
831  {
832  // flip interior ring orientation
833  QgsCurve *flipped = curve->reversed();
834  validRings << flipped;
835  delete curve;
836  }
837  else
838  {
839  validRings << curve;
840  }
841  }
842  mInteriorRings = validRings;
843 }
844 
846 {
848  {
849  // flip exterior ring orientation
850  mExteriorRing.reset( mExteriorRing->reversed() );
851  }
852 
853  QVector<QgsCurve *> validRings;
854  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
855  {
856  if ( curve && curve->orientation() != Qgis::AngularDirection::Clockwise )
857  {
858  // flip interior ring orientation
859  QgsCurve *flipped = curve->reversed();
860  validRings << flipped;
861  delete curve;
862  }
863  else
864  {
865  validRings << curve;
866  }
867  }
868  mInteriorRings = validRings;
869 }
870 
872 {
873  QPainterPath p;
874  if ( mExteriorRing )
875  {
876  QPainterPath ring = mExteriorRing->asQPainterPath();
877  ring.closeSubpath();
878  p.addPath( ring );
879  }
880 
881  for ( const QgsCurve *ring : mInteriorRings )
882  {
883  QPainterPath ringPath = ring->asQPainterPath();
884  ringPath.closeSubpath();
885  p.addPath( ringPath );
886  }
887 
888  return p;
889 }
890 
891 void QgsCurvePolygon::draw( QPainter &p ) const
892 {
893  if ( !mExteriorRing )
894  return;
895 
896  if ( mInteriorRings.empty() )
897  {
898  mExteriorRing->drawAsPolygon( p );
899  }
900  else
901  {
902  QPainterPath path;
903  mExteriorRing->addToPainterPath( path );
904 
905  for ( const QgsCurve *ring : mInteriorRings )
906  {
907  ring->addToPainterPath( path );
908  }
909  p.drawPath( path );
910  }
911 }
912 
914 {
915  if ( mExteriorRing )
916  {
917  mExteriorRing->transform( ct, d, transformZ );
918  }
919 
920  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
921  {
922  curve->transform( ct, d, transformZ );
923  }
924  clearCache();
925 }
926 
927 void QgsCurvePolygon::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
928 {
929  if ( mExteriorRing )
930  {
931  mExteriorRing->transform( t, zTranslate, zScale, mTranslate, mScale );
932  }
933 
934  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
935  {
936  curve->transform( t, zTranslate, zScale, mTranslate, mScale );
937  }
938  clearCache();
939 }
940 
942 {
943  QgsCoordinateSequence sequence;
944  sequence.append( QgsRingSequence() );
945 
946  if ( mExteriorRing )
947  {
948  sequence.back().append( QgsPointSequence() );
949  mExteriorRing->points( sequence.back().back() );
950  }
951 
952  for ( const QgsCurve *ring : mInteriorRings )
953  {
954  sequence.back().append( QgsPointSequence() );
955  ring->points( sequence.back().back() );
956  }
957 
958  return sequence;
959 }
960 
962 {
963  int count = 0;
964 
965  if ( mExteriorRing )
966  {
967  count += mExteriorRing->nCoordinates();
968  }
969 
970  for ( const QgsCurve *ring : mInteriorRings )
971  {
972  count += ring->nCoordinates();
973  }
974 
975  return count;
976 }
977 
979 {
980  if ( id.part != 0 )
981  return -1;
982 
983  if ( id.ring < 0 || id.ring >= ringCount() || !mExteriorRing )
984  return -1;
985 
986  int number = 0;
987  if ( id.ring == 0 )
988  {
989  return mExteriorRing->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
990  }
991  else
992  {
993  number += mExteriorRing->numPoints();
994  }
995 
996  for ( int i = 0; i < mInteriorRings.count(); ++i )
997  {
998  if ( id.ring == i + 1 )
999  {
1000  int partNumber = mInteriorRings.at( i )->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
1001  if ( partNumber == -1 )
1002  return -1;
1003  return number + partNumber;
1004  }
1005  else
1006  {
1007  number += mInteriorRings.at( i )->numPoints();
1008  }
1009  }
1010  return -1; // should not happen
1011 }
1012 
1014 {
1015  if ( !mExteriorRing )
1016  return true;
1017 
1018  return mExteriorRing->isEmpty();
1019 }
1020 
1021 double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1022 {
1023  if ( !mExteriorRing )
1024  {
1025  return -1;
1026  }
1027  QVector<QgsCurve *> segmentList;
1028  segmentList.append( mExteriorRing.get() );
1029  segmentList.append( mInteriorRings );
1030  return QgsGeometryUtils::closestSegmentFromComponents( segmentList, QgsGeometryUtils::Ring, pt, segmentPt, vertexAfter, leftOf, epsilon );
1031 }
1032 
1034 {
1035  if ( !mExteriorRing || vId.ring >= 1 + mInteriorRings.size() )
1036  {
1037  return false;
1038  }
1039 
1040  if ( vId.ring < 0 )
1041  {
1042  vId.ring = 0;
1043  vId.vertex = -1;
1044  if ( vId.part < 0 )
1045  {
1046  vId.part = 0;
1047  }
1048  return mExteriorRing->nextVertex( vId, vertex );
1049  }
1050  else
1051  {
1052  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings[vId.ring - 1];
1053 
1054  if ( ring->nextVertex( vId, vertex ) )
1055  {
1056  return true;
1057  }
1058  ++vId.ring;
1059  vId.vertex = -1;
1060  if ( vId.ring >= 1 + mInteriorRings.size() )
1061  {
1062  return false;
1063  }
1064  ring = mInteriorRings[ vId.ring - 1 ];
1065  return ring->nextVertex( vId, vertex );
1066  }
1067 }
1068 
1069 void ringAdjacentVertices( const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
1070 {
1071  int n = curve->numPoints();
1072  if ( vertex.vertex < 0 || vertex.vertex >= n )
1073  {
1074  previousVertex = QgsVertexId();
1075  nextVertex = QgsVertexId();
1076  return;
1077  }
1078 
1079  if ( vertex.vertex == 0 && n < 3 )
1080  {
1081  previousVertex = QgsVertexId();
1082  }
1083  else if ( vertex.vertex == 0 )
1084  {
1085  previousVertex = QgsVertexId( vertex.part, vertex.ring, n - 2 );
1086  }
1087  else
1088  {
1089  previousVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex - 1 );
1090  }
1091  if ( vertex.vertex == n - 1 && n < 3 )
1092  {
1093  nextVertex = QgsVertexId();
1094  }
1095  else if ( vertex.vertex == n - 1 )
1096  {
1097  nextVertex = QgsVertexId( vertex.part, vertex.ring, 1 );
1098  }
1099  else
1100  {
1101  nextVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex + 1 );
1102  }
1103 }
1104 
1105 void QgsCurvePolygon::adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex ) const
1106 {
1107  if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1108  {
1109  previousVertex = QgsVertexId();
1110  nextVertex = QgsVertexId();
1111  return;
1112  }
1113 
1114  if ( vertex.ring == 0 )
1115  {
1116  ringAdjacentVertices( mExteriorRing.get(), vertex, previousVertex, nextVertex );
1117  }
1118  else
1119  {
1120  ringAdjacentVertices( mInteriorRings.at( vertex.ring - 1 ), vertex, previousVertex, nextVertex );
1121  }
1122 }
1123 
1125 {
1126  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1127  {
1128  return false;
1129  }
1130 
1131  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1132  int n = ring->numPoints();
1133  bool success = ring->insertVertex( QgsVertexId( 0, 0, vId.vertex ), vertex );
1134  if ( !success )
1135  {
1136  return false;
1137  }
1138 
1139  // If first or last vertex is inserted, re-sync the last/first vertex
1140  if ( vId.vertex == 0 )
1141  ring->moveVertex( QgsVertexId( 0, 0, n ), vertex );
1142  else if ( vId.vertex == n )
1143  ring->moveVertex( QgsVertexId( 0, 0, 0 ), vertex );
1144 
1145  clearCache();
1146 
1147  return true;
1148 }
1149 
1151 {
1152  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1153  {
1154  return false;
1155  }
1156 
1157  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1158  int n = ring->numPoints();
1159  bool success = ring->moveVertex( vId, newPos );
1160  if ( success )
1161  {
1162  // If first or last vertex is moved, also move the last/first vertex
1163  if ( vId.vertex == 0 )
1164  ring->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos );
1165  else if ( vId.vertex == n - 1 )
1166  ring->moveVertex( QgsVertexId( vId.part, vId.ring, 0 ), newPos );
1167  clearCache();
1168  }
1169  return success;
1170 }
1171 
1173 {
1174  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1175  {
1176  return false;
1177  }
1178 
1179  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1180  int n = ring->numPoints();
1181  if ( n <= 4 )
1182  {
1183  //no points will be left in ring, so remove whole ring
1184  if ( vId.ring == 0 )
1185  {
1186  mExteriorRing.reset();
1187  if ( !mInteriorRings.isEmpty() )
1188  {
1189  mExteriorRing.reset( mInteriorRings.takeFirst() );
1190  }
1191  }
1192  else
1193  {
1194  removeInteriorRing( vId.ring - 1 );
1195  }
1196  clearCache();
1197  return true;
1198  }
1199 
1200  bool success = ring->deleteVertex( vId );
1201  if ( success )
1202  {
1203  // If first or last vertex is removed, re-sync the last/first vertex
1204  // Do not use "n - 2", but "ring->numPoints() - 1" as more than one vertex
1205  // may have been deleted (e.g. with CircularString)
1206  if ( vId.vertex == 0 )
1207  ring->moveVertex( QgsVertexId( 0, 0, ring->numPoints() - 1 ), ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) );
1208  else if ( vId.vertex == n - 1 )
1209  ring->moveVertex( QgsVertexId( 0, 0, 0 ), ring->vertexAt( QgsVertexId( 0, 0, ring->numPoints() - 1 ) ) );
1210  clearCache();
1211  }
1212  return success;
1213 }
1214 
1216 {
1217  if ( mExteriorRing && mExteriorRing->hasCurvedSegments() )
1218  {
1219  return true;
1220  }
1221 
1222  for ( const QgsCurve *ring : mInteriorRings )
1223  {
1224  if ( ring->hasCurvedSegments() )
1225  {
1226  return true;
1227  }
1228  }
1229  return false;
1230 }
1231 
1233 {
1234  return toPolygon( tolerance, toleranceType );
1235 }
1236 
1238 {
1239  if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1240  {
1241  //makes no sense - conversion of false to double!
1242  return false;
1243  }
1244 
1245  QgsCurve *ring = vertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[vertex.ring - 1];
1246  return ring->vertexAngle( vertex );
1247 }
1248 
1249 int QgsCurvePolygon::vertexCount( int /*part*/, int ring ) const
1250 {
1251  return ring == 0 ? mExteriorRing->vertexCount() : mInteriorRings[ring - 1]->vertexCount();
1252 }
1253 
1255 {
1256  return ( nullptr != mExteriorRing ) + mInteriorRings.size();
1257 }
1258 
1260 {
1261  return ringCount() > 0 ? 1 : 0;
1262 }
1263 
1265 {
1266  return id.ring == 0 ? mExteriorRing->vertexAt( id ) : mInteriorRings[id.ring - 1]->vertexAt( id );
1267 }
1268 
1269 double QgsCurvePolygon::segmentLength( QgsVertexId startVertex ) const
1270 {
1271  if ( !mExteriorRing || startVertex.ring < 0 || startVertex.ring >= 1 + mInteriorRings.size() )
1272  {
1273  return 0.0;
1274  }
1275 
1276  const QgsCurve *ring = startVertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[startVertex.ring - 1];
1277  return ring->segmentLength( startVertex );
1278 }
1279 
1280 bool QgsCurvePolygon::addZValue( double zValue )
1281 {
1282  if ( QgsWkbTypes::hasZ( mWkbType ) )
1283  return false;
1284 
1286 
1287  if ( mExteriorRing )
1288  mExteriorRing->addZValue( zValue );
1289  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1290  {
1291  curve->addZValue( zValue );
1292  }
1293  clearCache();
1294  return true;
1295 }
1296 
1297 bool QgsCurvePolygon::addMValue( double mValue )
1298 {
1299  if ( QgsWkbTypes::hasM( mWkbType ) )
1300  return false;
1301 
1303 
1304  if ( mExteriorRing )
1305  mExteriorRing->addMValue( mValue );
1306  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1307  {
1308  curve->addMValue( mValue );
1309  }
1310  clearCache();
1311  return true;
1312 }
1313 
1315 {
1316  if ( !is3D() )
1317  return false;
1318 
1320  if ( mExteriorRing )
1321  mExteriorRing->dropZValue();
1322  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1323  {
1324  curve->dropZValue();
1325  }
1326  clearCache();
1327  return true;
1328 }
1329 
1331 {
1332  if ( !isMeasure() )
1333  return false;
1334 
1336  if ( mExteriorRing )
1337  mExteriorRing->dropMValue();
1338  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1339  {
1340  curve->dropMValue();
1341  }
1342  clearCache();
1343  return true;
1344 }
1345 
1347 {
1348  if ( mExteriorRing )
1349  mExteriorRing->swapXy();
1350  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1351  {
1352  curve->swapXy();
1353  }
1354  clearCache();
1355 }
1356 
1358 {
1359  return clone();
1360 }
1361 
1363 {
1364  if ( !transformer )
1365  return false;
1366 
1367  bool res = true;
1368  if ( mExteriorRing )
1369  res = mExteriorRing->transform( transformer, feedback );
1370 
1371  if ( !res || ( feedback && feedback->isCanceled() ) )
1372  {
1373  clearCache();
1374  return false;
1375  }
1376 
1377  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1378  {
1379  res = curve->transform( transformer );
1380 
1381  if ( feedback && feedback->isCanceled() )
1382  res = false;
1383 
1384  if ( !res )
1385  break;
1386  }
1387  clearCache();
1388  return res;
1389 }
1390 
1391 void QgsCurvePolygon::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1392 {
1393  if ( mExteriorRing )
1394  mExteriorRing->filterVertices( filter );
1395 
1396  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1397  {
1398  curve->filterVertices( filter );
1399  }
1400  clearCache();
1401 }
1402 
1403 void QgsCurvePolygon::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1404 {
1405  if ( mExteriorRing )
1406  mExteriorRing->transformVertices( transform );
1407 
1408  for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1409  {
1410  curve->transformVertices( transform );
1411  }
1412  clearCache();
1413 }
1414 
1416 {
1417  return 1 + mInteriorRings.count();
1418 }
1419 
1421 {
1422  if ( index == 0 )
1423  return mExteriorRing.get();
1424  else
1425  return mInteriorRings.at( index - 1 );
1426 }
1427 
1429 {
1430  const QgsCurvePolygon *otherPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( other );
1431  if ( !otherPolygon )
1432  return -1;
1433 
1434  if ( mExteriorRing && !otherPolygon->mExteriorRing )
1435  return 1;
1436  else if ( !mExteriorRing && otherPolygon->mExteriorRing )
1437  return -1;
1438  else if ( mExteriorRing && otherPolygon->mExteriorRing )
1439  {
1440  int shellComp = mExteriorRing->compareTo( otherPolygon->mExteriorRing.get() );
1441  if ( shellComp != 0 )
1442  {
1443  return shellComp;
1444  }
1445  }
1446 
1447  const int nHole1 = mInteriorRings.size();
1448  const int nHole2 = otherPolygon->mInteriorRings.size();
1449  if ( nHole1 < nHole2 )
1450  {
1451  return -1;
1452  }
1453  if ( nHole1 > nHole2 )
1454  {
1455  return 1;
1456  }
1457 
1458  for ( int i = 0; i < nHole1; i++ )
1459  {
1460  const int holeComp = mInteriorRings.at( i )->compareTo( otherPolygon->mInteriorRings.at( i ) );
1461  if ( holeComp != 0 )
1462  {
1463  return holeComp;
1464  }
1465  }
1466 
1467  return 0;
1468 }
@ CounterClockwise
Counter-clockwise direction.
@ Clockwise
Clockwise direction.
VertexType
Types of vertex.
Definition: qgis.h:1028
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition: qgis.h:930
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)
void forceCounterClockwise()
Forces the polygon to respect the exterior ring is counter-clockwise, interior rings are clockwise co...
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
void forceClockwise()
Forces the polygon to respect the exterior ring is clockwise, interior rings are counter-clockwise co...
double roundness() const
Returns the roundness of the curve polygon.
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.
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
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1578
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