QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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 * QgsGeometryUtils::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 != QgsWkbTypes::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 return toPolygon()->area();
439}
440
442{
443 return toPolygon()->perimeter();
444}
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
double area() const override SIP_HOLDGIL
Returns the planar, 2-dimensional area of the geometry.
double perimeter() const override SIP_HOLDGIL
Returns the planar, 2-dimensional perimeter of the geometry.
static QgsPoint midpoint(const QgsPoint &pt1, const QgsPoint &pt2) SIP_HOLDGIL
Returns a middle point between points pt1 and pt2.
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.
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.
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).
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:59
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
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:343
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:562
QgsPoint project(double distance, double azimuth, double inclination=90.0) const SIP_HOLDGIL
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition: qgspoint.cpp:735
double distance3D(double x, double y, double z) const SIP_HOLDGIL
Returns the Cartesian 3D distance between this point and a specified x, y, z coordinate.
Definition: qgspoint.cpp:680
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
Definition: qgspoint.cpp:264
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
double azimuth(const QgsPoint &other) const SIP_HOLDGIL
Calculates Cartesian azimuth between this point and other one (clockwise in degree,...
Definition: qgspoint.cpp:716
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Definition: qgspoint.cpp:592
double m
Definition: qgspoint.h:55
double y
Definition: qgspoint.h:53
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:723
Polygon geometry type.
Definition: qgspolygon.h:34
Quadrilateral geometry type.
double perimeter() const SIP_HOLDGIL
Returns the perimeter of the quadrilateral, or 0 if the quadrilateral is empty.
static QgsQuadrilateral squareFromDiagonal(const QgsPoint &p1, const QgsPoint &p2) SIP_HOLDGIL
Construct a QgsQuadrilateral as a square from a diagonal.
bool setPoint(const QgsPoint &newPoint, Point index) SIP_HOLDGIL
Sets the point newPoint at the index.
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 rectangleFromCenterPoint(const QgsPoint &center, const QgsPoint &point) SIP_HOLDGIL
Construct a QgsQuadrilateral as a rectangle from center point center and another point point.
QgsLineString * toLineString(bool force2D=false) const
Returns the quadrilateral as a new linestring.
bool isValid() const SIP_HOLDGIL
Convenient method to determine if a QgsQuadrilateral is valid.
QgsQuadrilateral() SIP_HOLDGIL
Constructor for an empty quadrilateral geometry.
Point
Simple enumeration to ensure indices in setPoint.
bool operator==(const QgsQuadrilateral &other) const SIP_HOLDGIL
QString toString(int pointPrecision=17) const
Returns a string representation of the quadrilateral.
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:
double area() const SIP_HOLDGIL
Returns the area of the quadrilateral, or 0 if the quadrilateral is empty.
static QgsQuadrilateral fromRectangle(const QgsRectangle &rectangle) SIP_HOLDGIL
Construct a QgsQuadrilateral as a rectangle from a QgsRectangle.
bool operator!=(const QgsQuadrilateral &other) const SIP_HOLDGIL
QgsPolygon * toPolygon(bool force2D=false) const
Returns the quadrilateral as a new polygon.
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...
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 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...
QgsPointSequence points() const
Returns a list including the vertices of the quadrilateral.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:51
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:53
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:49
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:139
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1176
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2527
QVector< QgsPoint > QgsPointSequence