Coverage for pygeodesy/ellipsoids.py: 96%

768 statements  

« prev     ^ index     » next       coverage.py v7.17.0, created at 2026-10-26 09:04 -0400

1 

2# -*- coding: utf-8 -*- 

3 

4u'''Ellipsoidal and spherical earth models. 

5 

6Classes L{a_f2Tuple}, L{Ellipsoid} and L{Ellipsoid2}, an L{Ellipsoids} registry and 

72 dozen functions to convert I{equatorial} radius, I{polar} radius, I{eccentricities}, 

8I{flattenings} and I{inverse flattening}. 

9 

10See module L{datums} for L{Datum} and L{Transform} information and other details. 

11 

12Following is the list of predefined L{Ellipsoid}s, all instantiated lazily. 

13 

14@var Ellipsoids.Airy1830: Ellipsoid(name='Airy1830', a=6377563.396, f=0.00334085, e=0.08167337, b=6356256.90923729, f_=299.3249646) 

15@var Ellipsoids.AiryModified: Ellipsoid(name='AiryModified', a=6377340.189, f=0.00334085, e=0.08167337, b=6356034.44793853, f_=299.3249646) 

16@var Ellipsoids.ATS1977: Ellipsoid(name='ATS1977', a=6378135, f=0.00335281, e=0.08181922, b=6356750.30492159, f_=298.257) 

17@var Ellipsoids.Australia1966: Ellipsoid(name='Australia1966', a=6378160, f=0.00335289, e=0.08182018, b=6356774.71919531, f_=298.25) 

18@var Ellipsoids.Bessel1841: Ellipsoid(name='Bessel1841', a=6377397.155, f=0.00334277, e=0.08169683, b=6356078.962818, f_=299.1528128) 

19@var Ellipsoids.BesselModified: Ellipsoid(name='BesselModified', a=6377492.018, f=0.00334277, e=0.08169683, b=6356173.5087127, f_=299.1528128) 

20@var Ellipsoids.CGCS2000: Ellipsoid(name='CGCS2000', a=6378137, f=0.00335281, e=0.08181919, b=6356752.31414036, f_=298.2572221) 

21@var Ellipsoids.Clarke1866: Ellipsoid(name='Clarke1866', a=6378206.4, f=0.00339008, e=0.08227185, b=6356583.8, f_=294.97869821) 

22@var Ellipsoids.Clarke1880: Ellipsoid(name='Clarke1880', a=6378249.145, f=0.00340756, e=0.0824834, b=6356514.86954978, f_=293.465) 

23@var Ellipsoids.Clarke1880IGN: Ellipsoid(name='Clarke1880IGN', a=6378249.2, f=0.00340755, e=0.08248326, b=6356515, f_=293.46602129) 

24@var Ellipsoids.Clarke1880Mod: Ellipsoid(name='Clarke1880Mod', a=6378249.145, f=0.00340755, e=0.08248322, b=6356514.96639549, f_=293.46630766) 

25@var Ellipsoids.CPM1799: Ellipsoid(name='CPM1799', a=6375738.7, f=0.00299052, e=0.07727934, b=6356671.92557493, f_=334.39) 

26@var Ellipsoids.Delambre1810: Ellipsoid(name='Delambre1810', a=6376428, f=0.00321027, e=0.08006397, b=6355957.92616372, f_=311.5) 

27@var Ellipsoids.Engelis1985: Ellipsoid(name='Engelis1985', a=6378136.05, f=0.00335282, e=0.08181928, b=6356751.32272154, f_=298.2566) 

28@var Ellipsoids.Everest1967: Ellipsoid(name='Everest1967', a=6377298.556, f=0.00332445, e=0.08147298, b=6356097.55030068, f_=300.8017) 

29@var Ellipsoids.Everest1969: Ellipsoid(name='Everest1969', a=6377295.664, f=0.00332445, e=0.08147298, b=6356094.667915, f_=300.8017) 

30@var Ellipsoids.Everest1975: Ellipsoid(name='Everest1975', a=6377299.151, f=0.00332445, e=0.08147298, b=6356098.14512013, f_=300.8017255) 

31@var Ellipsoids.Fisher1968: Ellipsoid(name='Fisher1968', a=6378150, f=0.00335233, e=0.08181333, b=6356768.33724438, f_=298.3) 

32@var Ellipsoids.GEM10C: Ellipsoid(name='GEM10C', a=6378137, f=0.00335281, e=0.08181919, b=6356752.31424783, f_=298.2572236) 

33@var Ellipsoids.GPES: Ellipsoid(name='GPES', a=6378135, f=0, e=0, b=6378135, f_=0) 

34@var Ellipsoids.GRS67: Ellipsoid(name='GRS67', a=6378160, f=0.00335292, e=0.08182057, b=6356774.51609071, f_=298.24716743) 

35@var Ellipsoids.GRS80: Ellipsoid(name='GRS80', a=6378137, f=0.00335281, e=0.08181919, b=6356752.31414035, f_=298.2572221) 

36@var Ellipsoids.Helmert1906: Ellipsoid(name='Helmert1906', a=6378200, f=0.00335233, e=0.08181333, b=6356818.16962789, f_=298.3) 

37@var Ellipsoids.IAU76: Ellipsoid(name='IAU76', a=6378140, f=0.00335281, e=0.08181922, b=6356755.28815753, f_=298.257) 

38@var Ellipsoids.IERS1989: Ellipsoid(name='IERS1989', a=6378136, f=0.00335281, e=0.08181922, b=6356751.30156878, f_=298.257) 

39@var Ellipsoids.IERS1992TOPEX: Ellipsoid(name='IERS1992TOPEX', a=6378136.3, f=0.00335281, e=0.08181919, b=6356751.61659215, f_=298.25722356) 

40@var Ellipsoids.IERS2003: Ellipsoid(name='IERS2003', a=6378136.6, f=0.00335282, e=0.0818193, b=6356751.85797165, f_=298.25642) 

41@var Ellipsoids.Intl1924: Ellipsoid(name='Intl1924', a=6378388, f=0.003367, e=0.08199189, b=6356911.94612795, f_=297) 

42@var Ellipsoids.Intl1967: Ellipsoid(name='Intl1967', a=6378157.5, f=0.0033529, e=0.08182023, b=6356772.2, f_=298.24961539) 

43@var Ellipsoids.Krassovski1940: Ellipsoid(name='Krassovski1940', a=6378245, f=0.00335233, e=0.08181333, b=6356863.01877305, f_=298.3) 

44@var Ellipsoids.Krassowsky1940: Ellipsoid(name='Krassowsky1940', a=6378245, f=0.00335233, e=0.08181333, b=6356863.01877305, f_=298.3) 

45@var Ellipsoids.Maupertuis1738: Ellipsoid(name='Maupertuis1738', a=6397300, f=0.0052356, e=0.10219488, b=6363806.28272251, f_=191) 

46@var Ellipsoids.Mercury1960: Ellipsoid(name='Mercury1960', a=6378166, f=0.00335233, e=0.08181333, b=6356784.28360711, f_=298.3) 

47@var Ellipsoids.Mercury1968Mod: Ellipsoid(name='Mercury1968Mod', a=6378150, f=0.00335233, e=0.08181333, b=6356768.33724438, f_=298.3) 

48@var Ellipsoids.NWL1965: Ellipsoid(name='NWL1965', a=6378145, f=0.00335289, e=0.08182018, b=6356759.76948868, f_=298.25) 

49@var Ellipsoids.OSU86F: Ellipsoid(name='OSU86F', a=6378136.2, f=0.00335281, e=0.08181919, b=6356751.51693008, f_=298.2572236) 

50@var Ellipsoids.OSU91A: Ellipsoid(name='OSU91A', a=6378136.3, f=0.00335281, e=0.08181919, b=6356751.6165948, f_=298.2572236) 

51@var Ellipsoids.Plessis1817: Ellipsoid(name='Plessis1817', a=6376523, f=0.00324002, e=0.08043347, b=6355862.93325557, f_=308.64) 

52@var Ellipsoids.PZ90: Ellipsoid(name='PZ90', a=6378136, f=0.0033528, e=0.08181911, b=6356751.36174571, f_=298.2578393) 

53@var Ellipsoids.SGS85: Ellipsoid(name='SGS85', a=6378136, f=0.00335281, e=0.08181922, b=6356751.30156878, f_=298.257) 

54@var Ellipsoids.SoAmerican1969: Ellipsoid(name='SoAmerican1969', a=6378160, f=0.00335289, e=0.08182018, b=6356774.71919531, f_=298.25) 

55@var Ellipsoids.Sphere: Ellipsoid(name='Sphere', a=6371008.771415, f=0, e=0, b=6371008.771415, f_=0) 

56@var Ellipsoids.SphereAuthalic: Ellipsoid(name='SphereAuthalic', a=6371000, f=0, e=0, b=6371000, f_=0) 

57@var Ellipsoids.SpherePopular: Ellipsoid(name='SpherePopular', a=6378137, f=0, e=0, b=6378137, f_=0) 

58@var Ellipsoids.Struve1860: Ellipsoid(name='Struve1860', a=6378298.3, f=0.00339294, e=0.0823065, b=6356657.14266956, f_=294.73) 

59@var Ellipsoids.WGS60: Ellipsoid(name='WGS60', a=6378165, f=0.00335233, e=0.08181333, b=6356783.28695944, f_=298.3) 

60@var Ellipsoids.WGS66: Ellipsoid(name='WGS66', a=6378145, f=0.00335289, e=0.08182018, b=6356759.76948868, f_=298.25) 

61@var Ellipsoids.WGS72: Ellipsoid(name='WGS72', a=6378135, f=0.00335278, e=0.08181881, b=6356750.52001609, f_=298.26) 

62@var Ellipsoids.WGS84: Ellipsoid(name='WGS84', a=6378137, f=0.00335281, e=0.08181919, b=6356752.31424518, f_=298.25722356) 

63@var Ellipsoids.WGS84_NGS: Ellipsoid(name='WGS84_NGS', a=6378137, f=0.00335281, e=0.08181919, b=6356752.31414035, f_=298.2572221) 

64''' 

65# make sure int/int division yields float quotient, see .basics 

66from __future__ import division as _; del _ # noqa: E702 ; 

67 

68# from pygeodesy.albers import AlbersEqualAreaCylindrical # _MODS 

69from pygeodesy.basics import copysign0, isbool, _isin, isint, typename 

70from pygeodesy.constants import EPS, EPS_2, EPS0, EPS02, EPS1, INF, NINF, \ 

71 _EPSqrt, PI_2, PI_3, PI2, PI4, R_M, R_MA, R_FM, \ 

72 _EPStol as _TOL, _floatuple as _T, _isfinite, \ 

73 _over, _SQRT2, _0_0s, _0_0, _0_5, _1_0, _1_EPS, \ 

74 _2_0, _4_0, _90_0, _0_25, _3_0 # PYCHOK used! 

75# from pygeodesy.ellipses import Ellipse # _MODS 

76from pygeodesy.errors import _AssertionError, IntersectionError, _ValueError, _xattr, _xkwds_not 

77from pygeodesy.fmath import cbrt, cbrt2, fdot, fmean_, Fhorner, fpowers, hypot, hypot_, \ 

78 hypot1, hypot2, sqrt3, Fsum 

79# from pygeodesy.fsums import Fsum # from .fmath 

80# from pygeodesy.internals import typename # from .basics 

81from pygeodesy.interns import NN, _a_, _Airy1830_, _AiryModified_, _b_, _Bessel1841_, \ 

82 _Clarke1866_, _Clarke1880IGN_, _DMAIN_, _DOT_, _f_, _GRS80_, \ 

83 _Intl1924_, _incompatible_, _invalid_, _Krassovski1940_, \ 

84 _Krassowsky1940_, _lat_, _meridional_, _negative_, _not_finite_, \ 

85 _prime_vertical_, _Sphere_, _SPACE_, _vs_, _WGS72_, _WGS84_ 

86# from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS # from .named 

87from pygeodesy.named import _lazyNamedEnumItem as _lazy, _name__, _NamedEnum, \ 

88 _NamedEnumItem, _NamedTuple, _Pass, _ALL_LAZY, _MODS 

89from pygeodesy.namedTuples import Circle4Tuple, Distance2Tuple, Vector3Tuple, Vector4Tuple 

90from pygeodesy.props import deprecated_Property_RO, Property_RO, property_doc_, \ 

91 deprecated_property_RO, property_RO, property_ROver 

92from pygeodesy.streprs import Fmt, fstr, instr, strs, unstr 

93# from pygeodesy.triaxials.triaxial5 import _hartzell3, _plumbTo3 # _MODS 

94from pygeodesy.units import Azimuth, Bearing, Distance, Float, Float_, Lamd, Lat, _Lat0, \ 

95 Meter, Meter2, Meter3, Phi, Phid, Radius, Radius_, Scalar 

96from pygeodesy.utily import atan1, atan1d, atan2b, degrees90, m2radians, radians2m, sincos2d 

97 

98from math import asinh, atan, atanh, cos, degrees, exp, fabs, radians, sin, sinh, sqrt, tan # as _tan 

99 

100__all__ = _ALL_LAZY.ellipsoids 

101__version__ = '26.10.24' 

102 

103_f_0_0 = Float(f =_0_0) # zero flattening 

104_f__0_0 = Float(f_=_0_0) # zero inverse flattening 

105# see U{WGS84_f<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Constants.html>} 

106# _f_WGS84 = Float(f =_1_0 / (298257223563 / 1000000000)) # 0.003_352_810_664_747_480_5 

107_f__WGS84 = Float(f_=_1_0 / (1000000000 / 298257223563)) # 298.257223562_999_97 vs 298.257223563 

108 

109 

110def _aux(lat, inverse, auxLat, clip=90): 

111 '''Return a named auxiliary latitude in C{degrees}. 

112 ''' 

113 return Lat(lat, clip=clip, name=_lat_ if inverse else typename(auxLat)) 

114 

115 

116def _sin2cos2(rad): 

117 '''(INTERNAL) Return 2-tuple C{(sin(B{phi})**2, cos(B{phi})**2)}. 

118 ''' 

119 if rad: 

120 s2 = sin(rad)**2 

121 if s2 > EPS: 

122 c2 = _1_0 - s2 

123 if c2 > EPS: 

124 if c2 < EPS1: 

125 return s2, c2 

126 else: 

127 return _1_0, _0_0 # rad == PI_2 

128 return _0_0, _1_0 # rad == 0 

129 

130 

131class a_f2Tuple(_NamedTuple): 

132 '''2-Tuple C{(a, f)} specifying an ellipsoid by I{equatorial} 

133 radius C{a} in C{meter} and scalar I{flattening} C{f}. 

134 

135 @see: Class L{Ellipsoid2}. 

136 ''' 

137 _Names_ = (_a_, _f_) # name 'f' NOT 'f_' 

138 _Units_ = (_Pass, _Pass) 

139 

140 def __new__(cls, a, f, **name): 

141 '''New L{a_f2Tuple} ellipsoid specification. 

142 

143 @arg a: Equatorial radius (C{scalar} > 0). 

144 @arg f: Flattening (C{scalar} < 1, negative for I{prolate}). 

145 @kwarg name: Optional C{B{name}=NN} (C{str}). 

146 

147 @return: An L{a_f2Tuple}C{(a, f)}. 

148 

149 @raise UnitError: Invalid B{C{a}} or B{C{f}}. 

150 

151 @note: C{abs(B{f}) < }L{EPS<pygeodesy.constants.EPS>} is 

152 forced to C{B{f}=0}, I{spherical}. 

153 

154 @note: Negative C{B{f}} produces a I{prolate} ellipsoid. 

155 ''' 

156 a = Radius_(a=a) # low=EPS, high=None 

157 f = Float_( f=f, low=None, high=EPS1) 

158 if fabs(f) < EPS: # force spherical 

159 f = _f_0_0 

160 return _NamedTuple.__new__(cls, a, f, **name) 

161 

162 @Property_RO 

163 def b(self): 

164 '''Get the I{polar} radius (C{meter}), M{a * (1 - f)}. 

165 ''' 

166 return a_f2b(self.a, self.f) # PYCHOK .a and .f 

167 

168 def ellipsoid(self, **name): 

169 '''Return an L{Ellipsoid} for this 2-tuple C{(a, f)}. 

170 

171 @kwarg name: Optional C{B{name}=NN} (C{str}). 

172 

173 @raise NameError: A registered C{ellipsoid} with the 

174 same B{C{name}} already exists. 

175 ''' 

176 return Ellipsoid(self.a, f=self.f, name=self._name__(name)) # PYCHOK .a and .f 

177 

178 @Property_RO 

179 def f_(self): 

180 '''Get the I{inverse} flattening (C{scalar}), M{1 / f} == M{a / (a - b)}. 

181 ''' 

182 return f2f_(self.f) # PYCHOK .f 

183 

184 

185class Curvature2Tuple(_NamedTuple): 

186 '''2-Tuple C{(meridional, prime_vertical)} of radii of curvature, both in 

187 C{meter}, conventionally. 

188 ''' 

189 _Names_ = (_meridional_, _prime_vertical_) 

190 _Units_ = ( Meter, Meter) 

191 

192 @property_RO 

193 def transverse(self): 

194 '''Get this I{prime_vertical}, aka I{transverse} radius of curvature. 

195 ''' 

196 return self.prime_vertical 

197 

198 

199class Ellipsoid(_NamedEnumItem): 

