Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgspoint.h - description
3 : : -------------------
4 : : begin : Sat Jun 22 2002
5 : : copyright : (C) 2002 by Gary E.Sherman
6 : : email : sherman at mrcc.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 : : #ifndef QGSPOINTXY_H
19 : : #define QGSPOINTXY_H
20 : :
21 : : #include "qgis_core.h"
22 : : #include "qgsvector.h"
23 : :
24 : : #include "qgis.h"
25 : :
26 : : #include <iostream>
27 : : #include <QString>
28 : : #include <QPoint>
29 : : #include <QObject>
30 : :
31 : : class QgsPoint;
32 : :
33 : : /**
34 : : * \ingroup core
35 : : * \brief A class to represent a 2D point.
36 : : *
37 : : * A QgsPointXY represents a position with X and Y coordinates.
38 : : * In most scenarios it is preferable to use a QgsPoint instead which also
39 : : * supports Z and M values.
40 : : *
41 : : * \since QGIS 3.0
42 : : */
43 : : class CORE_EXPORT QgsPointXY
44 : : {
45 : : Q_GADGET
46 : :
47 : : Q_PROPERTY( double x READ x WRITE setX )
48 : : Q_PROPERTY( double y READ y WRITE setY )
49 : :
50 : : public:
51 : : /// Default constructor
52 : 323 : QgsPointXY() = default;
53 : :
54 : : //! Create a point from another point
55 : : QgsPointXY( const QgsPointXY &p ) SIP_HOLDGIL;
56 : :
57 : : /**
58 : : * Create a point from x,y coordinates
59 : : * \param x x coordinate
60 : : * \param y y coordinate
61 : : */
62 : 71662 : QgsPointXY( double x, double y ) SIP_HOLDGIL
63 : 71662 : : mX( x )
64 : 71662 : , mY( y )
65 : 71662 : , mIsEmpty( false )
66 : 71662 : {}
67 : :
68 : : /**
69 : : * Create a point from a QPointF
70 : : * \param point QPointF source
71 : : * \since QGIS 2.7
72 : : */
73 : 4 : QgsPointXY( QPointF point ) SIP_HOLDGIL
74 : 4 : : mX( point.x() )
75 : 4 : , mY( point.y() )
76 : 4 : , mIsEmpty( false )
77 : 4 : {}
78 : :
79 : : /**
80 : : * Create a point from a QPoint
81 : : * \param point QPoint source
82 : : * \since QGIS 2.7
83 : : */
84 : : QgsPointXY( QPoint point ) SIP_HOLDGIL
85 : : : mX( point.x() )
86 : : , mY( point.y() )
87 : : , mIsEmpty( false )
88 : : {}
89 : :
90 : : /**
91 : : * Create a new point.
92 : : * Z and M values will be dropped.
93 : : *
94 : : * \since QGIS 3.0
95 : : */
96 : : QgsPointXY( const QgsPoint &point ) SIP_HOLDGIL;
97 : :
98 : : // IMPORTANT - while QgsPointXY is inherited by QgsReferencedPointXY, we do NOT want a virtual destructor here
99 : : // because this class MUST be lightweight and we don't want the cost of the vtable here.
100 : : // see https://github.com/qgis/QGIS/pull/4720#issuecomment-308652392
101 : : ~QgsPointXY() = default;
102 : :
103 : : /**
104 : : * Sets the x value of the point
105 : : * \param x x coordinate
106 : : */
107 : 0 : void setX( double x ) SIP_HOLDGIL
108 : : {
109 : 0 : mX = x;
110 : 0 : mIsEmpty = false;
111 : 0 : }
112 : :
113 : : /**
114 : : * Sets the y value of the point
115 : : * \param y y coordinate
116 : : */
117 : 0 : void setY( double y ) SIP_HOLDGIL
118 : : {
119 : 0 : mY = y;
120 : 0 : mIsEmpty = false;
121 : 0 : }
122 : :
123 : : //! Sets the x and y value of the point
124 : 1 : void set( double x, double y ) SIP_HOLDGIL
125 : : {
126 : 1 : mX = x;
127 : 1 : mY = y;
128 : 1 : mIsEmpty = false;
129 : 1 : }
130 : :
131 : : /**
132 : : * Gets the x value of the point
133 : : * \returns x coordinate
134 : : */
135 : 124016 : double x() const SIP_HOLDGIL
136 : : {
137 : 124016 : return mX;
138 : : }
139 : :
140 : : /**
141 : : * Gets the y value of the point
142 : : * \returns y coordinate
143 : : */
144 : 63758 : double y() const SIP_HOLDGIL
145 : : {
146 : 63758 : return mY;
147 : : }
148 : :
149 : : /**
150 : : * Converts a point to a QPointF
151 : : * \returns QPointF with same x and y values
152 : : * \since QGIS 2.7
153 : : */
154 : 2 : QPointF toQPointF() const
155 : : {
156 : 2 : return QPointF( mX, mY );
157 : : }
158 : :
159 : : /**
160 : : * Returns a string representation of the point (x, y) with a preset \a precision.
161 : : * If \a precision is -1, then a default precision will be used.
162 : : */
163 : : QString toString( int precision = -1 ) const;
164 : :
165 : : /**
166 : : * Returns the well known text representation for the point (e.g. "POINT(x y)").
167 : : * The wkt is created without an SRID.
168 : : */
169 : : QString asWkt() const;
170 : :
171 : : /**
172 : : * Returns the squared distance between this point a specified x, y coordinate.
173 : : * \see distance()
174 : : */
175 : 204 : double sqrDist( double x, double y ) const SIP_HOLDGIL
176 : : {
177 : 204 : return ( mX - x ) * ( mX - x ) + ( mY - y ) * ( mY - y );
178 : : }
179 : :
180 : : /**
181 : : * Returns the squared distance between this point another point.
182 : : * \see distance()
183 : : */
184 : 204 : double sqrDist( const QgsPointXY &other ) const SIP_HOLDGIL
185 : : {
186 : 204 : return sqrDist( other.x(), other.y() );
187 : : }
188 : :
189 : : /**
190 : : * Returns the distance between this point and a specified x, y coordinate.
191 : : * \param x x-coordniate
192 : : * \param y y-coordinate
193 : : * \see sqrDist()
194 : : * \since QGIS 2.16
195 : : */
196 : 0 : double distance( double x, double y ) const SIP_HOLDGIL
197 : : {
198 : 0 : return std::sqrt( sqrDist( x, y ) );
199 : : }
200 : :
201 : : /**
202 : : * Returns the distance between this point and another point.
203 : : * \param other other point
204 : : * \see sqrDist()
205 : : * \since QGIS 2.16
206 : : */
207 : 26 : double distance( const QgsPointXY &other ) const SIP_HOLDGIL
208 : : {
209 : 26 : return std::sqrt( sqrDist( other ) );
210 : : }
211 : :
212 : : //! Returns the minimum distance between this point and a segment
213 : : double sqrDistToSegment( double x1, double y1, double x2, double y2, QgsPointXY &minDistPoint SIP_OUT, double epsilon = DEFAULT_SEGMENT_EPSILON ) const SIP_HOLDGIL;
214 : :
215 : : //! Calculates azimuth between this point and other one (clockwise in degree, starting from north)
216 : : double azimuth( const QgsPointXY &other ) const SIP_HOLDGIL;
217 : :
218 : : /**
219 : : * Returns a new point which corresponds to this point projected by a specified distance
220 : : * in a specified bearing.
221 : : * \param distance distance to project
222 : : * \param bearing angle to project in, clockwise in degrees starting from north
223 : : * \since QGIS 2.16
224 : : */
225 : : QgsPointXY project( double distance, double bearing ) const SIP_HOLDGIL;
226 : :
227 : : /**
228 : : * Returns TRUE if the geometry is empty.
229 : : * Unlike QgsPoint, this class is also used to retrieve graphical coordinates like QPointF.
230 : : * It therefore has the default coordinates (0.0).
231 : : * A QgsPointXY is considered empty, when the coordinates have not been explicitly filled in.
232 : : * \since QGIS 3.10
233 : : */
234 : 32205 : bool isEmpty() const SIP_HOLDGIL { return mIsEmpty; }
235 : :
236 : : /**
237 : : * Compares this point with another point with a fuzzy tolerance
238 : : * \param other point to compare with
239 : : * \param epsilon maximum difference for coordinates between the points
240 : : * \returns TRUE if points are equal within specified tolerance
241 : : * \since QGIS 2.9
242 : : */
243 : 94 : bool compare( const QgsPointXY &other, double epsilon = 4 * std::numeric_limits<double>::epsilon() ) const SIP_HOLDGIL
244 : : {
245 : 94 : return ( qgsDoubleNear( mX, other.x(), epsilon ) && qgsDoubleNear( mY, other.y(), epsilon ) );
246 : : }
247 : :
248 : : //! equality operator
249 : 1 : bool operator==( const QgsPointXY &other ) SIP_HOLDGIL
250 : : {
251 : 1 : if ( isEmpty() && other.isEmpty() )
252 : 0 : return true;
253 : 1 : if ( isEmpty() && !other.isEmpty() )
254 : 0 : return false;
255 : 1 : if ( ! isEmpty() && other.isEmpty() )
256 : 0 : return false;
257 : :
258 : 1 : bool equal = true;
259 : 1 : equal &= qgsDoubleNear( other.x(), mX, 1E-8 );
260 : 1 : equal &= qgsDoubleNear( other.y(), mY, 1E-8 );
261 : :
262 : 1 : return equal;
263 : 1 : }
264 : :
265 : : //! Inequality operator
266 : 0 : bool operator!=( const QgsPointXY &other ) const SIP_HOLDGIL
267 : : {
268 : 0 : if ( isEmpty() && other.isEmpty() )
269 : 0 : return false;
270 : 0 : if ( isEmpty() && !other.isEmpty() )
271 : 0 : return true;
272 : 0 : if ( ! isEmpty() && other.isEmpty() )
273 : 0 : return true;
274 : :
275 : 0 : bool equal = true;
276 : 0 : equal &= qgsDoubleNear( other.x(), mX, 1E-8 );
277 : 0 : equal &= qgsDoubleNear( other.y(), mY, 1E-8 );
278 : :
279 : 0 : return !equal;
280 : 0 : }
281 : :
282 : : //! Multiply x and y by the given value
283 : : void multiply( double scalar ) SIP_HOLDGIL
284 : : {
285 : : mX *= scalar;
286 : : mY *= scalar;
287 : : }
288 : :
289 : : //! Assignment
290 : 246 : QgsPointXY &operator=( const QgsPointXY &other ) SIP_HOLDGIL
291 : : {
292 : 246 : if ( &other != this )
293 : : {
294 : 246 : mX = other.x();
295 : 246 : mY = other.y();
296 : 246 : mIsEmpty = other.isEmpty();
297 : 246 : }
298 : :
299 : 246 : return *this;
300 : : }
301 : :
302 : : //! Calculates the vector obtained by subtracting a point from this point
303 : 0 : QgsVector operator-( const QgsPointXY &p ) const { return QgsVector( mX - p.mX, mY - p.mY ); }
304 : :
305 : : //! Adds a vector to this point in place
306 : 0 : QgsPointXY &operator+=( QgsVector v ) { *this = *this + v; return *this; }
307 : :
308 : : //! Subtracts a vector from this point in place
309 : : QgsPointXY &operator-=( QgsVector v ) { *this = *this - v; return *this; }
310 : :
311 : : //! Adds a vector to this point
312 : 24 : QgsPointXY operator+( QgsVector v ) const { return QgsPointXY( mX + v.x(), mY + v.y() ); }
313 : :
314 : : //! Subtracts a vector from this point
315 : : QgsPointXY operator-( QgsVector v ) const { return QgsPointXY( mX - v.x(), mY - v.y() ); }
316 : :
317 : : //! Multiplies the coordinates in this point by a scalar quantity
318 : : QgsPointXY operator*( double scalar ) const { return QgsPointXY( mX * scalar, mY * scalar ); }
319 : :
320 : : //! Divides the coordinates in this point by a scalar quantity
321 : : QgsPointXY operator/( double scalar ) const { return QgsPointXY( mX / scalar, mY / scalar ); }
322 : :
323 : : //! Multiplies the coordinates in this point by a scalar quantity in place
324 : : QgsPointXY &operator*=( double scalar ) { mX *= scalar; mY *= scalar; return *this; }
325 : :
326 : : //! Divides the coordinates in this point by a scalar quantity in place
327 : 0 : QgsPointXY &operator/=( double scalar ) { mX /= scalar; mY /= scalar; return *this; }
328 : :
329 : : //! Allows direct construction of QVariants from points.
330 : : operator QVariant() const
331 : : {
332 : : return QVariant::fromValue( *this );
333 : : }
334 : :
335 : : #ifdef SIP_RUN
336 : : SIP_PYOBJECT __repr__();
337 : : % MethodCode
338 : : QString str = QStringLiteral( "<QgsPointXY: %1>" ).arg( sipCpp->asWkt() );
339 : : sipRes = PyUnicode_FromString( str.toUtf8().constData() );
340 : : % End
341 : :
342 : : int __len__();
343 : : % MethodCode
344 : : sipRes = 2;
345 : : % End
346 : :
347 : :
348 : : SIP_PYOBJECT __getitem__( int );
349 : : % MethodCode
350 : : if ( a0 == 0 )
351 : : {
352 : : sipRes = Py_BuildValue( "d", sipCpp->x() );
353 : : }
354 : : else if ( a0 == 1 )
355 : : {
356 : : sipRes = Py_BuildValue( "d", sipCpp->y() );
357 : : }
358 : : else
359 : : {
360 : : QString msg = QString( "Bad index: %1" ).arg( a0 );
361 : : PyErr_SetString( PyExc_IndexError, msg.toLatin1().constData() );
362 : : }
363 : : % End
364 : :
365 : : long __hash__() const;
366 : : % MethodCode
367 : : sipRes = qHash( *sipCpp );
368 : : % End
369 : : #endif
370 : :
371 : : private:
372 : :
373 : : //! x coordinate
374 : 323 : double mX = 0; //std::numeric_limits<double>::quiet_NaN();
375 : :
376 : : //! y coordinate
377 : 323 : double mY = 0; //std::numeric_limits<double>::quiet_NaN();
378 : :
379 : : //! is point empty?
380 : 323 : bool mIsEmpty = true;
381 : :
382 : : friend uint qHash( const QgsPointXY &pnt );
383 : :
384 : : }; // class QgsPointXY
385 : :
386 : 0 : Q_DECLARE_METATYPE( QgsPointXY )
387 : :
388 : 117 : inline bool operator==( const QgsPointXY &p1, const QgsPointXY &p2 ) SIP_SKIP
389 : : {
390 : 117 : const bool nan1X = std::isnan( p1.x() );
391 : 117 : const bool nan2X = std::isnan( p2.x() );
392 : 117 : if ( nan1X != nan2X )
393 : 0 : return false;
394 : 117 : if ( !nan1X && !qgsDoubleNear( p1.x(), p2.x(), 1E-8 ) )
395 : 2 : return false;
396 : :
397 : 115 : const bool nan1Y = std::isnan( p1.y() );
398 : 115 : const bool nan2Y = std::isnan( p2.y() );
399 : 115 : if ( nan1Y != nan2Y )
400 : 0 : return false;
401 : :
402 : 115 : if ( !nan1Y && !qgsDoubleNear( p1.y(), p2.y(), 1E-8 ) )
403 : 0 : return false;
404 : :
405 : 115 : return true;
406 : 117 : }
407 : :
408 : : inline std::ostream &operator << ( std::ostream &os, const QgsPointXY &p ) SIP_SKIP
409 : : {
410 : : // Use Local8Bit for printouts
411 : : os << p.toString().toLocal8Bit().data();
412 : : return os;
413 : : }
414 : :
415 : 0 : inline uint qHash( const QgsPointXY &p ) SIP_SKIP
416 : : {
417 : : uint hash;
418 : 0 : uint h1 = qHash( static_cast< quint64 >( p.mX ) );
419 : 0 : uint h2 = qHash( static_cast< quint64 >( p.mY ) );
420 : 0 : hash = h1 ^ ( h2 << 1 );
421 : 0 : return hash;
422 : : }
423 : :
424 : :
425 : : #endif //QGSPOINTXY_H
|