1 /***************************************************************************
2                         qgsgeometrycollection.h
3   -------------------------------------------------------------------
4 Date                 : 28 Oct 2014
5 Copyright            : (C) 2014 by Marco Hugentobler
6 email                : marco.hugentobler at sourcepole dot com
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 
16 #ifndef QGSGEOMETRYCOLLECTION_H
17 #define QGSGEOMETRYCOLLECTION_H
18 
19 #include <QVector>
20 
21 
22 #include "qgis_core.h"
23 #include "qgis_sip.h"
24 #include "qgsabstractgeometry.h"
25 #include "qgsrectangle.h"
26 
27 class QgsPoint;
28 
29 
30 /**
31  * \ingroup core
32  * \class QgsGeometryCollection
33  * \brief Geometry collection
34  * \since QGIS 2.10
35  */
36 class CORE_EXPORT QgsGeometryCollection: public QgsAbstractGeometry
37 {
38   public:
39 
40 
41     /**
42      * Constructor for an empty geometry collection.
43      */
44     QgsGeometryCollection() SIP_HOLDGIL;
45 
46     QgsGeometryCollection( const QgsGeometryCollection &c );
47     QgsGeometryCollection &operator=( const QgsGeometryCollection &c );
48     ~QgsGeometryCollection() override;
49 
50     bool operator==( const QgsAbstractGeometry &other ) const override;
51     bool operator!=( const QgsAbstractGeometry &other ) const override;
52 
53     QgsGeometryCollection *clone() const override SIP_FACTORY;
54 
55     /**
56      * Returns the number of geometries within the collection.
57      */
numGeometries()58     int numGeometries() const SIP_HOLDGIL
59     {
60       return mGeometries.size();
61     }
62 
63 #ifdef SIP_RUN
64 
65     /**
66      * Returns the number of geometries within the collection.
67      */
68     int __len__() const;
69     % MethodCode
70     sipRes = sipCpp->numGeometries();
71     % End
72 
73     //! Ensures that bool(obj) returns TRUE (otherwise __len__() would be used)
74     int __bool__() const;
75     % MethodCode
76     sipRes = true;
77     % End
78 #endif
79 
80 
81     /**
82      * Returns a const reference to a geometry from within the collection.
83      * \param n index of geometry to return
84      * \note not available in Python bindings
85      */
86     const QgsAbstractGeometry *geometryN( int n ) const SIP_SKIP
87     {
88       return mGeometries.value( n );
89     }
90 
91 #ifndef SIP_RUN
92 
93     /**
94      * Returns a geometry from within the collection.
95      * \param n index of geometry to return
96      */
97     QgsAbstractGeometry *geometryN( int n ) SIP_HOLDGIL;
98 #else
99 
100     /**
101      * Returns a geometry from within the collection.
102      * \param n index of geometry to return.
103      * \throws IndexError if no geometry with the specified index exists.
104      */
105     SIP_PYOBJECT geometryN( int n ) SIP_TYPEHINT( QgsAbstractGeometry );
106     % MethodCode
107     if ( a0 < 0 || a0 >= sipCpp->numGeometries() )
108     {
109       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
110       sipIsErr = 1;
111     }
112     else
113     {
114       return sipConvertFromType( sipCpp->geometryN( a0 ), sipType_QgsAbstractGeometry, NULL );
115     }
116     % End
117 #endif
118 
119 
120     //methods inherited from QgsAbstractGeometry
121     bool isEmpty() const override SIP_HOLDGIL;
122     int dimension() const override SIP_HOLDGIL;
123     QString geometryType() const override SIP_HOLDGIL;
124     void clear() override;
125     QgsGeometryCollection *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const override SIP_FACTORY;
126     bool removeDuplicateNodes( double epsilon = 4 * std::numeric_limits<double>::epsilon(), bool useZValues = false ) override;
127     QgsAbstractGeometry *boundary() const override SIP_FACTORY;
128     void adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex SIP_OUT, QgsVertexId &nextVertex SIP_OUT ) const override;
129     int vertexNumberFromVertexId( QgsVertexId id ) const override;
130     bool boundingBoxIntersects( const QgsRectangle &rectangle ) const override SIP_HOLDGIL;
131 
132     /**
133      * Attempts to allocate memory for at least \a size geometries.
134      *
135      * If the number of geometries is known in advance, calling this function prior to adding geometries will prevent
136      * reallocations and memory fragmentation.
137      *
138      * \since QGIS 3.10
139      */
140     void reserve( int size ) SIP_HOLDGIL;
141 
142     //! Adds a geometry and takes ownership. Returns TRUE in case of success.
143     virtual bool addGeometry( QgsAbstractGeometry *g SIP_TRANSFER );
144 
145     /**
146      * Inserts a geometry before a specified index and takes ownership. Returns TRUE in case of success.
147      * \param g geometry to insert. Ownership is transferred to the collection.
148      * \param index position to insert geometry before
149      */
150     virtual bool insertGeometry( QgsAbstractGeometry *g SIP_TRANSFER, int index );
151 
152 #ifndef SIP_RUN
153 
154     /**
155      * Removes a geometry from the collection.
156      * \param nr index of geometry to remove
157      * \returns TRUE if removal was successful.
158      */
159     virtual bool removeGeometry( int nr );
160 #else
161 
162     /**
163      * Removes a geometry from the collection by index.
164      *
165      * \returns TRUE if removal was successful.
166      * \throws IndexError if no geometry with the specified index exists.
167      */
168     virtual bool removeGeometry( int nr );
169     % MethodCode
170     const int count = sipCpp->numGeometries();
171     if ( a0 < 0 || a0 >= count )
172     {
173       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
174       sipIsErr = 1;
175     }
176     else
177     {
178       return PyBool_FromLong( sipCpp->removeGeometry( a0 ) );
179     }
180     % End
181 #endif
182 
183     void normalize() final SIP_HOLDGIL;
184     void transform( const QgsCoordinateTransform &ct, Qgis::TransformDirection d = Qgis::TransformDirection::Forward, bool transformZ = false ) override SIP_THROW( QgsCsException );
185     void transform( const QTransform &t, double zTranslate = 0.0, double zScale = 1.0, double mTranslate = 0.0, double mScale = 1.0 ) override;
186 
187     void draw( QPainter &p ) const override;
188     QPainterPath asQPainterPath() const override;
189 
190     bool fromWkb( QgsConstWkbPtr &wkb ) override;
191     bool fromWkt( const QString &wkt ) override;
192 
193     int wkbSize( QgsAbstractGeometry::WkbFlags flags = QgsAbstractGeometry::WkbFlags() ) const override;
194     QByteArray asWkb( QgsAbstractGeometry::WkbFlags flags = QgsAbstractGeometry::WkbFlags() ) const override;
195     QString asWkt( int precision = 17 ) const override;
196     QDomElement asGml2( QDomDocument &doc, int precision = 17, const QString &ns = "gml", QgsAbstractGeometry::AxisOrder axisOrder = QgsAbstractGeometry::AxisOrder::XY ) const override;
197     QDomElement asGml3( QDomDocument &doc, int precision = 17, const QString &ns = "gml", QgsAbstractGeometry::AxisOrder axisOrder = QgsAbstractGeometry::AxisOrder::XY ) const override;
198     json asJsonObject( int precision = 17 ) const override SIP_SKIP;
199     QString asKml( int precision = 17 ) const override;
200 
201     QgsRectangle boundingBox() const override;
202 
203     QgsCoordinateSequence coordinateSequence() const override;
204     int nCoordinates() const override;
205 
206     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;
207     bool nextVertex( QgsVertexId &id, QgsPoint &vertex SIP_OUT ) const override;
208 
209     //low-level editing
210     bool insertVertex( QgsVertexId position, const QgsPoint &vertex ) override;
211     bool moveVertex( QgsVertexId position, const QgsPoint &newPos ) override;
212     bool deleteVertex( QgsVertexId position ) override;
213 
214     double length() const override SIP_HOLDGIL;
215     double area() const override SIP_HOLDGIL;
216     double perimeter() const override SIP_HOLDGIL;
217 
218     bool hasCurvedSegments() const override SIP_HOLDGIL;
219 
220     /**
221      * Returns a geometry without curves. Caller takes ownership
222      * \param tolerance segmentation tolerance
223      * \param toleranceType maximum segmentation angle or maximum difference between approximation and curve
224     */
225     QgsAbstractGeometry *segmentize( double tolerance = M_PI_2 / 90, SegmentationToleranceType toleranceType = MaximumAngle ) const override SIP_FACTORY;
226 
227     double vertexAngle( QgsVertexId vertex ) const override;
228     double segmentLength( QgsVertexId startVertex ) const override;
229     int vertexCount( int part = 0, int ring = 0 ) const override;
230     int ringCount( int part = 0 ) const override;
231     int partCount() const override;
232     QgsPoint vertexAt( QgsVertexId id ) const override;
233     bool isValid( QString &error SIP_OUT, Qgis::GeometryValidityFlags flags = Qgis::GeometryValidityFlags() ) const override;
234 
235     bool addZValue( double zValue = 0 ) override;
236     bool addMValue( double mValue = 0 ) override;
237     bool dropZValue() override;
238     bool dropMValue() override;
239     void swapXy() override;
240     QgsGeometryCollection *toCurveType() const override SIP_FACTORY;
241     const QgsAbstractGeometry *simplifiedTypeRef() const override SIP_HOLDGIL;
242 
243     bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = nullptr ) override;
244 
245 #ifndef SIP_RUN
246     void filterVertices( const std::function< bool( const QgsPoint & ) > &filter ) override;
247     void transformVertices( const std::function< QgsPoint( const QgsPoint & ) > &transform ) override;
248 
249     /**
250      * Cast the \a geom to a QgsGeometryCollection.
251      * Should be used by qgsgeometry_cast<QgsGeometryCollection *>( geometry ).
252      *
253      * \note Not available in Python. Objects will be automatically be converted to the appropriate target type.
254      * \since QGIS 3.0
255      */
cast(const QgsAbstractGeometry * geom)256     inline static const QgsGeometryCollection *cast( const QgsAbstractGeometry *geom )
257     {
258       if ( geom && QgsWkbTypes::isMultiType( geom->wkbType() ) )
259         return static_cast<const QgsGeometryCollection *>( geom );
260       return nullptr;
261     }
262 #endif
263 
264 
265 #ifdef SIP_RUN
266 
267     /**
268     * Returns the geometry at the specified ``index``.
269     *
270     * Indexes can be less than 0, in which case they correspond to geometries from the end of the collect. E.g. an index of -1
271     * corresponds to the last geometry in the collection.
272     *
273     * \throws IndexError if no geometry with the specified ``index`` exists.
274     *
275     * \since QGIS 3.6
276     */
277     SIP_PYOBJECT __getitem__( int index ) SIP_TYPEHINT( QgsAbstractGeometry );
278     % MethodCode
279     const int count = sipCpp->numGeometries();
280     if ( a0 < -count || a0 >= count )
281     {
282       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
283       sipIsErr = 1;
284     }
285     else if ( a0 >= 0 )
286     {
287       return sipConvertFromType( sipCpp->geometryN( a0 ), sipType_QgsAbstractGeometry, NULL );
288     }
289     else
290     {
291       return sipConvertFromType( sipCpp->geometryN( count + a0 ), sipType_QgsAbstractGeometry, NULL );
292     }
293     % End
294 
295     /**
296      * Deletes the geometry at the specified ``index``.
297      *
298      * Indexes can be less than 0, in which case they correspond to geometries from the end of the collection. E.g. an index of -1
299      * corresponds to the last geometry in the collection.
300      *
301      * \throws IndexError if no geometry at the ``index`` exists
302      *
303      * \since QGIS 3.6
304      */
305     void __delitem__( int index );
306     % MethodCode
307     const int count = sipCpp->numGeometries();
308     if ( a0 >= 0 && a0 < count )
309       sipCpp->removeGeometry( a0 );
310     else if ( a0 < 0 && a0 >= -count )
311       sipCpp->removeGeometry( count + a0 );
312     else
313     {
314       PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
315       sipIsErr = 1;
316     }
317     % End
318 
319     /**
320      * Iterates through all geometries in the collection.
321      *
322      * \since QGIS 3.6
323      */
324     SIP_PYOBJECT __iter__() SIP_TYPEHINT( QgsGeometryPartIterator );
325     % MethodCode
326     sipRes = sipConvertFromNewType( new QgsGeometryPartIterator( sipCpp ), sipType_QgsGeometryPartIterator, Py_None );
327     % End
328 #endif
329 
330     QgsGeometryCollection *createEmptyWithSameType() const override SIP_FACTORY;
331 
332   protected:
333     int childCount() const override;
334     QgsAbstractGeometry *childGeometry( int index ) const override;
335     int compareToSameClass( const QgsAbstractGeometry *other ) const final;
336 
337   protected:
338     QVector< QgsAbstractGeometry * > mGeometries;
339 
340     /**
341      * Returns whether child type names are omitted from Wkt representations of the collection
342      * \since QGIS 2.12
343      */
344     virtual bool wktOmitChildType() const;
345 
346     /**
347      * Reads a collection from a WKT string.
348      */
349     bool fromCollectionWkt( const QString &wkt, const QVector<QgsAbstractGeometry *> &subtypes, const QString &defaultChildWkbType = QString() );
350 
351     QgsRectangle calculateBoundingBox() const override;
352     void clearCache() const override;
353 
354   private:
355 
356     mutable QgsRectangle mBoundingBox;
357     mutable bool mHasCachedValidity = false;
358     mutable QString mValidityFailureReason;
359 };
360 
361 // clazy:excludeall=qstring-allocations
362 
363 #endif // QGSGEOMETRYCOLLECTION_H
364