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