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