QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgscoordinateformatter.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscoordinateformatter.cpp
3  --------------------------
4  begin : Decemeber 2015
5  copyright : (C) 2015 by Nyall Dawson
6  email : nyall dot dawson at gmail 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 "qgscoordinateformatter.h"
19 #include "qgis.h"
20 
21 #include <QObject> // for tr()
22 
23 QString QgsCoordinateFormatter::formatX( double x, QgsCoordinateFormatter::Format format, int precision, FormatFlags flags )
24 {
25  switch ( format )
26  {
27  case FormatPair:
28  return formatAsPair( x, precision );
29 
31  return formatXAsDegreesMinutesSeconds( x, precision, flags );
32 
34  return formatXAsDegreesMinutes( x, precision, flags );
35 
37  return formatXAsDegrees( x, precision, flags );
38  }
39  return QString(); //avoid warnings
40 }
41 
42 QString QgsCoordinateFormatter::formatY( double y, QgsCoordinateFormatter::Format format, int precision, FormatFlags flags )
43 {
44  switch ( format )
45  {
46  case FormatPair:
47  return formatAsPair( y, precision );
48 
50  return formatYAsDegreesMinutesSeconds( y, precision, flags );
51 
53  return formatYAsDegreesMinutes( y, precision, flags );
54 
56  return formatYAsDegrees( y, precision, flags );
57  }
58  return QString(); //avoid warnings
59 }
60 
61 QString QgsCoordinateFormatter::format( const QgsPointXY &point, QgsCoordinateFormatter::Format format, int precision, FormatFlags flags )
62 {
63  return QStringLiteral( "%1,%2" ).arg( formatX( point.x(), format, precision, flags ),
64  formatY( point.y(), format, precision, flags ) );
65 }
66 
67 QString QgsCoordinateFormatter::asPair( double x, double y, int precision )
68 {
69  QString s = formatAsPair( x, precision );
70  s += ',';
71  s += formatAsPair( y, precision );
72  return s;
73 }
74 
75 QString QgsCoordinateFormatter::formatAsPair( double val, int precision )
76 {
77  return std::isfinite( val ) ? QString::number( val, 'f', precision ) : QObject::tr( "infinite" );
78 }
79 
80 QString QgsCoordinateFormatter::formatXAsDegreesMinutesSeconds( double val, int precision, FormatFlags flags )
81 {
82  //first, limit longitude to -360 to 360 degree range
83  double wrappedX = std::fmod( val, 360.0 );
84  //next, wrap around longitudes > 180 or < -180 degrees, so that eg "190E" -> "170W"
85  if ( wrappedX > 180.0 )
86  {
87  wrappedX = wrappedX - 360.0;
88  }
89  else if ( wrappedX < -180.0 )
90  {
91  wrappedX = wrappedX + 360.0;
92  }
93 
94  int precisionMultiplier = std::pow( 10.0, precision );
95 
96  int degreesX = int( std::fabs( wrappedX ) );
97  double floatMinutesX = ( std::fabs( wrappedX ) - degreesX ) * 60.0;
98  int intMinutesX = int( floatMinutesX );
99  double secondsX = ( floatMinutesX - intMinutesX ) * 60.0;
100 
101  //make sure rounding to specified precision doesn't create seconds >= 60
102  if ( std::round( secondsX * precisionMultiplier ) >= 60 * precisionMultiplier )
103  {
104  secondsX = std::max( secondsX - 60, 0.0 );
105  intMinutesX++;
106  if ( intMinutesX >= 60 )
107  {
108  intMinutesX -= 60;
109  degreesX++;
110  }
111  }
112 
113  QString hemisphere;
114  QString sign;
115  if ( flags.testFlag( FlagDegreesUseStringSuffix ) )
116  {
117  hemisphere = wrappedX < 0 ? QObject::tr( "W" ) : QObject::tr( "E" );
118  }
119  else
120  {
121  if ( wrappedX < 0 )
122  {
123  sign = QObject::tr( "-" );
124  }
125  }
126  //check if coordinate is all zeros for the specified precision, and if so,
127  //remove the sign and hemisphere strings
128  if ( degreesX == 0 && intMinutesX == 0 && std::round( secondsX * precisionMultiplier ) == 0 )
129  {
130  sign.clear();
131  hemisphere.clear();
132  }
133 
134  //also remove directional prefix from 180 degree longitudes
135  if ( degreesX == 180 && intMinutesX == 0 && std::round( secondsX * precisionMultiplier ) == 0 )
136  {
137  hemisphere.clear();
138  }
139 
140  QString minutesX;
141  QString strSecondsX;
142 
143  //pad with leading digits if required
144  if ( flags.testFlag( FlagDegreesPadMinutesSeconds ) )
145  {
146  minutesX = QString( "%1" ).arg( intMinutesX, 2, 10, QChar( '0' ) );
147  int digits = 2 + ( precision == 0 ? 0 : 1 + precision ); //1 for decimal place if required
148  strSecondsX = QString( "%1" ).arg( secondsX, digits, 'f', precision, QChar( '0' ) );
149  }
150  else
151  {
152  minutesX = QString::number( intMinutesX );
153  strSecondsX = QString::number( secondsX, 'f', precision );
154  }
155 
156  return sign + QString::number( degreesX ) + QChar( 176 ) +
157  minutesX + QChar( 0x2032 ) +
158  strSecondsX + QChar( 0x2033 ) +
159  hemisphere;
160 }
161 
162 QString QgsCoordinateFormatter::formatYAsDegreesMinutesSeconds( double val, int precision, FormatFlags flags )
163 {
164  //first, limit latitude to -180 to 180 degree range
165  double wrappedY = std::fmod( val, 180.0 );
166  //next, wrap around latitudes > 90 or < -90 degrees, so that eg "110S" -> "70N"
167  if ( wrappedY > 90.0 )
168  {
169  wrappedY = wrappedY - 180.0;
170  }
171  else if ( wrappedY < -90.0 )
172  {
173  wrappedY = wrappedY + 180.0;
174  }
175 
176  int precisionMultiplier = std::pow( 10.0, precision );
177 
178  int degreesY = int( std::fabs( wrappedY ) );
179  double floatMinutesY = ( std::fabs( wrappedY ) - degreesY ) * 60.0;
180  int intMinutesY = int( floatMinutesY );
181  double secondsY = ( floatMinutesY - intMinutesY ) * 60.0;
182 
183  //make sure rounding to specified precision doesn't create seconds >= 60
184  if ( std::round( secondsY * precisionMultiplier ) >= 60 * precisionMultiplier )
185  {
186  secondsY = std::max( secondsY - 60, 0.0 );
187  intMinutesY++;
188  if ( intMinutesY >= 60 )
189  {
190  intMinutesY -= 60;
191  degreesY++;
192  }
193  }
194 
195  QString hemisphere;
196  QString sign;
197  if ( flags.testFlag( FlagDegreesUseStringSuffix ) )
198  {
199  hemisphere = wrappedY < 0 ? QObject::tr( "S" ) : QObject::tr( "N" );
200  }
201  else
202  {
203  if ( wrappedY < 0 )
204  {
205  sign = QObject::tr( "-" );
206  }
207  }
208  //check if coordinate is all zeros for the specified precision, and if so,
209  //remove the sign and hemisphere strings
210  if ( degreesY == 0 && intMinutesY == 0 && std::round( secondsY * precisionMultiplier ) == 0 )
211  {
212  sign = QString();
213  hemisphere.clear();
214  }
215 
216  QString strMinutesY;
217  QString strSecondsY;
218 
219  //pad with leading digits if required
220  if ( flags.testFlag( FlagDegreesPadMinutesSeconds ) )
221  {
222  strMinutesY = QString( "%1" ).arg( intMinutesY, 2, 10, QChar( '0' ) );
223  int digits = 2 + ( precision == 0 ? 0 : 1 + precision ); //1 for decimal place if required
224  strSecondsY = QString( "%1" ).arg( secondsY, digits, 'f', precision, QChar( '0' ) );
225  }
226  else
227  {
228  strMinutesY = QString::number( intMinutesY );
229  strSecondsY = QString::number( secondsY, 'f', precision );
230  }
231 
232  return sign + QString::number( degreesY ) + QChar( 176 ) +
233  strMinutesY + QChar( 0x2032 ) +
234  strSecondsY + QChar( 0x2033 ) +
235  hemisphere;
236 }
237 
238 QString QgsCoordinateFormatter::formatXAsDegreesMinutes( double val, int precision, FormatFlags flags )
239 {
240  //first, limit longitude to -360 to 360 degree range
241  double wrappedX = std::fmod( val, 360.0 );
242  //next, wrap around longitudes > 180 or < -180 degrees, so that eg "190E" -> "170W"
243  if ( wrappedX > 180.0 )
244  {
245  wrappedX = wrappedX - 360.0;
246  }
247  else if ( wrappedX < -180.0 )
248  {
249  wrappedX = wrappedX + 360.0;
250  }
251 
252  int degreesX = int( std::fabs( wrappedX ) );
253  double floatMinutesX = ( std::fabs( wrappedX ) - degreesX ) * 60.0;
254 
255  int precisionMultiplier = std::pow( 10.0, precision );
256 
257  //make sure rounding to specified precision doesn't create minutes >= 60
258  if ( std::round( floatMinutesX * precisionMultiplier ) >= 60 * precisionMultiplier )
259  {
260  floatMinutesX = std::max( floatMinutesX - 60, 0.0 );
261  degreesX++;
262  }
263 
264  QString hemisphere;
265  QString sign;
266  if ( flags.testFlag( FlagDegreesUseStringSuffix ) )
267  {
268  hemisphere = wrappedX < 0 ? QObject::tr( "W" ) : QObject::tr( "E" );
269  }
270  else
271  {
272  if ( wrappedX < 0 )
273  {
274  sign = QObject::tr( "-" );
275  }
276  }
277  //check if coordinate is all zeros for the specified precision, and if so,
278  //remove the sign and hemisphere strings
279  if ( degreesX == 0 && std::round( floatMinutesX * precisionMultiplier ) == 0 )
280  {
281  sign.clear();
282  hemisphere.clear();
283  }
284 
285  //also remove directional prefix from 180 degree longitudes
286  if ( degreesX == 180 && std::round( floatMinutesX * precisionMultiplier ) == 0 )
287  {
288  hemisphere.clear();
289  }
290 
291  //pad minutes with leading digits if required
292  int digits = 2 + ( precision == 0 ? 0 : 1 + precision ); //1 for decimal place if required
293  QString strMinutesX = flags.testFlag( FlagDegreesPadMinutesSeconds ) ? QString( "%1" ).arg( floatMinutesX, digits, 'f', precision, QChar( '0' ) )
294  : QString::number( floatMinutesX, 'f', precision );
295 
296  return sign + QString::number( degreesX ) + QChar( 176 ) +
297  strMinutesX + QChar( 0x2032 ) +
298  hemisphere;
299 }
300 
301 QString QgsCoordinateFormatter::formatYAsDegreesMinutes( double val, int precision, FormatFlags flags )
302 {
303  //first, limit latitude to -180 to 180 degree range
304  double wrappedY = std::fmod( val, 180.0 );
305  //next, wrap around latitudes > 90 or < -90 degrees, so that eg "110S" -> "70N"
306  if ( wrappedY > 90.0 )
307  {
308  wrappedY = wrappedY - 180.0;
309  }
310  else if ( wrappedY < -90.0 )
311  {
312  wrappedY = wrappedY + 180.0;
313  }
314 
315  int degreesY = int( std::fabs( wrappedY ) );
316  double floatMinutesY = ( std::fabs( wrappedY ) - degreesY ) * 60.0;
317 
318  int precisionMultiplier = std::pow( 10.0, precision );
319 
320  //make sure rounding to specified precision doesn't create minutes >= 60
321  if ( std::round( floatMinutesY * precisionMultiplier ) >= 60 * precisionMultiplier )
322  {
323  floatMinutesY = std::max( floatMinutesY - 60, 0.0 );
324  degreesY++;
325  }
326 
327  QString hemisphere;
328  QString sign;
329  if ( flags.testFlag( FlagDegreesUseStringSuffix ) )
330  {
331  hemisphere = wrappedY < 0 ? QObject::tr( "S" ) : QObject::tr( "N" );
332  }
333  else
334  {
335  if ( wrappedY < 0 )
336  {
337  sign = QObject::tr( "-" );
338  }
339  }
340  //check if coordinate is all zeros for the specified precision, and if so,
341  //remove the sign and hemisphere strings
342  if ( degreesY == 0 && std::round( floatMinutesY * precisionMultiplier ) == 0 )
343  {
344  sign.clear();
345  hemisphere.clear();
346  }
347 
348 
349  //pad minutes with leading digits if required
350  int digits = 2 + ( precision == 0 ? 0 : 1 + precision ); //1 for decimal place if required
351  QString strMinutesY = flags.testFlag( FlagDegreesPadMinutesSeconds ) ? QString( "%1" ).arg( floatMinutesY, digits, 'f', precision, QChar( '0' ) )
352  : QString::number( floatMinutesY, 'f', precision );
353 
354  return sign + QString::number( degreesY ) + QChar( 176 ) +
355  strMinutesY + QChar( 0x2032 ) +
356  hemisphere;
357 }
358 
359 QString QgsCoordinateFormatter::formatXAsDegrees( double val, int precision, FormatFlags flags )
360 {
361  //first, limit longitude to -360 to 360 degree range
362  double wrappedX = std::fmod( val, 360.0 );
363  //next, wrap around longitudes > 180 or < -180 degrees, so that eg "190E" -> "170W"
364  if ( wrappedX > 180.0 )
365  {
366  wrappedX = wrappedX - 360.0;
367  }
368  else if ( wrappedX < -180.0 )
369  {
370  wrappedX = wrappedX + 360.0;
371  }
372 
373  double absX = std::fabs( wrappedX );
374 
375  int precisionMultiplier = std::pow( 10.0, precision );
376 
377  QString hemisphere;
378  QString sign;
379  if ( flags.testFlag( FlagDegreesUseStringSuffix ) )
380  {
381  hemisphere = wrappedX < 0 ? QObject::tr( "W" ) : QObject::tr( "E" );
382  }
383  else
384  {
385  if ( wrappedX < 0 )
386  {
387  sign = QObject::tr( "-" );
388  }
389  }
390  //check if coordinate is all zeros for the specified precision, and if so,
391  //remove the sign and hemisphere strings
392  if ( std::round( absX * precisionMultiplier ) == 0 )
393  {
394  sign.clear();
395  hemisphere.clear();
396  }
397 
398  //also remove directional prefix from 180 degree longitudes
399  if ( std::round( absX * precisionMultiplier ) == 180 * precisionMultiplier )
400  {
401  sign.clear();
402  hemisphere.clear();
403  }
404 
405  return sign + QString::number( absX, 'f', precision ) + QChar( 176 ) + hemisphere;
406 }
407 
408 QString QgsCoordinateFormatter::formatYAsDegrees( double val, int precision, FormatFlags flags )
409 {
410  //first, limit latitude to -180 to 180 degree range
411  double wrappedY = std::fmod( val, 180.0 );
412  //next, wrap around latitudes > 90 or < -90 degrees, so that eg "110S" -> "70N"
413  if ( wrappedY > 90.0 )
414  {
415  wrappedY = wrappedY - 180.0;
416  }
417  else if ( wrappedY < -90.0 )
418  {
419  wrappedY = wrappedY + 180.0;
420  }
421 
422  double absY = std::fabs( wrappedY );
423 
424  int precisionMultiplier = std::pow( 10.0, precision );
425 
426  QString hemisphere;
427  QString sign;
428  if ( flags.testFlag( FlagDegreesUseStringSuffix ) )
429  {
430  hemisphere = wrappedY < 0 ? QObject::tr( "S" ) : QObject::tr( "N" );
431  }
432  else
433  {
434  if ( wrappedY < 0 )
435  {
436  sign = QObject::tr( "-" );
437  }
438  }
439  //check if coordinate is all zeros for the specified precision, and if so,
440  //remove the sign and hemisphere strings
441  if ( std::round( absY * precisionMultiplier ) == 0 )
442  {
443  sign.clear();
444  hemisphere.clear();
445  }
446 
447  return sign + QString::number( absY, 'f', precision ) + QChar( 176 ) + hemisphere;
448 }
QgsCoordinateFormatter::FormatDecimalDegrees
@ FormatDecimalDegrees
Decimal degrees, eg 30.7555 degrees.
Definition: qgscoordinateformatter.h:52
QgsPointXY::y
double y
Definition: qgspointxy.h:48
QgsPointXY::x
Q_GADGET double x
Definition: qgspointxy.h:47
qgis.h
QgsCoordinateFormatter::FormatDegreesMinutes
@ FormatDegreesMinutes
Degrees and decimal minutes, eg 30degrees 45.55'.
Definition: qgscoordinateformatter.h:51
precision
int precision
Definition: qgswfsgetfeature.cpp:49
QgsCoordinateFormatter::asPair
static QString asPair(double x, double y, int precision=12)
Formats coordinates as an "\a x,\a y" pair, with optional decimal precision (number of decimal places...
Definition: qgscoordinateformatter.cpp:67
QgsCoordinateFormatter::formatY
static QString formatY(double y, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a y coordinate value according to the specified parameters.
Definition: qgscoordinateformatter.cpp:42
QgsCoordinateFormatter::format
static QString format(const QgsPointXY &point, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a point according to the specified parameters.
Definition: qgscoordinateformatter.cpp:61
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:44
QgsCoordinateFormatter::FormatPair
@ FormatPair
Formats coordinates as an "x,y" pair.
Definition: qgscoordinateformatter.h:49
QgsCoordinateFormatter::FormatDegreesMinutesSeconds
@ FormatDegreesMinutesSeconds
Degrees, minutes and seconds, eg 30 degrees 45'30".
Definition: qgscoordinateformatter.h:50
QgsCoordinateFormatter::FlagDegreesUseStringSuffix
@ FlagDegreesUseStringSuffix
Include a direction suffix (eg 'N', 'E', 'S' or 'W'), otherwise a "-" prefix is used for west and sou...
Definition: qgscoordinateformatter.h:60
QgsCoordinateFormatter::Format
Format
Available formats for displaying coordinates.
Definition: qgscoordinateformatter.h:48
QgsCoordinateFormatter::FlagDegreesPadMinutesSeconds
@ FlagDegreesPadMinutesSeconds
Pad minute and second values with leading zeros, eg '05' instead of '5'.
Definition: qgscoordinateformatter.h:61
QgsCoordinateFormatter::formatX
static QString formatX(double x, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats an x coordinate value according to the specified parameters.
Definition: qgscoordinateformatter.cpp:23
qgscoordinateformatter.h