200 '''Ellipsoid with I{equatorial} and I{polar} radii, I{flattening}, I{inverse 

201 flattening} and other, often used, I{cached} attributes, supporting 

202 I{oblate} and I{prolate} ellipsoidal and I{spherical} earth models. 

203 ''' 

204 _a = 0 # equatorial radius, semi-axis (C{meter}) 

205 _b = 0 # polar radius, semi-axis (C{meter}): a * (f - 1) / f 

206 _f = 0 # (1st) flattening: (a - b) / a 

207 _f_ = 0 # inverse flattening: 1 / f = a / (a - b) 

208 

209 _geodsolve = NN # means, use PYGEODESY_GEODSOLVE 

210 _KsOrder = 8 # Krüger series order (4, 6 or 8) 

211 _rhumbsolve = NN # means, use PYGEODESY_RHUMBSOLVE 

212 

213 def __init__(self, a, b=None, f_=None, f=None, **name): 

214 '''New L{Ellipsoid} from the I{equatorial} radius I{and} either 

215 the I{polar} radius or I{inverse flattening} or I{flattening}. 

216 

217 @arg a: Equatorial radius, semi-axis (C{meter}). 

218 @arg b: Optional polar radius, semi-axis (C{meter}). 

219 @arg f_: Inverse flattening: M{a / (a - b)} (C{float} >>> 1.0). 

220 @arg f: Flattening: M{(a - b) / a} (C{scalar}, near zero for 

221 spherical). 

222 @kwarg name: Optional, unique C{B{name}=NN} (C{str}). 

223 

224 @raise NameError: Ellipsoid with the same B{C{name}} already exists. 

225 

226 @raise ValueError: Invalid B{C{a}}, B{C{b}}, B{C{f_}} or B{C{f}} or 

227 B{C{f_}} and B{C{f}} are incompatible. 

228 

229 @note: M{abs(f_) > 1 / EPS} or M{abs(1 / f_) < EPS} is forced 

230 to M{1 / f_ = 0}, spherical. 

231 ''' 

232 ff_ = f, f_ # assertion below 

233 n = _name__(**name) if name else NN 

234 try: 

235 a = Radius_(a=a) # low=EPS 

236 if not _isfinite(a): 

237 raise ValueError(_SPACE_(_a_, _not_finite_)) 

238 

239 if b: # not _isin(b, None, _0_0) 

240 b = Radius_(b=b) # low=EPS 

241 f = a_b2f(a, b) if f is None else Float(f=f) 

242 f_ = f2f_(f) if f_ is None else Float(f_=f_) 

243 elif f is not None: 

244 f = Float(f=f) 

245 b = a_f2b(a, f) 

246 f_ = f2f_(f) if f_ is None else Float(f_=f_) 

247 elif f_: 

248 f_ = Float(f_=f_) 

249 b = a_f_2b(a, f_) # a * (f_ - 1) / f_ 

250 f = f_2f(f_) 

251 else: # only a, spherical 

252 f_ = f = 0 

253 b = a # superfluous 

254 

255 if not (f < _1_0): # sanity check, see .ecef.Ecef.__init__ 

256 raise ValueError(_SPACE_(_f_, _invalid_)) 

257 if not _isfinite(b): 

258 raise ValueError(_SPACE_(_b_, _not_finite_)) 

259 

260 if fabs(f) < EPS or a == b or not f_: # spherical 

261 b = a 

262 f = _f_0_0 

263 f_ = _f__0_0 

264 

265 except (TypeError, ValueError) as x: 

266 d = _xkwds_not(None, b=b, f_=f_, f=f) 

267 t = instr(self, a=a, name=n, **d) 

268 raise _ValueError(t, cause=x) 

269 

270 self._a = a 

271 self._b = b 

272 self._f = f 

273 self._f_ = f_ 

274 

275 self._register(Ellipsoids, n) 

276 

277 if f and f_: # see test/testEllipsoidal 

278 d = dict(eps=_TOL) 

279 if None in ff_: # both f_ and f given 

280 d.update(Error=_ValueError, txt=_incompatible_) 

281 self._assert(_1_0 / f, f_=f_, **d) 

282 self._assert(_1_0 / f_, f =f, **d) 

283 self._assert(self.b2_a2, e21=self.e21, eps=EPS) 

284 

285 def __eq__(self, other): 

286 '''Compare this and an other ellipsoid. 

287 

288 @arg other: The other ellipsoid (L{Ellipsoid} or L{Ellipsoid2}). 

289 

290 @return: C{True} if equal, C{False} otherwise. 

291 ''' 

292 return self is other or (isinstance(other, Ellipsoid) and 

293 self.a == other.a and 

294 (self.f == other.f or self.b == other.b)) 

295 

296 def __hash__(self): 

297 return self._hash # memoized 

298 

299 @Property_RO 

300 def a(self): 

301 '''Get the I{equatorial} radius, semi-axis (C{meter}). 

302 ''' 

303 return self._a 

304 

305 equatoradius = a # = Requatorial 

306 

307 @Property_RO 

308 def a2(self): 

309 '''Get the I{equatorial} radius I{squared} (C{meter} I{squared}), M{a**2}. 

310 ''' 

311 return Meter2(a2=self.a**2) 

312 

313 @Property_RO 

314 def a2_(self): 

315 '''Get the inverse of the I{equatorial} radius I{squared} (C{meter} I{squared}), M{1 / a**2}. 

316 ''' 

317 return Float(a2_=_1_0 / self.a2) 

318 

319 @Property_RO 

320 def A(self): 

321 '''Get the UTM I{meridional (or rectifying)} radius (C{meter}). 

322 

323 @note: C{A * PI / 2} ≈= L{L<Ellipsoid.L>}, the I{quarter meridian}. 

324 

325 @see: I{Meridian arc unit} U{Q<https://StudyLib.net/doc/7443565/>}, 

326 the mean, meridional length I{per radian}. 

327 ''' 

328 A, n = self.a, self.n 

329 if n: 

330 d = (n + _1_0) * 1048576 / A 

331 if d: # use 6 n**2 terms, half-way between the _KsOrder's 4, 6, 8 

332 # <https://GeographicLib.SourceForge.io/C++/doc/tmseries30.html> 

333 # <https://GeographicLib.SourceForge.io/C++/doc/transversemercator.html> and 

334 # <https://www.MyGeodesy.id.AU/documents/Karney-Krueger%20equations.pdf> (3) 

335 # A *= fhorner(n**2, 1048576, 262144, 16384, 4096, 1600, 784, 441) / 1048576) / (1 + n) 

336 A = Radius(A=Fhorner(n**2, 1048576, 262144, 16384, 4096, 1600, 784, 441).fover(d)) 

337 return A 

338# # Moritz, H. <https://Geodesy.Geology.Ohio-State.edu/course/refpapers/00740128.pdf> 

339# # q = 4 / self.rocPolar 

340# # Q = (1 - 3 / 4 * e'2 + 45 / 64 * e'4 - 175 / 256 * e'6 + 11025 / 16384 * e'8) * rocPolar 

341# # = (4 + e'2 * (-3 + e'2 * (45 / 16 + e'2 * (-175 / 64 + e'2 * 11025 / 4096)))) / q 

342# return Radius(Q=Fhorner(self.e22, 4, -3, 45 / 16, -175 / 64, 11025 / 4096).fover(q)) 

343 

344 @Property_RO 

345 def a_b(self): 

346 '''Get the ratio I{equatorial} over I{polar} radius (C{float}), M{a / b} == M{1 / (1 - f)}. 

347 ''' 

348 return Float(a_b=self.a / self.b if self.f else _1_0) 

349 

350 @Property_RO 

351 def a2_b(self): 

352 '''Get the I{polar} meridional (or polar) radius of curvature (C{meter}), M{a**2 / b}. 

353 

354 @see: U{Radii of Curvature 

355 <https://WikiPedia.org/wiki/Earth_radius#Radii_of_curvature>} 

356 and U{Moritz, H. (1980), Geodetic Reference System 1980 

357 <https://WikiPedia.org/wiki/Earth_radius#cite_note-Moritz-2>}. 

358 

359 @note: Symbol C{c} is used by IUGG and IERS for the U{polar radius of curvature 

360 <https://WikiPedia.org/wiki/Earth_radius#Radii_of_curvature>}, see L{c2} 

361 and L{R2} or L{Rauthalic}. 

362 ''' 

363 return Radius(a2_b=self.a2 / self.b if self.f else self.a) # = rocPolar 

364 

365 @Property_RO 

366 def a2_b2(self): 

367 '''Get the ratio I{equatorial} over I{polar} radius I{squared} (C{float}), 

368 M{(a / b)**2} == M{1 / (1 - e**2)} == M{1 / (1 - e2)} == M{1 / e21}. 

369 ''' 

370 return Float(a2_b2=self.a_b**2 if self.f else _1_0) 

371 

372 @Property_RO 

373 def a_f(self): 

374 '''Get the I{equatorial} radius and I{flattening} (L{a_f2Tuple}), see method C{toEllipsoid2}. 

375 ''' 

376 return a_f2Tuple(self.a, self.f, name=self.name) 

377 

378 a_f2 = a_f # synonym 

379 

380 @Property_RO 

381 def _albersCyl(self): 

382 '''(INTERNAL) Helper for C{auxAuthalic}. 

383 ''' 

384 return _MODS.albers.AlbersEqualAreaCylindrical(datum=self, name=self.name) 

385 

386 @Property_RO 

387 def AlphaKs(self): 

388 '''Get the I{Krüger} U{Alpha series coefficients<https://GeographicLib.SourceForge.io/C++/doc/tmseries30.html>} (C{KsOrder}C{-tuple}). 

389 ''' 

390 return self._Kseries( # XXX int/int quotients may require from __future__ import division as _; del _ # noqa: E702 ; 

391 # n n**2 n**3 n**4 n**5 n**6 n**7 n**8 

392 _T(1/2, -2/3, 5/16, 41/180, -127/288, 7891/37800, 72161/387072, -18975107/50803200), 

393 _T(13/48, -3/5, 557/1440, 281/630, -1983433/1935360, 13769/28800, 148003883/174182400), # PYCHOK unaligned 

394 _T(61/240, -103/140, 15061/26880, 167603/181440, -67102379/29030400, 79682431/79833600), # PYCHOK unaligned 

395 _T(49561/161280, -179/168, 6601661/7257600, 97445/49896, -40176129013/7664025600), # PYCHOK unaligned 

396 _T(34729/80640, -3418889/1995840, 14644087/9123840, 2605413599/622702080), # PYCHOK unaligned 

397 _T(212378941/319334400, -30705481/10378368, 175214326799/58118860800), # PYCHOK unaligned 

398 _T(1522256789/1383782400, -16759934899/3113510400), # PYCHOK unaligned 

399 _T(1424729850961/743921418240)) # PYCHOK unaligned 

400 

401 @Property_RO 

402 def area(self): 

403 '''Get the ellipsoid's surface area (C{meter} I{squared}), M{4 * PI * c2}. 

404 

405 @see: Properties L{areax}, L{c2} and L{R2} and functions 

406 L{ellipsoidalExact.areaOf} and L{ellipsoidalKarney.areaOf}. 

407 ''' 

408 return Meter2(area=self.c2 * PI4) 

409 

410 @Property_RO 

411 def areax(self): 

412 '''Get the ellipsoid's surface area (C{meter} I{squared}), M{4 * PI * c2x}, more 

413 accurate for very I{oblate} ellipsoids. 

414 

415 @see: Properties L{area}, L{c2x} and L{R2x}, class L{GeodesicExact} and 

416 functions L{ellipsoidalExact.areaOf} and L{ellipsoidalKarney.areaOf}. 

417 ''' 

418 return Meter2(areax=self.c2x * PI4) 

419 

420 def _assert(self, val, eps=_TOL, f0=_0_0, Error=_AssertionError, txt=NN, **name_value): 

421 '''(INTERNAL) Assert a C{name=value} vs C{val}. 

422 ''' 

423 for n, v in name_value.items(): 

424 if fabs(v - val) > eps: # PYCHOK no cover 

425 t = (v, _vs_, val) 

426 t = _SPACE_.join(strs(t, prec=12, fmt=Fmt.g)) 

427 t = Fmt.EQUAL(self._DOT_(n), t) 

428 raise Error(t, txt=txt or Fmt.exceeds_eps(eps)) 

429 return Float(v if self.f else f0, name=n) 

430 raise Error(unstr(self._DOT_(typename(self._assert)), val, 

431 eps=eps, f0=f0, **name_value)) 

432 

433 def auxAuthalic(self, lat, inverse=False): 

434 '''Compute the I{authalic} auxiliary latitude (Xi) or the I{inverse} thereof. 

435 

436 @arg lat: The geodetic (or I{authalic}) latitude (C{degrees90}). 

437 @kwarg inverse: If C{True}, B{C{lat}} is the I{authalic} and 

438 return the geodetic latitude (C{bool}). 

439 

440 @return: The I{authalic} (or geodetic) latitude in C{degrees90}. 

441 

442 @see: U{Inverse-/AuthalicLatitude<https://GeographicLib.SourceForge.io/ 

443 C++/doc/classGeographicLib_1_1Ellipsoid.html>}, U{Authalic latitude 

444 <https://WikiPedia.org/wiki/Latitude#Authalic_latitude>}, and 

445 U{Snyder<https://Pubs.USGS.gov/pp/1395/report.pdf>}, p 16. 

446 ''' 

447 if self.f: 

448 _f = self._albersCyl._tanf if inverse else \ 

449 self._albersCyl._txif # PYCHOK attr 

450 lat = atan1d(_f(tan(Phid(lat)))) # PYCHOK attr 

451 return _aux(lat, inverse, Ellipsoid.auxAuthalic) 

452 

453 def auxConformal(self, lat, inverse=False): 

454 '''Compute the I{conformal} auxiliary latitude (Chi) or the I{inverse} thereof. 

455 

456 @arg lat: The geodetic (or I{conformal}) latitude (C{degrees90}). 

457 @kwarg inverse: If C{True}, B{C{lat}} is the I{conformal} and 

458 return the geodetic latitude (C{bool}). 

459 

460 @return: The I{conformal} (or geodetic) latitude in C{degrees90}. 

461 

462 @see: U{Inverse-/ConformalLatitude<https://GeographicLib.SourceForge.io/ 

463 C++/doc/classGeographicLib_1_1Ellipsoid.html>}, U{Conformal latitude 

464 <https://WikiPedia.org/wiki/Latitude#Conformal_latitude>}, and 

465 U{Snyder<https://Pubs.USGS.gov/pp/1395/report.pdf>}, pp 15-16. 

466 ''' 

467 if self.f: 

468 _f = self.es_tauf if inverse else self.es_taupf # PYCHOK attr 

469 lat = atan1d(_f(tan(Phid(lat)))) # PYCHOK attr 

470 return _aux(lat, inverse, Ellipsoid.auxConformal) 

471 

472 def auxGeocentric(self, lat, inverse=False, height=_0_0): 

473 '''Compute the I{geocentric} auxiliary latitude (Theta) or the I{inverse} thereof. 

474 

475 @arg lat: The geodetic (or I{geocentric}) latitude (C{degrees90}). 

476 @kwarg inverse: If C{True}, B{C{lat}} is the geocentric and 

477 return the I{geocentric} latitude (C{bool}). 

478 @kwarg height: Optional, ellipsoidal height (C{meter}). 

479 

480 @return: The I{geocentric} (or geodetic) latitude in C{degrees90}. 

481 

482 @see: U{Inverse-/GeocentricLatitude<https://GeographicLib.SourceForge.io/ 

483 C++/doc/classGeographicLib_1_1Ellipsoid.html>}, U{Geocentric latitude 

484 <https://WikiPedia.org/wiki/Latitude#Geocentric_latitude>}, and 

485 U{Snyder<https://Pubs.USGS.gov/pp/1395/report.pdf>}, pp 17-18. 

486 ''' 

487 if self.f: # and lat 

488 t = tan(Phid(lat)) 

489 f = self.b2_a2 

490 if height: 

491 if inverse: 

492 lat = atan1d(t * f) # geodetic n 

493 d, f = f, _1_0 

494 else: 

495 d = _1_0 

496 n = self.rocPrimeVertical(lat) 

497 f = _over(n * f + height, n * d + height) 

498 elif inverse: 

499 f = self.a2_b2 

500 lat = atan1d(t * f) 

501 return _aux(lat, inverse, Ellipsoid.auxGeocentric) 

502 

503 def auxIsometric(self, lat, inverse=False): 

504 '''Compute the I{isometric} auxiliary latitude (Psi) or the I{inverse} thereof. 

505 

506 @arg lat: The geodetic (or I{isometric}) latitude (C{degrees}). 

507 @kwarg inverse: If C{True}, B{C{lat}} is the I{isometric} and 

508 return the geodetic latitude (C{bool}). 

509 

510 @return: The I{isometric} (or geodetic) latitude in C{degrees}. 

511 

512 @note: The I{isometric} latitude for geodetic C{+/-90} is far 

513 outside the C{[-90..+90]} range but the inverse thereof 

514 is the original geodetic latitude. 

515 

516 @see: U{Inverse-/IsometricLatitude<https://GeographicLib.SourceForge.io/ 

517 C++/doc/classGeographicLib_1_1Ellipsoid.html>}, U{Isometric latitude 

518 <https://WikiPedia.org/wiki/Latitude#Isometric_latitude>}, and 

519 U{Snyder<https://Pubs.USGS.gov/pp/1395/report.pdf>}, pp 15-16. 

520 ''' 

521 if self.f: 

