1 /*************************************************************************** 2 qgscurvepolygon.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 QGSCURVEPOLYGON_H 19 #define QGSCURVEPOLYGON_H 20 21 #include "qgis_core.h" 22 #include "qgis_sip.h" 23 #include "qgssurface.h" 24 #include <memory> 25 26 class QgsPolygon; 27 28 /** 29 * \ingroup core 30 * \class QgsCurvePolygon 31 * \brief Curve polygon geometry type 32 * \since QGIS 2.10 33 */ 34 class CORE_EXPORT QgsCurvePolygon: public QgsSurface 35 { 36 public: 37 QgsCurvePolygon(); 38 QgsCurvePolygon( const QgsCurvePolygon &p ); 39 QgsCurvePolygon &operator=( const QgsCurvePolygon &p ); 40 41 bool operator==( const QgsAbstractGeometry &other ) const override; 42 bool operator!=( const QgsAbstractGeometry &other ) const override; 43 44 ~QgsCurvePolygon() override; 45 46 QString geometryType() const override SIP_HOLDGIL; 47 int dimension() const override SIP_HOLDGIL; 48 QgsCurvePolygon *clone() const override SIP_FACTORY; 49 void clear() override; 50 51 bool fromWkb( QgsConstWkbPtr &wkb ) override; 52 bool fromWkt( const QString &wkt ) override; 53 54 int wkbSize( QgsAbstractGeometry::WkbFlags flags = QgsAbstractGeometry::WkbFlags() ) const override; 55 QByteArray asWkb( QgsAbstractGeometry::WkbFlags flags = QgsAbstractGeometry::WkbFlags() ) const override; 56 QString asWkt( int precision = 17 ) const override; 57 QDomElement asGml2( QDomDocument &doc, int precision = 17, const QString &ns = "gml", QgsAbstractGeometry::AxisOrder axisOrder = QgsAbstractGeometry::AxisOrder::XY ) const override; 58 QDomElement asGml3( QDomDocument &doc, int precision = 17, const QString &ns = "gml", QgsAbstractGeometry::AxisOrder axisOrder = QgsAbstractGeometry::AxisOrder::XY ) const override; 59 json asJsonObject( int precision = 17 ) const override SIP_SKIP; 60 QString asKml( int precision = 17 ) const override; 61 void normalize() final SIP_HOLDGIL; 62 63 //surface interface 64 double area() const override SIP_HOLDGIL; 65 double perimeter() const override SIP_HOLDGIL; 66 QgsPolygon *surfaceToPolygon() const override SIP_FACTORY; 67 QgsAbstractGeometry *boundary() const override SIP_FACTORY; 68 QgsCurvePolygon *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const override SIP_FACTORY; 69 bool removeDuplicateNodes( double epsilon = 4 * std::numeric_limits<double>::epsilon(), bool useZValues = false ) override; 70 bool boundingBoxIntersects( const QgsRectangle &rectangle ) const override SIP_HOLDGIL; 71 72 //curve polygon interface 73 74 /** 75 * Returns the number of interior rings contained with the curve polygon. 76 * 77 * \see interiorRing() 78 */ numInteriorRings()79 int numInteriorRings() const SIP_HOLDGIL 80 { 81 return mInteriorRings.size(); 82 } 83 84 /** 85 * Returns the curve polygon's exterior ring. 86 * 87 * \see interiorRing() 88 */ exteriorRing()89 const QgsCurve *exteriorRing() const SIP_HOLDGIL 90 { 91 return mExteriorRing.get(); 92 } 93 94 /** 95 * Returns a non-const pointer to the curve polygon's exterior ring. 96 * Ownership stays with this QgsCurve. 97 * 98 * \see interiorRing() 99 * \note Not available in Python. 100 * \since QGIS 3.20 101 */ exteriorRing()102 QgsCurve *exteriorRing() SIP_SKIP 103 { 104 return mExteriorRing.get(); 105 } 106 107 #ifndef SIP_RUN 108 109 /** 110 * Retrieves an interior ring from the curve polygon. The first interior ring has index 0. 111 * 112 * \see numInteriorRings() 113 * \see exteriorRing() 114 */ interiorRing(int i)115 const QgsCurve *interiorRing( int i ) const SIP_HOLDGIL 116 { 117 if ( i < 0 || i >= mInteriorRings.size() ) 118 { 119 return nullptr; 120 } 121 return mInteriorRings.at( i ); 122 } 123 124 /** 125 * Retrieves an interior ring from the curve polygon. The first interior ring has index 0. 126 * 127 * \see numInteriorRings() 128 * \see exteriorRing() 129 * \note Not available in Python. 130 * \since QGIS 3.20 131 */ interiorRing(int i)132 QgsCurve *interiorRing( int i ) SIP_SKIP 133 { 134 if ( i < 0 || i >= mInteriorRings.size() ) 135 { 136 return nullptr; 137 } 138 return mInteriorRings.at( i ); 139 } 140 #else 141 142 /** 143 * Retrieves an interior ring from the curve polygon. The first interior ring has index 0. 144 * 145 * \throws IndexError if no interior ring with the specified index exists. 146 * 147 * \see numInteriorRings() 148 * \see exteriorRing() 149 */ 150 SIP_PYOBJECT interiorRing( int i ) SIP_HOLDGIL SIP_TYPEHINT( QgsCurve ); 151 % MethodCode 152 if ( a0 < 0 || a0 >= sipCpp->numInteriorRings() ) 153 { 154 PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) ); 155 sipIsErr = 1; 156 } 157 else 158 { 159 return sipConvertFromType( const_cast< QgsCurve * >( sipCpp->interiorRing( a0 ) ), sipType_QgsCurve, NULL ); 160 } 161 % End 162 #endif 163 164 /** 165 * Returns a new polygon geometry corresponding to a segmentized approximation 166 * of the curve. 167 * \param tolerance segmentation tolerance 168 * \param toleranceType maximum segmentation angle or maximum difference between approximation and curve 169 */ 170 virtual QgsPolygon *toPolygon( double tolerance = M_PI_2 / 90, SegmentationToleranceType toleranceType = MaximumAngle ) const SIP_FACTORY; 171 172 /** 173 * Sets the exterior ring of the polygon. The CurvePolygon type will be updated to match the dimensionality 174 * of the exterior ring. For instance, setting a 2D exterior ring on a 3D CurvePolygon will drop the z dimension 175 * from the CurvePolygon and all interior rings. 176 * \param ring new exterior ring. Ownership is transferred to the CurvePolygon. 177 * \see setInteriorRings() 178 * \see exteriorRing() 179 */ 180 virtual void setExteriorRing( QgsCurve *ring SIP_TRANSFER ); 181 182 //! Sets all interior rings (takes ownership) 183 void setInteriorRings( const QVector<QgsCurve *> &rings SIP_TRANSFER ); 184 //! Adds an interior ring to the geometry (takes ownership) 185 virtual void addInteriorRing( QgsCurve *ring SIP_TRANSFER ); 186 187 #ifndef SIP_RUN 188 189 /** 190 * Removes an interior ring from the polygon. The first interior ring has index 0. 191 * The corresponding ring is removed from the polygon and deleted. If a ring was successfully removed 192 * the function will return TRUE. It is not possible to remove the exterior ring using this method. 193 * \see removeInteriorRings() 194 */ 195 bool removeInteriorRing( int ringIndex ); 196 #else 197 198 /** 199 * Removes an interior ring from the polygon. The first interior ring has index 0. 200 * The corresponding ring is removed from the polygon and deleted. 201 * It is not possible to remove the exterior ring using this method. 202 * 203 * \throws IndexError if no interior ring with the specified index exists. 204 * 205 * \see removeInteriorRings() 206 */ 207 bool removeInteriorRing( int i ); 208 % MethodCode 209 if ( a0 < 0 || a0 >= sipCpp->numInteriorRings() ) 210 { 211 PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) ); 212 sipIsErr = 1; 213 } 214 else 215 { 216 return PyBool_FromLong( sipCpp->removeInteriorRing( a0 ) ); 217 } 218 % End 219 #endif 220 221 /** 222 * Removes the interior rings from the polygon. If the minimumAllowedArea 223 * parameter is specified then only rings smaller than this minimum 224 * area will be removed. 225 * \see removeInteriorRing() 226 * \since QGIS 3.0 227 */ 228 void removeInteriorRings( double minimumAllowedArea = -1 ); 229 230 /** 231 * Removes any interior rings which are not valid from the polygon. 232 * 233 * For example, this removes unclosed rings and rings with less than 4 vertices. 234 * 235 * \since QGIS 3.0 236 */ 237 void removeInvalidRings(); 238 239 /** 240 * Forces the geometry to respect the Right-Hand-Rule, in which the area that is 241 * bounded by the polygon is to the right of the boundary. In particular, the exterior 242 * ring is oriented in a clockwise direction and the interior rings in a counter-clockwise 243 * direction. 244 * 245 * \since QGIS 3.6 246 */ 247 void forceRHR(); 248 249 QPainterPath asQPainterPath() const override; 250 void draw( QPainter &p ) const override; 251 void transform( const QgsCoordinateTransform &ct, Qgis::TransformDirection d = Qgis::TransformDirection::Forward, bool transformZ = false ) override SIP_THROW( QgsCsException ); 252 void transform( const QTransform &t, double zTranslate = 0.0, double zScale = 1.0, double mTranslate = 0.0, double mScale = 1.0 ) override; 253 254 bool insertVertex( QgsVertexId position, const QgsPoint &vertex ) override; 255 bool moveVertex( QgsVertexId position, const QgsPoint &newPos ) override; 256 bool deleteVertex( QgsVertexId position ) override; 257 258 QgsCoordinateSequence coordinateSequence() const override; 259 int nCoordinates() const override; 260 int vertexNumberFromVertexId( QgsVertexId id ) const override; 261 bool isEmpty() const override SIP_HOLDGIL; 262 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; 263 264 bool nextVertex( QgsVertexId &id, QgsPoint &vertex SIP_OUT ) const override; 265 void adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex SIP_OUT, QgsVertexId &nextVertex SIP_OUT ) const override; 266 bool hasCurvedSegments() const override; 267 268 /** 269 * Returns a geometry without curves. Caller takes ownership 270 * \param tolerance segmentation tolerance 271 * \param toleranceType maximum segmentation angle or maximum difference between approximation and curve 272 */ 273 QgsAbstractGeometry *segmentize( double tolerance = M_PI_2 / 90, SegmentationToleranceType toleranceType = MaximumAngle ) const override SIP_FACTORY; 274 275 /** 276 * Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments. 277 * \param vertex the vertex id 278 * \returns rotation in radians, clockwise from north 279 */ 280 double vertexAngle( QgsVertexId vertex ) const override; 281 282 int vertexCount( int part = 0, int ring = 0 ) const override; 283 int ringCount( int part = 0 ) const override SIP_HOLDGIL; 284 int partCount() const override SIP_HOLDGIL; 285 QgsPoint vertexAt( QgsVertexId id ) const override; 286 double segmentLength( QgsVertexId startVertex ) const override; 287 288 bool addZValue( double zValue = 0 ) override; 289 bool addMValue( double mValue = 0 ) override; 290 bool dropZValue() override; 291 bool dropMValue() override; 292 void swapXy() override; 293 294 QgsCurvePolygon *toCurveType() const override SIP_FACTORY; 295 296 bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = nullptr ) override; 297 298 #ifndef SIP_RUN 299 void filterVertices( const std::function< bool( const QgsPoint & ) > &filter ) override; 300 void transformVertices( const std::function< QgsPoint( const QgsPoint & ) > &transform ) override; 301 302 /** 303 * Cast the \a geom to a QgsCurvePolygon. 304 * Should be used by qgsgeometry_cast<QgsCurvePolygon *>( geometry ). 305 * 306 * \note Not available in Python. Objects will be automatically be converted to the appropriate target type. 307 * \since QGIS 3.0 308 */ cast(const QgsAbstractGeometry * geom)309 inline static const QgsCurvePolygon *cast( const QgsAbstractGeometry *geom ) 310 { 311 if ( !geom ) 312 return nullptr; 313 314 const QgsWkbTypes::Type flatType = QgsWkbTypes::flatType( geom->wkbType() ); 315 if ( flatType == QgsWkbTypes::CurvePolygon 316 || flatType == QgsWkbTypes::Polygon 317 || flatType == QgsWkbTypes::Triangle ) 318 return static_cast<const QgsCurvePolygon *>( geom ); 319 return nullptr; 320 } 321 #endif 322 323 QgsCurvePolygon *createEmptyWithSameType() const override SIP_FACTORY; 324 325 #ifdef SIP_RUN 326 SIP_PYOBJECT __repr__(); 327 % MethodCode 328 QString wkt = sipCpp->asWkt(); 329 if ( wkt.length() > 1000 ) 330 wkt = wkt.left( 1000 ) + QStringLiteral( "..." ); 331 QString str = QStringLiteral( "<QgsCurvePolygon: %1>" ).arg( wkt ); 332 sipRes = PyUnicode_FromString( str.toUtf8().constData() ); 333 % End 334 #endif 335 336 protected: 337 338 int childCount() const override; 339 QgsAbstractGeometry *childGeometry( int index ) const override; 340 int compareToSameClass( const QgsAbstractGeometry *other ) const final; 341 342 protected: 343 344 std::unique_ptr< QgsCurve > mExteriorRing; 345 QVector<QgsCurve *> mInteriorRings; 346 347 QgsRectangle calculateBoundingBox() const override; 348 }; 349 350 // clazy:excludeall=qstring-allocations 351 352 #endif // QGSCURVEPOLYGON_H 353