1 // SPDX-License-Identifier: LGPL-2.1-or-later 2 // 3 // SPDX-FileCopyrightText: 2006-2007 Torsten Rahn <tackat@kde.org> 4 // SPDX-FileCopyrightText: 2007-2008 Inge Wallin <ingwa@kde.org> 5 // SPDX-FileCopyrightText: 2008 Patrick Spendrin <ps_ml@gmx.de> 6 // SPDX-FileCopyrightText: 2015 Alejandro Garcia Montoro <alejandro.garciamontoro@gmail.com> 7 // 8 9 10 #ifndef MARBLE_GEODATACOORDINATES_H 11 #define MARBLE_GEODATACOORDINATES_H 12 13 #include <QCoreApplication> 14 #include <QMetaType> 15 #include <QVector> 16 17 #include "geodata_export.h" 18 #include "MarbleGlobal.h" 19 20 class QString; 21 22 namespace Marble 23 { 24 25 class GeoDataCoordinatesPrivate; 26 class Quaternion; 27 28 /** 29 * @short A 3d point representation 30 * 31 * GeoDataCoordinates is the simple representation of a single three 32 * dimensional point. It can be used all through out marble as the data type 33 * for three dimensional objects. it comprises of a Quaternion for speed issues. 34 * This class was introduced to reflect the difference between a simple 3d point 35 * and the GeoDataGeometry object containing such a point. The latter is a 36 * GeoDataPoint and is simply derived from GeoDataCoordinates. 37 * @see GeoDataPoint 38 */ 39 40 class GEODATA_EXPORT GeoDataCoordinates 41 { 42 Q_DECLARE_TR_FUNCTIONS(GeoDataCoordinates) 43 44 public: 45 /** 46 * @brief enum used constructor to specify the units used 47 * 48 * Internally we always use radian for mathematical convenience. 49 * However the Marble's interfaces to the outside should default 50 * to degrees. 51 */ 52 enum Unit{ 53 Radian, 54 Degree 55 }; 56 57 /** 58 * @brief enum used to specify the notation / numerical system 59 * 60 * For degrees there exist two notations: 61 * "Decimal" (base-10) and the "Sexagesimal DMS" (base-60) which is 62 * traditionally used in cartography. Decimal notation 63 * uses floating point numbers to specify parts of a degree. The 64 * Sexagesimal DMS notation uses integer based 65 * Degrees-(Arc)Minutes-(Arc)Seconds to describe parts of a degree. 66 */ 67 enum Notation{ 68 Decimal, ///< "Decimal" notation (base-10) 69 DMS, ///< "Sexagesimal DMS" notation (base-60) 70 DM, ///< "Sexagesimal DM" notation (base-60) 71 UTM, 72 Astro /// < "RA and DEC" notation (used for astronomical sky coordinates) 73 }; 74 75 /** 76 * @brief The BearingType enum specifies where to measure the bearing 77 * along great circle arcs 78 * 79 * When traveling along a great circle arc defined by the two points 80 * A and B, the bearing varies along the arc. The "InitialBearing" bearing 81 * corresponds to the bearing value at A, the "FinalBearing" bearing to that 82 * at B. 83 */ 84 enum BearingType { 85 InitialBearing, 86 FinalBearing 87 }; 88 89 // Type definitions 90 using Vector = QVector<GeoDataCoordinates>; 91 using PtrVector = QVector<GeoDataCoordinates *>; 92 93 GeoDataCoordinates( const GeoDataCoordinates& other ); 94 95 /** 96 * @brief constructs an invalid instance 97 * 98 * Constructs an invalid instance such that calling isValid() 99 * on it will return @code false @endcode. 100 */ 101 GeoDataCoordinates(); 102 103 /** 104 * @brief create a geocoordinate from longitude and latitude 105 * @param lon longitude 106 * @param lat latitude 107 * @param alt altitude in meters (default: 0) 108 * @param unit units that lon and lat get measured in 109 * (default for Radian: north pole at pi/2, southpole at -pi/2) 110 * @param detail detail (default: 0) 111 */ 112 GeoDataCoordinates( qreal lon, qreal lat, qreal alt = 0, 113 GeoDataCoordinates::Unit unit = GeoDataCoordinates::Radian, 114 int detail = 0 ); 115 116 virtual ~GeoDataCoordinates(); 117 118 /** 119 * @brief Returns @code true @endcode if the coordinate is valid, @code false @endcode otherwise. 120 * @return whether the coordinate is valid 121 * 122 * A coordinate is valid, if at least one component has been set and the last 123 * assignment was not an invalid GeoDataCoordinates object. 124 */ 125 bool isValid() const; 126 127 /** 128 * @brief (re)set the coordinates in a GeoDataCoordinates object 129 * @param lon longitude 130 * @param lat latitude 131 * @param alt altitude in meters (default: 0) 132 * @param unit units that lon and lat get measured in 133 * (default for Radian: north pole at pi/2, southpole at -pi/2) 134 */ 135 void set( qreal lon, qreal lat, qreal alt = 0, 136 GeoDataCoordinates::Unit unit = GeoDataCoordinates::Radian ); 137 138 /** 139 * @brief use this function to get the longitude and latitude with one 140 * call - use the unit parameter to switch between Radian and DMS 141 * @param lon longitude 142 * @param lat latitude 143 * @param unit units that lon and lat get measured in 144 * (default for Radian: north pole at pi/2, southpole at -pi/2) 145 */ 146 void geoCoordinates(qreal& lon, qreal& lat, GeoDataCoordinates::Unit unit) const; 147 void geoCoordinates(qreal& lon, qreal& lat) const; 148 149 /** 150 * @brief use this function to get the longitude, latitude and altitude 151 * with one call - use the unit parameter to switch between Radian and DMS 152 * @param lon longitude 153 * @param lat latitude 154 * @param alt altitude in meters 155 * @param unit units that lon and lat get measured in 156 * (default for Radian: north pole at pi/2, southpole at -pi/2) 157 */ 158 void geoCoordinates(qreal& lon, qreal& lat, qreal& alt, GeoDataCoordinates::Unit unit) const; 159 void geoCoordinates(qreal& lon, qreal& lat, qreal& alt) const; 160 161 /** 162 * @brief set the longitude in a GeoDataCoordinates object 163 * @param lon longitude 164 * @param unit units that lon and lat get measured in 165 * (default for Radian: north pole at pi/2, southpole at -pi/2) 166 */ 167 void setLongitude( qreal lon, 168 GeoDataCoordinates::Unit unit = GeoDataCoordinates::Radian ); 169 170 /** 171 * @brief retrieves the longitude of the GeoDataCoordinates object 172 * use the unit parameter to switch between Radian and DMS 173 * @param unit units that lon and lat get measured in 174 * (default for Radian: north pole at pi/2, southpole at -pi/2) 175 * @return longitude 176 */ 177 qreal longitude(GeoDataCoordinates::Unit unit) const; 178 qreal longitude() const; 179 180 /** 181 * @brief retrieves the latitude of the GeoDataCoordinates object 182 * use the unit parameter to switch between Radian and DMS 183 * @param unit units that lon and lat get measured in 184 * (default for Radian: north pole at pi/2, southpole at -pi/2) 185 * @return latitude 186 */ 187 qreal latitude( GeoDataCoordinates::Unit unit ) const; 188 qreal latitude() const; 189 190 /** 191 * @brief set the longitude in a GeoDataCoordinates object 192 * @param lat longitude 193 * @param unit units that lon and lat get measured in 194 * (default for Radian: north pole at pi/2, southpole at -pi/2) 195 */ 196 void setLatitude( qreal lat, 197 GeoDataCoordinates::Unit unit = GeoDataCoordinates::Radian ); 198 199 /** 200 * @brief return the altitude of the Point in meters 201 */ 202 qreal altitude() const; 203 /** 204 * @brief set the altitude of the Point in meters 205 * @param altitude altitude 206 */ 207 void setAltitude( const qreal altitude ); 208 209 /** 210 * @brief retrieves the UTM zone of the GeoDataCoordinates object. 211 * If the point is located on one of the poles (latitude < 80S or 212 * latitude > 84N) there is no UTM zone associated; in this case, 213 * 0 is returned. 214 * @return UTM zone. 215 */ 216 int utmZone() const; 217 218 /** 219 * @brief retrieves the UTM easting of the GeoDataCoordinates object, 220 * in meters. 221 * @return UTM easting 222 */ 223 qreal utmEasting() const; 224 225 /** 226 * @brief retrieves the UTM latitude band of the GeoDataCoordinates object 227 * @return UTM latitude band 228 */ 229 QString utmLatitudeBand() const; 230 231 /** 232 * @brief retrieves the UTM northing of the GeoDataCoordinates object, 233 * in meters 234 * @return UTM northing 235 */ 236 qreal utmNorthing() const; 237 238 /** 239 * @brief return the detail flag 240 * detail range: 0 for most important points, 5 for least important 241 */ 242 quint8 detail() const; 243 244 /** 245 * @brief set the detail flag 246 * @param detail detail 247 */ 248 void setDetail(quint8 detail); 249 250 /** 251 * @brief Rotates one coordinate around another. 252 * @param axis The coordinate that serves as a rotation axis 253 * @param angle Rotation angle 254 * @param unit Unit of the result 255 * @return The coordinate rotated in anticlockwise direction 256 */ 257 GeoDataCoordinates rotateAround( const GeoDataCoordinates &axis, qreal angle, Unit unit = Radian ) const; 258 259 GeoDataCoordinates rotateAround(const Quaternion &rotAxis) const; 260 261 /** 262 * @brief Returns the bearing (true bearing, the angle between the line defined 263 * by this point and the other and the prime meridian) 264 * @param other The second point that, together with this point, defines a line 265 * @param unit Unit of the result 266 * @param type Type of the bearing 267 * @return The true bearing in the requested unit, not range normalized, 268 * in clockwise direction, with the value 0 corresponding to north 269 */ 270 qreal bearing( const GeoDataCoordinates &other, Unit unit = Radian, BearingType type = InitialBearing ) const; 271 272 /** 273 * @brief Returns the coordinates of the resulting point after moving this point 274 * according to the distance and bearing parameters 275 * @param bearing the same as above 276 * @param distance the distance on a unit sphere 277 */ 278 GeoDataCoordinates moveByBearing( qreal bearing, qreal distance ) const; 279 280 /** 281 * @brief return a Quaternion with the used coordinates 282 */ 283 const Quaternion &quaternion() const; 284 285 /** 286 * @brief slerp (spherical linear) interpolation between this coordinate and the given target coordinate 287 * @param target Destination coordinate 288 * @param t Fraction 0..1 to weight between this and target 289 * @return Interpolated coordinate between this (t<=0.0) and target (t>=1.0) 290 */ 291 GeoDataCoordinates interpolate( const GeoDataCoordinates &target, double t ) const; 292 293 /** 294 * @brief nlerp (normalized linear interpolation) between this coordinates and the given target coordinates 295 * @param target Destination coordinates 296 * @param t Fraction 0..1 to weight between this and target 297 * @return Interpolated coordinate between this (t<=0.0) and target (t>=1.0) 298 */ 299 GeoDataCoordinates nlerp(const GeoDataCoordinates &target, double t) const; 300 301 /** 302 * @brief squad (spherical and quadrangle) interpolation between b and c 303 * @param before First base point 304 * @param target Third base point (second interpolation point) 305 * @param after Fourth base point 306 * @param t Offset between b (t<=0) and c (t>=1) 307 */ 308 GeoDataCoordinates interpolate( const GeoDataCoordinates &before, const GeoDataCoordinates &target, const GeoDataCoordinates &after, double t ) const; 309 310 /** 311 * @brief return whether our coordinates represent a pole 312 * This method can be used to check whether the coordinate equals one of 313 * the poles. 314 */ 315 bool isPole( Pole = AnyPole ) const; 316 317 /** 318 * @brief This method calculates the shortest distance between two points on a sphere. 319 * @brief See: https://en.wikipedia.org/wiki/Great-circle_distance 320 */ 321 qreal sphericalDistanceTo(const GeoDataCoordinates &other) const; 322 323 /** 324 * @brief return Notation of string representation 325 */ 326 static GeoDataCoordinates::Notation defaultNotation(); 327 328 /** 329 * @brief set the Notation of the string representation 330 * @param notation Notation 331 */ 332 static void setDefaultNotation( GeoDataCoordinates::Notation notation ); 333 334 /** 335 * @brief normalize the longitude to always be -M_PI <= lon <= +M_PI (Radian). 336 * @param lon longitude 337 * @param unit unit of the result 338 */ 339 static qreal normalizeLon( qreal lon, 340 GeoDataCoordinates::Unit = GeoDataCoordinates::Radian ); 341 342 /** 343 * @brief normalize latitude to always be in -M_PI / 2. <= lat <= +M_PI / 2 (Radian). 344 * @param lat latitude 345 * @param unit unit of the result 346 */ 347 static qreal normalizeLat( qreal lat, 348 GeoDataCoordinates::Unit = GeoDataCoordinates::Radian ); 349 350 /** 351 * @brief normalize both longitude and latitude at the same time 352 * This method normalizes both latitude and longitude, so that the 353 * latitude and the longitude stay within the "usual" range. 354 * NOTE: If the latitude exceeds M_PI/2 (+90.0 deg) or -M_PI/2 (-90.0 deg) 355 * then this will be interpreted as a pole traversion where the point will 356 * end up on the opposite side of the globe. Therefore the longitude will 357 * change by M_PI (180 deg). 358 * If you don't want this behaviour use both normalizeLat() and 359 * normalizeLon() instead. 360 * @param lon the longitude value 361 * @param lat the latitude value 362 * @param unit unit of the result 363 */ 364 static void normalizeLonLat( qreal &lon, qreal &lat, 365 GeoDataCoordinates::Unit = GeoDataCoordinates::Radian ); 366 367 /** 368 * @brief try to parse the string into a coordinate pair 369 * @param string the string 370 * @param successful becomes true if the conversion succeeds 371 * @return the geodatacoordinates 372 */ 373 static GeoDataCoordinates fromString( const QString &string, bool& successful ); 374 375 /** 376 * @brief return a string representation of the coordinate 377 * this is a convenience function which uses the default notation 378 */ 379 QString toString() const; 380 381 /** 382 * @brief return a string with the notation given by notation 383 * 384 * @param notation set a notation different from the default one 385 * @param precision set the number of digits below degrees. 386 * The precision depends on the current notation: 387 * For Decimal representation the precision is the number of 388 * digits after the decimal point. 389 * In DMS a precision of 1 or 2 shows the arc minutes; a precision 390 * of 3 or 4 will show arc seconds. A precision beyond that will 391 * increase the number of digits after the arc second decimal point. 392 */ 393 QString toString( GeoDataCoordinates::Notation notation, int precision = -1 ) const; 394 395 static QString lonToString( qreal lon, GeoDataCoordinates::Notation notation, 396 GeoDataCoordinates::Unit unit = Radian, 397 int precision = -1, 398 char format = 'f' ); 399 /** 400 * @brief return a string representation of longitude of the coordinate 401 * convenience function that uses the default notation 402 */ 403 QString lonToString() const; 404 405 static QString latToString( qreal lat, GeoDataCoordinates::Notation notation, 406 GeoDataCoordinates::Unit unit = Radian, 407 int precision = -1, 408 char format = 'f' ); 409 /** 410 * @brief return a string representation of latitude of the coordinate 411 * convenience function that uses the default notation 412 */ 413 QString latToString() const; 414 415 bool operator==(const GeoDataCoordinates &other) const; 416 bool operator!=(const GeoDataCoordinates &other) const; 417 418 GeoDataCoordinates& operator=( const GeoDataCoordinates &other ); 419 420 /** Serialize the contents of the feature to @p stream. */ 421 void pack(QDataStream &stream) const; 422 /** Unserialize the contents of the feature from @p stream. */ 423 void unpack(QDataStream &stream); 424 425 private: 426 void detach(); 427 428 GeoDataCoordinatesPrivate *d; 429 430 static GeoDataCoordinates::Notation s_notation; 431 static const GeoDataCoordinates null; 432 }; 433 434 GEODATA_EXPORT uint qHash(const GeoDataCoordinates& coordinates ); 435 436 437 } 438 439 Q_DECLARE_METATYPE( Marble::GeoDataCoordinates ) 440 441 #endif 442