522 r = Phid(lat, clip=0) 

523 lat = degrees(atan1(self.es_tauf(sinh(r))) if inverse else 

524 asinh(self.es_taupf(tan(r)))) 

525 # clip=0, since auxIsometric(+/-90) is far outside [-90..+90] 

526 return _aux(lat, inverse, Ellipsoid.auxIsometric, clip=0) 

527 

528 def auxParametric(self, lat, inverse=False): 

529 '''Compute the I{parametric} auxiliary latitude (Beta) or the I{inverse} thereof. 

530 

531 @arg lat: The geodetic (or I{parametric}) latitude (C{degrees90}). 

532 @kwarg inverse: If C{True}, B{C{lat}} is the I{parametric} and 

533 return the geodetic latitude (C{bool}). 

534 

535 @return: The I{parametric} (or geodetic) latitude in C{degrees90}. 

536 

537 @see: U{Inverse-/ParametricLatitude<https://GeographicLib.SourceForge.io/ 

538 C++/doc/classGeographicLib_1_1Ellipsoid.html>}, U{Parametric latitude 

539 <https://WikiPedia.org/wiki/Latitude#Parametric_(or_reduced)_latitude>}, 

540 and U{Snyder<https://Pubs.USGS.gov/pp/1395/report.pdf>}, p 18. 

541 ''' 

542 if self.f: 

543 lat = self._Betad(Lat(lat), inverse=inverse) 

544 return _aux(lat, inverse, Ellipsoid.auxParametric) 

545 

546 auxReduced = auxParametric # synonymous 

547 

548 def auxRectifying(self, lat, inverse=False): 

549 '''Compute the I{rectifying} auxiliary latitude (Mu) or the I{inverse} thereof. 

550 

551 @arg lat: The geodetic (or I{rectifying}) latitude (C{degrees90}). 

552 @kwarg inverse: If C{True}, B{C{lat}} is the I{rectifying} and 

553 return the geodetic latitude (C{bool}). 

554 

555 @return: The I{rectifying} (or geodetic) latitude in C{degrees90}. 

556 

557 @see: U{Inverse-/RectifyingLatitude<https://GeographicLib.SourceForge.io/ 

558 C++/doc/classGeographicLib_1_1Ellipsoid.html>}, U{Rectifying latitude 

559 <https://WikiPedia.org/wiki/Latitude#Rectifying_latitude>}, and 

560 U{Snyder<https://Pubs.USGS.gov/pp/1395/report.pdf>}, pp 16-17. 

561 ''' 

562 if self.f: 

563 lat = Lat(lat) 

564 if 0 < fabs(lat) < _90_0: 

565 if inverse: 

566 e = self._elliptic_e22 

567 d = degrees90(e.fEinv(e.cE * lat / _90_0)) 

568 lat = self.auxParametric(d, inverse=True) 

569 else: 

570 lat = _over(self.Llat(lat), self.L) * _90_0 

571 return _aux(lat, inverse, Ellipsoid.auxRectifying) 

572 

573 @Property_RO 

574 def b(self): 

575 '''Get the I{polar} radius, semi-axis (C{meter}). 

576 ''' 

577 return self._b 

578 

579 polaradius = b # = Rpolar 

580 

581 @Property_RO 

582 def b_a(self): 

583 '''Get the ratio I{polar} over I{equatorial} radius (C{float}), M{b / a == f1 == 1 - f}. 

584 

585 @see: Property L{f1}. 

586 ''' 

587 return self._assert(self.b / self.a, b_a=self.f1, f0=_1_0) 

588 

589 @Property_RO 

590 def b2(self): 

591 '''Get the I{polar} radius I{squared} (C{float}), M{b**2}. 

592 ''' 

593 return Meter2(b2=self.b**2) 

594 

595 @Property_RO 

596 def b2_a(self): 

597 '''Get the I{equatorial} meridional radius of curvature (C{meter}), M{b**2 / a}, see C{rocMeridional}C{(0)}. 

598 

599 @see: U{Radii of Curvature<https://WikiPedia.org/wiki/Earth_radius#Radii_of_curvature>}. 

600 ''' 

601 return Radius(b2_a=_over(self.b2, self.a) if self.f else self.b) 

602 

603 @Property_RO 

604 def b2_a2(self): 

605 '''Get the ratio I{polar} over I{equatorial} radius I{squared} (C{float}), M{(b / a)**2} 

606 == M{(1 - f)**2} == M{1 - e**2} == C{e21}. 

607 ''' 

608 return Float(b2_a2=self.b_a**2 if self.f else _1_0) 

609 

610 def _Betad(self, lat, inverse=False): 

611 '''(INTERNAL) Get the I{parametric (or reduced) auxiliary latitude} or inverse thereof. 

612 ''' 

613 s, c = sincos2d(lat) # like Karney's tand(lat) 

614 s *= self.a_b if inverse else self.b_a 

615 return atan1d(s, c) # (s * a, c * b) if inverse else (s * b, c * a) 

616 

617 @Property_RO 

618 def BetaKs(self): 

619 '''Get the I{Krüger} U{Beta series coefficients<https://GeographicLib.SourceForge.io/C++/doc/tmseries30.html>} (C{KsOrder}C{-tuple}). 

620 ''' 

621 return self._Kseries( # XXX int/int quotients may require from __future__ import division as _; del _ # noqa: E702 ; 

622 # n n**2 n**3 n**4 n**5 n**6 n**7 n**8 

623 _T(1/2, -2/3, 37/96, -1/360, -81/512, 96199/604800, -5406467/38707200, 7944359/67737600), 

624 _T(1/48, 1/15, -437/1440, 46/105, -1118711/3870720, 51841/1209600, 24749483/348364800), # PYCHOK unaligned 

625 _T(17/480, -37/840, -209/4480, 5569/90720, 9261899/58060800, -6457463/17740800), # PYCHOK unaligned 

626 _T(4397/161280, -11/504, -830251/7257600, 466511/2494800, 324154477/7664025600), # PYCHOK unaligned 

627 _T(4583/161280, -108847/3991680, -8005831/63866880, 22894433/124540416), # PYCHOK unaligned 

628 _T(20648693/638668800, -16363163/518918400, -2204645983/12915302400), # PYCHOK unaligne 

629 _T(219941297/5535129600, -497323811/12454041600), # PYCHOK unaligned 

630 _T(191773887257/3719607091200)) # PYCHOK unaligned 

631 

632 @deprecated_Property_RO 

633 def c(self): # PYCHOK no cover 

634 '''DEPRECATED, use property C{R2} or C{Rauthalic}.''' 

635 return self.R2 

636 

637 @Property_RO 

638 def c2(self): 

639 '''Get the I{authalic} earth radius I{squared} (C{meter} I{squared}). 

640 

641 @see: Properties L{c2x}, L{area}, L{R2}, L{Rauthalic}, I{Karney's} U{equation (60) 

642 <https://Link.Springer.com/article/10.1007%2Fs00190-012-0578-z>} and C++ U{Ellipsoid.Area 

643 <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Ellipsoid.html>}, 

644 U{Authalic radius<https://WikiPedia.org/wiki/Earth_radius#Authalic_radius>}, U{Surface area 

645 <https://WikiPedia.org/wiki/Ellipsoid>} and U{surface area 

646 <https://www.Numericana.com/answer/geometry.htm#oblate>}. 

647 ''' 

648 return self._c2f(False) 

649 

650 @Property_RO 

651 def c2x(self): 

652 '''Get the I{authalic} earth radius I{squared} (C{meter} I{squared}), more accurate for very I{oblate} 

653 ellipsoids. 

654 

655 @see: Properties L{c2}, L{areax}, L{R2x}, L{Rauthalicx}, class L{GeodesicExact} and I{Karney}'s comments at C++ 

656 attribute U{GeodesicExact._c2<https://GeographicLib.SourceForge.io/C++/doc/GeodesicExact_8cpp_source.html>}. 

657 ''' 

658 return self._c2f(True) 

659 

660 def _c2f(self, c2x): 

661 '''(INTERNAL) Helper for C{.c2} and C{.c2x}. 

662 ''' 

663 f, c2 = self.f, self.b2 

664 if f: 

665 e = self.e 

666 if e > EPS0: 

667 if f > 0: # .isOblate 

668 c2 *= (asinh(sqrt(self.e22abs)) if c2x else atanh(e)) / e 

669 elif f < 0: # .isProlate 

670 c2 *= atan1(e) / e # XXX asin? 

671 c2 = Meter2(c2=(self.a2 + c2) * _0_5) 

672 return c2 

673 

674 def circle4(self, lat): 

675 '''Get the equatorial or a parallel I{circle of latitude}. 

676 

677 @arg lat: Geodetic latitude (C{degrees90} or C{str}). 

678 

679 @return: A L{Circle4Tuple}C{(radius, height, lat, beta)}. 

680 

681 @raise RangeError: Latitude B{C{lat}} outside valid range, only if 

682 L{rangerrors<pygeodesy.rangerrors>} is C{True}. 

683 

684 @raise TypeError: Invalid B{C{lat}}. 

685 

686 @raise ValueError: Invalid B{C{lat}}. 

687 

688 @see: Definition of U{I{p} and I{z} under B{Parametric (or reduced) latitude} 

689 <https://WikiPedia.org/wiki/Latitude>}, I{Karney's} C++ U{CircleRadius and CircleHeight 

690 <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Ellipsoid.html>} 

691 and method C{Triaxial.ellipse5}. 

692 ''' 

693 lat = _Lat0(lat) 

694 if lat: 

695 B = lat # beta 

696 if fabs(lat) < _90_0: 

697 if self.f: 

698 B = self._Betad(lat) 

699 z, r = sincos2d(B) 

700 r *= self.a 

701 z *= self.b 

702 else: # near-polar 

703 r, z = _0_0, copysign0(self.b, lat) 

704 else: # equatorial 

705 r = self.a 

706 z = lat = B = _0_0 

707 return Circle4Tuple(r, z, lat, B) 

708 

709 def degrees2m(self, deg, lat=0): 

710 '''Convert an arc in degrees to a distance along the equator or 

711 along a parallel of (geodetic) latitude. 

712 

713 @arg deg: The angle (C{degrees}). 

714 @kwarg lat: Parallel latitude (C{degrees90}, C{str}). 

715 

716 @return: Distance (C{meter}, same units as the equatorial 

717 and polar radii) or C{0} for near-polar B{C{lat}}. 

718 

719 @raise RangeError: Latitude B{C{lat}} outside valid range and 

720 L{rangerrors<pygeodesy.rangerrors>} is C{True}. 

721 

722 @raise ValueError: Invalid B{C{deg}} or B{C{lat}}. 

723 ''' 

724 return self.radians2m(radians(deg), lat=lat) 

725 

726 def distance2(self, lat0, lon0, lat1, lon1): 

727 '''I{Approximate} the distance and (initial) bearing between 

728 two points based on the U{local, flat earth approximation 

729 <https://www.EdWilliams.org/avform.htm#flat>} aka U{Hubeny 

730 <https://www.OVG.AT/de/vgi/files/pdf/3781/>} formula. 

731 

732 I{Suitable only for distances of several hundred Km or Miles 

733 and only between points not near-polar}. 

734 

735 @arg lat0: From latitude (C{degrees}). 

736 @arg lon0: From longitude (C{degrees}). 

737 @arg lat1: To latitude (C{degrees}). 

738 @arg lon1: To longitude (C{degrees}). 

739 

740 @return: A L{Distance2Tuple}C{(distance, initial)} with C{distance} 

741 in same units as this ellipsoid's axes. 

742 

743 @note: The meridional and prime_vertical radii of curvature are 

744 taken and scaled I{at the initial latitude}, see C{roc2}. 

745 

746 @see: Function L{pygeodesy.flatLocal}/L{pygeodesy.hubeny}. 

747 ''' 

748 phi0 = Phid(lat0=lat0) 

749 m, n = self.roc2_(phi0, scaled=True) 

750 m *= Phid(lat1=lat1) - phi0 

751 n *= Lamd(lon1=lon1) - Lamd(lon0=lon0) 

752 return Distance2Tuple(hypot(m, n), atan2b(n, m)) 

753 

754 @Property_RO 

755 def e(self): 

756 '''Get the I{unsigned, (1st) eccentricity} (C{float}), M{sqrt(1 - (b / a)**2))}, see C{a_b2e}. 

757 

758 @see: Property L{es}. 

759 ''' 

760 return Float(e=sqrt(self.e2abs) if self.e2 else _0_0) 

761 

762 @deprecated_Property_RO 

763 def e12(self): # see property ._e12 

764 '''DEPRECATED, use property C{e21}.''' 

765 return self.e21 

766 

767# @Property_RO 

768# def _e12(self): # see property ._elliptic_e12 

769# # (INTERNAL) until e12 above can be replaced with e21. 

770# return self.e2 / (_1_0 - self.e2) # see I{Karney}'s Ellipsoid._e12 = e2 / (1 - e2) 

771 

772 @Property_RO 

773 def e2(self): 

774 '''Get the I{signed, (1st) eccentricity squared} (C{float}), M{f * (2 - f) 

775 == 1 - (b / a)**2}, see C{a_b2e2}. 

776 ''' 

777 return self._assert(a_b2e2(self.a, self.b), e2=f2e2(self.f)) 

778 

779 @Property_RO 

780 def e2abs(self): 

781 '''Get the I{unsigned, (1st) eccentricity squared} (C{float}). 

782 ''' 

783 return fabs(self.e2) 

784 

785 @Property_RO 

786 def e2not(self): 

787 '''Get I{self.e**2 and NOT .e2 NOR .e2abs} (C{float}). 

788 ''' 

789 return self.e**2 

790 

791 @Property_RO 

792 def e21(self): 

793 '''Get 1 less I{1st eccentricity squared} (C{float}), M{1 - e**2} 

794 == M{1 - e2} == M{(1 - f)**2} == M{b**2 / a**2}, see C{b2_a2}. 

795 ''' 

796 return self._assert((_1_0 - self.f)**2, e21=_1_0 - self.e2, f0=_1_0) 

797 

798# _e2m = e21 # see I{Karney}'s Ellipsoid._e2m = 1 - _e2 

799 _1_e21 = a2_b2 # == M{1 / e21} == M{1 / (1 - e**2)} 

800 

801 @Property_RO 

802 def e22(self): 

803 '''Get the I{signed, 2nd eccentricity squared} (C{float}), M{e2 / (1 - e2) 

804 == e2 / (1 - f)**2 == (a / b)**2 - 1}, see C{a_b2e22}. 

805 ''' 

806 return self._assert(a_b2e22(self.a, self.b), e22=f2e22(self.f)) 

807 

808 @Property_RO 

809 def e22abs(self): 

810 '''Get the I{unsigned, 2nd eccentricity squared} (C{float}). 

811 ''' 

812 return fabs(self.e22) 

813 

814 @Property_RO 

815 def e32(self): 

816 '''Get the I{signed, 3rd eccentricity squared} (C{float}), M{e2 / (2 - e2) 

817 == (a**2 - b**2) / (a**2 + b**2)}, see C{a_b2e32}. 

818 ''' 

819 return self._assert(a_b2e32(self.a, self.b), e32=f2e32(self.f)) 

820 

821 @Property_RO 

822 def e32abs(self): 

823 '''Get the I{unsigned, 3rd eccentricity squared} (C{float}). 

824 ''' 

825 return fabs(self.e32) 

826 

827 @Property_RO 

828 def e4(self): 

829 '''Get the I{unsignd, (1st) eccentricity} to 4th power (C{float}), M{e**4 == e2**2}. 

830 ''' 

831 return Float(e4=self.e2**2 if self.e2 else _0_0) 

832 

833 eccentricity = e # eccentricity 

834# eccentricity2 = e2 # eccentricity squared 

835 eccentricity1st2 = e2 # first eccentricity squared, signed 

836 eccentricity2nd2 = e22 # second eccentricity squared, signed 

837 eccentricity3rd2 = e32 # third eccentricity squared, signed 

838 

839 def ecef(self, Ecef=None): 

840 '''Return U{ECEF<https://WikiPedia.org/wiki/ECEF>} converter. 

841 

842 @kwarg Ecef: ECEF class to use, default L{EcefKarney}. 

843 

844 @return: An ECEF converter for this C{ellipsoid}. 

845 

846 @raise TypeError: Invalid B{C{Ecef}}. 

847 

848 @see: Module L{pygeodesy.ecef}. 

849 ''' 

850 return _MODS.ecef._4Ecef(self, Ecef) 

851 

852 @Property_RO 

853 def _elliptic_e12(self): # see I{Karney}'s Ellipsoid._e12 

854 '''(INTERNAL) Elliptic helper for C{Rhumb}. 

855 ''' 

856 e12 = _over(self.e2, self.e2 - _1_0) # NOT DEPRECATED .e12! 

857 return _MODS.elliptic.Elliptic(e12) 

858 

859 @Property_RO 

860 def _elliptic_e22(self): # aka ._elliptic_ep2 

861 '''(INTERNAL) Elliptic helper for C{auxRectifying}, C{L}, C{Llat}. 

862 ''' 

863 # courtesy gaoflow <https://GitHub.com/mrJean1/PyGeodesy/pull/86> 

864 return _MODS.elliptic.Elliptic(-self.e22) # signed, like ._elliptic_e12 

865 

866 equatoradius = a # Requatorial 

867 

868 @Property_RO 

869 def equatorimeter(self): 

870 '''Get the ellipsoid's I{equatorial} perimeter (C{meter}). 

871 ''' 

