1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtPositioning module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39#include "qgeocoordinate.h"
40#include "qgeocoordinate_p.h"
41#include "qlocationutils_p.h"
42
43#include <QDateTime>
44#include <QHash>
45#include <QDataStream>
46#include <QDebug>
47#include <QMetaType>
48#include <qnumeric.h>
49#include <qmath.h>
50
51QT_BEGIN_NAMESPACE
52
53
54struct CoordinateStreamOperators
55{
56 CoordinateStreamOperators()
57 {
58#ifndef QT_NO_DATASTREAM
59 qRegisterMetaTypeStreamOperators<QGeoCoordinate>();
60#endif
61#ifndef QT_NO_DEBUG_STREAM
62 QMetaType::registerDebugStreamOperator<QGeoCoordinate>();
63#endif
64 }
65};
66Q_GLOBAL_STATIC(CoordinateStreamOperators, initStreamOperators);
67
68
69static const double qgeocoordinate_EARTH_MEAN_RADIUS = 6371.0072;
70
71
72QGeoCoordinatePrivate::QGeoCoordinatePrivate():
73 lat(qQNaN()),
74 lng(qQNaN()),
75 alt(qQNaN())
76{}
77
78QGeoCoordinatePrivate::QGeoCoordinatePrivate(const QGeoCoordinatePrivate &other)
79 : QSharedData(other),
80 lat(other.lat),
81 lng(other.lng),
82 alt(other.alt)
83{}
84
85QGeoCoordinatePrivate::~QGeoCoordinatePrivate()
86{}
87
88
89QGeoMercatorCoordinatePrivate::QGeoMercatorCoordinatePrivate():
90 QGeoCoordinatePrivate(),
91 m_mercatorX(qQNaN()),
92 m_mercatorY(qQNaN())
93{}
94
95QGeoMercatorCoordinatePrivate::QGeoMercatorCoordinatePrivate(const QGeoMercatorCoordinatePrivate &other)
96 : QGeoCoordinatePrivate(other),
97 m_mercatorX(other.m_mercatorX),
98 m_mercatorY(other.m_mercatorY)
99{}
100
101QGeoMercatorCoordinatePrivate::~QGeoMercatorCoordinatePrivate()
102{}
103
104/*!
105 \class QGeoCoordinate
106 \inmodule QtPositioning
107 \ingroup QtPositioning-positioning
108 \since 5.2
109
110 \brief The QGeoCoordinate class defines a geographical position on the surface of the Earth.
111
112 A QGeoCoordinate is defined by latitude, longitude, and optionally, altitude.
113
114 Use type() to determine whether a coordinate is a 2D coordinate (has
115 latitude and longitude only) or 3D coordinate (has latitude, longitude
116 and altitude). Use distanceTo() and azimuthTo() to calculate the distance
117 and bearing between coordinates.
118
119 The coordinate values should be specified using the WGS84 datum. For more information
120 on geographical terms see this article on \l {http://en.wikipedia.org/wiki/Geographic_coordinate_system}{coordinates} and
121 another on \l {http://en.wikipedia.org/wiki/Geodetic_system}{geodetic systems}
122 including WGS84.
123
124 Azimuth in this context is equivalent to a compass bearing based on true north.
125
126 This class is a \l Q_GADGET since Qt 5.5. It can be
127 \l{Cpp_value_integration_positioning}{directly used from C++ and QML}.
128*/
129
130/*!
131 \enum QGeoCoordinate::CoordinateType
132 Defines the types of a coordinate.
133
134 \value InvalidCoordinate An invalid coordinate. A coordinate is invalid if its latitude or longitude values are invalid.
135 \value Coordinate2D A coordinate with valid latitude and longitude values.
136 \value Coordinate3D A coordinate with valid latitude and longitude values, and also an altitude value.
137*/
138
139/*!
140 \enum QGeoCoordinate::CoordinateFormat
141 Defines the possible formatting options for toString().
142
143 \value Degrees Returns a string representation of the coordinates in decimal degrees format.
144 \value DegreesWithHemisphere Returns a string representation of the coordinates in decimal degrees format, using 'N', 'S', 'E' or 'W' to indicate the hemispheres of the coordinates.
145 \value DegreesMinutes Returns a string representation of the coordinates in degrees-minutes format.
146 \value DegreesMinutesWithHemisphere Returns a string representation of the coordinates in degrees-minutes format, using 'N', 'S', 'E' or 'W' to indicate the hemispheres of the coordinates.
147 \value DegreesMinutesSeconds Returns a string representation of the coordinates in degrees-minutes-seconds format.
148 \value DegreesMinutesSecondsWithHemisphere Returns a string representation of the coordinates in degrees-minutes-seconds format, using 'N', 'S', 'E' or 'W' to indicate the hemispheres of the coordinates.
149
150 \sa toString()
151*/
152
153/*!
154 \property QGeoCoordinate::latitude
155 \brief This property holds the latitude in decimal degrees.
156
157 The property is undefined (\l {qQNaN()}) if the latitude has not been set.
158 A positive latitude indicates the Northern Hemisphere, and a negative
159 latitude indicates the Southern Hemisphere. When setting the latitude the
160 new value should be in the
161 \l {http://en.wikipedia.org/wiki/World_Geodetic_System}{WGS84} datum format.
162
163 To be valid, the latitude must be between -90 to 90 inclusive.
164
165 While this property is introduced in Qt 5.5, the related accessor functions
166 exist since the first version of this class.
167
168 \since 5.5
169*/
170
171/*!
172 \property QGeoCoordinate::longitude
173 \brief This property holds the longitude in decimal degrees.
174
175 The property is undefined (\l {qQNaN()}) if the longitude has not been set.
176 A positive longitude indicates the Eastern Hemisphere, and a negative
177 longitude indicates the Western Hemisphere. When setting the longitude the
178 new value should be in the
179 \l {http://en.wikipedia.org/wiki/World_Geodetic_System}{WGS84} datum format.
180
181 To be valid, the longitude must be between -180 to 180 inclusive.
182
183 While this property is introduced in Qt 5.5, the related accessor functions
184 exist since the first version of this class.
185
186 \since 5.5
187*/
188
189/*!
190 \property QGeoCoordinate::altitude
191 \brief This property holds the altitude in meters above sea level.
192
193 The property is undefined (\l {qQNaN()}) if the altitude has not been set.
194
195 While this property is introduced in Qt 5.5, the related accessor functions
196 exist since the first version of this class.
197
198 \since 5.5
199*/
200
201/*!
202 \property QGeoCoordinate::isValid
203 \brief This property holds the validity of this geo coordinate.
204
205 The geo coordinate is valid if the \l [CPP]{longitude} and \l [CPP]{latitude}
206 properties have been set to valid values.
207
208 While this property is introduced in Qt 5.5, the related accessor functions
209 exist since the first version of this class.
210
211 \since 5.5
212*/
213
214/*!
215 Constructs a coordinate. The coordinate will be invalid until
216 setLatitude() and setLongitude() have been called.
217*/
218QGeoCoordinate::QGeoCoordinate()
219 : d(new QGeoCoordinatePrivate)
220{
221#ifndef QT_NO_DATASTREAM
222 initStreamOperators();
223#endif
224}
225
226/*!
227 Constructs a coordinate with the given \a latitude and \a longitude.
228
229 If the latitude is not between -90 to 90 inclusive, or the longitude
230 is not between -180 to 180 inclusive, none of the values are set and
231 the type() will be QGeoCoordinate::InvalidCoordinate.
232
233 \sa isValid()
234*/
235QGeoCoordinate::QGeoCoordinate(double latitude, double longitude)
236 : d(new QGeoCoordinatePrivate)
237{
238#ifndef QT_NO_DATASTREAM
239 initStreamOperators();
240#endif
241
242 if (QLocationUtils::isValidLat(lat: latitude) && QLocationUtils::isValidLong(lng: longitude)) {
243 d->lat = latitude;
244 d->lng = longitude;
245 }
246}
247
248/*!
249 Constructs a coordinate with the given \a latitude, \a longitude
250 and \a altitude.
251
252 If the latitude is not between -90 to 90 inclusive, or the longitude
253 is not between -180 to 180 inclusive, none of the values are set and
254 the type() will be QGeoCoordinate::InvalidCoordinate.
255
256 Note that \a altitude specifies the meters above sea level.
257
258 \sa isValid()
259*/
260QGeoCoordinate::QGeoCoordinate(double latitude, double longitude, double altitude)
261 : d(new QGeoCoordinatePrivate)
262{
263#ifndef QT_NO_DATASTREAM
264 initStreamOperators();
265#endif
266
267 if (QLocationUtils::isValidLat(lat: latitude) && QLocationUtils::isValidLong(lng: longitude)) {
268 d->lat = latitude;
269 d->lng = longitude;
270 d->alt = altitude;
271 }
272}
273
274/*!
275 Constructs a coordinate from the contents of \a other.
276*/
277QGeoCoordinate::QGeoCoordinate(const QGeoCoordinate &other)
278 : d(other.d)
279{}
280
281/*!
282 Assigns \a other to this coordinate and returns a reference to this coordinate.
283*/
284QGeoCoordinate &QGeoCoordinate::operator=(const QGeoCoordinate &other)
285{
286 if (this == &other)
287 return *this;
288
289 d = other.d;
290 return (*this);
291}
292
293/*!
294 Destroys the coordinate object.
295*/
296QGeoCoordinate::~QGeoCoordinate()
297{
298}
299
300/*!
301 Returns true if the latitude, longitude and altitude of this
302 coordinate are the same as those of \a other.
303
304 The longitude will be ignored if the latitude is +/- 90 degrees.
305*/
306bool QGeoCoordinate::operator==(const QGeoCoordinate &other) const
307{
308 bool latEqual = (qIsNaN(d: d->lat) && qIsNaN(d: other.d->lat))
309 || qFuzzyCompare(p1: d->lat, p2: other.d->lat);
310 bool lngEqual = (qIsNaN(d: d->lng) && qIsNaN(d: other.d->lng))
311 || qFuzzyCompare(p1: d->lng, p2: other.d->lng);
312 bool altEqual = (qIsNaN(d: d->alt) && qIsNaN(d: other.d->alt))
313 || qFuzzyCompare(p1: d->alt, p2: other.d->alt);
314
315 if (!qIsNaN(d: d->lat) && ((d->lat == 90.0) || (d->lat == -90.0)))
316 lngEqual = true;
317
318 return (latEqual && lngEqual && altEqual);
319}
320
321/*!
322 \fn bool QGeoCoordinate::operator!=(const QGeoCoordinate &other) const
323
324 Returns \c true if latitude, longitude, or altitude of this
325 coordinate are not identical to \a other.
326*/
327
328/*!
329 Returns \c true if the \l longitude and \l latitude are valid.
330*/
331bool QGeoCoordinate::isValid() const
332{
333 CoordinateType t = type();
334 return t == Coordinate2D || t == Coordinate3D;
335}
336
337/*!
338 Returns the type of this coordinate.
339*/
340QGeoCoordinate::CoordinateType QGeoCoordinate::type() const
341{
342 if (QLocationUtils::isValidLat(lat: d->lat)
343 && QLocationUtils::isValidLong(lng: d->lng)) {
344 if (qIsNaN(d: d->alt))
345 return Coordinate2D;
346 return Coordinate3D;
347 }
348 return InvalidCoordinate;
349}
350
351
352/*!
353 Returns the latitude, in decimal degrees. The return value is undefined
354 if the latitude has not been set.
355
356 A positive latitude indicates the Northern Hemisphere, and a negative
357 latitude indicates the Southern Hemisphere.
358
359 \sa setLatitude(), type()
360*/
361double QGeoCoordinate::latitude() const
362{
363 return d->lat;
364}
365
366/*!
367 Sets the latitude (in decimal degrees) to \a latitude. The value should
368 be in the WGS84 datum.
369
370 To be valid, the latitude must be between -90 to 90 inclusive.
371
372 \sa latitude()
373*/
374void QGeoCoordinate::setLatitude(double latitude)
375{
376 d->lat = latitude;
377}
378
379/*!
380 Returns the longitude, in decimal degrees. The return value is undefined
381 if the longitude has not been set.
382
383 A positive longitude indicates the Eastern Hemisphere, and a negative
384 longitude indicates the Western Hemisphere.
385
386 \sa setLongitude(), type()
387*/
388double QGeoCoordinate::longitude() const
389{
390 return d->lng;
391}
392
393/*!
394 Sets the longitude (in decimal degrees) to \a longitude. The value should
395 be in the WGS84 datum.
396
397 To be valid, the longitude must be between -180 to 180 inclusive.
398
399 \sa longitude()
400*/
401void QGeoCoordinate::setLongitude(double longitude)
402{
403 d->lng = longitude;
404}
405
406/*!
407 Returns the altitude (meters above sea level).
408
409 The return value is undefined if the altitude has not been set.
410
411 \sa setAltitude(), type()
412*/
413double QGeoCoordinate::altitude() const
414{
415 return d->alt;
416}
417
418/*!
419 Sets the altitude (meters above sea level) to \a altitude.
420
421 \sa altitude()
422*/
423void QGeoCoordinate::setAltitude(double altitude)
424{
425 d->alt = altitude;
426}
427
428/*!
429 Returns the distance (in meters) from this coordinate to the coordinate
430 specified by \a other. Altitude is not used in the calculation.
431
432 This calculation returns the great-circle distance between the two
433 coordinates, with an assumption that the Earth is spherical for the
434 purpose of this calculation.
435
436 Returns 0 if the type of this coordinate or the type of \a other is
437 QGeoCoordinate::InvalidCoordinate.
438*/
439qreal QGeoCoordinate::distanceTo(const QGeoCoordinate &other) const
440{
441 if (type() == QGeoCoordinate::InvalidCoordinate
442 || other.type() == QGeoCoordinate::InvalidCoordinate) {
443 return 0;
444 }
445
446 // Haversine formula
447 double dlat = qDegreesToRadians(degrees: other.d->lat - d->lat);
448 double dlon = qDegreesToRadians(degrees: other.d->lng - d->lng);
449 double haversine_dlat = sin(x: dlat / 2.0);
450 haversine_dlat *= haversine_dlat;
451 double haversine_dlon = sin(x: dlon / 2.0);
452 haversine_dlon *= haversine_dlon;
453 double y = haversine_dlat
454 + cos(x: qDegreesToRadians(degrees: d->lat))
455 * cos(x: qDegreesToRadians(degrees: other.d->lat))
456 * haversine_dlon;
457 double x = 2 * asin(x: sqrt(x: y));
458 return qreal(x * qgeocoordinate_EARTH_MEAN_RADIUS * 1000);
459}
460
461/*!
462 Returns the azimuth (or bearing) in degrees from this coordinate to the
463 coordinate specified by \a other. Altitude is not used in the calculation.
464
465 The bearing returned is the bearing from the origin to \a other along the
466 great-circle between the two coordinates. There is an assumption that the
467 Earth is spherical for the purpose of this calculation.
468
469 Returns 0 if the type of this coordinate or the type of \a other is
470 QGeoCoordinate::InvalidCoordinate.
471*/
472qreal QGeoCoordinate::azimuthTo(const QGeoCoordinate &other) const
473{
474 if (type() == QGeoCoordinate::InvalidCoordinate
475 || other.type() == QGeoCoordinate::InvalidCoordinate) {
476 return 0;
477 }
478
479 double dlon = qDegreesToRadians(degrees: other.d->lng - d->lng);
480 double lat1Rad = qDegreesToRadians(degrees: d->lat);
481 double lat2Rad = qDegreesToRadians(degrees: other.d->lat);
482
483 double y = sin(x: dlon) * cos(x: lat2Rad);
484 double x = cos(x: lat1Rad) * sin(x: lat2Rad) - sin(x: lat1Rad) * cos(x: lat2Rad) * cos(x: dlon);
485
486 double azimuth = qRadiansToDegrees(radians: atan2(y: y, x: x)) + 360.0;
487 double whole;
488 double fraction = modf(x: azimuth, iptr: &whole);
489 return qreal((int(whole + 360) % 360) + fraction);
490}
491
492void QGeoCoordinatePrivate::atDistanceAndAzimuth(const QGeoCoordinate &coord,
493 qreal distance, qreal azimuth,
494 double *lon, double *lat)
495{
496 double latRad = qDegreesToRadians(degrees: coord.d->lat);
497 double lonRad = qDegreesToRadians(degrees: coord.d->lng);
498 double cosLatRad = cos(x: latRad);
499 double sinLatRad = sin(x: latRad);
500
501 double azimuthRad = qDegreesToRadians(degrees: azimuth);
502
503 double ratio = (distance / (qgeocoordinate_EARTH_MEAN_RADIUS * 1000.0));
504 double cosRatio = cos(x: ratio);
505 double sinRatio = sin(x: ratio);
506
507 double resultLatRad = asin(x: sinLatRad * cosRatio
508 + cosLatRad * sinRatio * cos(x: azimuthRad));
509 double resultLonRad = lonRad + atan2(y: sin(x: azimuthRad) * sinRatio * cosLatRad,
510 x: cosRatio - sinLatRad * sin(x: resultLatRad));
511
512 *lat = qRadiansToDegrees(radians: resultLatRad);
513 *lon = qRadiansToDegrees(radians: resultLonRad);
514}
515
516/*!
517 Returns the coordinate that is reached by traveling \a distance meters
518 from the current coordinate at \a azimuth (or bearing) along a great-circle.
519 There is an assumption that the Earth is spherical for the purpose of this
520 calculation.
521
522 The altitude will have \a distanceUp added to it.
523
524 Returns an invalid coordinate if this coordinate is invalid.
525*/
526QGeoCoordinate QGeoCoordinate::atDistanceAndAzimuth(qreal distance, qreal azimuth, qreal distanceUp) const
527{
528 if (!isValid())
529 return QGeoCoordinate();
530
531 double resultLon, resultLat;
532 QGeoCoordinatePrivate::atDistanceAndAzimuth(coord: *this, distance, azimuth,
533 lon: &resultLon, lat: &resultLat);
534 double resultAlt = d->alt + distanceUp;
535 return QGeoCoordinate(resultLat, QLocationUtils::wrapLong(lng: resultLon), resultAlt);
536}
537
538/*!
539 Returns this coordinate as a string in the specified \a format.
540
541 For example, if this coordinate has a latitude of -27.46758, a longitude
542 of 153.027892 and an altitude of 28.1, these are the strings
543 returned depending on \a format:
544
545 \table
546 \header
547 \li \a format value
548 \li Returned string
549 \row
550 \li \l Degrees
551 \li -27.46758\unicode{0xB0}, 153.02789\unicode{0xB0}, 28.1m
552 \row
553 \li \l DegreesWithHemisphere
554 \li 27.46758\unicode{0xB0} S, 153.02789\unicode{0xB0} E, 28.1m
555 \row
556 \li \l DegreesMinutes
557 \li -27\unicode{0xB0} 28.054', 153\unicode{0xB0} 1.673', 28.1m
558 \row
559 \li \l DegreesMinutesWithHemisphere
560 \li 27\unicode{0xB0} 28.054 S', 153\unicode{0xB0} 1.673' E, 28.1m
561 \row
562 \li \l DegreesMinutesSeconds
563 \li -27\unicode{0xB0} 28' 3.2", 153\unicode{0xB0} 1' 40.4", 28.1m
564 \row
565 \li \l DegreesMinutesSecondsWithHemisphere
566 \li 27\unicode{0xB0} 28' 3.2" S, 153\unicode{0xB0} 1' 40.4" E, 28.1m
567 \endtable
568
569 The altitude field is omitted if no altitude is set.
570
571 If the coordinate is invalid, an empty string is returned.
572*/
573QString QGeoCoordinate::toString(CoordinateFormat format) const
574{
575 if (type() == QGeoCoordinate::InvalidCoordinate)
576 return QString();
577
578 QString latStr;
579 QString longStr;
580
581 double absLat = qAbs(t: d->lat);
582 double absLng = qAbs(t: d->lng);
583 QChar symbol(0x00B0); // degrees symbol
584
585 switch (format) {
586 case Degrees:
587 case DegreesWithHemisphere: {
588 latStr = QString::number(absLat, f: 'f', prec: 5) + symbol;
589 longStr = QString::number(absLng, f: 'f', prec: 5) + symbol;
590 break;
591 }
592 case DegreesMinutes:
593 case DegreesMinutesWithHemisphere: {
594 double latMin = (absLat - int(absLat)) * 60;
595 double lngMin = (absLng - int(absLng)) * 60;
596
597 // We use QString::number(val, 'f', 3) to represent minutes.
598 // It rounds up to the next integer in case the fraction > 0.9995.
599 // Such behavior should be handled specifically when the rounded
600 // value is 60, so that we overflow to degrees correctly.
601 // If we overflow, the minutes should unconditionally be 0.0.
602 if (latMin > 59.9995) {
603 absLat++;
604 latMin = 0.0f;
605 }
606 if (lngMin > 59.9995) {
607 absLng++;
608 lngMin = 0.0f;
609 }
610
611 latStr = QString::fromLatin1(str: "%1%2 %3'")
612 .arg(a: QString::number(int(absLat)))
613 .arg(a: symbol)
614 .arg(a: QString::number(latMin, f: 'f', prec: 3));
615 longStr = QString::fromLatin1(str: "%1%2 %3'")
616 .arg(a: QString::number(int(absLng)))
617 .arg(a: symbol)
618 .arg(a: QString::number(lngMin, f: 'f', prec: 3));
619 break;
620 }
621 case DegreesMinutesSeconds:
622 case DegreesMinutesSecondsWithHemisphere: {
623 double latMin = (absLat - int(absLat)) * 60;
624 double lngMin = (absLng - int(absLng)) * 60;
625 double latSec = (latMin - int(latMin)) * 60;
626 double lngSec = (lngMin - int(lngMin)) * 60;
627
628 // We use QString::number(val, 'f', 1) to represent seconds.
629 // It rounds up to the next integer in case the fraction >= 0.95.
630 // Such behavior should be handled specifically when the rounded
631 // value is 60, so that we overflow to minutes correctly.
632 // If we overflow, the seconds should unconditionally be 0.0.
633 if (latSec >= 59.95) {
634 latMin++;
635 latSec = 0.0f;
636 // We cast to int to represent minutes, so we can use qRound()
637 // to determine if we need to overflow to full degrees.
638 // If we overflow, the minutes will unconditionally be 0.0.
639 if (qRound(d: latMin) >= 60) {
640 absLat++;
641 latMin = 0.0f;
642 }
643 }
644 if (lngSec >= 59.95) {
645 lngMin++;
646 lngSec = 0.0f;
647 if (qRound(d: lngMin) >= 60) {
648 absLng++;
649 lngMin = 0.0f;
650 }
651 }
652
653 latStr = QString::fromLatin1(str: "%1%2 %3' %4\"")
654 .arg(a: QString::number(int(absLat)))
655 .arg(a: symbol)
656 .arg(a: QString::number(int(latMin)))
657 .arg(a: QString::number(latSec, f: 'f', prec: 1));
658 longStr = QString::fromLatin1(str: "%1%2 %3' %4\"")
659 .arg(a: QString::number(int(absLng)))
660 .arg(a: symbol)
661 .arg(a: QString::number(int(lngMin)))
662 .arg(a: QString::number(lngSec, f: 'f', prec: 1));
663 break;
664 }
665 }
666
667 // now add the "-" to the start, or append the hemisphere char
668 switch (format) {
669 case Degrees:
670 case DegreesMinutes:
671 case DegreesMinutesSeconds: {
672 if (d->lat < 0)
673 latStr.insert(i: 0, QStringLiteral("-"));
674 if (d->lng < 0)
675 longStr.insert(i: 0, QStringLiteral("-"));
676 break;
677 }
678 case DegreesWithHemisphere:
679 case DegreesMinutesWithHemisphere:
680 case DegreesMinutesSecondsWithHemisphere: {
681 if (d->lat < 0)
682 latStr.append(s: QString::fromLatin1(str: " S"));
683 else if (d->lat > 0)
684 latStr.append(s: QString::fromLatin1(str: " N"));
685 if (d->lng < 0)
686 longStr.append(s: QString::fromLatin1(str: " W"));
687 else if (d->lng > 0)
688 longStr.append(s: QString::fromLatin1(str: " E"));
689 break;
690 }
691 }
692
693 if (qIsNaN(d: d->alt))
694 return QString::fromLatin1(str: "%1, %2").arg(args&: latStr, args&: longStr);
695 return QString::fromLatin1(str: "%1, %2, %3m").arg(args&: latStr, args&: longStr, args: QString::number(d->alt));
696}
697
698QGeoCoordinate::QGeoCoordinate(QGeoCoordinatePrivate &dd):
699 d(&dd)
700{
701}
702
703#ifndef QT_NO_DEBUG_STREAM
704QDebug operator<<(QDebug dbg, const QGeoCoordinate &coord)
705{
706 QDebugStateSaver saver(dbg);
707 double lat = coord.latitude();
708 double lng = coord.longitude();
709
710 QTextStreamManipulator tsm = qSetRealNumberPrecision(precision: 11);
711 dbg << tsm;
712 dbg.nospace() << "QGeoCoordinate(";
713 if (qIsNaN(d: lat))
714 dbg << '?';
715 else
716 dbg << lat;
717 dbg << ", ";
718 if (qIsNaN(d: lng))
719 dbg << '?';
720 else
721 dbg << lng;
722 if (coord.type() == QGeoCoordinate::Coordinate3D) {
723 dbg << ", ";
724 dbg << coord.altitude();
725 }
726 dbg << ')';
727 return dbg;
728}
729#endif
730
731#ifndef QT_NO_DATASTREAM
732/*!
733 \fn QDataStream &operator<<(QDataStream &stream, const QGeoCoordinate &coordinate)
734
735 \relates QGeoCoordinate
736
737 Writes the given \a coordinate to the specified \a stream.
738
739 \sa {Serializing Qt Data Types}
740*/
741
742QDataStream &operator<<(QDataStream &stream, const QGeoCoordinate &coordinate)
743{
744 stream << coordinate.latitude();
745 stream << coordinate.longitude();
746 stream << coordinate.altitude();
747 return stream;
748}
749#endif
750
751#ifndef QT_NO_DATASTREAM
752/*!
753 \fn QDataStream &operator>>(QDataStream &stream, QGeoCoordinate &coordinate)
754 \relates QGeoCoordinate
755
756 Reads a coordinate from the specified \a stream into the given
757 \a coordinate.
758
759 \sa {Serializing Qt Data Types}
760*/
761
762QDataStream &operator>>(QDataStream &stream, QGeoCoordinate &coordinate)
763{
764 double value;
765 stream >> value;
766 coordinate.setLatitude(value);
767 stream >> value;
768 coordinate.setLongitude(value);
769 stream >> value;
770 coordinate.setAltitude(value);
771 return stream;
772}
773#endif
774
775/*! \fn uint qHash(const QGeoCoordinate &coordinate, uint seed = 0)
776 \relates QHash
777 \since Qt 5.7
778
779 Returns a hash value for \a coordinate, using \a seed to seed the calculation.
780*/
781uint qHash(const QGeoCoordinate &coordinate, uint seed)
782{
783 QtPrivate::QHashCombine hash;
784 // north and south pole are geographically equivalent (no matter the longitude)
785 if (coordinate.latitude() != 90.0 && coordinate.latitude() != -90.0)
786 seed = hash(seed, coordinate.longitude());
787 seed = hash(seed, coordinate.latitude());
788 seed = hash(seed, coordinate.altitude());
789 return seed;
790}
791
792QT_END_NAMESPACE
793

source code of qtlocation/src/positioning/qgeocoordinate.cpp