1#include "GeodeticUtils.h"
12 double equatorialRadius;
13 double eccentricitySquared;
16 Ellipsoid ellipsoids[] = {
17 {-1,
"Placeholder", 0, 0},
18 {1,
"Airy", 6377563, 0.00667054},
19 {2,
"Australian National", 6378160, 0.006694542},
20 {3,
"Bessel 1841", 6377397, 0.006674372},
21 {4,
"Bessel 1841 (Nambia) ", 6377484, 0.006674372},
22 {5,
"Clarke 1866", 6378206, 0.006768658},
23 {6,
"Clarke 1880", 6378249, 0.006803511},
24 {7,
"Everest", 6377276, 0.006637847},
25 {8,
"Fischer 1960 (Mercury) ", 6378166, 0.006693422},
26 {9,
"Fischer 1968", 6378150, 0.006693422},
27 {10,
"GRS 1967", 6378160, 0.006694605},
28 {11,
"GRS 1980", 6378137, 0.00669438},
29 {12,
"Helmert 1906", 6378200, 0.006693422},
30 {13,
"Hough", 6378270, 0.00672267},
31 {14,
"International", 6378388, 0.00672267},
32 {15,
"Krassovsky", 6378245, 0.006693422},
33 {16,
"Modified Airy", 6377340, 0.00667054},
34 {17,
"Modified Everest", 6377304, 0.006637847},
35 {18,
"Modified Fischer 1960", 6378155, 0.006693422},
36 {19,
"South American 1969", 6378160, 0.006694542},
37 {20,
"WGS 60", 6378165, 0.006693422},
38 {21,
"WGS 66", 6378145, 0.006694542},
39 {22,
"WGS-72", 6378135, 0.006694318},
40 {23,
"WGS-84", 6378137, 0.00669438}
44void LLtoUTMZone(
int& zoneNumber,
45 bool& northernHemisphere,
46 const double latitude,
47 const double longitude,
57 const double LongTemp = (longitude + 180) - (
int)((longitude + 180) / 360) * 360 - 180;
59 zoneNumber = (int)((LongTemp + 180) / 6) + 1;
61 if (latitude >= 56.0 && latitude < 64.0 && LongTemp >= 3.0 && LongTemp < 12.0)
65 if (latitude >= 72.0 && latitude < 84.0)
67 if (LongTemp >= 0.0 && LongTemp < 9.0) zoneNumber = 31;
68 else if (LongTemp >= 9.0 && LongTemp < 21.0) zoneNumber = 33;
69 else if (LongTemp >= 21.0 && LongTemp < 33.0) zoneNumber = 35;
70 else if (LongTemp >= 33.0 && LongTemp < 42.0) zoneNumber = 37;
73 northernHemisphere = latitude >= 0;
77void LLtoUTM(
double& easting,
79 const double latitude,
80 const double longitude,
83 const int referenceEllipsoid)
91 double a = ellipsoids[referenceEllipsoid].equatorialRadius;
92 double eccSquared = ellipsoids[referenceEllipsoid].eccentricitySquared;
95 double eccPrimeSquared;
99 double LongTemp = (longitude + 180) - (
int)((longitude + 180) / 360) * 360 - 180;
101 double LatRad = glm::radians(latitude);
102 double LongRad = glm::radians(LongTemp);
105 LLtoUTMZone(zone, north, latitude, longitude, referenceEllipsoid);
108 const double LongOrigin = (zone - 1) * 6 - 180 + 3;
109 const double LongOriginRad = glm::radians(LongOrigin);
111 eccPrimeSquared = (eccSquared) / (1 - eccSquared);
113 N = a / std::sqrt(1 - eccSquared * std::sin(LatRad) * std::sin(LatRad));
114 T = std::tan(LatRad) * std::tan(LatRad);
115 C = eccPrimeSquared * std::cos(LatRad) * std::cos(LatRad);
116 A = std::cos(LatRad) * (LongRad - LongOriginRad);
118 M = a * ((1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - 5 * eccSquared * eccSquared * eccSquared / 256) * LatRad
119 - (3 * eccSquared / 8 + 3 * eccSquared * eccSquared / 32 + 45 * eccSquared * eccSquared * eccSquared / 1024) * std::sin(2 * LatRad)
120 + (15 * eccSquared * eccSquared / 256 + 45 * eccSquared * eccSquared * eccSquared / 1024) * std::sin(4 * LatRad)
121 - (35 * eccSquared * eccSquared * eccSquared / 3072) * std::sin(6 * LatRad));
123 easting = (double)(k0 * N * (A + (1 - T + C) * A * A * A / 6
124 + (5 - 18 * T + T * T + 72 * C - 58 * eccPrimeSquared) * A * A * A * A * A / 120)
127 northing = (double)(k0 * (M + N * std::tan(LatRad) * (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24
128 + (61 - 58 * T + T * T + 600 * C - 330 * eccPrimeSquared) * A * A * A * A * A * A / 720)));
130 northing += 10000000.0;
138glm::dvec3 LLtoECEF(
double latitude,
141 constexpr double semimajorAxis = 6378137.0;
142 constexpr double semiminorAxis = 6356752.314245;
144 latitude = glm::radians(latitude);
145 longitude = glm::radians(longitude);
147 double sinLat = std::sin(latitude);
148 double cosLat = std::cos(latitude);
149 double sinLong = std::sin(longitude);
150 double cosLong = std::cos(longitude);
152 constexpr double fllipsoidFlatness = ((semimajorAxis - semiminorAxis) / semimajorAxis);
153 constexpr double firstEccentricitySquared = (fllipsoidFlatness * (2.0 - fllipsoidFlatness));
155 double n = semimajorAxis / std::sqrt(1.0 - firstEccentricitySquared * sinLat * sinLat);
156 return glm::dvec3((altitude + n) * cosLat * cosLong,
157 (altitude + n) * cosLat * sinLong,
158 (altitude + (1.0 - firstEccentricitySquared) * n) * sinLat);
161glm::dvec2 calcUTMVectorBetweenCoordinates(
double lat1,
double long1,
double lat2,
double long2,
int zone1,
int zone2) {
162 bool north1 = lat1 >= 0.0;
163 bool north2 = lat2 >= 0.0;
166 LLtoUTMZone(zone1, north1, lat1, long1);
169 LLtoUTMZone(zone2, north2, lat2, long2);
172 if (zone1 == zone2) {
178 LLtoUTM(easting1, northing1, lat1, long1, zone1, north1);
179 LLtoUTM(easting2, northing2, lat2, long2, zone2, north2);
181 if (north1 == north2) {
182 return glm::dvec2(
static_cast<float>(easting2 - easting1),
static_cast<float>(northing2 - northing1));
185 return glm::dvec2(
static_cast<float>(easting2 - easting1),
static_cast<float>(northing2 - (northing1 - 10000000.0)));
188 return glm::dvec2(
static_cast<float>(easting2 - easting1),
static_cast<float>((northing2 - 10000000.0) - northing1));
194float calcBearingBetweenCoordinates(
double lat1,
double long1,
double lat2,
double long2,
int zone) {
195 bool north = lat2 >= 0.0;
198 LLtoUTMZone(zone, north, lat2, long2);
201 lat1 = glm::radians(lat1);
202 long1 = glm::radians(long1);
203 lat2 = glm::radians(lat2);
204 long2 = glm::radians(long2);
206 double y = std::sin(long2-long1) * std::cos(lat2);
207 double x = (std::cos(lat1) * std::sin(lat2)) - (std::sin(lat1) * std::cos(lat2) * std::cos(long2 - long1));
208 double meridian = glm::radians((zone * 6.0) - 183.0);
209 double convergence = glm::degrees(std::atan(std::tan(long2 - meridian) * std::sin(lat2)));
211 return static_cast<float>(std::fmod(glm::degrees(std::atan2(y, x)) - convergence + 360.0, 360.0));
221 ecefRef = LLtoECEF(latitude, longitude, altitude);
223 latitude = glm::radians(latitude);
224 longitude = glm::radians(longitude);
226 double sinLat = std::sin(latitude);
227 double cosLat = std::cos(latitude);
228 double sinLong = std::sin(longitude);
229 double cosLong = std::cos(longitude);
231 ecefToENUMatrix = glm::dmat3x3(-sinLong, -sinLat * cosLong, cosLat * cosLong,
232 cosLong, -sinLat * sinLong, cosLat * sinLong,
233 0.0, cosLat, sinLat);
245 return ecefToENUMatrix * (LLtoECEF(latitude, longitude, altitude) - ecefRef);
252 return convert(lat2, long2) - convert(lat1, long1);
glm::dvec2 calcVectorBetweenCoordinates(double lat1, double long1, double lat2, double long2) const
Calculates a vector between the two geodetic points in ENU space.
LLToENUConverter(double latitude, double longitude, double altitude=0.0)
Constructs a new LLToENUConverter.
glm::dvec3 convert(double latitude, double longitude, double altitude=0.0) const
Converts the given latitude/longitude/altitude coordinates into local tangent plane ENU coordinates w...