QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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 {
97 QgsVector3D( point1.x(), point1.y(), std::isnan( point1.z() ) ? z : point1.z() ),
98 QgsVector3D( point2.x(), point2.y(), std::isnan( point2.z() ) ? z : point2.z() ),
99 QgsVector3D( point3.x(), point3.y(), std::isnan( point3.z() ) ? z : point3.z() )
100 );
101 const QgsPoint pV3( pType, v3.x(), v3.y(), v3.z() );
102 if ( p3.is3D() )
103 {
104 inclination = pV3.inclination( p3 );
105 distance = p3.distance3D( pV3 );
106 }
107 else
108 distance = p3.distance( pV3 );
109
110 break;
111 }
112 }
113
114 // Final points
115 QgsPoint fp1 = point1;
116 QgsPoint fp2 = point2;
117 QgsPoint fp3 = point2.project( distance, azimuth, inclination );
118 QgsPoint fp4 = point1.project( distance, azimuth, inclination );
119
120 if ( pType != Qgis::WkbType::PointZ )
121 {
122 fp1.dropZValue();
123 fp2.dropZValue();
124 fp3.dropZValue();
125 fp4.dropZValue();
126 }
127
128 if ( !std::isnan( m ) )
129 {
130 fp1.addMValue( m );
131 fp2.addMValue( m );
132 fp3.addMValue( m );
133 fp4.addMValue( m );
134 }
135
136 rect.setPoints( fp1, fp2, fp3, fp4 );
137 return rect;
138}
139
141{
142 if ( QgsPoint( p1.x(), p1.y() ) == QgsPoint( p2.x(), p2.y() ) )
143 return QgsQuadrilateral();
144
145 QgsQuadrilateral quad;
146 const double z = p1.z();
147 const double m = p1.m();
148
149 double xMin = 0, xMax = 0, yMin = 0, yMax = 0;
150
151 if ( p1.x() < p2.x() )
152 {
153 xMin = p1.x();
154 xMax = p2.x();
155 }
156 else
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 yMin = p2.y();
170 yMax = p1.y();
171 }
172
173 quad.setPoints( QgsPoint( p1.wkbType(), xMin, yMin, z, m ), QgsPoint( p1.wkbType(), xMin, yMax, z, m ), QgsPoint( p1.wkbType(), xMax, yMax, z, m ), QgsPoint( p1.wkbType(), xMax, yMin, z, m ) );
174
175 return quad;
176}
177
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(
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 QgsPoint( center.wkbType(), center.x() + xOffset, center.y() - yOffset, center.z(), center.m() )
218 );
219}
220
222{
223 QgsQuadrilateral quad;
224 quad.setPoints(
225 QgsPoint( rectangle.xMinimum(), rectangle.yMinimum() ),
226 QgsPoint( rectangle.xMinimum(), rectangle.yMaximum() ),
227 QgsPoint( rectangle.xMaximum(), rectangle.yMaximum() ),
228 QgsPoint( rectangle.xMaximum(), rectangle.yMinimum() )
229 );
230 return quad;
231}
232
233// Convenient method for comparison
234// TODO: should be have a equals method for QgsPoint allowing tolerance.
235static bool equalPoint( const QgsPoint &p1, const QgsPoint &p2, double epsilon )
236{
237 bool equal = true;
238 equal &= qgsDoubleNear( p1.x(), p2.x(), epsilon );
239 equal &= qgsDoubleNear( p1.y(), p2.y(), epsilon );
240 if ( p1.is3D() || p2.is3D() )
241 equal &= qgsDoubleNear( p1.z(), p2.z(), epsilon ) || ( std::isnan( p1.z() ) && std::isnan( p2.z() ) );
242 if ( p1.isMeasure() || p2.isMeasure() )
243 equal &= qgsDoubleNear( p1.m(), p2.m(), epsilon ) || ( std::isnan( p1.m() ) && std::isnan( p2.m() ) );
244
245 return equal;
246}
247
248bool QgsQuadrilateral::equals( const QgsQuadrilateral &other, double epsilon ) const
249{
250 if ( !( isValid() || other.isValid() ) )
251 {
252 return true;
253 }
254 else if ( !isValid() || !other.isValid() )
255 {
256 return false;
257 }
258 return (
259 ( equalPoint( mPoint1, other.mPoint1, epsilon ) )
260 && ( equalPoint( mPoint2, other.mPoint2, epsilon ) )
261 && ( equalPoint( mPoint3, other.mPoint3, epsilon ) )
262 && ( equalPoint( mPoint4, other.mPoint4, epsilon ) )
263 );
264}
265
267{
268 return equals( other );
269}
270
272{
273 return !operator==( other );
274}
275
276// Returns true if segments are not self-intersected ( [2-3] / [4-1] or [1-2] /
277// [3-4] )
278//
279// p3 p1 p1 p3
280// | \ /| | \ /|
281// | \/ | | \/ |
282// | /\ | or | /\ |
283// | / \| | / \|
284// p2 p4 p2 p4
285
286static bool isNotAntiParallelogram( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
287{
288 QgsPoint inter;
289 bool isIntersection1234 = QgsGeometryUtils::segmentIntersection( p1, p2, p3, p4, inter, isIntersection1234 );
290 bool isIntersection2341 = QgsGeometryUtils::segmentIntersection( p2, p3, p4, p1, inter, isIntersection2341 );
291
292 return !( isIntersection1234 || isIntersection2341 );
293}
294
295static bool isNotCollinear( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
296{
297 const bool isCollinear
298 = ( ( QgsGeometryUtils::segmentSide( p1, p2, p3 ) == 0 ) || ( QgsGeometryUtils::segmentSide( p1, p2, p4 ) == 0 ) || ( QgsGeometryUtils::segmentSide( p1, p3, p4 ) == 0 ) || ( QgsGeometryUtils::segmentSide( p2, p3, p4 ) == 0 ) );
299
300
301 return !isCollinear;
302}
303
304static bool notHaveDoublePoints( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
305{
306 const bool doublePoints = ( ( p1 == p2 ) || ( p1 == p3 ) || ( p1 == p4 ) || ( p2 == p3 ) || ( p2 == p4 ) || ( p3 == p4 ) );
307
308 return !doublePoints;
309}
310
311static bool haveSameType( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
312{
313 const bool sameType = !( ( p1.wkbType() != p2.wkbType() ) || ( p1.wkbType() != p3.wkbType() ) || ( p1.wkbType() != p4.wkbType() ) );
314 return sameType;
315}
316// Convenient method to validate inputs
317static bool validate( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
318{
319 return ( haveSameType( p1, p2, p3, p4 ) && notHaveDoublePoints( p1, p2, p3, p4 ) && isNotAntiParallelogram( p1, p2, p3, p4 ) && isNotCollinear( p1, p2, p3, p4 ) );
320}
321
323{
324 return validate( mPoint1, mPoint2, mPoint3, mPoint4 );
325}
326
327bool QgsQuadrilateral::setPoint( const QgsPoint &newPoint, Point index )
328{
329 switch ( index )
330 {
331 case Point1:
332 if ( validate( newPoint, mPoint2, mPoint3, mPoint4 ) == false )
333 return false;
334 mPoint1 = newPoint;
335 break;
336 case Point2:
337 if ( validate( mPoint1, newPoint, mPoint3, mPoint4 ) == false )
338 return false;
339 mPoint2 = newPoint;
340 break;
341 case Point3:
342 if ( validate( mPoint1, mPoint2, newPoint, mPoint4 ) == false )
343 return false;
344 mPoint3 = newPoint;
345 break;
346 case Point4:
347 if ( validate( mPoint1, mPoint2, mPoint3, newPoint ) == false )
348 return false;
349 mPoint4 = newPoint;
350 break;
351 }
352
353 return true;
354}
355
356bool QgsQuadrilateral::setPoints( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, const QgsPoint &p4 )
357{
358 if ( validate( p1, p2, p3, p4 ) == false )
359 return false;
360
361 mPoint1 = p1;
362 mPoint2 = p2;
363 mPoint3 = p3;
364 mPoint4 = p4;
365
366 return true;
367}
368
370{
372
373 pts << mPoint1 << mPoint2 << mPoint3 << mPoint4 << mPoint1;
374
375 return pts;
376}
377
379{
380 auto polygon = std::make_unique< QgsPolygon >();
381 if ( !isValid() )
382 {
383 return polygon.release();
384 }
385
386 polygon->setExteriorRing( toLineString( force2D ) );
387
388 return polygon.release();
389}
390
392{
393 auto ext = std::make_unique< QgsLineString>();
394 if ( !isValid() )
395 {
396 return ext.release();
397 }
398
400 pts = points();
401
402 ext->setPoints( pts );
403
404 if ( force2D )
405 ext->dropZValue();
406
407 if ( force2D )
408 ext->dropMValue();
409
410 return ext.release();
411}
412
413QString QgsQuadrilateral::toString( int pointPrecision ) const
414{
415 QString rep;
416 if ( !isValid() )
417 rep = u"Empty"_s;
418 else
419 rep = u"Quadrilateral (Point 1: %1, Point 2: %2, Point 3: %3, Point 4: %4)"_s.arg( mPoint1.asWkt( pointPrecision ), 0, 's' )
420 .arg( mPoint2.asWkt( pointPrecision ), 0, 's' )
421 .arg( mPoint3.asWkt( pointPrecision ), 0, 's' )
422 .arg( mPoint4.asWkt( pointPrecision ), 0, 's' );
423
424 return rep;
425}
426
428{
429 std::unique_ptr<QgsPolygon> polygon( toPolygon() );
430 return polygon->area();
431}
432
434{
435 std::unique_ptr<QgsPolygon> polygon( toPolygon() );
436 return polygon->perimeter();
437}
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:294
@ Point
Point.
Definition qgis.h:296
@ PointZ
PointZ.
Definition qgis.h:313
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:722
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition qgspoint.cpp:599
double azimuth(const QgsPoint &other) const
Calculates Cartesian azimuth between this point and other one (clockwise in degree,...
Definition qgspoint.cpp:717
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:510
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:466
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Definition qgspoint.cpp:629
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:734
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:60
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:62
double x() const
Returns X coordinate.
Definition qgsvector3d.h:58
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:6975
QVector< QgsPoint > QgsPointSequence