1 /*************************************************************************** 2 qgsinternalgeometryengine.h - QgsInternalGeometryEngine 3 4 --------------------- 5 begin : 13.1.2016 6 copyright : (C) 2016 by Matthias Kuhn 7 email : matthias@opengis.ch 8 *************************************************************************** 9 * * 10 * This program is free software; you can redistribute it and/or modify * 11 * it under the terms of the GNU General Public License as published by * 12 * the Free Software Foundation; either version 2 of the License, or * 13 * (at your option) any later version. * 14 * * 15 ***************************************************************************/ 16 #ifndef QGSINTERNALGEOMETRYENGINE_H 17 #define QGSINTERNALGEOMETRYENGINE_H 18 19 #define SIP_NO_FILE 20 21 #include <functional> 22 23 #include "qgspointxy.h" 24 25 class QgsGeometry; 26 class QgsAbstractGeometry; 27 class QgsLineString; 28 class QgsLineSegment2D; 29 class QgsFeedback; 30 31 /** 32 * \ingroup core 33 * \brief This class offers geometry processing methods. 34 * 35 * The methods are available via QgsGeometry::[geometryfunction] 36 * and therefore this does not need to be accessed directly. 37 * 38 * \note not available in Python bindings 39 */ 40 41 class QgsInternalGeometryEngine 42 { 43 public: 44 45 /** 46 * The caller is responsible that the geometry is available and unchanged 47 * for the whole lifetime of this object. 48 * \param geometry 49 */ 50 explicit QgsInternalGeometryEngine( const QgsGeometry &geometry ); 51 52 /** 53 * Returns an error string referring to the last error encountered. 54 * 55 * \since QGIS 3.16 56 */ 57 QString lastError() const; 58 59 /** 60 * Returns TRUE if the geometry is a polygon that is almost an axis-parallel rectangle. 61 * 62 * The \a maximumDeviation argument specifes the maximum angle (in degrees) that the polygon edges 63 * are allowed to deviate from axis parallel lines. 64 * 65 * By default the check will permit polygons with more than 4 edges, so long as the overall shape of 66 * the polygon is an axis-parallel rectangle (i.e. it is tolerant to rectangles with additional vertices 67 * added along the rectangle sides). If \a simpleRectanglesOnly is set to TRUE then the method will 68 * only return TRUE if the geometry is a simple rectangle consisting of 4 edges. 69 * 70 * \since QGIS 3.20 71 */ 72 bool isAxisParallelRectangle( double maximumDeviation, bool simpleRectanglesOnly = false ) const; 73 74 /** 75 * Will extrude a line or (segmentized) curve by a given offset and return a polygon 76 * representation of it. 77 * 78 * \param x offset in x direction 79 * \param y offset in y direction 80 * \returns an extruded polygon 81 */ 82 QgsGeometry extrude( double x, double y ) const; 83 84 /** 85 * Calculates the approximate pole of inaccessibility for a surface, which is the 86 * most distant internal point from the boundary of the surface. This function 87 * uses the 'polylabel' algorithm (Vladimir Agafonkin, 2016), which is an iterative 88 * approach guaranteed to find the true pole of inaccessibility within a specified 89 * tolerance. More precise tolerances require more iterations and will take longer 90 * to calculate. 91 * Optionally, the distance to the polygon boundary from the pole can be stored. 92 */ 93 QgsGeometry poleOfInaccessibility( double precision, double *distanceFromBoundary = nullptr ) const; 94 95 /** 96 * Attempts to orthogonalize a line or polygon geometry by shifting vertices to make the geometries 97 * angles either right angles or flat lines. This is an iterative algorithm which will loop until 98 * either the vertices are within a specified tolerance of right angles or a set number of maximum 99 * iterations is reached. The angle threshold parameter specifies how close to a right angle or 100 * straight line an angle must be before it is attempted to be straightened. 101 * \since QGIS 3.0 102 */ 103 QgsGeometry orthogonalize( double tolerance = 1.0E-8, int maxIterations = 1000, double angleThreshold = 15.0 ) const; 104 105 /** 106 * Densifies the geometry by adding the specified number of extra nodes within each 107 * segment of the geometry. 108 * If the geometry has z or m values present then these will be linearly interpolated 109 * at the added nodes. 110 * Curved geometry types are automatically segmentized by this routine. 111 * \since QGIS 3.0 112 */ 113 QgsGeometry densifyByCount( int extraNodesPerSegment ) const; 114 115 /** 116 * Densifies the geometry by adding regularly placed extra nodes inside each segment 117 * so that the maximum distance between any two nodes does not exceed the 118 * specified \a distance. 119 * E.g. specifying a distance 3 would cause the segment [0 0] -> [10 0] 120 * to be converted to [0 0] -> [2.5 0] -> [5 0] -> [7.5 0] -> [10 0], since 121 * 3 extra nodes are required on the segment and spacing these at 2.5 increments 122 * allows them to be evenly spaced over the segment. 123 * If the geometry has z or m values present then these will be linearly interpolated 124 * at the added nodes. 125 * Curved geometry types are automatically segmentized by this routine. 126 * \since QGIS 3.0 127 */ 128 QgsGeometry densifyByDistance( double distance ) const; 129 130 /** 131 * Calculates a variable width buffer for a (multi)curve geometry. 132 * 133 * The width of the buffer at each node in the input linestrings is calculated by 134 * calling the specified \a widthFunction, which must return an array of the buffer widths 135 * for every node in the line. 136 * 137 * The \a segments argument specifies the number of segments to approximate quarter-circle 138 * curves in the buffer. 139 * 140 * Non (multi)curve input geometries will return a null output geometry. 141 * 142 * \since QGIS 3.2 143 */ 144 QgsGeometry variableWidthBuffer( int segments, const std::function< std::unique_ptr< double[] >( const QgsLineString *line ) > &widthFunction ) const; 145 146 /** 147 * Calculates a tapered width buffer for a (multi)curve geometry. 148 * 149 * The buffer begins at a width of \a startWidth at the start of each curve, and 150 * ends at a width of \a endWidth. Note that unlike QgsGeometry::buffer() methods, \a startWidth 151 * and \a endWidth are the diameter of the buffer at these points, not the radius. 152 * 153 * The \a segments argument specifies the number of segments to approximate quarter-circle 154 * curves in the buffer. 155 * 156 * Non (multi)curve input geometries will return a null output geometry. 157 * 158 * \since QGIS 3.2 159 */ 160 QgsGeometry taperedBuffer( double startWidth, double endWidth, int segments ) const; 161 162 /** 163 * Calculates a variable width buffer using the m-values from a (multi)line geometry. 164 * 165 * The \a segments argument specifies the number of segments to approximate quarter-circle 166 * curves in the buffer. 167 * 168 * Non (multi)line input geometries will return a null output geometry. 169 * 170 * \since QGIS 3.2 171 */ 172 QgsGeometry variableWidthBufferByM( int segments ) const; 173 174 /** 175 * Returns a list of \a count random points generated inside a \a polygon geometry 176 * (if \a acceptPoint is specified, and restrictive, the number of points returned may 177 * be less than \a count). 178 * 179 * Optionally, a specific random \a seed can be used when generating points. If \a seed 180 * is 0, then a completely random sequence of points will be generated. 181 * 182 * The \a acceptPoint function is used to filter result candidates. If the function returns 183 * FALSE, then the point will not be accepted and another candidate generated. 184 * 185 * The optional \a feedback argument can be used to provide cancellation support during 186 * the point generation. 187 * 188 * When \a acceptPoint is specified, \a maxTriesPerPoint 189 * defines how many attempts to perform before giving up generating 190 * a point. 191 * 192 * \since QGIS 3.10 193 */ 194 static QVector< QgsPointXY > randomPointsInPolygon( const QgsGeometry &polygon, int count, 195 const std::function< bool( const QgsPointXY & ) > &acceptPoint, unsigned long seed = 0, QgsFeedback *feedback = nullptr, int maxTriesPerPoint = 0 ); 196 197 /** 198 * Attempts to convert a non-curved geometry into a curved geometry type (e.g. 199 * LineString to CompoundCurve, Polygon to CurvePolygon). 200 * 201 * The \a distanceTolerance specifies the maximum deviation allowed between the original location 202 * of vertices and where they would fall on the candidate curved geometry. 203 * 204 * This method only consider a segments as suitable for replacing with an arc if the points are all 205 * regularly spaced on the candidate arc. The \a pointSpacingAngleTolerance parameter specifies the maximum 206 * angular deviation (in radians) allowed when testing for regular point spacing. 207 * 208 * \note The API is considered EXPERIMENTAL and can be changed without a notice 209 * 210 * \since QGIS 3.14 211 */ 212 QgsGeometry convertToCurves( double distanceTolerance, double angleTolerance ) const; 213 214 /** 215 * Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) 216 * rotated rectangle which fully encompasses the geometry. 217 * The area, angle of the long axis (clockwise in degrees from North), 218 * width and height of the rotated bounding box will also be returned. 219 * 220 * If an error was encountered while creating the result, more information can be retrieved 221 * by calling lastError(). 222 * 223 * \since QGIS 3.16 224 */ 225 QgsGeometry orientedMinimumBoundingBox( double &area SIP_OUT, double &angle SIP_OUT, double &width SIP_OUT, double &height SIP_OUT ) const; 226 227 private: 228 const QgsAbstractGeometry *mGeometry = nullptr; 229 230 mutable QString mLastError; 231 }; 232 233 /** 234 * \brief A 2D ray which extends from an origin point to an infinite distance in a given direction. 235 * \ingroup core 236 * \note not available in Python bindings 237 * \since QGIS 3.2 238 */ 239 class CORE_EXPORT QgsRay2D 240 { 241 public: 242 243 /** 244 * Constructor for a ray starting at the given \a origin and extending an infinite distance 245 * in the specified \a direction. 246 */ QgsRay2D(const QgsPointXY & origin,QgsVector direction)247 QgsRay2D( const QgsPointXY &origin, QgsVector direction ) 248 : origin( origin ) 249 , direction( direction ) 250 {} 251 252 /** 253 * Finds the closest intersection point of the ray and a line \a segment. 254 * 255 * If found, the intersection point will be stored in \a intersectPoint. 256 * 257 * Returns TRUE if the ray intersects the line segment. 258 */ 259 bool intersects( const QgsLineSegment2D &segment, QgsPointXY &intersectPoint ) const; 260 261 private: 262 263 QgsPointXY origin; 264 QgsVector direction; 265 }; 266 267 ///@cond PRIVATE 268 269 // adapted for QGIS geometry classes from original work at https://github.com/trylock/visibility by trylock 270 271 /** 272 * \brief Compares two line segments based on their distance from a given point. 273 * 274 * Assumes: (1) the line segments are intersected by some ray from the origin 275 * (2) the line segments do not intersect except at their endpoints 276 * (3) no line segment is collinear with the origin 277 * \ingroup core 278 * \since QGIS 3.2 279 */ 280 class CORE_EXPORT QgsLineSegmentDistanceComparer 281 { 282 public: 283 284 /** 285 * Constructor for QgsLineSegmentDistanceComparer, comparing points 286 * to the specified \a origin point. 287 */ QgsLineSegmentDistanceComparer(const QgsPointXY & origin)288 explicit QgsLineSegmentDistanceComparer( const QgsPointXY &origin ) 289 : mOrigin( origin ) 290 {} 291 292 /** 293 * Checks whether the line segment \a ab is closer to the origin than the 294 * line segment \a cd. 295 * \param ab line segment: left hand side of the comparison operator 296 * \param cd line segment: right hand side of the comparison operator 297 * \returns TRUE if ab < cd (ab is closer than cd) to origin 298 */ 299 bool operator()( QgsLineSegment2D ab, QgsLineSegment2D cd ) const; 300 301 private: 302 303 QgsPointXY mOrigin; 304 305 }; 306 307 308 // adapted for QGIS geometry classes from original work at https://github.com/trylock/visibility by trylock 309 310 /** 311 * \brief Compares angles from an origin to points clockwise, starting at the positive y-axis. 312 * 313 * \ingroup core 314 * \since QGIS 3.2 315 */ 316 class CORE_EXPORT QgsClockwiseAngleComparer 317 { 318 public: QgsClockwiseAngleComparer(const QgsPointXY & origin)319 explicit QgsClockwiseAngleComparer( const QgsPointXY &origin ) 320 : mVertex( origin ) 321 {} 322 323 bool operator()( const QgsPointXY &a, const QgsPointXY &b ) const; 324 325 private: 326 327 QgsPointXY mVertex; 328 329 }; 330 331 ///@endcond PRIVATE 332 333 #endif // QGSINTERNALGEOMETRYENGINE_H 334