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