13 | * This allow many double precision numbers to be added together with twice the 14 | * normal precision. Thus the effective precision of the sum is 106 bits or 15 | * about 32 decimal places. 16 | *
17 | * The implementation follows J. R. Shewchuk, 18 | * Adaptive Precision 19 | * Floating-Point Arithmetic and Fast Robust Geometric Predicates, 20 | * Discrete & Computational Geometry 18(3) 305–363 (1997). 21 | *
22 | * In the documentation of the member functions, sum stands for the 23 | * value currently held in the accumulator. 24 | ***********************************************************************/ 25 | public class Accumulator { 26 | // _s + _t accumulators for the sum. 27 | private double _s, _t; 28 | /** 29 | * Construct from a double. 30 | *
31 | * @param y set sum = y. 32 | **********************************************************************/ 33 | public Accumulator(double y) { _s = y; _t = 0; } 34 | /** 35 | * Construct from another Accumulator. 36 | *
37 | * @param a set sum = a. 38 | **********************************************************************/ 39 | public Accumulator(Accumulator a) { _s = a._s; _t = a._t; } 40 | /** 41 | * Set the value to a double. 42 | *
43 | * @param y set sum = y. 44 | **********************************************************************/ 45 | public void Set(double y) { _s = y; _t = 0; } 46 | /** 47 | * Return the value held in the accumulator. 48 | *
49 | * @return sum. 50 | **********************************************************************/ 51 | public double Sum() { return _s; } 52 | /** 53 | * Return the result of adding a number to sum (but don't change 54 | * sum). 55 | *
56 | * @param y the number to be added to the sum. 57 | * @return sum + y. 58 | **********************************************************************/ 59 | public double Sum(double y) { 60 | Pair p = new Pair(); 61 | AddInternal(p, _s, _t, y); 62 | return p.first; 63 | } 64 | /** 65 | * Internal version of Add, p = [s, t] + y 66 | *
67 | * @param s the larger part of the accumulator. 68 | * @param t the smaller part of the accumulator. 69 | * @param y the addend. 70 | * @param p output Pair(s, t) with the result. 71 | **********************************************************************/ 72 | public static void AddInternal(Pair p, double s, double t, double y) { 73 | // Here's Shewchuk's solution... 74 | double u; // hold exact sum as [s, t, u] 75 | // Accumulate starting at least significant end 76 | GeoMath.sum(p, y, t); y = p.first; u = p.second; 77 | GeoMath.sum(p, y, s); s = p.first; t = p.second; 78 | // Start is s, t decreasing and non-adjacent. Sum is now (s + t + u) 79 | // exactly with s, t, u non-adjacent and in decreasing order (except for 80 | // possible zeros). The following code tries to normalize the result. 81 | // Ideally, we want s = round(s+t+u) and u = round(s+t+u - s). The 82 | // following does an approximate job (and maintains the decreasing 83 | // non-adjacent property). Here are two "failures" using 3-bit floats: 84 | // 85 | // Case 1: s is not equal to round(s+t+u) -- off by 1 ulp 86 | // [12, -1] - 8 -> [4, 0, -1] -> [4, -1] = 3 should be [3, 0] = 3 87 | // 88 | // Case 2: s+t is not as close to s+t+u as it shold be 89 | // [64, 5] + 4 -> [64, 8, 1] -> [64, 8] = 72 (off by 1) 90 | // should be [80, -7] = 73 (exact) 91 | // 92 | // "Fixing" these problems is probably not worth the expense. The 93 | // representation inevitably leads to small errors in the accumulated 94 | // values. The additional errors illustrated here amount to 1 ulp of the 95 | // less significant word during each addition to the Accumulator and an 96 | // additional possible error of 1 ulp in the reported sum. 97 | // 98 | // Incidentally, the "ideal" representation described above is not 99 | // canonical, because s = round(s + t) may not be true. For example, 100 | // with 3-bit floats: 101 | // 102 | // [128, 16] + 1 -> [160, -16] -- 160 = round(145). 103 | // But [160, 0] - 16 -> [128, 16] -- 128 = round(144). 104 | // 105 | if (s == 0) // This implies t == 0, 106 | s = u; // so result is u 107 | else 108 | t += u; // otherwise just accumulate u to t. 109 | p.first = s; p.second = t; 110 | } 111 | 112 | /** 113 | * Add a number to the accumulator. 114 | *
115 | * @param y set sum += y. 116 | **********************************************************************/ 117 | public void Add(double y) { 118 | Pair p = new Pair(); 119 | AddInternal(p, _s, _t, y); 120 | _s = p.first; _t = p.second; 121 | } 122 | /** 123 | * Negate an accumulator. 124 | *
125 | * Set sum = −sum. 126 | **********************************************************************/ 127 | public void Negate() { _s = -_s; _t = -_t; } 128 | /** 129 | * Take the remainder. 130 | *
131 | * @param y the modulus 132 | *
133 | * Put sum in the rangle [−y, y].
134 | **********************************************************************/
135 | public void Remainder(double y) {
136 | _s = Math.IEEEremainder(_s, y);
137 | Add(0.0); // renormalize
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/main/java/net/sf/geographiclib/Constants.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Implementation of the net.sf.geographiclib.Constants class
3 | *
4 | * Copyright (c) Charles Karney (2013)
13 | * Define constants specifying the WGS84 ellipsoid.
14 | ***********************************************************************/
15 | public class Constants {
16 | /**
17 | * The equatorial radius of WGS84 ellipsoid (6378137 m).
18 | **********************************************************************/
19 | public static final double WGS84_a = 6378137;
20 | /**
21 | * The flattening of WGS84 ellipsoid (1/298.257223563).
22 | **********************************************************************/
23 | public static final double WGS84_f = 1/298.257223563;
24 |
25 | private Constants() {}
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/net/sf/geographiclib/GeoMath.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Implementation of the net.sf.geographiclib.GeoMath class
3 | *
4 | * Copyright (c) Charles Karney (2013-2020)
13 | * Define mathematical functions and constants so that any version of Java
14 | * can be used.
15 | **********************************************************************/
16 | public class GeoMath {
17 | /**
18 | * The number of binary digits in the fraction of a double precision
19 | * number (equivalent to C++'s {@code numeric_limits
26 | * @param x the argument.
27 | * @return x2.
28 | **********************************************************************/
29 | public static double sq(double x) { return x * x; }
30 |
31 | /**
32 | * The inverse hyperbolic tangent function. This is defined in terms of
33 | * Math.log1p(x) in order to maintain accuracy near x = 0.
34 | * In addition, the odd parity of the function is enforced.
35 | *
36 | * @param x the argument.
37 | * @return atanh(x).
38 | **********************************************************************/
39 | public static double atanh(double x) {
40 | double y = Math.abs(x); // Enforce odd parity
41 | y = Math.log1p(2 * y/(1 - y))/2;
42 | return x > 0 ? y : (x < 0 ? -y : x);
43 | }
44 |
45 | /**
46 | * Normalize a sine cosine pair.
47 | *
48 | * @param p return parameter for normalized quantities with sinx2
49 | * + cosx2 = 1.
50 | * @param sinx the sine.
51 | * @param cosx the cosine.
52 | **********************************************************************/
53 | public static void norm(Pair p, double sinx, double cosx) {
54 | double r = Math.hypot(sinx, cosx);
55 | p.first = sinx/r; p.second = cosx/r;
56 | }
57 |
58 | /**
59 | * The error-free sum of two numbers.
60 | *
61 | * @param u the first number in the sum.
62 | * @param v the second number in the sum.
63 | * @param p output Pair(s, t) with s = round(u +
64 | * v) and t = u + v - s.
65 | *
66 | * See D. E. Knuth, TAOCP, Vol 2, 4.2.2, Theorem B.
67 | **********************************************************************/
68 | public static void sum(Pair p, double u, double v) {
69 | double s = u + v;
70 | double up = s - v;
71 | double vpp = s - up;
72 | up -= u;
73 | vpp -= v;
74 | double t = s != 0 ? 0.0 - (up + vpp) : s;
75 | // u + v = s + t
76 | // = round(u + v) + t
77 | p.first = s; p.second = t;
78 | }
79 |
80 | /**
81 | * Evaluate a polynomial.
82 | *
83 | * @param N the order of the polynomial.
84 | * @param p the coefficient array (of size N + s + 1 or more).
85 | * @param s starting index for the array.
86 | * @param x the variable.
87 | * @return the value of the polynomial.
88 | *
89 | * Evaluate y = ∑n=0..N
90 | * ps+n
91 | * xN−n. Return 0 if N < 0.
92 | * Return ps, if N = 0 (even if x is
93 | * infinite or a nan). The evaluation uses Horner's method.
94 | **********************************************************************/
95 | public static double polyval(int N, double p[], int s, double x) {
96 | double y = N < 0 ? 0 : p[s++];
97 | while (--N >= 0) y = y * x + p[s++];
98 | return y;
99 | }
100 |
101 | /**
102 | * Coarsen a value close to zero.
103 | *
104 | * @param x the argument
105 | * @return the coarsened value.
106 | *
107 | * This makes the smallest gap in x = 1/16 − nextafter(1/16, 0)
108 | * = 1/257 for reals = 0.7 pm on the earth if x is an angle
109 | * in degrees. (This is about 1000 times more resolution than we get with
110 | * angles around 90 degrees.) We use this to avoid having to deal with near
111 | * singular cases when x is non-zero but tiny (e.g.,
112 | * 10−200). This converts −0 to +0; however tiny
113 | * negative numbers get converted to −0.
114 | **********************************************************************/
115 | public static double AngRound(double x) {
116 | final double z = 1/16.0;
117 | double y = Math.abs(x);
118 | // The compiler mustn't "simplify" z - (z - y) to y
119 | y = y < z ? z - (z - y) : y;
120 | return Math.copySign(y, x);
121 | }
122 |
123 | /**
124 | * Normalize an angle.
125 | *
126 | * @param x the angle in degrees.
127 | * @return the angle reduced to the range [−180°, 180°).
128 | *
129 | * The range of x is unrestricted.
130 | **********************************************************************/
131 | public static double AngNormalize(double x) {
132 | double y = Math.IEEEremainder(x, 360.0);
133 | return Math.abs(y) == 180 ? Math.copySign(180.0, x) : y;
134 | }
135 |
136 | /**
137 | * Normalize a latitude.
138 | *
139 | * @param x the angle in degrees.
140 | * @return x if it is in the range [−90°, 90°], otherwise
141 | * return NaN.
142 | **********************************************************************/
143 | public static double LatFix(double x) {
144 | return Math.abs(x) > 90 ? Double.NaN : x;
145 | }
146 |
147 | /**
148 | * The exact difference of two angles reduced to [−180°, 180°].
149 | *
150 | * @param x the first angle in degrees.
151 | * @param y the second angle in degrees.
152 | * @param p output Pair(d, e) with d being the rounded
153 | * difference and e being the error.
154 | *
155 | * This computes z = y − x exactly, reduced to
156 | * [−180°, 180°]; and then sets z = d + e
157 | * where d is the nearest representable number to z and
158 | * e is the truncation error. If z = ±0° or
159 | * ±180°, then the sign of d is given by the sign of
160 | * y − x. The maximum absolute value of e is
161 | * 2−26 (for doubles).
162 | **********************************************************************/
163 | public static void AngDiff(Pair p, double x, double y) {
164 | sum(p, Math.IEEEremainder(-x, 360.0), Math.IEEEremainder(y, 360.0));
165 | sum(p, Math.IEEEremainder(p.first, 360.0), p.second);
166 | if (p.first == 0 || Math.abs(p.first) == 180)
167 | // p = [d, e]...
168 | // If e == 0, take sign from y - x
169 | // else (e != 0, implies d = +/-180), d and e must have opposite signs
170 | p.first = Math.copySign(p.first, p.second == 0 ? y - x : -p.second);
171 | }
172 |
173 | /**
174 | * Evaluate the sine and cosine function with the argument in degrees
175 | *
176 | * @param p return Pair(s, t) with s = sin(x) and
177 | * c = cos(x).
178 | * @param x in degrees.
179 | *
180 | * The results obey exactly the elementary properties of the trigonometric
181 | * functions, e.g., sin 9° = cos 81° = − sin 123456789°.
182 | **********************************************************************/
183 | public static void sincosd(Pair p, double x) {
184 | // In order to minimize round-off errors, this function exactly reduces
185 | // the argument to the range [-45, 45] before converting it to radians.
186 | double r; int q;
187 | r = x % 360.0;
188 | q = (int)Math.round(r / 90); // If r is NaN this returns 0
189 | r -= 90 * q;
190 | // now abs(r) <= 45
191 | r = Math.toRadians(r);
192 | // Possibly could call the gnu extension sincos
193 | double s = Math.sin(r), c = Math.cos(r);
194 | double sinx, cosx;
195 | switch (q & 3) {
196 | case 0: sinx = s; cosx = c; break;
197 | case 1: sinx = c; cosx = -s; break;
198 | case 2: sinx = -s; cosx = -c; break;
199 | default: sinx = -c; cosx = s; break; // case 3
200 | }
201 | if (sinx == 0) sinx = Math.copySign(sinx, x);
202 | p.first = sinx; p.second = 0.0 + cosx;
203 | }
204 |
205 | /**
206 | * Evaluate the sine and cosine function with reduced argument plus correction
207 | *
208 | * @param p return Pair(s, t) with s =
209 | * sin(x + t) and c = cos(x + t).
210 | * @param x reduced angle in degrees.
211 | * @param t correction in degrees.
212 | *
213 | * This is a variant of GeoMath.sincosd allowing a correction to the angle to
214 | * be supplied. x x must be in [−180°, 180°] and
215 | * t is assumed to be a small correction. GeoMath.AngRound is
216 | * applied to the reduced angle to prevent problems with x + t
217 | * being extremely close but not exactly equal to one of the four cardinal
218 | * directions.
219 | **********************************************************************/
220 | public static void sincosde(Pair p, double x, double t) {
221 | // In order to minimize round-off errors, this function exactly reduces
222 | // the argument to the range [-45, 45] before converting it to radians.
223 | double r; int q;
224 | q = (int)Math.round(x / 90); // If r is NaN this returns 0
225 | r = x - 90 * q;
226 | // now abs(r) <= 45
227 | r = Math.toRadians(GeoMath.AngRound(r + t));
228 | // Possibly could call the gnu extension sincos
229 | double s = Math.sin(r), c = Math.cos(r);
230 | double sinx, cosx;
231 | switch (q & 3) {
232 | case 0: sinx = s; cosx = c; break;
233 | case 1: sinx = c; cosx = -s; break;
234 | case 2: sinx = -s; cosx = -c; break;
235 | default: sinx = -c; cosx = s; break; // case 3
236 | }
237 | if (sinx == 0) sinx = Math.copySign(sinx, x);
238 | p.first = sinx; p.second = 0.0 + cosx;
239 | }
240 |
241 | /**
242 | * Evaluate the atan2 function with the result in degrees
243 | *
244 | * @param y the sine of the angle
245 | * @param x the cosine of the angle
246 | * @return atan2(y, x) in degrees.
247 | *
248 | * The result is in the range [−180° 180°]. N.B.,
249 | * atan2d(±0, −1) = ±180°.
250 | **********************************************************************/
251 | public static double atan2d(double y, double x) {
252 | // In order to minimize round-off errors, this function rearranges the
253 | // arguments so that result of atan2 is in the range [-pi/4, pi/4] before
254 | // converting it to degrees and mapping the result to the correct
255 | // quadrant.
256 | int q = 0;
257 | if (Math.abs(y) > Math.abs(x)) { double t; t = x; x = y; y = t; q = 2; }
258 | if (x < 0) { x = -x; ++q; }
259 | // here x >= 0 and x >= abs(y), so angle is in [-pi/4, pi/4]
260 | double ang = Math.toDegrees(Math.atan2(y, x));
261 | switch (q) {
262 | // Note that atan2d(-0.0, 1.0) will return -0. However, we expect that
263 | // atan2d will not be called with y = -0. If need be, include
264 | //
265 | // case 0: ang = 0 + ang; break;
266 | //
267 | // and handle mpfr as in AngRound.
268 | case 1: ang = Math.copySign(180.0, y) - ang; break;
269 | case 2: ang = 90 - ang; break;
270 | case 3: ang = -90 + ang; break;
271 | default: break;
272 | }
273 | return ang;
274 | }
275 |
276 | private GeoMath() {}
277 | }
278 |
--------------------------------------------------------------------------------
/src/main/java/net/sf/geographiclib/GeodesicData.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Implementation of the net.sf.geographiclib.GeodesicData class
3 | *
4 | * Copyright (c) Charles Karney (2013)
13 | * GeodesicLine facilitates the determination of a series of points on a single
14 | * geodesic. The starting point (lat1, lon1) and the azimuth
15 | * azi1 are specified in the constructor; alternatively, the {@link
16 | * Geodesic#Line Geodesic.Line} method can be used to create a GeodesicLine.
17 | * {@link #Position Position} returns the location of point 2 a distance
18 | * s12 along the geodesic. Alternatively {@link #ArcPosition
19 | * ArcPosition} gives the position of point 2 an arc length a12 along
20 | * the geodesic.
21 | *
22 | * You can register the position of a reference point 3 a distance (arc
23 | * length), s13 (a13) along the geodesic with the
24 | * {@link #SetDistance SetDistance} ({@link #SetArc SetArc}) functions. Points
25 | * a fractional distance along the line can be found by providing, for example,
26 | * 0.5 * {@link #Distance} as an argument to {@link #Position Position}. The
27 | * {@link Geodesic#InverseLine Geodesic.InverseLine} or
28 | * {@link Geodesic#DirectLine Geodesic.DirectLine} methods return GeodesicLine
29 | * objects with point 3 set to the point 2 of the corresponding geodesic
30 | * problem. GeodesicLine objects created with the public constructor or with
31 | * {@link Geodesic#Line Geodesic.Line} have s13 and a13 set to
32 | * NaNs.
33 | *
34 | * The calculations are accurate to better than 15 nm (15 nanometers). See
35 | * Sec. 9 of
36 | * arXiv:1102.1215v1 for
37 | * details. The algorithms used by this class are based on series expansions
38 | * using the flattening f as a small parameter. These are only accurate
39 | * for |f| < 0.02; however reasonably accurate results will be
40 | * obtained for |f| < 0.2.
41 | *
42 | * The algorithms are described in
43 | *
52 | * Here's an example of using this class
53 | *
115 | * @param g A {@link Geodesic} object used to compute the necessary
116 | * information about the GeodesicLine.
117 | * @param lat1 latitude of point 1 (degrees).
118 | * @param lon1 longitude of point 1 (degrees).
119 | * @param azi1 azimuth at point 1 (degrees).
120 | *
121 | * lat1 should be in the range [−90°, 90°].
122 | *
123 | * If the point is at a pole, the azimuth is defined by keeping lon1
124 | * fixed, writing lat1 = ±(90° − ε), and
125 | * taking the limit ε → 0+.
126 | **********************************************************************/
127 | public GeodesicLine(Geodesic g,
128 | double lat1, double lon1, double azi1) {
129 | this(g, lat1, lon1, azi1, GeodesicMask.ALL);
130 | }
131 |
132 | /**
133 | * Constructor for a geodesic line staring at latitude lat1, longitude
134 | * lon1, and azimuth azi1 (all in degrees) with a subset of the
135 | * capabilities included.
136 | *
137 | * @param g A {@link Geodesic} object used to compute the necessary
138 | * information about the GeodesicLine.
139 | * @param lat1 latitude of point 1 (degrees).
140 | * @param lon1 longitude of point 1 (degrees).
141 | * @param azi1 azimuth at point 1 (degrees).
142 | * @param caps bitor'ed combination of {@link GeodesicMask} values
143 | * specifying the capabilities the GeodesicLine object should possess,
144 | * i.e., which quantities can be returned in calls to {@link #Position
145 | * Position}.
146 | *
147 | * The {@link GeodesicMask} values are
148 | *
300 | * @param s12 distance from point 1 to point 2 (meters); it can be
301 | * negative.
302 | * @return a {@link GeodesicData} object with the following fields:
303 | * lat1, lon1, azi1, lat2, lon2,
304 | * azi2, s12, a12. Some of these results may be
305 | * missing if the GeodesicLine did not include the relevant capability.
306 | *
307 | * The values of lon2 and azi2 returned are in the range
308 | * [−180°, 180°].
309 | *
310 | * The GeodesicLine object must have been constructed with caps
311 | * |= {@link GeodesicMask#DISTANCE_IN}; otherwise no parameters are set.
312 | **********************************************************************/
313 | public GeodesicData Position(double s12) {
314 | return Position(false, s12, GeodesicMask.STANDARD);
315 | }
316 | /**
317 | * Compute the position of point 2 which is a distance s12 (meters)
318 | * from point 1 and with a subset of the geodesic results returned.
319 | *
320 | * @param s12 distance from point 1 to point 2 (meters); it can be
321 | * negative.
322 | * @param outmask a bitor'ed combination of {@link GeodesicMask} values
323 | * specifying which results should be returned.
324 | * @return a {@link GeodesicData} object including the requested results.
325 | *
326 | * The GeodesicLine object must have been constructed with caps
327 | * |= {@link GeodesicMask#DISTANCE_IN}; otherwise no parameters are set.
328 | * Requesting a value which the GeodesicLine object is not capable of
329 | * computing is not an error (no parameters will be set). The value of
330 | * lon2 returned is normally in the range [−180°, 180°];
331 | * however if the outmask includes the
332 | * {@link GeodesicMask#LONG_UNROLL} flag, the longitude is "unrolled" so that
333 | * the quantity lon2 − lon1 indicates how many times and
334 | * in what sense the geodesic encircles the ellipsoid.
335 | **********************************************************************/
336 | public GeodesicData Position(double s12, int outmask) {
337 | return Position(false, s12, outmask);
338 | }
339 |
340 | /**
341 | * Compute the position of point 2 which is an arc length a12
342 | * (degrees) from point 1.
343 | *
344 | * @param a12 arc length from point 1 to point 2 (degrees); it can
345 | * be negative.
346 | * @return a {@link GeodesicData} object with the following fields:
347 | * lat1, lon1, azi1, lat2, lon2,
348 | * azi2, s12, a12. Some of these results may be
349 | * missing if the GeodesicLine did not include the relevant capability.
350 | *
351 | * The values of lon2 and azi2 returned are in the range
352 | * [−180°, 180°].
353 | *
354 | * The GeodesicLine object must have been constructed with caps
355 | * |= {@link GeodesicMask#DISTANCE_IN}; otherwise no parameters are set.
356 | **********************************************************************/
357 | public GeodesicData ArcPosition(double a12) {
358 | return Position(true, a12, GeodesicMask.STANDARD);
359 | }
360 | /**
361 | * Compute the position of point 2 which is an arc length a12
362 | * (degrees) from point 1 and with a subset of the geodesic results returned.
363 | *
364 | * @param a12 arc length from point 1 to point 2 (degrees); it can
365 | * be negative.
366 | * @param outmask a bitor'ed combination of {@link GeodesicMask} values
367 | * specifying which results should be returned.
368 | * @return a {@link GeodesicData} object giving lat1, lon2,
369 | * azi2, and a12.
370 | *
371 | * Requesting a value which the GeodesicLine object is not capable of
372 | * computing is not an error (no parameters will be set). The value of
373 | * lon2 returned is in the range [−180°, 180°], unless
374 | * the outmask includes the {@link GeodesicMask#LONG_UNROLL} flag.
375 | **********************************************************************/
376 | public GeodesicData ArcPosition(double a12, int outmask) {
377 | return Position(true, a12, outmask);
378 | }
379 |
380 | /**
381 | * The general position function. {@link #Position(double, int) Position}
382 | * and {@link #ArcPosition(double, int) ArcPosition} are defined in terms of
383 | * this function.
384 | *
385 | * @param arcmode boolean flag determining the meaning of the second
386 | * parameter; if arcmode is false, then the GeodesicLine object must have
387 | * been constructed with caps |= {@link GeodesicMask#DISTANCE_IN}.
388 | * @param s12_a12 if arcmode is false, this is the distance between
389 | * point 1 and point 2 (meters); otherwise it is the arc length between
390 | * point 1 and point 2 (degrees); it can be negative.
391 | * @param outmask a bitor'ed combination of {@link GeodesicMask} values
392 | * specifying which results should be returned.
393 | * @return a {@link GeodesicData} object with the requested results.
394 | *
395 | * The {@link GeodesicMask} values possible for outmask are
396 | *
422 | * Requesting a value which the GeodesicLine object is not capable of
423 | * computing is not an error; Double.NaN is returned instead.
424 | **********************************************************************/
425 | public GeodesicData Position(boolean arcmode, double s12_a12,
426 | int outmask) {
427 | outmask &= _caps & GeodesicMask.OUT_MASK;
428 | GeodesicData r = new GeodesicData();
429 | if (!( Init() &&
430 | (arcmode ||
431 | (_caps & (GeodesicMask.OUT_MASK & GeodesicMask.DISTANCE_IN)) != 0)
432 | ))
433 | // Uninitialized or impossible distance calculation requested
434 | return r;
435 | r.lat1 = _lat1; r.azi1 = _azi1;
436 | r.lon1 = ((outmask & GeodesicMask.LONG_UNROLL) != 0) ? _lon1 :
437 | GeoMath.AngNormalize(_lon1);
438 |
439 | // Avoid warning about uninitialized B12.
440 | double sig12, ssig12, csig12, B12 = 0, AB1 = 0;
441 | if (arcmode) {
442 | // Interpret s12_a12 as spherical arc length
443 | r.a12 = s12_a12;
444 | sig12 = Math.toRadians(s12_a12);
445 | Pair p = new Pair();
446 | GeoMath.sincosd(p, s12_a12); ssig12 = p.first; csig12 = p.second;
447 | } else {
448 | // Interpret s12_a12 as distance
449 | r.s12 = s12_a12;
450 | double
451 | tau12 = s12_a12 / (_b * (1 + _A1m1)),
452 | s = Math.sin(tau12),
453 | c = Math.cos(tau12);
454 | // tau2 = tau1 + tau12
455 | B12 = - Geodesic.SinCosSeries(true,
456 | _stau1 * c + _ctau1 * s,
457 | _ctau1 * c - _stau1 * s,
458 | _C1pa);
459 | sig12 = tau12 - (B12 - _B11);
460 | ssig12 = Math.sin(sig12); csig12 = Math.cos(sig12);
461 | if (Math.abs(_f) > 0.01) {
462 | // Reverted distance series is inaccurate for |f| > 1/100, so correct
463 | // sig12 with 1 Newton iteration. The following table shows the
464 | // approximate maximum error for a = WGS_a() and various f relative to
465 | // GeodesicExact.
466 | // erri = the error in the inverse solution (nm)
467 | // errd = the error in the direct solution (series only) (nm)
468 | // errda = the error in the direct solution
469 | // (series + 1 Newton) (nm)
470 | //
471 | // f erri errd errda
472 | // -1/5 12e6 1.2e9 69e6
473 | // -1/10 123e3 12e6 765e3
474 | // -1/20 1110 108e3 7155
475 | // -1/50 18.63 200.9 27.12
476 | // -1/100 18.63 23.78 23.37
477 | // -1/150 18.63 21.05 20.26
478 | // 1/150 22.35 24.73 25.83
479 | // 1/100 22.35 25.03 25.31
480 | // 1/50 29.80 231.9 30.44
481 | // 1/20 5376 146e3 10e3
482 | // 1/10 829e3 22e6 1.5e6
483 | // 1/5 157e6 3.8e9 280e6
484 | double
485 | ssig2 = _ssig1 * csig12 + _csig1 * ssig12,
486 | csig2 = _csig1 * csig12 - _ssig1 * ssig12;
487 | B12 = Geodesic.SinCosSeries(true, ssig2, csig2, _C1a);
488 | double serr = (1 + _A1m1) * (sig12 + (B12 - _B11)) - s12_a12 / _b;
489 | sig12 = sig12 - serr / Math.sqrt(1 + _k2 * GeoMath.sq(ssig2));
490 | ssig12 = Math.sin(sig12); csig12 = Math.cos(sig12);
491 | // Update B12 below
492 | }
493 | r.a12 = Math.toDegrees(sig12);
494 | }
495 |
496 | double ssig2, csig2, sbet2, cbet2, salp2, calp2;
497 | // sig2 = sig1 + sig12
498 | ssig2 = _ssig1 * csig12 + _csig1 * ssig12;
499 | csig2 = _csig1 * csig12 - _ssig1 * ssig12;
500 | double dn2 = Math.sqrt(1 + _k2 * GeoMath.sq(ssig2));
501 | if ((outmask & (GeodesicMask.DISTANCE | GeodesicMask.REDUCEDLENGTH |
502 | GeodesicMask.GEODESICSCALE)) != 0) {
503 | if (arcmode || Math.abs(_f) > 0.01)
504 | B12 = Geodesic.SinCosSeries(true, ssig2, csig2, _C1a);
505 | AB1 = (1 + _A1m1) * (B12 - _B11);
506 | }
507 | // sin(bet2) = cos(alp0) * sin(sig2)
508 | sbet2 = _calp0 * ssig2;
509 | // Alt: cbet2 = Math.hypot(csig2, salp0 * ssig2);
510 | cbet2 = Math.hypot(_salp0, _calp0 * csig2);
511 | if (cbet2 == 0)
512 | // I.e., salp0 = 0, csig2 = 0. Break the degeneracy in this case
513 | cbet2 = csig2 = Geodesic.tiny_;
514 | // tan(alp0) = cos(sig2)*tan(alp2)
515 | salp2 = _salp0; calp2 = _calp0 * csig2; // No need to normalize
516 |
517 | if ((outmask & GeodesicMask.DISTANCE) != 0 && arcmode)
518 | r.s12 = _b * ((1 + _A1m1) * sig12 + AB1);
519 |
520 | if ((outmask & GeodesicMask.LONGITUDE) != 0) {
521 | // tan(omg2) = sin(alp0) * tan(sig2)
522 | double somg2 = _salp0 * ssig2, comg2 = csig2, // No need to normalize
523 | E = Math.copySign(1, _salp0); // east or west going?
524 | // omg12 = omg2 - omg1
525 | double omg12 = ((outmask & GeodesicMask.LONG_UNROLL) != 0)
526 | ? E * (sig12
527 | - (Math.atan2( ssig2, csig2) - Math.atan2( _ssig1, _csig1))
528 | + (Math.atan2(E*somg2, comg2) - Math.atan2(E*_somg1, _comg1)))
529 | : Math.atan2(somg2 * _comg1 - comg2 * _somg1,
530 | comg2 * _comg1 + somg2 * _somg1);
531 | double lam12 = omg12 + _A3c *
532 | ( sig12 + (Geodesic.SinCosSeries(true, ssig2, csig2, _C3a)
533 | - _B31));
534 | double lon12 = Math.toDegrees(lam12);
535 | r.lon2 = ((outmask & GeodesicMask.LONG_UNROLL) != 0) ? _lon1 + lon12 :
536 | GeoMath.AngNormalize(r.lon1 + GeoMath.AngNormalize(lon12));
537 | }
538 |
539 | if ((outmask & GeodesicMask.LATITUDE) != 0)
540 | r.lat2 = GeoMath.atan2d(sbet2, _f1 * cbet2);
541 |
542 | if ((outmask & GeodesicMask.AZIMUTH) != 0)
543 | r.azi2 = GeoMath.atan2d(salp2, calp2);
544 |
545 | if ((outmask &
546 | (GeodesicMask.REDUCEDLENGTH | GeodesicMask.GEODESICSCALE)) != 0) {
547 | double
548 | B22 = Geodesic.SinCosSeries(true, ssig2, csig2, _C2a),
549 | AB2 = (1 + _A2m1) * (B22 - _B21),
550 | J12 = (_A1m1 - _A2m1) * sig12 + (AB1 - AB2);
551 | if ((outmask & GeodesicMask.REDUCEDLENGTH) != 0)
552 | // Add parens around (_csig1 * ssig2) and (_ssig1 * csig2) to ensure
553 | // accurate cancellation in the case of coincident points.
554 | r.m12 = _b * ((dn2 * (_csig1 * ssig2) - _dn1 * (_ssig1 * csig2))
555 | - _csig1 * csig2 * J12);
556 | if ((outmask & GeodesicMask.GEODESICSCALE) != 0) {
557 | double t = _k2 * (ssig2 - _ssig1) * (ssig2 + _ssig1) / (_dn1 + dn2);
558 | r.M12 = csig12 + (t * ssig2 - csig2 * J12) * _ssig1 / _dn1;
559 | r.M21 = csig12 - (t * _ssig1 - _csig1 * J12) * ssig2 / dn2;
560 | }
561 | }
562 |
563 | if ((outmask & GeodesicMask.AREA) != 0) {
564 | double
565 | B42 = Geodesic.SinCosSeries(false, ssig2, csig2, _C4a);
566 | double salp12, calp12;
567 | if (_calp0 == 0 || _salp0 == 0) {
568 | // alp12 = alp2 - alp1, used in atan2 so no need to normalize
569 | salp12 = salp2 * _calp1 - calp2 * _salp1;
570 | calp12 = calp2 * _calp1 + salp2 * _salp1;
571 | } else {
572 | // tan(alp) = tan(alp0) * sec(sig)
573 | // tan(alp2-alp1) = (tan(alp2) -tan(alp1)) / (tan(alp2)*tan(alp1)+1)
574 | // = calp0 * salp0 * (csig1-csig2) / (salp0^2 + calp0^2 * csig1*csig2)
575 | // If csig12 > 0, write
576 | // csig1 - csig2 = ssig12 * (csig1 * ssig12 / (1 + csig12) + ssig1)
577 | // else
578 | // csig1 - csig2 = csig1 * (1 - csig12) + ssig12 * ssig1
579 | // No need to normalize
580 | salp12 = _calp0 * _salp0 *
581 | (csig12 <= 0 ? _csig1 * (1 - csig12) + ssig12 * _ssig1 :
582 | ssig12 * (_csig1 * ssig12 / (1 + csig12) + _ssig1));
583 | calp12 = GeoMath.sq(_salp0) + GeoMath.sq(_calp0) * _csig1 * csig2;
584 | }
585 | r.S12 = _c2 * Math.atan2(salp12, calp12) + _A4 * (B42 - _B41);
586 | }
587 |
588 | return r;
589 | }
590 |
591 | /**
592 | * Specify position of point 3 in terms of distance.
593 | *
594 | * @param s13 the distance from point 1 to point 3 (meters); it
595 | * can be negative.
596 | *
597 | * This is only useful if the GeodesicLine object has been constructed
598 | * with caps |= {@link GeodesicMask#DISTANCE_IN}.
599 | **********************************************************************/
600 | public void SetDistance(double s13) {
601 | _s13 = s13;
602 | GeodesicData g = Position(false, _s13, 0);
603 | _a13 = g.a12;
604 | }
605 |
606 | /**
607 | * Specify position of point 3 in terms of arc length.
608 | *
609 | * @param a13 the arc length from point 1 to point 3 (degrees); it
610 | * can be negative.
611 | *
612 | * The distance s13 is only set if the GeodesicLine object has been
613 | * constructed with caps |= {@link GeodesicMask#DISTANCE}.
614 | **********************************************************************/
615 | void SetArc(double a13) {
616 | _a13 = a13;
617 | GeodesicData g = Position(true, _a13, GeodesicMask.DISTANCE);
618 | _s13 = g.s12;
619 | }
620 |
621 | /**
622 | * Specify position of point 3 in terms of either distance or arc length.
623 | *
624 | * @param arcmode boolean flag determining the meaning of the second
625 | * parameter; if arcmode is false, then the GeodesicLine object must
626 | * have been constructed with caps |=
627 | * {@link GeodesicMask#DISTANCE_IN}.
628 | * @param s13_a13 if arcmode is false, this is the distance from
629 | * point 1 to point 3 (meters); otherwise it is the arc length from
630 | * point 1 to point 3 (degrees); it can be negative.
631 | **********************************************************************/
632 | public void GenSetDistance(boolean arcmode, double s13_a13) {
633 | if (arcmode)
634 | SetArc(s13_a13);
635 | else
636 | SetDistance(s13_a13);
637 | }
638 |
639 | /**
640 | * @return true if the object has been initialized.
641 | **********************************************************************/
642 | private boolean Init() { return _caps != 0; }
643 |
644 | /**
645 | * @return lat1 the latitude of point 1 (degrees).
646 | **********************************************************************/
647 | public double Latitude()
648 | { return Init() ? _lat1 : Double.NaN; }
649 |
650 | /**
651 | * @return lon1 the longitude of point 1 (degrees).
652 | **********************************************************************/
653 | public double Longitude()
654 | { return Init() ? _lon1 : Double.NaN; }
655 |
656 | /**
657 | * @return azi1 the azimuth (degrees) of the geodesic line at point 1.
658 | **********************************************************************/
659 | public double Azimuth()
660 | { return Init() ? _azi1 : Double.NaN; }
661 |
662 | /**
663 | * @return pair of sine and cosine of azi1 the azimuth (degrees) of
664 | * the geodesic line at point 1.
665 | **********************************************************************/
666 | public Pair AzimuthCosines() {
667 | return new Pair(Init() ? _salp1 : Double.NaN,
668 | Init() ? _calp1 : Double.NaN);
669 | }
670 |
671 | /**
672 | * @return azi0 the azimuth (degrees) of the geodesic line as it
673 | * crosses the equator in a northward direction.
674 | **********************************************************************/
675 | public double EquatorialAzimuth() {
676 | return Init() ?
677 | GeoMath.atan2d(_salp0, _calp0) : Double.NaN;
678 | }
679 |
680 | /**
681 | * @return pair of sine and cosine of azi0 the azimuth of the geodesic
682 | * line as it crosses the equator in a northward direction.
683 | **********************************************************************/
684 | public Pair EquatorialAzimuthCosines() {
685 | return new Pair(Init() ? _salp0 : Double.NaN,
686 | Init() ? _calp0 : Double.NaN);
687 | }
688 |
689 | /**
690 | * @return a1 the arc length (degrees) between the northward
691 | * equatorial crossing and point 1.
692 | **********************************************************************/
693 | public double EquatorialArc() {
694 | return Init() ?
695 | GeoMath.atan2d(_ssig1, _csig1) : Double.NaN;
696 | }
697 |
698 | /**
699 | * @return a the equatorial radius of the ellipsoid (meters). This is
700 | * the value inherited from the Geodesic object used in the constructor.
701 | **********************************************************************/
702 | public double EquatorialRadius()
703 | { return Init() ? _a : Double.NaN; }
704 |
705 | /**
706 | * @return f the flattening of the ellipsoid. This is the value
707 | * inherited from the Geodesic object used in the constructor.
708 | **********************************************************************/
709 | public double Flattening()
710 | { return Init() ? _f : Double.NaN; }
711 |
712 | /**
713 | * @return caps the computational capabilities that this object was
714 | * constructed with. LATITUDE and AZIMUTH are always included.
715 | **********************************************************************/
716 | public int Capabilities() { return _caps; }
717 |
718 | /**
719 | * @param testcaps a set of bitor'ed {@link GeodesicMask} values.
720 | * @return true if the GeodesicLine object has all these capabilities.
721 | **********************************************************************/
722 | public boolean Capabilities(int testcaps) {
723 | testcaps &= GeodesicMask.OUT_ALL;
724 | return (_caps & testcaps) == testcaps;
725 | }
726 |
727 | /**
728 | * The distance or arc length to point 3.
729 | *
730 | * @param arcmode boolean flag determining the meaning of returned
731 | * value.
732 | * @return s13 if arcmode is false; a13 if
733 | * arcmode is true.
734 | **********************************************************************/
735 | public double GenDistance(boolean arcmode)
736 | { return Init() ? (arcmode ? _a13 : _s13) : Double.NaN; }
737 |
738 | /**
739 | * @return s13, the distance to point 3 (meters).
740 | **********************************************************************/
741 | public double Distance() { return GenDistance(false); }
742 |
743 | /**
744 | * @return a13, the arc length to point 3 (degrees).
745 | **********************************************************************/
746 | public double Arc() { return GenDistance(true); }
747 | }
748 |
--------------------------------------------------------------------------------
/src/main/java/net/sf/geographiclib/GeodesicMask.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Implementation of the net.sf.geographiclib.GeodesicMask class
3 | *
4 | * Copyright (c) Charles Karney (2013-2014)
13 | * These masks do double duty. They specify (via the outmask parameter)
14 | * which results to return in the {@link GeodesicData} object returned by the
15 | * general routines {@link Geodesic#Direct(double, double, double, double, int)
16 | * Geodesic.Direct} and {@link Geodesic#Inverse(double, double, double, double,
17 | * int) Geodesic.Inverse} routines. They also signify (via the caps
18 | * parameter) to the {@link GeodesicLine#GeodesicLine(Geodesic, double, double,
19 | * double, int) GeodesicLine.GeodesicLine} constructor and to {@link
20 | * Geodesic#Line(double, double, double, int) Geodesic.Line} what capabilities
21 | * should be included in the {@link GeodesicLine} object.
22 | **********************************************************************/
23 | public class GeodesicMask {
24 | protected static final int CAP_NONE = 0;
25 | protected static final int CAP_C1 = 1<<0;
26 | protected static final int CAP_C1p = 1<<1;
27 | protected static final int CAP_C2 = 1<<2;
28 | protected static final int CAP_C3 = 1<<3;
29 | protected static final int CAP_C4 = 1<<4;
30 | protected static final int CAP_ALL = 0x1F;
31 | protected static final int CAP_MASK = CAP_ALL;
32 | protected static final int OUT_ALL = 0x7F80;
33 | protected static final int OUT_MASK = 0xFF80; // Include LONG_UNROLL
34 |
35 | /**
36 | * No capabilities, no output.
37 | **********************************************************************/
38 | public static final int NONE = 0;
39 | /**
40 | * Calculate latitude lat2. (It's not necessary to include this as a
41 | * capability to {@link GeodesicLine} because this is included by default.)
42 | **********************************************************************/
43 | public static final int LATITUDE = 1<<7 | CAP_NONE;
44 | /**
45 | * Calculate longitude lon2.
46 | **********************************************************************/
47 | public static final int LONGITUDE = 1<<8 | CAP_C3;
48 | /**
49 | * Calculate azimuths azi1 and azi2. (It's not necessary to
50 | * include this as a capability to {@link GeodesicLine} because this is
51 | * included by default.)
52 | **********************************************************************/
53 | public static final int AZIMUTH = 1<<9 | CAP_NONE;
54 | /**
55 | * Calculate distance s12.
56 | **********************************************************************/
57 | public static final int DISTANCE = 1<<10 | CAP_C1;
58 | /**
59 | * All of the above, the "standard" output and capabilities.
60 | **********************************************************************/
61 | public static final int STANDARD = LATITUDE | LONGITUDE |
62 | AZIMUTH | DISTANCE;
63 | /**
64 | * Allow distance s12 to be used as input in the direct
65 | * geodesic problem.
66 | **********************************************************************/
67 | public static final int DISTANCE_IN = 1<<11 | CAP_C1 | CAP_C1p;
68 | /**
69 | * Calculate reduced length m12.
70 | **********************************************************************/
71 | public static final int REDUCEDLENGTH = 1<<12 | CAP_C1 | CAP_C2;
72 | /**
73 | * Calculate geodesic scales M12 and M21.
74 | **********************************************************************/
75 | public static final int GEODESICSCALE = 1<<13 | CAP_C1 | CAP_C2;
76 | /**
77 | * Calculate area S12.
78 | **********************************************************************/
79 | public static final int AREA = 1<<14 | CAP_C4;
80 | /**
81 | * All capabilities, calculate everything. (LONG_UNROLL is not included in
82 | * this mask.)
83 | **********************************************************************/
84 | public static final int ALL = OUT_ALL| CAP_ALL;
85 | /**
86 | * Unroll lon2.
87 | **********************************************************************/
88 | public static final int LONG_UNROLL = 1<<15;
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/net/sf/geographiclib/GeographicErr.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Implementation of the net.sf.geographiclib.GeographicErr class
3 | *
4 | * Copyright (c) Charles Karney (2013)
13 | * A class to handle exceptions. It's derived from RuntimeException so it
14 | * can be caught by the usual catch clauses.
15 | **********************************************************************/
16 | public class GeographicErr extends RuntimeException {
17 | /**
18 | * Constructor
19 | *
20 | * @param msg a string message, which is accessible in the catch
21 | * clause via getMessage().
22 | **********************************************************************/
23 | public GeographicErr(String msg) { super(msg); }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/net/sf/geographiclib/Gnomonic.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Implementation of the net.sf.geographiclib.Gnomonic class
3 | *
4 | * Copyright (c) BMW Car IT GmbH (2014-2019)
14 | * Note: Gnomonic.java has been ported to Java from its C++ equivalent
15 | * Gnomonic.cpp, authored by C. F. F. Karney and licensed under MIT/X11
16 | * license. The following documentation is mostly the same as for its C++
17 | * equivalent, but has been adopted to apply to this Java implementation.
18 | *
19 | * Gnomonic projection centered at an arbitrary position C on the
20 | * ellipsoid. This projection is derived in Section 8 of
21 | *
32 | * The gnomonic projection of a point P on the ellipsoid is defined as
33 | * follows: compute the geodesic line from C to P; compute the
34 | * reduced length m12, geodesic scale M12, and ρ =
35 | * m12/M12; finally, this gives the coordinates x and
36 | * y of P in gnomonic projection with x = ρ sin
37 | * azi1; y = ρ cos azi1, where azi1 is the
38 | * azimuth of the geodesic at C. The method
39 | * {@link Gnomonic#Forward(double, double, double, double)} performs the
40 | * forward projection and
41 | * {@link Gnomonic#Reverse(double, double, double, double)} is the
42 | * inverse of the projection. The methods also return the azimuth
43 | * azi of the geodesic at P and reciprocal scale
44 | * rk in the azimuthal direction. The scale in the radial
45 | * direction is 1/rk2.
46 | *
47 | * For a sphere, ρ reduces to a tan(s12/a), where
48 | * s12 is the length of the geodesic from C to P, and the
49 | * gnomonic projection has the property that all geodesics appear as straight
50 | * lines. For an ellipsoid, this property holds only for geodesics interesting
51 | * the centers. However geodesic segments close to the center are approximately
52 | * straight.
53 | *
54 | * Consider a geodesic segment of length l. Let T be the point on
55 | * the geodesic (extended if necessary) closest to C, the center of the
56 | * projection, and t, be the distance CT. To lowest order, the
57 | * maximum deviation (as a true distance) of the corresponding gnomonic line
58 | * segment (i.e., with the same end points) from the geodesic is
66 | * This result applies for any surface. For an ellipsoid of revolution,
67 | * consider all geodesics whose end points are within a distance r of
68 | * C. For a given r, the deviation is maximum when the latitude
69 | * of C is 45°, when endpoints are a distance r away, and
70 | * when their azimuths from the center are ± 45° or ±
71 | * 135°. To lowest order in r and the flattening f, the
72 | * deviation is f (r/2a)3 r.
73 | *
74 | * CAUTION: The definition of this projection for a sphere is standard.
75 | * However, there is no standard for how it should be extended to an ellipsoid.
76 | * The choices are:
77 | *
105 | * Example of use:
106 | *
107 | *
143 | * @param earth the {@link Geodesic} object to use for geodesic
144 | * calculations.
145 | */
146 | public Gnomonic(Geodesic earth) {
147 | _earth = earth;
148 | _a = _earth.EquatorialRadius();
149 | _f = _earth.Flattening();
150 | }
151 |
152 | /**
153 | * Forward projection, from geographic to gnomonic.
154 | *
155 | * @param lat0 latitude of center point of projection (degrees).
156 | * @param lon0 longitude of center point of projection (degrees).
157 | * @param lat latitude of point (degrees).
158 | * @param lon longitude of point (degrees).
159 | * @return {@link GnomonicData} object with the following fields:
160 | * lat0, lon0, lat, lon, x, y,
161 | * azi, rk.
162 | *
163 | * lat0 and lat should be in the range [−90°,
164 | * 90°] and lon0 and lon should be in the range
165 | * [−540°, 540°). The scale of the projection is
166 | * 1/rk2 in the "radial" direction, azi clockwise
167 | * from true north, and is 1/rk in the direction perpendicular to
168 | * this. If the point lies "over the horizon", i.e., if rk ≤ 0,
169 | * then NaNs are returned for x and y (the correct values are
170 | * returned for azi and rk). A call to Forward followed by a
171 | * call to Reverse will return the original (lat, lon) (to
172 | * within roundoff) provided the point in not over the horizon.
173 | */
174 | public GnomonicData Forward(double lat0, double lon0, double lat, double lon)
175 | {
176 | GeodesicData inv =
177 | _earth.Inverse(lat0, lon0, lat, lon,
178 | GeodesicMask.AZIMUTH | GeodesicMask.GEODESICSCALE |
179 | GeodesicMask.REDUCEDLENGTH);
180 | GnomonicData fwd =
181 | new GnomonicData(lat0, lon0, lat, lon, Double.NaN, Double.NaN,
182 | inv.azi2, inv.M12);
183 |
184 | if (inv.M12 > 0) {
185 | double rho = inv.m12 / inv.M12;
186 | Pair p = new Pair();
187 | GeoMath.sincosd(p, inv.azi1);
188 | fwd.x = rho * p.first;
189 | fwd.y = rho * p.second;
190 | }
191 |
192 | return fwd;
193 | }
194 |
195 | /**
196 | * Reverse projection, from gnomonic to geographic.
197 | *
198 | * @param lat0 latitude of center point of projection (degrees).
199 | * @param lon0 longitude of center point of projection (degrees).
200 | * @param x easting of point (meters).
201 | * @param y northing of point (meters).
202 | * @return {@link GnomonicData} object with the following fields:
203 | * lat0, lon0, lat, lon, x, y,
204 | * azi, rk.
205 | *
206 | * lat0 should be in the range [−90°, 90°] and
207 | * lon0 should be in the range [−540°, 540°).
208 | * lat will be in the range [−90°, 90°] and lon
209 | * will be in the range [−180°, 180°]. The scale of the
210 | * projection is 1/rk2 in the "radial" direction,
211 | * azi clockwise from true north, and is 1/rk in the direction
212 | * perpendicular to this. Even though all inputs should return a valid
213 | * lat and lon, it's possible that the procedure fails to
214 | * converge for very large x or y; in this case NaNs are
215 | * returned for all the output arguments. A call to Reverse followed by a
216 | * call to Forward will return the original (x, y) (to
217 | * roundoff).
218 | */
219 | public GnomonicData Reverse(double lat0, double lon0, double x, double y) {
220 | GnomonicData rev =
221 | new GnomonicData(lat0, lon0, Double.NaN, Double.NaN, x, y, Double.NaN,
222 | Double.NaN);
223 |
224 | double azi0 = GeoMath.atan2d(x, y);
225 | double rho = Math.hypot(x, y);
226 | double s = _a * Math.atan(rho / _a);
227 | boolean little = rho <= _a;
228 |
229 | if (!little)
230 | rho = 1 / rho;
231 |
232 | GeodesicLine line =
233 | _earth.Line(lat0, lon0, azi0, GeodesicMask.LATITUDE
234 | | GeodesicMask.LONGITUDE | GeodesicMask.AZIMUTH
235 | | GeodesicMask.DISTANCE_IN | GeodesicMask.REDUCEDLENGTH
236 | | GeodesicMask.GEODESICSCALE);
237 |
238 | int count = numit_, trip = 0;
239 | GeodesicData pos = null;
240 |
241 | while (count-- > 0) {
242 | pos =
243 | line.Position(s, GeodesicMask.LONGITUDE | GeodesicMask.LATITUDE
244 | | GeodesicMask.AZIMUTH | GeodesicMask.DISTANCE_IN
245 | | GeodesicMask.REDUCEDLENGTH
246 | | GeodesicMask.GEODESICSCALE);
247 |
248 | if (trip > 0)
249 | break;
250 |
251 | double ds =
252 | little ? ((pos.m12 / pos.M12) - rho) * pos.M12 * pos.M12
253 | : (rho - (pos.M12 / pos.m12)) * pos.m12 * pos.m12;
254 | s -= ds;
255 |
256 | if (Math.abs(ds) <= eps_ * _a)
257 | trip++;
258 | }
259 |
260 | if (trip == 0)
261 | return rev;
262 |
263 | rev.lat = pos.lat2;
264 | rev.lon = pos.lon2;
265 | rev.azi = pos.azi2;
266 | rev.rk = pos.M12;
267 |
268 | return rev;
269 | }
270 |
271 | /**
272 | * @return a the equatorial radius of the ellipsoid (meters). This is
273 | * the value inherited from the Geodesic object used in the constructor.
274 | **********************************************************************/
275 | public double EquatorialRadius() { return _a; }
276 |
277 | /**
278 | * @return f the flattening of the ellipsoid. This is
279 | * the value inherited from the Geodesic object used in the constructor.
280 | **********************************************************************/
281 | public double Flattening() { return _f; }
282 | }
283 |
--------------------------------------------------------------------------------
/src/main/java/net/sf/geographiclib/GnomonicData.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Implementation of the net.sf.geographiclib.GnomonicData class
3 | *
4 | * Copyright (c) BMW Car IT GmbH (2014-2016)
13 |
14 | * This is used to return the results for a gnomonic projection of a point
15 | * (lat, lon) given a center point of projection (lat0,
16 | * lon0). The returned GnomonicData objects always include the
17 | * parameters provided to
18 | * {@link Gnomonic#Forward Gnomonic.Forward}
19 | * and
20 | * {@link Gnomonic#Reverse Gnomonic.Reverse}
21 | * and it always includes the fields x, y, azi. and
22 | * rk.
23 | **********************************************************************/
24 | public class GnomonicData {
25 | /**
26 | * latitude of center point of projection (degrees).
27 | **********************************************************************/
28 | public double lat0;
29 | /**
30 | * longitude of center point of projection (degrees).
31 | **********************************************************************/
32 | public double lon0;
33 | /**
34 | * latitude of point (degrees).
35 | **********************************************************************/
36 | public double lat;
37 | /**
38 | * longitude of point (degrees).
39 | **********************************************************************/
40 | public double lon;
41 | /**
42 | * easting of point (meters).
43 | **********************************************************************/
44 | public double x;
45 | /**
46 | * northing of point (meters).
47 | **********************************************************************/
48 | public double y;
49 | /**
50 | * azimuth of geodesic at point (degrees).
51 | **********************************************************************/
52 | public double azi;
53 | /**
54 | * reciprocal of azimuthal scale at point.
55 | **********************************************************************/
56 | public double rk;
57 |
58 | /**
59 | * Initialize all the fields to Double.NaN.
60 | **********************************************************************/
61 | public GnomonicData() {
62 | lat0 = lon0 = lat = lon = x = y = azi = rk = Double.NaN;
63 | }
64 |
65 | /**
66 | * Constructor initializing all the fields for gnomonic projection of a point
67 | * (lat, lon) given a center point of projection (lat0,
68 | * lon0).
69 | *
70 | * @param lat0
71 | * latitude of center point of projection (degrees).
72 | * @param lon0
73 | * longitude of center point of projection (degrees).
74 | * @param lat
75 | * latitude of point (degrees).
76 | * @param lon
77 | * longitude of point (degrees).
78 | * @param x
79 | * easting of point (meters).
80 | * @param y
81 | * northing of point (meters).
82 | * @param azi
83 | * azimuth of geodesic at point (degrees).
84 | * @param rk
85 | * reciprocal of azimuthal scale at point.
86 | */
87 | public GnomonicData(double lat0, double lon0, double lat, double lon,
88 | double x, double y, double azi, double rk) {
89 | this.lat0 = lat0;
90 | this.lon0 = lon0;
91 | this.lat = lat;
92 | this.lon = lon;
93 | this.x = x;
94 | this.y = y;
95 | this.azi = azi;
96 | this.rk = rk;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/net/sf/geographiclib/Pair.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Implementation of the net.sf.geographiclib.Pair class
3 | *
4 | * Copyright (c) Charles Karney (2013-2020)
13 | * This duplicates the C++ class {@code std::pair
27 | * @param first the first member of the pair.
28 | * @param second the second member of the pair.
29 | **********************************************************************/
30 | public Pair(double first, double second)
31 | { this.first = first; this.second = second; }
32 | /**
33 | * No-argument Constructor
34 | **********************************************************************/
35 | public Pair() {}
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/net/sf/geographiclib/PolygonArea.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Implementation of the net.sf.geographiclib.PolygonArea class
3 | *
4 | * Copyright (c) Charles Karney (2013-2022)
13 | * This computes the area of a geodesic polygon using the method given
14 | * Section 6 of
15 | *
25 | * Arbitrarily complex polygons are allowed. In the case self-intersecting of
26 | * polygons the area is accumulated "algebraically", e.g., the areas of the 2
27 | * loops in a figure-8 polygon will partially cancel.
28 | *
29 | * This class lets you add vertices one at a time to the polygon. The area
30 | * and perimeter are accumulated at two times the standard floating point
31 | * precision to guard against the loss of accuracy with many-sided polygons.
32 | * At any point you can ask for the perimeter and area so far. There's an
33 | * option to treat the points as defining a polyline instead of a polygon; in
34 | * that case, only the perimeter is computed.
35 | *
36 | * Example of use:
37 | *
152 | * @param earth the Geodesic object to use for geodesic calculations.
153 | * @param polyline if true that treat the points as defining a polyline
154 | * instead of a polygon.
155 | **********************************************************************/
156 | public PolygonArea(Geodesic earth, boolean polyline) {
157 | _earth = earth;
158 | _area0 = _earth.EllipsoidArea();
159 | _polyline = polyline;
160 | _mask = GeodesicMask.LATITUDE | GeodesicMask.LONGITUDE |
161 | GeodesicMask.DISTANCE |
162 | (_polyline ? GeodesicMask.NONE :
163 | GeodesicMask.AREA | GeodesicMask.LONG_UNROLL);
164 | _perimetersum = new Accumulator(0);
165 | if (!_polyline)
166 | _areasum = new Accumulator(0);
167 | Clear();
168 | }
169 |
170 | /**
171 | * Clear PolygonArea, allowing a new polygon to be started.
172 | **********************************************************************/
173 | public void Clear() {
174 | _num = 0;
175 | _crossings = 0;
176 | _perimetersum.Set(0);
177 | if (!_polyline) _areasum.Set(0);
178 | _lat0 = _lon0 = _lat1 = _lon1 = Double.NaN;
179 | }
180 |
181 | /**
182 | * Add a point to the polygon or polyline.
183 | *
184 | * @param lat the latitude of the point (degrees).
185 | * @param lon the latitude of the point (degrees).
186 | *
187 | * lat should be in the range [−90°, 90°].
188 | **********************************************************************/
189 | public void AddPoint(double lat, double lon) {
190 | if (_num == 0) {
191 | _lat0 = _lat1 = lat;
192 | _lon0 = _lon1 = lon;
193 | } else {
194 | GeodesicData g = _earth.Inverse(_lat1, _lon1, lat, lon, _mask);
195 | _perimetersum.Add(g.s12);
196 | if (!_polyline) {
197 | _areasum.Add(g.S12);
198 | _crossings += transit(_lon1, lon);
199 | }
200 | _lat1 = lat; _lon1 = lon;
201 | }
202 | ++_num;
203 | }
204 |
205 | /**
206 | * Add an edge to the polygon or polyline.
207 | *
208 | * @param azi azimuth at current point (degrees).
209 | * @param s distance from current point to next point (meters).
210 | *
211 | * This does nothing if no points have been added yet. Use
212 | * PolygonArea.CurrentPoint to determine the position of the new vertex.
213 | **********************************************************************/
214 | public void AddEdge(double azi, double s) {
215 | if (_num > 0) { // Do nothing if _num is zero
216 | GeodesicData g = _earth.Direct(_lat1, _lon1, azi, s, _mask);
217 | _perimetersum.Add(g.s12);
218 | if (!_polyline) {
219 | _areasum.Add(g.S12);
220 | _crossings += transitdirect(_lon1, g.lon2);
221 | }
222 | _lat1 = g.lat2; _lon1 = g.lon2;
223 | ++_num;
224 | }
225 | }
226 |
227 | /**
228 | * Return the results so far.
229 | *
230 | * @return PolygonResult(num, perimeter, area) where
231 | * num is the number of vertices, perimeter is the perimeter
232 | * of the polygon or the length of the polyline (meters), and area
233 | * is the area of the polygon (meters2) or Double.NaN of
234 | * polyline is true in the constructor.
235 | *
236 | * Counter-clockwise traversal counts as a positive area.
237 | **********************************************************************/
238 | public PolygonResult Compute() { return Compute(false, true); }
239 | /**
240 | * Return the results so far.
241 | *
242 | * @param reverse if true then clockwise (instead of counter-clockwise)
243 | * traversal counts as a positive area.
244 | * @param sign if true then return a signed result for the area if
245 | * the polygon is traversed in the "wrong" direction instead of returning
246 | * the area for the rest of the earth.
247 | * @return PolygonResult(num, perimeter, area) where
248 | * num is the number of vertices, perimeter is the perimeter
249 | * of the polygon or the length of the polyline (meters), and area
250 | * is the area of the polygon (meters2) or Double.NaN of
251 | * polyline is true in the constructor.
252 | *
253 | * More points can be added to the polygon after this call.
254 | **********************************************************************/
255 | public PolygonResult Compute(boolean reverse, boolean sign) {
256 | if (_num < 2)
257 | return new PolygonResult(_num, 0, _polyline ? Double.NaN : 0);
258 | if (_polyline)
259 | return new PolygonResult(_num, _perimetersum.Sum(), Double.NaN);
260 |
261 | GeodesicData g = _earth.Inverse(_lat1, _lon1, _lat0, _lon0, _mask);
262 | Accumulator tempsum = new Accumulator(_areasum);
263 | tempsum.Add(g.S12);
264 |
265 | return
266 | new PolygonResult(_num, _perimetersum.Sum(g.s12),
267 | AreaReduceA(tempsum, _area0,
268 | _crossings + transit(_lon1, _lon0),
269 | reverse, sign));
270 | }
271 |
272 | /**
273 | * Return the results assuming a tentative final test point is added;
274 | * however, the data for the test point is not saved. This lets you report
275 | * a running result for the perimeter and area as the user moves the mouse
276 | * cursor. Ordinary floating point arithmetic is used to accumulate the
277 | * data for the test point; thus the area and perimeter returned are less
278 | * accurate than if AddPoint and Compute are used.
279 | *
280 | * @param lat the latitude of the test point (degrees).
281 | * @param lon the longitude of the test point (degrees).
282 | * @param reverse if true then clockwise (instead of counter-clockwise)
283 | * traversal counts as a positive area.
284 | * @param sign if true then return a signed result for the area if
285 | * the polygon is traversed in the "wrong" direction instead of returning
286 | * the area for the rest of the earth.
287 | * @return PolygonResult(num, perimeter, area) where
288 | * num is the number of vertices, perimeter is the perimeter
289 | * of the polygon or the length of the polyline (meters), and area
290 | * is the area of the polygon (meters2) or Double.NaN of
291 | * polyline is true in the constructor.
292 | *
293 | * lat should be in the range [−90°, 90°].
294 | **********************************************************************/
295 | public PolygonResult TestPoint(double lat, double lon,
296 | boolean reverse, boolean sign) {
297 | if (_num == 0)
298 | return new PolygonResult(1, 0, _polyline ? Double.NaN : 0);
299 |
300 | double perimeter = _perimetersum.Sum();
301 | double tempsum = _polyline ? 0 : _areasum.Sum();
302 | int crossings = _crossings;
303 | int num = _num + 1;
304 | for (int i = 0; i < (_polyline ? 1 : 2); ++i) {
305 | GeodesicData g =
306 | _earth.Inverse(i == 0 ? _lat1 : lat, i == 0 ? _lon1 : lon,
307 | i != 0 ? _lat0 : lat, i != 0 ? _lon0 : lon,
308 | _mask);
309 | perimeter += g.s12;
310 | if (!_polyline) {
311 | tempsum += g.S12;
312 | crossings += transit(i == 0 ? _lon1 : lon,
313 | i != 0 ? _lon0 : lon);
314 | }
315 | }
316 |
317 | if (_polyline)
318 | return new PolygonResult(num, perimeter, Double.NaN);
319 |
320 | return new PolygonResult(num, perimeter,
321 | AreaReduceB(tempsum, _area0, crossings,
322 | reverse, sign));
323 | }
324 |
325 | /**
326 | * Return the results assuming a tentative final test point is added via an
327 | * azimuth and distance; however, the data for the test point is not saved.
328 | * This lets you report a running result for the perimeter and area as the
329 | * user moves the mouse cursor. Ordinary floating point arithmetic is used
330 | * to accumulate the data for the test point; thus the area and perimeter
331 | * returned are less accurate than if AddPoint and Compute are used.
332 | *
333 | * @param azi azimuth at current point (degrees).
334 | * @param s distance from current point to final test point (meters).
335 | * @param reverse if true then clockwise (instead of counter-clockwise)
336 | * traversal counts as a positive area.
337 | * @param sign if true then return a signed result for the area if
338 | * the polygon is traversed in the "wrong" direction instead of returning
339 | * the area for the rest of the earth.
340 | * @return PolygonResult(num, perimeter, area) where
341 | * num is the number of vertices, perimeter is the perimeter
342 | * of the polygon or the length of the polyline (meters), and area
343 | * is the area of the polygon (meters2) or Double.NaN of
344 | * polyline is true in the constructor.
345 | **********************************************************************/
346 | public PolygonResult TestEdge(double azi, double s,
347 | boolean reverse, boolean sign) {
348 | if (_num == 0) // we don't have a starting point!
349 | return new PolygonResult(0, Double.NaN, Double.NaN);
350 |
351 | int num = _num + 1;
352 | double perimeter = _perimetersum.Sum() + s;
353 | if (_polyline)
354 | return new PolygonResult(num, perimeter, Double.NaN);
355 |
356 | double tempsum = _areasum.Sum();
357 | int crossings = _crossings;
358 | {
359 | double lat, lon, s12, S12, t;
360 | GeodesicData g =
361 | _earth.Direct(_lat1, _lon1, azi, false, s, _mask);
362 | tempsum += g.S12;
363 | crossings += transitdirect(_lon1, g.lon2);
364 | crossings += transit(g.lon2, _lon0);
365 | g = _earth.Inverse(g.lat2, g.lon2, _lat0, _lon0, _mask);
366 | perimeter += g.s12;
367 | tempsum += g.S12;
368 | }
369 |
370 | return new PolygonResult(num, perimeter,
371 | AreaReduceB(tempsum, _area0, crossings,
372 | reverse, sign));
373 | }
374 |
375 | /**
376 | * @return a the equatorial radius of the ellipsoid (meters). This is
377 | * the value inherited from the Geodesic object used in the constructor.
378 | **********************************************************************/
379 | public double EquatorialRadius() { return _earth.EquatorialRadius(); }
380 |
381 | /**
382 | * @return f the flattening of the ellipsoid. This is the value
383 | * inherited from the Geodesic object used in the constructor.
384 | **********************************************************************/
385 | public double Flattening() { return _earth.Flattening(); }
386 |
387 | /**
388 | * Report the previous vertex added to the polygon or polyline.
389 | *
390 | * @return Pair(lat, lon), the current latitude and longitude.
391 | *
392 | * If no points have been added, then Double.NaN is returned. Otherwise,
393 | * lon will be in the range [−180°, 180°].
394 | **********************************************************************/
395 | public Pair CurrentPoint() { return new Pair(_lat1, _lon1); }
396 | }
397 |
--------------------------------------------------------------------------------
/src/main/java/net/sf/geographiclib/PolygonResult.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Implementation of the net.sf.geographiclib.PolygonResult class
3 | *
4 | * Copyright (c) Charles Karney (2013)
29 | * @param num the number of vertices in the polygon.
30 | * @param perimeter the perimeter of the polygon or the length of the
31 | * polyline (meters).
32 | * @param area the area of the polygon (meters2).
33 | **********************************************************************/
34 | public PolygonResult(int num, double perimeter, double area) {
35 | this.num = num;
36 | this.perimeter = perimeter;
37 | this.area = area;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/net/sf/geographiclib/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
7 | * The documentation for other versions is available at
8 | *
10 | * Licensed under the
11 | * MIT/X11 License; see
12 | *
13 | * LICENSE.txt.
14 | *
15 | *
17 | * GeographicLib-Java is a Java implementation of the geodesic algorithms from
18 | * GeographicLib. This is a
19 | * self-contained library which makes it easy to do geodesic computations for
20 | * an ellipsoid of revolution in a Java program. It requires Java version 1.8
21 | * or later.
22 | *
23 | *
25 | * Download either the source or the pre-built package as follows:
26 | *
27 | *
48 | * Included with the source are 3 small test programs
49 | *
61 | * Here, for example, is {@code Inverse.java}
87 | * Here's how to compile and run {@code Inverse.java} using
88 | * maven (the recommended way) and
89 | * without using maven. (Thanks to Skip Breidbach for supplying the maven
90 | * support.)
91 | *
92 | *
118 | * The important classes are
119 | *
143 | * The documentation is generated using javadoc when
144 | * {@code mvn package -P release} is run (the top of the documentation tree is
145 | * {@code target/apidocs/index.html}). This is also available on the web at
146 | *
147 | * https://geographiclib.sourceforge.io/Java/doc/index.html.
148 | *
149 | *
44 | *
51 | *
54 | * {@code
55 | * import net.sf.geographiclib.*;
56 | * public class GeodesicLineTest {
57 | * public static void main(String[] args) {
58 | * // Print waypoints between JFK and SIN
59 | * Geodesic geod = Geodesic.WGS84;
60 | * double
61 | * lat1 = 40.640, lon1 = -73.779, // JFK
62 | * lat2 = 1.359, lon2 = 103.989; // SIN
63 | * GeodesicLine line = geod.InverseLine(lat1, lon1, lat2, lon2,
64 | * GeodesicMask.DISTANCE_IN |
65 | * GeodesicMask.LATITUDE |
66 | * GeodesicMask.LONGITUDE);
67 | * double ds0 = 500e3; // Nominal distance between points = 500 km
68 | * // The number of intervals
69 | * int num = (int)(Math.ceil(line.Distance() / ds0));
70 | * {
71 | * // Use intervals of equal length
72 | * double ds = line.Distance() / num;
73 | * for (int i = 0; i <= num; ++i) {
74 | * GeodesicData g = line.Position(i * ds,
75 | * GeodesicMask.LATITUDE |
76 | * GeodesicMask.LONGITUDE);
77 | * System.out.println(i + " " + g.lat2 + " " + g.lon2);
78 | * }
79 | * }
80 | * {
81 | * // Slightly faster, use intervals of equal arc length
82 | * double da = line.Arc() / num;
83 | * for (int i = 0; i <= num; ++i) {
84 | * GeodesicData g = line.ArcPosition(i * da,
85 | * GeodesicMask.LATITUDE |
86 | * GeodesicMask.LONGITUDE);
87 | * System.out.println(i + " " + g.lat2 + " " + g.lon2);
88 | * }
89 | * }
90 | * }
91 | * }}
92 | **********************************************************************/
93 | public class GeodesicLine {
94 |
95 | private static final int nC1_ = Geodesic.nC1_;
96 | private static final int nC1p_ = Geodesic.nC1p_;
97 | private static final int nC2_ = Geodesic.nC2_;
98 | private static final int nC3_ = Geodesic.nC3_;
99 | private static final int nC4_ = Geodesic.nC4_;
100 |
101 | private double _lat1, _lon1, _azi1;
102 | private double _a, _f, _b, _c2, _f1, _salp0, _calp0, _k2,
103 | _salp1, _calp1, _ssig1, _csig1, _dn1, _stau1, _ctau1, _somg1, _comg1,
104 | _A1m1, _A2m1, _A3c, _B11, _B21, _B31, _A4, _B41;
105 | private double _a13, _s13;
106 | // index zero elements of _C1a, _C1pa, _C2a, _C3a are unused
107 | private double _C1a[], _C1pa[], _C2a[], _C3a[],
108 | _C4a[]; // all the elements of _C4a are used
109 | private int _caps;
110 |
111 | /**
112 | * Constructor for a geodesic line staring at latitude lat1, longitude
113 | * lon1, and azimuth azi1 (all in degrees).
114 | *
149 | *
176 | **********************************************************************/
177 | public GeodesicLine(Geodesic g,
178 | double lat1, double lon1, double azi1,
179 | int caps) {
180 | azi1 = GeoMath.AngNormalize(azi1);
181 | double salp1, calp1;
182 | Pair p = new Pair();
183 | // Guard against underflow in salp0
184 | GeoMath.sincosd(p, GeoMath.AngRound(azi1));
185 | salp1 = p.first; calp1 = p.second;
186 | LineInit(g, lat1, lon1, azi1, salp1, calp1, caps, p);
187 | }
188 |
189 | private void LineInit(Geodesic g,
190 | double lat1, double lon1,
191 | double azi1, double salp1, double calp1,
192 | int caps, Pair p) {
193 | _a = g._a;
194 | _f = g._f;
195 | _b = g._b;
196 | _c2 = g._c2;
197 | _f1 = g._f1;
198 | // Always allow latitude and azimuth and unrolling the longitude
199 | _caps = caps | GeodesicMask.LATITUDE | GeodesicMask.AZIMUTH |
200 | GeodesicMask.LONG_UNROLL;
201 |
202 | _lat1 = GeoMath.LatFix(lat1);
203 | _lon1 = lon1;
204 | _azi1 = azi1; _salp1 = salp1; _calp1 = calp1;
205 | double cbet1, sbet1;
206 | GeoMath.sincosd(p, GeoMath.AngRound(_lat1));
207 | sbet1 = _f1 * p.first; cbet1 = p.second;
208 | // Ensure cbet1 = +epsilon at poles
209 | GeoMath.norm(p, sbet1, cbet1);
210 | sbet1 = p.first; cbet1 = Math.max(Geodesic.tiny_, p.second);
211 | _dn1 = Math.sqrt(1 + g._ep2 * GeoMath.sq(sbet1));
212 |
213 | // Evaluate alp0 from sin(alp1) * cos(bet1) = sin(alp0),
214 | _salp0 = _salp1 * cbet1; // alp0 in [0, pi/2 - |bet1|]
215 | // Alt: calp0 = Math.hypot(sbet1, calp1 * cbet1). The following
216 | // is slightly better (consider the case salp1 = 0).
217 | _calp0 = Math.hypot(_calp1, _salp1 * sbet1);
218 | // Evaluate sig with tan(bet1) = tan(sig1) * cos(alp1).
219 | // sig = 0 is nearest northward crossing of equator.
220 | // With bet1 = 0, alp1 = pi/2, we have sig1 = 0 (equatorial line).
221 | // With bet1 = pi/2, alp1 = -pi, sig1 = pi/2
222 | // With bet1 = -pi/2, alp1 = 0 , sig1 = -pi/2
223 | // Evaluate omg1 with tan(omg1) = sin(alp0) * tan(sig1).
224 | // With alp0 in (0, pi/2], quadrants for sig and omg coincide.
225 | // No atan2(0,0) ambiguity at poles since cbet1 = +epsilon.
226 | // With alp0 = 0, omg1 = 0 for alp1 = 0, omg1 = pi for alp1 = pi.
227 | _ssig1 = sbet1; _somg1 = _salp0 * sbet1;
228 | _csig1 = _comg1 = sbet1 != 0 || _calp1 != 0 ? cbet1 * _calp1 : 1;
229 | GeoMath.norm(p, _ssig1, _csig1);
230 | _ssig1 = p.first; _csig1 = p.second; // sig1 in (-pi, pi]
231 | // GeoMath.norm(_somg1, _comg1); -- don't need to normalize!
232 |
233 | _k2 = GeoMath.sq(_calp0) * g._ep2;
234 | double eps = _k2 / (2 * (1 + Math.sqrt(1 + _k2)) + _k2);
235 |
236 | if ((_caps & GeodesicMask.CAP_C1) != 0) {
237 | _A1m1 = Geodesic.A1m1f(eps);
238 | _C1a = new double[nC1_ + 1];
239 | Geodesic.C1f(eps, _C1a);
240 | _B11 = Geodesic.SinCosSeries(true, _ssig1, _csig1, _C1a);
241 | double s = Math.sin(_B11), c = Math.cos(_B11);
242 | // tau1 = sig1 + B11
243 | _stau1 = _ssig1 * c + _csig1 * s;
244 | _ctau1 = _csig1 * c - _ssig1 * s;
245 | // Not necessary because C1pa reverts C1a
246 | // _B11 = -SinCosSeries(true, _stau1, _ctau1, _C1pa, nC1p_);
247 | }
248 |
249 | if ((_caps & GeodesicMask.CAP_C1p) != 0) {
250 | _C1pa = new double[nC1p_ + 1];
251 | Geodesic.C1pf(eps, _C1pa);
252 | }
253 |
254 | if ((_caps & GeodesicMask.CAP_C2) != 0) {
255 | _C2a = new double[nC2_ + 1];
256 | _A2m1 = Geodesic.A2m1f(eps);
257 | Geodesic.C2f(eps, _C2a);
258 | _B21 = Geodesic.SinCosSeries(true, _ssig1, _csig1, _C2a);
259 | }
260 |
261 | if ((_caps & GeodesicMask.CAP_C3) != 0) {
262 | _C3a = new double[nC3_];
263 | g.C3f(eps, _C3a);
264 | _A3c = -_f * _salp0 * g.A3f(eps);
265 | _B31 = Geodesic.SinCosSeries(true, _ssig1, _csig1, _C3a);
266 | }
267 |
268 | if ((_caps & GeodesicMask.CAP_C4) != 0) {
269 | _C4a = new double[nC4_];
270 | g.C4f(eps, _C4a);
271 | // Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0)
272 | _A4 = GeoMath.sq(_a) * _calp0 * _salp0 * g._e2;
273 | _B41 = Geodesic.SinCosSeries(false, _ssig1, _csig1, _C4a);
274 | }
275 | }
276 |
277 | protected GeodesicLine(Geodesic g,
278 | double lat1, double lon1,
279 | double azi1, double salp1, double calp1,
280 | int caps, boolean arcmode, double s13_a13) {
281 | Pair p = new Pair();
282 | LineInit(g, lat1, lon1, azi1, salp1, calp1, caps, p);
283 | GenSetDistance(arcmode, s13_a13);
284 | }
285 |
286 | /**
287 | * A default constructor. If GeodesicLine.Position is called on the
288 | * resulting object, it returns immediately (without doing any calculations).
289 | * The object can be set with a call to {@link Geodesic.Line}. Use {@link
290 | * Init()} to test whether object is still in this uninitialized state.
291 | * (This constructor was useful in C++, e.g., to allow vectors of
292 | * GeodesicLine objects. It may not be needed in Java, so make it private.)
293 | **********************************************************************/
294 | private GeodesicLine() { _caps = 0; }
295 |
296 | /**
297 | * Compute the position of point 2 which is a distance s12 (meters)
298 | * from point 1.
299 | *
397 | *
421 | *
22 | *
31 | *
59 | *
60 | * (K(T) − K(C))
61 | * l2 t / 32.
62 | *
63 | *
64 | * where K is the Gaussian curvature.
65 | *
78 | *
104 | *
108 | * // Example of using the Gnomonic.java class
109 | * import net.sf.geographiclib.Geodesic;
110 | * import net.sf.geographiclib.Gnomonic;
111 | * import net.sf.geographiclib.GnomonicData;
112 | * public class ExampleGnomonic {
113 | * public static void main(String[] args) {
114 | * Geodesic geod = Geodesic.WGS84;
115 | * double lat0 = 48 + 50 / 60.0, lon0 = 2 + 20 / 60.0; // Paris
116 | * Gnomonic gnom = new Gnomonic(geod);
117 | * {
118 | * // Sample forward calculation
119 | * double lat = 50.9, lon = 1.8; // Calais
120 | * GnomonicData proj = gnom.Forward(lat0, lon0, lat, lon);
121 | * System.out.println(proj.x + " " + proj.y);
122 | * }
123 | * {
124 | * // Sample reverse calculation
125 | * double x = -38e3, y = 230e3;
126 | * GnomonicData proj = gnom.Reverse(lat0, lon0, x, y);
127 | * System.out.println(proj.lat + " " + proj.lon);
128 | * }
129 | * }
130 | * }
131 | *
132 | */
133 |
134 | public class Gnomonic {
135 | private static final double eps_ = 0.01 * Math.sqrt(Math.ulp(1.0));
136 | private static final int numit_ = 10;
137 | private Geodesic _earth;
138 | private double _a, _f;
139 |
140 | /**
141 | * Constructor for Gnomonic.
142 | *
16 | *
24 | *
38 | * {@code
39 | * // Compute the area of a geodesic polygon.
40 | *
41 | * // This program reads lines with lat, lon for each vertex of a polygon.
42 | * // At the end of input, the program prints the number of vertices,
43 | * // the perimeter of the polygon and its area (for the WGS84 ellipsoid).
44 | *
45 | * import java.util.*;
46 | * import net.sf.geographiclib.*;
47 | *
48 | * public class Planimeter {
49 | * public static void main(String[] args) {
50 | * PolygonArea p = new PolygonArea(Geodesic.WGS84, false);
51 | * try {
52 | * Scanner in = new Scanner(System.in);
53 | * while (true) {
54 | * double lat = in.nextDouble(), lon = in.nextDouble();
55 | * p.AddPoint(lat, lon);
56 | * }
57 | * }
58 | * catch (Exception e) {}
59 | * PolygonResult r = p.Compute();
60 | * System.out.println(r.num + " " + r.perimeter + " " + r.area);
61 | * }
62 | * }}
63 | **********************************************************************/
64 | public class PolygonArea {
65 |
66 | private Geodesic _earth;
67 | private double _area0; // Full ellipsoid area
68 | private boolean _polyline; // Assume polyline (don't close and skip area)
69 | private int _mask;
70 | private int _num;
71 | private int _crossings;
72 | private Accumulator _areasum, _perimetersum;
73 | private double _lat0, _lon0, _lat1, _lon1;
74 | private static int transit(double lon1, double lon2) {
75 | // Return 1 or -1 if crossing prime meridian in east or west direction.
76 | // Otherwise return zero.
77 | // Compute lon12 the same way as Geodesic.Inverse.
78 | Pair p = new Pair();
79 | GeoMath.AngDiff(p, lon1, lon2);
80 | lon1 = GeoMath.AngNormalize(lon1);
81 | lon2 = GeoMath.AngNormalize(lon2);
82 | double lon12 = p.first;
83 | return
84 | lon12 > 0 && ((lon1 < 0 && lon2 >= 0) ||
85 | (lon1 > 0 && lon2 == 0)) ? 1 :
86 | (lon12 < 0 && lon1 >= 0 && lon2 < 0 ? -1 : 0);
87 | }
88 | // an alternate version of transit to deal with longitudes in the direct
89 | // problem.
90 | private static int transitdirect(double lon1, double lon2) {
91 | // We want to compute exactly
92 | // int(floor(lon2 / 360)) - int(floor(lon1 / 360))
93 | lon1 = Math.IEEEremainder(lon1, 720.0);
94 | lon2 = Math.IEEEremainder(lon2, 720.0);
95 | return ( (lon2 >= 0 && lon2 < 360 ? 0 : 1) -
96 | (lon1 >= 0 && lon1 < 360 ? 0 : 1) );
97 | }
98 | // reduce Accumulator area to allowed range
99 | private static double AreaReduceA(Accumulator area, double area0,
100 | int crossings,
101 | boolean reverse, boolean sign) {
102 | area.Remainder(area0);
103 | if ((crossings & 1) != 0)
104 | area.Add((area.Sum() < 0 ? 1 : -1) * area0/2);
105 | // area is with the clockwise sense. If !reverse convert to
106 | // counter-clockwise convention.
107 | if (!reverse)
108 | area.Negate();
109 | // If sign put area in (-area0/2, area0/2], else put area in [0, area0)
110 | if (sign) {
111 | if (area.Sum() > area0/2)
112 | area.Add(-area0);
113 | else if (area.Sum() <= -area0/2)
114 | area.Add(+area0);
115 | } else {
116 | if (area.Sum() >= area0)
117 | area.Add(-area0);
118 | else if (area.Sum() < 0)
119 | area.Add(+area0);
120 | }
121 | return 0 + area.Sum();
122 | }
123 | // reduce double area to allowed range
124 | private static double AreaReduceB(double area, double area0,
125 | int crossings,
126 | boolean reverse, boolean sign) {
127 | area = Math.IEEEremainder(area, area0);
128 | if ((crossings & 1) != 0)
129 | area += (area < 0 ? 1 : -1) * area0/2;
130 | // area is with the clockwise sense. If !reverse convert to
131 | // counter-clockwise convention.
132 | if (!reverse)
133 | area *= -1;
134 | // If sign put area in (-area0/2, area0/2], else put area in [0, area0)
135 | if (sign) {
136 | if (area > area0/2)
137 | area -= area0;
138 | else if (area <= -area0/2)
139 | area += area0;
140 | } else {
141 | if (area >= area0)
142 | area -= area0;
143 | else if (area < 0)
144 | area += area0;
145 | }
146 | return 0 + area;
147 | }
148 |
149 | /**
150 | * Constructor for PolygonArea.
151 | * Geodesic routines from GeographicLib implemented in Java
3 | * @author Charles F. F. Karney (charles@karney.com)
4 | * @version 2.0
5 | *
6 | * https://geographiclib.sourceforge.io/Java/
.
9 | * Abstract
16 | * Downloading
24 | * The pre-built package
28 | * GeographicLib-Java is available as a
29 | *
30 | * pre-built package on Maven Central (thanks to Chris Bennight for help on
31 | * this deployment). So, if you use
32 | * maven to build your code, you just
33 | * need to include the dependency {@code
34 | *
39 | * in your {@code pom.xml}.
40 | *
41 | * Obtaining the source
42 | * The source is hosted on
43 | * github.
44 | * Releases are tagged as v1.52, v2.0, etc.
45 | *
46 | * Sample programs
47 | *
50 | *
60 | * {@code
62 | * // Solve the inverse geodesic problem.
63 | * //
64 | * // This program reads in lines with lat1, lon1, lat2, lon2 and prints
65 | * // out lines with azi1, azi2, s12 (for the WGS84 ellipsoid).
66 | *
67 | * import java.util.*;
68 | * import net.sf.geographiclib.*;
69 | * public class Inverse {
70 | * public static void main(String[] args) {
71 | * try {
72 | * Scanner in = new Scanner(System.in);
73 | * double lat1, lon1, lat2, lon2;
74 | * while (true) {
75 | * lat1 = in.nextDouble(); lon1 = in.nextDouble();
76 | * lat2 = in.nextDouble(); lon2 = in.nextDouble();
77 | * GeodesicData g = Geodesic.WGS84.Inverse(lat1, lon1, lat2, lon2);
78 | * System.out.format("%.11f %.11f %.6f%n", g.azi1, g.azi2, g.s12);
79 | * }
80 | * }
81 | * catch (Exception e) {}
82 | * }
83 | * }}
84 | *
85 | * Compiling and running a sample program
86 | * Using maven
93 | * The sample code includes a {@code pom.xml} which specifies
94 | * GeographicLib-Java as a dependency which maven will download from Maven
95 | * Central. You can compile and run Inverse.java with
96 | * cd inverse
97 | * mvn compile
98 | * echo -30 0 29.5 179.5 | mvn -q exec:java
99 | *
100 | * Without using maven
101 | * Compile and run as follows
102 | * cd inverse/src/main/java
103 | * javac -cp .:../../../../src/main/java Inverse.java
104 | * echo -30 0 29.5 179.5 | java -cp .:../../../../src/main/java Inverse
105 | *
106 | * Using the library
107 | *
108 | *
117 | *
110 | * import net.sf.geographiclib.*
111 | * in your source code.
112 | *
120 | *
142 | * External links
150 | *
151 | *
182 | *
183 | * Change log
184 | *
185 | *
355 | **********************************************************************/
356 | package net.sf.geographiclib;
357 |
--------------------------------------------------------------------------------
/src/test/java/net/sf/geographiclib/GeodesicTest.java:
--------------------------------------------------------------------------------
1 | package net.sf.geographiclib;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertTrue;
5 | import org.junit.Test;
6 |
7 | public class GeodesicTest {
8 |
9 | private static final PolygonArea polygon =
10 | new PolygonArea(Geodesic.WGS84, false);
11 | private static final PolygonArea polyline =
12 | new PolygonArea(Geodesic.WGS84, true);
13 |
14 | private static PolygonResult Planimeter(double points[][]) {
15 | polygon.Clear();
16 | for (int i = 0; i < points.length; ++i) {
17 | polygon.AddPoint(points[i][0], points[i][1]);
18 | }
19 | return polygon.Compute(false, true);
20 | }
21 |
22 | private static PolygonResult PolyLength(double points[][]) {
23 | polyline.Clear();
24 | for (int i = 0; i < points.length; ++i) {
25 | polyline.AddPoint(points[i][0], points[i][1]);
26 | }
27 | return polyline.Compute(false, true);
28 | }
29 |
30 | private static final double testcases[][] = {
31 | {35.60777, -139.44815, 111.098748429560326,
32 | -11.17491, -69.95921, 129.289270889708762,
33 | 8935244.5604818305, 80.50729714281974, 6273170.2055303837,
34 | 0.16606318447386067, 0.16479116945612937, 12841384694976.432},
35 | {55.52454, 106.05087, 22.020059880982801,
36 | 77.03196, 197.18234, 109.112041110671519,
37 | 4105086.1713924406, 36.892740690445894, 3828869.3344387607,
38 | 0.80076349608092607, 0.80101006984201008, 61674961290615.615},
39 | {-21.97856, 142.59065, -32.44456876433189,
40 | 41.84138, 98.56635, -41.84359951440466,
41 | 8394328.894657671, 75.62930491011522, 6161154.5773110616,
42 | 0.24816339233950381, 0.24930251203627892, -6637997720646.717},
43 | {-66.99028, 112.2363, 173.73491240878403,
44 | -12.70631, 285.90344, 2.512956620913668,
45 | 11150344.2312080241, 100.278634181155759, 6289939.5670446687,
46 | -0.17199490274700385, -0.17722569526345708, -121287239862139.744},
47 | {-17.42761, 173.34268, -159.033557661192928,
48 | -15.84784, 5.93557, -20.787484651536988,
49 | 16076603.1631180673, 144.640108810286253, 3732902.1583877189,
50 | -0.81273638700070476, -0.81299800519154474, 97825992354058.708},
51 | {32.84994, 48.28919, 150.492927788121982,
52 | -56.28556, 202.29132, 48.113449399816759,
53 | 16727068.9438164461, 150.565799985466607, 3147838.1910180939,
54 | -0.87334918086923126, -0.86505036767110637, -72445258525585.010},
55 | {6.96833, 52.74123, 92.581585386317712,
56 | -7.39675, 206.17291, 90.721692165923907,
57 | 17102477.2496958388, 154.147366239113561, 2772035.6169917581,
58 | -0.89991282520302447, -0.89986892177110739, -1311796973197.995},
59 | {-50.56724, -16.30485, -105.439679907590164,
60 | -33.56571, -94.97412, -47.348547835650331,
61 | 6455670.5118668696, 58.083719495371259, 5409150.7979815838,
62 | 0.53053508035997263, 0.52988722644436602, 41071447902810.047},
63 | {-58.93002, -8.90775, 140.965397902500679,
64 | -8.91104, 133.13503, 19.255429433416599,
65 | 11756066.0219864627, 105.755691241406877, 6151101.2270708536,
66 | -0.26548622269867183, -0.27068483874510741, -86143460552774.735},
67 | {-68.82867, -74.28391, 93.774347763114881,
68 | -50.63005, -8.36685, 34.65564085411343,
69 | 3956936.926063544, 35.572254987389284, 3708890.9544062657,
70 | 0.81443963736383502, 0.81420859815358342, -41845309450093.787},
71 | {-10.62672, -32.0898, -86.426713286747751,
72 | 5.883, -134.31681, -80.473780971034875,
73 | 11470869.3864563009, 103.387395634504061, 6184411.6622659713,
74 | -0.23138683500430237, -0.23155097622286792, 4198803992123.548},
75 | {-21.76221, 166.90563, 29.319421206936428,
76 | 48.72884, 213.97627, 43.508671946410168,
77 | 9098627.3986554915, 81.963476716121964, 6299240.9166992283,
78 | 0.13965943368590333, 0.14152969707656796, 10024709850277.476},
79 | {-19.79938, -174.47484, 71.167275780171533,
80 | -11.99349, -154.35109, 65.589099775199228,
81 | 2319004.8601169389, 20.896611684802389, 2267960.8703918325,
82 | 0.93427001867125849, 0.93424887135032789, -3935477535005.785},
83 | {-11.95887, -116.94513, 92.712619830452549,
84 | 4.57352, 7.16501, 78.64960934409585,
85 | 13834722.5801401374, 124.688684161089762, 5228093.177931598,
86 | -0.56879356755666463, -0.56918731952397221, -9919582785894.853},
87 | {-87.85331, 85.66836, -65.120313040242748,
88 | 66.48646, 16.09921, -4.888658719272296,
89 | 17286615.3147144645, 155.58592449699137, 2635887.4729110181,
90 | -0.90697975771398578, -0.91095608883042767, 42667211366919.534},
91 | {1.74708, 128.32011, -101.584843631173858,
92 | -11.16617, 11.87109, -86.325793296437476,
93 | 12942901.1241347408, 116.650512484301857, 5682744.8413270572,
94 | -0.44857868222697644, -0.44824490340007729, 10763055294345.653},
95 | {-25.72959, -144.90758, -153.647468693117198,
96 | -57.70581, -269.17879, -48.343983158876487,
97 | 9413446.7452453107, 84.664533838404295, 6356176.6898881281,
98 | 0.09492245755254703, 0.09737058264766572, 74515122850712.444},
99 | {-41.22777, 122.32875, 14.285113402275739,
100 | -7.57291, 130.37946, 10.805303085187369,
101 | 3812686.035106021, 34.34330804743883, 3588703.8812128856,
102 | 0.82605222593217889, 0.82572158200920196, -2456961531057.857},
103 | {11.01307, 138.25278, 79.43682622782374,
104 | 6.62726, 247.05981, 103.708090215522657,
105 | 11911190.819018408, 107.341669954114577, 6070904.722786735,
106 | -0.29767608923657404, -0.29785143390252321, 17121631423099.696},
107 | {-29.47124, 95.14681, -163.779130441688382,
108 | -27.46601, -69.15955, -15.909335945554969,
109 | 13487015.8381145492, 121.294026715742277, 5481428.9945736388,
110 | -0.51527225545373252, -0.51556587964721788, 104679964020340.318}};
111 |
112 | @Test
113 | public void InverseCheck() {
114 | for (int i = 0; i < testcases.length; ++i) {
115 | double
116 | lat1 = testcases[i][0], lon1 = testcases[i][1], azi1 = testcases[i][2],
117 | lat2 = testcases[i][3], lon2 = testcases[i][4], azi2 = testcases[i][5],
118 | s12 = testcases[i][6], a12 = testcases[i][7], m12 = testcases[i][8],
119 | M12 = testcases[i][9], M21 = testcases[i][10], S12 = testcases[i][11];
120 | GeodesicData inv = Geodesic.WGS84.Inverse(lat1, lon1, lat2, lon2,
121 | GeodesicMask.ALL |
122 | GeodesicMask.LONG_UNROLL);
123 | assertEquals(lon2, inv.lon2, 1e-13);
124 | assertEquals(azi1, inv.azi1, 1e-13);
125 | assertEquals(azi2, inv.azi2, 1e-13);
126 | assertEquals(s12, inv.s12, 1e-8);
127 | assertEquals(a12, inv.a12, 1e-13);
128 | assertEquals(m12, inv.m12, 1e-8);
129 | assertEquals(M12, inv.M12, 1e-15);
130 | assertEquals(M21, inv.M21, 1e-15);
131 | assertEquals(S12, inv.S12, 0.1);
132 | }
133 | }
134 |
135 | @Test
136 | public void DirectCheck() {
137 | for (int i = 0; i < testcases.length; ++i) {
138 | double
139 | lat1 = testcases[i][0], lon1 = testcases[i][1], azi1 = testcases[i][2],
140 | lat2 = testcases[i][3], lon2 = testcases[i][4], azi2 = testcases[i][5],
141 | s12 = testcases[i][6], a12 = testcases[i][7], m12 = testcases[i][8],
142 | M12 = testcases[i][9], M21 = testcases[i][10], S12 = testcases[i][11];
143 | GeodesicData dir = Geodesic.WGS84.Direct(lat1, lon1, azi1, s12,
144 | GeodesicMask.ALL |
145 | GeodesicMask.LONG_UNROLL);
146 | assertEquals(lat2, dir.lat2, 1e-13);
147 | assertEquals(lon2, dir.lon2, 1e-13);
148 | assertEquals(azi2, dir.azi2, 1e-13);
149 | assertEquals(a12, dir.a12, 1e-13);
150 | assertEquals(m12, dir.m12, 1e-8);
151 | assertEquals(M12, dir.M12, 1e-15);
152 | assertEquals(M21, dir.M21, 1e-15);
153 | assertEquals(S12, dir.S12, 0.1);
154 | }
155 | }
156 |
157 | @Test
158 | public void ArcDirectCheck() {
159 | for (int i = 0; i < testcases.length; ++i) {
160 | double
161 | lat1 = testcases[i][0], lon1 = testcases[i][1], azi1 = testcases[i][2],
162 | lat2 = testcases[i][3], lon2 = testcases[i][4], azi2 = testcases[i][5],
163 | s12 = testcases[i][6], a12 = testcases[i][7], m12 = testcases[i][8],
164 | M12 = testcases[i][9], M21 = testcases[i][10], S12 = testcases[i][11];
165 | GeodesicData dir = Geodesic.WGS84.ArcDirect(lat1, lon1, azi1, a12,
166 | GeodesicMask.ALL |
167 | GeodesicMask.LONG_UNROLL);
168 | assertEquals(lat2, dir.lat2, 1e-13);
169 | assertEquals(lon2, dir.lon2, 1e-13);
170 | assertEquals(azi2, dir.azi2, 1e-13);
171 | assertEquals(s12, dir.s12, 1e-8);
172 | assertEquals(m12, dir.m12, 1e-8);
173 | assertEquals(M12, dir.M12, 1e-15);
174 | assertEquals(M21, dir.M21, 1e-15);
175 | assertEquals(S12, dir.S12, 0.1);
176 | }
177 | }
178 |
179 | @Test
180 | public void GeodSolve0() {
181 | GeodesicData inv = Geodesic.WGS84.Inverse(40.6, -73.8,
182 | 49.01666667, 2.55);
183 | assertEquals(inv.azi1, 53.47022, 0.5e-5);
184 | assertEquals(inv.azi2, 111.59367, 0.5e-5);
185 | assertEquals(inv.s12, 5853226, 0.5);
186 | }
187 |
188 | @Test
189 | public void GeodSolve1() {
190 | GeodesicData dir = Geodesic.WGS84.Direct(40.63972222, -73.77888889,
191 | 53.5, 5850e3);
192 | assertEquals(dir.lat2, 49.01467, 0.5e-5);
193 | assertEquals(dir.lon2, 2.56106, 0.5e-5);
194 | assertEquals(dir.azi2, 111.62947, 0.5e-5);
195 | }
196 |
197 | @Test
198 | public void GeodSolve2() {
199 | // Check fix for antipodal prolate bug found 2010-09-04
200 | Geodesic geod = new Geodesic(6.4e6, -1/150.0);
201 | GeodesicData inv = geod.Inverse(0.07476, 0, -0.07476, 180);
202 | assertEquals(inv.azi1, 90.00078, 0.5e-5);
203 | assertEquals(inv.azi2, 90.00078, 0.5e-5);
204 | assertEquals(inv.s12, 20106193, 0.5);
205 | inv = geod.Inverse(0.1, 0, -0.1, 180);
206 | assertEquals(inv.azi1, 90.00105, 0.5e-5);
207 | assertEquals(inv.azi2, 90.00105, 0.5e-5);
208 | assertEquals(inv.s12, 20106193, 0.5);
209 | }
210 |
211 | @Test
212 | public void GeodSolve4() {
213 | // Check fix for short line bug found 2010-05-21
214 | GeodesicData inv = Geodesic.WGS84.Inverse(36.493349428792, 0,
215 | 36.49334942879201, .0000008);
216 | assertEquals(inv.s12, 0.072, 0.5e-3);
217 | }
218 |
219 | @Test
220 | public void GeodSolve5() {
221 | // Check fix for point2=pole bug found 2010-05-03
222 | GeodesicData dir = Geodesic.WGS84.Direct(0.01777745589997, 30, 0, 10e6);
223 | assertEquals(dir.lat2, 90, 0.5e-5);
224 | if (dir.lon2 < 0) {
225 | assertEquals(dir.lon2, -150, 0.5e-5);
226 | assertEquals(Math.abs(dir.azi2), 180, 0.5e-5);
227 | } else {
228 | assertEquals(dir.lon2, 30, 0.5e-5);
229 | assertEquals(dir.azi2, 0, 0.5e-5);
230 | }
231 | }
232 |
233 | @Test
234 | public void GeodSolve6() {
235 | // Check fix for volatile sbet12a bug found 2011-06-25 (gcc 4.4.4
236 | // x86 -O3). Found again on 2012-03-27 with tdm-mingw32 (g++ 4.6.1).
237 | GeodesicData inv =
238 | Geodesic.WGS84.Inverse(88.202499451857, 0,
239 | -88.202499451857, 179.981022032992859592);
240 | assertEquals(inv.s12, 20003898.214, 0.5e-3);
241 | inv = Geodesic.WGS84.Inverse(89.262080389218, 0,
242 | -89.262080389218, 179.992207982775375662);
243 | assertEquals(inv.s12, 20003925.854, 0.5e-3);
244 | inv = Geodesic.WGS84.Inverse(89.333123580033, 0, -89.333123580032997687,
245 | 179.99295812360148422);
246 | assertEquals(inv.s12, 20003926.881, 0.5e-3);
247 | }
248 |
249 | @Test
250 | public void GeodSolve9() {
251 | // Check fix for volatile x bug found 2011-06-25 (gcc 4.4.4 x86 -O3)
252 | GeodesicData inv =
253 | Geodesic.WGS84.Inverse(56.320923501171, 0,
254 | -56.320923501171, 179.664747671772880215);
255 | assertEquals(inv.s12, 19993558.287, 0.5e-3);
256 | }
257 |
258 | @Test
259 | public void GeodSolve10() {
260 | // Check fix for adjust tol1_ bug found 2011-06-25 (Visual Studio
261 | // 10 rel + debug)
262 | GeodesicData inv =
263 | Geodesic.WGS84.Inverse(52.784459512564, 0,
264 | -52.784459512563990912, 179.634407464943777557);
265 | assertEquals(inv.s12, 19991596.095, 0.5e-3);
266 | }
267 |
268 | @Test
269 | public void GeodSolve11() {
270 | // Check fix for bet2 = -bet1 bug found 2011-06-25 (Visual Studio
271 | // 10 rel + debug)
272 | GeodesicData inv =
273 | Geodesic.WGS84.Inverse(48.522876735459, 0,
274 | -48.52287673545898293, 179.599720456223079643);
275 | assertEquals(inv.s12, 19989144.774, 0.5e-3);
276 | }
277 |
278 | @Test
279 | public void GeodSolve12() {
280 | // Check fix for inverse geodesics on extreme prolate/oblate
281 | // ellipsoids Reported 2012-08-29 Stefan Guenther
282 | //
189 | *
230 | *
201 | *
227 | *
204 | *
210 | *
234 | *
238 | *
242 | *
257 | *
261 | *
276 | *
280 | *
283 | *
287 | *
291 | *
295 | *
299 | *
303 | *
318 | *
322 | *
333 | *
337 | *
354 | *