QGIS API Documentation 3.99.0-Master (09f76ad7019)
Loading...
Searching...
No Matches
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
20#include "qgsgeometryutils.h"
21
22#include <QString>
23
24using namespace Qt::StringLiterals;
25
27
28QgsQuadrilateral::QgsQuadrilateral( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
29{
30 setPoints( p1, p2, p3, p4 );
31}
32
34{
35 setPoints( QgsPoint( p1 ), QgsPoint( p2 ), QgsPoint( p3 ), QgsPoint( p4 ) );
36}
37
39{
41
42 double z = std::numeric_limits< double >::quiet_NaN();
43 double m = std::numeric_limits< double >::quiet_NaN();
44
45 // We don't need the m value right away, it will only be inserted at the end.
46 if ( p1.isMeasure() )
47 m = p1.m();
48 if ( p2.isMeasure() && std::isnan( m ) )
49 m = p2.m();
50 if ( p3.isMeasure() && std::isnan( m ) )
51 m = p3.m();
52
53 if ( p1.is3D() )
54 z = p1.z();
55 if ( p2.is3D() && std::isnan( z ) )
56 z = p2.z();
57 if ( p3.is3D() && std::isnan( z ) )
58 z = p3.z();
59 if ( !std::isnan( z ) )
60 {
61 pType = QgsWkbTypes::addZ( pType );
62 }
63 else
64 {
65 // This is only necessary to facilitate the calculation of the perpendicular
66 // point with QgsVector3D.
67 if ( mode == Projected )
68 z = 0;
69 }
70 const QgsPoint point1( pType, p1.x(), p1.y(), std::isnan( p1.z() ) ? z : p1.z() );
71 const QgsPoint point2( pType, p2.x(), p2.y(), std::isnan( p2.z() ) ? z : p2.z() );
72 const QgsPoint point3( pType, p3.x(), p3.y(), std::isnan( p3.z() ) ? z : p3.z() );
73
75 double inclination = 90.0;
76 double distance = 0;
77 const double azimuth = point1.azimuth( point2 ) + 90.0 * QgsGeometryUtilsBase::leftOfLine( point3.x(), point3.y(), point1.x(), point1.y(), point2.x(), point2.y() );
78 switch ( mode )
79 {
80 case Distance:
81 {
82 if ( point2.is3D() && point3.is3D() )
83 {
84 inclination = point2.inclination( point3 );
85 distance = point2.distance3D( point3 );
86 }
87 else
88 {
89 distance = point2.distance( point3 );
90 }
91
92 break;
93 }
94 case Projected:
95 {
96 const QgsVector3D v3 = QgsVector3D::perpendicularPoint( QgsVector3D( point1.x(), point1.y(), std::isnan( point1.z() ) ? z : point1.z() ),
97 QgsVector3D( point2.x(), point2.y(), std::isnan( point2.z() ) ? z : point2.z() ),
98 QgsVector3D( point3.x(), point3.y(), std::isnan( point3.z() ) ? z : point3.z() ) );
99 const QgsPoint pV3( pType, v3.x(), v3.y(), v3.z() );
100 if ( p3.is3D() )
101 {
102 inclination = pV3.inclination( p3 );
103 distance = p3.distance3D( pV3 );
104 }
105 else
106 distance = p3.distance( pV3 );
107
108 break;
109 }
110 }
111
112 // Final points
113 QgsPoint fp1 = point1;
114 QgsPoint fp2 = point2;
115 QgsPoint fp3 = point2.project( distance, azimuth, inclination );
116 QgsPoint fp4 = point1.project( distance, azimuth, inclination ) ;
117
118 if ( pType != Qgis::WkbType::PointZ )
119 {
120 fp1.dropZValue();
121 fp2.dropZValue();
122 fp3.dropZValue();
123 fp4.dropZValue();
124 }
125
126 if ( !std::isnan( m ) )
127 {
128 fp1.addMValue( m );
129 fp2.addMValue( m );
130 fp3.addMValue( m );
131 fp4.addMValue( m );
132 }
133
134 rect.setPoints( fp1, fp2, fp3, fp4 );
135 return rect;
136
137}
138
140{
141 if ( QgsPoint( p1.x(), p1.y() ) == QgsPoint( p2.x(), p2.y() ) )
142 return QgsQuadrilateral();
143
144 QgsQuadrilateral quad;
145 const double z = p1.z();
146 const double m = p1.m();
147
148 double xMin = 0, xMax = 0, yMin = 0, yMax = 0;
149
150 if ( p1.x() < p2.x() )
151 {
152 xMin = p1.x();
153 xMax = p2.x();
154 }
155 else
156 {
157
158 xMin = p2.x();
159 xMax = p1.x();
160 }
161
162 if ( p1.y() < p2.y() )
163 {
164 yMin = p1.y();
165 yMax = p2.y();
166 }
167 else
168 {
169
170 yMin = p2.y();
171 yMax = p1.y();
172 }
173
174 quad.setPoints( QgsPoint( p1.wkbType(), xMin, yMin, z, m ),
175 QgsPoint( p1.wkbType(), xMin, yMax, z, m ),
176 QgsPoint( p1.wkbType(), xMax, yMax, z, m ),
177 QgsPoint( p1.wkbType(), xMax, yMin, z, m ) );
178
179 return quad;
180}
181
183{
184
185 if ( QgsPoint( p1.x(), p1.y() ) == QgsPoint( p2.x(), p2.y() ) )
186 return QgsQuadrilateral();
187
188 const double z = p1.z();
189 const double m = p1.m();
190
191 QgsQuadrilateral quad;
192 QgsPoint point2, point3 = QgsPoint( p2.x(), p2.y() ), point4;
193
194 const double azimuth = p1.azimuth( point3 ) + 90.0;
195 const double distance = p1.distance( point3 ) / 2.0;
196 const QgsPoint midPoint = QgsGeometryUtils::midpoint( p1, point3 );
197
198 point2 = midPoint.project( -distance, azimuth );
199 point4 = midPoint.project( distance, azimuth );
200
201 // add z and m, could be NaN
202 point2 = QgsPoint( p1.wkbType(), point2.x(), point2.y(), z, m );
203 point3 = QgsPoint( p1.wkbType(), point3.x(), point3.y(), z, m );
204 point4 = QgsPoint( p1.wkbType(), point4.x(), point4.y(), z, m );
205
206 quad.setPoints( p1, point2, point3, point4 );
207
208 return quad;
209}
210
212{
213 if ( QgsPoint( center.x(), center.y() ) == QgsPoint( point.x(), point.y() ) )
214 return QgsQuadrilateral();
215 const double xOffset = std::fabs( point.x() - center.x() );
216 const double yOffset = std::fabs( point.y() - center.y() );
217
218 return QgsQuadrilateral( QgsPoint( center.wkbType(), center.x() - xOffset, center.y() - yOffset, center.z(), center.m() ),
219 QgsPoint( center.wkbType(), center.x() - xOffset, center.y() + yOffset, center.z(), center.m() ),
220 QgsPoint( center.wkbType(), center.x() + xOffset, center.y() + yOffset, center.z(), center.m() ),
221 QgsPoint( center.wkbType(), center.x() + xOffset, center.y() - yOffset, center.z(), center.m() ) );
222}
223
225{
226 QgsQuadrilateral quad;
227 quad.setPoints(
228 QgsPoint( rectangle.xMinimum(), rectangle.yMinimum() ),
229 QgsPoint( rectangle.xMinimum(), rectangle.yMaximum() ),
230 QgsPoint( rectangle.xMaximum(), rectangle.yMaximum() ),
231 QgsPoint( rectangle.xMaximum(), rectangle.yMinimum() )
232 );
233 return quad;
234}
235
236// Convenient method for comparison
237// TODO: should be have a equals method for QgsPoint allowing tolerance.
238static bool equalPoint( const QgsPoint &p1, const QgsPoint &p2, double epsilon )
239{
240 bool equal = true;
241 equal &= qgsDoubleNear( p1.x(), p2.x(), epsilon );
242 equal &= qgsDoubleNear( p1.y(), p2.y(), epsilon );
243 if ( p1.is3D() || p2.is3D() )
244 equal &= qgsDoubleNear( p1.z(), p2.z(), epsilon ) || ( std::isnan( p1.z() ) && std::isnan( p2.z() ) );
245 if ( p1.isMeasure() || p2.isMeasure() )
246 equal &= qgsDoubleNear( p1.m(), p2.m(), epsilon ) || ( std::isnan( p1.m() ) && std::isnan( p2.m() ) );
247
248 return equal;
249}
250
251bool QgsQuadrilateral::equals( const QgsQuadrilateral &other, double epsilon ) const
252{
253 if ( !( isValid() || other.isValid() ) )
254 {
255 return true;
256 }
257 else if ( !isValid() || !other.isValid() )
258 {
259 return false;
260 }
261 return ( ( equalPoint( mPoint1, other.mPoint1, epsilon ) ) &&
262 ( equalPoint( mPoint2, other.mPoint2, epsilon ) ) &&
263 ( equalPoint( mPoint3, other.mPoint3, epsilon ) ) &&
264 ( equalPoint( mPoint4, other.mPoint4, epsilon ) ) );
265}
266
268{
269 return equals( other );
270}
271
273{
274 return !operator==( other );
275}
276
277// Returns true if segments are not self-intersected ( [2-3] / [4-1] or [1-2] /
278// [3-4] )
279//
280// p3 p1 p1 p3
281// | \ /| | \ /|
282// | \/ | | \/ |
283// | /\ | or | /\ |
284// | / \| | / \|
285// p2 p4 p2 p4
286
287static bool isNotAntiParallelogram( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
288{
289 QgsPoint inter;
290 bool isIntersection1234 = QgsGeometryUtils::segmentIntersection( p1, p2, p3, p4, inter, isIntersection1234 );
291 bool isIntersection2341 = QgsGeometryUtils::segmentIntersection( p2, p3, p4, p1, inter, isIntersection2341 );
292
293 return !( isIntersection1234 || isIntersection2341 );
294}
295
296static bool isNotCollinear( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
297{
298 const bool isCollinear =
299 (
300 ( QgsGeometryUtils::segmentSide( p1, p2, p3 ) == 0 ) ||
301 ( QgsGeometryUtils::segmentSide( p1, p2, p4 ) == 0 ) ||
302 ( QgsGeometryUtils::segmentSide( p1, p3, p4 ) == 0 ) ||
303 ( QgsGeometryUtils::segmentSide( p2, p3, p4 ) == 0 )
304 );
305
306
307 return !isCollinear;
308}
309
310static bool notHaveDoublePoints( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
311{
312 const bool doublePoints =
313 (
314 ( p1 == p2 ) || ( p1 == p3 ) || ( p1 == p4 ) || ( p2 == p3 ) || ( p2 == p4 ) || ( p3 == p4 ) );
315
316 return !doublePoints;
317}
318
319static bool haveSameType( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
320{
321 const bool sameType = !( ( p1.wkbType() != p2.wkbType() ) || ( p1.wkbType() != p3.wkbType() ) || ( p1.wkbType() != p4.wkbType() ) );
322 return sameType;
323}
324// Convenient method to validate inputs
325static bool validate( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
326{
327 return (
328 haveSameType( p1, p2, p3, p4 ) &&
329 notHaveDoublePoints( p1, p2, p3, p4 ) &&
330 isNotAntiParallelogram( p1, p2, p3, p4 ) &&
331 isNotCollinear( p1, p2, p3, p4 )
332 );
333}
334
336{
337 return validate( mPoint1, mPoint2, mPoint3, mPoint4 );
338}
339
340bool QgsQuadrilateral::setPoint( const QgsPoint &newPoint, Point index )
341{
342 switch ( index )
343 {
344 case Point1:
345 if ( validate( newPoint, mPoint2, mPoint3, mPoint4 ) == false )
346 return false;
347 mPoint1 = newPoint;
348 break;
349 case Point2:
350 if ( validate( mPoint1, newPoint, mPoint3, mPoint4 ) == false )
351 return false;
352 mPoint2 = newPoint;
353 break;
354 case Point3:
355 if ( validate( mPoint1, mPoint2, newPoint, mPoint4 ) == false )
356 return false;
357 mPoint3 = newPoint;
358 break;
359 case Point4:
360 if ( validate( mPoint1, mPoint2, mPoint3, newPoint ) == false )
361 return false;
362 mPoint4 = newPoint;
363 break;
364 }
365
366 return true;
367}
368
369bool QgsQuadrilateral::setPoints( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
370{
371 if ( validate( p1, p2, p3, p4 ) == false )
372 return false;
373
374 mPoint1 = p1;
375 mPoint2 = p2;
376 mPoint3 = p3;
377 mPoint4 = p4;
378
379 return true;
380}
381
383{
385
386 pts << mPoint1 << mPoint2 << mPoint3 << mPoint4 << mPoint1;
387
388 return pts;
389}
390
392{
393 auto polygon = std::make_unique< QgsPolygon >();
394 if ( !isValid() )
395 {
396 return polygon.release();
397 }
398
399 polygon->setExteriorRing( toLineString( force2D ) );
400
401 return polygon.release();
402}
403
405{
406 auto ext = std::make_unique< QgsLineString>();
407 if ( !isValid() )
408 {
409 return ext.release();
410 }
411
413 pts = points();
414
415 ext->setPoints( pts );
416
417 if ( force2D )
418 ext->dropZValue();
419
420 if ( force2D )
421 ext->dropMValue();
422
423 return ext.release();
424}
425
426QString QgsQuadrilateral::toString( int pointPrecision ) const
427{
428 QString rep;
429 if ( !isValid() )
430 rep = u"Empty"_s;
431 else
432 rep = u"Quadrilateral (Point 1: %1, Point 2: %2, Point 3: %3, Point 4: %4)"_s
433 .arg( mPoint1.asWkt( pointPrecision ), 0, 's' )
434 .arg( mPoint2.asWkt( pointPrecision ), 0, 's' )
435 .arg( mPoint3.asWkt( pointPrecision ), 0, 's' )
436 .arg( mPoint4.asWkt( pointPrecision ), 0, 's' );
437
438 return rep;
439}
440
442{
443 std::unique_ptr<QgsPolygon> polygon( toPolygon() );
444 return polygon->area();
445}
446
448{
449 std::unique_ptr<QgsPolygon> polygon( toPolygon() );
450 return polygon->perimeter();
451}
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:280
@ Point
Point.
Definition qgis.h:282
@ PointZ
PointZ.
Definition qgis.h:299
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.
Represents a 2D point.
Definition qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
double inclination(const QgsPoint &other) const
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition qgspoint.cpp:716
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition qgspoint.cpp:593
double azimuth(const QgsPoint &other) const
Calculates Cartesian azimuth between this point and other one (clockwise in degree,...
Definition qgspoint.cpp:711
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:493
double z
Definition qgspoint.h:58
double x
Definition qgspoint.h:56
double distance(double x, double y) const
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition qgspoint.h:449
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Definition qgspoint.cpp:623
double m
Definition qgspoint.h:59
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:728
double y
Definition qgspoint.h:57
Polygon geometry type.
Definition qgspolygon.h:37
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.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
Definition qgsvector3d.h:33
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:52
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:54
double x() const
Returns X coordinate.
Definition qgsvector3d.h:50
static QgsVector3D perpendicularPoint(const QgsVector3D &v1, const QgsVector3D &v2, const QgsVector3D &vp)
Returns the perpendicular point of vector vp from [v1 - v2].
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6935
QVector< QgsPoint > QgsPointSequence