872 return Meter(equatorimeter=self.a * PI2) 

873 

874 def e2s(self, s): 

875 '''Compute norm M{sqrt(1 - e2 * s**2)}. 

876 

877 @arg s: Sine value (C{scalar}). 

878 

879 @return: Norm (C{float}). 

880 

881 @raise ValueError: Invalid B{C{s}}. 

882 ''' 

883 return sqrt(self.e2s2(s)) if self.e2 else _1_0 

884 

885 def e2s2(self, s): 

886 '''Compute M{1 - e2 * s**2}. 

887 

888 @arg s: Sine value (C{scalar}). 

889 

890 @return: Result (C{float}). 

891 

892 @raise ValueError: Invalid B{C{s}}. 

893 ''' 

894 r, e2 = _1_0, self.e2 

895 if e2: # and s 

896 try: 

897 r -= e2 * Scalar(s=s)**2 

898 if r < 0: 

899 raise ValueError(_negative_) 

900 except (TypeError, ValueError) as x: 

901 t = self._DOT_(typename(Ellipsoid.e2s2)) 

902 raise _ValueError(t, s, cause=x) 

903 return r 

904 

905 @Property_RO 

906 def es(self): 

907 '''Get the I{signed (1st) eccentricity} (C{float}). 

908 

909 @see: Property L{e}. 

910 ''' 

911 # note, self.e is always non-negative 

912 return Float(es=copysign0(self.e, self.f)) # see .ups 

913 

914 def es_atanh(self, x): 

915 '''Compute M{es * atanh(es * x)} or M{-es * atan(es * x)} 

916 for I{oblate} respectively I{prolate} ellipsoids where 

917 I{es} is the I{signed} (1st) eccentricity. 

918 

919 @raise ValueError: Invalid B{C{x}}. 

920 

921 @see: Function U{Math::eatanhe<https://GeographicLib.SourceForge.io/ 

922 C++/doc/classGeographicLib_1_1Math.html>}. 

923 ''' 

924 return self._es_atanh(Scalar(x=x)) if self.f else _0_0 

925 

926 def _es_atanh(self, x): # see .albers._atanhee, .AuxLat._atanhee 

927 '''(INTERNAL) Helper for .es_atanh, ._es_taupf2 and ._exp_es_atanh. 

928 ''' 

929 es = self.es # signOf(es) == signOf(f) 

930 return es * (atanh(es * x) if es > 0 else # .isOblate 

931 (-atan(es * x) if es < 0 else # .isProlate 

932 _0_0)) # .isSpherical 

933 

934 @Property_RO 

935 def es_c(self): 

936 '''Get M{(1 - f) * exp(es_atanh(1))} (C{float}), M{b_a * exp(es_atanh(1))}. 

937 ''' 

938 return Float(es_c=(self._exp_es_atanh_1 * self.b_a) if self.f else _1_0) 

939 

940 def es_tauf(self, taup): 

941 '''Compute I{Karney}'s U{equations (19), (20) and (21) 

942 <https://ArXiv.org/abs/1002.1417>}. 

943 

944 @see: I{Karney}'s C++ method U{Math::tauf<https://GeographicLib. 

945 SourceForge.io/C++/doc/classGeographicLib_1_1Math.html>} and 

946 and I{Veness}' JavaScript method U{toLatLon<https://www. 

947 Movable-Type.co.UK/scripts/latlong-utm-mgrs.html>}. 

948 ''' 

949 t = Scalar(taup=taup) 

950 if self.f: # .isEllipsoidal 

951 a = fabs(t) 

952 T = (self._exp_es_atanh_1 if a > 70 else self._1_e21) * t 

953 if fabs(T * _EPSqrt) < _2_0: # handles +/- INF and NAN 

954 s = (a * _TOL) if a > _1_0 else _TOL 

955 for T, _, d in self._es_tauf3(t, T): # max 2 

956 if fabs(d) < s: 

957 break 

958 else: 

959 t = unstr(self.es_tauf, taup) 

960 raise _AssertionError(Fmt.no_convergence(d, s), txt=t) 

961 t = Scalar(tauf=T) 

962 return t 

963 

964 def _es_tauf3(self, taup, T, N=9): # in .utm.Utm._toLLEB 

965 '''(INTERNAL) Yield a 3-tuple C{(τi, iteration, delta)} for at most 

966 B{C{N}} Newton iterations, converging rapidly except when C{delta} 

967 toggles on +/-1.12e-16 or +/-4.47e-16, see C{.utm.Utm._toLLEB}. 

968 ''' 

969 e = self._1_e21 

970 _F2_ = Fsum(T).fsum2f_ # τ0 

971 _tf2 = self._es_taupf2 

972 for i in range(1, N + 1): 

973 a, h = _tf2(T) 

974 # = (taup - a) / hypot1(a) / ((e + T**2) / h) 

975 d = _over((taup - a) * (T**2 + e), hypot1(a) * h) 

976 T, d = _F2_(d) # τi, (τi - τi-1) 

977 yield T, i, d 

978 

979 def es_taupf(self, tau): 

980 '''Compute I{Karney}'s U{equations (7), (8) and (9) 

981 <https://ArXiv.org/abs/1002.1417>}. 

982 

983 @see: I{Karney}'s C++ method U{Math::taupf<https://GeographicLib. 

984 SourceForge.io/C++/doc/classGeographicLib_1_1Math.html>}. 

985 ''' 

986 t = Scalar(tau=tau) 

987 if self.f: # .isEllipsoidal 

988 t, _ = self._es_taupf2(t) 

989 t = Scalar(taupf=t) 

990 return t 

991 

992 def _es_taupf2(self, tau): 

993 '''(INTERNAL) Return 2-tuple C{(es_taupf(tau), hypot1(tau))}. 

994 ''' 

995 if _isfinite(tau): 

996 h = hypot1(tau) 

997 s = sinh(self._es_atanh(tau / h)) 

998 a = hypot1(s) * tau - h * s 

999 else: 

1000 a, h = tau, INF 

1001 return a, h 

1002 

1003 @Property_RO 

1004 def _exp_es_atanh_1(self): 

1005 '''(INTERNAL) Helper for .es_c and .es_tauf. 

1006 ''' 

1007 return exp(self._es_atanh(_1_0)) if self.es else _1_0 

1008 

1009 @Property_RO 

1010 def f(self): 

1011 '''Get the I{flattening} (C{scalar}), M{(a - b) / a}, C{0} for spherical, negative for prolate. 

1012 ''' 

1013 return self._f 

1014 

1015 @Property_RO 

1016 def f_(self): 

1017 '''Get the I{inverse flattening} (C{scalar}), M{1 / f} == M{a / (a - b)}, C{0} for spherical, see C{a_b2f_}. 

1018 ''' 

1019 return self._f_ 

1020 

1021 @Property_RO 

1022 def f1(self): 

1023 '''Get the I{1 - flattening} (C{float}), M{f1 == 1 - f == b / a}. 

1024 

1025 @see: Property L{b_a}. 

1026 ''' 

1027 return Float(f1=_1_0 - self.f) 

1028 

1029 @Property_RO 

1030 def f2(self): 

1031 '''Get the I{2nd flattening} (C{float}), M{(a - b) / b == f / (1 - f)}, C{0} for spherical, see C{a_b2f2}. 

1032 ''' 

1033 return self._assert(self.a_b - _1_0, f2=f2f2(self.f)) 

1034 

1035 @deprecated_Property_RO 

1036 def geodesic(self): 

1037 '''DEPRECATED, use property C{geodesicw}.''' 

1038 return self.geodesicw 

1039 

1040 def geodesic_(self, exact=True): 

1041 '''Get the an I{exact} C{Geodesic...} instance for this ellipsoid. 

1042 

1043 @kwarg exact: If C{bool} return L{GeodesicExact}C{(exact=B{exact}, ...)}, 

1044 otherwise a L{Geodesic}, L{GeodesicExact} or L{GeodesicSolve} 

1045 instance for I{this} ellipsoid. 

1046 

1047 @return: The C{exact} geodesic (C{Geodesic...}). 

1048 

1049 @raise TypeError: Invalid B{C{exact}}. 

1050 

1051 @raise ValueError: Incompatible B{C{exact}} ellipsoid. 

1052 ''' 

1053 if isbool(exact): # for consistenccy with C{.rhumb_} 

1054 g = _MODS.geodesicx.GeodesicExact(self, C4order=30 if exact else 24, 

1055 name=self.name) 

1056 else: 

1057 g = exact 

1058 E = _xattr(g, ellipsoid=None) 

1059 if not (E is self and isinstance(g, self._Geodesics)): 

1060 raise _ValueError(exact=g, ellipsoid=E, txt_not_=self.name) 

1061 return g 

1062 

1063 @property_ROver 

1064 def _Geodesics(self): 

1065 '''(INTERNAL) Get all C{Geodesic...} classes, I{once}. 

1066 ''' 

1067 t = (_MODS.geodesicx.GeodesicExact, 

1068 _MODS.geodsolve.GeodesicSolve) 

1069 try: 

1070 t += (_MODS.geodesicw.Geodesic, 

1071 _MODS.geodesicw._wrapped.Geodesic) 

1072 except ImportError: 

1073 pass 

1074 return t # overwrite property_ROver 

1075 

1076 @property_RO 

1077 def geodesicw(self): 

1078 '''Get this ellipsoid's I{wrapped} U{geodesicw.Geodesic 

1079 <https://GeographicLib.SourceForge.io/Python/doc/code.html>}, provided 

1080 I{Karney}'s U{geographiclib<https://PyPI.org/project/geographiclib>} 

1081 package is installed. 

1082 ''' 

1083 # if not self.isEllipsoidal: 

1084 # raise _IsnotError(_ellipsoidal_, ellipsoid=self) 

1085 return _MODS.geodesicw.Geodesic(self) 

1086 

1087 @property_RO 

1088 def geodesicx(self): 

1089 '''Get this ellipsoid's I{exact} L{GeodesicExact}. 

1090 ''' 

1091 # if not self.isEllipsoidal: 

1092 # raise _IsnotError(_ellipsoidal_, ellipsoid=self) 

1093 return _MODS.geodesicx.GeodesicExact(self, name=self.name) 

1094 

1095 @property 

1096 def geodsolve(self): 

1097 '''Get this ellipsoid's L{GeodesicSolve}, the I{wrapper} around utility 

1098 U{GeodSolve<https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>}, 

1099 provided the path to the C{GeodSolve} executable is specified with env 

1100 variable C{PYGEODESY_GEODSOLVE} or re-/set with this property.. 

1101 ''' 

1102 # if not self.isEllipsoidal: 

1103 # raise _IsnotError(_ellipsoidal_, ellipsoid=self) 

1104 return _MODS.geodsolve.GeodesicSolve(self, path=self._geodsolve, name=self.name) 

1105 

1106 @geodsolve.setter # PYCHOK setter! 

1107 def geodsolve(self, path): 

1108 '''Re-/set the (fully qualified) path to the U{GeodSolve 

1109 <https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>} executable, 

1110 overriding env variable C{PYGEODESY_GEODSOLVE} (C{str}). 

1111 ''' 

1112 self._geodsolve = path 

1113 

1114 def hartzell4(self, pov, los=None): 

1115 '''Compute the intersection of this ellipsoid's surface and a Line-Of-Sight 

1116 from a Point-Of-View in space. 

1117 

1118 @arg pov: Point-Of-View outside this ellipsoid (C{Cartesian}, L{Ecef9Tuple} 

1119 or L{Vector3d}). 

1120 @kwarg los: Line-Of-Sight, I{direction} to this ellipsoid (L{Los}, L{Vector3d}) 

1121 or C{True} for the I{normal, perpendicular, plumb} to the surface 

1122 of this ellipsoid or C{False} or C{None} to point to its center. 

1123 

1124 @return: L{Vector4Tuple}C{(x, y, z, h)} with the cartesian coordinates C{x}, 

1125 C{y} and C{z} of the projection on or the intersection with this 

1126 ellipsoid and the I{distance} C{h} from B{C{pov}} to C{(x, y, z)} 

1127 along B{C{los}}, all in C{meter}, conventionally. 

1128 

1129 @raise IntersectionError: Null B{C{pov}} or B{C{los}} vector, or B{C{pov}} 

1130 is inside this ellipsoid or B{C{los}} points 

1131 outside this ellipsoid or in opposite direction. 

1132 

1133 @raise TypeError: Invalid B{C{pov}} or B{C{los}}. 

1134 

1135 @see: U{I{Satellite Line-of-Sight Intersection with Earth}<https://StephenHartzell. 

1136 Medium.com/satellite-line-of-sight-intersection-with-earth-d786b4a6a9b6>} and 

1137 methods L{Ellipsoid.height4} and L{Triaxial.hartzell4}. 

1138 ''' 

1139 try: 

1140 m = _MODS._triaxials_triaxial5 

1141 v, d, i = m._hartzell3(pov, los, self._triaxial) # PYLINT E1101 

1142 except Exception as x: 

1143 raise IntersectionError(pov=pov, los=los, cause=x) 

1144 return Vector4Tuple(v.x, v.y, v.z, d, iteration=i, name__=self.hartzell4) 

1145 

1146 @Property_RO 

1147 def _hash(self): 

1148 return hash((self.a, self.f)) 

1149 

1150 def height4(self, xyz, normal=True): 

1151 '''Compute the projection on and the height of a cartesian above or below 

1152 this ellipsoid's surface. 

1153 

1154 @arg xyz: The cartesian (C{Cartesian}, L{Ecef9Tuple}, L{Vector3d}, 

1155 L{Vector3Tuple} or L{Vector4Tuple}). 

1156 @kwarg normal: If C{True}, the projection is perpendicular to (the nearest 

1157 point on) this ellipsoid's surface, otherwise the C{radial} 

1158 line to this ellipsoid's center (C{bool}). 

1159 

1160 @return: L{Vector4Tuple}C{(x, y, z, h)} with the cartesian coordinates C{x}, 

1161 C{y} and C{z} of the projection on and the height C{h} above or 

1162 below this ellipsoid's surface, all in C{meter}, conventionally. 

1163 

1164 @raise ValueError: Null B{C{xyz}}. 

1165 

1166 @raise TypeError: Non-cartesian B{C{xyz}}. 

1167 

1168 @see: U{Distance to<https://StackOverflow.com/questions/22959698/distance-from-given-point-to-given-ellipse>} 

1169 and U{intersection with<https://MathWorld.Wolfram.com/Ellipse-LineIntersection.html>} an ellipse and 

1170 methods L{Ellipsoid.hartzell4} and L{Triaxial.height4}. 

1171 ''' 

1172 v = _MODS.vector3d._otherV3d(xyz=xyz) 

1173 r = v.length 

1174 

1175 a, b, i = self.a, self.b, None 

1176 if r < EPS0: # EPS 

1177 v = v.times(_0_0) 

1178 h = -a 

1179 

1180 elif self.isSpherical: 

1181 v = v.times(a / r) 

1182 h = r - a 

1183 

1184 elif normal: # perpendicular to ellipsoid 

1185 x, y = hypot(v.x, v.y), fabs(v.z) 

1186 if x < EPS0: # PYCHOK no cover 

1187 z = copysign0(b, v.z) 

1188 v = Vector3Tuple(v.x, v.y, z) 

1189 h = y - b # polar 

1190 elif y < EPS0: # PYCHOK no cover 

1191 t = a / r 

1192 v = v.times_(t, t, 0) # force z=0.0 

1193 h = x - a # equatorial 

1194 else: # normal in 1st quadrant 

1195 m = _MODS._triaxials_triaxial5 

1196 x, y, i = m._plumbTo3(x, y, self) # PYLINT E1101 

1197 t, v = v, v.times_(x, x, y) 

1198 h = t.minus(v).length 

1199 

1200 else: # radial to ellipsoid's center 

1201 h = hypot_(a * v.z, b * v.x, b * v.y) 

1202 t = (a * b / h) if h > EPS0 else _0_0 # EPS 

1203 v = v.times(t) 

1204 h = r * (_1_0 - t) 

1205 

1206 return Vector4Tuple(v.x, v.y, v.z, h, iteration=i, name__=self.height4) 

1207 

1208 @Property_RO 

1209 def heightMax(self): 

1210 '''Get the height limit (C{meter}, conventionally). 

1211 ''' 

1212 return self.a / EPS_2 # self.a * _2_EPS, about 12M lightyears 

1213 

1214 def _hubeny_2(self, phi2, phi1, lam21, scaled=True, squared=True): 

1215 '''(INTERNAL) like function C{pygeodesy.flatLocal_}/C{pygeodesy.hubeny_}, 

1216 returning the I{angular} distance in C{radians squared} or C{radians} 

1217 ''' 

1218 m, n = self.roc2_((phi2 + phi1) * _0_5, scaled=scaled) 

1219 h, r = (hypot2, self.a2_) if squared else (hypot, _1_0 / self.a) 

1220 return h(m * (phi2 - phi1), n * lam21) * r 

1221 

1222 @Property_RO 

1223 def isEllipsoidal(self): 

1224 '''Is this model I{ellipsoidal} (C{bool})? 

1225 ''' 

1226 return self.f != 0 

1227 

1228 @Property_RO 

1229 def isOblate(self): 

1230 '''Is this ellipsoid I{oblate} (C{bool})? I{Prolate} or 

1231 spherical otherwise. 

1232 ''' 

1233 return self.f > 0 

1234 

1235 @Property_RO 

1236 def isProlate(self): 

