File indexing completed on 2024-04-28 03:48:56

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2006-2007 Torsten Rahn <tackat@kde.org>
0004 // SPDX-FileCopyrightText: 2007-2008 Inge Wallin <ingwa@kde.org>
0005 // SPDX-FileCopyrightText: 2008 Patrick Spendrin <ps_ml@gmx.de>
0006 // SPDX-FileCopyrightText: 2015 Alejandro Garcia Montoro <alejandro.garciamontoro@gmail.com>
0007 //
0008 
0009 
0010 #ifndef MARBLE_GEODATACOORDINATES_H
0011 #define MARBLE_GEODATACOORDINATES_H
0012 
0013 #include <QCoreApplication>
0014 #include <QMetaType>
0015 #include <QVector>
0016 
0017 #include "geodata_export.h"
0018 #include "MarbleGlobal.h"
0019 
0020 class QString;
0021 
0022 namespace Marble
0023 {
0024 
0025 class GeoDataCoordinatesPrivate;
0026 class Quaternion;
0027 
0028 /**
0029  * @short A 3d point representation
0030  *
0031  * GeoDataCoordinates is the simple representation of a single three
0032  * dimensional point. It can be used all through out marble as the data type
0033  * for three dimensional objects. it comprises of a Quaternion for speed issues.
0034  * This class was introduced to reflect the difference between a simple 3d point
0035  * and the GeoDataGeometry object containing such a point. The latter is a 
0036  * GeoDataPoint and is simply derived from GeoDataGeometry.
0037  * @see GeoDataPoint
0038 */
0039 
0040 class GEODATA_EXPORT GeoDataCoordinates
0041 {
0042  Q_DECLARE_TR_FUNCTIONS(GeoDataCoordinates)
0043 
0044  public:
0045     /**
0046      * @brief enum used constructor to specify the units used
0047      *
0048      * Internally we always use radian for mathematical convenience.
0049      * However the Marble's interfaces to the outside should default
0050      * to degrees.
0051      */
0052     enum Unit{
0053         Radian,
0054         Degree
0055     };
0056 
0057     /**
0058      * @brief enum used to specify the notation / numerical system
0059      *
0060      * For degrees there exist two notations:
0061      * "Decimal" (base-10) and the "Sexagesimal DMS" (base-60) which is
0062      * traditionally used in cartography. Decimal notation
0063      * uses floating point numbers to specify parts of a degree. The
0064      * Sexagesimal DMS notation uses integer based
0065      * Degrees-(Arc)Minutes-(Arc)Seconds to describe parts of a degree.
0066      */
0067     enum Notation{
0068         Decimal, ///< "Decimal" notation (base-10)
0069         DMS,     ///< "Sexagesimal DMS" notation (base-60)
0070         DM,      ///< "Sexagesimal DM" notation (base-60)
0071         UTM,
0072         Astro    /// < "RA and DEC" notation (used for astronomical sky coordinates)
0073     };
0074 
0075     /**
0076      * @brief The BearingType enum specifies where to measure the bearing
0077      * along great circle arcs
0078      *
0079      * When traveling along a great circle arc defined by the two points
0080      * A and B, the bearing varies along the arc. The "InitialBearing" bearing
0081      * corresponds to the bearing value at A, the "FinalBearing" bearing to that
0082      * at B.
0083      */
0084     enum BearingType {
0085         InitialBearing,
0086         FinalBearing
0087     };
0088 
0089     // Type definitions
0090     using Vector = QVector<GeoDataCoordinates>;
0091     using PtrVector = QVector<GeoDataCoordinates *>;
0092 
0093     GeoDataCoordinates( const GeoDataCoordinates& other );
0094 
0095     /**
0096      * @brief constructs an invalid instance
0097      *
0098      * Constructs an invalid instance such that calling isValid()
0099      * on it will return @code false @endcode.
0100      */
0101     GeoDataCoordinates();
0102 
0103     /**
0104      * @brief create a geocoordinate from longitude and latitude
0105      * @param lon longitude
0106      * @param lat latitude
0107      * @param alt altitude in meters (default: 0)
0108      * @param unit units that lon and lat get measured in
0109      * (default for Radian: north pole at pi/2, southpole at -pi/2)
0110      * @param detail detail (default: 0)
0111      */
0112     GeoDataCoordinates( qreal lon, qreal lat, qreal alt = 0,
0113                         GeoDataCoordinates::Unit unit = GeoDataCoordinates::Radian,
0114                         int detail = 0 );
0115 
0116     virtual ~GeoDataCoordinates();
0117 
0118     /**
0119      * @brief Returns @code true @endcode if the coordinate is valid, @code false @endcode otherwise.
0120      * @return whether the coordinate is valid
0121      *
0122      * A coordinate is valid, if at least one component has been set and the last
0123      * assignment was not an invalid GeoDataCoordinates object.
0124      */
0125     bool isValid() const;
0126 
0127     /**
0128     * @brief (re)set the coordinates in a GeoDataCoordinates object
0129     * @param lon longitude
0130     * @param lat latitude
0131     * @param alt altitude in meters (default: 0)
0132     * @param unit units that lon and lat get measured in
0133     * (default for Radian: north pole at pi/2, southpole at -pi/2)
0134     */
0135     void set( qreal lon, qreal lat, qreal alt = 0,
0136               GeoDataCoordinates::Unit unit = GeoDataCoordinates::Radian );
0137 
0138     /**
0139     * @brief use this function to get the longitude and latitude with one
0140     * call - use the unit parameter to switch between Radian and DMS
0141     * @param lon longitude
0142     * @param lat latitude
0143     * @param unit units that lon and lat get measured in
0144     * (default for Radian: north pole at pi/2, southpole at -pi/2)
0145     */
0146     void geoCoordinates(qreal& lon, qreal& lat, GeoDataCoordinates::Unit unit) const;
0147     void geoCoordinates(qreal& lon, qreal& lat) const;
0148 
0149     /**
0150     * @brief use this function to get the longitude, latitude and altitude
0151     * with one call - use the unit parameter to switch between Radian and DMS
0152     * @param lon longitude
0153     * @param lat latitude
0154     * @param alt altitude in meters
0155     * @param unit units that lon and lat get measured in
0156     * (default for Radian: north pole at pi/2, southpole at -pi/2)
0157     */
0158     void geoCoordinates(qreal& lon, qreal& lat, qreal& alt, GeoDataCoordinates::Unit unit) const;
0159     void geoCoordinates(qreal& lon, qreal& lat, qreal& alt) const;
0160 
0161     /**
0162     * @brief set the longitude in a GeoDataCoordinates object
0163     * @param lon longitude
0164     * @param unit units that lon and lat get measured in
0165     * (default for Radian: north pole at pi/2, southpole at -pi/2)
0166     */
0167     void setLongitude( qreal lon,
0168               GeoDataCoordinates::Unit unit = GeoDataCoordinates::Radian );
0169 
0170     /**
0171     * @brief retrieves the longitude of the GeoDataCoordinates object
0172     * use the unit parameter to switch between Radian and DMS
0173     * @param unit units that lon and lat get measured in
0174     * (default for Radian: north pole at pi/2, southpole at -pi/2)
0175     * @return longitude
0176     */
0177     qreal longitude(GeoDataCoordinates::Unit unit) const;
0178     qreal longitude() const;
0179 
0180     /**
0181     * @brief retrieves the latitude of the GeoDataCoordinates object
0182     * use the unit parameter to switch between Radian and DMS
0183     * @param unit units that lon and lat get measured in
0184     * (default for Radian: north pole at pi/2, southpole at -pi/2)
0185     * @return latitude
0186     */
0187     qreal latitude( GeoDataCoordinates::Unit unit ) const;
0188     qreal latitude() const;
0189 
0190     /**
0191     * @brief set the longitude in a GeoDataCoordinates object
0192     * @param lat longitude
0193     * @param unit units that lon and lat get measured in
0194     * (default for Radian: north pole at pi/2, southpole at -pi/2)
0195     */
0196     void setLatitude( qreal lat,
0197               GeoDataCoordinates::Unit unit = GeoDataCoordinates::Radian );
0198 
0199     /**
0200         * @brief return the altitude of the Point in meters
0201         */
0202     qreal altitude() const;
0203     /**
0204     * @brief set the altitude of the Point in meters
0205     * @param altitude altitude
0206     */
0207     void setAltitude( const qreal altitude );
0208 
0209     /**
0210     * @brief retrieves the UTM zone of the GeoDataCoordinates object.
0211     * If the point is located on one of the poles (latitude < 80S or
0212     * latitude > 84N) there is no UTM zone associated; in this case,
0213     * 0 is returned.
0214     * @return UTM zone.
0215     */
0216     int utmZone() const;
0217 
0218     /**
0219     * @brief retrieves the UTM easting of the GeoDataCoordinates object,
0220     * in meters.
0221     * @return UTM easting
0222     */
0223     qreal utmEasting() const;
0224 
0225     /**
0226     * @brief retrieves the UTM latitude band of the GeoDataCoordinates object
0227     * @return UTM latitude band
0228     */
0229     QString utmLatitudeBand() const;
0230 
0231     /**
0232     * @brief retrieves the UTM northing of the GeoDataCoordinates object,
0233     * in meters
0234     * @return UTM northing
0235     */
0236     qreal utmNorthing() const;
0237 
0238     /**
0239     * @brief return the detail flag
0240     * detail range: 0 for most important points, 5 for least important
0241     */
0242     quint8 detail() const;
0243 
0244     /**
0245     * @brief set the detail flag
0246     * @param detail detail
0247     */
0248     void setDetail(quint8 detail);
0249 
0250     /**
0251      * @brief Rotates one coordinate around another.
0252      * @param axis The coordinate that serves as a rotation axis
0253      * @param angle Rotation angle
0254      * @param unit Unit of the result
0255      * @return The coordinate rotated in anticlockwise direction
0256      */
0257     GeoDataCoordinates rotateAround( const GeoDataCoordinates &axis, qreal angle, Unit unit = Radian ) const;
0258 
0259     GeoDataCoordinates rotateAround(const Quaternion &rotAxis) const;
0260 
0261     /**
0262      * @brief Returns the bearing (true bearing, the angle between the line defined
0263      * by this point and the other and the prime meridian)
0264      * @param other The second point that, together with this point, defines a line
0265      * @param unit Unit of the result
0266      * @param type Type of the bearing
0267      * @return The true bearing in the requested unit, not range normalized,
0268      * in clockwise direction, with the value 0 corresponding to north
0269      */
0270     qreal bearing( const GeoDataCoordinates &other, Unit unit = Radian, BearingType type = InitialBearing ) const;
0271 
0272     /**
0273      * @brief Returns the coordinates of the resulting point after moving this point
0274      * according to the distance and bearing parameters
0275      * @param bearing the same as above
0276      * @param distance the distance on a unit sphere
0277      */
0278     GeoDataCoordinates moveByBearing( qreal bearing, qreal distance ) const;
0279 
0280     /**
0281     * @brief return a Quaternion with the used coordinates
0282     */
0283     const Quaternion &quaternion() const;
0284 
0285     /**
0286      * @brief slerp (spherical linear) interpolation between this coordinate and the given target coordinate
0287      * @param target Destination coordinate
0288      * @param t Fraction 0..1 to weight between this and target
0289      * @return Interpolated coordinate between this (t<=0.0) and target (t>=1.0)
0290      */
0291     GeoDataCoordinates interpolate( const GeoDataCoordinates &target, double t ) const;
0292 
0293     /**
0294      * @brief nlerp (normalized linear interpolation) between this coordinates and the given target coordinates
0295      * @param target Destination coordinates
0296      * @param t Fraction 0..1 to weight between this and target
0297      * @return Interpolated coordinate between this (t<=0.0) and target (t>=1.0)
0298      */
0299     GeoDataCoordinates nlerp(const GeoDataCoordinates &target, double t) const;
0300 
0301     /**
0302      * @brief squad (spherical and quadrangle) interpolation between b and c
0303      * @param before First base point
0304      * @param target Third base point (second interpolation point)
0305      * @param after Fourth base point
0306      * @param t Offset between b (t<=0) and c (t>=1)
0307      */
0308     GeoDataCoordinates interpolate( const GeoDataCoordinates &before, const GeoDataCoordinates &target, const GeoDataCoordinates &after, double t ) const;
0309 
0310     /**
0311     * @brief return whether our coordinates represent a pole
0312     * This method can be used to check whether the coordinate equals one of 
0313     * the poles. 
0314     */
0315     bool isPole( Pole = AnyPole ) const;
0316 
0317     /**
0318      * @brief This method calculates the shortest distance between two points on a sphere.
0319      * @brief See: https://en.wikipedia.org/wiki/Great-circle_distance
0320      */
0321     qreal sphericalDistanceTo(const GeoDataCoordinates &other) const;
0322 
0323     /**
0324     * @brief return Notation of string representation
0325     */
0326     static GeoDataCoordinates::Notation defaultNotation();
0327 
0328     /**
0329     * @brief set the Notation of the string representation
0330     * @param notation Notation
0331     */
0332     static void setDefaultNotation( GeoDataCoordinates::Notation notation );
0333 
0334     /**
0335      * @brief normalize the longitude to always be -M_PI <= lon <= +M_PI (Radian).
0336      * @param lon longitude
0337      * @param unit unit of the result
0338      */
0339     static qreal normalizeLon( qreal lon,
0340                                GeoDataCoordinates::Unit = GeoDataCoordinates::Radian );
0341 
0342     /**
0343      * @brief normalize latitude to always be in -M_PI / 2. <= lat <= +M_PI / 2 (Radian).
0344      * @param lat latitude
0345      * @param unit unit of the result
0346      */
0347     static qreal normalizeLat( qreal lat,
0348                                GeoDataCoordinates::Unit = GeoDataCoordinates::Radian );
0349 
0350     /**
0351      * @brief normalize both longitude and latitude at the same time
0352      * This method normalizes both latitude and longitude, so that the 
0353      * latitude and the longitude stay within the "usual" range.
0354      * NOTE: If the latitude exceeds M_PI/2 (+90.0 deg) or -M_PI/2 (-90.0 deg)
0355      * then this will be interpreted as a pole traversion where the point will  
0356      * end up on the opposite side of the globe. Therefore the longitude will 
0357      * change by M_PI (180 deg).
0358      * If you don't want this behaviour use both normalizeLat() and
0359      * normalizeLon() instead.  
0360      * @param lon the longitude value
0361      * @param lat the latitude value
0362      * @param unit unit of the result
0363      */
0364     static void normalizeLonLat( qreal &lon, qreal &lat,
0365                                  GeoDataCoordinates::Unit = GeoDataCoordinates::Radian );
0366     
0367     /**
0368      * @brief try to parse the string into a coordinate pair
0369      * @param string the string
0370      * @param successful becomes true if the conversion succeeds
0371      * @return the geodatacoordinates
0372      */     
0373     static GeoDataCoordinates fromString( const QString &string, bool& successful );
0374      
0375     /**
0376     * @brief return a string representation of the coordinate
0377     * this is a convenience function which uses the default notation
0378     */
0379     QString toString() const;
0380 
0381     /**
0382     * @brief return a string with the notation given by notation
0383     *
0384     * @param notation set a notation different from the default one
0385     * @param precision set the number of digits below degrees.
0386     * The precision depends on the current notation: 
0387     * For Decimal representation the precision is the number of 
0388     * digits after the decimal point.
0389     * In DMS a precision of 1 or 2 shows the arc minutes; a precision
0390     * of 3 or 4 will show arc seconds. A precision beyond that will 
0391     * increase the number of digits after the arc second decimal point. 
0392     */
0393     QString toString( GeoDataCoordinates::Notation notation, int precision = -1 ) const;
0394     
0395     static QString lonToString( qreal lon, GeoDataCoordinates::Notation notation,   
0396                                            GeoDataCoordinates::Unit unit = Radian, 
0397                                            int precision = -1, 
0398                                            char format = 'f' );
0399     /**
0400      * @brief return a string representation of longitude of the coordinate
0401      * convenience function that uses the default notation
0402      */
0403     QString lonToString() const;
0404 
0405     static QString latToString( qreal lat, GeoDataCoordinates::Notation notation,
0406                                            GeoDataCoordinates::Unit unit = Radian,
0407                                            int precision = -1,
0408                                            char format = 'f' );
0409     /**
0410      * @brief return a string representation of latitude of the coordinate
0411      * convenience function that uses the default notation
0412      */
0413     QString latToString() const;
0414 
0415     bool operator==(const GeoDataCoordinates &other) const;
0416     bool operator!=(const GeoDataCoordinates &other) const;
0417 
0418     GeoDataCoordinates& operator=( const GeoDataCoordinates &other );
0419 
0420     /** Serialize the contents of the feature to @p stream. */
0421     void pack(QDataStream &stream) const;
0422     /** Unserialize the contents of the feature from @p stream. */
0423     void unpack(QDataStream &stream);
0424 
0425  private:
0426     void detach();
0427 
0428     GeoDataCoordinatesPrivate *d;
0429 
0430     static GeoDataCoordinates::Notation s_notation;
0431     static const GeoDataCoordinates null;
0432 };
0433 
0434 GEODATA_EXPORT uint qHash(const GeoDataCoordinates& coordinates );
0435 
0436 
0437 }
0438 
0439 Q_DECLARE_METATYPE( Marble::GeoDataCoordinates )
0440 
0441 #endif