QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
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 = qgis::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 : qgis::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 
467 double QgsCurvePolygon::area() const
468 {
469  if ( !mExteriorRing )
470  {
471  return 0.0;
472  }
473 
474  double totalArea = 0.0;
475 
476  if ( mExteriorRing->isRing() )
477  {
478  double area = 0.0;
479  mExteriorRing->sumUpArea( area );
480  totalArea += std::fabs( area );
481  }
482 
483  for ( const QgsCurve *ring : mInteriorRings )
484  {
485  double area = 0.0;
486  if ( ring->isRing() )
487  {
488  ring->sumUpArea( area );
489  totalArea -= std::fabs( area );
490  }
491  }
492  return totalArea;
493 }
494 
496 {
497  if ( !mExteriorRing )
498  return 0.0;
499 
500  //sum perimeter of rings
501  double perimeter = mExteriorRing->length();
502  for ( const QgsCurve *ring : mInteriorRings )
503  {
504  perimeter += ring->length();
505  }
506  return perimeter;
507 }
508 
510 {
511  std::unique_ptr< QgsPolygon > polygon( new QgsPolygon() );
512  if ( !mExteriorRing )
513  return polygon.release();
514 
515  polygon->setExteriorRing( exteriorRing()->curveToLine() );
516  QVector<QgsCurve *> interiors;
517  int n = numInteriorRings();
518  interiors.reserve( n );
519  for ( int i = 0; i < n; ++i )
520  {
521  interiors.append( interiorRing( i )->curveToLine() );
522  }
523  polygon->setInteriorRings( interiors );
524  return polygon.release();
525 }
526 
528 {
529  if ( !mExteriorRing )
530  return nullptr;
531 
532  if ( mInteriorRings.isEmpty() )
533  {
534  return mExteriorRing->clone();
535  }
536  else
537  {
538  QgsMultiCurve *multiCurve = new QgsMultiCurve();
539  int nInteriorRings = mInteriorRings.size();
540  multiCurve->reserve( nInteriorRings + 1 );
541  multiCurve->addGeometry( mExteriorRing->clone() );
542  for ( int i = 0; i < nInteriorRings; ++i )
543  {
544  multiCurve->addGeometry( mInteriorRings.at( i )->clone() );
545  }
546  return multiCurve;
547  }
548 }
549 
550 QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
551 {
552  if ( !mExteriorRing )
553  return nullptr;
554 
555 
556  std::unique_ptr< QgsCurvePolygon > polygon( createEmptyWithSameType() );
557 
558  // exterior ring
559  auto exterior = std::unique_ptr<QgsCurve> { static_cast< QgsCurve *>( mExteriorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) ) };
560 
561  if ( !exterior )
562  return nullptr;
563 
564  polygon->mExteriorRing = std::move( exterior );
565 
566  //interior rings
567  for ( auto interior : mInteriorRings )
568  {
569  if ( !interior )
570  continue;
571 
572  QgsCurve *gridifiedInterior = static_cast< QgsCurve * >( interior->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
573 
574  if ( !gridifiedInterior )
575  continue;
576 
577  polygon->mInteriorRings.append( gridifiedInterior );
578  }
579 
580  return polygon.release();
581 
582 }
583 
584 bool QgsCurvePolygon::removeDuplicateNodes( double epsilon, bool useZValues )
585 {
586  bool result = false;
587  auto cleanRing = [epsilon, useZValues ]( QgsCurve * ring )->bool
588  {
589  if ( ring->numPoints() <= 4 )
590  return false;
591 
592  if ( ring->removeDuplicateNodes( epsilon, useZValues ) )
593  {
594  QgsPoint startPoint;
596  ring->pointAt( 0, startPoint, type );
597  // ensure ring is properly closed - if we removed the final node, it may no longer be properly closed
598  ring->moveVertex( QgsVertexId( -1, -1, ring->numPoints() - 1 ), startPoint );
599  return true;
600  }
601 
602  return false;
603  };
604  if ( mExteriorRing )
605  {
606  result = cleanRing( mExteriorRing.get() );
607  }
608  for ( QgsCurve *ring : qgis::as_const( mInteriorRings ) )
609  {
610  if ( cleanRing( ring ) ) result = true;
611  }
612  return result;
613 }
614 
615 QgsPolygon *QgsCurvePolygon::toPolygon( double tolerance, SegmentationToleranceType toleranceType ) const
616 {
617  std::unique_ptr< QgsPolygon > poly( new QgsPolygon() );
618  if ( !mExteriorRing )
619  {
620  return poly.release();
621  }
622 
623  poly->setExteriorRing( mExteriorRing->curveToLine( tolerance, toleranceType ) );
624 
625  QVector<QgsCurve *> rings;
626  rings.reserve( mInteriorRings.size() );
627  for ( const QgsCurve *ring : mInteriorRings )
628  {
629  rings.push_back( ring->curveToLine( tolerance, toleranceType ) );
630  }
631  poly->setInteriorRings( rings );
632  return poly.release();
633 }
634 
636 {
637  if ( !ring )
638  {
639  return;
640  }
641  mExteriorRing.reset( ring );
642 
643  //set proper wkb type
645  {
647  }
649  {
651  }
652 
653  //match dimensionality for rings
654  for ( QgsCurve *ring : qgis::as_const( mInteriorRings ) )
655  {
656  if ( is3D() )
657  ring->addZValue();
658  else
659  ring->dropZValue();
660 
661  if ( isMeasure() )
662  ring->addMValue();
663  else
664  ring->dropMValue();
665  }
666  clearCache();
667 }
668 
669 void QgsCurvePolygon::setInteriorRings( const QVector<QgsCurve *> &rings )
670 {
671  qDeleteAll( mInteriorRings );
672  mInteriorRings.clear();
673 
674  //add rings one-by-one, so that they can each be converted to the correct type for the CurvePolygon
675  for ( QgsCurve *ring : rings )
676  {
677  addInteriorRing( ring );
678  }
679  clearCache();
680 }
681 
683 {
684  if ( !ring )
685  return;
686 
687  //ensure dimensionality of ring matches curve polygon
688  if ( !is3D() )
689  ring->dropZValue();
690  else if ( !ring->is3D() )
691  ring->addZValue();
692 
693  if ( !isMeasure() )
694  ring->dropMValue();
695  else if ( !ring->isMeasure() )
696  ring->addMValue();
697 
698  mInteriorRings.append( ring );
699  clearCache();
700 }
701 
703 {
704  if ( nr < 0 || nr >= mInteriorRings.size() )
705  {
706  return false;
707  }
708  delete mInteriorRings.takeAt( nr );
709  clearCache();
710  return true;
711 }
712 
713 void QgsCurvePolygon::removeInteriorRings( double minimumAllowedArea )
714 {
715  for ( int ringIndex = mInteriorRings.size() - 1; ringIndex >= 0; --ringIndex )
716  {
717  if ( minimumAllowedArea < 0 )
718  delete mInteriorRings.takeAt( ringIndex );
719  else
720  {
721  double area = 0.0;
722  mInteriorRings.at( ringIndex )->sumUpArea( area );
723  if ( area < minimumAllowedArea )
724  delete mInteriorRings.takeAt( ringIndex );
725  }
726  }
727 
728  clearCache();
729 }
730 
732 {
733  QVector<QgsCurve *> validRings;
734  validRings.reserve( mInteriorRings.size() );
735  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
736  {
737  if ( !curve->isRing() )
738  {
739  // remove invalid rings
740  delete curve;
741  }
742  else
743  {
744  validRings << curve;
745  }
746  }
747  mInteriorRings = validRings;
748 }
749 
751 {
752  if ( mExteriorRing && mExteriorRing->orientation() != QgsCurve::Clockwise )
753  {
754  // flip exterior ring orientation
755  std::unique_ptr< QgsCurve > flipped( mExteriorRing->reversed() );
756  mExteriorRing = std::move( flipped );
757  }
758 
759  QVector<QgsCurve *> validRings;
760  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
761  {
762  if ( curve && curve->orientation() != QgsCurve::CounterClockwise )
763  {
764  // flip interior ring orientation
765  QgsCurve *flipped = curve->reversed();
766  validRings << flipped;
767  delete curve;
768  }
769  else
770  {
771  validRings << curve;
772  }
773  }
774  mInteriorRings = validRings;
775 }
776 
778 {
779  QPainterPath p;
780  if ( mExteriorRing )
781  {
782  QPainterPath ring = mExteriorRing->asQPainterPath();
783  ring.closeSubpath();
784  p.addPath( ring );
785  }
786 
787  for ( const QgsCurve *ring : mInteriorRings )
788  {
789  QPainterPath ringPath = ring->asQPainterPath();
790  ringPath.closeSubpath();
791  p.addPath( ringPath );
792  }
793 
794  return p;
795 }
796 
797 void QgsCurvePolygon::draw( QPainter &p ) const
798 {
799  if ( !mExteriorRing )
800  return;
801 
802  if ( mInteriorRings.empty() )
803  {
804  mExteriorRing->drawAsPolygon( p );
805  }
806  else
807  {
808  QPainterPath path;
809  mExteriorRing->addToPainterPath( path );
810 
811  for ( const QgsCurve *ring : mInteriorRings )
812  {
813  ring->addToPainterPath( path );
814  }
815  p.drawPath( path );
816  }
817 }
818 
820 {
821  if ( mExteriorRing )
822  {
823  mExteriorRing->transform( ct, d, transformZ );
824  }
825 
826  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
827  {
828  curve->transform( ct, d, transformZ );
829  }
830  clearCache();
831 }
832 
833 void QgsCurvePolygon::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
834 {
835  if ( mExteriorRing )
836  {
837  mExteriorRing->transform( t, zTranslate, zScale, mTranslate, mScale );
838  }
839 
840  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
841  {
842  curve->transform( t, zTranslate, zScale, mTranslate, mScale );
843  }
844  clearCache();
845 }
846 
848 {
849  QgsCoordinateSequence sequence;
850  sequence.append( QgsRingSequence() );
851 
852  if ( mExteriorRing )
853  {
854  sequence.back().append( QgsPointSequence() );
855  mExteriorRing->points( sequence.back().back() );
856  }
857 
858  for ( const QgsCurve *ring : mInteriorRings )
859  {
860  sequence.back().append( QgsPointSequence() );
861  ring->points( sequence.back().back() );
862  }
863 
864  return sequence;
865 }
866 
868 {
869  int count = 0;
870 
871  if ( mExteriorRing )
872  {
873  count += mExteriorRing->nCoordinates();
874  }
875 
876  for ( const QgsCurve *ring : mInteriorRings )
877  {
878  count += ring->nCoordinates();
879  }
880 
881  return count;
882 }
883 
885 {
886  if ( id.part != 0 )
887  return -1;
888 
889  if ( id.ring < 0 || id.ring >= ringCount() )
890  return -1;
891 
892  int number = 0;
893  if ( id.ring == 0 && mExteriorRing )
894  {
895  return mExteriorRing->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
896  }
897  else
898  {
899  number += mExteriorRing->numPoints();
900  }
901 
902  for ( int i = 0; i < mInteriorRings.count(); ++i )
903  {
904  if ( id.ring == i + 1 )
905  {
906  int partNumber = mInteriorRings.at( i )->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
907  if ( partNumber == -1 )
908  return -1;
909  return number + partNumber;
910  }
911  else
912  {
913  number += mInteriorRings.at( i )->numPoints();
914  }
915  }
916  return -1; // should not happen
917 }
918 
920 {
921  if ( !mExteriorRing )
922  return true;
923 
924  return mExteriorRing->isEmpty();
925 }
926 
927 double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
928 {
929  if ( !mExteriorRing )
930  {
931  return -1;
932  }
933  QVector<QgsCurve *> segmentList;
934  segmentList.append( mExteriorRing.get() );
935  segmentList.append( mInteriorRings );
936  return QgsGeometryUtils::closestSegmentFromComponents( segmentList, QgsGeometryUtils::Ring, pt, segmentPt, vertexAfter, leftOf, epsilon );
937 }
938 
940 {
941  if ( !mExteriorRing || vId.ring >= 1 + mInteriorRings.size() )
942  {
943  return false;
944  }
945 
946  if ( vId.ring < 0 )
947  {
948  vId.ring = 0;
949  vId.vertex = -1;
950  if ( vId.part < 0 )
951  {
952  vId.part = 0;
953  }
954  return mExteriorRing->nextVertex( vId, vertex );
955  }
956  else
957  {
958  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings[vId.ring - 1];
959 
960  if ( ring->nextVertex( vId, vertex ) )
961  {
962  return true;
963  }
964  ++vId.ring;
965  vId.vertex = -1;
966  if ( vId.ring >= 1 + mInteriorRings.size() )
967  {
968  return false;
969  }
970  ring = mInteriorRings[ vId.ring - 1 ];
971  return ring->nextVertex( vId, vertex );
972  }
973 }
974 
975 void ringAdjacentVertices( const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
976 {
977  int n = curve->numPoints();
978  if ( vertex.vertex < 0 || vertex.vertex >= n )
979  {
980  previousVertex = QgsVertexId();
981  nextVertex = QgsVertexId();
982  return;
983  }
984 
985  if ( vertex.vertex == 0 && n < 3 )
986  {
987  previousVertex = QgsVertexId();
988  }
989  else if ( vertex.vertex == 0 )
990  {
991  previousVertex = QgsVertexId( vertex.part, vertex.ring, n - 2 );
992  }
993  else
994  {
995  previousVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex - 1 );
996  }
997  if ( vertex.vertex == n - 1 && n < 3 )
998  {
999  nextVertex = QgsVertexId();
1000  }
1001  else if ( vertex.vertex == n - 1 )
1002  {
1003  nextVertex = QgsVertexId( vertex.part, vertex.ring, 1 );
1004  }
1005  else
1006  {
1007  nextVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex + 1 );
1008  }
1009 }
1010 
1011 void QgsCurvePolygon::adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex ) const
1012 {
1013  if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1014  {
1015  previousVertex = QgsVertexId();
1016  nextVertex = QgsVertexId();
1017  return;
1018  }
1019 
1020  if ( vertex.ring == 0 )
1021  {
1022  ringAdjacentVertices( mExteriorRing.get(), vertex, previousVertex, nextVertex );
1023  }
1024  else
1025  {
1026  ringAdjacentVertices( mInteriorRings.at( vertex.ring - 1 ), vertex, previousVertex, nextVertex );
1027  }
1028 }
1029 
1031 {
1032  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1033  {
1034  return false;
1035  }
1036 
1037  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1038  int n = ring->numPoints();
1039  bool success = ring->insertVertex( QgsVertexId( 0, 0, vId.vertex ), vertex );
1040  if ( !success )
1041  {
1042  return false;
1043  }
1044 
1045  // If first or last vertex is inserted, re-sync the last/first vertex
1046  if ( vId.vertex == 0 )
1047  ring->moveVertex( QgsVertexId( 0, 0, n ), vertex );
1048  else if ( vId.vertex == n )
1049  ring->moveVertex( QgsVertexId( 0, 0, 0 ), vertex );
1050 
1051  clearCache();
1052 
1053  return true;
1054 }
1055 
1057 {
1058  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1059  {
1060  return false;
1061  }
1062 
1063  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1064  int n = ring->numPoints();
1065  bool success = ring->moveVertex( vId, newPos );
1066  if ( success )
1067  {
1068  // If first or last vertex is moved, also move the last/first vertex
1069  if ( vId.vertex == 0 )
1070  ring->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos );
1071  else if ( vId.vertex == n - 1 )
1072  ring->moveVertex( QgsVertexId( vId.part, vId.ring, 0 ), newPos );
1073  clearCache();
1074  }
1075  return success;
1076 }
1077 
1079 {
1080  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1081  {
1082  return false;
1083  }
1084 
1085  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1086  int n = ring->numPoints();
1087  if ( n <= 4 )
1088  {
1089  //no points will be left in ring, so remove whole ring
1090  if ( vId.ring == 0 )
1091  {
1092  mExteriorRing.reset();
1093  if ( !mInteriorRings.isEmpty() )
1094  {
1095  mExteriorRing.reset( mInteriorRings.takeFirst() );
1096  }
1097  }
1098  else
1099  {
1100  removeInteriorRing( vId.ring - 1 );
1101  }
1102  clearCache();
1103  return true;
1104  }
1105 
1106  bool success = ring->deleteVertex( vId );
1107  if ( success )
1108  {
1109  // If first or last vertex is removed, re-sync the last/first vertex
1110  // Do not use "n - 2", but "ring->numPoints() - 1" as more than one vertex
1111  // may have been deleted (e.g. with CircularString)
1112  if ( vId.vertex == 0 )
1113  ring->moveVertex( QgsVertexId( 0, 0, ring->numPoints() - 1 ), ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) );
1114  else if ( vId.vertex == n - 1 )
1115  ring->moveVertex( QgsVertexId( 0, 0, 0 ), ring->vertexAt( QgsVertexId( 0, 0, ring->numPoints() - 1 ) ) );
1116  clearCache();
1117  }
1118  return success;
1119 }
1120 
1122 {
1123  if ( mExteriorRing && mExteriorRing->hasCurvedSegments() )
1124  {
1125  return true;
1126  }
1127 
1128  for ( const QgsCurve *ring : mInteriorRings )
1129  {
1130  if ( ring->hasCurvedSegments() )
1131  {
1132  return true;
1133  }
1134  }
1135  return false;
1136 }
1137 
1139 {
1140  return toPolygon( tolerance, toleranceType );
1141 }
1142 
1144 {
1145  if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1146  {
1147  //makes no sense - conversion of false to double!
1148  return false;
1149  }
1150 
1151  QgsCurve *ring = vertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[vertex.ring - 1];
1152  return ring->vertexAngle( vertex );
1153 }
1154 
1155 int QgsCurvePolygon::vertexCount( int /*part*/, int ring ) const
1156 {
1157  return ring == 0 ? mExteriorRing->vertexCount() : mInteriorRings[ring - 1]->vertexCount();
1158 }
1159 
1161 {
1162  return ( nullptr != mExteriorRing ) + mInteriorRings.size();
1163 }
1164 
1166 {
1167  return ringCount() > 0 ? 1 : 0;
1168 }
1169 
1171 {
1172  return id.ring == 0 ? mExteriorRing->vertexAt( id ) : mInteriorRings[id.ring - 1]->vertexAt( id );
1173 }
1174 
1175 double QgsCurvePolygon::segmentLength( QgsVertexId startVertex ) const
1176 {
1177  if ( !mExteriorRing || startVertex.ring < 0 || startVertex.ring >= 1 + mInteriorRings.size() )
1178  {
1179  return 0.0;
1180  }
1181 
1182  const QgsCurve *ring = startVertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[startVertex.ring - 1];
1183  return ring->segmentLength( startVertex );
1184 }
1185 
1186 bool QgsCurvePolygon::addZValue( double zValue )
1187 {
1188  if ( QgsWkbTypes::hasZ( mWkbType ) )
1189  return false;
1190 
1192 
1193  if ( mExteriorRing )
1194  mExteriorRing->addZValue( zValue );
1195  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1196  {
1197  curve->addZValue( zValue );
1198  }
1199  clearCache();
1200  return true;
1201 }
1202 
1203 bool QgsCurvePolygon::addMValue( double mValue )
1204 {
1205  if ( QgsWkbTypes::hasM( mWkbType ) )
1206  return false;
1207 
1209 
1210  if ( mExteriorRing )
1211  mExteriorRing->addMValue( mValue );
1212  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1213  {
1214  curve->addMValue( mValue );
1215  }
1216  clearCache();
1217  return true;
1218 }
1219 
1221 {
1222  if ( !is3D() )
1223  return false;
1224 
1226  if ( mExteriorRing )
1227  mExteriorRing->dropZValue();
1228  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1229  {
1230  curve->dropZValue();
1231  }
1232  clearCache();
1233  return true;
1234 }
1235 
1237 {
1238  if ( !isMeasure() )
1239  return false;
1240 
1242  if ( mExteriorRing )
1243  mExteriorRing->dropMValue();
1244  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1245  {
1246  curve->dropMValue();
1247  }
1248  clearCache();
1249  return true;
1250 }
1251 
1253 {
1254  if ( mExteriorRing )
1255  mExteriorRing->swapXy();
1256  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1257  {
1258  curve->swapXy();
1259  }
1260  clearCache();
1261 }
1262 
1264 {
1265  return clone();
1266 }
1267 
1269 {
1270  if ( !transformer )
1271  return false;
1272 
1273  bool res = true;
1274  if ( mExteriorRing )
1275  res = mExteriorRing->transform( transformer, feedback );
1276 
1277  if ( !res || ( feedback && feedback->isCanceled() ) )
1278  {
1279  clearCache();
1280  return false;
1281  }
1282 
1283  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1284  {
1285  res = curve->transform( transformer );
1286 
1287  if ( feedback && feedback->isCanceled() )
1288  res = false;
1289 
1290  if ( !res )
1291  break;
1292  }
1293  clearCache();
1294  return res;
1295 }
1296 
1297 void QgsCurvePolygon::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1298 {
1299  if ( mExteriorRing )
1300  mExteriorRing->filterVertices( filter );
1301 
1302  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1303  {
1304  curve->filterVertices( filter );
1305  }
1306  clearCache();
1307 }
1308 
1309 void QgsCurvePolygon::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1310 {
1311  if ( mExteriorRing )
1312  mExteriorRing->transformVertices( transform );
1313 
1314  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1315  {
1316  curve->transformVertices( transform );
1317  }
1318  clearCache();
1319 }
1320 
1322 {
1323  return 1 + mInteriorRings.count();
1324 }
1325 
1327 {
1328  if ( index == 0 )
1329  return mExteriorRing.get();
1330  else
1331  return mInteriorRings.at( index - 1 );
1332 }
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.
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:130
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:54
Class for doing transforms between two map coordinate systems.
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
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.
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
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
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.
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
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
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:189
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
@ Clockwise
Clockwise orientation.
Definition: qgscurve.h:240
@ CounterClockwise
Counter-clockwise orientation.
Definition: qgscurve.h:241
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
Definition: qgscurve.cpp:78
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
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:38
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
Definition: qgspoint.cpp:438
Polygon geometry type.
Definition: qgspolygon.h:34
A rectangle specified with double values.
Definition: qgsrectangle.h:42
Surface geometry type.
Definition: qgssurface.h:34
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:938
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1100
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:1207
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:702
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1146
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1050
static Type addM(Type type) SIP_HOLDGIL
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1171
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:1225
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.
int vertex
Vertex number.
int part
Part number.
int ring
Ring number.
VertexType
Type of vertex.