1 /***************************************************************************
2                          qgscurvepolygon.h
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 #ifndef QGSCURVEPOLYGON_H
19 #define QGSCURVEPOLYGON_H
20 
21 #include "qgis_core.h"
22 #include "qgis_sip.h"
23 #include "qgssurface.h"
24 #include <memory>
25 
26 class QgsPolygon;
27 
28 /**
29  * \ingroup core
30  * \class QgsCurvePolygon
31  * \brief Curve polygon geometry type
32  * \since QGIS 2.10
33  */
34 class CORE_EXPORT QgsCurvePolygon: public QgsSurface
35 {
36   public:
37     QgsCurvePolygon();
38     QgsCurvePolygon( const QgsCurvePolygon &p );
39     QgsCurvePolygon &operator=( const QgsCurvePolygon &p );
40 
41     bool operator==( const QgsAbstractGeometry &other ) const override;
42     bool operator!=( const QgsAbstractGeometry &other ) const override;
43 
44     ~QgsCurvePolygon() override;
45 
46     QString geometryType() const override SIP_HOLDGIL;
47     int dimension() const override SIP_HOLDGIL;
48     QgsCurvePolygon *clone() const override SIP_FACTORY;
49     void clear() override;
50 
51     bool fromWkb( QgsConstWkbPtr &wkb ) override;
52     bool fromWkt( const QString &wkt ) override;
53 
54     int wkbSize( QgsAbstractGeometry::WkbFlags flags = QgsAbstractGeometry::WkbFlags() ) const override;
55     QByteArray asWkb( QgsAbstractGeometry::WkbFlags flags = QgsAbstractGeometry::WkbFlags() ) const override;
56     QString asWkt( int precision = 17 ) const override;
57     QDomElement asGml2( QDomDocument &doc, int precision = 17, const QString &ns = "gml", QgsAbstractGeometry::AxisOrder axisOrder = QgsAbstractGeometry::AxisOrder::XY ) const override;
58     QDomElement asGml3( QDomDocument &doc, int precision = 17, const QString &ns = "gml", QgsAbstractGeometry::AxisOrder axisOrder = QgsAbstractGeometry::AxisOrder::XY ) const override;
59     json asJsonObject( int precision = 17 ) const override SIP_SKIP;
60     QString asKml( int precision = 17 ) const override;
61     void normalize() final SIP_HOLDGIL;
62 
63     //surface interface
64     double area() const override SIP_HOLDGIL;
65     double perimeter() const override SIP_HOLDGIL;
66     QgsPolygon *surfaceToPolygon() const override SIP_FACTORY;
67     QgsAbstractGeometry *boundary() const override SIP_FACTORY;
68     QgsCurvePolygon *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const override SIP_FACTORY;
69     bool removeDuplicateNodes( double epsilon = 4 * std::numeric_limits<double>::epsilon(), bool useZValues = false ) override;
70     bool boundingBoxIntersects( const QgsRectangle &rectangle ) const override SIP_HOLDGIL;
71 
72     //curve polygon interface
73 
74     /**
75      * Returns the number of interior rings contained with the curve polygon.
76      *
77      * \see interiorRing()
78      */
numInteriorRings()79     int numInteriorRings() const SIP_HOLDGIL
80     {
81       return mInteriorRings.size();
82     }
83 
84     /**
85      * Returns the curve polygon's exterior ring.
86      *
87      * \see interiorRing()
88      */
exteriorRing()89     const QgsCurve *exteriorRing() const SIP_HOLDGIL
90     {
91       return mExteriorRing.get();
92     }
93 
94     /**
95      * Returns a non-const pointer to the curve polygon's exterior ring.
96      * Ownership stays with this QgsCurve.
97      *
98      * \see interiorRing()
99      * \note Not available in Python.
100      * \since QGIS 3.20
101      */
exteriorRing()102     QgsCurve *exteriorRing() SIP_SKIP
103     {
104       return mExteriorRing.get();
105     }
106 
107 #ifndef SIP_RUN
108 
109     /**
110      * Retrieves an interior ring from the curve polygon. The first interior ring has index 0.
111      *
112      * \see numInteriorRings()
113      * \see exteriorRing()
114      */
interiorRing(int i)115     const QgsCurve *interiorRing( int i ) const SIP_HOLDGIL
116     {
117       if ( i < 0 || i >= mInteriorRings.size() )
118       {
119         return nullptr;
120       }
121       return mInteriorRings.at( i );
122     }
123 
124     /**
125      * Retrieves an interior ring from the curve polygon. The first interior ring has index 0.
126      *
127      * \see numInteriorRings()
128      * \see exteriorRing()
129      * \note Not available in Python.
130      * \since QGIS 3.20
131      */
interiorRing(int i)132     QgsCurve *interiorRing( int i ) SIP_SKIP
133     {
134       if ( i < 0 || i >= mInteriorRings.size() )
135       {
136         return nullptr;
137       }
138       return mInteriorRings.at( i );
139     }
140 #else
141 
142     /**
143      * Retrieves an interior ring from the curve polygon. The first interior ring has index 0.
144      *
145      * \throws IndexError if no interior ring with the specified index exists.
146      *
147      * \see numInteriorRings()
148      * \see exteriorRing()
149      */
150     SIP_PYOBJECT interiorRing( int i ) SIP_HOLDGIL SIP_TYPEHINT( QgsCurve );
151     % MethodCode
152     if ( a0 < 0 || a0 >= sipCpp->numInteriorRings() )
153     {
154       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
155       sipIsErr = 1;
156     }
157     else
158     {
159       return sipConvertFromType( const_cast< QgsCurve * >( sipCpp->interiorRing( a0 ) ), sipType_QgsCurve, NULL );
160     }
161     % End
162 #endif
163 
164     /**
165      * Returns a new polygon geometry corresponding to a segmentized approximation
166      * of the curve.
167      * \param tolerance segmentation tolerance
168      * \param toleranceType maximum segmentation angle or maximum difference between approximation and curve
169     */
170     virtual QgsPolygon *toPolygon( double tolerance = M_PI_2 / 90, SegmentationToleranceType toleranceType = MaximumAngle ) const SIP_FACTORY;
171 
172     /**
173      * Sets the exterior ring of the polygon. The CurvePolygon type will be updated to match the dimensionality
174      * of the exterior ring. For instance, setting a 2D exterior ring on a 3D CurvePolygon will drop the z dimension
175      * from the CurvePolygon and all interior rings.
176      * \param ring new exterior ring. Ownership is transferred to the CurvePolygon.
177      * \see setInteriorRings()
178      * \see exteriorRing()
179      */
180     virtual void setExteriorRing( QgsCurve *ring SIP_TRANSFER );
181 
182     //! Sets all interior rings (takes ownership)
183     void setInteriorRings( const QVector<QgsCurve *> &rings SIP_TRANSFER );
184     //! Adds an interior ring to the geometry (takes ownership)
185     virtual void addInteriorRing( QgsCurve *ring SIP_TRANSFER );
186 
187 #ifndef SIP_RUN
188 
189     /**
190      * Removes an interior ring from the polygon. The first interior ring has index 0.
191      * The corresponding ring is removed from the polygon and deleted. If a ring was successfully removed
192      * the function will return TRUE.  It is not possible to remove the exterior ring using this method.
193      * \see removeInteriorRings()
194      */
195     bool removeInteriorRing( int ringIndex );
196 #else
197 
198     /**
199      * Removes an interior ring from the polygon. The first interior ring has index 0.
200      * The corresponding ring is removed from the polygon and deleted.
201      * It is not possible to remove the exterior ring using this method.
202      *
203      * \throws IndexError if no interior ring with the specified index exists.
204      *
205      * \see removeInteriorRings()
206      */
207     bool removeInteriorRing( int i );
208     % MethodCode
209     if ( a0 < 0 || a0 >= sipCpp->numInteriorRings() )
210     {
211       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
212       sipIsErr = 1;
213     }
214     else
215     {
216       return PyBool_FromLong( sipCpp->removeInteriorRing( a0 ) );
217     }
218     % End
219 #endif
220 
221     /**
222      * Removes the interior rings from the polygon. If the minimumAllowedArea
223      * parameter is specified then only rings smaller than this minimum
224      * area will be removed.
225      * \see removeInteriorRing()
226      * \since QGIS 3.0
227      */
228     void removeInteriorRings( double minimumAllowedArea = -1 );
229 
230     /**
231      * Removes any interior rings which are not valid from the polygon.
232      *
233      * For example, this removes unclosed rings and rings with less than 4 vertices.
234      *
235      * \since QGIS 3.0
236      */
237     void removeInvalidRings();
238 
239     /**
240      * Forces the geometry to respect the Right-Hand-Rule, in which the area that is
241      * bounded by the polygon is to the right of the boundary. In particular, the exterior
242      * ring is oriented in a clockwise direction and the interior rings in a counter-clockwise
243      * direction.
244      *
245      * \since QGIS 3.6
246      */
247     void forceRHR();
248 
249     QPainterPath asQPainterPath() const override;
250     void draw( QPainter &p ) const override;
251     void transform( const QgsCoordinateTransform &ct, Qgis::TransformDirection d = Qgis::TransformDirection::Forward, bool transformZ = false ) override SIP_THROW( QgsCsException );
252     void transform( const QTransform &t, double zTranslate = 0.0, double zScale = 1.0, double mTranslate = 0.0, double mScale = 1.0 ) override;
253 
254     bool insertVertex( QgsVertexId position, const QgsPoint &vertex ) override;
255     bool moveVertex( QgsVertexId position, const QgsPoint &newPos ) override;
256     bool deleteVertex( QgsVertexId position ) override;
257 
258     QgsCoordinateSequence coordinateSequence() const override;
259     int nCoordinates() const override;
260     int vertexNumberFromVertexId( QgsVertexId id ) const override;
261     bool isEmpty() const override SIP_HOLDGIL;
262     double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt SIP_OUT, QgsVertexId &vertexAfter SIP_OUT, int *leftOf SIP_OUT = nullptr, double epsilon = 4 * std::numeric_limits<double>::epsilon() ) const override;
263 
264     bool nextVertex( QgsVertexId &id, QgsPoint &vertex SIP_OUT ) const override;
265     void adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex SIP_OUT, QgsVertexId &nextVertex SIP_OUT ) const override;
266     bool hasCurvedSegments() const override;
267 
268     /**
269      * Returns a geometry without curves. Caller takes ownership
270      * \param tolerance segmentation tolerance
271      * \param toleranceType maximum segmentation angle or maximum difference between approximation and curve
272     */
273     QgsAbstractGeometry *segmentize( double tolerance = M_PI_2 / 90, SegmentationToleranceType toleranceType = MaximumAngle ) const override SIP_FACTORY;
274 
275     /**
276      * Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
277      * \param vertex the vertex id
278      * \returns rotation in radians, clockwise from north
279      */
280     double vertexAngle( QgsVertexId vertex ) const override;
281 
282     int vertexCount( int part = 0, int ring = 0 ) const override;
283     int ringCount( int part = 0 ) const override SIP_HOLDGIL;
284     int partCount() const override SIP_HOLDGIL;
285     QgsPoint vertexAt( QgsVertexId id ) const override;
286     double segmentLength( QgsVertexId startVertex ) const override;
287 
288     bool addZValue( double zValue = 0 ) override;
289     bool addMValue( double mValue = 0 ) override;
290     bool dropZValue() override;
291     bool dropMValue() override;
292     void swapXy() override;
293 
294     QgsCurvePolygon *toCurveType() const override SIP_FACTORY;
295 
296     bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = nullptr ) override;
297 
298 #ifndef SIP_RUN
299     void filterVertices( const std::function< bool( const QgsPoint & ) > &filter ) override;
300     void transformVertices( const std::function< QgsPoint( const QgsPoint & ) > &transform ) override;
301 
302     /**
303      * Cast the \a geom to a QgsCurvePolygon.
304      * Should be used by qgsgeometry_cast<QgsCurvePolygon *>( geometry ).
305      *
306      * \note Not available in Python. Objects will be automatically be converted to the appropriate target type.
307      * \since QGIS 3.0
308      */
cast(const QgsAbstractGeometry * geom)309     inline static const QgsCurvePolygon *cast( const QgsAbstractGeometry *geom )
310     {
311       if ( !geom )
312         return nullptr;
313 
314       const QgsWkbTypes::Type flatType = QgsWkbTypes::flatType( geom->wkbType() );
315       if ( flatType == QgsWkbTypes::CurvePolygon
316            || flatType == QgsWkbTypes::Polygon
317            || flatType == QgsWkbTypes::Triangle )
318         return static_cast<const QgsCurvePolygon *>( geom );
319       return nullptr;
320     }
321 #endif
322 
323     QgsCurvePolygon *createEmptyWithSameType() const override SIP_FACTORY;
324 
325 #ifdef SIP_RUN
326     SIP_PYOBJECT __repr__();
327     % MethodCode
328     QString wkt = sipCpp->asWkt();
329     if ( wkt.length() > 1000 )
330       wkt = wkt.left( 1000 ) + QStringLiteral( "..." );
331     QString str = QStringLiteral( "<QgsCurvePolygon: %1>" ).arg( wkt );
332     sipRes = PyUnicode_FromString( str.toUtf8().constData() );
333     % End
334 #endif
335 
336   protected:
337 
338     int childCount() const override;
339     QgsAbstractGeometry *childGeometry( int index ) const override;
340     int compareToSameClass( const QgsAbstractGeometry *other ) const final;
341 
342   protected:
343 
344     std::unique_ptr< QgsCurve > mExteriorRing;
345     QVector<QgsCurve *> mInteriorRings;
346 
347     QgsRectangle calculateBoundingBox() const override;
348 };
349 
350 // clazy:excludeall=qstring-allocations
351 
352 #endif // QGSCURVEPOLYGON_H
353