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