File indexing completed on 2025-01-05 03:56:26
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2008-05-05 0007 * Description : Geodetic tools 0008 * 0009 * SPDX-FileCopyrightText: 2008-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0010 * SPDX-FileCopyrightText: 2015-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #ifndef DIGIKAM_GEODETIC_TOOLS_H 0017 #define DIGIKAM_GEODETIC_TOOLS_H 0018 0019 // C++ includes 0020 0021 #include <cmath> 0022 0023 // Qt includes 0024 0025 #include <QString> 0026 #include <QPointF> 0027 0028 // Local includes 0029 0030 #include "digikam_export.h" 0031 0032 namespace Digikam 0033 { 0034 0035 namespace Coordinates 0036 { 0037 0038 /// Converting between radians and degrees 0039 0040 inline double toRadians(double deg) 0041 { 0042 return (deg * M_PI / 180.0); 0043 } 0044 0045 inline double toRadiansFactor() 0046 { 0047 return (M_PI / 180.0); 0048 } 0049 0050 inline double toDegrees(double rad) 0051 { 0052 return (rad * 180.0 / M_PI); 0053 } 0054 0055 inline double toDegreesFactor() 0056 { 0057 return (180.0 / M_PI); 0058 } 0059 0060 } // namespace Coordinates 0061 0062 // ------------------------------------------------------------------------------------------------ 0063 0064 /** 0065 * Geometric figure that can be used to describe the approximate shape of the earth. 0066 * In mathematical terms, it is a surface formed by the rotation of an ellipse about 0067 * its minor axis. An ellipsoid requires two defining parameters: 0068 * - semi-major axis and inverse flattening, or 0069 * - semi-major axis and semi-minor axis. 0070 */ 0071 class DIGIKAM_EXPORT Ellipsoid 0072 { 0073 0074 public: 0075 0076 /** 0077 * WGS 1984 ellipsoid with axis in metres. This ellipsoid is used 0078 * in GPS systems and is the default for most <code>org.geotools</code> packages. 0079 */ 0080 static Ellipsoid WGS84(); 0081 0082 /** 0083 * GRS 80 ellipsoid with axis in metres. 0084 */ 0085 static Ellipsoid GRS80(); 0086 0087 /** 0088 * International 1924 ellipsoid with axis in metres. 0089 */ 0090 static Ellipsoid INTERNATIONAL_1924(); 0091 0092 /** 0093 * Clarke 1866 ellipsoid with axis in metres. 0094 */ 0095 static Ellipsoid CLARKE_1866(); 0096 0097 /** 0098 * A sphere with a radius of 6371000 metres. Spheres use a simpler 0099 * algorithm for orthodromic distance computation, which 0100 * may be faster and more robust. 0101 */ 0102 static Ellipsoid SPHERE(); 0103 0104 /** 0105 * Constructs a new ellipsoid using the specified axis length. 0106 * 0107 * @param name The ellipsoid name. 0108 * @param semiMajorAxis The equatorial radius. 0109 * @param semiMinorAxis The polar radius. 0110 */ 0111 static Ellipsoid createEllipsoid(const QString& name, 0112 double semiMajorAxis, 0113 double semiMinorAxis); 0114 0115 /** 0116 * Constructs a new ellipsoid using the specified axis length and inverse flattening value. 0117 * 0118 * @param name The ellipsoid name. 0119 * @param semiMajorAxis The equatorial radius. 0120 * @param inverseFlattening The inverse flattening value. 0121 * values. 0122 */ 0123 static Ellipsoid createFlattenedSphere(const QString& name, 0124 double semiMajorAxis, 0125 double inverseFlattening); 0126 0127 /** 0128 * Length of the semi-major axis of the ellipsoid. This is the 0129 * equatorial radius in axis linear unit. 0130 * 0131 * @return Length of semi-major axis. 0132 */ 0133 double semiMajorAxis() const; 0134 0135 /** 0136 * Length of the semi-minor axis of the ellipsoid. This is the 0137 * polar radius in axis linear unit. 0138 * 0139 * @return Length of semi-minor axis. 0140 */ 0141 double semiMinorAxis() const; 0142 0143 /** 0144 * The ratio of the distance between the center and a focus of the ellipse 0145 * to the length of its semimajor axis. The eccentricity can alternately be 0146 * computed from the equation: e=sqrt(2f-f^2). 0147 */ 0148 double eccentricity() const; 0149 0150 /** 0151 * Returns the value of the inverse of the flattening constant. Flattening is a value 0152 * used to indicate how closely an ellipsoid approaches a spherical shape. The inverse 0153 * flattening is related to the equatorial/polar radius by the formula 0154 * 0155 * ivf=r_e/(r_e-r_p). 0156 * 0157 * For perfect spheres (i.e. if isSphere returns @c true), 0158 * the DoublePOSITIVE_INFINITY value is used. 0159 * 0160 * @return The inverse flattening value. 0161 */ 0162 double inverseFlattening() const; 0163 0164 /** 0165 * Indicates if the inverse flattening is definitive for 0166 * this ellipsoid. Some ellipsoids use the IVF as the defining value, and calculate the polar 0167 * radius whenever asked. Other ellipsoids use the polar radius to calculate the IVF whenever 0168 * asked. This distinction can be important to avoid floating-point rounding errors. 0169 * 0170 * @return @c true if the inverse flattening is 0171 * definitive, or @c false if the polar radius 0172 * is definitive. 0173 */ 0174 bool isIvfDefinitive() const; 0175 0176 /** 0177 * @c true if the ellipsoid is degenerate and is actually a sphere. The sphere is 0178 * completely defined by the semi-major axis, which is the 0179 * radius of the sphere. 0180 * 0181 * @return @c true if the ellipsoid is degenerate and is actually a sphere. 0182 */ 0183 bool isSphere() const; 0184 0185 /** 0186 * Returns the orthodromic distance between two geographic coordinates. 0187 * The orthodromic distance is the shortest distance between two points 0188 * on a sphere's surface. The orthodromic path is always on a great circle. 0189 * This is different from the loxodromic distance, which is a 0190 * longer distance on a path with a constant direction on the compass. 0191 * 0192 * @param x1 Longitude of first point (in decimal degrees). 0193 * @param y1 Latitude of first point (in decimal degrees). 0194 * @param x2 Longitude of second point (in decimal degrees). 0195 * @param y2 Latitude of second point (in decimal degrees). 0196 * @return The orthodromic distance (in the units of this ellipsoid's axis). 0197 */ 0198 double orthodromicDistance(double x1, double y1, double x2, double y2); 0199 0200 /** 0201 * Returns the Radius Of Curvature for the given latitude, 0202 * using the geometric mean of two radii of 0203 * curvature for all azimuths. 0204 * @param latitude in degrees 0205 */ 0206 double radiusOfCurvature(double latitude); 0207 0208 protected: 0209 0210 /** 0211 * Constructs a new ellipsoid using the specified axis length. The properties map is 0212 * given unchanged to the AbstractIdentifiedObjectAbstractIdentifiedObject(Map) 0213 * super-class constructor. 0214 * 0215 * @param semiMajorAxis The equatorial radius. 0216 * @param semiMinorAxis The polar radius. 0217 * @param inverseFlattening The inverse of the flattening value. 0218 * @param ivfDefinitive @c true if the inverse flattening is definitive. 0219 * 0220 * @see createEllipsoid 0221 * @see createFlattenedSphere 0222 */ 0223 Ellipsoid(const QString& name, 0224 double semiMajorAxis, 0225 double semiMinorAxis, 0226 double inverseFlattening, 0227 bool ivfDefinitive); 0228 Ellipsoid(const QString& name, 0229 double radius, 0230 bool ivfDefinitive); 0231 0232 protected: 0233 0234 QString name; 0235 0236 /** 0237 * The equatorial radius. 0238 * @see getSemiMajorAxis 0239 */ 0240 double m_semiMajorAxis; 0241 0242 /** 0243 * The polar radius. 0244 * @see getSemiMinorAxis 0245 */ 0246 double m_semiMinorAxis; 0247 0248 /** 0249 * The inverse of the flattening value, or DBL_MAX 0250 * if the ellipsoid is a sphere. 0251 * 0252 * @see getInverseFlattening 0253 */ 0254 double m_inverseFlattening; 0255 0256 /** 0257 * Tells if the Inverse Flattening definitive for this ellipsoid. 0258 * 0259 * @see isIvfDefinitive 0260 */ 0261 bool m_ivfDefinitive; 0262 0263 bool m_isSphere; 0264 }; 0265 0266 // ------------------------------------------------------------------------------------------------ 0267 0268 class DIGIKAM_EXPORT GeodeticCalculator 0269 { 0270 /** 0271 * Performs geodetic calculations on an ellipsoid. This class encapsulates 0272 * a generic ellipsoid and calculates the following properties: 0273 * 0274 * 0275 * Distance and azimuth between two points. 0276 * Point located at a given distance and azimuth from an other point. 0277 * 0278 * 0279 * The calculation use the following information: 0280 * 0281 * 0282 * The starting position (setStartingPosition), which is always considered valid. 0283 * It is initially set at (0,0) and can only be changed to another legitimate value. 0284 * Only one of the following: 0285 * 0286 * The destination position (setDestinationPosition), or 0287 * An azimuth and distance (setDirection). 0288 * 0289 * The latest one set overrides the other and determines what will be calculated. 0290 * 0291 * 0292 */ 0293 0294 public: 0295 0296 explicit GeodeticCalculator(const Ellipsoid& e = Ellipsoid::WGS84()); 0297 0298 /** 0299 * Returns the referenced ellipsoid. 0300 */ 0301 Ellipsoid ellipsoid() const; 0302 0303 /** 0304 * Set the starting point in geographic coordinates. 0305 * The azimuth, the orthodromic distance and the destination point 0306 * are discarded. They will need to be specified again. 0307 * Coordinates positive North and East. 0308 * 0309 * @param longitude The longitude in decimal degrees between -180 and +180° 0310 * @param latitude The latitude in decimal degrees between -90 and +90° 0311 */ 0312 void setStartingGeographicPoint(double longitude, double latitude); 0313 0314 /** 0315 * Set the destination point in geographic coordinates. The azimuth and distance values 0316 * will be updated as a side effect of this call. They will be recomputed the next time 0317 * getAzimuth() or getOrthodromicDistance() are invoked. 0318 * Coordinates positive North and East. 0319 * 0320 * @param longitude The longitude in decimal degrees between -180 and +180° 0321 * @param latitude The latitude in decimal degrees between -90 and +90° 0322 * 0323 */ 0324 void setDestinationGeographicPoint(double longitude, double latitude); 0325 0326 /** 0327 * Returns the destination point. This method returns the point set by the last 0328 * call to a setDestinationGeographicPoint(...) 0329 * method, except if setDirection(...) has been 0330 * invoked after. In this later case, the destination point will be computed from the 0331 * starting point to the azimuth and distance specified. 0332 * Coordinates positive North and East. 0333 * 0334 * @return The destination point. The x and y coordinates 0335 * are the longitude and latitude in decimal degrees, respectively. 0336 */ 0337 bool destinationGeographicPoint(double* longitude, double* latitude); 0338 QPointF destinationGeographicPoint(); 0339 0340 /** 0341 * Set the azimuth and the distance from the startingGeographicPoint 0342 * starting point. The destination point will be updated as a side effect of this call. 0343 * It will be recomputed the next time destinationGeographicPoint() is invoked. 0344 * Azimuth 0° North. 0345 * 0346 * @param azimuth The azimuth in decimal degrees from -180° to 180°. 0347 * @param distance The orthodromic distance in the same units as the ellipsoid axis. 0348 */ 0349 void setDirection(double azimuth, double distance); 0350 0351 /** 0352 * Returns the azimuth. This method returns the value set by the last call to 0353 * <code>setDirection(double,double) setDirection(azimuth,distance)</code>, 0354 * except if <code>setDestinationGeographicPoint(double,double) 0355 * setDestinationGeographicPoint(...)</code> has been invoked after. In this later case, the 0356 * azimuth will be computed from the startingGeographicPoint starting point 0357 * to the destination point. 0358 * 0359 * @return The azimuth, in decimal degrees from -180° to +180°. 0360 */ 0361 double azimuth(); 0362 0363 /** 0364 * Returns the orthodromic distance. This method returns the value set by the last call to 0365 * <code>setDirection(double,double) setDirection(azimuth,distance)</code>, 0366 * except if <code>setDestinationGeographicPoint(double,double) 0367 * setDestinationGeographicPoint(...)</code> has been invoked after. In this later case, the 0368 * distance will be computed from the startingGeographicPoint starting point 0369 * to the destination point. 0370 * 0371 * @return The orthodromic distance, in the same units as the 0372 * getEllipsoid ellipsoid axis. 0373 */ 0374 double orthodromicDistance(); 0375 0376 /** 0377 * Computes the orthodromic distance using the algorithm implemented in the Geotools's 0378 * ellipsoid class (if available), and check if the error is smaller than some tolerance 0379 * error. 0380 */ 0381 bool checkOrthodromicDistance(); 0382 0383 /** 0384 * Computes the destination point from the starting 0385 * point, the azimuth and the orthodromic distance. 0386 */ 0387 bool computeDestinationPoint(); 0388 0389 /** 0390 * Calculates the meridian arc length between two points in the same meridian 0391 * in the referenced ellipsoid. 0392 * 0393 * @param latitude1 The latitude of the first point (in decimal degrees). 0394 * @param latitude2 The latitude of the second point (in decimal degrees). 0395 * @return Returned the meridian arc length between latitude1 and latitude2 0396 */ 0397 double meridianArcLength(double latitude1, double latitude2); 0398 0399 /** 0400 * Calculates the meridian arc length between two points in the same meridian 0401 * in the referenced ellipsoid. 0402 * 0403 * @param P1 The latitude of the first point (in radians). 0404 * @param P2 The latitude of the second point (in radians). 0405 * @return Returned the meridian arc length between P1 and P2 0406 */ 0407 double meridianArcLengthRadians(double P1, double P2); 0408 0409 /** 0410 * Computes the azimuth and orthodromic distance from the 0411 * startingGeographicPoint starting point and the 0412 * destinationGeographicPoint destination point. 0413 */ 0414 bool computeDirection(); 0415 0416 protected: 0417 0418 double castToAngleRange(const double alpha); 0419 0420 /** 0421 * Checks the latitude validity. The argument @c latitude should be 0422 * greater or equal than -90 degrees and lower or equals than +90 degrees. As 0423 * a convenience, this method converts the latitude to radians. 0424 * 0425 * @param latitude The latitude value in decimal degrees. 0426 */ 0427 bool checkLatitude(double* latitude); 0428 0429 /** 0430 * Checks the longitude validity. The argument @c longitude should be 0431 * greater or equal than -180 degrees and lower or equals than +180 degrees. As 0432 * a convenience, this method converts the longitude to radians. 0433 * 0434 * @param longitude The longitude value in decimal degrees. 0435 */ 0436 bool checkLongitude(double* longitude); 0437 0438 /** 0439 * Checks the azimuth validity. The argument @c azimuth should be 0440 * greater or equal than -180 degrees and lower or equals than +180 degrees. 0441 * As a convenience, this method converts the azimuth to radians. 0442 * 0443 * @param azimuth The azimuth value in decimal degrees. 0444 */ 0445 bool checkAzimuth(double* azimuth); 0446 0447 /** 0448 * Checks the orthodromic distance validity. Arguments @c orthodromicDistance 0449 * should be greater or equal than 0 and lower or equals than the maximum orthodromic distance. 0450 * 0451 * @param distance The orthodromic distance value. 0452 */ 0453 bool checkOrthodromicDistance(const double distance); 0454 0455 /** 0456 * Tolerance factors from the strictest (<code>TOLERANCE_0</CODE>) 0457 * to the most relax one (<code>TOLERANCE_3</CODE>). 0458 */ 0459 double TOLERANCE_0, TOLERANCE_1, TOLERANCE_2, TOLERANCE_3; 0460 0461 /** 0462 * Tolerance factor for assertions. It has no impact on computed values. 0463 */ 0464 double TOLERANCE_CHECK; 0465 0466 /** 0467 * The encapsulated ellipsoid. 0468 */ 0469 Ellipsoid m_ellipsoid; 0470 0471 /** 0472 * The semi major axis of the referenced ellipsoid. 0473 */ 0474 double m_semiMajorAxis; 0475 0476 /** 0477 * The semi minor axis of the referenced ellipsoid. 0478 */ 0479 double m_semiMinorAxis; 0480 0481 /** 0482 * The eccentricity squared of the referenced ellipsoid. 0483 */ 0484 double m_eccentricitySquared; 0485 0486 /** 0487 * The maximum orthodromic distance that could be calculated onto the referenced ellipsoid. 0488 */ 0489 double m_maxOrthodromicDistance; 0490 0491 /** 0492 * GPNARC parameters computed from the ellipsoid. 0493 */ 0494 double A, B, C, D, E, F; 0495 0496 /** 0497 * GPNHRI parameters computed from the ellipsoid. 0498 * 0499 * @c f if the flattening of the referenced ellipsoid. @c f2, 0500 * @c f3 and @c f4 are <var>f<sup>2</sup></var>, 0501 * <var>f<sup>3</sup></var> and <var>f<sup>4</sup></var> respectively. 0502 */ 0503 double fo, f, f2, f3, f4; 0504 0505 /** 0506 * Parameters computed from the ellipsoid. 0507 */ 0508 double T1, T2, T4, T6; 0509 0510 /** 0511 * Parameters computed from the ellipsoid. 0512 */ 0513 double a01, a02, a03, a21, a22, a23, a42, a43, a63; 0514 0515 /** 0516 * The (<var>latitude</var>, <var>longitude</var>) coordinate of the first point 0517 * in radians. This point is set by setStartingGeographicPoint. 0518 */ 0519 double m_lat1, m_long1; 0520 0521 /** 0522 * The (<var>latitude</var>, <var>longitude</var>) coordinate of the destination point 0523 * in radians. This point is set by setDestinationGeographicPoint. 0524 */ 0525 double m_lat2, m_long2; 0526 0527 /** 0528 * The distance and azimuth (in radians) from the starting point 0529 * (long1, lat1) to the destination point 0530 * (long2, lat2). 0531 */ 0532 double m_distance, m_azimuth; 0533 0534 /** 0535 * Tell if the destination point is valid. 0536 * @c false if long2 and lat2 need to be computed. 0537 */ 0538 bool m_destinationValid; 0539 0540 /** 0541 * Tell if the azimuth and the distance are valids. 0542 * @c false if distance and azimuth need to be computed. 0543 */ 0544 bool m_directionValid; 0545 }; 0546 0547 } // namespace Digikam 0548 0549 #endif // DIGIKAM_GEODETIC_TOOLS_H