QGIS API Documentation  3.2.0-Bonn (bc43194)
qgscurvepolygon.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscurvepolygon.cpp
3  ---------------------
4  begin : September 2014
5  copyright : (C) 2014 by Marco Hugentobler
6  email : marco at sourcepole dot ch
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgscurvepolygon.h"
19 #include "qgsapplication.h"
20 #include "qgscircularstring.h"
21 #include "qgscompoundcurve.h"
22 #include "qgsgeometryutils.h"
23 #include "qgslinestring.h"
24 #include "qgspolygon.h"
25 #include "qgswkbptr.h"
26 #include "qgsmulticurve.h"
27 #include <QPainter>
28 #include <QPainterPath>
29 #include <memory>
30 
32 {
34 }
35 
37 {
38  clear();
39 }
40 
42 {
43  auto result = qgis::make_unique< QgsCurvePolygon >();
44  result->mWkbType = mWkbType;
45  return result.release();
46 }
47 
49 {
50  return QStringLiteral( "CurvePolygon" );
51 }
52 
54 {
55  return 2;
56 }
57 
59  : QgsSurface( p )
60 
61 {
62  mWkbType = p.mWkbType;
63  if ( p.mExteriorRing )
64  {
65  mExteriorRing.reset( static_cast<QgsCurve *>( p.mExteriorRing->clone() ) );
66  }
67 
68  for ( const QgsCurve *ring : p.mInteriorRings )
69  {
70  mInteriorRings.push_back( static_cast<QgsCurve *>( ring->clone() ) );
71  }
72 }
73 
75 {
76  if ( &p != this )
77  {
78  clearCache();
80  if ( p.mExteriorRing )
81  {
82  mExteriorRing.reset( static_cast<QgsCurve *>( p.mExteriorRing->clone() ) );
83  }
84 
85  for ( const QgsCurve *ring : p.mInteriorRings )
86  {
87  mInteriorRings.push_back( static_cast<QgsCurve *>( ring->clone() ) );
88  }
89  }
90  return *this;
91 }
92 
94 {
95  const QgsCurvePolygon *otherPolygon = qgsgeometry_cast< const QgsCurvePolygon * >( &other );
96  if ( !otherPolygon )
97  return false;
98 
99  //run cheap checks first
100  if ( mWkbType != otherPolygon->mWkbType )
101  return false;
102 
103  if ( ( !mExteriorRing && otherPolygon->mExteriorRing ) || ( mExteriorRing && !otherPolygon->mExteriorRing ) )
104  return false;
105 
106  if ( mInteriorRings.count() != otherPolygon->mInteriorRings.count() )
107  return false;
108 
109  // compare rings
110  if ( mExteriorRing && otherPolygon->mExteriorRing )
111  {
112  if ( *mExteriorRing != *otherPolygon->mExteriorRing )
113  return false;
114  }
115 
116  for ( int i = 0; i < mInteriorRings.count(); ++i )
117  {
118  if ( ( !mInteriorRings.at( i ) && otherPolygon->mInteriorRings.at( i ) ) ||
119  ( mInteriorRings.at( i ) && !otherPolygon->mInteriorRings.at( i ) ) )
120  return false;
121 
122  if ( mInteriorRings.at( i ) && otherPolygon->mInteriorRings.at( i ) &&
123  *mInteriorRings.at( i ) != *otherPolygon->mInteriorRings.at( i ) )
124  return false;
125  }
126 
127  return true;
128 }
129 
131 {
132  return !operator==( other );
133 }
134 
136 {
137  return new QgsCurvePolygon( *this );
138 }
139 
141 {
143  mExteriorRing.reset();
144  qDeleteAll( mInteriorRings );
145  mInteriorRings.clear();
146  clearCache();
147 }
148 
149 
151 {
152  clear();
153  if ( !wkbPtr )
154  {
155  return false;
156  }
157 
158  QgsWkbTypes::Type type = wkbPtr.readHeader();
160  {
161  return false;
162  }
163  mWkbType = type;
164 
165  int nRings;
166  wkbPtr >> nRings;
167  std::unique_ptr< QgsCurve > currentCurve;
168  for ( int i = 0; i < nRings; ++i )
169  {
170  QgsWkbTypes::Type curveType = wkbPtr.readHeader();
171  wkbPtr -= 1 + sizeof( int );
172  QgsWkbTypes::Type flatCurveType = QgsWkbTypes::flatType( curveType );
173  if ( flatCurveType == QgsWkbTypes::LineString )
174  {
175  currentCurve.reset( new QgsLineString() );
176  }
177  else if ( flatCurveType == QgsWkbTypes::CircularString )
178  {
179  currentCurve.reset( new QgsCircularString() );
180  }
181  else if ( flatCurveType == QgsWkbTypes::CompoundCurve )
182  {
183  currentCurve.reset( new QgsCompoundCurve() );
184  }
185  else
186  {
187  return false;
188  }
189  currentCurve->fromWkb( wkbPtr ); // also updates wkbPtr
190  if ( i == 0 )
191  {
192  mExteriorRing = std::move( currentCurve );
193  }
194  else
195  {
196  mInteriorRings.append( currentCurve.release() );
197  }
198  }
199 
200  return true;
201 }
202 
203 bool QgsCurvePolygon::fromWkt( const QString &wkt )
204 {
205  clear();
206 
207  QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
208 
210  return false;
211 
212  mWkbType = parts.first;
213 
214  QString defaultChildWkbType = QStringLiteral( "LineString%1%2" ).arg( is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
215 
216  const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
217  for ( const QString &childWkt : blocks )
218  {
219  QPair<QgsWkbTypes::Type, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
220 
221  QgsWkbTypes::Type flatCurveType = QgsWkbTypes::flatType( childParts.first );
222  if ( flatCurveType == QgsWkbTypes::LineString )
223  mInteriorRings.append( new QgsLineString() );
224  else if ( flatCurveType == QgsWkbTypes::CircularString )
225  mInteriorRings.append( new QgsCircularString() );
226  else if ( flatCurveType == QgsWkbTypes::CompoundCurve )
227  mInteriorRings.append( new QgsCompoundCurve() );
228  else
229  {
230  clear();
231  return false;
232  }
233  if ( !mInteriorRings.back()->fromWkt( childWkt ) )
234  {
235  clear();
236  return false;
237  }
238  }
239 
240  if ( mInteriorRings.isEmpty() )
241  {
242  clear();
243  return false;
244  }
245 
246  mExteriorRing.reset( mInteriorRings.takeFirst() );
247 
248  //scan through rings and check if dimensionality of rings is different to CurvePolygon.
249  //if so, update the type dimensionality of the CurvePolygon to match
250  bool hasZ = false;
251  bool hasM = false;
252  if ( mExteriorRing )
253  {
254  hasZ = hasZ || mExteriorRing->is3D();
255  hasM = hasM || mExteriorRing->isMeasure();
256  }
257  for ( const QgsCurve *curve : qgis::as_const( mInteriorRings ) )
258  {
259  hasZ = hasZ || curve->is3D();
260  hasM = hasM || curve->isMeasure();
261  if ( hasZ && hasM )
262  break;
263  }
264  if ( hasZ )
265  addZValue( 0 );
266  if ( hasM )
267  addMValue( 0 );
268 
269  return true;
270 }
271 
273 {
274  if ( mExteriorRing )
275  {
276  return mExteriorRing->boundingBox();
277  }
278  return QgsRectangle();
279 }
280 
281 QByteArray QgsCurvePolygon::asWkb() const
282 {
283  int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
284  QVector<QByteArray> wkbForRings;
285  if ( mExteriorRing )
286  {
287  QByteArray wkb( mExteriorRing->asWkb() );
288  binarySize += wkb.length();
289  wkbForRings << wkb;
290  }
291  for ( const QgsCurve *curve : mInteriorRings )
292  {
293  QByteArray wkb( curve->asWkb() );
294  binarySize += wkb.length();
295  wkbForRings << wkb;
296  }
297 
298  QByteArray wkbArray;
299  wkbArray.resize( binarySize );
300  QgsWkbPtr wkbPtr( wkbArray );
301  wkbPtr << static_cast<char>( QgsApplication::endian() );
302  wkbPtr << static_cast<quint32>( wkbType() );
303  wkbPtr << static_cast<quint32>( wkbForRings.count() );
304  for ( const QByteArray &wkb : qgis::as_const( wkbForRings ) )
305  {
306  wkbPtr << wkb;
307  }
308  return wkbArray;
309 }
310 
311 QString QgsCurvePolygon::asWkt( int precision ) const
312 {
313  QString wkt = wktTypeStr() + " (";
314  if ( mExteriorRing )
315  {
316  QString childWkt = mExteriorRing->asWkt( precision );
317  if ( qgsgeometry_cast<QgsLineString *>( mExteriorRing.get() ) )
318  {
319  // Type names of linear geometries are omitted
320  childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
321  }
322  wkt += childWkt + ',';
323  }
324  for ( const QgsCurve *curve : mInteriorRings )
325  {
326  QString childWkt = curve->asWkt( precision );
327  if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
328  {
329  // Type names of linear geometries are omitted
330  childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
331  }
332  wkt += childWkt + ',';
333  }
334  if ( wkt.endsWith( ',' ) )
335  {
336  wkt.chop( 1 ); // Remove last ','
337  }
338  wkt += ')';
339  return wkt;
340 }
341 
342 QDomElement QgsCurvePolygon::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
343 {
344  // GML2 does not support curves
345  QDomElement elemPolygon = doc.createElementNS( ns, QStringLiteral( "Polygon" ) );
346 
347  if ( isEmpty() )
348  return elemPolygon;
349 
350  QDomElement elemOuterBoundaryIs = doc.createElementNS( ns, QStringLiteral( "outerBoundaryIs" ) );
351  std::unique_ptr< QgsLineString > exteriorLineString( exteriorRing()->curveToLine() );
352  QDomElement outerRing = exteriorLineString->asGml2( doc, precision, ns, axisOrder );
353  outerRing.toElement().setTagName( QStringLiteral( "LinearRing" ) );
354  elemOuterBoundaryIs.appendChild( outerRing );
355  elemPolygon.appendChild( elemOuterBoundaryIs );
356  std::unique_ptr< QgsLineString > interiorLineString;
357  for ( int i = 0, n = numInteriorRings(); i < n; ++i )
358  {
359  QDomElement elemInnerBoundaryIs = doc.createElementNS( ns, QStringLiteral( "innerBoundaryIs" ) );
360  interiorLineString.reset( interiorRing( i )->curveToLine() );
361  QDomElement innerRing = interiorLineString->asGml2( doc, precision, ns, axisOrder );
362  innerRing.toElement().setTagName( QStringLiteral( "LinearRing" ) );
363  elemInnerBoundaryIs.appendChild( innerRing );
364  elemPolygon.appendChild( elemInnerBoundaryIs );
365  }
366  return elemPolygon;
367 }
368 
369 QDomElement QgsCurvePolygon::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
370 {
371  QDomElement elemCurvePolygon = doc.createElementNS( ns, QStringLiteral( "Polygon" ) );
372 
373  if ( isEmpty() )
374  return elemCurvePolygon;
375 
376  QDomElement elemExterior = doc.createElementNS( ns, QStringLiteral( "exterior" ) );
377  QDomElement curveElem = exteriorRing()->asGml3( doc, precision, ns, axisOrder );
378  if ( curveElem.tagName() == QLatin1String( "LineString" ) )
379  {
380  curveElem.setTagName( QStringLiteral( "LinearRing" ) );
381  }
382  elemExterior.appendChild( curveElem );
383  elemCurvePolygon.appendChild( elemExterior );
384 
385  for ( int i = 0, n = numInteriorRings(); i < n; ++i )
386  {
387  QDomElement elemInterior = doc.createElementNS( ns, QStringLiteral( "interior" ) );
388  QDomElement innerRing = interiorRing( i )->asGml3( doc, precision, ns, axisOrder );
389  if ( innerRing.tagName() == QLatin1String( "LineString" ) )
390  {
391  innerRing.setTagName( QStringLiteral( "LinearRing" ) );
392  }
393  elemInterior.appendChild( innerRing );
394  elemCurvePolygon.appendChild( elemInterior );
395  }
396  return elemCurvePolygon;
397 }
398 
399 QString QgsCurvePolygon::asJson( int precision ) const
400 {
401  // GeoJSON does not support curves
402  QString json = QStringLiteral( "{\"type\": \"Polygon\", \"coordinates\": [" );
403 
404  std::unique_ptr< QgsLineString > exteriorLineString( exteriorRing()->curveToLine() );
405  QgsPointSequence exteriorPts;
406  exteriorLineString->points( exteriorPts );
407  json += QgsGeometryUtils::pointsToJSON( exteriorPts, precision ) + ", ";
408 
409  std::unique_ptr< QgsLineString > interiorLineString;
410  for ( int i = 0, n = numInteriorRings(); i < n; ++i )
411  {
412  interiorLineString.reset( interiorRing( i )->curveToLine() );
413  QgsPointSequence interiorPts;
414  interiorLineString->points( interiorPts );
415  json += QgsGeometryUtils::pointsToJSON( interiorPts, precision ) + ", ";
416  }
417  if ( json.endsWith( QLatin1String( ", " ) ) )
418  {
419  json.chop( 2 ); // Remove last ", "
420  }
421  json += QLatin1String( "] }" );
422  return json;
423 }
424 
425 double QgsCurvePolygon::area() const
426 {
427  if ( !mExteriorRing )
428  {
429  return 0.0;
430  }
431 
432  double totalArea = 0.0;
433 
434  if ( mExteriorRing->isRing() )
435  {
436  double area = 0.0;
437  mExteriorRing->sumUpArea( area );
438  totalArea += std::fabs( area );
439  }
440 
441  for ( const QgsCurve *ring : mInteriorRings )
442  {
443  double area = 0.0;
444  if ( ring->isRing() )
445  {
446  ring->sumUpArea( area );
447  totalArea -= std::fabs( area );
448  }
449  }
450  return totalArea;
451 }
452 
454 {
455  if ( !mExteriorRing )
456  return 0.0;
457 
458  //sum perimeter of rings
459  double perimeter = mExteriorRing->length();
460  for ( const QgsCurve *ring : mInteriorRings )
461  {
462  perimeter += ring->length();
463  }
464  return perimeter;
465 }
466 
468 {
469  std::unique_ptr< QgsPolygon > polygon( new QgsPolygon() );
470  if ( !mExteriorRing )
471  return polygon.release();
472 
473  polygon->setExteriorRing( exteriorRing()->curveToLine() );
474  QVector<QgsCurve *> interiors;
475  int n = numInteriorRings();
476  interiors.reserve( n );
477  for ( int i = 0; i < n; ++i )
478  {
479  interiors.append( interiorRing( i )->curveToLine() );
480  }
481  polygon->setInteriorRings( interiors );
482  return polygon.release();
483 }
484 
486 {
487  if ( !mExteriorRing )
488  return nullptr;
489 
490  if ( mInteriorRings.isEmpty() )
491  {
492  return mExteriorRing->clone();
493  }
494  else
495  {
496  QgsMultiCurve *multiCurve = new QgsMultiCurve();
497  multiCurve->addGeometry( mExteriorRing->clone() );
498  int nInteriorRings = mInteriorRings.size();
499  for ( int i = 0; i < nInteriorRings; ++i )
500  {
501  multiCurve->addGeometry( mInteriorRings.at( i )->clone() );
502  }
503  return multiCurve;
504  }
505 }
506 
507 QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
508 {
509  if ( !mExteriorRing )
510  return nullptr;
511 
512 
513  std::unique_ptr< QgsCurvePolygon > polygon( createEmptyWithSameType() );
514 
515  // exterior ring
516  auto exterior = std::unique_ptr<QgsCurve> { static_cast< QgsCurve *>( mExteriorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) ) };
517 
518  if ( !exterior )
519  return nullptr;
520 
521  polygon->mExteriorRing = std::move( exterior );
522 
523  //interior rings
524  for ( auto interior : mInteriorRings )
525  {
526  if ( !interior )
527  continue;
528 
529  QgsCurve *gridifiedInterior = static_cast< QgsCurve * >( interior->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
530 
531  if ( !gridifiedInterior )
532  continue;
533 
534  polygon->mInteriorRings.append( gridifiedInterior );
535  }
536 
537  return polygon.release();
538 
539 }
540 
541 bool QgsCurvePolygon::removeDuplicateNodes( double epsilon, bool useZValues )
542 {
543  bool result = false;
544  auto cleanRing = [epsilon, useZValues ]( QgsCurve * ring )->bool
545  {
546  if ( ring->numPoints() <= 4 )
547  return false;
548 
549  if ( ring->removeDuplicateNodes( epsilon, useZValues ) )
550  {
551  QgsPoint startPoint;
553  ring->pointAt( 0, startPoint, type );
554  // ensure ring is properly closed - if we removed the final node, it may no longer be properly closed
555  ring->moveVertex( QgsVertexId( -1, -1, ring->numPoints() - 1 ), startPoint );
556  return true;
557  }
558 
559  return false;
560  };
561  if ( mExteriorRing )
562  {
563  result = cleanRing( mExteriorRing.get() );
564  }
565  for ( QgsCurve *ring : qgis::as_const( mInteriorRings ) )
566  {
567  result = result || cleanRing( ring );
568  }
569  return result;
570 }
571 
572 QgsPolygon *QgsCurvePolygon::toPolygon( double tolerance, SegmentationToleranceType toleranceType ) const
573 {
574  std::unique_ptr< QgsPolygon > poly( new QgsPolygon() );
575  if ( !mExteriorRing )
576  {
577  return poly.release();
578  }
579 
580  poly->setExteriorRing( mExteriorRing->curveToLine( tolerance, toleranceType ) );
581 
582  QVector<QgsCurve *> rings;
583  for ( const QgsCurve *ring : mInteriorRings )
584  {
585  rings.push_back( ring->curveToLine( tolerance, toleranceType ) );
586  }
587  poly->setInteriorRings( rings );
588  return poly.release();
589 }
590 
592 {
593  if ( !ring )
594  {
595  return;
596  }
597  mExteriorRing.reset( ring );
598 
599  //set proper wkb type
601  {
603  }
605  {
607  }
608 
609  //match dimensionality for rings
610  for ( QgsCurve *ring : qgis::as_const( mInteriorRings ) )
611  {
612  if ( is3D() )
613  ring->addZValue();
614  else
615  ring->dropZValue();
616 
617  if ( isMeasure() )
618  ring->addMValue();
619  else
620  ring->dropMValue();
621  }
622  clearCache();
623 }
624 
625 void QgsCurvePolygon::setInteriorRings( const QVector<QgsCurve *> &rings )
626 {
627  qDeleteAll( mInteriorRings );
628  mInteriorRings.clear();
629 
630  //add rings one-by-one, so that they can each be converted to the correct type for the CurvePolygon
631  for ( QgsCurve *ring : rings )
632  {
633  addInteriorRing( ring );
634  }
635  clearCache();
636 }
637 
639 {
640  if ( !ring )
641  return;
642 
643  //ensure dimensionality of ring matches curve polygon
644  if ( !is3D() )
645  ring->dropZValue();
646  else if ( !ring->is3D() )
647  ring->addZValue();
648 
649  if ( !isMeasure() )
650  ring->dropMValue();
651  else if ( !ring->isMeasure() )
652  ring->addMValue();
653 
654  mInteriorRings.append( ring );
655  clearCache();
656 }
657 
659 {
660  if ( nr < 0 || nr >= mInteriorRings.size() )
661  {
662  return false;
663  }
664  delete mInteriorRings.takeAt( nr );
665  clearCache();
666  return true;
667 }
668 
669 void QgsCurvePolygon::removeInteriorRings( double minimumAllowedArea )
670 {
671  for ( int ringIndex = mInteriorRings.size() - 1; ringIndex >= 0; --ringIndex )
672  {
673  if ( minimumAllowedArea < 0 )
674  delete mInteriorRings.takeAt( ringIndex );
675  else
676  {
677  double area = 0.0;
678  mInteriorRings.at( ringIndex )->sumUpArea( area );
679  if ( area < minimumAllowedArea )
680  delete mInteriorRings.takeAt( ringIndex );
681  }
682  }
683 
684  clearCache();
685 }
686 
688 {
689  QVector<QgsCurve *> validRings;
690  validRings.reserve( mInteriorRings.size() );
691  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
692  {
693  if ( !curve->isRing() )
694  {
695  // remove invalid rings
696  delete curve;
697  }
698  else
699  {
700  validRings << curve;
701  }
702  }
703  mInteriorRings = validRings;
704 }
705 
706 void QgsCurvePolygon::draw( QPainter &p ) const
707 {
708  if ( !mExteriorRing )
709  return;
710 
711  if ( mInteriorRings.empty() )
712  {
713  mExteriorRing->drawAsPolygon( p );
714  }
715  else
716  {
717  QPainterPath path;
718  mExteriorRing->addToPainterPath( path );
719 
720  for ( const QgsCurve *ring : mInteriorRings )
721  {
722  ring->addToPainterPath( path );
723  }
724  p.drawPath( path );
725  }
726 }
727 
729 {
730  if ( mExteriorRing )
731  {
732  mExteriorRing->transform( ct, d, transformZ );
733  }
734 
735  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
736  {
737  curve->transform( ct, d, transformZ );
738  }
739  clearCache();
740 }
741 
742 void QgsCurvePolygon::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
743 {
744  if ( mExteriorRing )
745  {
746  mExteriorRing->transform( t, zTranslate, zScale, mTranslate, mScale );
747  }
748 
749  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
750  {
751  curve->transform( t, zTranslate, zScale, mTranslate, mScale );
752  }
753  clearCache();
754 }
755 
757 {
758  QgsCoordinateSequence sequence;
759  sequence.append( QgsRingSequence() );
760 
761  if ( mExteriorRing )
762  {
763  sequence.back().append( QgsPointSequence() );
764  mExteriorRing->points( sequence.back().back() );
765  }
766 
767  for ( const QgsCurve *ring : mInteriorRings )
768  {
769  sequence.back().append( QgsPointSequence() );
770  ring->points( sequence.back().back() );
771  }
772 
773  return sequence;
774 }
775 
777 {
778  int count = 0;
779 
780  if ( mExteriorRing )
781  {
782  count += mExteriorRing->nCoordinates();
783  }
784 
785  for ( const QgsCurve *ring : mInteriorRings )
786  {
787  count += ring->nCoordinates();
788  }
789 
790  return count;
791 }
792 
794 {
795  if ( id.part != 0 )
796  return -1;
797 
798  if ( id.ring < 0 || id.ring >= ringCount() )
799  return -1;
800 
801  int number = 0;
802  if ( id.ring == 0 && mExteriorRing )
803  {
804  return mExteriorRing->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
805  }
806  else
807  {
808  number += mExteriorRing->numPoints();
809  }
810 
811  for ( int i = 0; i < mInteriorRings.count(); ++i )
812  {
813  if ( id.ring == i + 1 )
814  {
815  int partNumber = mInteriorRings.at( i )->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
816  if ( partNumber == -1 )
817  return -1;
818  return number + partNumber;
819  }
820  else
821  {
822  number += mInteriorRings.at( i )->numPoints();
823  }
824  }
825  return -1; // should not happen
826 }
827 
829 {
830  if ( !mExteriorRing )
831  return true;
832 
833  return mExteriorRing->isEmpty();
834 }
835 
836 double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
837 {
838  if ( !mExteriorRing )
839  {
840  return -1;
841  }
842  QVector<QgsCurve *> segmentList;
843  segmentList.append( mExteriorRing.get() );
844  segmentList.append( mInteriorRings );
845  return QgsGeometryUtils::closestSegmentFromComponents( segmentList, QgsGeometryUtils::Ring, pt, segmentPt, vertexAfter, leftOf, epsilon );
846 }
847 
849 {
850  if ( !mExteriorRing || vId.ring >= 1 + mInteriorRings.size() )
851  {
852  return false;
853  }
854 
855  if ( vId.ring < 0 )
856  {
857  vId.ring = 0;
858  vId.vertex = -1;
859  if ( vId.part < 0 )
860  {
861  vId.part = 0;
862  }
863  return mExteriorRing->nextVertex( vId, vertex );
864  }
865  else
866  {
867  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings[vId.ring - 1];
868 
869  if ( ring->nextVertex( vId, vertex ) )
870  {
871  return true;
872  }
873  ++vId.ring;
874  vId.vertex = -1;
875  if ( vId.ring >= 1 + mInteriorRings.size() )
876  {
877  return false;
878  }
879  ring = mInteriorRings[ vId.ring - 1 ];
880  return ring->nextVertex( vId, vertex );
881  }
882 }
883 
884 void ringAdjacentVertices( const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
885 {
886  int n = curve->numPoints();
887  if ( vertex.vertex < 0 || vertex.vertex >= n )
888  {
889  previousVertex = QgsVertexId();
890  nextVertex = QgsVertexId();
891  return;
892  }
893 
894  if ( vertex.vertex == 0 && n < 3 )
895  {
896  previousVertex = QgsVertexId();
897  }
898  else if ( vertex.vertex == 0 )
899  {
900  previousVertex = QgsVertexId( vertex.part, vertex.ring, n - 2 );
901  }
902  else
903  {
904  previousVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex - 1 );
905  }
906  if ( vertex.vertex == n - 1 && n < 3 )
907  {
908  nextVertex = QgsVertexId();
909  }
910  else if ( vertex.vertex == n - 1 )
911  {
912  nextVertex = QgsVertexId( vertex.part, vertex.ring, 1 );
913  }
914  else
915  {
916  nextVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex + 1 );
917  }
918 }
919 
921 {
922  if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
923  {
924  previousVertex = QgsVertexId();
925  nextVertex = QgsVertexId();
926  return;
927  }
928 
929  if ( vertex.ring == 0 )
930  {
931  ringAdjacentVertices( mExteriorRing.get(), vertex, previousVertex, nextVertex );
932  }
933  else
934  {
935  ringAdjacentVertices( mInteriorRings.at( vertex.ring - 1 ), vertex, previousVertex, nextVertex );
936  }
937 }
938 
940 {
941  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
942  {
943  return false;
944  }
945 
946  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
947  int n = ring->numPoints();
948  bool success = ring->insertVertex( QgsVertexId( 0, 0, vId.vertex ), vertex );
949  if ( !success )
950  {
951  return false;
952  }
953 
954  // If first or last vertex is inserted, re-sync the last/first vertex
955  if ( vId.vertex == 0 )
956  ring->moveVertex( QgsVertexId( 0, 0, n ), vertex );
957  else if ( vId.vertex == n )
958  ring->moveVertex( QgsVertexId( 0, 0, 0 ), vertex );
959 
960  clearCache();
961 
962  return true;
963 }
964 
966 {
967  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
968  {
969  return false;
970  }
971 
972  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
973  int n = ring->numPoints();
974  bool success = ring->moveVertex( vId, newPos );
975  if ( success )
976  {
977  // If first or last vertex is moved, also move the last/first vertex
978  if ( vId.vertex == 0 )
979  ring->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos );
980  else if ( vId.vertex == n - 1 )
981  ring->moveVertex( QgsVertexId( vId.part, vId.ring, 0 ), newPos );
982  clearCache();
983  }
984  return success;
985 }
986 
988 {
989  if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
990  {
991  return false;
992  }
993 
994  QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
995  int n = ring->numPoints();
996  if ( n <= 4 )
997  {
998  //no points will be left in ring, so remove whole ring
999  if ( vId.ring == 0 )
1000  {
1001  mExteriorRing.reset();
1002  if ( !mInteriorRings.isEmpty() )
1003  {
1004  mExteriorRing.reset( mInteriorRings.takeFirst() );
1005  }
1006  }
1007  else
1008  {
1009  removeInteriorRing( vId.ring - 1 );
1010  }
1011  clearCache();
1012  return true;
1013  }
1014 
1015  bool success = ring->deleteVertex( vId );
1016  if ( success )
1017  {
1018  // If first or last vertex is removed, re-sync the last/first vertex
1019  // Do not use "n - 2", but "ring->numPoints() - 1" as more than one vertex
1020  // may have been deleted (e.g. with CircularString)
1021  if ( vId.vertex == 0 )
1022  ring->moveVertex( QgsVertexId( 0, 0, ring->numPoints() - 1 ), ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) );
1023  else if ( vId.vertex == n - 1 )
1024  ring->moveVertex( QgsVertexId( 0, 0, 0 ), ring->vertexAt( QgsVertexId( 0, 0, ring->numPoints() - 1 ) ) );
1025  clearCache();
1026  }
1027  return success;
1028 }
1029 
1031 {
1032  if ( mExteriorRing && mExteriorRing->hasCurvedSegments() )
1033  {
1034  return true;
1035  }
1036 
1037  for ( const QgsCurve *ring : mInteriorRings )
1038  {
1039  if ( ring->hasCurvedSegments() )
1040  {
1041  return true;
1042  }
1043  }
1044  return false;
1045 }
1046 
1048 {
1049  return toPolygon( tolerance, toleranceType );
1050 }
1051 
1053 {
1054  if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1055  {
1056  //makes no sense - conversion of false to double!
1057  return false;
1058  }
1059 
1060  QgsCurve *ring = vertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[vertex.ring - 1];
1061  return ring->vertexAngle( vertex );
1062 }
1063 
1064 int QgsCurvePolygon::vertexCount( int /*part*/, int ring ) const
1065 {
1066  return ring == 0 ? mExteriorRing->vertexCount() : mInteriorRings[ring - 1]->vertexCount();
1067 }
1068 
1070 {
1071  return ( nullptr != mExteriorRing ) + mInteriorRings.size();
1072 }
1073 
1075 {
1076  return ringCount() > 0 ? 1 : 0;
1077 }
1078 
1080 {
1081  return id.ring == 0 ? mExteriorRing->vertexAt( id ) : mInteriorRings[id.ring - 1]->vertexAt( id );
1082 }
1083 
1084 double QgsCurvePolygon::segmentLength( QgsVertexId startVertex ) const
1085 {
1086  if ( !mExteriorRing || startVertex.ring < 0 || startVertex.ring >= 1 + mInteriorRings.size() )
1087  {
1088  return 0.0;
1089  }
1090 
1091  const QgsCurve *ring = startVertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[startVertex.ring - 1];
1092  return ring->segmentLength( startVertex );
1093 }
1094 
1095 bool QgsCurvePolygon::addZValue( double zValue )
1096 {
1097  if ( QgsWkbTypes::hasZ( mWkbType ) )
1098  return false;
1099 
1101 
1102  if ( mExteriorRing )
1103  mExteriorRing->addZValue( zValue );
1104  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1105  {
1106  curve->addZValue( zValue );
1107  }
1108  clearCache();
1109  return true;
1110 }
1111 
1112 bool QgsCurvePolygon::addMValue( double mValue )
1113 {
1114  if ( QgsWkbTypes::hasM( mWkbType ) )
1115  return false;
1116 
1118 
1119  if ( mExteriorRing )
1120  mExteriorRing->addMValue( mValue );
1121  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1122  {
1123  curve->addMValue( mValue );
1124  }
1125  clearCache();
1126  return true;
1127 }
1128 
1130 {
1131  if ( !is3D() )
1132  return false;
1133 
1135  if ( mExteriorRing )
1136  mExteriorRing->dropZValue();
1137  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1138  {
1139  curve->dropZValue();
1140  }
1141  clearCache();
1142  return true;
1143 }
1144 
1146 {
1147  if ( !isMeasure() )
1148  return false;
1149 
1151  if ( mExteriorRing )
1152  mExteriorRing->dropMValue();
1153  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1154  {
1155  curve->dropMValue();
1156  }
1157  clearCache();
1158  return true;
1159 }
1160 
1162 {
1163  if ( mExteriorRing )
1164  mExteriorRing->swapXy();
1165  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1166  {
1167  curve->swapXy();
1168  }
1169  clearCache();
1170 }
1171 
1173 {
1174  return clone();
1175 }
1176 
1177 void QgsCurvePolygon::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1178 {
1179  if ( mExteriorRing )
1180  mExteriorRing->filterVertices( filter );
1181 
1182  for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
1183  {
1184  curve->filterVertices( filter );
1185  }
1186  clearCache();
1187 }
1188 
1190 {
1191  return 1 + mInteriorRings.count();
1192 }
1193 
1195 {
1196  if ( index == 0 )
1197  return mExteriorRing.get();
1198  else
1199  return mInteriorRings.at( index - 1 );
1200 }
bool isMeasure() const
Returns true if the geometry contains m values.
QByteArray asWkb() const override
Returns a WKB representation of the geometry.
virtual void setExteriorRing(QgsCurve *ring)
Sets the exterior ring of the polygon.
void ringAdjacentVertices(const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex)
bool dropMValue() override
Drops any measure values which exist in the geometry.
static QString pointsToJSON(const QgsPointSequence &points, int precision)
Returns a geoJSON coordinates string.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
A rectangle specified with double values.
Definition: qgsrectangle.h:40
bool operator==(const QgsAbstractGeometry &other) const override
QgsCurvePolygon & operator=(const QgsCurvePolygon &p)
virtual bool deleteVertex(QgsVertexId position)=0
Deletes a vertex within the geometry.
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
QgsAbstractGeometry & operator=(const QgsAbstractGeometry &geom)
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgssurface.h:78
static QPair< QgsWkbTypes::Type, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
virtual bool insertVertex(QgsVertexId position, const QgsPoint &vertex)=0
Inserts a vertex into the geometry.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
virtual void addInteriorRing(QgsCurve *ring)
Adds an interior ring to the geometry (takes ownership)
QVector< QgsRingSequence > QgsCoordinateSequence
void swapXy() override
Swaps the x and y coordinates from the geometry.
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
Definition: qgscurve.cpp:70
QgsCurvePolygon * toCurveType() const override
Returns the geometry converted to the more generic curve type.
const QgsCurve * interiorRing(int i) const
TransformDirection
Enum used to indicate the direction (forward or inverse) of the transform.
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
QgsAbstractGeometry * boundary() const override
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
Curve polygon geometry type.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle...
void clear() override
Clears the geometry, ie reset it to a null geometry.
static endian_t endian()
Returns whether this machine uses big or little endian.
double area() const override
Returns the area of the geometry.
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:768
static QStringList wktGetChildBlocks(const QString &wkt, const QString &defaultType=QString())
Parses a WKT string and returns of list of blocks contained in the WKT.
static Type dropM(Type type)
Drops the m dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:938
QgsWkbTypes::Type mWkbType
bool removeInteriorRing(int ringIndex)
Removes an interior ring from the polygon.
QVector< QgsCurve * > mInteriorRings
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
void removeInvalidRings()
Removes any interior rings which are not valid from the polygon.
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
Definition: qgspoint.cpp:365
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
int numInteriorRings() const
~QgsCurvePolygon() override
int vertexNumberFromVertexId(QgsVertexId id) const override
Returns the vertex number corresponding to a vertex id.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:67
void filterVertices(const std::function< bool(const QgsPoint &) > &filter) override
Filters the vertices from the geometry in place, removing any which do not return true for the filter...
int childCount() const override
Returns number of child geometries (for geometries with child geometries) or child points (for geomet...
int nCoordinates() const override
Returns the number of nodes contained in the geometry.
virtual double segmentLength(QgsVertexId startVertex) const =0
Returns the length of the segment of the geometry which begins at startVertex.
static Type addM(Type type)
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:889
QString asJson(int precision=17) const override
Returns a GeoJSON representation of the geometry.
Utility class for identifying a unique vertex within a geometry.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
int dimension() const override
Returns the inherent dimension of the geometry.
QgsCurvePolygon * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership...
QgsRectangle calculateBoundingBox() const override
Default calculator for the minimal bounding box for the geometry.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
QString geometryType() const override
Returns a unique string representing the geometry type.
QgsCurvePolygon * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid...
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:663
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
static Type addZ(Type type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:864
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Multi curve geometry collection.
Definition: qgsmulticurve.h:29
double perimeter() const override
Returns the perimeter of the geometry.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
virtual QgsPolygon * toPolygon(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a new polygon geometry corresponding to a segmentized approximation of the curve...
QgsAbstractGeometry * childGeometry(int index) const override
Returns pointer to child geometry (for geometries with child geometries - i.e.
Abstract base class for all geometries.
QgsCoordinateSequence coordinateSequence() const override
Retrieves the sequence of geometries, rings and nodes.
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
Definition: qgscurve.cpp:169
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
AxisOrder
Axis order for GML generation.
bool operator!=(const QgsAbstractGeometry &other) const override
double vertexAngle(QgsVertexId vertex) const override
Returns approximate rotation angle for a vertex.
int partCount() const override
Returns count of parts contained in the geometry.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
QVector< QgsPoint > QgsPointSequence
void adjacentVertices(QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex) const override
Returns the vertices adjacent to a specified vertex within a geometry.
QVector< QgsPointSequence > QgsRingSequence
static Type dropZ(Type type)
Drops the z dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:920
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
virtual bool moveVertex(QgsVertexId position, const QgsPoint &newPos)=0
Moves a vertex within the geometry.
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Class for doing transforms between two map coordinate systems.
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *std::numeric_limits< double >::epsilon()) const override
Searches for the closest segment of the geometry to a given point.
QgsAbstractGeometry * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:818
Compound curve geometry type.
Circular string geometry type.
QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML2 representation of the geometry.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
QgsCurvePolygon * clone() const override
Clones the geometry by performing a deep copy.
virtual bool dropMValue()=0
Drops any measure values which exist in the geometry.
QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML3 representation of the geometry.
Polygon geometry type.
Definition: qgspolygon.h:31
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
const QgsCurve * exteriorRing() const
void setInteriorRings(const QVector< QgsCurve *> &rings)
Sets all interior rings (takes ownership)
std::unique_ptr< QgsCurve > mExteriorRing
QgsPolygon * surfaceToPolygon() const override
Gets a polygon representation of this surface.
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:427
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:53
void removeInteriorRings(double minimumAllowedArea=-1)
Removes the interior rings from the polygon.
bool isEmpty() const override
Returns true if the geometry is empty.
virtual int numPoints() const =0
Returns the number of points in the curve.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
double ANALYSIS_EXPORT leftOf(const QgsPoint &thepoint, const QgsPoint *p1, const QgsPoint *p2)
Returns whether &#39;thepoint&#39; is left or right of the line from &#39;p1&#39; to &#39;p2&#39;. Negativ values mean left a...
Definition: MathUtils.cpp:292
virtual bool dropZValue()=0
Drops any z-dimensions which exist in the geometry.
static double closestSegmentFromComponents(T &container, ComponentType ctype, const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon)
virtual QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const =0
Returns a GML3 representation of the geometry.