1 """Define the :class:`~geographiclib.geodesic.Geodesic` class
3 The ellipsoid parameters are defined by the constructor. The direct and
4 inverse geodesic problems are solved by
6 * :meth:`~geographiclib.geodesic.Geodesic.Inverse` Solve the inverse
8 * :meth:`~geographiclib.geodesic.Geodesic.Direct` Solve the direct
10 * :meth:`~geographiclib.geodesic.Geodesic.ArcDirect` Solve the direct
11 geodesic problem in terms of spherical arc length
13 :class:`~geographiclib.geodesicline.GeodesicLine` objects can be created
16 * :meth:`~geographiclib.geodesic.Geodesic.Line`
17 * :meth:`~geographiclib.geodesic.Geodesic.DirectLine`
18 * :meth:`~geographiclib.geodesic.Geodesic.ArcDirectLine`
19 * :meth:`~geographiclib.geodesic.Geodesic.InverseLine`
21 :class:`~geographiclib.polygonarea.PolygonArea` objects can be created
24 * :meth:`~geographiclib.geodesic.Geodesic.Polygon`
26 The public attributes for this class are
28 * :attr:`~geographiclib.geodesic.Geodesic.a`
29 :attr:`~geographiclib.geodesic.Geodesic.f`
31 *outmask* and *caps* bit masks are
33 * :const:`~geographiclib.geodesic.Geodesic.EMPTY`
34 * :const:`~geographiclib.geodesic.Geodesic.LATITUDE`
35 * :const:`~geographiclib.geodesic.Geodesic.LONGITUDE`
36 * :const:`~geographiclib.geodesic.Geodesic.AZIMUTH`
37 * :const:`~geographiclib.geodesic.Geodesic.DISTANCE`
38 * :const:`~geographiclib.geodesic.Geodesic.STANDARD`
39 * :const:`~geographiclib.geodesic.Geodesic.DISTANCE_IN`
40 * :const:`~geographiclib.geodesic.Geodesic.REDUCEDLENGTH`
41 * :const:`~geographiclib.geodesic.Geodesic.GEODESICSCALE`
42 * :const:`~geographiclib.geodesic.Geodesic.AREA`
43 * :const:`~geographiclib.geodesic.Geodesic.ALL`
44 * :const:`~geographiclib.geodesic.Geodesic.LONG_UNROLL`
48 >>> from geographiclib.geodesic import Geodesic
49 >>> # The geodesic inverse problem
50 ... Geodesic.WGS84.Inverse(-41.32, 174.81, 40.96, -5.50)
52 'a12': 179.6197069334283,
53 's12': 19959679.26735382,
55 'azi2': 18.825195123248392,
56 'azi1': 161.06766998615882,
86 """Solve geodesic problems"""
88 GEOGRAPHICLIB_GEODESIC_ORDER = 6
89 nA1_ = GEOGRAPHICLIB_GEODESIC_ORDER
90 nC1_ = GEOGRAPHICLIB_GEODESIC_ORDER
91 nC1p_ = GEOGRAPHICLIB_GEODESIC_ORDER
92 nA2_ = GEOGRAPHICLIB_GEODESIC_ORDER
93 nC2_ = GEOGRAPHICLIB_GEODESIC_ORDER
94 nA3_ = GEOGRAPHICLIB_GEODESIC_ORDER
96 nC3_ = GEOGRAPHICLIB_GEODESIC_ORDER
97 nC3x_ = (nC3_ * (nC3_ - 1)) // 2
98 nC4_ = GEOGRAPHICLIB_GEODESIC_ORDER
99 nC4x_ = (nC4_ * (nC4_ + 1)) // 2
101 maxit2_ = maxit1_ + Math.digits + 10
103 tiny_ = math.sqrt(Math.minval)
106 tol2_ = math.sqrt(tol0_)
107 tolb_ = tol0_ * tol2_
108 xthresh_ = 1000 * tol2_
110 CAP_NONE = GeodesicCapability.CAP_NONE
111 CAP_C1 = GeodesicCapability.CAP_C1
112 CAP_C1p = GeodesicCapability.CAP_C1p
113 CAP_C2 = GeodesicCapability.CAP_C2
114 CAP_C3 = GeodesicCapability.CAP_C3
115 CAP_C4 = GeodesicCapability.CAP_C4
116 CAP_ALL = GeodesicCapability.CAP_ALL
117 CAP_MASK = GeodesicCapability.CAP_MASK
118 OUT_ALL = GeodesicCapability.OUT_ALL
119 OUT_MASK = GeodesicCapability.OUT_MASK
122 """Private: Evaluate a trig series using Clenshaw summation."""
130 ar = 2 * (cosx - sinx) * (cosx + sinx)
141 k -= 1; y1 = ar * y0 - y1 + c[k]
142 k -= 1; y0 = ar * y1 - y0 + c[k]
143 return ( 2 * sinx * cosx * y0
if sinp
144 else cosx * (y0 - y1) )
148 """Private: solve astroid equation."""
154 if not(q == 0
and r <= 0):
162 disc = S * (S + 2 * r3)
169 T3 += -math.sqrt(disc)
if T3 < 0
else math.sqrt(disc)
173 u += T + (r2 / T
if T != 0
else 0)
176 ang = math.atan2(math.sqrt(-disc), -(S + r3))
179 u += 2 * r * math.cos(ang / 3)
180 v = math.sqrt(Math.sq(u) + q)
182 uv = q / (v - u)
if u < 0
else u + v
183 w = (uv - q) / (2 * v)
186 k = uv / (math.sqrt(uv + Math.sq(w)) + w)
195 """Private: return A1-1."""
200 t = Math.polyval(m, coeff, 0, Math.sq(eps)) / coeff[m + 1]
201 return (t + eps) / (1 - eps)
205 """Private: return C1."""
217 for l
in range(1, Geodesic.nC1_ + 1):
218 m = (Geodesic.nC1_ - l) // 2
219 c[l] = d * Math.polyval(m, coeff, o, eps2) / coeff[o + m + 1]
225 """Private: return C1'"""
227 205, -432, 768, 1536,
228 4005, -4736, 3840, 12288,
237 for l
in range(1, Geodesic.nC1p_ + 1):
238 m = (Geodesic.nC1p_ - l) // 2
239 c[l] = d * Math.polyval(m, coeff, o, eps2) / coeff[o + m + 1]
245 """Private: return A2-1"""
247 -11, -28, -192, 0, 256,
250 t = Math.polyval(m, coeff, 0, Math.sq(eps)) / coeff[m + 1]
251 return (t - eps) / (1 + eps)
255 """Private: return C2"""
267 for l
in range(1, Geodesic.nC2_ + 1):
268 m = (Geodesic.nC2_ - l) // 2
269 c[l] = d * Math.polyval(m, coeff, o, eps2) / coeff[o + m + 1]
275 """Construct a Geodesic object
277 :param a: the equatorial radius of the ellipsoid in meters
278 :param f: the flattening of the ellipsoid
280 An exception is thrown if *a* or the polar semi-axis *b* = *a* (1 -
281 *f*) is not a finite positive quantity.
286 """The equatorial radius in meters (readonly)"""
288 """The flattening (readonly)"""
292 self.
_n = self.
f / ( 2 - self.
f)
295 self.
_c2 = (Math.sq(self.
a) + Math.sq(self.
_b) *
296 (1
if self.
_e2 == 0
else
297 (Math.atanh(math.sqrt(self.
_e2))
if self.
_e2 > 0
else
298 math.atan(math.sqrt(-self.
_e2))) /
299 math.sqrt(
abs(self.
_e2))))/2
310 min(1.0, 1-self.
f/2) / 2 )
311 if not(Math.isfinite(self.
a)
and self.
a > 0):
312 raise ValueError(
"Equatorial radius is not positive")
313 if not(Math.isfinite(self.
_b)
and self.
_b > 0):
314 raise ValueError(
"Polar semi-axis is not positive")
323 """Private: return coefficients for A3"""
333 for j
in range(Geodesic.nA3_ - 1, -1, -1):
334 m =
min(Geodesic.nA3_ - j - 1, j)
335 self.
_A3x[k] = Math.polyval(m, coeff, o, self.
_n) / coeff[o + m + 1]
340 """Private: return coefficients for C3"""
359 for l
in range(1, Geodesic.nC3_):
360 for j
in range(Geodesic.nC3_ - 1, l - 1, -1):
361 m =
min(Geodesic.nC3_ - j - 1, j)
362 self.
_C3x[k] = Math.polyval(m, coeff, o, self.
_n) / coeff[o + m + 1]
367 """Private: return coefficients for C4"""
371 -224, -4784, 1573, 45045,
372 -10656, 14144, -4576, -858, 45045,
373 64, 624, -4576, 6864, -3003, 15015,
374 100, 208, 572, 3432, -12012, 30030, 45045,
377 5792, 1040, -1287, 135135,
378 5952, -11648, 9152, -2574, 135135,
379 -64, -624, 4576, -6864, 3003, 135135,
382 -8448, 4992, -1144, 225225,
383 -1440, 4160, -4576, 1716, 225225,
386 3584, -3328, 1144, 315315,
392 for l
in range(Geodesic.nC4_):
393 for j
in range(Geodesic.nC4_ - 1, l - 1, -1):
394 m = Geodesic.nC4_ - j - 1
395 self.
_C4x[k] = Math.polyval(m, coeff, o, self.
_n) / coeff[o + m + 1]
400 """Private: return A3"""
402 return Math.polyval(Geodesic.nA3_ - 1, self.
_A3x, 0, eps)
405 """Private: return C3"""
410 for l
in range(1, Geodesic.nC3_):
411 m = Geodesic.nC3_ - l - 1
413 c[l] = mult * Math.polyval(m, self.
_C3x, o, eps)
417 """Private: return C4"""
422 for l
in range(Geodesic.nC4_):
423 m = Geodesic.nC4_ - l - 1
424 c[l] = mult * Math.polyval(m, self.
_C4x, o, eps)
430 ssig1, csig1, dn1, ssig2, csig2, dn2, cbet1, cbet2, outmask,
433 """Private: return a bunch of lengths"""
437 outmask &= Geodesic.OUT_MASK
442 s12b = m12b = m0 = M12 = M21 = Math.nan
443 if outmask & (Geodesic.DISTANCE | Geodesic.REDUCEDLENGTH |
444 Geodesic.GEODESICSCALE):
445 A1 = Geodesic._A1m1f(eps)
446 Geodesic._C1f(eps, C1a)
447 if outmask & (Geodesic.REDUCEDLENGTH | Geodesic.GEODESICSCALE):
448 A2 = Geodesic._A2m1f(eps)
449 Geodesic._C2f(eps, C2a)
453 if outmask & Geodesic.DISTANCE:
454 B1 = (Geodesic._SinCosSeries(
True, ssig2, csig2, C1a) -
455 Geodesic._SinCosSeries(
True, ssig1, csig1, C1a))
457 s12b = A1 * (sig12 + B1)
458 if outmask & (Geodesic.REDUCEDLENGTH | Geodesic.GEODESICSCALE):
459 B2 = (Geodesic._SinCosSeries(
True, ssig2, csig2, C2a) -
460 Geodesic._SinCosSeries(
True, ssig1, csig1, C2a))
461 J12 = m0x * sig12 + (A1 * B1 - A2 * B2)
462 elif outmask & (Geodesic.REDUCEDLENGTH | Geodesic.GEODESICSCALE):
464 for l
in range(1, Geodesic.nC2_):
465 C2a[l] = A1 * C1a[l] - A2 * C2a[l]
466 J12 = m0x * sig12 + (Geodesic._SinCosSeries(
True, ssig2, csig2, C2a) -
467 Geodesic._SinCosSeries(
True, ssig1, csig1, C2a))
468 if outmask & Geodesic.REDUCEDLENGTH:
473 m12b = (dn2 * (csig1 * ssig2) - dn1 * (ssig1 * csig2) -
475 if outmask & Geodesic.GEODESICSCALE:
476 csig12 = csig1 * csig2 + ssig1 * ssig2
477 t = self.
_ep2 * (cbet1 - cbet2) * (cbet1 + cbet2) / (dn1 + dn2)
478 M12 = csig12 + (t * ssig2 - csig2 * J12) * ssig1 / dn1
479 M21 = csig12 - (t * ssig1 - csig1 * J12) * ssig2 / dn2
480 return s12b, m12b, m0, M12, M21
484 lam12, slam12, clam12,
487 """Private: Find a starting value for Newton's method."""
491 sig12 = -1; salp2 = calp2 = dnm = Math.nan
493 sbet12 = sbet2 * cbet1 - cbet2 * sbet1
494 cbet12 = cbet2 * cbet1 + sbet2 * sbet1
500 sbet12a = sbet2 * cbet1
501 sbet12a += cbet2 * sbet1
503 shortline = cbet12 >= 0
and sbet12 < 0.5
and cbet2 * lam12 < 0.5
505 sbetm2 = Math.sq(sbet1 + sbet2)
508 sbetm2 /= sbetm2 + Math.sq(cbet1 + cbet2)
509 dnm = math.sqrt(1 + self.
_ep2 * sbetm2)
510 omg12 = lam12 / (self.
_f1 * dnm)
511 somg12 = math.sin(omg12); comg12 = math.cos(omg12)
513 somg12 = slam12; comg12 = clam12
515 salp1 = cbet2 * somg12
517 sbet12 + cbet2 * sbet1 * Math.sq(somg12) / (1 + comg12)
if comg12 >= 0
518 else sbet12a - cbet2 * sbet1 * Math.sq(somg12) / (1 - comg12))
520 ssig12 = math.hypot(salp1, calp1)
521 csig12 = sbet1 * sbet2 + cbet1 * cbet2 * comg12
523 if shortline
and ssig12 < self.
_etol2:
525 salp2 = cbet1 * somg12
526 calp2 = sbet12 - cbet1 * sbet2 * (Math.sq(somg12) / (1 + comg12)
527 if comg12 >= 0
else 1 - comg12)
528 salp2, calp2 = Math.norm(salp2, calp2)
530 sig12 = math.atan2(ssig12, csig12)
531 elif (
abs(self.
_n) >= 0.1
or
533 ssig12 >= 6 *
abs(self.
_n) * math.pi * Math.sq(cbet1)):
544 lam12x = math.atan2(-slam12, -clam12)
547 k2 = Math.sq(sbet1) * self.
_ep2
548 eps = k2 / (2 * (1 + math.sqrt(1 + k2)) + k2)
549 lamscale = self.
f * cbet1 * self.
_A3f(eps) * math.pi
550 betscale = lamscale * cbet1
551 x = lam12x / lamscale
552 y = sbet12a / betscale
555 cbet12a = cbet2 * cbet1 - sbet2 * sbet1
556 bet12a = math.atan2(sbet12a, cbet12a)
560 dummy, m12b, m0, dummy, dummy = self.
_Lengths(
561 self.
_n, math.pi + bet12a, sbet1, -cbet1, dn1, sbet2, cbet2, dn2,
562 cbet1, cbet2, Geodesic.REDUCEDLENGTH, C1a, C2a)
563 x = -1 + m12b / (cbet1 * cbet2 * m0 * math.pi)
564 betscale = (sbet12a / x
if x < -0.01
565 else -self.
f * Math.sq(cbet1) * math.pi)
566 lamscale = betscale / cbet1
567 y = lam12x / lamscale
569 if y > -Geodesic.tol1_
and x > -1 - Geodesic.xthresh_:
572 salp1 =
min(1.0, -x); calp1 = - math.sqrt(1 - Math.sq(salp1))
574 calp1 =
max((0.0
if x > -Geodesic.tol1_
else -1.0), x)
575 salp1 = math.sqrt(1 - Math.sq(calp1))
611 k = Geodesic._Astroid(x, y)
612 omg12a = lamscale * ( -x * k/(1 + k)
if self.
f >= 0
613 else -y * (1 + k)/k )
614 somg12 = math.sin(omg12a); comg12 = -math.cos(omg12a)
616 salp1 = cbet2 * somg12
617 calp1 = sbet12a - cbet2 * sbet1 * Math.sq(somg12) / (1 - comg12)
620 salp1, calp1 = Math.norm(salp1, calp1)
623 return sig12, salp1, calp1, salp2, calp2, dnm
627 def _Lambda12(self, sbet1, cbet1, dn1, sbet2, cbet2, dn2, salp1, calp1,
628 slam120, clam120, diffp,
631 """Private: Solve hybrid problem"""
632 if sbet1 == 0
and calp1 == 0:
635 calp1 = -Geodesic.tiny_
638 salp0 = salp1 * cbet1
639 calp0 = math.hypot(calp1, salp1 * sbet1)
644 ssig1 = sbet1; somg1 = salp0 * sbet1
645 csig1 = comg1 = calp1 * cbet1
646 ssig1, csig1 = Math.norm(ssig1, csig1)
653 salp2 = salp0 / cbet2
if cbet2 != cbet1
else salp1
658 calp2 = (math.sqrt(Math.sq(calp1 * cbet1) +
659 ((cbet2 - cbet1) * (cbet1 + cbet2)
if cbet1 < -sbet1
660 else (sbet1 - sbet2) * (sbet1 + sbet2))) / cbet2
661 if cbet2 != cbet1
or abs(sbet2) != -sbet1
else abs(calp1))
664 ssig2 = sbet2; somg2 = salp0 * sbet2
665 csig2 = comg2 = calp2 * cbet2
666 ssig2, csig2 = Math.norm(ssig2, csig2)
670 sig12 = math.atan2(
max(0.0, csig1 * ssig2 - ssig1 * csig2),
671 csig1 * csig2 + ssig1 * ssig2)
674 somg12 =
max(0.0, comg1 * somg2 - somg1 * comg2)
675 comg12 = comg1 * comg2 + somg1 * somg2
677 eta = math.atan2(somg12 * clam120 - comg12 * slam120,
678 comg12 * clam120 + somg12 * slam120)
681 k2 = Math.sq(calp0) * self.
_ep2
682 eps = k2 / (2 * (1 + math.sqrt(1 + k2)) + k2)
684 B312 = (Geodesic._SinCosSeries(
True, ssig2, csig2, C3a) -
685 Geodesic._SinCosSeries(
True, ssig1, csig1, C3a))
686 domg12 = -self.
f * self.
_A3f(eps) * salp0 * (sig12 + B312)
691 dlam12 = - 2 * self.
_f1 * dn1 / sbet1
693 dummy, dlam12, dummy, dummy, dummy = self.
_Lengths(
694 eps, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, cbet1, cbet2,
695 Geodesic.REDUCEDLENGTH, C1a, C2a)
696 dlam12 *= self.
_f1 / (calp2 * cbet2)
700 return (lam12, salp2, calp2, sig12, ssig1, csig1, ssig2, csig2, eps,
705 """Private: General version of the inverse problem"""
706 a12 = s12 = m12 = M12 = M21 = S12 = Math.nan
708 outmask &= Geodesic.OUT_MASK
712 lon12, lon12s = Math.AngDiff(lon1, lon2)
714 lonsign = 1
if lon12 >= 0
else -1
716 lon12 = lonsign * Math.AngRound(lon12)
717 lon12s = Math.AngRound((180 - lon12) - lonsign * lon12s)
718 lam12 = math.radians(lon12)
720 slam12, clam12 = Math.sincosd(lon12s); clam12 = -clam12
722 slam12, clam12 = Math.sincosd(lon12)
725 lat1 = Math.AngRound(Math.LatFix(lat1))
726 lat2 = Math.AngRound(Math.LatFix(lat2))
729 swapp = -1
if abs(lat1) <
abs(lat2)
else 1
732 lat2, lat1 = lat1, lat2
734 latsign = 1
if lat1 < 0
else -1
751 sbet1, cbet1 = Math.sincosd(lat1); sbet1 *= self.
_f1
753 sbet1, cbet1 = Math.norm(sbet1, cbet1); cbet1 =
max(Geodesic.tiny_, cbet1)
755 sbet2, cbet2 = Math.sincosd(lat2); sbet2 *= self.
_f1
757 sbet2, cbet2 = Math.norm(sbet2, cbet2); cbet2 =
max(Geodesic.tiny_, cbet2)
769 sbet2 = sbet1
if sbet2 < 0
else -sbet1
771 if abs(sbet2) == -sbet1:
774 dn1 = math.sqrt(1 + self.
_ep2 * Math.sq(sbet1))
775 dn2 = math.sqrt(1 + self.
_ep2 * Math.sq(sbet2))
783 meridian = lat1 == -90
or slam12 == 0
790 calp1 = clam12; salp1 = slam12
791 calp2 = 1.0; salp2 = 0.0
794 ssig1 = sbet1; csig1 = calp1 * cbet1
795 ssig2 = sbet2; csig2 = calp2 * cbet2
798 sig12 = math.atan2(
max(0.0, csig1 * ssig2 - ssig1 * csig2),
799 csig1 * csig2 + ssig1 * ssig2)
801 s12x, m12x, dummy, M12, M21 = self.
_Lengths(
802 self.
_n, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, cbet1, cbet2,
803 outmask | Geodesic.DISTANCE | Geodesic.REDUCEDLENGTH, C1a, C2a)
812 if sig12 < 1
or m12x >= 0:
813 if sig12 < 3 * Geodesic.tiny_:
814 sig12 = m12x = s12x = 0.0
817 a12 = math.degrees(sig12)
824 somg12 = 2.0; comg12 = 0.0; omg12 = 0.0
828 (self.
f <= 0
or lon12s >= self.
f * 180)):
831 calp1 = calp2 = 0.0; salp1 = salp2 = 1.0
832 s12x = self.
a * lam12
833 sig12 = omg12 = lam12 / self.
_f1
834 m12x = self.
_b * math.sin(sig12)
835 if outmask & Geodesic.GEODESICSCALE:
836 M12 = M21 = math.cos(sig12)
837 a12 = lon12 / self.
_f1
846 sbet1, cbet1, dn1, sbet2, cbet2, dn2, lam12, slam12, clam12, C1a, C2a)
850 s12x = sig12 * self.
_b * dnm
851 m12x = (Math.sq(dnm) * self.
_b * math.sin(sig12 / dnm))
852 if outmask & Geodesic.GEODESICSCALE:
853 M12 = M21 = math.cos(sig12 / dnm)
854 a12 = math.degrees(sig12)
855 omg12 = lam12 / (self.
_f1 * dnm)
871 tripn = tripb =
False
873 salp1a = Geodesic.tiny_; calp1a = 1.0
874 salp1b = Geodesic.tiny_; calp1b = -1.0
876 while numit < Geodesic.maxit2_:
879 (v, salp2, calp2, sig12, ssig1, csig1, ssig2, csig2,
881 sbet1, cbet1, dn1, sbet2, cbet2, dn2,
882 salp1, calp1, slam12, clam12, numit < Geodesic.maxit1_,
886 if tripb
or not (
abs(v) >= (8
if tripn
else 1) * Geodesic.tol0_):
889 if v > 0
and (numit > Geodesic.maxit1_
or
890 calp1/salp1 > calp1b/salp1b):
891 salp1b = salp1; calp1b = calp1
892 elif v < 0
and (numit > Geodesic.maxit1_
or
893 calp1/salp1 < calp1a/salp1a):
894 salp1a = salp1; calp1a = calp1
897 if numit < Geodesic.maxit1_
and dv > 0:
899 sdalp1 = math.sin(dalp1); cdalp1 = math.cos(dalp1)
900 nsalp1 = salp1 * cdalp1 + calp1 * sdalp1
901 if nsalp1 > 0
and abs(dalp1) < math.pi:
902 calp1 = calp1 * cdalp1 - salp1 * sdalp1
904 salp1, calp1 = Math.norm(salp1, calp1)
908 tripn =
abs(v) <= 16 * Geodesic.tol0_
918 salp1 = (salp1a + salp1b)/2
919 calp1 = (calp1a + calp1b)/2
920 salp1, calp1 = Math.norm(salp1, calp1)
922 tripb = (
abs(salp1a - salp1) + (calp1a - calp1) < Geodesic.tolb_
or
923 abs(salp1 - salp1b) + (calp1 - calp1b) < Geodesic.tolb_)
925 lengthmask = (outmask |
927 if (outmask & (Geodesic.REDUCEDLENGTH |
928 Geodesic.GEODESICSCALE))
929 else Geodesic.EMPTY))
930 s12x, m12x, dummy, M12, M21 = self.
_Lengths(
931 eps, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, cbet1, cbet2,
932 lengthmask, C1a, C2a)
936 a12 = math.degrees(sig12)
937 if outmask & Geodesic.AREA:
939 sdomg12 = math.sin(domg12); cdomg12 = math.cos(domg12)
940 somg12 = slam12 * cdomg12 - clam12 * sdomg12
941 comg12 = clam12 * cdomg12 + slam12 * sdomg12
945 if outmask & Geodesic.DISTANCE:
948 if outmask & Geodesic.REDUCEDLENGTH:
951 if outmask & Geodesic.AREA:
953 salp0 = salp1 * cbet1
954 calp0 = math.hypot(calp1, salp1 * sbet1)
956 if calp0 != 0
and salp0 != 0:
958 ssig1 = sbet1; csig1 = calp1 * cbet1
959 ssig2 = sbet2; csig2 = calp2 * cbet2
960 k2 = Math.sq(calp0) * self.
_ep2
961 eps = k2 / (2 * (1 + math.sqrt(1 + k2)) + k2)
963 A4 = Math.sq(self.
a) * calp0 * salp0 * self.
_e2
964 ssig1, csig1 = Math.norm(ssig1, csig1)
965 ssig2, csig2 = Math.norm(ssig2, csig2)
968 B41 = Geodesic._SinCosSeries(
False, ssig1, csig1, C4a)
969 B42 = Geodesic._SinCosSeries(
False, ssig2, csig2, C4a)
970 S12 = A4 * (B42 - B41)
975 if not meridian
and somg12 > 1:
976 somg12 = math.sin(omg12); comg12 = math.cos(omg12)
981 sbet2 - sbet1 < 1.75):
985 domg12 = 1 + comg12; dbet1 = 1 + cbet1; dbet2 = 1 + cbet2
986 alp12 = 2 * math.atan2( somg12 * ( sbet1 * dbet2 + sbet2 * dbet1 ),
987 domg12 * ( sbet1 * sbet2 + dbet1 * dbet2 ) )
990 salp12 = salp2 * calp1 - calp2 * salp1
991 calp12 = calp2 * calp1 + salp2 * salp1
996 if salp12 == 0
and calp12 < 0:
997 salp12 = Geodesic.tiny_ * calp1
999 alp12 = math.atan2(salp12, calp12)
1000 S12 += self.
_c2 * alp12
1001 S12 *= swapp * lonsign * latsign
1007 salp2, salp1 = salp1, salp2
1008 calp2, calp1 = calp1, calp2
1009 if outmask & Geodesic.GEODESICSCALE:
1012 salp1 *= swapp * lonsign; calp1 *= swapp * latsign
1013 salp2 *= swapp * lonsign; calp2 *= swapp * latsign
1015 return a12, s12, salp1, calp1, salp2, calp2, m12, M12, M21, S12
1018 outmask = GeodesicCapability.STANDARD):
1019 """Solve the inverse geodesic problem
1021 :param lat1: latitude of the first point in degrees
1022 :param lon1: longitude of the first point in degrees
1023 :param lat2: latitude of the second point in degrees
1024 :param lon2: longitude of the second point in degrees
1025 :param outmask: the :ref:`output mask <outmask>`
1026 :return: a :ref:`dict`
1028 Compute geodesic between (*lat1*, *lon1*) and (*lat2*, *lon2*).
1029 The default value of *outmask* is STANDARD, i.e., the *lat1*,
1030 *lon1*, *azi1*, *lat2*, *lon2*, *azi2*, *s12*, *a12* entries are
1035 a12, s12, salp1,calp1, salp2,calp2, m12, M12, M21, S12 = self.
_GenInverse(
1036 lat1, lon1, lat2, lon2, outmask)
1037 outmask &= Geodesic.OUT_MASK
1038 if outmask & Geodesic.LONG_UNROLL:
1039 lon12, e = Math.AngDiff(lon1, lon2)
1040 lon2 = (lon1 + lon12) + e
1042 lon2 = Math.AngNormalize(lon2)
1043 result = {
'lat1': Math.LatFix(lat1),
1044 'lon1': lon1
if outmask & Geodesic.LONG_UNROLL
else
1045 Math.AngNormalize(lon1),
1046 'lat2': Math.LatFix(lat2),
1049 if outmask & Geodesic.DISTANCE: result[
's12'] = s12
1050 if outmask & Geodesic.AZIMUTH:
1051 result[
'azi1'] = Math.atan2d(salp1, calp1)
1052 result[
'azi2'] = Math.atan2d(salp2, calp2)
1053 if outmask & Geodesic.REDUCEDLENGTH: result[
'm12'] = m12
1054 if outmask & Geodesic.GEODESICSCALE:
1055 result[
'M12'] = M12; result[
'M21'] = M21
1056 if outmask & Geodesic.AREA: result[
'S12'] = S12
1060 def _GenDirect(self, lat1, lon1, azi1, arcmode, s12_a12, outmask):
1061 """Private: General version of direct problem"""
1064 if not arcmode: outmask |= Geodesic.DISTANCE_IN
1065 line = GeodesicLine(self, lat1, lon1, azi1, outmask)
1066 return line._GenPosition(arcmode, s12_a12, outmask)
1069 outmask = GeodesicCapability.STANDARD):
1070 """Solve the direct geodesic problem
1072 :param lat1: latitude of the first point in degrees
1073 :param lon1: longitude of the first point in degrees
1074 :param azi1: azimuth at the first point in degrees
1075 :param s12: the distance from the first point to the second in
1077 :param outmask: the :ref:`output mask <outmask>`
1078 :return: a :ref:`dict`
1080 Compute geodesic starting at (*lat1*, *lon1*) with azimuth *azi1*
1081 and length *s12*. The default value of *outmask* is STANDARD, i.e.,
1082 the *lat1*, *lon1*, *azi1*, *lat2*, *lon2*, *azi2*, *s12*, *a12*
1083 entries are returned.
1087 a12, lat2, lon2, azi2, s12, m12, M12, M21, S12 = self.
_GenDirect(
1088 lat1, lon1, azi1,
False, s12, outmask)
1089 outmask &= Geodesic.OUT_MASK
1090 result = {
'lat1': Math.LatFix(lat1),
1091 'lon1': lon1
if outmask & Geodesic.LONG_UNROLL
else
1092 Math.AngNormalize(lon1),
1093 'azi1': Math.AngNormalize(azi1),
1096 if outmask & Geodesic.LATITUDE: result[
'lat2'] = lat2
1097 if outmask & Geodesic.LONGITUDE: result[
'lon2'] = lon2
1098 if outmask & Geodesic.AZIMUTH: result[
'azi2'] = azi2
1099 if outmask & Geodesic.REDUCEDLENGTH: result[
'm12'] = m12
1100 if outmask & Geodesic.GEODESICSCALE:
1101 result[
'M12'] = M12; result[
'M21'] = M21
1102 if outmask & Geodesic.AREA: result[
'S12'] = S12
1106 outmask = GeodesicCapability.STANDARD):
1107 """Solve the direct geodesic problem in terms of spherical arc length
1109 :param lat1: latitude of the first point in degrees
1110 :param lon1: longitude of the first point in degrees
1111 :param azi1: azimuth at the first point in degrees
1112 :param a12: spherical arc length from the first point to the second
1114 :param outmask: the :ref:`output mask <outmask>`
1115 :return: a :ref:`dict`
1117 Compute geodesic starting at (*lat1*, *lon1*) with azimuth *azi1*
1118 and arc length *a12*. The default value of *outmask* is STANDARD,
1119 i.e., the *lat1*, *lon1*, *azi1*, *lat2*, *lon2*, *azi2*, *s12*,
1120 *a12* entries are returned.
1124 a12, lat2, lon2, azi2, s12, m12, M12, M21, S12 = self.
_GenDirect(
1125 lat1, lon1, azi1,
True, a12, outmask)
1126 outmask &= Geodesic.OUT_MASK
1127 result = {
'lat1': Math.LatFix(lat1),
1128 'lon1': lon1
if outmask & Geodesic.LONG_UNROLL
else
1129 Math.AngNormalize(lon1),
1130 'azi1': Math.AngNormalize(azi1),
1132 if outmask & Geodesic.DISTANCE: result[
's12'] = s12
1133 if outmask & Geodesic.LATITUDE: result[
'lat2'] = lat2
1134 if outmask & Geodesic.LONGITUDE: result[
'lon2'] = lon2
1135 if outmask & Geodesic.AZIMUTH: result[
'azi2'] = azi2
1136 if outmask & Geodesic.REDUCEDLENGTH: result[
'm12'] = m12
1137 if outmask & Geodesic.GEODESICSCALE:
1138 result[
'M12'] = M12; result[
'M21'] = M21
1139 if outmask & Geodesic.AREA: result[
'S12'] = S12
1143 caps = GeodesicCapability.STANDARD |
1144 GeodesicCapability.DISTANCE_IN):
1145 """Return a GeodesicLine object
1147 :param lat1: latitude of the first point in degrees
1148 :param lon1: longitude of the first point in degrees
1149 :param azi1: azimuth at the first point in degrees
1150 :param caps: the :ref:`capabilities <outmask>`
1151 :return: a :class:`~geographiclib.geodesicline.GeodesicLine`
1153 This allows points along a geodesic starting at (*lat1*, *lon1*),
1154 with azimuth *azi1* to be found. The default value of *caps* is
1155 STANDARD | DISTANCE_IN, allowing direct geodesic problem to be
1161 return GeodesicLine(self, lat1, lon1, azi1, caps)
1164 caps = GeodesicCapability.STANDARD |
1165 GeodesicCapability.DISTANCE_IN):
1166 """Private: general form of DirectLine"""
1169 if not arcmode: caps |= Geodesic.DISTANCE_IN
1170 line = GeodesicLine(self, lat1, lon1, azi1, caps)
1172 line.SetArc(s12_a12)
1174 line.SetDistance(s12_a12)
1178 caps = GeodesicCapability.STANDARD |
1179 GeodesicCapability.DISTANCE_IN):
1180 """Define a GeodesicLine object in terms of the direct geodesic
1181 problem specified in terms of spherical arc length
1183 :param lat1: latitude of the first point in degrees
1184 :param lon1: longitude of the first point in degrees
1185 :param azi1: azimuth at the first point in degrees
1186 :param s12: the distance from the first point to the second in
1188 :param caps: the :ref:`capabilities <outmask>`
1189 :return: a :class:`~geographiclib.geodesicline.GeodesicLine`
1191 This function sets point 3 of the GeodesicLine to correspond to
1192 point 2 of the direct geodesic problem. The default value of *caps*
1193 is STANDARD | DISTANCE_IN, allowing direct geodesic problem to be
1201 caps = GeodesicCapability.STANDARD |
1202 GeodesicCapability.DISTANCE_IN):
1203 """Define a GeodesicLine object in terms of the direct geodesic
1204 problem specified in terms of spherical arc length
1206 :param lat1: latitude of the first point in degrees
1207 :param lon1: longitude of the first point in degrees
1208 :param azi1: azimuth at the first point in degrees
1209 :param a12: spherical arc length from the first point to the second
1211 :param caps: the :ref:`capabilities <outmask>`
1212 :return: a :class:`~geographiclib.geodesicline.GeodesicLine`
1214 This function sets point 3 of the GeodesicLine to correspond to
1215 point 2 of the direct geodesic problem. The default value of *caps*
1216 is STANDARD | DISTANCE_IN, allowing direct geodesic problem to be
1224 caps = GeodesicCapability.STANDARD |
1225 GeodesicCapability.DISTANCE_IN):
1226 """Define a GeodesicLine object in terms of the invese geodesic problem
1228 :param lat1: latitude of the first point in degrees
1229 :param lon1: longitude of the first point in degrees
1230 :param lat2: latitude of the second point in degrees
1231 :param lon2: longitude of the second point in degrees
1232 :param caps: the :ref:`capabilities <outmask>`
1233 :return: a :class:`~geographiclib.geodesicline.GeodesicLine`
1235 This function sets point 3 of the GeodesicLine to correspond to
1236 point 2 of the inverse geodesic problem. The default value of *caps*
1237 is STANDARD | DISTANCE_IN, allowing direct geodesic problem to be
1243 a12, _, salp1, calp1, _, _, _, _, _, _ = self.
_GenInverse(
1244 lat1, lon1, lat2, lon2, 0)
1245 azi1 = Math.atan2d(salp1, calp1)
1246 if caps & (Geodesic.OUT_MASK & Geodesic.DISTANCE_IN):
1247 caps |= Geodesic.DISTANCE
1248 line = GeodesicLine(self, lat1, lon1, azi1, caps, salp1, calp1)
1253 """Return a PolygonArea object
1255 :param polyline: if True then the object describes a polyline
1256 instead of a polygon
1257 :return: a :class:`~geographiclib.polygonarea.PolygonArea`
1262 return PolygonArea(self, polyline)
1264 EMPTY = GeodesicCapability.EMPTY
1265 """No capabilities, no output."""
1266 LATITUDE = GeodesicCapability.LATITUDE
1267 """Calculate latitude *lat2*."""
1268 LONGITUDE = GeodesicCapability.LONGITUDE
1269 """Calculate longitude *lon2*."""
1270 AZIMUTH = GeodesicCapability.AZIMUTH
1271 """Calculate azimuths *azi1* and *azi2*."""
1272 DISTANCE = GeodesicCapability.DISTANCE
1273 """Calculate distance *s12*."""
1274 STANDARD = GeodesicCapability.STANDARD
1275 """All of the above."""
1276 DISTANCE_IN = GeodesicCapability.DISTANCE_IN
1277 """Allow distance *s12* to be used as input in the direct geodesic
1279 REDUCEDLENGTH = GeodesicCapability.REDUCEDLENGTH
1280 """Calculate reduced length *m12*."""
1281 GEODESICSCALE = GeodesicCapability.GEODESICSCALE
1282 """Calculate geodesic scales *M12* and *M21*."""
1283 AREA = GeodesicCapability.AREA
1284 """Calculate area *S12*."""
1285 ALL = GeodesicCapability.ALL
1286 """All of the above."""
1287 LONG_UNROLL = GeodesicCapability.LONG_UNROLL
1288 """Unroll longitudes, rather than reducing them to the range
1293 Geodesic.WGS84 =
Geodesic(Constants.WGS84_a, Constants.WGS84_f)
1294 """Instantiation for the WGS84 ellipsoid"""