--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qtmobility/src/location/qgeocoordinate.cpp Fri Apr 16 15:51:22 2010 +0300
@@ -0,0 +1,571 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qgeocoordinate.h"
+#include "qlocationutils_p.h"
+
+#include <QDateTime>
+#include <QHash>
+#include <QDataStream>
+#include <QDebug>
+#include <qnumeric.h>
+
+#include <math.h>
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+QTM_BEGIN_NAMESPACE
+
+static const double qgeocoordinate_EARTH_MEAN_RADIUS = 6371.0072;
+
+inline static double qgeocoordinate_degToRad(double deg)
+{
+ return deg * M_PI / 180;
+}
+inline static double qgeocoordinate_radToDeg(double rad)
+{
+ return rad * 180 / M_PI;
+}
+
+
+class QGeoCoordinatePrivate
+{
+public:
+ double lat;
+ double lng;
+ double alt;
+
+ QGeoCoordinatePrivate() {
+ lat = qQNaN();
+ lng = qQNaN();
+ alt = qQNaN();
+ }
+};
+
+
+/*!
+ \class QGeoCoordinate
+ \brief The QGeoCoordinate class defines a geographical position on the surface of the Earth.
+ \ingroup location
+
+ A QGeoCoordinate is defined by latitude, longitude, and optionally, altitude.
+
+ Use type() to determine whether a coordinate is a 2D coordinate (has
+ latitude and longitude only) or 3D coordinate (has latitude, longitude
+ and altitude). Use distanceTo() and azimuthTo() to calculate the distance
+ and bearing between coordinates.
+
+ The coordinate values should be specified using the WGS84 datum.
+*/
+
+/*!
+ \enum QGeoCoordinate::CoordinateType
+ Defines the types of a coordinate.
+
+ \value InvalidCoordinate An invalid coordinate. A coordinate is invalid if its latitude or longitude values are invalid.
+ \value Coordinate2D A coordinate with valid latitude and longitude values.
+ \value Coordinate3D A coordinate with valid latitude and longitude values, and also an altitude value.
+*/
+
+/*!
+ \enum QGeoCoordinate::CoordinateFormat
+ Defines the possible formatting options for toString().
+
+ \value Degrees Returns a string representation of the coordinates in decimal degrees format.
+ \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.
+ \value DegreesMinutes Returns a string representation of the coordinates in degrees-minutes format.
+ \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.
+ \value DegreesMinutesSeconds Returns a string representation of the coordinates in degrees-minutes-seconds format.
+ \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.
+
+ \sa toString()
+*/
+
+
+/*!
+ Constructs a coordinate. The coordinate will be invalid until
+ setLatitude() and setLongitude() have been called.
+*/
+QGeoCoordinate::QGeoCoordinate()
+ : d(new QGeoCoordinatePrivate)
+{
+}
+
+/*!
+ Constructs a coordinate with the given \a latitude and \a longitude.
+
+ If the latitude is not between -90 to 90 inclusive, or the longitude
+ is not between -180 to 180 inclusive, none of the values are set and
+ the type() will be QGeoCoordinate::InvalidCoordinate.
+
+ \sa isValid()
+*/
+QGeoCoordinate::QGeoCoordinate(double latitude, double longitude)
+ : d(new QGeoCoordinatePrivate)
+{
+ if (QLocationUtils::isValidLat(latitude) && QLocationUtils::isValidLong(longitude)) {
+ d->lat = latitude;
+ d->lng = longitude;
+ }
+}
+
+/*!
+ Constructs a coordinate with the given \a latitude, \a longitude
+ and \a altitude.
+
+ If the latitude is not between -90 to 90 inclusive, or the longitude
+ is not between -180 to 180 inclusive, none of the values are set and
+ the type() will be QGeoCoordinate::InvalidCoordinate.
+
+ Note that \a altitude specifies the metres above sea level.
+
+ \sa isValid()
+*/
+QGeoCoordinate::QGeoCoordinate(double latitude, double longitude, double altitude)
+ : d(new QGeoCoordinatePrivate)
+{
+ if (QLocationUtils::isValidLat(latitude) && QLocationUtils::isValidLong(longitude)) {
+ d->lat = latitude;
+ d->lng = longitude;
+ d->alt = altitude;
+ }
+}
+
+/*!
+ Constructs a coordinate from the contents of \a other.
+*/
+QGeoCoordinate::QGeoCoordinate(const QGeoCoordinate &other)
+ : d(new QGeoCoordinatePrivate)
+{
+ operator=(other);
+}
+
+/*!
+ Destroys the coordinate object.
+*/
+QGeoCoordinate::~QGeoCoordinate()
+{
+ delete d;
+}
+
+/*!
+ Assigns \a other to this coordinate and returns a reference to this
+ coordinate.
+*/
+QGeoCoordinate &QGeoCoordinate::operator=(const QGeoCoordinate & other)
+{
+ if (this == &other)
+ return *this;
+
+ d->lat = other.d->lat;
+ d->lng = other.d->lng;
+ d->alt = other.d->alt;
+
+ return *this;
+}
+
+/*!
+ Returns true if the latitude, longitude and altitude of this
+ coordinate are the same as those of \a other.
+*/
+bool QGeoCoordinate::operator==(const QGeoCoordinate &other) const
+{
+ return ((qIsNaN(d->lat) && qIsNaN(other.d->lat)) || qFuzzyCompare(d->lat, other.d->lat))
+ && ((qIsNaN(d->lng) && qIsNaN(other.d->lng)) || qFuzzyCompare(d->lng, other.d->lng))
+ && ((qIsNaN(d->alt) && qIsNaN(other.d->alt)) || qFuzzyCompare(d->alt, other.d->alt));
+}
+
+/*!
+ \fn bool QGeoCoordinate::operator!=(const QGeoCoordinate &other) const;
+
+ Returns true if the latitude, longitude or altitude of this
+ coordinate are not the same as those of \a other.
+*/
+
+/*!
+ Returns true if the type() is Coordinate2D or Coordinate3D.
+*/
+bool QGeoCoordinate::isValid() const
+{
+ CoordinateType t = type();
+ return t == Coordinate2D || t == Coordinate3D;
+}
+
+/*!
+ Returns the type of this coordinate.
+*/
+QGeoCoordinate::CoordinateType QGeoCoordinate::type() const
+{
+ if (QLocationUtils::isValidLat(d->lat)
+ && QLocationUtils::isValidLong(d->lng)) {
+ if (qIsNaN(d->alt))
+ return Coordinate2D;
+ return Coordinate3D;
+ }
+ return InvalidCoordinate;
+}
+
+
+/*!
+ Returns the latitude, in decimal degrees. The return value is undefined
+ if the latitude has not been set.
+
+ A positive latitude indicates the Northern Hemisphere, and a negative
+ latitude indicates the Southern Hemisphere.
+
+ \sa setLatitude(), type()
+*/
+double QGeoCoordinate::latitude() const
+{
+ return d->lat;
+}
+
+/*!
+ Sets the latitude (in decimal degrees) to \a latitude. The value should
+ be in the WGS84 datum.
+
+ To be valid, the latitude must be between -90 to 90 inclusive.
+
+ \sa latitude()
+*/
+void QGeoCoordinate::setLatitude(double latitude)
+{
+ d->lat = latitude;
+}
+
+/*!
+ Returns the longitude, in decimal degrees. The return value is undefined
+ if the longitude has not been set.
+
+ A positive longitude indicates the Eastern Hemisphere, and a negative
+ longitude indicates the Western Hemisphere.
+
+ \sa setLongitude(), type()
+*/
+double QGeoCoordinate::longitude() const
+{
+ return d->lng;
+}
+
+/*!
+ Sets the longitude (in decimal degrees) to \a longitude. The value should
+ be in the WGS84 datum.
+
+ To be valid, the longitude must be between -180 to 180 inclusive.
+
+ \sa longitude()
+*/
+void QGeoCoordinate::setLongitude(double longitude)
+{
+ d->lng = longitude;
+}
+
+/*!
+ Returns the altitude (meters above sea level).
+
+ The return value is undefined if the altitude has not been set.
+
+ \sa setAltitude(), type()
+*/
+double QGeoCoordinate::altitude() const
+{
+ return d->alt;
+}
+
+/*!
+ Sets the altitude (meters above sea level) to \a altitude.
+
+ \sa altitude()
+*/
+void QGeoCoordinate::setAltitude(double altitude)
+{
+ d->alt = altitude;
+}
+
+/*!
+ Returns the distance (in meters) from this coordinate to the coordinate
+ specified by \a other. Altitude is not used in the calculation.
+
+ This calculation returns the great-circle distance between the two
+ coordinates, with an assumption that the Earth is spherical for the
+ purpose of this calculation.
+
+ Returns 0 if the type of this coordinate or the type of \a other is
+ QGeoCoordinate::InvalidCoordinate.
+*/
+qreal QGeoCoordinate::distanceTo(const QGeoCoordinate &other) const
+{
+ if (type() == QGeoCoordinate::InvalidCoordinate
+ || other.type() == QGeoCoordinate::InvalidCoordinate) {
+ return 0;
+ }
+
+ // Haversine formula
+ double dlat = qgeocoordinate_degToRad(other.d->lat - d->lat);
+ double dlon = qgeocoordinate_degToRad(other.d->lng - d->lng);
+ double y = sin(dlat / 2.0) * sin(dlat / 2.0)
+ + cos(qgeocoordinate_degToRad(d->lat))
+ * cos(qgeocoordinate_degToRad(other.d->lat))
+ * sin(dlon / 2.0) * sin(dlon / 2.0);
+ double x = 2 * atan2(sqrt(y), sqrt(1 - y));
+ return qreal(x * qgeocoordinate_EARTH_MEAN_RADIUS * 1000);
+}
+
+/*!
+ Returns the azimuth (or bearing) in degrees from this coordinate to the
+ coordinate specified by \a other. Altitude is not used in the calculation.
+
+ There is an assumption that the Earth is spherical for the purpose of
+ this calculation.
+
+ Returns 0 if the type of this coordinate or the type of \a other is
+ QGeoCoordinate::InvalidCoordinate.
+*/
+qreal QGeoCoordinate::azimuthTo(const QGeoCoordinate &other) const
+{
+ if (type() == QGeoCoordinate::InvalidCoordinate
+ || other.type() == QGeoCoordinate::InvalidCoordinate) {
+ return 0;
+ }
+
+ double dlon = qgeocoordinate_degToRad(other.d->lng - d->lng);
+ double lat1Rad = qgeocoordinate_degToRad(d->lat);
+ double lat2Rad = qgeocoordinate_degToRad(other.d->lat);
+
+ double y = sin(dlon) * cos(lat2Rad);
+ double x = cos(lat1Rad) * sin(lat2Rad) - sin(lat1Rad) * cos(lat2Rad) * cos(dlon);
+
+ double whole;
+ double fraction = modf(qgeocoordinate_radToDeg(atan2(y, x)), &whole);
+ return qreal((int(whole + 360) % 360) + fraction);
+}
+
+/*!
+ Returns this coordinate as a string in the specified \a format.
+
+ For example, if this coordinate has a latitude of -27.46758, a longitude
+ of 153.027892 and an altitude of 28.1, these are the strings
+ returned depending on \a format:
+
+ \table
+ \header
+ \o \a format value
+ \o Returned string
+ \row
+ \o \l Degrees
+ \o -27.46758\unicode{0xB0}, 153.02789\unicode{0xB0}, 28.1m
+ \row
+ \o \l DegreesWithHemisphere
+ \o 27.46758\unicode{0xB0} S, 153.02789\unicode{0xB0} E, 28.1m
+ \row
+ \o \l DegreesMinutes
+ \o -27\unicode{0xB0} 28.054', 153\unicode{0xB0} 1.673', 28.1m
+ \row
+ \o \l DegreesMinutesWithHemisphere
+ \o 27\unicode{0xB0} 28.054 S', 153\unicode{0xB0} 1.673' E, 28.1m
+ \row
+ \o \l DegreesMinutesSeconds
+ \o -27\unicode{0xB0} 28' 3.2", 153\unicode{0xB0} 1' 40.4", 28.1m
+ \row
+ \o \l DegreesMinutesSecondsWithHemisphere
+ \o 27\unicode{0xB0} 28' 3.2" S, 153\unicode{0xB0} 1' 40.4" E, 28.1m
+ \endtable
+
+ The altitude field is omitted if no altitude is set.
+
+ If the coordinate is invalid, an empty string is returned.
+*/
+QString QGeoCoordinate::toString(CoordinateFormat format) const
+{
+ if (type() == QGeoCoordinate::InvalidCoordinate)
+ return QString();
+
+ QString latStr;
+ QString longStr;
+
+ double absLat = qAbs(d->lat);
+ double absLng = qAbs(d->lng);
+ QChar symbol(0x00B0); // degrees symbol
+
+ switch (format) {
+ case Degrees:
+ case DegreesWithHemisphere: {
+ latStr = QString::number(absLat, 'f', 5) + symbol;
+ longStr = QString::number(absLng, 'f', 5) + symbol;
+ break;
+ }
+ case DegreesMinutes:
+ case DegreesMinutesWithHemisphere: {
+ double latMin = (absLat - int(absLat)) * 60;
+ double lngMin = (absLng - int(absLng)) * 60;
+ latStr = QString("%1%2 %3'")
+ .arg(QString::number(int(absLat)))
+ .arg(symbol)
+ .arg(QString::number(latMin, 'f', 3));
+ longStr = QString("%1%2 %3'")
+ .arg(QString::number(int(absLng)))
+ .arg(symbol)
+ .arg(QString::number(lngMin, 'f', 3));
+ break;
+ }
+ case DegreesMinutesSeconds:
+ case DegreesMinutesSecondsWithHemisphere: {
+ double latMin = (absLat - int(absLat)) * 60;
+ double lngMin = (absLng - int(absLng)) * 60;
+ double latSec = (latMin - int(latMin)) * 60;
+ double lngSec = (lngMin - int(lngMin)) * 60;
+
+ latStr = QString("%1%2 %3' %4\"")
+ .arg(QString::number(int(absLat)))
+ .arg(symbol)
+ .arg(QString::number(int(latMin)))
+ .arg(QString::number(latSec, 'f', 1));
+ longStr = QString("%1%2 %3' %4\"")
+ .arg(QString::number(int(absLng)))
+ .arg(symbol)
+ .arg(QString::number(int(lngMin)))
+ .arg(QString::number(lngSec, 'f', 1));
+ break;
+ }
+ }
+
+ // now add the "-" to the start, or append the hemisphere char
+ switch (format) {
+ case Degrees:
+ case DegreesMinutes:
+ case DegreesMinutesSeconds: {
+ if (d->lat < 0)
+ latStr.insert(0, "-");
+ if (d->lng < 0)
+ longStr.insert(0, "-");
+ break;
+ }
+ case DegreesWithHemisphere:
+ case DegreesMinutesWithHemisphere:
+ case DegreesMinutesSecondsWithHemisphere: {
+ if (d->lat < 0)
+ latStr.append(" S");
+ else if (d->lat > 0)
+ latStr.append(" N");
+ if (d->lng < 0)
+ longStr.append(" W");
+ else if (d->lng > 0)
+ longStr.append(" E");
+ break;
+ }
+ }
+
+ if (qIsNaN(d->alt))
+ return QString("%1, %2").arg(latStr, longStr);
+ return QString("%1, %2, %3m").arg(latStr, longStr, QString::number(d->alt));
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QGeoCoordinate &coord)
+{
+ double lat = coord.latitude();
+ double lng = coord.longitude();
+
+ dbg.nospace() << "QGeoCoordinate(";
+ if (qIsNaN(lat))
+ dbg.nospace() << '?';
+ else
+ dbg.nospace() << lat;
+ dbg.nospace() << ", ";
+ if (qIsNaN(lng))
+ dbg.nospace() << '?';
+ else
+ dbg.nospace() << lng;
+ if (coord.type() == QGeoCoordinate::Coordinate3D) {
+ dbg.nospace() << ", ";
+ dbg.nospace() << coord.altitude();
+ }
+ dbg.nospace() << ')';
+ return dbg;
+}
+#endif
+
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QGeoCoordinate &coordinate)
+
+ \relates QGeoCoordinate
+
+ Writes the given \a coordinate to the specified \a stream.
+
+ \sa {Format of the QDataStream Operators}
+*/
+
+QDataStream &operator<<(QDataStream &stream, const QGeoCoordinate &coordinate)
+{
+ stream << coordinate.latitude();
+ stream << coordinate.longitude();
+ stream << coordinate.altitude();
+ return stream;
+}
+#endif
+
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QGeoCoordinate &coordinate)
+ \relates QGeoCoordinate
+
+ Reads a coordinate from the specified \a stream into the given
+ \a coordinate.
+
+ \sa {Format of the QDataStream Operators}
+*/
+
+QDataStream &operator>>(QDataStream &stream, QGeoCoordinate &coordinate)
+{
+ double value;
+ stream >> value;
+ coordinate.setLatitude(value);
+ stream >> value;
+ coordinate.setLongitude(value);
+ stream >> value;
+ coordinate.setAltitude(value);
+ return stream;
+}
+#endif
+
+QTM_END_NAMESPACE