OpenCPN Partial API docs
Loading...
Searching...
No Matches
geodesic.cpp
1#include <stdlib.h>
2#include <stdio.h>
3#include <string.h>
4#include <math.h>
5
6#include "geodesic.h"
7
8/* These methods implemented using the Vincenty method.
9 *
10 * Vincenty's original paper is available here:
11 * http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
12 *
13 * Great examples of the methods are available at these locations
14 * http://www.movable-type.co.uk/scripts/latlong-vincenty.html
15 * http://www.codeproject.com/KB/cs/Vincentys_Formula.aspx
16 *
17 * The movable-type.co.uk page contains code (C) 2002-2008 Chris Veness
18 * and available under a LGPL license.
19 *
20 * The codeproject.com page contains code available under the
21 * Code Project Open License (CPOL) 1.02.
22 *
23 * These references are a courtesy only, as the code here is original
24 * and not a derivitive work of the code provided on these pages.
25 */
26
27/*
28 * The Vincenty method does not converge for antipodal points, but the
29 * code here handles this special case. Since the earth is a flattened
30 * sphere, the shortest route is over the poles. The trip via the North or
31 * South Pole is equivalent, so we arbitrarily pick the South Pole.
32 *
33 * The length of a great circle route halfway around the world through the poles
34 * is 0.5 of the circumference of a circle with a radius of the semi minor axis.
35 */
36
37double Geodesic::GreatCircleDistBear(double Lon1, double Lat1, double Lon2,
38 double Lat2, double *Dist, double *Bear1,
39 double *Bear2) {
40 /* Geodesic parameters per WGS-84 */
41 double a = GEODESIC_WGS84_SEMI_MAJORAXIS; /* in meters */
42 double b = GEODESIC_WGS84_SEMI_MINORAXIS; /* in meters */
43 double f = (GEODESIC_WGS84_SEMI_MAJORAXIS - GEODESIC_WGS84_SEMI_MINORAXIS) /
44 GEODESIC_WGS84_SEMI_MAJORAXIS; /* Flattening */
45
46 double dLon; /* Change in longitude */
47 double rLat1, rLat2; /* Reduced Latitude */
48 double lambda, lambdaprime; /* Counting variables */
49 double sinrLat1, cosrLat1, sinrLat2, cosrLat2, sinlambda,
50 coslambda; /* Intermediate calculations */
51 double sinsigma, cossigma, cos2sigmam, sigma, sinalpha, cos2alpha,
52 C; /* Intermediate calculations */
53 double u2, A, B; /* Intermediate calculations */
54 double dist; /* Great circle distance */
55 int itersleft = 50; /* Convergence attempts */
56
57 /* Initialize the output variables */
58 if (Dist) *Dist = 0.0;
59 if (Bear1) *Bear1 = 0.0;
60 if (Bear2) *Bear2 = 0.0;
61
62 if (fabs(Lon1 - Lon2) < 1e-12 && fabs(Lat1 - Lat2) < 1e-12) {
63 /* The start and end points are the same - the distance is zero */
64 return 0.0;
65 }
66
67 /* Convert inputs from degrees to radians */
68 Lon1 = GEODESIC_DEG2RAD(Lon1);
69 Lat1 = GEODESIC_DEG2RAD(Lat1);
70 Lon2 = GEODESIC_DEG2RAD(Lon2);
71 Lat2 = GEODESIC_DEG2RAD(Lat2);
72
73 /* Start the algorithm */
74 rLat1 = atan((1 - f) * tan(Lat1));
75 rLat2 = atan((1 - f) * tan(Lat2));
76 sinrLat1 = sin(rLat1);
77 cosrLat1 = cos(rLat1);
78 sinrLat2 = sin(rLat2);
79 cosrLat2 = cos(rLat2);
80 dLon = Lon2 - Lon1;
81
82 lambda = dLon;
83 lambdaprime = 2 * M_PI;
84
85 do {
86 sinlambda = sin(lambda);
87 coslambda = cos(lambda);
88 sinsigma =
89 sqrt(pow(cosrLat2 * sinlambda, 2.0) +
90 pow(cosrLat1 * sinrLat2 - sinrLat1 * cosrLat2 * coslambda, 2.0));
91 if (sinsigma < 1e-12) {
92 /* The points are antipodal */
93 dist = M_PI * b;
94 if (Dist) *Dist = dist;
95 if (Bear1) *Bear1 = 180.0; /* Start heading for the South Pole */
96 if (Bear2) *Bear2 = 0.0; /* Wind up heading for the North Pole */
97
98 return dist;
99 }
100 cossigma = sinrLat1 * sinrLat2 + cosrLat1 * cosrLat2 * coslambda;
101 sigma = atan2(sinsigma, cossigma);
102 if (sinsigma == 0.0)
103 sinalpha = 0.0;
104 else
105 sinalpha = cosrLat1 * cosrLat2 * sinlambda / sinsigma;
106 cos2alpha = 1 - pow(sinalpha, 2.0);
107 if (cos2alpha == 0.0)
108 cos2sigmam = 0.0;
109 else
110 cos2sigmam = cossigma - 2 * sinrLat1 * sinrLat2 / cos2alpha;
111 // if( isnan( cos2sigmam ) ) cos2sigmam = 0.0; /* Special case to handle
112 // circles at the equator */
113 C = f / 16 * cos2alpha * (4 + f * (4 - 3 * cos2alpha));
114 lambdaprime = lambda;
115 lambda =
116 dLon +
117 (1.0 - C) * f * sinalpha *
118 (sigma + C * sinsigma *
119 (cos2sigmam +
120 C * cossigma * (-1.0 + 2.0 * pow(cos2sigmam, 2.0))));
121 } while (fabs(lambda - lambdaprime) > 1e-12 && (itersleft--));
122
123 if (itersleft == 0) {
124 /* It didn't converge. Assume antipodal points. */
125 dist = M_PI * b;
126 if (Dist) *Dist = dist;
127 if (Bear1) *Bear1 = 180.0; /* Start heading for the South Pole */
128 if (Bear2) *Bear2 = 0.0; /* Wind up heading for the North Pole */
129
130 return dist;
131 }
132 u2 = cos2alpha * (pow(a, 2.0) - pow(b, 2.0)) / pow(b, 2.0);
133 A = 1 + u2 / 16384 * (4096 + u2 * (-768 + u2 * (320 - 175 * u2)));
134 B = u2 / 1024 * (256 + u2 * (-128 + u2 * (74 - 74 * u2)));
135 dist = b * A *
136 (sigma -
137 B * sinsigma *
138 (cos2sigmam +
139 B / 4 *
140 (cossigma * (-1.0 + 2.0 * pow(cos2sigmam, 2.0)) -
141 B / 6 * cos2sigmam * (-3.0 + 4.0 * pow(sinsigma, 2.0)) *
142 (-3 + 4 * pow(cos2sigmam, 2.0)))));
143 if (Dist) *Dist = dist;
144 if (Bear1) {
145 *Bear1 = GEODESIC_RAD2DEG(
146 atan2(cosrLat2 * sinlambda,
147 cosrLat1 * sinrLat2 - sinrLat1 * cosrLat2 * coslambda));
148 while (*Bear1 < 0.0) *Bear1 += 360.0;
149 }
150 if (Bear2) {
151 *Bear2 = GEODESIC_RAD2DEG(
152 atan2(cosrLat1 * sinlambda,
153 -sinrLat1 * cosrLat2 + cosrLat1 * sinrLat2 * coslambda));
154 while (*Bear2 < 0.0) *Bear2 += 360.0;
155 }
156 return dist;
157}
158
159/* Vincenty's direct solution. The equation numbers related to
160 * the equation numbers in the following:
161 * http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
162 */
163void Geodesic::GreatCircleTravel(double Lon1, double Lat1, double Dist,
164 double Bear1, double *Lon2, double *Lat2,
165 double *Bear2) {
166 /* Geodesic parameters per WGS-84 */
167 double a = GEODESIC_WGS84_SEMI_MAJORAXIS; /* in meters */
168 double b = GEODESIC_WGS84_SEMI_MINORAXIS; /* in meters */
169 double f = (GEODESIC_WGS84_SEMI_MAJORAXIS - GEODESIC_WGS84_SEMI_MINORAXIS) /
170 GEODESIC_WGS84_SEMI_MAJORAXIS; /* Flattening */
171
172 /* Initialize to where we started from */
173 if (Lon2) *Lon2 = Lon1;
174 if (Lat2) *Lat2 = Lat1;
175 if (Bear2) *Bear2 = Bear1;
176
177 if (Dist < 1e-12) {
178 /* There's no distance to travel, so we're done. */
179 return;
180 }
181
182 /* Convert inputs from degrees to radians */
183 Lon1 = GEODESIC_DEG2RAD(Lon1);
184 Lat1 = GEODESIC_DEG2RAD(Lat1);
185 Bear1 = GEODESIC_DEG2RAD(Bear1);
186 if (Lon2) *Lon2 = Lon1;
187 if (Lat2) *Lat2 = Lat1;
188 if (Bear2) *Bear2 = Bear1;
189
190 double sigma1; /* Angular distance on the sphere from equator to Lon1/Lat1 */
191 double rLat1; /* Reduced Latitude */
192 double sinrLat1; /* Sine of reduced latitude */
193 double cosrLat1; /* Cosine of reduced latitude */
194 double sinalpha1; /* Sine of the initial bearing */
195 double cosalpha1; /* Cosine of the initial bearing */
196 double sinalpha; /* Sine of the azimuth of the geodesic at the equator */
197 double sin2alpha; /* sinalpha^2 */
198 double cos2alpha; /* cosalpha^2 */
199 double sigma; /* Angular distance on the sphere */
200 double sinsigma; /* Sine of the angular distance on the sphere */
201 double cossigma; /* Cosine of the angular distance on the sphere */
202 double sigmaprime; /* Previous value of sigma */
203 double lambda; /* Difference in longitude on an auxiliary sphere */
204 double L; /* Difference in longitude, positive East */
205 double u2, A, B, C, twosigmam, costwosigmam, cos2twosigmam, deltasigma,
206 distoverba; /* Intermediate calculations */
207
208 /* Start the algorithm */
209 rLat1 = (1.0 - f) * tan(Lat1); /* tan U=(1-f)*tan(phi) */
210 cosrLat1 = 1.0 / sqrt(1.0 + rLat1 * rLat1); /* via trig identity */
211 sinrLat1 = rLat1 * cosrLat1; /* via trig identity */
212 sinalpha1 = sin(Bear1);
213 cosalpha1 = cos(Bear1);
214
215 sigma1 = atan2(rLat1, cosalpha1); /* Eq. 1 */
216
217 sinalpha = cosrLat1 * sinalpha1; /* Eq. 2 */
218 sin2alpha = sinalpha * sinalpha;
219
220 cos2alpha = 1 - (sin2alpha); /* cos2=1-sin2 */
221 u2 = cos2alpha * ((a * a) - (b * b)) / (b * b);
222 A = 1 +
223 (u2 / 16384) * (4096 + u2 * (-768 + u2 * (320 - 175 * u2))); /* Eq. 3 */
224
225 B = (u2 / 1024) * (256 + u2 * (-128 + u2 * (74 - 47 * u2))); /* Eq. 4 */
226
227 distoverba = Dist / (b * A);
228 sigma = distoverba;
229 sigmaprime = sigma - 1.0; /* Something to get the loop started */
230 while (fabs(sigmaprime - sigma) > 1e-12) {
231 twosigmam = 2 * sigma1 + sigma; /* Eq. 5 */
232 costwosigmam = cos(twosigmam);
233 cos2twosigmam = costwosigmam * costwosigmam;
234 sinsigma = sin(sigma);
235
236 deltasigma = B * sinsigma *
237 (costwosigmam + (B / 4.0) * /* Eq. 6 */
238 (cos(sigma) * (-1 + 2 * cos2twosigmam) -
239 (B / 6.0) * costwosigmam *
240 (-3 + 4 * sinsigma * sinsigma) *
241 (-3 + 4 * cos2twosigmam)));
242
243 sigmaprime = sigma;
244 sigma = distoverba + deltasigma; /* Eq. 7 */
245 }
246 sinsigma = sin(sigma);
247 cossigma = cos(sigma);
248 twosigmam = 2 * sigma1 + sigma; /* Eq. 5 */
249 costwosigmam = cos(twosigmam);
250 cos2twosigmam = costwosigmam * costwosigmam;
251
252 if (Lat2) {
253 *Lat2 = atan2(/* Eq. 8 */
254 sinrLat1 * cossigma + cosrLat1 * sinsigma * cosalpha1,
255 (1 - f) *
256 sqrt(sin2alpha + pow(sinrLat1 * sinsigma -
257 cosrLat1 * cossigma * cosalpha1,
258 2.0)));
259 *Lat2 = GEODESIC_RAD2DEG(*Lat2);
260 }
261
262 if (Lon2) {
263 lambda = atan2(sinsigma * sinalpha1, /* Eq. 9 */
264 cosrLat1 * cossigma - sinrLat1 * sinsigma * cosalpha1);
265
266 C = (f / 16.0) * cos2alpha * (4 + f * (4 - 3 * cos2alpha)); /* Eq. 10 */
267
268 L = lambda - (1 - C) * f * sinalpha *
269 (sigma + C * sinsigma * /* Eq. 11 */
270 (costwosigmam +
271 C * cossigma * (-1 + 2 * cos2twosigmam)));
272 *Lon2 = GEODESIC_RAD2DEG(Lon1 + L);
273 }
274
275 if (Bear2) {
276 *Bear2 = atan2(sinalpha, /* Eq. 12 */
277 -sinrLat1 * sinsigma + cosrLat1 * cossigma * cosalpha1);
278 *Bear2 = GEODESIC_RAD2DEG(*Bear2);
279 }
280
281 return;
282}