QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsquadrilateral.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsquadrilateral.cpp
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 *
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#include "qgsquadrilateral.h"
19#include "qgsgeometryutils.h"
20
22
23QgsQuadrilateral::QgsQuadrilateral( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
24{
25 setPoints( p1, p2, p3, p4 );
26}
27
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 double m = std::numeric_limits< double >::quiet_NaN();
39
40 // We don't need the m value right away, it will only be inserted at the end.
41 if ( p1.isMeasure() )
42 m = p1.m();
43 if ( p2.isMeasure() && std::isnan( m ) )
44 m = p2.m();
45 if ( p3.isMeasure() && std::isnan( m ) )
46 m = p3.m();
47
48 if ( p1.is3D() )
49 z = p1.z();
50 if ( p2.is3D() && std::isnan( z ) )
51 z = p2.z();
52 if ( p3.is3D() && std::isnan( z ) )
53 z = p3.z();
54 if ( !std::isnan( z ) )
55 {
56 pType = QgsWkbTypes::addZ( pType );
57 }
58 else
59 {
60 // This is only necessary to facilitate the calculation of the perpendicular
61 // point with QgsVector3D.
62 if ( mode == Projected )
63 z = 0;
64 }
65 const QgsPoint point1( pType, p1.x(), p1.y(), std::isnan( p1.z() ) ? z : p1.z() );
66 const QgsPoint point2( pType, p2.x(), p2.y(), std::isnan( p2.z() ) ? z : p2.z() );
67 const QgsPoint point3( pType, p3.x(), p3.y(), std::isnan( p3.z() ) ? z : p3.z() );
68
70 double inclination = 90.0;
71 double distance = 0;
72 const double azimuth = point1.azimuth( point2 ) + 90.0 * QgsGeometryUtilsBase::leftOfLine( point3.x(), point3.y(), point1.x(), point1.y(), point2.x(), point2.y() );
73 switch ( mode )
74 {
75 case Distance:
76 {
77 if ( point2.is3D() && point3.is3D() )
78 {
79 inclination = point2.inclination( point3 );
80 distance = point2.distance3D( point3 );
81 }
82 else
83 {
84 distance = point2.distance( point3 );
85 }
86
87 break;
88 }
89 case Projected:
90 {
91 const QgsVector3D v3 = QgsVector3D::perpendicularPoint( QgsVector3D( point1.x(), point1.y(), std::isnan( point1.z() ) ? z : point1.z() ),
92 QgsVector3D( point2.x(), point2.y(), std::isnan( point2.z() ) ? z : point2.z() ),
93 QgsVector3D( point3.x(), point3.y(), std::isnan( point3.z() ) ? z : point3.z() ) );
94 const QgsPoint pV3( pType, v3.x(), v3.y(), v3.z() );
95 if ( p3.is3D() )
96 {
97 inclination = pV3.inclination( p3 );
98 distance = p3.distance3D( pV3 );
99 }
100 else
101 distance = p3.distance( pV3 );
102
103 break;
104 }
105 }
106
107 // Final points
108 QgsPoint fp1 = point1;
109 QgsPoint fp2 = point2;
110 QgsPoint fp3 = point2.project( distance, azimuth, inclination );
111 QgsPoint fp4 = point1.project( distance, azimuth, inclination ) ;
112
113 if ( pType != Qgis::WkbType::PointZ )
114 {
115 fp1.dropZValue();
116 fp2.dropZValue();
117 fp3.dropZValue();
118 fp4.dropZValue();
119 }
120
121 if ( !std::isnan( m ) )
122 {
123 fp1.addMValue( m );
124 fp2.addMValue( m );
125 fp3.addMValue( m );
126 fp4.addMValue( m );
127 }
128
129 rect.setPoints( fp1, fp2, fp3, fp4 );
130 return rect;
131
132}
133
135{
136 if ( QgsPoint( p1.x(), p1.y() ) == QgsPoint( p2.x(), p2.y() ) )
137 return QgsQuadrilateral();
138
139 QgsQuadrilateral quad;
140 const double z = p1.z();
141 const double m = p1.m();
142
143 double xMin = 0, xMax = 0, yMin = 0, yMax = 0;
144
145 if ( p1.x() < p2.x() )
146 {
147 xMin = p1.x();
148 xMax = p2.x();
149 }
150 else
151 {
152
153 xMin = p2.x();
154 xMax = p1.x();
155 }
156
157 if ( p1.y() < p2.y() )
158 {
159 yMin = p1.y();
160 yMax = p2.y();
161 }
162 else
163 {
164
165 yMin = p2.y();
166 yMax = p1.y();
167 }
168
169 quad.setPoints( QgsPoint( p1.wkbType(), xMin, yMin, z, m ),
170 QgsPoint( p1.wkbType(), xMin, yMax, z, m ),
171 QgsPoint( p1.wkbType(), xMax, yMax, z, m ),
172 QgsPoint( p1.wkbType(), xMax, yMin, z, m ) );
173
174 return quad;
175}
176
178{
179
180 if ( QgsPoint( p1.x(), p1.y() ) == QgsPoint( p2.x(), p2.y() ) )
181 return QgsQuadrilateral();
182
183 const double z = p1.z();
184 const double m = p1.m();
185
186 QgsQuadrilateral quad;
187 QgsPoint point2, point3 = QgsPoint( p2.x(), p2.y() ), point4;
188
189 const double azimuth = p1.azimuth( point3 ) + 90.0;
190 const double distance = p1.distance( point3 ) / 2.0;
191 const QgsPoint midPoint = QgsGeometryUtils::midpoint( p1, point3 );
192
193 point2 = midPoint.project( -distance, azimuth );
194 point4 = midPoint.project( distance, azimuth );
195
196 // add z and m, could be NaN
197 point2 = QgsPoint( p1.wkbType(), point2.x(), point2.y(), z, m );
198 point3 = QgsPoint( p1.wkbType(), point3.x(), point3.y(), z, m );
199 point4 = QgsPoint( p1.wkbType(), point4.x(), point4.y(), z, m );
200
201 quad.setPoints( p1, point2, point3, point4 );
202
203 return quad;
204}
205
207{
208 if ( QgsPoint( center.x(), center.y() ) == QgsPoint( point.x(), point.y() ) )
209 return QgsQuadrilateral();
210 const double xOffset = std::fabs( point.x() - center.x() );
211 const double yOffset = std::fabs( point.y() - center.y() );
212
213 return QgsQuadrilateral( QgsPoint( center.wkbType(), center.x() - xOffset, center.y() - yOffset, center.z(), center.m() ),
214 QgsPoint( center.wkbType(), center.x() - xOffset, center.y() + yOffset, center.z(), center.m() ),
215 QgsPoint( center.wkbType(), center.x() + xOffset, center.y() + yOffset, center.z(), center.m() ),
216 QgsPoint( center.wkbType(), center.x() + xOffset, center.y() - yOffset, center.z(), center.m() ) );
217}
218
220{
221 QgsQuadrilateral quad;
222 quad.setPoints(
223 QgsPoint( rectangle.xMinimum(), rectangle.yMinimum() ),
224 QgsPoint( rectangle.xMinimum(), rectangle.yMaximum() ),
225 QgsPoint( rectangle.xMaximum(), rectangle.yMaximum() ),
226 QgsPoint( rectangle.xMaximum(), rectangle.yMinimum() )
227 );
228 return quad;
229}
230
231// Convenient method for comparison
232// TODO: should be have a equals method for QgsPoint allowing tolerance.
233static bool equalPoint( const QgsPoint &p1, const QgsPoint &p2, double epsilon )
234{
235 bool equal = true;
236 equal &= qgsDoubleNear( p1.x(), p2.x(), epsilon );
237 equal &= qgsDoubleNear( p1.y(), p2.y(), epsilon );
238 if ( p1.is3D() || p2.is3D() )
239 equal &= qgsDoubleNear( p1.z(), p2.z(), epsilon ) || ( std::isnan( p1.z() ) && std::isnan( p2.z() ) );
240 if ( p1.isMeasure() || p2.isMeasure() )
241 equal &= qgsDoubleNear( p1.m(), p2.m(), epsilon ) || ( std::isnan( p1.m() ) && std::isnan( p2.m() ) );
242
243 return equal;
244}
245
246bool QgsQuadrilateral::equals( const QgsQuadrilateral &other, double epsilon ) const
247{
248 if ( !( isValid() || other.isValid() ) )
249 {
250 return true;
251 }
252 else if ( !isValid() || !other.isValid() )
253 {
254 return false;
255 }
256 return ( ( equalPoint( mPoint1, other.mPoint1, epsilon ) ) &&
257 ( equalPoint( mPoint2, other.mPoint2, epsilon ) ) &&
258 ( equalPoint( mPoint3, other.mPoint3, epsilon ) ) &&
259 ( equalPoint( mPoint4, other.mPoint4, epsilon ) ) );
260}
261
263{
264 return equals( other );
265}
266
268{
269 return !operator==( other );
270}
271
272// Returns true if segments are not self-intersected ( [2-3] / [4-1] or [1-2] /
273// [3-4] )
274//
275// p3 p1 p1 p3
276// | \ /| | \ /|
277// | \/ | | \/ |
278// | /\ | or | /\ |
279// | / \| | / \|
280// p2 p4 p2 p4
281
282static bool isNotAntiParallelogram( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
283{
284 QgsPoint inter;
285 bool isIntersection1234 = QgsGeometryUtils::segmentIntersection( p1, p2, p3, p4, inter, isIntersection1234 );
286 bool isIntersection2341 = QgsGeometryUtils::segmentIntersection( p2, p3, p4, p1, inter, isIntersection2341 );
287
288 return !( isIntersection1234 || isIntersection2341 );
289}
290
291static bool isNotCollinear( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
292{
293 const bool isCollinear =
294 (
295 ( QgsGeometryUtils::segmentSide( p1, p2, p3 ) == 0 ) ||
296 ( QgsGeometryUtils::segmentSide( p1, p2, p4 ) == 0 ) ||
297 ( QgsGeometryUtils::segmentSide( p1, p3, p4 ) == 0 ) ||
298 ( QgsGeometryUtils::segmentSide( p2, p3, p4 ) == 0 )
299 );
300
301
302 return !isCollinear;
303}
304
305static bool notHaveDoublePoints( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
306{
307 const bool doublePoints =
308 (
309 ( p1 == p2 ) || ( p1 == p3 ) || ( p1 == p4 ) || ( p2 == p3 ) || ( p2 == p4 ) || ( p3 == p4 ) );
310
311 return !doublePoints;
312}
313
314static bool haveSameType( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
315{
316 const bool sameType = !( ( p1.wkbType() != p2.wkbType() ) || ( p1.wkbType() != p3.wkbType() ) || ( p1.wkbType() != p4.wkbType() ) );
317 return sameType;
318}
319// Convenient method to validate inputs
320static bool validate( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
321{
322 return (
323 haveSameType( p1, p2, p3, p4 ) &&
324 notHaveDoublePoints( p1, p2, p3, p4 ) &&
325 isNotAntiParallelogram( p1, p2, p3, p4 ) &&
326 isNotCollinear( p1, p2, p3, p4 )
327 );
328}
329
331{
332 return validate( mPoint1, mPoint2, mPoint3, mPoint4 );
333}
334
335bool QgsQuadrilateral::setPoint( const QgsPoint &newPoint, Point index )
336{
337 switch ( index )
338 {
339 case Point1:
340 if ( validate( newPoint, mPoint2, mPoint3, mPoint4 ) == false )
341 return false;
342 mPoint1 = newPoint;
343 break;
344 case Point2:
345 if ( validate( mPoint1, newPoint, mPoint3, mPoint4 ) == false )
346 return false;
347 mPoint2 = newPoint;
348 break;
349 case Point3:
350 if ( validate( mPoint1, mPoint2, newPoint, mPoint4 ) == false )
351 return false;
352 mPoint3 = newPoint;
353 break;
354 case Point4:
355 if ( validate( mPoint1, mPoint2, mPoint3, newPoint ) == false )
356 return false;
357 mPoint4 = newPoint;
358 break;
359 }
360
361 return true;
362}
363
364bool QgsQuadrilateral::setPoints( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
365{
366 if ( validate( p1, p2, p3, p4 ) == false )
367 return false;
368
369 mPoint1 = p1;
370 mPoint2 = p2;
371 mPoint3 = p3;
372 mPoint4 = p4;
373
374 return true;
375}
376
378{
380
381 pts << mPoint1 << mPoint2 << mPoint3 << mPoint4 << mPoint1;
382
383 return pts;
384}
385
387{
388 std::unique_ptr<QgsPolygon> polygon = std::make_unique< QgsPolygon >();
389 if ( !isValid() )
390 {
391 return polygon.release();
392 }
393
394 polygon->setExteriorRing( toLineString( force2D ) );
395
396 return polygon.release();
397}
398
400{
401 std::unique_ptr<QgsLineString> ext = std::make_unique< QgsLineString>();
402 if ( !isValid() )
403 {
404 return ext.release();
405 }
406
408 pts = points();
409
410 ext->setPoints( pts );
411
412 if ( force2D )
413 ext->dropZValue();
414
415 if ( force2D )
416 ext->dropMValue();
417
418 return ext.release();
419}
420
421QString QgsQuadrilateral::toString( int pointPrecision ) const
422{
423 QString rep;
424 if ( !isValid() )
425 rep = QStringLiteral( "Empty" );
426 else
427 rep = QStringLiteral( "Quadrilateral (Point 1: %1, Point 2: %2, Point 3: %3, Point 4: %4)" )
428 .arg( mPoint1.asWkt( pointPrecision ), 0, 's' )
429 .arg( mPoint2.asWkt( pointPrecision ), 0, 's' )
430 .arg( mPoint3.asWkt( pointPrecision ), 0, 's' )
431 .arg( mPoint4.asWkt( pointPrecision ), 0, 's' );
432
433 return rep;
434}
435
437{
438 std::unique_ptr<QgsPolygon> polygon( toPolygon() );
439 return polygon->area();
440}
441
443{
444 std::unique_ptr<QgsPolygon> polygon( toPolygon() );
445 return polygon->perimeter();
446}
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition: qgis.h:182
@ PointZ
PointZ.
bool isMeasure() const
Returns true if the geometry contains m values.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
static int leftOfLine(const double x, const double y, const double x1, const double y1, const double x2, const double y2)
Returns a value < 0 if the point (x, y) is left of the line from (x1, y1) -> (x2, y2).
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)
Compute the intersection between two segments.
static int segmentSide(const QgsPoint &pt1, const QgsPoint &pt3, const QgsPoint &pt2)
For line defined by points pt1 and pt3, find out on which side of the line is point pt2.
static QgsPoint midpoint(const QgsPoint &pt1, const QgsPoint &pt2)
Returns a middle point between points pt1 and pt2.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
A class to represent a 2D point.
Definition: qgspointxy.h:60
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
double inclination(const QgsPoint &other) const
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition: qgspoint.cpp:689
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:564
double azimuth(const QgsPoint &other) const
Calculates Cartesian azimuth between this point and other one (clockwise in degree,...
Definition: qgspoint.cpp:682
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
Definition: qgspoint.cpp:261
Q_GADGET double x
Definition: qgspoint.h:52
double distance3D(double x, double y, double z) const
Returns the Cartesian 3D distance between this point and a specified x, y, z coordinate.
Definition: qgspoint.h:437
double z
Definition: qgspoint.h:54
double distance(double x, double y) const
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition: qgspoint.h:393
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Definition: qgspoint.cpp:594
double m
Definition: qgspoint.h:55
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition: qgspoint.cpp:701
double y
Definition: qgspoint.h:53
Polygon geometry type.
Definition: qgspolygon.h:33
Quadrilateral geometry type.
bool setPoint(const QgsPoint &newPoint, Point index)
Sets the point newPoint at the index.
bool operator!=(const QgsQuadrilateral &other) const
double perimeter() const
Returns the perimeter of the quadrilateral, or 0 if the quadrilateral is empty.
static QgsQuadrilateral fromRectangle(const QgsRectangle &rectangle)
Construct a QgsQuadrilateral as a rectangle from a QgsRectangle.
static QgsQuadrilateral rectangleFromCenterPoint(const QgsPoint &center, const QgsPoint &point)
Construct a QgsQuadrilateral as a rectangle from center point center and another point point.
static QgsQuadrilateral rectangleFromExtent(const QgsPoint &p1, const QgsPoint &p2)
Construct a QgsQuadrilateral as a rectangle from an extent, defined by two opposite corner points.
QgsLineString * toLineString(bool force2D=false) const
Returns the quadrilateral as a new linestring.
Point
Simple enumeration to ensure indices in setPoint.
QString toString(int pointPrecision=17) const
Returns a string representation of the quadrilateral.
bool isValid() const
Convenient method to determine if a QgsQuadrilateral is valid.
double area() const
Returns the area of the quadrilateral, or 0 if the quadrilateral is empty.
static QgsQuadrilateral squareFromDiagonal(const QgsPoint &p1, const QgsPoint &p2)
Construct a QgsQuadrilateral as a square from a diagonal.
QgsQuadrilateral()
Constructor for an empty quadrilateral geometry.
bool equals(const QgsQuadrilateral &other, double epsilon=4 *std::numeric_limits< double >::epsilon()) const
Compares two QgsQuadrilateral, allowing specification of the maximum allowable difference between poi...
QgsPolygon * toPolygon(bool force2D=false) const
Returns the quadrilateral as a new polygon.
static QgsQuadrilateral rectangleFrom3Points(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode)
Construct a QgsQuadrilateral as a Rectangle from 3 points.
ConstructionOption
A quadrilateral can be constructed from 3 points where the second distance can be determined by the t...
@ Distance
Second distance is equal to the distance between 2nd and 3rd point.
@ Projected
Second distance is equal to the distance of the perpendicular projection of the 3rd point on the segm...
bool operator==(const QgsQuadrilateral &other) const
QgsPointSequence points() const
Returns a list including the vertices of the quadrilateral.
bool setPoints(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4)
Set all points Returns false if the QgsQuadrilateral is not valid:
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:201
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:211
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:196
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:206
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
Definition: qgsvector3d.h:31
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:50
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:52
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:48
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:156
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1068
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5207
QVector< QgsPoint > QgsPointSequence