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