qtmobility/src/location/qgeocoordinate.cpp
changeset 1 2b40d63a9c3d
child 11 06b8e2af4411
equal deleted inserted replaced
0:cfcbf08528c4 1:2b40d63a9c3d
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 #include "qgeocoordinate.h"
       
    42 #include "qlocationutils_p.h"
       
    43 
       
    44 #include <QDateTime>
       
    45 #include <QHash>
       
    46 #include <QDataStream>
       
    47 #include <QDebug>
       
    48 #include <qnumeric.h>
       
    49 
       
    50 #include <math.h>
       
    51 
       
    52 #ifndef M_PI
       
    53 #define M_PI 3.14159265358979323846
       
    54 #endif
       
    55 
       
    56 QTM_BEGIN_NAMESPACE
       
    57 
       
    58 static const double qgeocoordinate_EARTH_MEAN_RADIUS = 6371.0072;
       
    59 
       
    60 inline static double qgeocoordinate_degToRad(double deg)
       
    61 {
       
    62     return deg * M_PI / 180;
       
    63 }
       
    64 inline static double qgeocoordinate_radToDeg(double rad)
       
    65 {
       
    66     return rad * 180 / M_PI;
       
    67 }
       
    68 
       
    69 
       
    70 class QGeoCoordinatePrivate
       
    71 {
       
    72 public:
       
    73     double lat;
       
    74     double lng;
       
    75     double alt;
       
    76 
       
    77     QGeoCoordinatePrivate() {
       
    78         lat = qQNaN();
       
    79         lng = qQNaN();
       
    80         alt = qQNaN();
       
    81     }
       
    82 };
       
    83 
       
    84 
       
    85 /*!
       
    86     \class QGeoCoordinate
       
    87     \brief The QGeoCoordinate class defines a geographical position on the surface of the Earth.
       
    88     \ingroup location
       
    89 
       
    90     A QGeoCoordinate is defined by latitude, longitude, and optionally, altitude.
       
    91 
       
    92     Use type() to determine whether a coordinate is a 2D coordinate (has
       
    93     latitude and longitude only) or 3D coordinate (has latitude, longitude
       
    94     and altitude). Use distanceTo() and azimuthTo() to calculate the distance
       
    95     and bearing between coordinates.
       
    96 
       
    97     The coordinate values should be specified using the WGS84 datum.
       
    98 */
       
    99 
       
   100 /*!
       
   101     \enum QGeoCoordinate::CoordinateType
       
   102     Defines the types of a coordinate.
       
   103 
       
   104     \value InvalidCoordinate An invalid coordinate. A coordinate is invalid if its latitude or longitude values are invalid.
       
   105     \value Coordinate2D A coordinate with valid latitude and longitude values.
       
   106     \value Coordinate3D A coordinate with valid latitude and longitude values, and also an altitude value.
       
   107 */
       
   108 
       
   109 /*!
       
   110     \enum QGeoCoordinate::CoordinateFormat
       
   111     Defines the possible formatting options for toString().
       
   112 
       
   113     \value Degrees Returns a string representation of the coordinates in decimal degrees format.
       
   114     \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.
       
   115     \value DegreesMinutes Returns a string representation of the coordinates in degrees-minutes format.
       
   116     \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.
       
   117     \value DegreesMinutesSeconds Returns a string representation of the coordinates in degrees-minutes-seconds format.
       
   118     \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.
       
   119 
       
   120     \sa toString()
       
   121 */
       
   122 
       
   123 
       
   124 /*!
       
   125     Constructs a coordinate. The coordinate will be invalid until
       
   126     setLatitude() and setLongitude() have been called.
       
   127 */
       
   128 QGeoCoordinate::QGeoCoordinate()
       
   129         : d(new QGeoCoordinatePrivate)
       
   130 {
       
   131 }
       
   132 
       
   133 /*!
       
   134     Constructs a coordinate with the given \a latitude and \a longitude.
       
   135 
       
   136     If the latitude is not between -90 to 90 inclusive, or the longitude
       
   137     is not between -180 to 180 inclusive, none of the values are set and
       
   138     the type() will be QGeoCoordinate::InvalidCoordinate.
       
   139 
       
   140     \sa isValid()
       
   141 */
       
   142 QGeoCoordinate::QGeoCoordinate(double latitude, double longitude)
       
   143         : d(new QGeoCoordinatePrivate)
       
   144 {
       
   145     if (QLocationUtils::isValidLat(latitude) && QLocationUtils::isValidLong(longitude)) {
       
   146         d->lat = latitude;
       
   147         d->lng = longitude;
       
   148     }
       
   149 }
       
   150 
       
   151 /*!
       
   152     Constructs a coordinate with the given \a latitude, \a longitude
       
   153     and \a altitude.
       
   154 
       
   155     If the latitude is not between -90 to 90 inclusive, or the longitude
       
   156     is not between -180 to 180 inclusive, none of the values are set and
       
   157     the type() will be QGeoCoordinate::InvalidCoordinate.
       
   158 
       
   159     Note that \a altitude specifies the metres above sea level.
       
   160 
       
   161     \sa isValid()
       
   162 */
       
   163 QGeoCoordinate::QGeoCoordinate(double latitude, double longitude, double altitude)
       
   164         : d(new QGeoCoordinatePrivate)
       
   165 {
       
   166     if (QLocationUtils::isValidLat(latitude) && QLocationUtils::isValidLong(longitude)) {
       
   167         d->lat = latitude;
       
   168         d->lng = longitude;
       
   169         d->alt = altitude;
       
   170     }
       
   171 }
       
   172 
       
   173 /*!
       
   174     Constructs a coordinate from the contents of \a other.
       
   175 */
       
   176 QGeoCoordinate::QGeoCoordinate(const QGeoCoordinate &other)
       
   177         : d(new QGeoCoordinatePrivate)
       
   178 {
       
   179     operator=(other);
       
   180 }
       
   181 
       
   182 /*!
       
   183     Destroys the coordinate object.
       
   184 */
       
   185 QGeoCoordinate::~QGeoCoordinate()
       
   186 {
       
   187     delete d;
       
   188 }
       
   189 
       
   190 /*!
       
   191     Assigns \a other to this coordinate and returns a reference to this
       
   192     coordinate.
       
   193 */
       
   194 QGeoCoordinate &QGeoCoordinate::operator=(const QGeoCoordinate & other)
       
   195 {
       
   196     if (this == &other)
       
   197         return *this;
       
   198 
       
   199     d->lat = other.d->lat;
       
   200     d->lng = other.d->lng;
       
   201     d->alt = other.d->alt;
       
   202 
       
   203     return *this;
       
   204 }
       
   205 
       
   206 /*!
       
   207     Returns true if the latitude, longitude and altitude of this
       
   208     coordinate are the same as those of \a other.
       
   209 */
       
   210 bool QGeoCoordinate::operator==(const QGeoCoordinate &other) const
       
   211 {
       
   212     return ((qIsNaN(d->lat) && qIsNaN(other.d->lat)) || qFuzzyCompare(d->lat, other.d->lat))
       
   213            && ((qIsNaN(d->lng) && qIsNaN(other.d->lng)) || qFuzzyCompare(d->lng, other.d->lng))
       
   214            && ((qIsNaN(d->alt) && qIsNaN(other.d->alt)) || qFuzzyCompare(d->alt, other.d->alt));
       
   215 }
       
   216 
       
   217 /*!
       
   218     \fn bool QGeoCoordinate::operator!=(const QGeoCoordinate &other) const;
       
   219 
       
   220     Returns true if the latitude, longitude or altitude of this
       
   221     coordinate are not the same as those of \a other.
       
   222 */
       
   223 
       
   224 /*!
       
   225     Returns true if the type() is Coordinate2D or Coordinate3D.
       
   226 */
       
   227 bool QGeoCoordinate::isValid() const
       
   228 {
       
   229     CoordinateType t = type();
       
   230     return t == Coordinate2D || t == Coordinate3D;
       
   231 }
       
   232 
       
   233 /*!
       
   234     Returns the type of this coordinate.
       
   235 */
       
   236 QGeoCoordinate::CoordinateType QGeoCoordinate::type() const
       
   237 {
       
   238     if (QLocationUtils::isValidLat(d->lat)
       
   239             && QLocationUtils::isValidLong(d->lng)) {
       
   240         if (qIsNaN(d->alt))
       
   241             return Coordinate2D;
       
   242         return Coordinate3D;
       
   243     }
       
   244     return InvalidCoordinate;
       
   245 }
       
   246 
       
   247 
       
   248 /*!
       
   249     Returns the latitude, in decimal degrees. The return value is undefined
       
   250     if the latitude has not been set.
       
   251 
       
   252     A positive latitude indicates the Northern Hemisphere, and a negative
       
   253     latitude indicates the Southern Hemisphere.
       
   254 
       
   255     \sa setLatitude(), type()
       
   256 */
       
   257 double QGeoCoordinate::latitude() const
       
   258 {
       
   259     return d->lat;
       
   260 }
       
   261 
       
   262 /*!
       
   263     Sets the latitude (in decimal degrees) to \a latitude. The value should
       
   264     be in the WGS84 datum.
       
   265 
       
   266     To be valid, the latitude must be between -90 to 90 inclusive.
       
   267 
       
   268     \sa latitude()
       
   269 */
       
   270 void QGeoCoordinate::setLatitude(double latitude)
       
   271 {
       
   272     d->lat = latitude;
       
   273 }
       
   274 
       
   275 /*!
       
   276     Returns the longitude, in decimal degrees. The return value is undefined
       
   277     if the longitude has not been set.
       
   278 
       
   279     A positive longitude indicates the Eastern Hemisphere, and a negative
       
   280     longitude indicates the Western Hemisphere.
       
   281 
       
   282     \sa setLongitude(), type()
       
   283 */
       
   284 double QGeoCoordinate::longitude() const
       
   285 {
       
   286     return d->lng;
       
   287 }
       
   288 
       
   289 /*!
       
   290     Sets the longitude (in decimal degrees) to \a longitude. The value should
       
   291     be in the WGS84 datum.
       
   292 
       
   293     To be valid, the longitude must be between -180 to 180 inclusive.
       
   294 
       
   295     \sa longitude()
       
   296 */
       
   297 void QGeoCoordinate::setLongitude(double longitude)
       
   298 {
       
   299     d->lng = longitude;
       
   300 }
       
   301 
       
   302 /*!
       
   303     Returns the altitude (meters above sea level).
       
   304 
       
   305     The return value is undefined if the altitude has not been set.
       
   306 
       
   307     \sa setAltitude(), type()
       
   308 */
       
   309 double QGeoCoordinate::altitude() const
       
   310 {
       
   311     return d->alt;
       
   312 }
       
   313 
       
   314 /*!
       
   315     Sets the altitude (meters above sea level) to \a altitude.
       
   316 
       
   317     \sa altitude()
       
   318 */
       
   319 void QGeoCoordinate::setAltitude(double altitude)
       
   320 {
       
   321     d->alt = altitude;
       
   322 }
       
   323 
       
   324 /*!
       
   325     Returns the distance (in meters) from this coordinate to the coordinate
       
   326     specified by \a other. Altitude is not used in the calculation.
       
   327 
       
   328     This calculation returns the great-circle distance between the two
       
   329     coordinates, with an assumption that the Earth is spherical for the
       
   330     purpose of this calculation.
       
   331 
       
   332     Returns 0 if the type of this coordinate or the type of \a other is
       
   333     QGeoCoordinate::InvalidCoordinate.
       
   334 */
       
   335 qreal QGeoCoordinate::distanceTo(const QGeoCoordinate &other) const
       
   336 {
       
   337     if (type() == QGeoCoordinate::InvalidCoordinate
       
   338             || other.type() == QGeoCoordinate::InvalidCoordinate) {
       
   339         return 0;
       
   340     }
       
   341 
       
   342     // Haversine formula
       
   343     double dlat = qgeocoordinate_degToRad(other.d->lat - d->lat);
       
   344     double dlon = qgeocoordinate_degToRad(other.d->lng - d->lng);
       
   345     double y = sin(dlat / 2.0) * sin(dlat / 2.0)
       
   346                + cos(qgeocoordinate_degToRad(d->lat))
       
   347                * cos(qgeocoordinate_degToRad(other.d->lat))
       
   348                * sin(dlon / 2.0) * sin(dlon / 2.0);
       
   349     double x = 2 * atan2(sqrt(y), sqrt(1 - y));
       
   350     return qreal(x * qgeocoordinate_EARTH_MEAN_RADIUS * 1000);
       
   351 }
       
   352 
       
   353 /*!
       
   354     Returns the azimuth (or bearing) in degrees from this coordinate to the
       
   355     coordinate specified by \a other. Altitude is not used in the calculation.
       
   356 
       
   357     There is an assumption that the Earth is spherical for the purpose of
       
   358     this calculation.
       
   359 
       
   360     Returns 0 if the type of this coordinate or the type of \a other is
       
   361     QGeoCoordinate::InvalidCoordinate.
       
   362 */
       
   363 qreal QGeoCoordinate::azimuthTo(const QGeoCoordinate &other) const
       
   364 {
       
   365     if (type() == QGeoCoordinate::InvalidCoordinate
       
   366             || other.type() == QGeoCoordinate::InvalidCoordinate) {
       
   367         return 0;
       
   368     }
       
   369 
       
   370     double dlon = qgeocoordinate_degToRad(other.d->lng - d->lng);
       
   371     double lat1Rad = qgeocoordinate_degToRad(d->lat);
       
   372     double lat2Rad = qgeocoordinate_degToRad(other.d->lat);
       
   373 
       
   374     double y = sin(dlon) * cos(lat2Rad);
       
   375     double x = cos(lat1Rad) * sin(lat2Rad) - sin(lat1Rad) * cos(lat2Rad) * cos(dlon);
       
   376 
       
   377     double whole;
       
   378     double fraction = modf(qgeocoordinate_radToDeg(atan2(y, x)), &whole);
       
   379     return qreal((int(whole + 360) % 360) + fraction);
       
   380 }
       
   381 
       
   382 /*!
       
   383     Returns this coordinate as a string in the specified \a format.
       
   384 
       
   385     For example, if this coordinate has a latitude of -27.46758, a longitude
       
   386     of 153.027892 and an altitude of 28.1, these are the strings
       
   387     returned depending on \a format:
       
   388 
       
   389     \table
       
   390     \header
       
   391         \o \a format value
       
   392         \o Returned string
       
   393     \row
       
   394         \o \l Degrees
       
   395         \o -27.46758\unicode{0xB0}, 153.02789\unicode{0xB0}, 28.1m
       
   396     \row
       
   397         \o \l DegreesWithHemisphere
       
   398         \o 27.46758\unicode{0xB0} S, 153.02789\unicode{0xB0} E, 28.1m
       
   399     \row
       
   400         \o \l DegreesMinutes
       
   401         \o -27\unicode{0xB0} 28.054', 153\unicode{0xB0} 1.673', 28.1m
       
   402     \row
       
   403         \o \l DegreesMinutesWithHemisphere
       
   404         \o 27\unicode{0xB0} 28.054 S', 153\unicode{0xB0} 1.673' E, 28.1m
       
   405     \row
       
   406         \o \l DegreesMinutesSeconds
       
   407         \o -27\unicode{0xB0} 28' 3.2", 153\unicode{0xB0} 1' 40.4", 28.1m
       
   408     \row
       
   409         \o \l DegreesMinutesSecondsWithHemisphere
       
   410         \o 27\unicode{0xB0} 28' 3.2" S, 153\unicode{0xB0} 1' 40.4" E, 28.1m
       
   411     \endtable
       
   412 
       
   413     The altitude field is omitted if no altitude is set.
       
   414 
       
   415     If the coordinate is invalid, an empty string is returned.
       
   416 */
       
   417 QString QGeoCoordinate::toString(CoordinateFormat format) const
       
   418 {
       
   419     if (type() == QGeoCoordinate::InvalidCoordinate)
       
   420         return QString();
       
   421 
       
   422     QString latStr;
       
   423     QString longStr;
       
   424 
       
   425     double absLat = qAbs(d->lat);
       
   426     double absLng = qAbs(d->lng);
       
   427     QChar symbol(0x00B0);   // degrees symbol
       
   428 
       
   429     switch (format) {
       
   430         case Degrees:
       
   431         case DegreesWithHemisphere: {
       
   432             latStr = QString::number(absLat, 'f', 5) + symbol;
       
   433             longStr = QString::number(absLng, 'f', 5) + symbol;
       
   434             break;
       
   435         }
       
   436         case DegreesMinutes:
       
   437         case DegreesMinutesWithHemisphere: {
       
   438             double latMin = (absLat - int(absLat)) * 60;
       
   439             double lngMin = (absLng - int(absLng)) * 60;
       
   440             latStr = QString("%1%2 %3'")
       
   441                      .arg(QString::number(int(absLat)))
       
   442                      .arg(symbol)
       
   443                      .arg(QString::number(latMin, 'f', 3));
       
   444             longStr = QString("%1%2 %3'")
       
   445                       .arg(QString::number(int(absLng)))
       
   446                       .arg(symbol)
       
   447                       .arg(QString::number(lngMin, 'f', 3));
       
   448             break;
       
   449         }
       
   450         case DegreesMinutesSeconds:
       
   451         case DegreesMinutesSecondsWithHemisphere: {
       
   452             double latMin = (absLat - int(absLat)) * 60;
       
   453             double lngMin = (absLng - int(absLng)) * 60;
       
   454             double latSec = (latMin - int(latMin)) * 60;
       
   455             double lngSec = (lngMin - int(lngMin)) * 60;
       
   456 
       
   457             latStr = QString("%1%2 %3' %4\"")
       
   458                      .arg(QString::number(int(absLat)))
       
   459                      .arg(symbol)
       
   460                      .arg(QString::number(int(latMin)))
       
   461                      .arg(QString::number(latSec, 'f', 1));
       
   462             longStr = QString("%1%2 %3' %4\"")
       
   463                       .arg(QString::number(int(absLng)))
       
   464                       .arg(symbol)
       
   465                       .arg(QString::number(int(lngMin)))
       
   466                       .arg(QString::number(lngSec, 'f', 1));
       
   467             break;
       
   468         }
       
   469     }
       
   470 
       
   471     // now add the "-" to the start, or append the hemisphere char
       
   472     switch (format) {
       
   473         case Degrees:
       
   474         case DegreesMinutes:
       
   475         case DegreesMinutesSeconds: {
       
   476             if (d->lat < 0)
       
   477                 latStr.insert(0, "-");
       
   478             if (d->lng < 0)
       
   479                 longStr.insert(0, "-");
       
   480             break;
       
   481         }
       
   482         case DegreesWithHemisphere:
       
   483         case DegreesMinutesWithHemisphere:
       
   484         case DegreesMinutesSecondsWithHemisphere: {
       
   485             if (d->lat < 0)
       
   486                 latStr.append(" S");
       
   487             else if (d->lat > 0)
       
   488                 latStr.append(" N");
       
   489             if (d->lng < 0)
       
   490                 longStr.append(" W");
       
   491             else if (d->lng > 0)
       
   492                 longStr.append(" E");
       
   493             break;
       
   494         }
       
   495     }
       
   496 
       
   497     if (qIsNaN(d->alt))
       
   498         return QString("%1, %2").arg(latStr, longStr);
       
   499     return QString("%1, %2, %3m").arg(latStr, longStr, QString::number(d->alt));
       
   500 }
       
   501 
       
   502 #ifndef QT_NO_DEBUG_STREAM
       
   503 QDebug operator<<(QDebug dbg, const QGeoCoordinate &coord)
       
   504 {
       
   505     double lat = coord.latitude();
       
   506     double lng = coord.longitude();
       
   507 
       
   508     dbg.nospace() << "QGeoCoordinate(";
       
   509     if (qIsNaN(lat))
       
   510         dbg.nospace() << '?';
       
   511     else
       
   512         dbg.nospace() << lat;
       
   513     dbg.nospace() << ", ";
       
   514     if (qIsNaN(lng))
       
   515         dbg.nospace() << '?';
       
   516     else
       
   517         dbg.nospace() << lng;
       
   518     if (coord.type() == QGeoCoordinate::Coordinate3D) {
       
   519         dbg.nospace() << ", ";
       
   520         dbg.nospace() << coord.altitude();
       
   521     }
       
   522     dbg.nospace() << ')';
       
   523     return dbg;
       
   524 }
       
   525 #endif
       
   526 
       
   527 #ifndef QT_NO_DATASTREAM
       
   528 /*!
       
   529     \fn QDataStream &operator<<(QDataStream &stream, const QGeoCoordinate &coordinate)
       
   530 
       
   531     \relates QGeoCoordinate
       
   532 
       
   533     Writes the given \a coordinate to the specified \a stream.
       
   534 
       
   535     \sa {Format of the QDataStream Operators}
       
   536 */
       
   537 
       
   538 QDataStream &operator<<(QDataStream &stream, const QGeoCoordinate &coordinate)
       
   539 {
       
   540     stream << coordinate.latitude();
       
   541     stream << coordinate.longitude();
       
   542     stream << coordinate.altitude();
       
   543     return stream;
       
   544 }
       
   545 #endif
       
   546 
       
   547 #ifndef QT_NO_DATASTREAM
       
   548 /*!
       
   549     \fn  QDataStream &operator>>(QDataStream &stream, QGeoCoordinate &coordinate)
       
   550     \relates QGeoCoordinate
       
   551 
       
   552     Reads a coordinate from the specified \a stream into the given
       
   553     \a coordinate.
       
   554 
       
   555     \sa {Format of the QDataStream Operators}
       
   556 */
       
   557 
       
   558 QDataStream &operator>>(QDataStream &stream, QGeoCoordinate &coordinate)
       
   559 {
       
   560     double value;
       
   561     stream >> value;
       
   562     coordinate.setLatitude(value);
       
   563     stream >> value;
       
   564     coordinate.setLongitude(value);
       
   565     stream >> value;
       
   566     coordinate.setAltitude(value);
       
   567     return stream;
       
   568 }
       
   569 #endif
       
   570 
       
   571 QTM_END_NAMESPACE