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