QGIS API Documentation  2.99.0-Master (0a63d1f)
qgspoint.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspoint.cpp - 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 
19 #include "qgspoint.h"
20 #include "qgis.h"
21 #include <cmath>
22 #include <QTextStream>
23 #include <QObject> // for tr()
24 
25 #include "qgsexception.h"
26 
27 //
28 // QgsVector
29 //
30 
32  : mX( 0.0 )
33  , mY( 0.0 )
34 {
35 }
36 
37 QgsVector::QgsVector( double x, double y )
38  : mX( x )
39  , mY( y )
40 {
41 }
42 
44 {
45  return QgsVector( -mX, -mY );
46 }
47 
48 QgsVector QgsVector::operator*( double scalar ) const
49 {
50  return QgsVector( mX * scalar, mY * scalar );
51 }
52 
53 QgsVector QgsVector::operator/( double scalar ) const
54 {
55  return *this * ( 1.0 / scalar );
56 }
57 
59 {
60  return mX * v.mX + mY * v.mY;
61 }
62 
64 {
65  return QgsVector( mX + other.mX, mY + other.mY );
66 }
67 
69 {
70  mX += other.mX;
71  mY += other.mY;
72  return *this;
73 }
74 
76 {
77  return QgsVector( mX - other.mX, mY - other.mY );
78 }
79 
81 {
82  mX -= other.mX;
83  mY -= other.mY;
84  return *this;
85 }
86 
87 double QgsVector::length() const
88 {
89  return sqrt( mX * mX + mY * mY );
90 }
91 
92 double QgsVector::x() const
93 {
94  return mX;
95 }
96 
97 double QgsVector::y() const
98 {
99  return mY;
100 }
101 
103 {
104  return QgsVector( -mY, mX );
105 }
106 
107 double QgsVector::angle() const
108 {
109  double theAngle = atan2( mY, mX );
110  return theAngle < 0.0 ? theAngle + 2.0 * M_PI : theAngle;
111 }
112 
113 double QgsVector::angle( QgsVector v ) const
114 {
115  return v.angle() - angle();
116 }
117 
118 QgsVector QgsVector::rotateBy( double rot ) const
119 {
120  double theAngle = atan2( mY, mX ) + rot;
121  double len = length();
122  return QgsVector( len * cos( theAngle ), len * sin( theAngle ) );
123 }
124 
126 {
127  double len = length();
128 
129  if ( len == 0.0 )
130  {
131  throw QgsException( QStringLiteral( "normalized vector of null vector undefined" ) );
132  }
133 
134  return *this / len;
135 }
136 
137 bool QgsVector::operator==( QgsVector other ) const
138 {
139  return qgsDoubleNear( mX, other.mX ) && qgsDoubleNear( mY, other.mY );
140 }
141 
142 bool QgsVector::operator!=( QgsVector other ) const
143 {
144  return !qgsDoubleNear( mX, other.mX ) || !qgsDoubleNear( mY, other.mY );
145 }
146 
147 
148 //
149 // QgsPoint
150 //
151 
153 {
154  m_x = p.x();
155  m_y = p.y();
156 }
157 
158 QPointF QgsPoint::toQPointF() const
159 {
160  return QPointF( m_x, m_y );
161 }
162 
163 QString QgsPoint::toString() const
164 {
165  QString rep;
166  QTextStream ot( &rep );
167  ot.setRealNumberPrecision( 12 );
168  ot << m_x << ", " << m_y;
169  return rep;
170 }
171 
172 QString QgsPoint::toString( int thePrecision ) const
173 {
174  QString x = qIsFinite( m_x ) ? QString::number( m_x, 'f', thePrecision ) : QObject::tr( "infinite" );
175  QString y = qIsFinite( m_y ) ? QString::number( m_y, 'f', thePrecision ) : QObject::tr( "infinite" );
176  return QStringLiteral( "%1,%2" ).arg( x, y );
177 }
178 
179 QString QgsPoint::toDegreesMinutesSeconds( int thePrecision, const bool useSuffix, const bool padded ) const
180 {
181  //first, limit longitude to -360 to 360 degree range
182  double myWrappedX = fmod( m_x, 360.0 );
183  //next, wrap around longitudes > 180 or < -180 degrees, so that, e.g., "190E" -> "170W"
184  if ( myWrappedX > 180.0 )
185  {
186  myWrappedX = myWrappedX - 360.0;
187  }
188  else if ( myWrappedX < -180.0 )
189  {
190  myWrappedX = myWrappedX + 360.0;
191  }
192 
193  //first, limit latitude to -180 to 180 degree range
194  double myWrappedY = fmod( m_y, 180.0 );
195  //next, wrap around latitudes > 90 or < -90 degrees, so that, e.g., "110S" -> "70N"
196  if ( myWrappedY > 90.0 )
197  {
198  myWrappedY = myWrappedY - 180.0;
199  }
200  else if ( myWrappedY < -90.0 )
201  {
202  myWrappedY = myWrappedY + 180.0;
203  }
204 
205  int myDegreesX = int( qAbs( myWrappedX ) );
206  double myFloatMinutesX = double(( qAbs( myWrappedX ) - myDegreesX ) * 60 );
207  int myIntMinutesX = int( myFloatMinutesX );
208  double mySecondsX = double( myFloatMinutesX - myIntMinutesX ) * 60;
209 
210  int myDegreesY = int( qAbs( myWrappedY ) );
211  double myFloatMinutesY = double(( qAbs( myWrappedY ) - myDegreesY ) * 60 );
212  int myIntMinutesY = int( myFloatMinutesY );
213  double mySecondsY = double( myFloatMinutesY - myIntMinutesY ) * 60;
214 
215  //make sure rounding to specified precision doesn't create seconds >= 60
216  if ( qRound( mySecondsX * pow( 10.0, thePrecision ) ) >= 60 * pow( 10.0, thePrecision ) )
217  {
218  mySecondsX = qMax( mySecondsX - 60, 0.0 );
219  myIntMinutesX++;
220  if ( myIntMinutesX >= 60 )
221  {
222  myIntMinutesX -= 60;
223  myDegreesX++;
224  }
225  }
226  if ( qRound( mySecondsY * pow( 10.0, thePrecision ) ) >= 60 * pow( 10.0, thePrecision ) )
227  {
228  mySecondsY = qMax( mySecondsY - 60, 0.0 );
229  myIntMinutesY++;
230  if ( myIntMinutesY >= 60 )
231  {
232  myIntMinutesY -= 60;
233  myDegreesY++;
234  }
235  }
236 
237  QString myXHemisphere;
238  QString myYHemisphere;
239  QString myXSign;
240  QString myYSign;
241  if ( useSuffix )
242  {
243  myXHemisphere = myWrappedX < 0 ? QObject::tr( "W" ) : QObject::tr( "E" );
244  myYHemisphere = myWrappedY < 0 ? QObject::tr( "S" ) : QObject::tr( "N" );
245  }
246  else
247  {
248  if ( myWrappedX < 0 )
249  {
250  myXSign = QObject::tr( "-" );
251  }
252  if ( myWrappedY < 0 )
253  {
254  myYSign = QObject::tr( "-" );
255  }
256  }
257  //check if coordinate is all zeros for the specified precision, and if so,
258  //remove the sign and hemisphere strings
259  if ( myDegreesX == 0 && myIntMinutesX == 0 && qRound( mySecondsX * pow( 10.0, thePrecision ) ) == 0 )
260  {
261  myXSign = QString();
262  myXHemisphere = QString();
263  }
264  if ( myDegreesY == 0 && myIntMinutesY == 0 && qRound( mySecondsY * pow( 10.0, thePrecision ) ) == 0 )
265  {
266  myYSign = QString();
267  myYHemisphere = QString();
268  }
269  //also remove directional prefix from 180 degree longitudes
270  if ( myDegreesX == 180 && myIntMinutesX == 0 && qRound( mySecondsX * pow( 10.0, thePrecision ) ) == 0 )
271  {
272  myXHemisphere = QString();
273  }
274  //pad minutes with leading digits if required
275  QString myMinutesX = padded ? QStringLiteral( "%1" ).arg( myIntMinutesX, 2, 10, QChar( '0' ) ) : QString::number( myIntMinutesX );
276  QString myMinutesY = padded ? QStringLiteral( "%1" ).arg( myIntMinutesY, 2, 10, QChar( '0' ) ) : QString::number( myIntMinutesY );
277  //pad seconds with leading digits if required
278  int digits = 2 + ( thePrecision == 0 ? 0 : 1 + thePrecision ); //1 for decimal place if required
279  QString myStrSecondsX = padded ? QStringLiteral( "%1" ).arg( mySecondsX, digits, 'f', thePrecision, QChar( '0' ) ) : QString::number( mySecondsX, 'f', thePrecision );
280  QString myStrSecondsY = padded ? QStringLiteral( "%1" ).arg( mySecondsY, digits, 'f', thePrecision, QChar( '0' ) ) : QString::number( mySecondsY, 'f', thePrecision );
281 
282  QString rep = myXSign + QString::number( myDegreesX ) + QChar( 176 ) +
283  myMinutesX + QChar( 0x2032 ) +
284  myStrSecondsX + QChar( 0x2033 ) +
285  myXHemisphere + ',' +
286  myYSign + QString::number( myDegreesY ) + QChar( 176 ) +
287  myMinutesY + QChar( 0x2032 ) +
288  myStrSecondsY + QChar( 0x2033 ) +
289  myYHemisphere;
290  return rep;
291 }
292 
293 QString QgsPoint::toDegreesMinutes( int thePrecision, const bool useSuffix, const bool padded ) const
294 {
295  //first, limit longitude to -360 to 360 degree range
296  double myWrappedX = fmod( m_x, 360.0 );
297  //next, wrap around longitudes > 180 or < -180 degrees, so that, e.g., "190E" -> "170W"
298  if ( myWrappedX > 180.0 )
299  {
300  myWrappedX = myWrappedX - 360.0;
301  }
302  else if ( myWrappedX < -180.0 )
303  {
304  myWrappedX = myWrappedX + 360.0;
305  }
306 
307  int myDegreesX = int( qAbs( myWrappedX ) );
308  double myFloatMinutesX = double(( qAbs( myWrappedX ) - myDegreesX ) * 60 );
309 
310  int myDegreesY = int( qAbs( m_y ) );
311  double myFloatMinutesY = double(( qAbs( m_y ) - myDegreesY ) * 60 );
312 
313  //make sure rounding to specified precision doesn't create minutes >= 60
314  if ( qRound( myFloatMinutesX * pow( 10.0, thePrecision ) ) >= 60 * pow( 10.0, thePrecision ) )
315  {
316  myFloatMinutesX = qMax( myFloatMinutesX - 60, 0.0 );
317  myDegreesX++;
318  }
319  if ( qRound( myFloatMinutesY * pow( 10.0, thePrecision ) ) >= 60 * pow( 10.0, thePrecision ) )
320  {
321  myFloatMinutesY = qMax( myFloatMinutesY - 60, 0.0 );
322  myDegreesY++;
323  }
324 
325  QString myXHemisphere;
326  QString myYHemisphere;
327  QString myXSign;
328  QString myYSign;
329  if ( useSuffix )
330  {
331  myXHemisphere = myWrappedX < 0 ? QObject::tr( "W" ) : QObject::tr( "E" );
332  myYHemisphere = m_y < 0 ? QObject::tr( "S" ) : QObject::tr( "N" );
333  }
334  else
335  {
336  if ( myWrappedX < 0 )
337  {
338  myXSign = QObject::tr( "-" );
339  }
340  if ( m_y < 0 )
341  {
342  myYSign = QObject::tr( "-" );
343  }
344  }
345  //check if coordinate is all zeros for the specified precision, and if so,
346  //remove the sign and hemisphere strings
347  if ( myDegreesX == 0 && qRound( myFloatMinutesX * pow( 10.0, thePrecision ) ) == 0 )
348  {
349  myXSign = QString();
350  myXHemisphere = QString();
351  }
352  if ( myDegreesY == 0 && qRound( myFloatMinutesY * pow( 10.0, thePrecision ) ) == 0 )
353  {
354  myYSign = QString();
355  myYHemisphere = QString();
356  }
357  //also remove directional prefix from 180 degree longitudes
358  if ( myDegreesX == 180 && qRound( myFloatMinutesX * pow( 10.0, thePrecision ) ) == 0 )
359  {
360  myXHemisphere = QString();
361  }
362 
363  //pad minutes with leading digits if required
364  int digits = 2 + ( thePrecision == 0 ? 0 : 1 + thePrecision ); //1 for decimal place if required
365  QString myStrMinutesX = padded ? QStringLiteral( "%1" ).arg( myFloatMinutesX, digits, 'f', thePrecision, QChar( '0' ) ) : QString::number( myFloatMinutesX, 'f', thePrecision );
366  QString myStrMinutesY = padded ? QStringLiteral( "%1" ).arg( myFloatMinutesY, digits, 'f', thePrecision, QChar( '0' ) ) : QString::number( myFloatMinutesY, 'f', thePrecision );
367 
368  QString rep = myXSign + QString::number( myDegreesX ) + QChar( 176 ) +
369  myStrMinutesX + QChar( 0x2032 ) +
370  myXHemisphere + ',' +
371  myYSign + QString::number( myDegreesY ) + QChar( 176 ) +
372  myStrMinutesY + QChar( 0x2032 ) +
373  myYHemisphere;
374  return rep;
375 }
376 
377 QString QgsPoint::wellKnownText() const
378 {
379  return QStringLiteral( "POINT(%1 %2)" ).arg( qgsDoubleToString( m_x ), qgsDoubleToString( m_y ) );
380 }
381 
382 double QgsPoint::sqrDist( double x, double y ) const
383 {
384  return ( m_x - x ) * ( m_x - x ) + ( m_y - y ) * ( m_y - y );
385 }
386 
387 double QgsPoint::sqrDist( const QgsPoint& other ) const
388 {
389  return sqrDist( other.x(), other.y() );
390 }
391 
392 double QgsPoint::distance( double x, double y ) const
393 {
394  return sqrt( sqrDist( x, y ) );
395 }
396 
397 double QgsPoint::distance( const QgsPoint& other ) const
398 {
399  return sqrt( sqrDist( other ) );
400 }
401 
402 double QgsPoint::azimuth( const QgsPoint& other ) const
403 {
404  double dx = other.x() - m_x;
405  double dy = other.y() - m_y;
406  return ( atan2( dx, dy ) * 180.0 / M_PI );
407 }
408 
409 QgsPoint QgsPoint::project( double distance, double bearing ) const
410 {
411  double rads = bearing * M_PI / 180.0;
412  double dx = distance * sin( rads );
413  double dy = distance * cos( rads );
414  return QgsPoint( m_x + dx, m_y + dy );
415 }
416 
417 bool QgsPoint::compare( const QgsPoint &other, double epsilon ) const
418 {
419  return ( qgsDoubleNear( m_x, other.x(), epsilon ) && qgsDoubleNear( m_y, other.y(), epsilon ) );
420 }
421 
422 // operators
423 bool QgsPoint::operator==( const QgsPoint & other )
424 {
425  if ( qgsDoubleNear( m_x, other.x() ) && qgsDoubleNear( m_y, other.y() ) )
426  return true;
427  else
428  return false;
429 }
430 
431 bool QgsPoint::operator!=( const QgsPoint & other ) const
432 {
433  if ( qgsDoubleNear( m_x, other.x() ) && qgsDoubleNear( m_y, other.y() ) )
434  return false;
435  else
436  return true;
437 }
438 
440 {
441  if ( &other != this )
442  {
443  m_x = other.x();
444  m_y = other.y();
445  }
446 
447  return *this;
448 }
449 
450 void QgsPoint::multiply( double scalar )
451 {
452  m_x *= scalar;
453  m_y *= scalar;
454 }
455 
456 int QgsPoint::onSegment( const QgsPoint& a, const QgsPoint& b ) const
457 {
458  //algorithm from 'graphics GEMS', A. Paeth: 'A Fast 2D Point-on-line test'
459  if (
460  qAbs(( b.y() - a.y() ) *( m_x - a.x() ) - ( m_y - a.y() ) *( b.x() - a.x() ) )
461  >= qMax( qAbs( b.x() - a.x() ), qAbs( b.y() - a.y() ) )
462  )
463  {
464  return 0;
465  }
466  if (( b.x() < a.x() && a.x() < m_x ) || ( b.y() < a.y() && a.y() < m_y ) )
467  {
468  return 1;
469  }
470  if (( m_x < a.x() && a.x() < b.x() ) || ( m_y < a.y() && a.y() < b.y() ) )
471  {
472  return 1;
473  }
474  if (( a.x() < b.x() && b.x() < m_x ) || ( a.y() < b.y() && b.y() < m_y ) )
475  {
476  return 3;
477  }
478  if (( m_x < b.x() && b.x() < a.x() ) || ( m_y < b.y() && b.y() < a.y() ) )
479  {
480  return 3;
481  }
482 
483  return 2;
484 }
485 
486 double QgsPoint::sqrDistToSegment( double x1, double y1, double x2, double y2, QgsPoint& minDistPoint, double epsilon ) const
487 {
488  double nx, ny; //normal vector
489 
490  nx = y2 - y1;
491  ny = -( x2 - x1 );
492 
493  double t;
494  t = ( m_x * ny - m_y * nx - x1 * ny + y1 * nx ) / (( x2 - x1 ) * ny - ( y2 - y1 ) * nx );
495 
496  if ( t < 0.0 )
497  {
498  minDistPoint.setX( x1 );
499  minDistPoint.setY( y1 );
500  }
501  else if ( t > 1.0 )
502  {
503  minDistPoint.setX( x2 );
504  minDistPoint.setY( y2 );
505  }
506  else
507  {
508  minDistPoint.setX( x1 + t *( x2 - x1 ) );
509  minDistPoint.setY( y1 + t *( y2 - y1 ) );
510  }
511 
512  double dist = sqrDist( minDistPoint );
513  //prevent rounding errors if the point is directly on the segment
514  if ( qgsDoubleNear( dist, 0.0, epsilon ) )
515  {
516  minDistPoint.setX( m_x );
517  minDistPoint.setY( m_y );
518  return 0.0;
519  }
520  return dist;
521 }
QgsVector operator+(QgsVector other) const
Adds another vector to this vector.
Definition: qgspoint.cpp:63
double y
Definition: qgspoint.h:148
QgsVector()
Default constructor for QgsVector.
Definition: qgspoint.cpp:31
bool operator!=(QgsVector other) const
Inequality operator.
Definition: qgspoint.cpp:142
QgsVector & operator+=(QgsVector other)
Adds another vector to this vector in place.
Definition: qgspoint.cpp:68
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspoint.cpp:158
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Definition: qgspoint.cpp:392
void multiply(double scalar)
Multiply x and y by the given value.
Definition: qgspoint.cpp:450
double azimuth(const QgsPoint &other) const
Calculates azimuth between this point and other one (clockwise in degree, starting from north) ...
Definition: qgspoint.cpp:402
QgsPoint & operator=(const QgsPoint &other)
Assignment.
Definition: qgspoint.cpp:439
QString toDegreesMinutes(int thePrecision, const bool useSuffix=true, const bool padded=false) const
Return a string representation as degrees minutes.
Definition: qgspoint.cpp:293
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:193
QgsPoint()
Default constructor.
Definition: qgspoint.h:152
double sqrDistToSegment(double x1, double y1, double x2, double y2, QgsPoint &minDistPoint, double epsilon=DEFAULT_SEGMENT_EPSILON) const
Returns the minimum distance between this point and a segment.
Definition: qgspoint.cpp:486
double angle(QgsVector v) const
Returns the angle between this vector and another vector in radians.
Definition: qgspoint.cpp:113
bool compare(const QgsPoint &other, double epsilon=4 *DBL_EPSILON) const
Compares this point with another point with a fuzzy tolerance.
Definition: qgspoint.cpp:417
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:181
#define M_PI
bool operator==(const QgsPoint &other)
equality operator
Definition: qgspoint.cpp:423
QString toDegreesMinutesSeconds(int thePrecision, const bool useSuffix=true, const bool padded=false) const
Return a string representation as degrees minutes seconds.
Definition: qgspoint.cpp:179
A class to represent a point.
Definition: qgspoint.h:143
QgsVector operator/(double scalar) const
Returns a vector where the components have been divided by a scalar value.
Definition: qgspoint.cpp:53
QString toString() const
String representation of the point (x,y)
Definition: qgspoint.cpp:163
double length() const
Returns the length of the vector.
Definition: qgspoint.cpp:87
A class to represent a vector.
Definition: qgspoint.h:33
void setX(double x)
Sets the x value of the point.
Definition: qgspoint.h:193
void setY(double y)
Sets the y value of the point.
Definition: qgspoint.h:201
double angle() const
Returns the angle of the vector in radians.
Definition: qgspoint.cpp:107
QString wellKnownText() const
Return the well known text representation for the point.
Definition: qgspoint.cpp:377
QgsVector perpVector() const
Returns the perpendicular vector to this vector (rotated 90 degrees counter-clockwise) ...
Definition: qgspoint.cpp:102
QgsVector operator*(double scalar) const
Returns a vector where the components have been multiplied by a scalar value.
Definition: qgspoint.cpp:48
QgsVector & operator-=(QgsVector other)
Subtracts another vector to this vector in place.
Definition: qgspoint.cpp:80
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
Definition: qgspoint.cpp:382
QgsVector rotateBy(double rot) const
Rotates the vector by a specified angle.
Definition: qgspoint.cpp:118
double x() const
Returns the vector&#39;s x-component.
Definition: qgspoint.cpp:92
QgsVector operator-() const
Swaps the sign of the x and y components of the vector.
Definition: qgspoint.cpp:43
QgsPoint project(double distance, double bearing) const
Returns a new point which corresponds to this point projected by a specified distance in a specified ...
Definition: qgspoint.cpp:409
double y() const
Returns the vector&#39;s y-component.
Definition: qgspoint.cpp:97
Defines a qgis exception class.
Definition: qgsexception.h:27
bool operator==(QgsVector other) const
Equality operator.
Definition: qgspoint.cpp:137
bool operator!=(const QgsPoint &other) const
Inequality operator.
Definition: qgspoint.cpp:431
QgsVector normalized() const
Returns the vector&#39;s normalized (or "unit") vector (ie same angle but length of 1.0).
Definition: qgspoint.cpp:125
int onSegment(const QgsPoint &a, const QgsPoint &b) const
Test if this point is on the segment defined by points a, b.
Definition: qgspoint.cpp:456
double x
Definition: qgspoint.h:147