1237 '''Is this ellipsoid I{prolate} (C{bool})? I{Oblate} or 

1238 spherical otherwise. 

1239 ''' 

1240 return self.f < 0 

1241 

1242 @Property_RO 

1243 def isSpherical(self): 

1244 '''Is this ellipsoid I{spherical} (C{bool})? 

1245 ''' 

1246 return self.f == 0 

1247 

1248 def _Kseries(self, *AB8Ks): 

1249 '''(INTERNAL) Compute the 4-, 6- or 8-th order I{Krüger} Alpha 

1250 or Beta series coefficients per I{Karney}'s U{equations (35) 

1251 and (36)<https://ArXiv.org/pdf/1002.1417v3.pdf>}. 

1252 

1253 @arg AB8Ks: 8-Tuple of 8-th order I{Krüger} Alpha or Beta series 

1254 coefficient tuples. 

1255 

1256 @return: I{Krüger} series coefficients (L{KsOrder}C{-tuple}). 

1257 

1258 @see: I{Karney}'s 30-th order U{TMseries30 

1259 <https://GeographicLib.SourceForge.io/C++/doc/tmseries30.html>}. 

1260 ''' 

1261 k = self.KsOrder 

1262 if self.n: 

1263 ns = fpowers(self.n, k) 

1264 ks = tuple(fdot(AB8Ks[i][:k-i], *ns[i:]) for i in range(k)) 

1265 else: 

1266 ks = _0_0s(k) 

1267 return ks 

1268 

1269 @property_doc_(''' the I{Krüger} series' order (C{int}), see properties C{AlphaKs}, C{BetaKs}.''') 

1270 def KsOrder(self): 

1271 '''Get the I{Krüger} series' order (C{int} 4, 6 or 8). 

1272 ''' 

1273 return self._KsOrder 

1274 

1275 @KsOrder.setter # PYCHOK setter! 

1276 def KsOrder(self, order): 

1277 '''Set the I{Krüger} series' order (C{int} 4, 6 or 8). 

1278 

1279 @raise ValueError: Invalid B{C{order}}. 

1280 ''' 

1281 if not (isint(order) and _isin(order, 4, 6, 8)): 

1282 raise _ValueError(order=order) 

1283 if self._KsOrder != order: 

1284 Ellipsoid.AlphaKs._update(self) 

1285 Ellipsoid.BetaKs._update(self) 

1286 self._KsOrder = order 

1287 

1288 @Property_RO 

1289 def L(self): 

1290 '''Get the I{quarter meridian} C{L}, aka the C{polar distance} 

1291 along a meridian between the equator and a pole (C{meter}), 

1292 M{b * Elliptic(-e2 / (1 - e2)).cE} or M{b * PI / 2}. 

1293 ''' 

1294 r = self._elliptic_e22.cE if self.f else PI_2 

1295 return Distance(L=self.b * r) 

1296 

1297 def Llat(self, lat): 

1298 '''Return the I{meridional length}, the distance along a meridian 

1299 between the equator and a (geodetic) latitude, see C{L}. 

1300 

1301 @arg lat: Geodetic latitude (C{degrees90}). 

1302 

1303 @return: The meridional length at B{C{lat}}, negative on southern 

1304 hemisphere (C{meter}). 

1305 ''' 

1306 r = self._elliptic_e22.fEd(self.auxParametric(lat)) if self.f else Phid(lat) 

1307 return Distance(Llat=self.b * r) 

1308 

1309 Lmeridian = Llat # meridional distance 

1310 

1311 @property_RO 

1312 def _Lpd(self): 

1313 '''Get the I{quarter meridian} per degree (C{meter}), M{self.L / 90}. 

1314 ''' 

1315 return Meter(_Lpd=self.L / _90_0) 

1316 

1317 @property_RO 

1318 def _Lpr(self): 

1319 '''Get the I{quarter meridian} per radian (C{meter}), M{self.L / PI_2}. 

1320 ''' 

1321 return Meter(_Lpr=self.L / PI_2) 

1322 

1323 @deprecated_Property_RO 

1324 def majoradius(self): # PYCHOK no cover 

1325 '''DEPRECATED, use property C{a} or C{Requatorial}.''' 

1326 return self.a 

1327 

1328 def m2degrees(self, distance, lat=0): 

1329 '''Convert a distance to an arc in degrees along the equator or 

1330 along a parallel of (geodetic) latitude. 

1331 

1332 @arg distance: Distance (C{meter}). 

1333 @kwarg lat: Parallel latitude (C{degrees90}, C{str}). 

1334 

1335 @return: Angle (C{degrees}) or C{0} for near-polar B{C{lat}}. 

1336 

1337 @raise RangeError: Latitude B{C{lat}} outside valid range and 

1338 L{rangerrors<pygeodesy.rangerrors>} is C{True}. 

1339 

1340 @raise ValueError: Invalid B{C{distance}} or B{C{lat}}. 

1341 ''' 

1342 return degrees(self.m2radians(distance, lat=lat)) 

1343 

1344 def m2radians(self, distance, lat=0): 

1345 '''Convert a distance to an angle along the equator or along 

1346 a parallel of (geodetic) latitude. 

1347 

1348 @arg distance: Distance (C{meter}). 

1349 @kwarg lat: Parallel latitude (C{degrees90}, C{str}). 

1350 

1351 @return: Angle (C{radians}) or C{INF} for near-polar B{C{lat}}. 

1352 

1353 @raise RangeError: Latitude B{C{lat}} outside valid range and 

1354 L{rangerrors<pygeodesy.rangerrors>} is C{True}. 

1355 

1356 @raise ValueError: Invalid B{C{distance}} or B{C{lat}}. 

1357 ''' 

1358 r = self.circle4(lat).radius if lat else self.a 

1359 return m2radians(distance, radius=r, lat=0) 

1360 

1361 @deprecated_Property_RO 

1362 def minoradius(self): # PYCHOK no cover 

1363 '''DEPRECATED, use property C{b}, C{polaradius} or C{Rpolar}.''' 

1364 return self.b 

1365 

1366 @Property_RO 

1367 def n(self): 

1368 '''Get the I{3rd flattening} (C{float}), M{f / (2 - f) == (a - b) / (a + b)}, see C{a_b2n}. 

1369 ''' 

1370 return self._assert(a_b2n(self.a, self.b), n=f2n(self.f)) 

1371 

1372 flattening = f 

1373 flattening1st = f 

1374 flattening2nd = f2 

1375 flattening3rd = n 

1376 

1377 polaradius = b # Rpolar 

1378 

1379 @property_RO 

1380 def polarimeter(self): 

1381 '''Get the ellipsoid's I{polar}, meridional perimeter (C{meter}). 

1382 ''' 

1383 return Meter(polarimeter=self.L * _4_0) 

1384 

1385# Q = A # I{meridian arc unit} C{Q}, the mean, meridional length I{per radian} 

1386 

1387 @deprecated_Property_RO 

1388 def quarteradius(self): # PYCHOK no cover 

1389 '''DEPRECATED, use property C{L} or method C{Llat}.''' 

1390 return self.L 

1391 

1392 @Property_RO 

1393 def R1(self): 

1394 '''Get the I{mean} earth radius per I{IUGG} (C{meter}), M{(2 * a + b) / 3 == a * (1 - f / 3)}. 

1395 

1396 @see: U{Earth radius<https://WikiPedia.org/wiki/Earth_radius>} 

1397 and method C{Rgeometric}. 

1398 ''' 

1399 return Radius(R1=fmean_(self.a, self.a, self.b) if self.f else self.a) 

1400 

1401 Rmean = R1 

1402 

1403 @Property_RO 

1404 def R2(self): 

1405 '''Get the I{authalic} earth radius (C{meter}), M{sqrt(c2)}. 

1406 

1407 @see: C{R2x}, C{c2}, C{area} and U{Earth radius 

1408 <https://WikiPedia.org/wiki/Earth_radius>}. 

1409 ''' 

1410 return Radius(R2=sqrt(self.c2) if self.f else self.a) 

1411# # Moritz, H. <https://Geodesy.Geology.Ohio-State.edu/course/refpapers/00740128.pdf> 

1412# # R2 = (1 - 2/3 * e'2 + 26/45 * e'4 - 100/189 * e'6 + 7034/14175 * e'8) * rocPolar 

1413# # = (3 + e'2 * (-2 + e'2 * (26/15 + e'2 * (-100/63 + e'2 * 7034/4725)))) * rocPolar / 3 

1414# return Fhorner(self.e22, 3, -2, 26 / 15, -100 / 63, 7034 / 4725).fover(3 / self.rocPolar) 

1415 

1416 Rauthalic = R2 

1417 

1418 @Property_RO 

1419 def R2x(self): 

1420 '''Get the I{authalic} earth radius (C{meter}), M{sqrt(c2x)}. 

1421 

1422 @see: C{R2}, C{c2x} and C{areax}. 

1423 ''' 

1424 return Radius(R2x=sqrt(self.c2x) if self.f else self.a) 

1425 

1426 Rauthalicx = R2x 

1427 

1428 @Property_RO 

1429 def R3(self): 

1430 '''Get the I{volumetric} earth radius (C{meter}), M{(a * a * b)**(1/3)}. 

1431 

1432 @see: U{Earth radius<https://WikiPedia.org/wiki/Earth_radius>} and C{volume}. 

1433 ''' 

1434 r = (cbrt(self.b_a) * self.a) if self.f else self.a 

1435 return Radius(R3=r) 

1436 

1437 Rvolumetric = R3 

1438 

1439 def radians2m(self, rad, lat=0): 

1440 '''Convert an angle to the distance along the equator or along 

1441 a parallel of (geodetic) latitude. 

1442 

1443 @arg rad: The angle (C{radians}). 

1444 @kwarg lat: Parallel latitude (C{degrees90}, C{str}). 

1445 

1446 @return: Distance (C{meter}, same units as the equatorial 

1447 and polar radii) or C{0} for near-polar B{C{lat}}. 

1448 

1449 @raise RangeError: Latitude B{C{lat}} outside valid range and 

1450 L{rangerrors<pygeodesy.rangerrors>} is C{True}. 

1451 

1452 @raise ValueError: Invalid B{C{rad}} or B{C{lat}}. 

1453 ''' 

1454 r = self.circle4(lat).radius if lat else self.a 

1455 return radians2m(rad, radius=r, lat=0) 

1456 

1457 @Property_RO 

1458 def Rbiaxial(self): 

1459 '''Get the I{biaxial, quadratic} mean earth radius (C{meter}), M{sqrt((a**2 + b**2) / 2)}. 

1460 

1461 @see: C{Rtriaxial} 

1462 ''' 

1463 a, b = self.a, self.b 

1464 if b != a: 

1465 b = hypot(a, b) / _SQRT2 

1466 return Radius(Rbiaxial=b) 

1467 

1468 Requatorial = a # for consistent naming 

1469 

1470 def Rgeocentric(self, lat): 

1471 '''Compute the I{geocentric} earth radius of (geodetic) latitude. 

1472 

1473 @arg lat: Latitude (C{degrees90}). 

1474 

1475 @return: Geocentric earth radius (C{meter}). 

1476 

1477 @raise ValueError: Invalid B{C{lat}}. 

1478 

1479 @see: U{Geocentric Radius 

1480 <https://WikiPedia.org/wiki/Earth_radius#Geocentric_radius>} 

1481 ''' 

1482 r, p = self.a, Phid(lat) 

1483 if p and self.f: 

1484 if fabs(p) < PI_2: 

1485 s2, c2 = _sin2cos2(p) 

1486 # R == sqrt((a2**2 * c2 + b2**2 * s2) / (a2 * c2 + b2 * s2)) 

1487 # == sqrt(a2**2 * (c2 + (b2 / a2)**2 * s2) / (a2 * (c2 + b2 / a2 * s2))) 

1488 # == sqrt(a2 * (c2 + (b2 / a2)**2 * s2) / (c2 + (b2 / a2) * s2)) 

1489 # == a * sqrt((c2 + b2_a2 * b2_a2 * s2) / (c2 + b2_a2 * s2)) 

1490 s2 *= self.b2_a2 

1491 r *= sqrt((c2 + self.b2_a2 * s2) / (c2 + s2)) 

1492 else: 

1493 r = self.b 

1494 return Radius(Rgeocentric=r) 

1495 

1496 @Property_RO 

1497 def Rgeometric(self): 

1498 '''Get the I{geometric} mean earth radius (C{meter}), M{sqrt(a * b)}. 

1499 

1500 @see: C{R1}. 

1501 ''' 

1502 g = sqrt(self.a * self.b) if self.f else self.a 

1503 return Radius(Rgeometric=g) 

1504 

1505 def rhumb_(self, exact=True): 

1506 '''Get the an I{exact} C{Rhumb...} instance for this ellipsoid. 

1507 

1508 @kwarg exact: If C{bool} or C{None} return L{Rhumb}C{(exact=B{exact}, ...)}, 

1509 otherwise a L{Rhumb}, L{RhumbAux} or L{RhumbSolve} instance 

1510 for I{this} ellipsoid. 

1511 

1512 @return: The C{exact} rhumb (C{Rhumb...}). 

1513 

1514 @raise TypeError: Invalid B{C{exact}}. 

1515 

1516 @raise ValueError: Incompatible B{C{exact}} ellipsoid. 

1517 ''' 

1518 if isbool(exact): # use Rhumb for backward compatibility 

1519 r = _MODS.rhumb.ekx.Rhumb(self, exact=exact, name=self.name) 

1520 else: 

1521 r = exact 

1522 E = _xattr(r, ellipsoid=None) 

1523 if not (E is self and isinstance(r, self._Rhumbs)): 

1524 raise _ValueError(exact=r, ellipsosid=E, txt_not_=self.name) 

1525 return r 

1526 

1527 @property_RO 

1528 def rhumbaux(self): 

1529 '''Get this ellipsoid's I{Auxiliary} C{rhumb.RhumbAux}. 

1530 ''' 

1531 # if not self.isEllipsoidal: 

1532 # raise _IsnotError(_ellipsoidal_, ellipsoid=self) 

1533 return _MODS.rhumb.aux_.RhumbAux(self, name=self.name) 

1534 

1535 @property_RO 

1536 def rhumbekx(self): 

1537 '''Get this ellipsoid's I{Elliptic, Krüger} C{rhumb.Rhumb}. 

1538 ''' 

1539 # if not self.isEllipsoidal: 

1540 # raise _IsnotError(_ellipsoidal_, ellipsoid=self) 

1541 return _MODS.rhumb.ekx.Rhumb(self, name=self.name) 

1542 

1543 @property_ROver 

1544 def _Rhumbs(self): 

1545 '''(INTERNAL) Get all C{Rhumb...} classes, I{once}. 

1546 ''' 

1547 r = _MODS.rhumb 

1548 return (r.aux_.RhumbAux, # overwrite property_ROver 

1549 r.ekx.Rhumb, r.solve.RhumbSolve) 

1550 

1551 @property 

1552 def rhumbsolve(self): 

1553 '''Get this ellipsoid's L{RhumbSolve}, the I{wrapper} around utility 

1554 U{RhumbSolve<https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>}, 

1555 provided the path to the C{RhumbSolve} executable is specified with env 

1556 variable C{PYGEODESY_RHUMBSOLVE} or re-/set with this property. 

1557 ''' 

1558 # if not self.isEllipsoidal: 

1559 # raise _IsnotError(_ellipsoidal_, ellipsoid=self) 

1560 return _MODS.rhumb.solve.RhumbSolve(self, path=self._rhumbsolve, name=self.name) 

1561 

1562 @rhumbsolve.setter # PYCHOK setter! 

1563 def rhumbsolve(self, path): 

1564 '''Re-/set the (fully qualified) path to the U{RhumbSolve 

1565 <https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>} executable, 

1566 overriding env variable C{PYGEODESY_RHUMBSOLVE} (C{str}). 

1567 ''' 

1568 self._rhumbsolve = path 

1569 

1570 @deprecated_property_RO 

1571 def rhumbx(self): 

1572 '''DEPRECATED on 2023.11.28, use property C{rhumbekx}.''' 

1573 return self.rhumbekx 

1574 

1575 def Rlat(self, lat): 

1576 '''I{Average} the earth radius between C{equatoradius} at C{0} and 

1577 C{polaradius} at C{+/-90} degrees latitude. 

1578 

1579 @arg lat: Latitude (C{degrees90}, C{str} or C{Ang}). 

1580 

1581 @return: Averaged earth radius (C{meter}). 

1582 

1583 @raise RangeError: Latitude B{C{lat}} outside valid range, only if 

1584 L{rangerrors<pygeodesy.rangerrors>} is C{True}. 

1585 

1586 @raise TypeError: Invalid B{C{lat}}. 

1587 

1588 @raise ValueError: Invalid B{C{lat}}. 

1589 ''' 

1590 # r = a - (a - b) * |lat| / 90 

1591 r = self.a 

1592 if self.f: # .isEllipsoidal 

1593 lat = _Lat0(lat) 

1594 if lat: 

1595 r -= (r - self.b) * fabs(lat) / _90_0 

1596 r = Radius(Rlat=r) 

1597 return r 

1598 

1599 Rpolar = b # for consistent naming 

1600 

1601 def roc1_(self, sa, ca=None): 

