1 /***************************************************************************
2                          qgscircularstring.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 QGSCIRCULARSTRING_H
19 #define QGSCIRCULARSTRING_H
20 
21 #include <QVector>
22 
23 #include "qgis_core.h"
24 #include "qgis_sip.h"
25 #include "qgscurve.h"
26 
27 
28 /**
29  * \ingroup core
30  * \class QgsCircularString
31  * \brief Circular string geometry type
32  * \since QGIS 2.10
33  */
34 class CORE_EXPORT QgsCircularString: public QgsCurve
35 {
36   public:
37 
38     /**
39      * Constructs an empty circular string.
40      */
41     QgsCircularString() SIP_HOLDGIL;
42 
43     /**
44      * Constructs a circular string with a single
45      * arc passing through \a p1, \a p2 and \a p3.
46      *
47      * \since QGIS 3.2
48      */
49     QgsCircularString( const QgsPoint &p1,
50                        const QgsPoint &p2,
51                        const QgsPoint &p3 ) SIP_HOLDGIL;
52 
53     /**
54      * Construct a circular string from arrays of coordinates. If the z or m
55      * arrays are non-empty then the resultant circular string will have
56      * z and m types accordingly.
57      *
58      * This constructor is more efficient then calling setPoints().
59      *
60      * If the sizes of \a x and \a y are non-equal then the resultant circular string
61      * will be created using the minimum size of these arrays.
62      *
63      * \warning It is the caller's responsibility to ensure that the supplied arrays
64      * are of odd sizes.
65      *
66      * \since QGIS 3.20
67      */
68     QgsCircularString( const QVector<double> &x, const QVector<double> &y,
69                        const QVector<double> &z = QVector<double>(),
70                        const QVector<double> &m = QVector<double>() ) SIP_HOLDGIL;
71 
72 
73     /**
74      * Creates a circular string with a single arc representing
75      * the curve from \a p1 to \a p2 with the specified \a center.
76      *
77      * If \a useShortestArc is TRUE, then the arc returned will be that corresponding
78      * to the shorter arc from \a p1 to \a p2. If it is FALSE, the longer arc from \a p1
79      * to \a p2 will be used (i.e. winding the other way around the circle).
80      *
81      * \since QGIS 3.2
82      */
83     static QgsCircularString fromTwoPointsAndCenter( const QgsPoint &p1,
84         const QgsPoint &p2,
85         const QgsPoint &center,
86         bool useShortestArc = true );
87 
88     bool equals( const QgsCurve &other ) const override;
89 
90     QString geometryType() const override SIP_HOLDGIL;
91     int dimension() const override SIP_HOLDGIL;
92     QgsCircularString *clone() const override SIP_FACTORY;
93     void clear() override;
94 
95     bool fromWkb( QgsConstWkbPtr &wkb ) override;
96     bool fromWkt( const QString &wkt ) override;
97 
98     int wkbSize( QgsAbstractGeometry::WkbFlags flags = QgsAbstractGeometry::WkbFlags() ) const override;
99     QByteArray asWkb( QgsAbstractGeometry::WkbFlags flags = QgsAbstractGeometry::WkbFlags() ) const override;
100     QString asWkt( int precision = 17 ) const override;
101     QDomElement asGml2( QDomDocument &doc, int precision = 17, const QString &ns = "gml", QgsAbstractGeometry::AxisOrder axisOrder = QgsAbstractGeometry::AxisOrder::XY ) const override;
102     QDomElement asGml3( QDomDocument &doc, int precision = 17, const QString &ns = "gml", QgsAbstractGeometry::AxisOrder axisOrder = QgsAbstractGeometry::AxisOrder::XY ) const override;
103     json asJsonObject( int precision = 17 ) const override SIP_SKIP;
104     bool isEmpty() const override SIP_HOLDGIL;
105     bool isValid( QString &error SIP_OUT, Qgis::GeometryValidityFlags flags = Qgis::GeometryValidityFlags() ) const override;
106     int numPoints() const override SIP_HOLDGIL;
107     int indexOf( const QgsPoint &point ) const final;
108 
109     /**
110      * Returns the point at index i within the circular string.
111      */
112     QgsPoint pointN( int i ) const SIP_HOLDGIL;
113 
114     void points( QgsPointSequence &pts SIP_OUT ) const override;
115 
116     /**
117      * Sets the circular string's points
118      */
119     void setPoints( const QgsPointSequence &points );
120 
121     /**
122      * Appends the contents of another circular \a string to the end of this circular string.
123      *
124      * \param string circular string to append. Ownership is not transferred.
125      *
126      * \warning It is the caller's responsibility to ensure that the first point in the appended
127      * \a string matches the last point in the existing curve, or the result will be undefined.
128      *
129      * \since QGIS 3.20
130      */
131     void append( const QgsCircularString *string );
132 
133     double length() const override;
134     QgsPoint startPoint() const override SIP_HOLDGIL;
135     QgsPoint endPoint() const override SIP_HOLDGIL;
136     QgsLineString *curveToLine( double tolerance = M_PI_2 / 90, SegmentationToleranceType toleranceType = MaximumAngle ) const override SIP_FACTORY;
137     QgsCircularString *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const override SIP_FACTORY;
138     bool removeDuplicateNodes( double epsilon = 4 * std::numeric_limits<double>::epsilon(), bool useZValues = false ) override;
139 
140     void draw( QPainter &p ) const override;
141     void transform( const QgsCoordinateTransform &ct, Qgis::TransformDirection d = Qgis::TransformDirection::Forward, bool transformZ = false ) override SIP_THROW( QgsCsException );
142     void transform( const QTransform &t, double zTranslate = 0.0, double zScale = 1.0, double mTranslate = 0.0, double mScale = 1.0 ) override;
143     void addToPainterPath( QPainterPath &path ) const override;
144     void drawAsPolygon( QPainter &p ) const override;
145     bool insertVertex( QgsVertexId position, const QgsPoint &vertex ) override;
146     bool moveVertex( QgsVertexId position, const QgsPoint &newPos ) override;
147     bool deleteVertex( QgsVertexId position ) override;
148     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;
149     bool pointAt( int node, QgsPoint &point, Qgis::VertexType &type ) const override;
150     void sumUpArea( double &sum SIP_OUT ) const override;
151     bool hasCurvedSegments() const override;
152     double vertexAngle( QgsVertexId vertex ) const override;
153     double segmentLength( QgsVertexId startVertex ) const override;
154     QgsCircularString *reversed() const override  SIP_FACTORY;
155     QgsPoint *interpolatePoint( double distance ) const override SIP_FACTORY;
156     QgsCircularString *curveSubstring( double startDistance, double endDistance ) const override SIP_FACTORY;
157     bool addZValue( double zValue = 0 ) override;
158     bool addMValue( double mValue = 0 ) override;
159     bool dropZValue() override;
160     bool dropMValue() override;
161     void swapXy() override;
162     double xAt( int index ) const override SIP_HOLDGIL;
163     double yAt( int index ) const override SIP_HOLDGIL;
164 
165     bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = nullptr ) override;
166     void scroll( int firstVertexIndex ) final;
167 
168 #ifndef SIP_RUN
169     void filterVertices( const std::function< bool( const QgsPoint & ) > &filter ) override;
170     void transformVertices( const std::function< QgsPoint( const QgsPoint & ) > &transform ) override;
171     std::tuple< std::unique_ptr< QgsCurve >, std::unique_ptr< QgsCurve > > splitCurveAtVertex( int index ) const final;
172 
173     /**
174      * Cast the \a geom to a QgsCircularString.
175      * Should be used by qgsgeometry_cast<QgsCircularString *>( geometry ).
176      *
177      * \note Not available in Python. Objects will be automatically be converted to the appropriate target type.
178      * \since QGIS 3.0
179      */
cast(const QgsAbstractGeometry * geom)180     inline static const QgsCircularString *cast( const QgsAbstractGeometry *geom )
181     {
182       if ( geom && QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::CircularString )
183         return static_cast<const QgsCircularString *>( geom );
184       return nullptr;
185     }
186 #endif
187 
188     QgsCircularString *createEmptyWithSameType() const override SIP_FACTORY;
189 
190 #ifdef SIP_RUN
191     SIP_PYOBJECT __repr__();
192     % MethodCode
193     QString wkt = sipCpp->asWkt();
194     if ( wkt.length() > 1000 )
195       wkt = wkt.left( 1000 ) + QStringLiteral( "..." );
196     QString str = QStringLiteral( "<QgsCircularString: %1>" ).arg( wkt );
197     sipRes = PyUnicode_FromString( str.toUtf8().constData() );
198     % End
199 #endif
200 
201   protected:
202 
203     int compareToSameClass( const QgsAbstractGeometry *other ) const final;
204     QgsRectangle calculateBoundingBox() const override;
205 
206   private:
207     QVector<double> mX;
208     QVector<double> mY;
209     QVector<double> mZ;
210     QVector<double> mM;
211 
212 #if 0
213     static void arcTo( QPainterPath &path, QPointF pt1, QPointF pt2, QPointF pt3 );
214 #endif
215     //bounding box of a single segment
216     static QgsRectangle segmentBoundingBox( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3 );
217     static QgsPointSequence compassPointsOnSegment( double p1Angle, double p2Angle, double p3Angle, double centerX, double centerY, double radius );
218     static double closestPointOnArc( double x1, double y1, double x2, double y2, double x3, double y3,
219                                      const QgsPoint &pt, QgsPoint &segmentPt,  QgsVertexId &vertexAfter, int *leftOf, double epsilon );
220     void insertVertexBetween( int after, int before, int pointOnCircle );
221     void deleteVertex( int i );
222 
223 };
224 
225 // clazy:excludeall=qstring-allocations
226 
227 #endif // QGSCIRCULARSTRING_H
228