QGIS API Documentation 3.27.0-Master (1c05421486)
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
19#include "qgis.h"
20
21#include <QObject> // for tr()
22#include <QLocale>
23
24QString 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
43QString 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
63{
64 const QString formattedX = formatX( point.x(), format, precision, flags );
65 const QString formattedY = formatY( point.y(), format, precision, flags );
66
67 switch ( order )
68 {
71 return QStringLiteral( "%1%2%3" ).arg( formattedX, QgsCoordinateFormatter::separator(), formattedY );
72
74 return QStringLiteral( "%1%2%3" ).arg( formattedY, QgsCoordinateFormatter::separator(), formattedX );
75 }
77}
78
79QString QgsCoordinateFormatter::asPair( double x, double y, int precision, Qgis::CoordinateOrder order )
80{
81 const QString formattedX = formatAsPair( x, precision );
82 const QString formattedY = formatAsPair( y, precision );
83
84 switch ( order )
85 {
88 return QStringLiteral( "%1%2%3" ).arg( formattedX, QgsCoordinateFormatter::separator(), formattedY );
89
91 return QStringLiteral( "%1%2%3" ).arg( formattedY, QgsCoordinateFormatter::separator(), formattedX );
92 }
94}
95
97{
98 return QLocale().decimalPoint() == QLatin1Char( ',' ) ? QLatin1Char( ' ' ) : QLatin1Char( ',' );
99}
100
101QString QgsCoordinateFormatter::formatAsPair( double val, int precision )
102{
103 return std::isfinite( val ) ? QLocale().toString( val, 'f', precision ) : QObject::tr( "infinite" );
104}
105
106QString QgsCoordinateFormatter::formatXAsDegreesMinutesSeconds( double val, int precision, FormatFlags flags )
107{
108 //first, limit longitude to -360 to 360 degree range
109 double wrappedX = std::fmod( val, 360.0 );
110 //next, wrap around longitudes > 180 or < -180 degrees, so that eg "190E" -> "170W"
111 if ( wrappedX > 180.0 )
112 {
113 wrappedX = wrappedX - 360.0;
114 }
115 else if ( wrappedX < -180.0 )
116 {
117 wrappedX = wrappedX + 360.0;
118 }
119
120 const int precisionMultiplier = std::pow( 10.0, precision );
121
122 int degreesX = int( std::fabs( wrappedX ) );
123 const double floatMinutesX = ( std::fabs( wrappedX ) - degreesX ) * 60.0;
124 int intMinutesX = int( floatMinutesX );
125 double secondsX = ( floatMinutesX - intMinutesX ) * 60.0;
126
127 //make sure rounding to specified precision doesn't create seconds >= 60
128 if ( std::round( secondsX * precisionMultiplier ) >= 60 * precisionMultiplier )
129 {
130 secondsX = std::max( secondsX - 60, 0.0 );
131 intMinutesX++;
132 if ( intMinutesX >= 60 )
133 {
134 intMinutesX -= 60;
135 degreesX++;
136 }
137 }
138
139 QString hemisphere;
140 QString sign;
141 if ( flags.testFlag( FlagDegreesUseStringSuffix ) )
142 {
143 hemisphere = wrappedX < 0 ? QObject::tr( "W" ) : QObject::tr( "E" );
144 }
145 else
146 {
147 if ( wrappedX < 0 )
148 {
149 sign = QLocale().negativeSign();
150 }
151 }
152 //check if coordinate is all zeros for the specified precision, and if so,
153 //remove the sign and hemisphere strings
154 if ( degreesX == 0 && intMinutesX == 0 && std::round( secondsX * precisionMultiplier ) == 0 )
155 {
156 sign.clear();
157 hemisphere.clear();
158 }
159
160 //also remove directional prefix from 180 degree longitudes
161 if ( degreesX == 180 && intMinutesX == 0 && std::round( secondsX * precisionMultiplier ) == 0 )
162 {
163 hemisphere.clear();
164 }
165
166 QString minutesX;
167 QString strSecondsX;
168
169 //pad with leading digits if required
170 if ( flags.testFlag( FlagDegreesPadMinutesSeconds ) )
171 {
172 minutesX = QString( "%L1" ).arg( intMinutesX, 2, 10, QChar( '0' ) );
173 const int digits = 2 + ( precision == 0 ? 0 : 1 + precision ); //1 for decimal place if required
174 strSecondsX = QString( "%L1" ).arg( secondsX, digits, 'f', precision, QChar( '0' ) );
175 }
176 else
177 {
178 minutesX = QLocale().toString( intMinutesX );
179 strSecondsX = QLocale().toString( secondsX, 'f', precision );
180 }
181
182 return sign + QLocale().toString( degreesX ) + QChar( 176 ) +
183 minutesX + QChar( 0x2032 ) +
184 strSecondsX + QChar( 0x2033 ) +
185 hemisphere;
186}
187
188QString QgsCoordinateFormatter::formatYAsDegreesMinutesSeconds( double val, int precision, FormatFlags flags )
189{
190 //first, limit latitude to -180 to 180 degree range
191 double wrappedY = std::fmod( val, 180.0 );
192 //next, wrap around latitudes > 90 or < -90 degrees, so that eg "110S" -> "70N"
193 if ( wrappedY > 90.0 )
194 {
195 wrappedY = wrappedY - 180.0;
196 }
197 else if ( wrappedY < -90.0 )
198 {
199 wrappedY = wrappedY + 180.0;
200 }
201
202 const int precisionMultiplier = std::pow( 10.0, precision );
203
204 int degreesY = int( std::fabs( wrappedY ) );
205 const double floatMinutesY = ( std::fabs( wrappedY ) - degreesY ) * 60.0;
206 int intMinutesY = int( floatMinutesY );
207 double secondsY = ( floatMinutesY - intMinutesY ) * 60.0;
208
209 //make sure rounding to specified precision doesn't create seconds >= 60
210 if ( std::round( secondsY * precisionMultiplier ) >= 60 * precisionMultiplier )
211 {
212 secondsY = std::max( secondsY - 60, 0.0 );
213 intMinutesY++;
214 if ( intMinutesY >= 60 )
215 {
216 intMinutesY -= 60;
217 degreesY++;
218 }
219 }
220
221 QString hemisphere;
222 QString sign;
223 if ( flags.testFlag( FlagDegreesUseStringSuffix ) )
224 {
225 hemisphere = wrappedY < 0 ? QObject::tr( "S" ) : QObject::tr( "N" );
226 }
227 else
228 {
229 if ( wrappedY < 0 )
230 {
231 sign = QLocale().negativeSign();
232 }
233 }
234 //check if coordinate is all zeros for the specified precision, and if so,
235 //remove the sign and hemisphere strings
236 if ( degreesY == 0 && intMinutesY == 0 && std::round( secondsY * precisionMultiplier ) == 0 )
237 {
238 sign = QString();
239 hemisphere.clear();
240 }
241
242 QString strMinutesY;
243 QString strSecondsY;
244
245 //pad with leading digits if required
246 if ( flags.testFlag( FlagDegreesPadMinutesSeconds ) )
247 {
248 strMinutesY = QString( "%L1" ).arg( intMinutesY, 2, 10, QChar( '0' ) );
249 const int digits = 2 + ( precision == 0 ? 0 : 1 + precision ); //1 for decimal place if required
250 strSecondsY = QString( "%L1" ).arg( secondsY, digits, 'f', precision, QChar( '0' ) );
251 }
252 else
253 {
254 strMinutesY = QLocale().toString( intMinutesY );
255 strSecondsY = QLocale().toString( secondsY, 'f', precision );
256 }
257
258 return sign + QLocale().toString( degreesY ) + QChar( 176 ) +
259 strMinutesY + QChar( 0x2032 ) +
260 strSecondsY + QChar( 0x2033 ) +
261 hemisphere;
262}
263
264QString QgsCoordinateFormatter::formatXAsDegreesMinutes( double val, int precision, FormatFlags flags )
265{
266 //first, limit longitude to -360 to 360 degree range
267 double wrappedX = std::fmod( val, 360.0 );
268 //next, wrap around longitudes > 180 or < -180 degrees, so that eg "190E" -> "170W"
269 if ( wrappedX > 180.0 )
270 {
271 wrappedX = wrappedX - 360.0;
272 }
273 else if ( wrappedX < -180.0 )
274 {
275 wrappedX = wrappedX + 360.0;
276 }
277
278 int degreesX = int( std::fabs( wrappedX ) );
279 double floatMinutesX = ( std::fabs( wrappedX ) - degreesX ) * 60.0;
280
281 const int precisionMultiplier = std::pow( 10.0, precision );
282
283 //make sure rounding to specified precision doesn't create minutes >= 60
284 if ( std::round( floatMinutesX * precisionMultiplier ) >= 60 * precisionMultiplier )
285 {
286 floatMinutesX = std::max( floatMinutesX - 60, 0.0 );
287 degreesX++;
288 }
289
290 QString hemisphere;
291 QString sign;
292 if ( flags.testFlag( FlagDegreesUseStringSuffix ) )
293 {
294 hemisphere = wrappedX < 0 ? QObject::tr( "W" ) : QObject::tr( "E" );
295 }
296 else
297 {
298 if ( wrappedX < 0 )
299 {
300 sign = QLocale().negativeSign();
301 }
302 }
303 //check if coordinate is all zeros for the specified precision, and if so,
304 //remove the sign and hemisphere strings
305 if ( degreesX == 0 && std::round( floatMinutesX * precisionMultiplier ) == 0 )
306 {
307 sign.clear();
308 hemisphere.clear();
309 }
310
311 //also remove directional prefix from 180 degree longitudes
312 if ( degreesX == 180 && std::round( floatMinutesX * precisionMultiplier ) == 0 )
313 {
314 hemisphere.clear();
315 }
316
317 //pad minutes with leading digits if required
318 const int digits = 2 + ( precision == 0 ? 0 : 1 + precision ); //1 for decimal place if required
319 const QString strMinutesX = flags.testFlag( FlagDegreesPadMinutesSeconds ) ? QString( "%1" ).arg( floatMinutesX, digits, 'f', precision, QChar( '0' ) )
320 : QLocale().toString( floatMinutesX, 'f', precision );
321
322 return sign + QLocale().toString( degreesX ) + QChar( 176 ) +
323 strMinutesX + QChar( 0x2032 ) +
324 hemisphere;
325}
326
327QString QgsCoordinateFormatter::formatYAsDegreesMinutes( double val, int precision, FormatFlags flags )
328{
329 //first, limit latitude to -180 to 180 degree range
330 double wrappedY = std::fmod( val, 180.0 );
331 //next, wrap around latitudes > 90 or < -90 degrees, so that eg "110S" -> "70N"
332 if ( wrappedY > 90.0 )
333 {
334 wrappedY = wrappedY - 180.0;
335 }
336 else if ( wrappedY < -90.0 )
337 {
338 wrappedY = wrappedY + 180.0;
339 }
340
341 int degreesY = int( std::fabs( wrappedY ) );
342 double floatMinutesY = ( std::fabs( wrappedY ) - degreesY ) * 60.0;
343
344 const int precisionMultiplier = std::pow( 10.0, precision );
345
346 //make sure rounding to specified precision doesn't create minutes >= 60
347 if ( std::round( floatMinutesY * precisionMultiplier ) >= 60 * precisionMultiplier )
348 {
349 floatMinutesY = std::max( floatMinutesY - 60, 0.0 );
350 degreesY++;
351 }
352
353 QString hemisphere;
354 QString sign;
355 if ( flags.testFlag( FlagDegreesUseStringSuffix ) )
356 {
357 hemisphere = wrappedY < 0 ? QObject::tr( "S" ) : QObject::tr( "N" );
358 }
359 else
360 {
361 if ( wrappedY < 0 )
362 {
363 sign = QLocale().negativeSign();
364 }
365 }
366 //check if coordinate is all zeros for the specified precision, and if so,
367 //remove the sign and hemisphere strings
368 if ( degreesY == 0 && std::round( floatMinutesY * precisionMultiplier ) == 0 )
369 {
370 sign.clear();
371 hemisphere.clear();
372 }
373
374
375 //pad minutes with leading digits if required
376 const int digits = 2 + ( precision == 0 ? 0 : 1 + precision ); //1 for decimal place if required
377 const QString strMinutesY = flags.testFlag( FlagDegreesPadMinutesSeconds ) ? QString( "%1" ).arg( floatMinutesY, digits, 'f', precision, QChar( '0' ) )
378 : QLocale().toString( floatMinutesY, 'f', precision );
379
380 return sign + QLocale().toString( degreesY ) + QChar( 176 ) +
381 strMinutesY + QChar( 0x2032 ) +
382 hemisphere;
383}
384
385QString QgsCoordinateFormatter::formatXAsDegrees( double val, int precision, FormatFlags flags )
386{
387 //first, limit longitude to -360 to 360 degree range
388 double wrappedX = std::fmod( val, 360.0 );
389 //next, wrap around longitudes > 180 or < -180 degrees, so that eg "190E" -> "170W"
390 if ( wrappedX > 180.0 )
391 {
392 wrappedX = wrappedX - 360.0;
393 }
394 else if ( wrappedX < -180.0 )
395 {
396 wrappedX = wrappedX + 360.0;
397 }
398
399 const double absX = std::fabs( wrappedX );
400
401 const int precisionMultiplier = std::pow( 10.0, precision );
402
403 QString hemisphere;
404 QString sign;
405 if ( flags.testFlag( FlagDegreesUseStringSuffix ) )
406 {
407 hemisphere = wrappedX < 0 ? QObject::tr( "W" ) : QObject::tr( "E" );
408 }
409 else
410 {
411 if ( wrappedX < 0 )
412 {
413 sign = QLocale().negativeSign();
414 }
415 }
416 //check if coordinate is all zeros for the specified precision, and if so,
417 //remove the sign and hemisphere strings
418 if ( std::round( absX * precisionMultiplier ) == 0 )
419 {
420 sign.clear();
421 hemisphere.clear();
422 }
423
424 //also remove directional prefix from 180 degree longitudes
425 if ( std::round( absX * precisionMultiplier ) == 180 * precisionMultiplier )
426 {
427 sign.clear();
428 hemisphere.clear();
429 }
430
431 return sign + QLocale().toString( absX, 'f', precision ) + QChar( 176 ) + hemisphere;
432}
433
434QString QgsCoordinateFormatter::formatYAsDegrees( double val, int precision, FormatFlags flags )
435{
436 //first, limit latitude to -180 to 180 degree range
437 double wrappedY = std::fmod( val, 180.0 );
438 //next, wrap around latitudes > 90 or < -90 degrees, so that eg "110S" -> "70N"
439 if ( wrappedY > 90.0 )
440 {
441 wrappedY = wrappedY - 180.0;
442 }
443 else if ( wrappedY < -90.0 )
444 {
445 wrappedY = wrappedY + 180.0;
446 }
447
448 const double absY = std::fabs( wrappedY );
449
450 const int precisionMultiplier = std::pow( 10.0, precision );
451
452 QString hemisphere;
453 QString sign;
454 if ( flags.testFlag( FlagDegreesUseStringSuffix ) )
455 {
456 hemisphere = wrappedY < 0 ? QObject::tr( "S" ) : QObject::tr( "N" );
457 }
458 else
459 {
460 if ( wrappedY < 0 )
461 {
462 sign = QLocale().negativeSign();
463 }
464 }
465 //check if coordinate is all zeros for the specified precision, and if so,
466 //remove the sign and hemisphere strings
467 if ( std::round( absY * precisionMultiplier ) == 0 )
468 {
469 sign.clear();
470 hemisphere.clear();
471 }
472
473 return sign + QLocale().toString( absY, 'f', precision ) + QChar( 176 ) + hemisphere;
474}
CoordinateOrder
Order of coordinates.
Definition: qgis.h:1147
@ XY
Easting/Northing (or Longitude/Latitude for geographic CRS)
@ Default
Respect the default axis ordering for the CRS, as defined in the CRS's parameters.
@ YX
Northing/Easting (or Latitude/Longitude for geographic CRS)
static QString asPair(double x, double y, int precision=12, Qgis::CoordinateOrder order=Qgis::CoordinateOrder::XY)
Formats coordinates as an "\a x,\a y" pair, with optional decimal precision (number of decimal places...
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 format(const QgsPointXY &point, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix, Qgis::CoordinateOrder order=Qgis::CoordinateOrder::XY)
Formats a point according to the specified parameters.
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 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
#define BUILTIN_UNREACHABLE
Definition: qgis.h:3128
int precision