1602 '''Compute the I{prime-vertical}, I{normal} radius of curvature 

1603 of (geodetic) latitude, I{unscaled}. 

1604 

1605 @arg sa: Sine of the latitude (C{float}, [-1.0..+1.0]). 

1606 @kwarg ca: Optional cosine of the latitude (C{float}, [-1.0..+1.0]) 

1607 to use an alternate formula. 

1608 

1609 @return: The prime-vertical radius of curvature (C{float}). 

1610 

1611 @note: The delta between both formulae with C{Ellipsoids.WGS84} 

1612 is less than 2 nanometer over the entire latitude range. 

1613 

1614 @see: Method L{roc2_} and class L{EcefYou}. 

1615 ''' 

1616 if sa and self.f: # .isEllipsoidal 

1617 if ca is None: 

1618 r = self.e2s2(sa) # see .roc2_ and _EcefBase._forward 

1619 n = sqrt(self.a2 / r) if r > EPS02 else _0_0 

1620 elif ca: # derived from EcefYou.forward 

1621 h = hypot(ca, self.b_a * sa) 

1622 n = self.a / h 

1623 else: 

1624 n = self.a2_b / fabs(sa) 

1625 else: 

1626 n = self.a 

1627 return n 

1628 

1629 def roc2(self, lat, scaled=False): 

1630 '''Compute the I{meridional} and I{prime-vertical}, I{normal} 

1631 radii of curvature of (geodetic) latitude. 

1632 

1633 @arg lat: Latitude (C{degrees90}). 

1634 @kwarg scaled: Scale prime_vertical by C{cos(radians(B{lat}))} (C{bool}). 

1635 

1636 @return: An L{Curvature2Tuple}C{(meridional, prime_vertical)} with 

1637 the radii of curvature. 

1638 

1639 @raise ValueError: Invalid B{C{lat}}. 

1640 

1641 @see: Methods L{roc2_} and L{roc1_}, U{Local, flat earth approximation 

1642 <https://www.EdWilliams.org/avform.htm#flat>} and meridional and 

1643 prime vertical U{Radii of Curvature<https://WikiPedia.org/wiki/ 

1644 Earth_radius#Radii_of_curvature>}. 

1645 ''' 

1646 return self.roc2_(Phid(lat), scaled=scaled) 

1647 

1648 def roc2_(self, phi, scaled=False): 

1649 '''Compute the I{meridional} and I{prime-vertical}, I{normal} radii of 

1650 curvature of (geodetic) latitude. 

1651 

1652 @arg phi: Latitude (C{radians}). 

1653 @kwarg scaled: Scale prime_vertical by C{cos(B{phi})} (C{bool}). 

1654 

1655 @return: An L{Curvature2Tuple}C{(meridional, prime_vertical)} with the 

1656 radii of curvature. 

1657 

1658 @raise ValueError: Invalid B{C{phi}}. 

1659 

1660 @see: Methods L{roc2} and L{roc1_}, property L{rocEquatorial2}, U{Local, 

1661 flat earth approximation<https://www.EdWilliams.org/avform.htm#flat>} 

1662 and the meridional and prime vertical U{Radii of Curvature 

1663 <https://WikiPedia.org/wiki/Earth_radius#Radii_of_curvature>}. 

1664 ''' 

1665 p = fabs(Phi(phi)) 

1666 if self.f: 

1667 r = self.e2s2(sin(p)) 

1668 if r > EPS02: 

1669 n = sqrt(self.a2 / r) 

1670 m = n * self.e21 / r 

1671 else: 

1672 m = n = _0_0 

1673 else: 

1674 m = n = self.a 

1675 if scaled and p: 

1676 n *= cos(p) if p < PI_2 else _0_0 

1677 return Curvature2Tuple(m, n) 

1678 

1679 def rocAzimuth(self, lat, azimuth): 

1680 '''Compute the I{directional} radius of curvature of (geodetic) latitude 

1681 and C{azimuth} compass direction. 

1682 

1683 @see: Method L{rocBearing<Ellipsoid.rocBearing>} for details, using C{azimuth} for C{bearing}. 

1684 ''' 

1685 return Radius(rocAzimuth=self._rocDirectional(lat, Azimuth(azimuth))) 

1686 

1687 def rocBearing(self, lat, bearing): 

1688 '''Compute the I{directional} radius of curvature of (geodetic) latitude 

1689 and C{bearing} compass direction. 

1690 

1691 @arg lat: Latitude (C{degrees90}). 

1692 @arg bearing: Direction (compass C{degrees360}). 

1693 

1694 @return: Directional radius of curvature (C{meter}). 

1695 

1696 @raise RangeError: Latitude B{C{lat}} outside valid range and 

1697 L{rangerrors<pygeodesy.rangerrors>} is C{True}. 

1698 

1699 @raise ValueError: Invalid B{C{lat}} or B{C{bearing}}. 

1700 

1701 @see: U{Radii of Curvature<https://WikiPedia.org/wiki/Earth_radius#Radii_of_curvature>} 

1702 ''' 

1703 return Radius(rocBearing=self._rocDirectional(lat, Bearing(bearing))) 

1704 

1705 def _rocDirectional(self, lat, deg): 

1706 '''(INTERNAL) Helper for C{rocAzimuth} and C{rocBearing}. 

1707 ''' 

1708 if self.f: 

1709 s2, c2 = _sin2cos2(radians(deg)) 

1710 m, n = self.roc2_(Phid(lat)) 

1711 if n < m: # == n / (c2 * n / m + s2) 

1712 c2 *= n / m 

1713 elif m < n: # == m / (c2 + s2 * m / n) 

1714 s2 *= m / n 

1715 n = m 

1716 r = _over(n, c2 + s2) # == 1 / (c2 / m + s2 / n) 

1717 else: 

1718 r = self.b # == self.a 

1719 return r 

1720 

1721 @Property_RO 

1722 def rocEquatorial2(self): 

1723 '''Get the I{meridional} and I{prime-vertical}, I{normal} radii of curvature 

1724 at the equator as L{Curvature2Tuple}C{(meridional, prime_vertical)}. 

1725 

1726 @see: Methods L{rocMeridional} and L{rocPrimeVertical}, properties L{b2_a}, 

1727 L{a2_b}, C{rocPolar} and polar and equatorial U{Radii of Curvature 

1728 <https://WikiPedia.org/wiki/Earth_radius#Radii_of_curvature>}. 

1729 ''' 

1730 m = self.b2_a if self.f else self.a 

1731 return Curvature2Tuple(m, self.a) 

1732 

1733 def rocGauss(self, lat): 

1734 '''Compute the I{Gaussian} radius of curvature of (geodetic) latitude. 

1735 

1736 @arg lat: Latitude (C{degrees90}). 

1737 

1738 @return: Gaussian radius of curvature (C{meter}). 

1739 

1740 @raise ValueError: Invalid B{C{lat}}. 

1741 

1742 @see: Non-directional U{Radii of Curvature<https://WikiPedia.org/wiki/ 

1743 Earth_radius#Radii_of_curvature>} 

1744 ''' 

1745 # using ... 

1746 # m, n = self.roc2_(Phid(lat)) 

1747 # return sqrt(m * n) 

1748 # ... requires 1 or 2 sqrt 

1749 g = self.b 

1750 if self.f: 

1751 s2, c2 = _sin2cos2(Phid(lat)) 

1752 g = _over(g, c2 + self.b2_a2 * s2) 

1753 return Radius(rocGauss=g) 

1754 

1755 def rocMean(self, lat): 

1756 '''Compute the I{mean} radius of curvature of (geodetic) latitude. 

1757 

1758 @arg lat: Latitude (C{degrees90}). 

1759 

1760 @return: Mean radius of curvature (C{meter}). 

1761 

1762 @raise ValueError: Invalid B{C{lat}}. 

1763 

1764 @see: Non-directional U{Radii of Curvature<https://WikiPedia.org/wiki/ 

1765 Earth_radius#Radii_of_curvature>} 

1766 ''' 

1767 if self.f: 

1768 m, n = self.roc2_(Phid(lat)) 

1769 m *= _over(n * _2_0, m + n) # == 2 / (1 / m + 1 / n) 

1770 else: 

1771 m = self.a 

1772 return Radius(rocMean=m) 

1773 

1774 def rocMeridional(self, lat): 

1775 '''Compute the I{meridional} radius of curvature of (geodetic) latitude. 

1776 

1777 @arg lat: Latitude (C{degrees90}). 

1778 

1779 @return: Meridional radius of curvature (C{meter}). 

1780 

1781 @raise ValueError: Invalid B{C{lat}}. 

1782 

1783 @see: Methods L{roc2} and L{roc2_}, U{Local, flat earth approximation 

1784 <https://www.EdWilliams.org/avform.htm#flat>} and U{Radii of 

1785 Curvature<https://WikiPedia.org/wiki/Earth_radius#Radii_of_curvature>}. 

1786 ''' 

1787 r = self.roc2_(Phid(lat)) if lat else self.rocEquatorial2 

1788 return Radius(rocMeridional=r.meridional) 

1789 

1790 rocPolar = a2_b # synonymous 

1791 

1792 def rocPrimeVertical(self, lat): 

1793 '''Compute the I{prime-vertical}, I{normal} radius of curvature of 

1794 (geodetic) latitude, aka the I{transverse} radius of curvature. 

1795 

1796 @arg lat: Latitude (C{degrees90}). 

1797 

1798 @return: Prime-vertical radius of curvature (C{meter}). 

1799 

1800 @raise ValueError: Invalid B{C{lat}}. 

1801 

1802 @see: Methods L{roc2}, L{roc2_} and L{roc1_}, U{Local, flat earth 

1803 approximation<https://www.EdWilliams.org/avform.htm#flat>} and 

1804 U{Radii of Curvature<https://WikiPedia.org/wiki/ 

1805 Earth_radius#Radii_of_curvature>}. 

1806 ''' 

1807 r = self.roc1_(sin(Phid(lat))) if lat else self.a 

1808 return Radius(rocPrimeVertical=r) 

1809 

1810 rocTransverse = rocPrimeVertical # synonymous 

1811 

1812 @deprecated_Property_RO 

1813 def Rquadratic(self): # PYCHOK no cover 

1814 '''DEPRECATED, use property C{Rbiaxial} or C{Rtriaxial}.''' 

1815 return self.Rbiaxial 

1816 

1817 @deprecated_Property_RO 

1818 def Rr(self): # PYCHOK no cover 

1819 '''DEPRECATED, use property C{Rrectifying}.''' 

1820 return self.Rrectifying 

1821 

1822 @Property_RO 

1823 def Rrectifying(self): 

1824 '''Get the I{rectifying} earth radius (C{meter}), M{((a**(3/2) + b**(3/2)) / 2)**(2/3)}. 

1825 

1826 @see: U{Earth radius<https://WikiPedia.org/wiki/Earth_radius>}. 

1827 ''' 

1828 r = self.a 

1829 if self.f: 

1830 r *= cbrt2((sqrt3(self.b_a) + _1_0) * _0_5) 

1831 return Radius(Rrectifying=r) 

1832 

1833 @deprecated_Property_RO 

1834 def Rs(self): # PYCHOK no cover 

1835 '''DEPRECATED, use property C{Rgeometric}.''' 

1836 return self.Rgeometric 

1837 

1838 @Property_RO 

1839 def Rtriaxial(self): 

1840 '''Get the I{triaxial, quadratic} mean earth radius (C{meter}), M{sqrt((3 * a**2 + b**2) / 4)}. 

1841 

1842 @see: C{Rbiaxial} 

1843 ''' 

1844 q, b = self.a, self.b 

1845 if b < q: 

1846 q *= sqrt((self.b2_a2 + _3_0) * _0_25) 

1847 elif b > q: 

1848 q = sqrt((self.a2_b2 * _3_0 + _1_0) * _0_25) * b 

1849 return Radius(Rtriaxial=q) 

1850 

1851 def toEllipse(self, **name): 

1852 '''Get this ellipsoid's meridional ellipse L{Ellipse<pygeodesy.Ellipse>}. 

1853 

1854 @kwarg name: Optional, unique C{B{name}=NN} (C{str}). 

1855 ''' 

1856 return _MODS.ellipses.Ellipse(self.a, self.b, **name) 

1857 

1858 def toEllipsoid2(self, **name): 

1859 '''Get a copy of this ellipsoid as an L{Ellipsoid2}. 

1860 

1861 @kwarg name: Optional, unique C{B{name}=NN} (C{str}). 

1862 

1863 @see: Property C{a_f}. 

1864 ''' 

1865 return Ellipsoid2(self, None, **name) 

1866 

1867 def toStr(self, prec=8, terse=5, **sep_name): # PYCHOK expected 

1868 '''Return this ellipsoid as a text string. 

1869 

1870 @kwarg prec: Number of decimal digits, unstripped (C{int}). 

1871 @kwarg terse: Limit the number of items (C{int}, 0...18), 

1872 use C{B{terse}=0} or C{=None} for all. 

1873 @kwarg sep_name: Optional C{B{name}=NN} (C{str}) or C{None} 

1874 to exclude this ellipsoid's name and separator 

1875 C{B{sep}=", "} to join the items (C{str}). 

1876 

1877 @return: This C{Ellipsoid}'s attributes (C{str}). 

1878 ''' 

1879 E = Ellipsoid 

1880 t = (E.a, E.f, E.e, E.b, E.f_, E.e2, 

1881 E.e21, E.e22, E.e32, E.n, 

1882 E.A, E.L, E.R1, E.R2, E.R3, 

1883 E.Rbiaxial, E.Rtriaxial) 

1884 if terse: 

1885 t = t[:terse] 

1886 return self._instr(prec=prec, props=t, **sep_name) 

1887 

1888 def toTriaxial(self, **name): 

1889 '''Convert this ellipsoid to a L{Triaxial_}. 

1890 

1891 @kwarg name: Optional C{B{name}=NN} (C{str}). 

1892 

1893 @return: A L{Triaxial_} or L{Triaxial} with the C{X} axis 

1894 pointing east and C{Z} pointing north. 

1895 

1896 @see: Method L{Triaxial_.toEllipsoid}. 

1897 ''' 

1898 T = self._triaxial 

1899 return T.copy(**name) if name else T 

1900 

1901 @property_RO 

1902 def _triaxial(self): 

1903 '''(INTERNAL) Get this ellipsoid's un-/ordered C{Triaxial/_}. 

1904 ''' 

1905 a, b, t = self.a, self.b, _MODS.triaxials 

1906 T = t.Triaxial if a > b else t.Triaxial_ 

1907 return T(a, a, b, name=self.name) 

1908 

1909 @Property_RO 

1910 def volume(self): 

1911 '''Get the ellipsoid's I{volume} (C{meter**3}), M{4 / 3 * PI * R3**3}. 

1912 

1913 @see: C{R3}. 

1914 ''' 

1915 return Meter3(volume=self.a2 * self.b * PI_3 * _4_0) 

1916 

1917 

1918class Ellipsoid2(Ellipsoid): 

1919 '''An L{Ellipsoid} specified by I{equatorial} radius and I{flattening}. 

1920 ''' 

1921 def __init__(self, a, f=None, **name): 

1922 '''New L{Ellipsoid2}. 

1923 

1924 @arg a: Equatorial radius, semi-axis (C{meter}) or a previous 

1925 L{Ellipsoid} instance. 

1926 @arg f: Flattening: (C{float} < 1.0, negative for I{prolate}), 

1927 if B{C{a}} is in C{meter}. 

1928 @kwarg name: Optional, unique C{B{name}=NN} (C{str}). 

1929 

1930 @raise NameError: Ellipsoid with that B{C{name}} already exists. 

1931 

1932 @raise ValueError: Invalid B{C{a}} or B{C{f}}. 

1933 

1934 @note: C{abs(B{f}) < EPS} is forced to C{B{f}=0}, I{spherical}. 

1935 Negative C{B{f}} produces a I{prolate} ellipsoid. 

1936 ''' 

1937 if f is None and isinstance(a, Ellipsoid): 

1938 Ellipsoid.__init__(self, a.a, f =a.f, 

1939 b=a.b, f_=a.f_, **name) 

1940 else: 

1941 Ellipsoid.__init__(self, a, f=f, **name) 

1942 

1943 

1944def _ispherical_a_b(a, b): 

1945 '''(INTERNAL) C{True} for spherical or invalid C{a} or C{b}. 

1946 ''' 

1947 return a < EPS0 or b < EPS0 or fabs(a - b) < EPS0 

1948 

1949 

1950def _ispherical_f(f): 

1951 '''(INTERNAL) C{True} for spherical or invalid C{f}. 

1952 ''' 

1953 return f > EPS1 or fabs(f) < EPS 

1954 

1955 

1956def _ispherical_f_(f_): 

1957 '''(INTERNAL) C{True} for spherical or invalid C{f_}. 

1958 ''' 

1959 f_ = fabs(f_) 

1960 return f_ < EPS or f_ > _1_EPS 

1961 

1962 

1963def a_b2e(a, b): 

1964 '''Return C{e}, the I{1st eccentricity} for a given I{equatorial} and I{polar} radius. 

1965 

1966 @arg a: Equatorial radius (C{scalar} > 0). 

1967 @arg b: Polar radius (C{scalar} > 0). 

1968 

1969 @return: The I{unsigned}, (1st) eccentricity (C{float} or C{0}), M{sqrt(1 - (b / a)**2)}. 

1970 

1971 @note: The result is always I{non-negative} and C{0} for I{near-spherical} ellipsoids. 

1972 ''' 

1973 e2 = _a2b2e2(a, b, b2=False) 

1974 return Float(e=sqrt(fabs(e2)) if e2 else _0_0) # == sqrt(fabs((a - b) * (a + b))) / a 

1975 

1976 

1977def a_b2e2(a, b): 

