QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
Go to the documentation of this file.
1 /***************************************************************************
3  -------------------
4  begin : November 2018
5  copyright : (C) 2018 by Loïc Bartoletti
6  email : loic dot bartoletti at oslandia dot com
7  ***************************************************************************/
8
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17
19 #include "qgsgeometryutils.h"
20
22
23 QgsQuadrilateral::QgsQuadrilateral( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
24 {
25  setPoints( p1, p2, p3, p4 );
26 }
27
28 QgsQuadrilateral::QgsQuadrilateral( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3, const QgsPointXY &p4 )
29 {
30  setPoints( QgsPoint( p1 ), QgsPoint( p2 ), QgsPoint( p3 ), QgsPoint( p4 ) );
31 }
32
34 {
36
37  double z = std::numeric_limits< double >::quiet_NaN();
38
39  if ( p1.is3D() )
40  z = p1.z();
41  if ( p2.is3D() && std::isnan( z ) )
42  z = p2.z();
43  if ( p3.is3D() && std::isnan( z ) )
44  z = p3.z();
45  if ( !std::isnan( z ) )
46  {
47  pType = QgsWkbTypes::addZ( pType );
48  }
49  else
50  {
51  // This is only necessary to facilitate the calculation of the perpendicular
52  // point with QgsVector3D.
53  if ( mode == Projected )
54  z = 0;
55  }
56  QgsPoint point1( pType, p1.x(), p1.y(), std::isnan( p1.z() ) ? z : p1.z() );
57  QgsPoint point2( pType, p2.x(), p2.y(), std::isnan( p2.z() ) ? z : p2.z() );
58  QgsPoint point3( pType, p3.x(), p3.y(), std::isnan( p3.z() ) ? z : p3.z() );
59
61  double inclination = 90.0;
62  double distance = 0;
63  double azimuth = point1.azimuth( point2 ) + 90.0 * QgsGeometryUtils::leftOfLine( point3.x(), point3.y(), point1.x(), point1.y(), point2.x(), point2.y() );
64  switch ( mode )
65  {
66  case Distance:
67  {
68  if ( point2.is3D() && point3.is3D() )
69  {
70  inclination = point2.inclination( point3 );
71  distance = point2.distance3D( point3 );
72  }
73  else
74  {
75  distance = point2.distance( point3 );
76  }
77
78  rect.setPoints( point1, point2, point2.project( distance, azimuth, inclination ), point1.project( distance, azimuth, inclination ) );
79  break;
80  }
81  case Projected:
82  {
83  QgsVector3D v3 = QgsVector3D::perpendicularPoint( QgsVector3D( point1.x(), point1.y(), std::isnan( point1.z() ) ? z : point1.z() ),
84  QgsVector3D( point2.x(), point2.y(), std::isnan( point2.z() ) ? z : point2.z() ),
85  QgsVector3D( point3.x(), point3.y(), std::isnan( point3.z() ) ? z : point3.z() ) );
86  QgsPoint pV3( pType, v3.x(), v3.y(), v3.z() );
87  if ( p3.is3D() )
88  {
89  inclination = pV3.inclination( p3 );
90  distance = p3.distance3D( pV3 );
91  }
92  else
93  distance = p3.distance( pV3 );
94
95  // Final points
96  QgsPoint fp1 = point1;
97  QgsPoint fp2 = point2;
98  QgsPoint fp3 = point2.project( distance, azimuth, inclination );
99  QgsPoint fp4 = point1.project( distance, azimuth, inclination ) ;
100
101  if ( pType != QgsWkbTypes::PointZ )
102  {
103  fp1.dropZValue();
104  fp2.dropZValue();
105  fp3.dropZValue();
106  fp4.dropZValue();
107  }
108  rect.setPoints( fp1, fp2, fp3, fp4 );
109  break;
110  }
111  }
112
113  return rect;
114
115 }
116
118 {
119  if ( QgsPoint( p1.x(), p1.y() ) == QgsPoint( p2.x(), p2.y() ) )
121
123  double z = p1.z();
124
125  double xMin = 0, xMax = 0, yMin = 0, yMax = 0;
126
127  if ( p1.x() < p2.x() )
128  {
129  xMin = p1.x();
130  xMax = p2.x();
131  }
132  else
133  {
134
135  xMin = p2.x();
136  xMax = p1.x();
137  }
138
139  if ( p1.y() < p2.y() )
140  {
141  yMin = p1.y();
142  yMax = p2.y();
143  }
144  else
145  {
146
147  yMin = p2.y();
148  yMax = p1.y();
149  }
150
151  quad.setPoints( QgsPoint( xMin, yMin, z ),
152  QgsPoint( xMin, yMax, z ),
153  QgsPoint( xMax, yMax, z ),
154  QgsPoint( xMax, yMin, z ) );
155
157 }
158
160 {
161
162  if ( QgsPoint( p1.x(), p1.y() ) == QgsPoint( p2.x(), p2.y() ) )
164
166  QgsPoint point2, point3 = QgsPoint( p2.x(), p2.y() ), point4;
167
168  double azimuth = p1.azimuth( point3 ) + 90.0;
169  double distance = p1.distance( point3 ) / 2.0;
170  QgsPoint midPoint = QgsGeometryUtils::midpoint( p1, point3 );
171
172  point2 = midPoint.project( -distance, azimuth );
173  point4 = midPoint.project( distance, azimuth );
174
175  if ( p1.is3D() )
176  {
177  double z = 0;
178  z = p1.z();
179  point2 = QgsPoint( point2.x(), point2.y(), z );
180  point3 = QgsPoint( point3.x(), point3.y(), z );
181  point4 = QgsPoint( point4.x(), point4.y(), z );
182  }
183
184  quad.setPoints( p1, point2, point3, point4 );
185
187 }
188
190 {
191  if ( QgsPoint( center.x(), center.y() ) == QgsPoint( point.x(), point.y() ) )
193  double xOffset = std::fabs( point.x() - center.x() );
194  double yOffset = std::fabs( point.y() - center.y() );
195
196  return QgsQuadrilateral( QgsPoint( center.x() - xOffset, center.y() - yOffset, center.z() ),
197  QgsPoint( center.x() - xOffset, center.y() + yOffset, center.z() ),
198  QgsPoint( center.x() + xOffset, center.y() + yOffset, center.z() ),
199  QgsPoint( center.x() + xOffset, center.y() - yOffset, center.z() ) );
200 }
201
203 {
206  QgsPoint( rectangle.xMinimum(), rectangle.yMinimum() ),
207  QgsPoint( rectangle.xMinimum(), rectangle.yMaximum() ),
208  QgsPoint( rectangle.xMaximum(), rectangle.yMaximum() ),
209  QgsPoint( rectangle.xMaximum(), rectangle.yMinimum() )
210  );
212 }
213
214 // Convenient method for comparison
215 // TODO: should be have a equals method for QgsPoint allowing tolerance.
216 static bool equalPoint( const QgsPoint &p1, const QgsPoint &p2, double epsilon )
217 {
218  bool equal = true;
219  equal &= qgsDoubleNear( p1.x(), p2.x(), epsilon );
220  equal &= qgsDoubleNear( p1.y(), p2.y(), epsilon );
221  if ( p1.is3D() || p2.is3D() )
222  equal &= qgsDoubleNear( p1.z(), p2.z(), epsilon ) || ( std::isnan( p1.z() ) && std::isnan( p2.z() ) );
223  if ( p1.isMeasure() || p2.isMeasure() )
224  equal &= qgsDoubleNear( p1.m(), p2.m(), epsilon ) || ( std::isnan( p1.m() ) && std::isnan( p2.m() ) );
225
226  return equal;
227 }
228
230 {
231  if ( !( isValid() || other.isValid() ) )
232  {
233  return true;
234  }
235  else if ( !isValid() || !other.isValid() )
236  {
237  return false;
238  }
239  return ( ( equalPoint( mPoint1, other.mPoint1, epsilon ) ) &&
240  ( equalPoint( mPoint2, other.mPoint2, epsilon ) ) &&
241  ( equalPoint( mPoint3, other.mPoint3, epsilon ) ) &&
242  ( equalPoint( mPoint4, other.mPoint4, epsilon ) ) );
243 }
244
246 {
247  return equals( other );
248 }
249
251 {
252  return !operator==( other );
253 }
254
255 // Returns true if segments are not self-intersected ( [2-3] / [4-1] or [1-2] /
256 // [3-4] )
257 //
258 // p3 p1 p1 p3
259 // | \ /| | \ /|
260 // | \/ | | \/ |
261 // | /\ | or | /\ |
262 // | / \| | / \|
263 // p2 p4 p2 p4
264
265 static bool isNotAntiParallelogram( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
266 {
267  QgsPoint inter;
268  bool isIntersection1234 = QgsGeometryUtils::segmentIntersection( p1, p2, p3, p4, inter, isIntersection1234 );
269  bool isIntersection2341 = QgsGeometryUtils::segmentIntersection( p2, p3, p4, p1, inter, isIntersection2341 );
270
271  return !( isIntersection1234 || isIntersection2341 );
272 }
273
274 static bool isNotCollinear( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
275 {
276  bool isCollinear =
277  (
278  ( QgsGeometryUtils::segmentSide( p1, p2, p3 ) == 0 ) ||
279  ( QgsGeometryUtils::segmentSide( p1, p2, p4 ) == 0 ) ||
280  ( QgsGeometryUtils::segmentSide( p1, p3, p4 ) == 0 ) ||
281  ( QgsGeometryUtils::segmentSide( p2, p3, p4 ) == 0 )
282  );
283
284
285  return !isCollinear;
286 }
287
288 static bool notHaveDoublePoints( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
289 {
290  bool doublePoints =
291  (
292  ( p1 == p2 ) || ( p1 == p3 ) || ( p1 == p4 ) || ( p2 == p3 ) || ( p2 == p4 ) || ( p3 == p4 ) );
293
294  return !doublePoints;
295 }
296
297 static bool haveSameType( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
298 {
299  bool sameType = !( ( p1.wkbType() != p2.wkbType() ) || ( p1.wkbType() != p3.wkbType() ) || ( p1.wkbType() != p4.wkbType() ) );
300  return sameType;
301 }
302 // Convenient method to validate inputs
303 static bool validate( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
304 {
305  return (
306  haveSameType( p1, p2, p3, p4 ) &&
307  notHaveDoublePoints( p1, p2, p3, p4 ) &&
308  isNotAntiParallelogram( p1, p2, p3, p4 ) &&
309  isNotCollinear( p1, p2, p3, p4 )
310  );
311 }
312
314 {
315  return validate( mPoint1, mPoint2, mPoint3, mPoint4 );
316 }
317
318 bool QgsQuadrilateral::setPoint( const QgsPoint &newPoint, Point index )
319 {
320  switch ( index )
321  {
322  case Point1:
323  if ( validate( newPoint, mPoint2, mPoint3, mPoint4 ) == false )
324  return false;
325  mPoint1 = newPoint;
326  break;
327  case Point2:
328  if ( validate( mPoint1, newPoint, mPoint3, mPoint4 ) == false )
329  return false;
330  mPoint2 = newPoint;
331  break;
332  case Point3:
333  if ( validate( mPoint1, mPoint2, newPoint, mPoint4 ) == false )
334  return false;
335  mPoint3 = newPoint;
336  break;
337  case Point4:
338  if ( validate( mPoint1, mPoint2, mPoint3, newPoint ) == false )
339  return false;
340  mPoint4 = newPoint;
341  break;
342  }
343
344  return true;
345 }
346
347 bool QgsQuadrilateral::setPoints( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
348 {
349  if ( validate( p1, p2, p3, p4 ) == false )
350  return false;
351
352  mPoint1 = p1;
353  mPoint2 = p2;
354  mPoint3 = p3;
355  mPoint4 = p4;
356
357  return true;
358 }
359
361 {
362  QgsPointSequence pts;
363
364  pts << mPoint1 << mPoint2 << mPoint3 << mPoint4 << mPoint1;
365
366  return pts;
367 }
368
370 {
371  std::unique_ptr<QgsPolygon> polygon = qgis::make_unique< QgsPolygon >();
372  if ( !isValid() )
373  {
374  return polygon.release();
375  }
376
377  polygon->setExteriorRing( toLineString( force2D ) );
378
379  return polygon.release();
380 }
381
383 {
384  std::unique_ptr<QgsLineString> ext = qgis::make_unique< QgsLineString>();
385  if ( !isValid() )
386  {
387  return ext.release();
388  }
389
390  QgsPointSequence pts;
391  pts = points();
392
393  ext->setPoints( pts );
394
395  if ( force2D )
396  ext->dropZValue();
397
398  return ext.release();
399 }
400
401 QString QgsQuadrilateral::toString( int pointPrecision ) const
402 {
403  QString rep;
404  if ( !isValid() )
405  rep = QStringLiteral( "Empty" );
406  else
407  rep = QStringLiteral( "Quadrilateral (Point 1: %1, Point 2: %2, Point 3: %3, Point 4: %4)" )
408  .arg( mPoint1.asWkt( pointPrecision ), 0, 's' )
409  .arg( mPoint2.asWkt( pointPrecision ), 0, 's' )
410  .arg( mPoint3.asWkt( pointPrecision ), 0, 's' )
411  .arg( mPoint4.asWkt( pointPrecision ), 0, 's' );
412
413  return rep;
414 }
415
417 {
419 }
420
422 {
424 }
bool isValid() const SIP_HOLDGIL
Convenient method to determine if a QgsQuadrilateral is valid.
Point
Simple enumeration to ensure indices in setPoint.
QgsGeometryUtils::segmentSide
static int segmentSide(const QgsPoint &pt1, const QgsPoint &pt3, const QgsPoint &pt2) SIP_HOLDGIL
For line defined by points pt1 and pt3, find out on which side of the line is point pt3.
Definition: qgsgeometryutils.cpp:1058
QgsWkbTypes::Point
@ Point
Definition: qgswkbtypes.h:72
QgsPolygon
Polygon geometry type.
Definition: qgspolygon.h:34
QgsPoint::distance
double distance(double x, double y) const SIP_HOLDGIL
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition: qgspoint.h:332
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:38
QgsPoint::asWkt
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
Definition: qgspoint.cpp:259
QgsVector3D::y
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:51
QgsVector3D
3 Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double preci...
Definition: qgsvector3d.h:32
@ Point1
ConstructionOption
A quadrilateral can be constructed from 3 points where the second distance can be determined by the t...
QgsRectangle::yMinimum
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1139
QgsPoint::z
double z
Definition: qgspoint.h:43
QString toString(int pointPrecision=17) const
Returns a string representation of the quadrilateral.
@ Projected
Second distance is equal to the distance of the perpendicualr projection of the 3rd point on the segm...
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
QgsPointSequence points() const
Returns a list including the vertices of the quadrilateral.
bool setPoint(const QgsPoint &newPoint, Point index) SIP_HOLDGIL
Sets the point newPoint at the index.
@ Point4
QgsGeometryUtils::leftOfLine
static int leftOfLine(const double x, const double y, const double x1, const double y1, const double x2, const double y2) SIP_HOLDGIL
Returns a value < 0 if the point (x, y) is left of the line from (x1, y1) -> (x2, y2).
Definition: qgsgeometryutils.cpp:589
QgsLineString
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
bool equals(const QgsQuadrilateral &other, double epsilon=4 *std::numeric_limits< double >::epsilon()) const SIP_HOLDGIL
Compares two QgsQuadrilateral, allowing specification of the maximum allowable difference between poi...
QgsLineString * toLineString(bool force2D=false) const
Returns the quadrilateral as a new linestring.
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QgsPoint::project
QgsPoint project(double distance, double azimuth, double inclination=90.0) const SIP_HOLDGIL
Returns a new point which correspond to this point projected by a specified distance with specified a...
Definition: qgspoint.cpp:715
bool setPoints(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4) SIP_HOLDGIL
Set all points Returns false if the QgsQuadrilateral is not valid:
@ Distance
Second distance is equal to the distance between 2nd and 3rd point.
QgsAbstractGeometry::isMeasure
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
Definition: qgsabstractgeometry.h:215
QgsRectangle::xMaximum
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
QgsPoint::y
double y
Definition: qgspoint.h:42
static QgsQuadrilateral rectangleFromExtent(const QgsPoint &p1, const QgsPoint &p2) SIP_HOLDGIL
Construct a QgsQuadrilateral as a rectangle from an extent, defined by two opposite corner points.
static QgsQuadrilateral squareFromDiagonal(const QgsPoint &p1, const QgsPoint &p2) SIP_HOLDGIL
Construct a QgsQuadrilateral as a square from a diagonal.
static QgsQuadrilateral rectangleFrom3Points(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode) SIP_HOLDGIL
Construct a QgsQuadrilateral as a Rectangle from 3 points.
bool operator!=(const QgsQuadrilateral &other) const SIP_HOLDGIL
QgsAbstractGeometry::wkbType
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
Definition: qgsabstractgeometry.h:193
QgsVector3D::z
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:53
QgsVector3D::perpendicularPoint
static QgsVector3D perpendicularPoint(const QgsVector3D &v1, const QgsVector3D &v2, const QgsVector3D &vp)
Returns the perpendicular point of vector vp from [v1 - v2].
Definition: qgsvector3d.h:138
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
QgsPoint::azimuth
double azimuth(const QgsPoint &other) const SIP_HOLDGIL
Calculates Cartesian azimuth between this point and other one (clockwise in degree,...
Definition: qgspoint.cpp:696
QgsCurvePolygon::area
double area() const override SIP_HOLDGIL
Returns the planar, 2-dimensional area of the geometry.
Definition: qgscurvepolygon.cpp:466
QgsPoint::dropZValue
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Definition: qgspoint.cpp:582
@ Point2
QgsWkbTypes::PointZ
@ PointZ
Definition: qgswkbtypes.h:86
QgsPoint::x
Definition: qgspoint.h:41
double perimeter() const SIP_HOLDGIL
Returns the perimeter of the quadrilateral, or 0 if the quadrilateral is empty.
QgsPoint::m
double m
Definition: qgspoint.h:44
Constructor for an empty quadrilateral geometry.
QgsRectangle::xMinimum
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
QgsCurvePolygon::perimeter
double perimeter() const override SIP_HOLDGIL
Returns the planar, 2-dimensional perimeter of the geometry.
Definition: qgscurvepolygon.cpp:494
QgsGeometryUtils::segmentIntersection
static bool segmentIntersection(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q1, const QgsPoint &q2, QgsPoint &intersectionPoint, bool &isIntersection, double tolerance=1e-8, bool acceptImproperIntersection=false) SIP_HOLDGIL
Compute the intersection between two segments.
Definition: qgsgeometryutils.cpp:261
static QgsQuadrilateral fromRectangle(const QgsRectangle &rectangle) SIP_HOLDGIL
Construct a QgsQuadrilateral as a rectangle from a QgsRectangle.
QgsPoint::inclination
double inclination(const QgsPoint &other) const SIP_HOLDGIL
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition: qgspoint.cpp:703
qgsgeometryutils.h
QgsAbstractGeometry::is3D
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
Definition: qgsabstractgeometry.h:206
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:44
QgsGeometryUtils::midpoint
static QgsPoint midpoint(const QgsPoint &pt1, const QgsPoint &pt2) SIP_HOLDGIL
Returns a middle point between points pt1 and pt2.
Definition: qgsgeometryutils.cpp:1376
QgsRectangle::yMaximum
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
QgsPointSequence
QVector< QgsPoint > QgsPointSequence
Definition: qgsabstractgeometry.h:46
static QgsQuadrilateral rectangleFromCenterPoint(const QgsPoint &center, const QgsPoint &point) SIP_HOLDGIL
Construct a QgsQuadrilateral as a rectangle from center point center and another point point.
double area() const SIP_HOLDGIL
Returns the area of the quadrilateral, or 0 if the quadrilateral is empty.
bool operator==(const QgsQuadrilateral &other) const SIP_HOLDGIL
@ Point3