1 /***************************************************************************
2                          qgscurve.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 QGSCURVE_H
19 #define QGSCURVE_H
20 
21 #include "qgis_core.h"
22 #include "qgis_sip.h"
23 #include "qgsabstractgeometry.h"
24 #include "qgsrectangle.h"
25 #include <QPainterPath>
26 
27 class QgsLineString;
28 
29 /**
30  * \ingroup core
31  * \class QgsCurve
32  * \brief Abstract base class for curved geometry type
33  * \since QGIS 2.10
34  */
35 class CORE_EXPORT QgsCurve: public QgsAbstractGeometry SIP_ABSTRACT
36 {
37   public:
38 
39     /**
40      * Constructor for QgsCurve.
41      */
42     QgsCurve() = default;
43 
44     /**
45      * Checks whether this curve exactly equals another curve.
46      * \since QGIS 3.0
47      */
48     virtual bool equals( const QgsCurve &other ) const = 0;
49 
50     bool operator==( const QgsAbstractGeometry &other ) const override;
51     bool operator!=( const QgsAbstractGeometry &other ) const override;
52 
53     QgsCurve *clone() const override = 0 SIP_FACTORY;
54 
55     /**
56      * Returns the starting point of the curve.
57      * \see endPoint
58      */
59     virtual QgsPoint startPoint() const = 0;
60 
61     /**
62      * Returns the end point of the curve.
63      * \see startPoint
64      */
65     virtual QgsPoint endPoint() const = 0;
66 
67     /**
68      * Returns TRUE if the curve is closed.
69      *
70      * \see isClosed2D()
71      */
72     virtual bool isClosed() const SIP_HOLDGIL;
73 
74     /**
75      * Returns true if the curve is closed.
76      *
77      * Unlike isClosed. It looks only for XY coordinates.
78      *
79      * \see isClosed()
80      *
81      * \since QGIS 3.20
82      */
83     virtual bool isClosed2D() const SIP_HOLDGIL;
84 
85     /**
86      * Returns TRUE if the curve is a ring.
87      */
88     virtual bool isRing() const SIP_HOLDGIL;
89 
90     /**
91      * Returns a new line string geometry corresponding to a segmentized approximation
92      * of the curve.
93      * \param tolerance segmentation tolerance
94      * \param toleranceType maximum segmentation angle or maximum difference between approximation and curve
95      *
96      * Uses a MaximumAngle tolerance of 1 degrees by default (360
97      * segments in a full circle)
98      */
99     virtual QgsLineString *curveToLine( double tolerance = M_PI_2 / 90, SegmentationToleranceType toleranceType = MaximumAngle ) const = 0 SIP_FACTORY;
100 
101     /**
102      * Adds a curve to a painter path.
103      */
104     virtual void addToPainterPath( QPainterPath &path ) const = 0;
105     QPainterPath asQPainterPath() const override;
106 
107     /**
108      * Draws the curve as a polygon on the specified QPainter.
109      * \param p destination QPainter
110      */
111     virtual void drawAsPolygon( QPainter &p ) const = 0;
112 
113     /**
114      * Returns a list of points within the curve.
115      */
116     virtual void points( QgsPointSequence &pt SIP_OUT ) const = 0;
117 
118     /**
119      * Returns the number of points in the curve.
120      */
121     virtual int numPoints() const = 0;
122 
123 #ifdef SIP_RUN
124     int __len__() const;
125     % Docstring
126     Returns the number of points in the curve.
127     % End
128     % MethodCode
129     sipRes = sipCpp->numPoints();
130     % End
131 
132     //! Ensures that bool(obj) returns TRUE (otherwise __len__() would be used)
133     int __bool__() const;
134     % MethodCode
135     sipRes = true;
136     % End
137 #endif
138 
139     /**
140      * Sums up the area of the curve by iterating over the vertices (shoelace formula).
141      */
142     virtual void sumUpArea( double &sum SIP_OUT ) const = 0;
143 
144     QgsCoordinateSequence coordinateSequence() const override;
145     bool nextVertex( QgsVertexId &id, QgsPoint &vertex SIP_OUT ) const override;
146     void adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex SIP_OUT, QgsVertexId &nextVertex SIP_OUT ) const override;
147     int vertexNumberFromVertexId( QgsVertexId id ) const override;
148 
149     /**
150      * Returns the point and vertex id of a point within the curve.
151      * \param node node number, where the first node is 0
152      * \param point will be set to point at corresponding node in the curve
153      * \param type will be set to the vertex type of the node
154      * \returns TRUE if node exists within the curve
155      */
156     virtual bool pointAt( int node, QgsPoint &point SIP_OUT, Qgis::VertexType &type SIP_OUT ) const = 0;
157 
158     /**
159      * Returns the index of the first vertex matching the given \a point, or -1 if a matching
160      * vertex is not found.
161      *
162      * \note If the curve has m or z values then the search \a point must have exactly matching
163      * m and z values in order to be matched against the curve's vertices.
164      * \note This method only matches against segment vertices, not curve vertices.
165      *
166      * \since QGIS 3.20
167      */
168     virtual int indexOf( const QgsPoint &point ) const = 0;
169 
170     /**
171      * Returns a reversed copy of the curve, where the direction of the curve has been flipped.
172      * \since QGIS 2.14
173      */
174     virtual QgsCurve *reversed() const = 0 SIP_FACTORY;
175 
176     QgsAbstractGeometry *boundary() const override SIP_FACTORY;
177 
178     QString asKml( int precision = 17 ) const override;
179 
180     /**
181      * Returns a geometry without curves. Caller takes ownership
182      * \param tolerance segmentation tolerance
183      * \param toleranceType maximum segmentation angle or maximum difference between approximation and curve
184     */
185     QgsCurve *segmentize( double tolerance = M_PI_2 / 90, SegmentationToleranceType toleranceType = MaximumAngle ) const override SIP_FACTORY;
186 
187     int vertexCount( int part = 0, int ring = 0 ) const override;
188     int ringCount( int part = 0 ) const override;
189     int partCount() const override;
190     QgsPoint vertexAt( QgsVertexId id ) const override;
191     QgsCurve *toCurveType() const override SIP_FACTORY;
192     void normalize() final SIP_HOLDGIL;
193 
194     QgsRectangle boundingBox() const override;
195     bool isValid( QString &error SIP_OUT, Qgis::GeometryValidityFlags flags = Qgis::GeometryValidityFlags() ) const override;
196 
197     /**
198      * Returns the x-coordinate of the specified node in the line string.
199     * \param index index of node, where the first node in the line is 0
200     * \returns x-coordinate of node, or 0.0 if index is out of bounds
201     */
202     virtual double xAt( int index ) const = 0;
203 
204     /**
205      * Returns the y-coordinate of the specified node in the line string.
206      * \param index index of node, where the first node in the line is 0
207      * \returns y-coordinate of node, or 0.0 if index is out of bounds
208      */
209     virtual double yAt( int index ) const = 0;
210 
211     /**
212      * Returns a QPolygonF representing the points.
213      */
214     virtual QPolygonF asQPolygonF() const;
215 
216     /**
217      * Returns an interpolated point on the curve at the specified \a distance.
218      *
219      * If z or m values are present, the output z and m will be interpolated using
220      * the existing vertices' z or m values.
221      *
222      * If distance is negative, or is greater than the length of the curve, NULLPTR
223      * will be returned.
224      *
225      * \since QGIS 3.4
226      */
227     virtual QgsPoint *interpolatePoint( double distance ) const = 0 SIP_FACTORY;
228 
229     /**
230      * Returns a new curve representing a substring of this curve.
231      *
232      * The \a startDistance and \a endDistance arguments specify the length along the curve
233      * which the substring should start and end at. If the \a endDistance is greater than the
234      * total length of the curve then any "extra" length will be ignored.
235      *
236      * If z or m values are present, the output z and m will be interpolated using
237      * the existing vertices' z or m values.
238      *
239      * \since QGIS 3.4
240      */
241     virtual QgsCurve *curveSubstring( double startDistance, double endDistance ) const = 0 SIP_FACTORY;
242 
243     /**
244      * Returns the straight distance of the curve, i.e. the direct/euclidean distance
245      * between the first and last vertex of the curve. (Also known as
246      * "as the crow flies" distance).
247      *
248      * \since QGIS 3.2
249      */
250     double straightDistance2d() const;
251 
252     /**
253      * Returns the curve sinuosity, which is the ratio of the curve length() to curve
254      * straightDistance2d(). Larger numbers indicate a more "sinuous" curve (i.e. more
255      * "bendy"). The minimum value returned of 1.0 indicates a perfectly straight curve.
256      *
257      * If a curve isClosed(), it has infinite sinuosity and will return NaN.
258      *
259      * \since QGIS 3.2
260      */
261     double sinuosity() const;
262 
263     //! Curve orientation
264     enum Orientation
265     {
266       Clockwise, //!< Clockwise orientation
267       CounterClockwise, //!< Counter-clockwise orientation
268     };
269 
270     /**
271      * Returns the curve's orientation, e.g. clockwise or counter-clockwise.
272      *
273      * \warning The result is not predictable for non-closed curves.
274      *
275      * \since QGIS 3.6
276      */
277     Orientation orientation() const;
278 
279     /**
280      * Scrolls the curve vertices so that they start with the vertex at the given index.
281      *
282      * \warning This should only be called on closed curves, or the shape of the curve will be altered and
283      * the result is undefined.
284      *
285      * \warning The \a firstVertexIndex must correspond to a segment vertex and not a curve point or the result
286      * is undefined.
287      *
288      * \since QGIS 3.20
289      */
290     virtual void scroll( int firstVertexIndex ) = 0;
291 
292 #ifndef SIP_RUN
293 
294     /**
295      * Cast the \a geom to a QgsCurve.
296      * Should be used by qgsgeometry_cast<QgsCurve *>( geometry ).
297      *
298      * \note Not available in Python. Objects will be automatically be converted to the appropriate target type.
299      * \since QGIS 3.0
300      */
cast(const QgsAbstractGeometry * geom)301     inline static const QgsCurve *cast( const QgsAbstractGeometry *geom )
302     {
303       if ( !geom )
304         return nullptr;
305 
306       const QgsWkbTypes::Type type = geom->wkbType();
307       if ( QgsWkbTypes::geometryType( type ) == QgsWkbTypes::LineGeometry && QgsWkbTypes::isSingleType( type ) )
308       {
309         return static_cast<const QgsCurve *>( geom );
310       }
311       return nullptr;
312     }
313 
314     /**
315      * Splits the curve at the specified vertex \a index, returning two curves which represent the portion of the
316      * curve up to an including the vertex at \a index, and the portion of the curve from the vertex at \a index (inclusive)
317      * to the end of the curve.
318      *
319      * \note The vertex \a index must correspond to a segment vertex, not a curve vertex.
320      *
321      * \note Not available in Python bindings.
322      * \since QGIS 3.20
323      */
324     virtual std::tuple< std::unique_ptr< QgsCurve >, std::unique_ptr< QgsCurve > > splitCurveAtVertex( int index ) const = 0;
325 
326 #endif
327 
328 
329   protected:
330 
331     void clearCache() const override;
332 
333     int childCount() const override;
334     QgsPoint childPoint( int index ) const override;
335 #ifndef SIP_RUN
336 
337     /**
338      * Helper function for QgsCurve subclasses to snap to grids.
339      * \note Not available in Python bindings.
340      */
341     bool snapToGridPrivate( double hSpacing, double vSpacing, double dSpacing, double mSpacing,
342                             const QVector<double> &srcX, const QVector<double> &srcY, const QVector<double> &srcZ, const QVector<double> &srcM,
343                             QVector<double> &outX, QVector<double> &outY, QVector<double> &outZ, QVector<double> &outM ) const;
344 #endif
345 
346     /**
347      * Cached bounding box.
348      */
349     mutable QgsRectangle mBoundingBox;
350 
351   private:
352 
353     mutable bool mHasCachedValidity = false;
354     mutable QString mValidityFailureReason;
355 
356     friend class TestQgsGeometry;
357 };
358 
359 #endif // QGSCURVE_H
360