1978 '''Return C{e2}, the I{1st eccentricity squared} for a given I{equatorial} and I{polar} radius. 

1979 

1980 @arg a: Equatorial radius (C{scalar} > 0). 

1981 @arg b: Polar radius (C{scalar} > 0). 

1982 

1983 @return: The I{signed}, (1st) eccentricity I{squared} (C{float} or C{0}), M{1 - (b / a)**2}. 

1984 

1985 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0} 

1986 for I{near-spherical} ellipsoids. 

1987 ''' 

1988 return Float(e2=_a2b2e2(a, b, b2=False)) 

1989 

1990 

1991def a_b2e22(a, b): 

1992 '''Return C{e22}, the I{2nd eccentricity squared} for a given I{equatorial} and I{polar} radius. 

1993 

1994 @arg a: Equatorial radius (C{scalar} > 0). 

1995 @arg b: Polar radius (C{scalar} > 0). 

1996 

1997 @return: The I{signed}, 2nd eccentricity I{squared} (C{float} or C{0}), M{(a / b)**2 - 1}. 

1998 

1999 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0} 

2000 for I{near-spherical} ellipsoids. 

2001 ''' 

2002 return Float(e22=_a2b2e2(a, b, a2=False)) 

2003 

2004 

2005def a_b2e32(a, b): 

2006 '''Return C{e32}, the I{3rd eccentricity squared} for a given I{equatorial} and I{polar} radius. 

2007 

2008 @arg a: Equatorial radius (C{scalar} > 0). 

2009 @arg b: Polar radius (C{scalar} > 0). 

2010 

2011 @return: The I{signed}, 3rd eccentricity I{squared} (C{float} or C{0}), 

2012 M{(a**2 - b**2) / (a**2 + b**2)}. 

2013 

2014 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0} 

2015 for I{near-spherical} ellipsoids. 

2016 ''' 

2017 return Float(e32=_a2b2e2(a, b)) 

2018 

2019 

2020def _a2b2e2(a, b, a2=True, b2=True): 

2021 '''(INTERNAL) Helper for C{a_b2e}, C{a_b2e2}, C{a_b2e22} and C{a_b2e32}. 

2022 ''' 

2023 if _ispherical_a_b(a, b): 

2024 e2 = _0_0 

2025 else: # a > 0, b > 0 

2026 a, b = (_1_0, b / a) if a > b else (a / b, _1_0) 

2027 a2b2 = float(a - b) * (a + b) 

2028 e2 = _over(a2b2, (a**2 if a2 else _0_0) + 

2029 (b**2 if b2 else _0_0)) if a2b2 else _0_0 

2030 return e2 

2031 

2032 

2033def a_b2f(a, b): 

2034 '''Return C{f}, the I{flattening} for a given I{equatorial} and I{polar} radius. 

2035 

2036 @arg a: Equatorial radius (C{scalar} > 0). 

2037 @arg b: Polar radius (C{scalar} > 0). 

2038 

2039 @return: The flattening (C{scalar} or C{0}), M{(a - b) / a}. 

2040 

2041 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0} 

2042 for I{near-spherical} ellipsoids. 

2043 ''' 

2044 f = 0 if _ispherical_a_b(a, b) else _over(float(a - b), a) 

2045 return _f_0_0 if _ispherical_f(f) else Float(f=f) 

2046 

2047 

2048def a_b2f_(a, b): 

2049 '''Return C{f_}, the I{inverse flattening} for a given I{equatorial} and I{polar} radius. 

2050 

2051 @arg a: Equatorial radius (C{scalar} > 0). 

2052 @arg b: Polar radius (C{scalar} > 0). 

2053 

2054 @return: The inverse flattening (C{scalar} or C{0}), M{a / (a - b)}. 

2055 

2056 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0} 

2057 for I{near-spherical} ellipsoids. 

2058 ''' 

2059 f_ = 0 if _ispherical_a_b(a, b) else _over(a, float(a - b)) 

2060 return _f__0_0 if _ispherical_f_(f_) else Float(f_=f_) 

2061 

2062 

2063def a_b2f2(a, b): 

2064 '''Return C{f2}, the I{2nd flattening} for a given I{equatorial} and I{polar} radius. 

2065 

2066 @arg a: Equatorial radius (C{scalar} > 0). 

2067 @arg b: Polar radius (C{scalar} > 0). 

2068 

2069 @return: The I{signed}, 2nd flattening (C{scalar} or C{0}), M{(a - b) / b}. 

2070 

2071 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0} 

2072 for I{near-spherical} ellipsoids. 

2073 ''' 

2074 t = 0 if _ispherical_a_b(a, b) else float(a - b) 

2075 return Float(f2=_0_0 if fabs(t) < EPS0 else _over(t, b)) 

2076 

2077 

2078def a_b2n(a, b): 

2079 '''Return C{n}, the I{3rd flattening} for a given I{equatorial} and I{polar} radius. 

2080 

2081 @arg a: Equatorial radius (C{scalar} > 0). 

2082 @arg b: Polar radius (C{scalar} > 0). 

2083 

2084 @return: The I{signed}, 3rd flattening (C{scalar} or C{0}), M{(a - b) / (a + b)}. 

2085 

2086 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0} 

2087 for I{near-spherical} ellipsoids. 

2088 ''' 

2089 t = 0 if _ispherical_a_b(a, b) else float(a - b) 

2090 return Float(n=_0_0 if fabs(t) < EPS0 else _over(t, a + b)) 

2091 

2092 

2093def a_f2b(a, f): 

2094 '''Return C{b}, the I{polar} radius for a given I{equatorial} radius and I{flattening}. 

2095 

2096 @arg a: Equatorial radius (C{scalar} > 0). 

2097 @arg f: Flattening (C{scalar} < 1, negative for I{prolate}). 

2098 

2099 @return: The polar radius (C{float}), M{a * (1 - f)}. 

2100 ''' 

2101 b = a if _ispherical_f(f) else (a * (_1_0 - f)) 

2102 return Radius_(b=a if _ispherical_a_b(a, b) else b) 

2103 

2104 

2105def a_f_2b(a, f_): 

2106 '''Return C{b}, the I{polar} radius for a given I{equatorial} radius and I{inverse flattening}. 

2107 

2108 @arg a: Equatorial radius (C{scalar} > 0). 

2109 @arg f_: Inverse flattening (C{scalar} >>> 1). 

2110 

2111 @return: The polar radius (C{float}), M{a * (f_ - 1) / f_}. 

2112 ''' 

2113 b = a if _ispherical_f_(f_) else _over(a * (f_ - _1_0), f_) 

2114 return Radius_(b=a if _ispherical_a_b(a, b) else b) 

2115 

2116 

2117def b_f2a(b, f): 

2118 '''Return C{a}, the I{equatorial} radius for a given I{polar} radius and I{flattening}. 

2119 

2120 @arg b: Polar radius (C{scalar} > 0). 

2121 @arg f: Flattening (C{scalar} < 1, negative for I{prolate}). 

2122 

2123 @return: The equatorial radius (C{float}), M{b / (1 - f)}. 

2124 ''' 

2125 t = _1_0 - f 

2126 a = b if fabs(t) < EPS0 else _over(b, t) 

2127 return Radius_(a=b if _ispherical_a_b(a, b) else a) 

2128 

2129 

2130def b_f_2a(b, f_): 

2131 '''Return C{a}, the I{equatorial} radius for a given I{polar} radius and I{inverse flattening}. 

2132 

2133 @arg b: Polar radius (C{scalar} > 0). 

2134 @arg f_: Inverse flattening (C{scalar} >>> 1). 

2135 

2136 @return: The equatorial radius (C{float}), M{b * f_ / (f_ - 1)}. 

2137 ''' 

2138 t = f_ - _1_0 

2139 a = b if _ispherical_f_(f_) or fabs(t) < EPS0 \ 

2140 or fabs(t - f_) < EPS0 else _over(b * f_, t) 

2141 return Radius_(a=b if _ispherical_a_b(a, b) else a) 

2142 

2143 

2144def e2f(e): 

2145 '''Return C{f}, the I{flattening} for a given I{1st eccentricity}. 

2146 

2147 @arg e: The (1st) eccentricity (0 <= C{float} < 1) 

2148 

2149 @return: The flattening (C{scalar} or C{0}). 

2150 

2151 @see: Function L{e22f}. 

2152 ''' 

2153 return e22f(e**2) 

2154 

2155 

2156def e22f(e2): 

2157 '''Return C{f}, the I{flattening} for a given I{1st eccentricity squared}. 

2158 

2159 @arg e2: The (1st) eccentricity I{squared}, I{signed} (L{NINF} < C{float} < 1) 

2160 

2161 @return: The flattening (C{float} or C{0}), M{e2 / (sqrt(1 - e2) + 1)}. 

2162 ''' 

2163 return Float(f=_over(e2, sqrt(_1_0 - e2) + _1_0)) if e2 and e2 < _1_0 else _f_0_0 

2164 

2165 

2166def f2e2(f): 

2167 '''Return C{e2}, the I{1st eccentricity squared} for a given I{flattening}. 

2168 

2169 @arg f: Flattening (C{scalar} < 1, negative for I{prolate}). 

2170 

2171 @return: The I{signed}, (1st) eccentricity I{squared} (C{float} < 1), M{f * (2 - f)}. 

2172 

2173 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0} 

2174 for I{near-spherical} ellipsoids. 

2175 

2176 @see: U{Eccentricity conversions<https://GeographicLib.SourceForge.io/ 

2177 C++/doc/classGeographicLib_1_1Ellipsoid.html>} and U{Flattening 

2178 <https://WikiPedia.org/wiki/Flattening>}. 

2179 ''' 

2180 return Float(e2=_0_0 if _ispherical_f(f) else (f * (_2_0 - f))) 

2181 

2182 

2183def f2e22(f): 

2184 '''Return C{e22}, the I{2nd eccentricity squared} for a given I{flattening}. 

2185 

2186 @arg f: Flattening (C{scalar} < 1, negative for I{prolate}). 

2187 

2188 @return: The I{signed}, 2nd eccentricity I{squared} (C{float} > -1 or C{INF}), 

2189 M{f * (2 - f) / (1 - f)**2}. 

2190 

2191 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0} 

2192 for near-spherical ellipsoids. 

2193 

2194 @see: U{Eccentricity conversions<https://GeographicLib.SourceForge.io/ 

2195 C++/doc/classGeographicLib_1_1Ellipsoid.html>}. 

2196 ''' 

2197 # e2 / (1 - e2) == f * (2 - f) / (1 - f)**2 

2198 t = (_1_0 - f)**2 

2199 return Float(e22=INF if t < EPS0 else _over(f2e2(f), t)) # PYCHOK type 

2200 

2201 

2202def f2e32(f): 

2203 '''Return C{e32}, the I{3rd eccentricity squared} for a given I{flattening}. 

2204 

2205 @arg f: Flattening (C{scalar} < 1, negative for I{prolate}). 

2206 

2207 @return: The I{signed}, 3rd eccentricity I{squared} (C{float}), 

2208 M{f * (2 - f) / (1 + (1 - f)**2)}. 

2209 

2210 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0} 

2211 for I{near-spherical} ellipsoids. 

2212 

2213 @see: U{Eccentricity conversions<https://GeographicLib.SourceForge.io/ 

2214 C++/doc/classGeographicLib_1_1Ellipsoid.html>}. 

2215 ''' 

2216 # e2 / (2 - e2) == f * (2 - f) / (1 + (1 - f)**2) 

2217 e2 = f2e2(f) 

2218 return Float(e32=_over(e2, _2_0 - e2)) 

2219 

2220 

2221def f_2f(f_): 

2222 '''Return C{f}, the I{flattening} for a given I{inverse flattening}. 

2223 

2224 @arg f_: Inverse flattening (C{scalar} >>> 1). 

2225 

2226 @return: The flattening (C{scalar} or C{0}), M{1 / f_}. 

2227 

2228 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0} 

2229 for I{near-spherical} ellipsoids. 

2230 ''' 

2231 f = 0 if _ispherical_f_(f_) else _over(_1_0, f_) 

2232 return _f_0_0 if _ispherical_f(f) else Float(f=f) # PYCHOK type 

2233 

2234 

2235def f2f_(f): 

2236 '''Return C{f_}, the I{inverse flattening} for a given I{flattening}. 

2237 

2238 @arg f: Flattening (C{scalar} < 1, negative for I{prolate}). 

2239 

2240 @return: The inverse flattening (C{scalar} or C{0}), M{1 / f}. 

2241 

2242 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0} 

2243 for I{near-spherical} ellipsoids. 

2244 ''' 

2245 f_ = 0 if _ispherical_f(f) else _over(_1_0, f) 

2246 return _f__0_0 if _ispherical_f_(f_) else Float(f_=f_) # PYCHOK type 

2247 

2248 

2249def f2f2(f): 

2250 '''Return C{f2}, the I{2nd flattening} for a given I{flattening}. 

2251 

2252 @arg f: Flattening (C{scalar} < 1, negative for I{prolate}). 

2253 

2254 @return: The I{signed}, 2nd flattening (C{scalar} or C{INF}), M{f / (1 - f)}. 

2255 

2256 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0} 

2257 for I{near-spherical} ellipsoids. 

2258 

2259 @see: U{Eccentricity conversions<https://GeographicLib.SourceForge.io/ 

2260 C++/doc/classGeographicLib_1_1Ellipsoid.html>} and U{Flattening 

2261 <https://WikiPedia.org/wiki/Flattening>}. 

2262 ''' 

2263 t = _1_0 - f 

2264 return Float(f2=_0_0 if _ispherical_f(f) else 

2265 (INF if fabs(t) < EPS else _over(f, t))) # PYCHOK type 

2266 

2267 

2268def f2n(f): 

2269 '''Return C{n}, the I{3rd flattening} for a given I{flattening}. 

2270 

2271 @arg f: Flattening (C{scalar} < 1, negative for I{prolate}). 

2272 

2273 @return: The I{signed}, 3rd flattening (-1 <= C{float} < 1), 

2274 M{f / (2 - f)}. 

2275 

2276 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0} 

2277 for I{near-spherical} ellipsoids. 

2278 

2279 @see: U{Eccentricity conversions<https://GeographicLib.SourceForge.io/ 

2280 C++/doc/classGeographicLib_1_1Ellipsoid.html>} and U{Flattening 

2281 <https://WikiPedia.org/wiki/Flattening>}. 

2282 ''' 

2283 return Float(n=_0_0 if _ispherical_f(f) else _over(f, float(_2_0 - f))) 

2284 

2285 

2286def n2e2(n): 

2287 '''Return C{e2}, the I{1st eccentricity squared} for a given I{3rd flattening}. 

2288 

2289 @arg n: The 3rd flattening (-1 <= C{scalar} < 1). 

2290 

2291 @return: The I{signed}, (1st) eccentricity I{squared} (C{float} or NINF), 

2292 M{4 * n / (1 + n)**2}. 

2293 

2294 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0} 

2295 for I{near-spherical} ellipsoids. 

2296 

2297 @see: U{Flattening<https://WikiPedia.org/wiki/Flattening>}. 

2298 ''' 

2299 t = (n + _1_0)**2 

2300 return Float(e2=_0_0 if fabs(n) < EPS0 else 

2301 (NINF if t < EPS0 else _over(_4_0 * n, t))) 

2302 

2303 

2304def n2f(n): 

2305 '''Return C{f}, the I{flattening} for a given I{3rd flattening}. 

2306 

2307 @arg n: The 3rd flattening (-1 <= C{scalar} < 1). 

2308 

2309 @return: The flattening (C{scalar} or NINF), M{2 * n / (1 + n)}. 

2310 

2311 @see: U{Eccentricity conversions<https://GeographicLib.SourceForge.io/ 

2312 C++/doc/classGeographicLib_1_1Ellipsoid.html>} and U{Flattening 

2313 <https://WikiPedia.org/wiki/Flattening>}. 

2314 ''' 

2315 t = n + _1_0 

2316 f = 0 if fabs(n) < EPS0 else (NINF if t < EPS0 else _over(_2_0 * n, t)) 

2317 return _f_0_0 if _ispherical_f(f) else Float(f=f) 

2318 

2319 

2320def n2f_(n): 

2321 '''Return C{f_}, the I{inverse flattening} for a given I{3rd flattening}. 

2322 

2323 @arg n: The 3rd flattening (-1 <= C{scalar} < 1). 

2324 

2325 @return: The inverse flattening (C{scalar} or C{0}), M{1 / f}. 

2326 

2327 @see: L{n2f} and L{f2f_}. 

2328 ''' 

2329 return f2f_(n2f(n)) 

2330 

2331 

2332class Ellipsoids(_NamedEnum): 

2333 '''(INTERNAL) L{Ellipsoid} registry, I{must} be a sub-class 

2334 to accommodate the L{_LazyNamedEnumItem} properties. 

2335 ''' 

2336 def _Lazy(self, a, b, f_, **kwds): 

2337 '''(INTERNAL) Instantiate the L{Ellipsoid}. 

2338 ''' 

2339 return Ellipsoid(a, b=b, f_=f_, **kwds) 

2340 

2341Ellipsoids = Ellipsoids(Ellipsoid) # PYCHOK singleton 

2342'''Some pre-defined L{Ellipsoid}s, all I{lazily} instantiated.''' 

2343# <https://www.GNU.org/software/gama/manual/html_node/Supported-ellipsoids.html> 

2344# <https://GSSC.ESA.int/navipedia/index.php/Reference_Frames_in_GNSS> 

2345# <https://kb.OSU.edu/dspace/handle/1811/77986> 

2346# <https://www.IBM.com/docs/en/db2/11.5?topic=systems-supported-spheroids> 

2347# <https://w3.Energistics.org/archive/Epicentre/Epicentre_v3.0/DataModel/LogicalDictionary/StandardValues/ellipsoid.html> 

2348# <https://GitHub.com/locationtech/proj4j/blob/master/src/main/java/org/locationtech/proj4j/datum/Ellipsoid.java> 

2349Ellipsoids._assert( # <https://WikiPedia.org/wiki/Earth_ellipsoid> 

2350 Airy1830 = _lazy(_Airy1830_, *_T(6377563.396, _0_0, 299.3249646)), # b=6356256.909 

2351 AiryModified = _lazy(_AiryModified_, *_T(6377340.189, _0_0, 299.3249646)), # b=6356034.448 

2352# APL4_9 = _lazy('APL4_9', *_T(6378137.0, _0_0, 298.24985392)), # Appl. Phys. Lab. 1965 

2353# ANS = _lazy('ANS', *_T(6378160.0, _0_0, 298.25)), # Australian Nat. Spheroid 

2354# AN_SA96 = _lazy('AN_SA96', *_T(6378160.0, _0_0, 298.24985392)), # Australian Nat. South America 

2355 Australia1966 = _lazy('Australia1966', *_T(6378160.0, _0_0, 298.25)), # b=6356774.7192 

2356 ATS1977 = _lazy('ATS1977', *_T(6378135.0, _0_0, 298.257)), # "Average Terrestrial System" 

2357 Bessel1841 = _lazy(_Bessel1841_, *_T(6377397.155, 6356078.962818, 299.152812797)), 

2358 BesselModified = _lazy('BesselModified', *_T(6377492.018, _0_0, 299.1528128)), 

2359# BesselNamibia = _lazy('BesselNamibia', *_T(6377483.865, _0_0, 299.1528128)), 

2360 CGCS2000 = _lazy('CGCS2000', *_T(R_MA, _0_0, 298.257222101)), # BeiDou Coord System (BDC) 

2361# Clarke1858 = _lazy('Clarke1858', *_T(6378293.639, _0_0, 294.260676369)), 

2362 Clarke1866 = _lazy(_Clarke1866_, *_T(6378206.4, 6356583.8, 294.978698214)), 

2363 Clarke1880 = _lazy('Clarke1880', *_T(6378249.145, 6356514.86954978, 293.465)), 

2364 Clarke1880IGN = _lazy(_Clarke1880IGN_, *_T(6378249.2, 6356515.0, 293.466021294)), 

2365 Clarke1880Mod = _lazy('Clarke1880Mod', *_T(6378249.145, 6356514.96639549, 293.466307656)), # aka Clarke1880Arc 

2366 CPM1799 = _lazy('CPM1799', *_T(6375738.7, 6356671.92557493, 334.39)), # Comm. des Poids et Mesures 

2367 Delambre1810 = _lazy('Delambre1810', *_T(6376428.0, 6355957.92616372, 311.5)), # Belgium 

2368 Engelis1985 = _lazy('Engelis1985', *_T(6378136.05, 6356751.32272154, 298.2566)), 

2369# Everest1830 = _lazy('Everest1830', *_T(6377276.345, _0_0, 300.801699997)), 

2370# Everest1948 = _lazy('Everest1948', *_T(6377304.063, _0_0, 300.801699997)), 

2371# Everest1956 = _lazy('Everest1956', *_T(6377301.243, _0_0, 300.801699997)), 

2372 Everest1967 = _lazy('Everest1967', *_T(6377298.556, _0_0, 300.801699997)), 

2373 Everest1969 = _lazy('Everest1969', *_T(6377295.664, 6356094.667915, 300.801699997)), 

2374 Everest1975 = _lazy('Everest1975', *_T(6377299.151, 6356098.14512013, 300.8017255)), 

2375 Fisher1968 = _lazy('Fisher1968', *_T(6378150.0, 6356768.33724438, 298.3)), 

2376# Fisher1968Mod = _lazy('Fisher1968Mod', *_T(6378155.0, _0_0, 298.3)), 

2377 GEM10C = _lazy('GEM10C', *_T(R_MA, 6356752.31424783, 298.2572236)), 

2378 GPES = _lazy('GPES', *_T(6378135.0, 6356750.0, _0_0)), # "Gen. Purpose Earth Spheroid" 

2379 GRS67 = _lazy('GRS67', *_T(6378160.0, _0_0, 298.247167427)), # Lucerne b=6356774.516 

2380# GRS67Truncated = _lazy('GRS67Truncated', *_T(6378160.0, _0_0, 298.25)), 

2381 GRS80 = _lazy(_GRS80_, *_T(R_MA, 6356752.314140347, 298.25722210088)), # IUGG, ITRS, ETRS89 

2382# Hayford1924 = _lazy('Hayford1924', *_T(6378388.0, 6356911.94612795, None)), # aka Intl1924 f_=297 

2383 Helmert1906 = _lazy('Helmert1906', *_T(6378200.0, 6356818.16962789, 298.3)), 

2384# Hough1960 = _lazy('Hough1960', *_T(6378270.0, _0_0, 297.0)), 

2385 IAU76 = _lazy('IAU76', *_T(6378140.0, _0_0, 298.257)), # Int'l Astronomical Union 

2386 IERS1989 = _lazy('IERS1989', *_T(6378136.0, _0_0, 298.257)), # b=6356751.302 

2387 IERS1992TOPEX = _lazy('IERS1992TOPEX', *_T(6378136.3, 6356751.61659215, 298.257223563)), # IERS/TOPEX/Poseidon/McCarthy 

2388 IERS2003 = _lazy('IERS2003', *_T(6378136.6, 6356751.85797165, 298.25642)), 

2389 Intl1924 = _lazy(_Intl1924_, *_T(6378388.0, _0_0, 297.0)), # aka Hayford b=6356911.9462795 

2390 Intl1967 = _lazy('Intl1967', *_T(6378157.5, 6356772.2, 298.24961539)), # New Int'l 

2391 Krassovski1940 = _lazy(_Krassovski1940_, *_T(6378245.0, 6356863.01877305, 298.3)), # spelling 

2392 Krassowsky1940 = _lazy(_Krassowsky1940_, *_T(6378245.0, 6356863.01877305, 298.3)), # spelling 

2393# Kaula = _lazy('Kaula', *_T(6378163.0, _0_0, 298.24)), # Kaula 1961 

2394# Lerch = _lazy('Lerch', *_T(6378139.0, _0_0, 298.257)), # Lerch 1979 

2395 Maupertuis1738 = _lazy('Maupertuis1738', *_T(6397300.0, 6363806.28272251, 191.0)), # France 

2396 Mercury1960 = _lazy('Mercury1960', *_T(6378166.0, 6356784.28360711, 298.3)), 

2397 Mercury1968Mod = _lazy('Mercury1968Mod', *_T(6378150.0, 6356768.33724438, 298.3)), 

2398# MERIT = _lazy('MERIT', *_T(6378137.0, _0_0, 298.257)), # MERIT 1983 

2399# NWL10D = _lazy('NWL10D', *_T(6378135.0, _0_0, 298.26)), # Naval Weapons Lab. 

2400 NWL1965 = _lazy('NWL1965', *_T(6378145.0, 6356759.76948868, 298.25)), # Naval Weapons Lab. 

2401# NWL9D = _lazy('NWL9D', *_T(6378145.0, 6356759.76948868, 298.25)), # NWL1965 

2402 OSU86F = _lazy('OSU86F', *_T(6378136.2, 6356751.51693008, 298.2572236)), 

2403 OSU91A = _lazy('OSU91A', *_T(6378136.3, 6356751.6165948, 298.2572236)), 

2404# Plessis1817 = _lazy('Plessis1817', *_T(6397523.0, 6355863.0, 153.56512242)), # XXX incorrect? 

2405 Plessis1817 = _lazy('Plessis1817', *_T(6376523.0, 6355862.93325557, 308.64)), # XXX IGN France 1972 

2406# Prolate = _lazy('Prolate', *_T(6356752.3, R_MA, _0_0)), 

2407 PZ90 = _lazy('PZ90', *_T(6378136.0, _0_0, 298.257839303)), # GLOSNASS PZ-90 and PZ-90.11 

2408# SEAsia = _lazy('SEAsia', *_T(6378155.0, _0_0, 298.3)), # SouthEast Asia 

2409 SGS85 = _lazy('SGS85', *_T(6378136.0, 6356751.30156878, 298.257)), # Soviet Geodetic System 

2410 SoAmerican1969 = _lazy('SoAmerican1969', *_T(6378160.0, 6356774.71919531, 298.25)), # South American 

2411 Sphere = _lazy(_Sphere_, *_T(R_M, R_M, _0_0)), # pseudo 

2412 SphereAuthalic = _lazy('SphereAuthalic', *_T(R_FM, R_FM, _0_0)), # pseudo 

2413 SpherePopular = _lazy('SpherePopular', *_T(R_MA, R_MA, _0_0)), # EPSG:3857 Spheroid 

2414 Struve1860 = _lazy('Struve1860', *_T(6378298.3, 6356657.14266956, 294.73)), 

2415# Walbeck = _lazy('Walbeck', *_T(6376896.0, _0_0, 302.78)), 

2416# WarOffice = _lazy('WarOffice', *_T(6378300.0, _0_0, 296.0)), 

2417 WGS60 = _lazy('WGS60', *_T(6378165.0, 6356783.28695944, 298.3)), 

2418 WGS66 = _lazy('WGS66', *_T(6378145.0, 6356759.76948868, 298.25)), 

2419 WGS72 = _lazy(_WGS72_, *_T(6378135.0, _0_0, 298.26)), # b=6356750.52 

2420 WGS84 = _lazy(_WGS84_, *_T(R_MA, _0_0, _f__WGS84)), # b=6356752.3142451793 

2421# U{NOAA/NOS/NGS/inverse<https://GitHub.com/noaa-ngs/inverse/blob/main/invers3d.f>} 

2422 WGS84_NGS = _lazy('WGS84_NGS', *_T(R_MA, _0_0, 298.257222100882711243162836600094)) 

2423) 

2424 

2425_EWGS84 = Ellipsoids.WGS84 # (INTERNAL) shared 

2426 

2427if __name__ == _DMAIN_: 

2428 

2429 from pygeodesy import nameof, printf 

2430 

2431 def _e13(f): 

2432 return fstr(f, prec=13, fmt=Fmt.e) 

2433 

2434 for E in (_EWGS84, Ellipsoids.GRS80, # NAD83, 

2435 Ellipsoids.Sphere, Ellipsoids.SpherePopular, 

2436 Ellipsoid(_EWGS84.b, _EWGS84.a, name='_Prolate')): 

2437 e = f2n(E.f) - E.n 

2438 printf('# %s: %s', _DOT_('Ellipsoids', E.name), E.toStr(prec=10, terse=0), nl=1) 

2439 printf('# (e-fmt) f_=%s, f=%s, e=%s, e2=%s, n=%s (%s)', _e13(E.f_), _e13(E.f), 

2440 _e13(E.e), _e13(E.e2), 

2441 _e13(E.n), fstr(e, prec=9, fmt=Fmt.e)) 

2442 printf('# %s %s', Ellipsoid.AlphaKs.name, fstr(E.AlphaKs, prec=20)) 

2443 printf('# %s %s', Ellipsoid.BetaKs.name, fstr(E.BetaKs, prec=20)) 

2444 printf('# %s %s', nameof(Ellipsoid.KsOrder), E.KsOrder) # property 

2445 

2446 from pygeodesy.internals import _pregistry 

2447 # __doc__ of this file, force all into registry 

2448 _pregistry(Ellipsoids) 

2449 

2450# % python3.13 -m pygeodesy.ellipsoids 

2451 

2452# Ellipsoids.WGS84: name='WGS84', a=6378137, f=0.0033528107, e=0.0818191908, b=6356752.3142451793, f_=298.257223563, e2=0.00669438, e21=0.99330562, e22=0.0067394967, e32=0.0033584313, n=0.0016792204, A=6367449.1458234144, L=10001965.7293127235, R1=6371008.7714150595, R2=6371007.1809184738, R3=6371000.7900091587, Rbiaxial=6367453.6345163295, Rtriaxial=6372797.5559594007 

2453# (e-fmt) f_=2.98257223563e+02, f=3.3528106647475e-03, e=8.1819190842622e-02, e2=6.6943799901413e-03, n=1.6792203863837e-03 (0.0e+00) 

2454# AlphaKs 0.00083773182062446994, 0.00000076085277735725, 0.00000000119764550324, 0.00000000000242917068, 0.00000000000000571182, 0.0000000000000000148, 0.00000000000000000004, 0.0 

2455# BetaKs 0.00083773216405794875, 0.0000000590587015222, 0.00000000016734826653, 0.00000000000021647981, 0.00000000000000037879, 0.00000000000000000072, 0.0, 0.0 

2456# KsOrder 8 

2457 

2458# Ellipsoids.GRS80: name='GRS80', a=6378137, f=0.0033528107, e=0.081819191, b=6356752.3141403468, f_=298.2572221009, e2=0.00669438, e21=0.99330562, e22=0.0067394968, e32=0.0033584313, n=0.0016792204, A=6367449.1457710434, L=10001965.7292304561, R1=6371008.7713801153, R2=6371007.1808835147, R3=6371000.7899741363, Rbiaxial=6367453.6344640013, Rtriaxial=6372797.5559332585 

2459# (e-fmt) f_=2.9825722210088e+02, f=3.3528106811837e-03, e=8.1819191042833e-02, e2=6.6943800229036e-03, n=1.6792203946295e-03 (0.0e+00) 

2460# AlphaKs 0.00083773182472890429, 0.00000076085278481561, 0.00000000119764552086, 0.00000000000242917073, 0.00000000000000571182, 0.0000000000000000148, 0.00000000000000000004, 0.0 

2461# BetaKs 0.0008377321681623882, 0.00000005905870210374, 0.000000000167348269, 0.00000000000021647982, 0.00000000000000037879, 0.00000000000000000072, 0.0, 0.0 

2462# KsOrder 8 

2463 

2464# Ellipsoids.Sphere: name='Sphere', a=6371008.7714149999, f=0, e=0, b=6371008.7714149999, f_=0, e2=0, e21=1, e22=0, e32=0, n=0, A=6371008.7714149999, L=10007557.1761167478, R1=6371008.7714149999, R2=6371008.7714149999, R3=6371008.7714149999, Rbiaxial=6371008.7714149999, Rtriaxial=6371008.7714149999 

2465# (e-fmt) f_=0.0e+00, f=0.0e+00, e=0.0e+00, e2=0.0e+00, n=0.0e+00 (0.0e+00) 

2466# AlphaKs 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 

2467# BetaKs 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 

2468# KsOrder 8 

2469 

2470# Ellipsoids.SpherePopular: name='SpherePopular', a=6378137, f=0, e=0, b=6378137, f_=0, e2=0, e21=1, e22=0, e32=0, n=0, A=6378137, L=10018754.171394622, R1=6378137, R2=6378137, R3=6378137, Rbiaxial=6378137, Rtriaxial=6378137 

2471# (e-fmt) f_=0.0e+00, f=0.0e+00, e=0.0e+00, e2=0.0e+00, n=0.0e+00 (0.0e+00) 

2472# AlphaKs 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 

2473# BetaKs 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 

2474# KsOrder 8 

2475 

2476# Ellipsoids._Prolate: name='_Prolate', a=6356752.3142451793, f=-0.0033640898, e=0.0820944379, b=6378137, f_=-297.257223563, e2=-0.0067394967, e21=1.0067394967, e22=-0.00669438, e32=-0.0033584313, n=-0.0016792204, A=6367449.1458234144, L=10001965.7293127235, R1=6363880.5428301189, R2=6363878.9413582645, R3=6363872.5644020075, Rbiaxial=6367453.6345163295, Rtriaxial=6362105.2243882557 

2477# (e-fmt) f_=-2.97257223563e+02, f=-3.3640898209765e-03, e=8.2094437949696e-02, e2=-6.7394967422765e-03, n=-1.6792203863837e-03 (0.0e+00) 

2478# AlphaKs -0.00084149152514366627, 0.00000076653480614871, -0.00000000120934503389, 0.0000000000024576225, -0.00000000000000578863, 0.00000000000000001502, -0.00000000000000000004, 0.0 

2479# BetaKs -0.00084149187224351817, 0.00000005842735196773, -0.0000000001680487236, 0.00000000000021706261, -0.00000000000000038002, 0.00000000000000000073, -0.0, 0.0 

2480# KsOrder 8 

2481 

2482# **) MIT License 

2483# 

2484# Copyright (C) 2016-2026 -- mrJean1 at Gmail -- All Rights Reserved. 

2485# 

2486# Permission is hereby granted, free of charge, to any person obtaining a 

2487# copy of this software and associated documentation files (the "Software"), 

2488# to deal in the Software without restriction, including without limitation 

2489# the rights to use, copy, modify, merge, publish, distribute, sublicense, 

2490# and/or sell copies of the Software, and to permit persons to whom the 

2491# Software is furnished to do so, subject to the following conditions: 

2492# 

2493# The above copyright notice and this permission notice shall be included 

2494# in all copies or substantial portions of the Software. 

2495# 

2496# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 

2497# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 

2498# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 

2499# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 

2500# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 

2501# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 

2502# OTHER DEALINGS IN THE SOFTWARE.