Coverage for pygeodesy/ellipsoids.py: 96%

1

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

3

4 u'''Ellipsoidal and spherical earth models.

5

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

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

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

9

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

11

12 Following 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, f_=299.3249646, b=6356256.90923729)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

63'''

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

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

66

67# from pygeodesy.albers import AlbersEqualAreaCylindrical # _MODS

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

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

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

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

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

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

74# from pygeodesy.ellipses import Ellipse # _MODS

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

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

77 hypot1, hypot2, sqrt3, Fsum

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

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

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

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

82 _height_, _Intl1924_, _incompatible_, _invalid_, _Krassovski1940_, \

83 _Krassowsky1940_, _lat_, _meridional_, _negative_, _not_finite_, \

84 _prime_vertical_, _radius_, _Sphere_, _SPACE_, _vs_, _WGS72_, _WGS84_

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

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

87 _NamedEnumItem, _NamedTuple, _Pass, _ALL_LAZY, _MODS

88 from pygeodesy.namedTuples import Distance2Tuple, Vector3Tuple, Vector4Tuple

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

90 deprecated_property_RO, property_RO, property_ROver

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

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

93 from pygeodesy.units import Azimuth, Bearing, Distance, Float, Float_, Height, Lamd, Lat, \

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

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

96

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

98

99 __all__ = _ALL_LAZY.ellipsoids

100 __version__ = '26.02.14'

101

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

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

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

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

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

107

108

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

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

111 '''

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

113

114

115 def _sin2cos2(rad):

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

117 '''

118 if rad:

119 s2 = sin(rad)**2

120 if s2 > EPS:

121 c2 = _1_0 - s2

122 if c2 > EPS:

123 if c2 < EPS1:

124 return s2, c2

125 else:

126 return _1_0, _0_0 # rad == PI_2

127 return _0_0, _1_0 # rad == 0

128

129

130 class a_f2Tuple(_NamedTuple):

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

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

133

134 @see: Class L{Ellipsoid2}.

135 '''

136 _Names_ = (_a_, _f_) # name 'f' not 'f_'

137 _Units_ = (_Pass, _Pass)

138

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

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

141

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

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

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

145

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

147

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

149

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

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

152

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

154 '''

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

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

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

158 f = _f_0_0

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

160

161 @Property_RO

162 def b(self):

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

164 '''

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

166

167 def ellipsoid(self, **name):

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

169

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

171

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

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

174 '''

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

176

177 @Property_RO

178 def f_(self):

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

180 '''

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

182

183

184 class Circle4Tuple(_NamedTuple):

185 '''4-Tuple C{(radius, height, lat, beta)} of the C{radius} and C{height},

186 both conventionally in C{meter} of a parallel I{circle of latitude} at

187 (geodetic) latitude C{lat} and the I{parametric (or reduced) auxiliary

188 latitude} C{beta}, both in C{degrees90}.

189

190 The C{height} is the (signed) distance along the z-axis between the

191 parallel and the equator. At near-polar C{lat}s, the C{radius} is C{0},

192 the C{height} is the ellipsoid's (signed) polar radius and C{beta}

193 equals C{lat}.

194 '''

195 _Names_ = (_radius_, _height_, _lat_, _beta_)

196 _Units_ = ( Radius, Height, Lat, Lat)

197

198

199 class Curvature2Tuple(_NamedTuple):

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

201 C{meter}, conventionally.

202 '''

203 _Names_ = (_meridional_, _prime_vertical_)

204 _Units_ = ( Meter, Meter)

205

206 @property_RO

207 def transverse(self):

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

209 '''

210 return self.prime_vertical

211

212

213 class Ellipsoid(_NamedEnumItem):

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

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

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

217 '''

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

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

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

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

222

223 _geodsolve = NN # means, use PYGEODESY_GEODSOLVE

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

225 _rhumbsolve = NN # means, use PYGEODESY_RHUMBSOLVE

226

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

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

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

230

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

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

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

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

235 spherical).

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

237

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

239

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

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

242

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

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

245 '''

246 ff_ = f, f_ # assertion below

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

248 try:

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

250 if not _isfinite(a):

251 raise ValueError(_SPACE_(_a_, _not_finite_))

252

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

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

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

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

257 elif f is not None:

258 f = Float(f=f)

259 b = a_f2b(a, f)

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

261 elif f_:

262 f_ = Float(f_=f_)

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

264 f = f_2f(f_)

265 else: # only a, spherical

266 f_ = f = 0

267 b = a # superfluous

268

269 if not f < _1_0: # sanity check, see .ecef.Ecef.__init__

270 raise ValueError(_SPACE_(_f_, _invalid_))

271 if not _isfinite(b):

272 raise ValueError(_SPACE_(_b_, _not_finite_))

273

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

275 b = a

276 f = _f_0_0

277 f_ = _f__0_0

278

279 except (TypeError, ValueError) as x:

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

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

282 raise _ValueError(t, cause=x)

283

284 self._a = a

285 self._b = b

286 self._f = f

287 self._f_ = f_

288

289 self._register(Ellipsoids, n)

290

291 if f and f_: # see test/testEllipsoidal

292 d = dict(eps=_TOL)

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

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

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

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

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

298

299 def __eq__(self, other):

300 '''Compare this and an other ellipsoid.

301

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

303

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

305 '''

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

307 self.a == other.a and

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

309

310 def __hash__(self):

311 return self._hash # memoized

312

313 @Property_RO

314 def a(self):

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

316 '''

317 return self._a

318

319 equatoradius = a # = Requatorial

320

321 @Property_RO

322 def a2(self):

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

324 '''

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

326

327 @Property_RO

328 def a2_(self):

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

330 '''

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

332

333 @Property_RO

334 def a_b(self):

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

336 '''

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

338

339 @Property_RO

340 def a2_b(self):

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

342

343 @see: U{Radii of Curvature

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

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

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

347

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

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

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

351 '''

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

353

354 @Property_RO

355 def a2_b2(self):

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

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

358 '''

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

360

361 @Property_RO

362 def a_f(self):

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

364 '''

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

366

367 a_f2 = a_f # synonym

368

369 @Property_RO

370 def A(self):

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

372

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

374

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

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

377 '''

378 A, n = self.a, self.n

379 if n:

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

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

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

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

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

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

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

387 return A

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

389# # q = 4 / self.rocPolar

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

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

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

393

394 @Property_RO

395 def _albersCyl(self):

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

397 '''

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

399

400 @Property_RO

401 def AlphaKs(self):

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

403 '''

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

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

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

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

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

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

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

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

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

413 _T(1424729850961/743921418240)) # PYCHOK unaligned

414

415 @Property_RO

416 def area(self):

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

418

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

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

421 '''

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

423

424 @Property_RO

425 def areax(self):

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

427 accurate for very I{oblate} ellipsoids.

428

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

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

431 '''

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

433

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

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

436 '''

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

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

439 t = (v, _vs_, val)

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

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

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

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

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

445 eps=eps, f0=f0, **name_value))

446

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

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

449

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

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

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

453

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

455

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

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

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

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

460 '''

461 if self.f:

462 f = self._albersCyl._tanf if inverse else \

463 self._albersCyl._txif # PYCHOK attr

464 lat = atan1d(f(tan(Phid(lat)))) # PYCHOK attr

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

466

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

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

469

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

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

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

473

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

475

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

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

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

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

480 '''

481 if self.f:

482 f = self.es_tauf if inverse else self.es_taupf # PYCHOK attr

483 lat = atan1d(f(tan(Phid(lat)))) # PYCHOK attr

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

485

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

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

488

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

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

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

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

493

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

495

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

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

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

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

500 '''

501 if self.f: # and lat

502 t = tan(Phid(lat))

503 f = self.b2_a2

504 if height:

505 if inverse:

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

507 d, f = f, _1_0

508 else:

509 d = _1_0

510 n = self.rocPrimeVertical(lat)

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

512 elif inverse:

513 f = self.a2_b2

514 lat = atan1d(t * f)

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

516

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

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

519

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

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

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

523

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

525

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

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

528 is the original geodetic latitude.

529

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

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

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

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

534 '''

535 if self.f:

536 r = Phid(lat, clip=0)

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

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

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

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

541

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

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

544

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

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

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

548

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

550

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

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

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

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

555 '''

556 if self.f:

557 lat = self._beta(Lat(lat), inverse=inverse)

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

559

560 auxReduced = auxParametric # synonymous

561

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

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

564

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

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

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

568

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

570

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

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

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

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

575 '''

576 if self.f:

577 lat = Lat(lat)

578 if 0 < fabs(lat) < _90_0:

579 if inverse:

580 e = self._elliptic_e22

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

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

583 else:

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

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

586

587 @Property_RO

588 def b(self):

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

590 '''

591 return self._b

592

593 polaradius = b # = Rpolar

594

595 @Property_RO

596 def b_a(self):

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

598

599 @see: Property L{f1}.

600 '''

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

602

603 @Property_RO

604 def b2(self):

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

606 '''

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

608

609 @Property_RO

610 def b2_a(self):

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

612

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

614 '''

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

616

617 @Property_RO

618 def b2_a2(self):

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

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

621 '''

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

623

624 def _beta(self, lat, inverse=False):

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

626 '''

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

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

629 return atan1d(s, c)

630

631 @Property_RO

632 def BetaKs(self):

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

634 '''

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

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

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

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

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

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

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

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

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

644 _T(191773887257/3719607091200)) # PYCHOK unaligned

645

646 @deprecated_Property_RO

647 def c(self): # PYCHOK no cover

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

649 return self.R2

650

651 @Property_RO

652 def c2(self):

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

654

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

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

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

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

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

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

661 '''

662 return self._c2f(False)

663

664 @Property_RO

665 def c2x(self):

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

667 ellipsoids.

668

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

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

671 '''

672 return self._c2f(True)

673

674 def _c2f(self, c2x):

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

676 '''

677 f, c2 = self.f, self.b2

678 if f:

679 e = self.e

680 if e > EPS0:

681 if f > 0: # .isOblate

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

683 elif f < 0: # .isProlate

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

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

686 return c2

687

688 def circle4(self, lat):

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

690

691 @arg lat: Geodetic latitude (C{degrees90}, C{str}).

692

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

694

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

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

697

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

699

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

701

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

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

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

705 and method C{Rlat}.

706 '''

707 lat = Lat(lat)

708 if lat:

709 B = lat # beta

710 if fabs(lat) < _90_0:

711 if self.f:

712 B = self._beta(lat)

713 z, r = sincos2d(B)

714 r *= self.a

715 z *= self.b

716 else: # near-polar

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

718 else: # equator

719 r = self.a

720 z = lat = B = _0_0

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

722

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

724 '''Convert an angle along the equator or along a parallel

725 of (geodetic) latitude to the distance.

726

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

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

729

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

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

732

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

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

735

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

737 '''

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

739

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

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

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

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

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

745

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

747 and only between points not near-polar}.

748

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

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

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

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

753

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

755 in same units as this ellipsoid's axes.

756

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

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

759

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

761 '''

762 phi0 = Phid(lat0=lat0)

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

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

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

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

767

768 @Property_RO

769 def e(self):

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

771

772 @see: Property L{es}.

773 '''

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

775

776 @deprecated_Property_RO

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

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

779 return self.e21

780

781# @Property_RO

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

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

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

785

786 @Property_RO

787 def e2(self):

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

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

790 '''

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

792

793 @Property_RO

794 def e2abs(self):

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

796 '''

797 return fabs(self.e2)

798

799 @Property_RO

800 def e21(self):

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

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

803 '''

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

805

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

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

808

809 @Property_RO

810 def e22(self):

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

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

813 '''

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

815

816 @Property_RO

817 def e22abs(self):

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

819 '''

820 return fabs(self.e22)

821

822 @Property_RO

823 def e32(self):

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

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

826 '''

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

828

829 @Property_RO

830 def e32abs(self):

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

832 '''

833 return fabs(self.e32)

834

835 @Property_RO

836 def e4(self):

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

838 '''

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

840

841 eccentricity = e # eccentricity

842# eccentricity2 = e2 # eccentricity squared

843 eccentricity1st2 = e2 # first eccentricity squared, signed

844 eccentricity2nd2 = e22 # second eccentricity squared, signed

845 eccentricity3rd2 = e32 # third eccentricity squared, signed

846

847 def ecef(self, Ecef=None):

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

849

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

851

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

853

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

855

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

857 '''

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

859

860 @Property_RO

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

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

863 '''

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

865 return _MODS.elliptic.Elliptic(e12)

866

867 @Property_RO

868 def _elliptic_e22(self): # aka ._elliptic_ep2

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

870 '''

871 return _MODS.elliptic.Elliptic(-self.e22abs) # complex

872

873 equatoradius = a # Requatorial

874

875 @Property_RO

876 def equatorimeter(self):

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

878 '''

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

880

881 def e2s(self, s):

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

883

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

885

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

887

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

889 '''

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

891

892 def e2s2(self, s):

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

894

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

896

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

898

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

900 '''

901 r, e2 = _1_0, self.e2

902 if e2: # and s

903 try:

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

905 if r < 0:

906 raise ValueError(_negative_)

907 except (TypeError, ValueError) as x:

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

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

910 return r

911

912 @Property_RO

913 def es(self):

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

915

916 @see: Property L{e}.

917 '''

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

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

920

921 def es_atanh(self, x):

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

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

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

925

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

927

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

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

930 '''

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

932

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

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

935 '''

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

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

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

939 _0_0)) # .isSpherical

940

941 @Property_RO

942 def es_c(self):

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

944 '''

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

946

947 def es_tauf(self, taup):

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

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

950

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

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

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

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

955 '''

956 t = Scalar(taup=taup)

957 if self.f: # .isEllipsoidal

958 a = fabs(t)

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

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

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

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

963 if fabs(d) < s:

964 break

965 t = Scalar(tauf=T)

966 return t

967

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

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

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

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

972 '''

973 e = self._1_e21

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

975 _tf2 = self._es_taupf2

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

977 a, h = _tf2(T)

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

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

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

981 yield T, i, d

982

983 def es_taupf(self, tau):

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

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

986

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

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

989 '''

990 t = Scalar(tau=tau)

991 if self.f: # .isEllipsoidal

992 t, _ = self._es_taupf2(t)

993 t = Scalar(taupf=t)

994 return t

995

996 def _es_taupf2(self, tau):

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

998 '''

999 if _isfinite(tau):

1000 h = hypot1(tau)

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

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

1003 else:

1004 a, h = tau, INF

1005 return a, h

1006

1007 @Property_RO

1008 def _exp_es_atanh_1(self):

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

1010 '''

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

1012

1013 @Property_RO

1014 def f(self):

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

1016 '''

1017 return self._f

1018

1019 @Property_RO

1020 def f_(self):

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

1022 '''

1023 return self._f_

1024

1025 @Property_RO

1026 def f1(self):

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

1028

1029 @see: Property L{b_a}.

1030 '''

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

1032

1033 @Property_RO

1034 def f2(self):

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

1036 '''

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

1038

1039 @deprecated_Property_RO

1040 def geodesic(self):

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

1042 return self.geodesicw

1043

1044 def geodesic_(self, exact=True):

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

1046

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

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

1049 instance for I{this} ellipsoid.

1050

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

1052

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

1054

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

1056 '''

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

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

1059 name=self.name)

1060 else:

1061 g = exact

1062 E = _xattr(g, ellipsoid=None)

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

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

1065 return g

1066

1067 @property_ROver

1068 def _Geodesics(self):

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

1070 '''

1071 t = (_MODS.geodesicx.GeodesicExact,

1072 _MODS.geodsolve.GeodesicSolve)

1073 try:

1074 t += (_MODS.geodesicw.Geodesic,

1075 _MODS.geodesicw._wrapped.Geodesic)

1076 except ImportError:

1077 pass

1078 return t # overwrite property_ROver

1079

1080 @property_RO

1081 def geodesicw(self):

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

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

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

1085 package is installed.

1086 '''

1087 # if not self.isEllipsoidal:

1088 # raise _IsnotError(_ellipsoidal_, ellipsoid=self)

1089 return _MODS.geodesicw.Geodesic(self)

1090

1091 @property_RO

1092 def geodesicx(self):

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

1094 '''

1095 # if not self.isEllipsoidal:

1096 # raise _IsnotError(_ellipsoidal_, ellipsoid=self)

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

1098

1099 @property

1100 def geodsolve(self):

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

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

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

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

1105 '''

1106 # if not self.isEllipsoidal:

1107 # raise _IsnotError(_ellipsoidal_, ellipsoid=self)

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

1109

1110 @geodsolve.setter # PYCHOK setter!

1111 def geodsolve(self, path):

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

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

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

1115 '''

1116 self._geodsolve = path

1117

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

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

1120 from a Point-Of-View in space.

1121

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

1123 or L{Vector3d}).

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

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

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

1127

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

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

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

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

1132

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

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

1135 outside this ellipsoid or in opposite direction.

1136

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

1138

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

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

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

1142 '''

1143 try:

1144 m = _MODS._triaxials_triaxial5

1145 v, d, i = m._hartzell3(pov, los, self._triaxial)

1146 except Exception as x:

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

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

1149

1150 @Property_RO

1151 def _hash(self):

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

1153

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

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

1156 this ellipsoid's surface.

1157

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

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

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

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

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

1163

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

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

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

1167

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

1169

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

1171

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

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

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

1175 '''

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

1177 r = v.length

1178

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

1180 if r < EPS0: # EPS

1181 v = v.times(_0_0)

1182 h = -a

1183

1184 elif self.isSpherical:

1185 v = v.times(a / r)

1186 h = r - a

1187

1188 elif normal: # perpendicular to ellipsoid

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

1190 if x < EPS0: # PYCHOK no cover

1191 z = copysign0(b, v.z)

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

1193 h = y - b # polar

1194 elif y < EPS0: # PYCHOK no cover

1195 t = a / r

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

1197 h = x - a # equatorial

1198 else: # normal in 1st quadrant

1199 m = _MODS._triaxials_triaxial5

1200 x, y, i = m._plumbTo3(x, y, self)

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

1202 h = t.minus(v).length

1203

1204 else: # radial to ellipsoid's center

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

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

1207 v = v.times(t)

1208 h = r * (_1_0 - t)

1209

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

1211

1212 def _heightB(self, sa, ca, z, p): # in ecef.EcefSudano, ecec.EcefVeness

1213 '''(INTERNAL) Height above ellipsoid (Bowring eqn 7) at C{lat}.

1214 '''

1215 # sa, ca = sincos2d(lat)

1216 # p = hypot(x, y) # distance to polar axis

1217

1218 # r = a / self.e2s(sa) # length of normal terminated by polar axis

1219 # h = p * ca + z * sa - (a * a / r)

1220 return (p * ca + fabs(z * sa) - self.a * self.e2s(sa)) if sa else (p - self.a)

1221

1222 @Property_RO

1223 def _heightMax(self):

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

1225 '''

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

1227

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

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

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

1231 '''

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

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

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

1235

1236 @Property_RO

1237 def isEllipsoidal(self):

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

1239 '''

1240 return self.f != 0

1241

1242 @Property_RO

1243 def isOblate(self):

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

1245 spherical otherwise.

1246 '''

1247 return self.f > 0

1248

1249 @Property_RO

1250 def isProlate(self):

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

1252 spherical otherwise.

1253 '''

1254 return self.f < 0

1255

1256 @Property_RO

1257 def isSpherical(self):

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

1259 '''

1260 return self.f == 0

1261

1262 def _Kseries(self, *AB8Ks):

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

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

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

1266

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

1268 coefficient tuples.

1269

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

1271

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

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

1274 '''

1275 k = self.KsOrder

1276 if self.n:

1277 ns = fpowers(self.n, k)

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

1279 else:

1280 ks = _0_0s(k)

1281 return ks

1282

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

1284 def KsOrder(self):

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

1286 '''

1287 return self._KsOrder

1288

1289 @KsOrder.setter # PYCHOK setter!

1290 def KsOrder(self, order):

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

1292

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

1294 '''

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

1296 raise _ValueError(order=order)

1297 if self._KsOrder != order:

1298 Ellipsoid.AlphaKs._update(self)

1299 Ellipsoid.BetaKs._update(self)

1300 self._KsOrder = order

1301

1302 @Property_RO

1303 def L(self):

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

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

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

1307 '''

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

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

1310

1311 def Llat(self, lat):

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

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

1314

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

1316

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

1318 hemisphere (C{meter}).

1319 '''

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

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

1322

1323 Lmeridian = Llat # meridional distance

1324

1325 @property_RO

1326 def _Lpd(self):

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

1328 '''

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

1330

1331 @property_RO

1332 def _Lpr(self):

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

1334 '''

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

1336

1337 @deprecated_Property_RO

1338 def majoradius(self): # PYCHOK no cover

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

1340 return self.a

1341

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

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

1344 a parallel of (geodetic) latitude.

1345

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

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

1348

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

1350

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

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

1353

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

1355 '''

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

1357

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

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

1360 a parallel of (geodetic) latitude.

1361

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

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

1364

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

1366

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

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

1369

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

1371 '''

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

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

1374

1375 @deprecated_Property_RO

1376 def minoradius(self): # PYCHOK no cover

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

1378 return self.b

1379

1380 @Property_RO

1381 def n(self):

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

1383 '''

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

1385

1386 flattening = f

1387 flattening1st = f

1388 flattening2nd = f2

1389 flattening3rd = n

1390

1391 polaradius = b # Rpolar

1392

1393 @property_RO

1394 def polarimeter(self):

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

1396 '''

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

1398

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

1400

1401 @deprecated_Property_RO

1402 def quarteradius(self): # PYCHOK no cover

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

1404 return self.L

1405

1406 @Property_RO

1407 def R1(self):

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

1409

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

1411 and method C{Rgeometric}.

1412 '''

1413 r = Fsum(self.a, self.a, self.b).fover(_3_0) if self.f else self.a

1414 return Radius(R1=r)

1415

1416 Rmean = R1

1417

1418 @Property_RO

1419 def R2(self):

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

1421

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

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

1424 '''

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

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

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

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

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

1430

1431 Rauthalic = R2

1432

1433 @Property_RO

1434 def R2x(self):

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

1436

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

1438 '''

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

1440

1441 Rauthalicx = R2x

1442

1443 @Property_RO

1444 def R3(self):

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

1446

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

1448 '''

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

1450 return Radius(R3=r)

1451

1452 Rvolumetric = R3

1453

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

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

1456 a parallel of (geodetic) latitude.

1457

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

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

1460

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

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

1463

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

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

1466

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

1468 '''

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

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

1471

1472 @Property_RO

1473 def Rbiaxial(self):

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

1475

1476 @see: C{Rtriaxial}

1477 '''

1478 a, b = self.a, self.b

1479 if b != a:

1480 b = hypot(a, b) / _SQRT2

1481 return Radius(Rbiaxial=b)

1482

1483 Requatorial = a # for consistent naming

1484

1485 def Rgeocentric(self, lat):

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

1487

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

1489

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

1491

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

1493

1494 @see: U{Geocentric Radius

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

1496 '''

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

1498 if p and self.f:

1499 if fabs(p) < PI_2:

1500 s2, c2 = _sin2cos2(p)

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

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

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

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

1505 s2 *= self.b2_a2

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

1507 else:

1508 r = self.b

1509 return Radius(Rgeocentric=r)

1510

1511 @Property_RO

1512 def Rgeometric(self):

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

1514

1515 @see: C{R1}.

1516 '''

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

1518 return Radius(Rgeometric=g)

1519

1520 def rhumb_(self, exact=True):

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

1522

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

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

1525 for I{this} ellipsoid.

1526

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

1528

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

1530

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

1532 '''

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

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

1535 else:

1536 r = exact

1537 E = _xattr(r, ellipsoid=None)

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

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

1540 return r

1541

1542 @property_RO

1543 def rhumbaux(self):

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

1545 '''

1546 # if not self.isEllipsoidal:

1547 # raise _IsnotError(_ellipsoidal_, ellipsoid=self)

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

1549

1550 @property_RO

1551 def rhumbekx(self):

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

1553 '''

1554 # if not self.isEllipsoidal:

1555 # raise _IsnotError(_ellipsoidal_, ellipsoid=self)

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

1557

1558 @property_ROver

1559 def _Rhumbs(self):

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

1561 '''

1562 r = _MODS.rhumb

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

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

1565

1566 @property

1567 def rhumbsolve(self):

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

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

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

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

1572 '''

1573 # if not self.isEllipsoidal:

1574 # raise _IsnotError(_ellipsoidal_, ellipsoid=self)

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

1576

1577 @rhumbsolve.setter # PYCHOK setter!

1578 def rhumbsolve(self, path):

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

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

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

1582 '''

1583 self._rhumbsolve = path

1584

1585 @deprecated_property_RO

1586 def rhumbx(self):

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

1588 return self.rhumbekx

1589

1590 def Rlat(self, lat):

1591 '''I{Approximate} the earth radius of (geodetic) latitude.

1592

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

1594

1595 @return: Approximate earth radius (C{meter}).

1596

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

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

1599

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

1601

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

1603

1604 @note: C{Rlat(B{90})} equals C{Rpolar}.

1605

1606 @see: Method C{circle4}.

1607 '''

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

1609 r = self.a

1610 if self.f and lat: # .isEllipsoidal

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

1612 r = Radius(Rlat=r)

1613 return r

1614

1615 Rpolar = b # for consistent naming

1616

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

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

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

1620

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

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

1623 to use an alternate formula.

1624

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

1626

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

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

1629

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

1631 '''

1632 if sa and self.f: # .isEllipsoidal

1633 if ca is None:

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

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

1636 elif ca: # derived from EcefYou.forward

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

1638 n = self.a / h

1639 else:

1640 n = self.a2_b / fabs(sa)

1641 else:

1642 n = self.a

1643 return n

1644

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

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

1647 radii of curvature of (geodetic) latitude.

1648

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

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

1651

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

1653 the radii of curvature.

1654

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

1656

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

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

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

1660 Earth_radius#Radii_of_curvature>}.

1661 '''

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

1663

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

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

1666 curvature of (geodetic) latitude.

1667

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

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

1670

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

1672 radii of curvature.

1673

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

1675

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

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

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

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

1680 '''

1681 p = fabs(Phi(phi))

1682 if self.f:

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

1684 if r > EPS02:

1685 n = sqrt(self.a2 / r)

1686 m = n * self.e21 / r

1687 else:

1688 m = n = _0_0

1689 else:

1690 m = n = self.a

1691 if scaled and p:

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

1693 return Curvature2Tuple(m, n)

1694

1695 def rocAzimuth(self, lat, azimuth):

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

1697 and C{azimuth} compass direction.

1698

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

1700 '''

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

1702

1703 def rocBearing(self, lat, bearing):

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

1705 and C{bearing} compass direction.

1706

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

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

1709

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

1711

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

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

1714

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

1716

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

1718 '''

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

1720

1721 def _rocDirectional(self, lat, deg):

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

1723 '''

1724 if self.f:

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

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

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

1728 c2 *= n / m

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

1730 s2 *= m / n

1731 n = m

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

1733 else:

1734 r = self.b # == self.a

1735 return r

1736

1737 @Property_RO

1738 def rocEquatorial2(self):

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

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

1741

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

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

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

1745 '''

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

1747 return Curvature2Tuple(m, self.a)

1748

1749 def rocGauss(self, lat):

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

1751

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

1753

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

1755

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

1757

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

1759 Earth_radius#Radii_of_curvature>}

1760 '''

1761 # using ...

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

1763 # return sqrt(m * n)

1764 # ... requires 1 or 2 sqrt

1765 g = self.b

1766 if self.f:

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

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

1769 return Radius(rocGauss=g)

1770

1771 def rocMean(self, lat):

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

1773

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

1775

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

1777

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

1779

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

1781 Earth_radius#Radii_of_curvature>}

1782 '''

1783 if self.f:

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

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

1786 else:

1787 m = self.a

1788 return Radius(rocMean=m)

1789

1790 def rocMeridional(self, lat):

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

1792

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

1794

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

1796

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

1798

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

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

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

1802 '''

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

1804 return Radius(rocMeridional=r.meridional)

1805

1806 rocPolar = a2_b # synonymous

1807

1808 def rocPrimeVertical(self, lat):

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

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

1811

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

1813

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

1815

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

1817

1818 @see: Methods L{roc2}, L{roc2_} and L{roc1_}, U{Local, flat earth

1819 approximation<https://www.EdWilliams.org/avform.htm#flat>} and

1820 U{Radii of Curvature<https://WikiPedia.org/wiki/

1821 Earth_radius#Radii_of_curvature>}.

1822 '''

1823 r = self.roc1_(sin(Phid(lat))) if lat else self.a

1824 return Radius(rocPrimeVertical=r)

1825

1826 rocTransverse = rocPrimeVertical # synonymous

1827

1828 @deprecated_Property_RO

1829 def Rquadratic(self): # PYCHOK no cover

1830 '''DEPRECATED, use property C{Rbiaxial} or C{Rtriaxial}.'''

1831 return self.Rbiaxial

1832

1833 @deprecated_Property_RO

1834 def Rr(self): # PYCHOK no cover

1835 '''DEPRECATED, use property C{Rrectifying}.'''

1836 return self.Rrectifying

1837

1838 @Property_RO

1839 def Rrectifying(self):

1840 '''Get the I{rectifying} earth radius (C{meter}), M{((a**(3/2) + b**(3/2)) / 2)**(2/3)}.

1841

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

1843 '''

1844 r = self.a

1845 if self.f:

1846 r *= cbrt2((sqrt3(self.b_a) + _1_0) * _0_5)

1847 return Radius(Rrectifying=r)

1848

1849 @deprecated_Property_RO

1850 def Rs(self): # PYCHOK no cover

1851 '''DEPRECATED, use property C{Rgeometric}.'''

1852 return self.Rgeometric

1853

1854 @Property_RO

1855 def Rtriaxial(self):

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

1857

1858 @see: C{Rbiaxial}

1859 '''

1860 q, b = self.a, self.b

1861 if b < q:

1862 q *= sqrt((self.b2_a2 + _3_0) * _0_25)

1863 elif b > q:

1864 q = sqrt((self.a2_b2 * _3_0 + _1_0) * _0_25) * b

1865 return Radius(Rtriaxial=q)

1866

1867 def toEllipse(self, **name):

1868 '''Get this ellipsoid's meridional ellipse L{Ellipse<pygeodesy.Ellipse>}.

1869

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

1871 '''

1872 return _MODS.ellipses.Ellipse(self.a, self.b, **name)

1873

1874 def toEllipsoid2(self, **name):

1875 '''Get a copy of this ellipsoid as an L{Ellipsoid2}.

1876

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

1878

1879 @see: Property C{a_f}.

1880 '''

1881 return Ellipsoid2(self, None, **name)

1882

1883 def toStr(self, prec=8, terse=4, **sep_name): # PYCHOK expected

1884 '''Return this ellipsoid as a text string.

1885

1886 @kwarg prec: Number of decimal digits, unstripped (C{int}).

1887 @kwarg terse: Limit the number of items (C{int}, 0...18),

1888 use C{B{terse}=0} or C{=None} for all.

1889 @kwarg sep_name: Optional C{B{name}=NN} (C{str}) or C{None}

1890 to exclude this ellipsoid's name and separator

1891 C{B{sep}=", "} to join the items (C{str}).

1892

1893 @return: This C{Ellipsoid}'s attributes (C{str}).

1894 '''

1895 E = Ellipsoid

1896 t = (E.a, E.f, E.f_, E.b, E.f2, E.n, E.e,

1897 E.e2, E.e21, E.e22, E.e32,

1898 E.A, E.L, E.R1, E.R2, E.R3,

1899 E.Rbiaxial, E.Rtriaxial)

1900 if terse:

1901 t = t[:terse]

1902 return self._instr(prec=prec, props=t, **sep_name)

1903

1904 def toTriaxial(self, **name):

1905 '''Convert this ellipsoid to a L{Triaxial_}.

1906

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

1908

1909 @return: A L{Triaxial_} or L{Triaxial} with the C{X} axis

1910 pointing east and C{Z} pointing north.

1911

1912 @see: Method L{Triaxial_.toEllipsoid}.

1913 '''

1914 T = self._triaxial

1915 return T.copy(**name) if name else T

1916

1917 @property_RO

1918 def _triaxial(self):

1919 '''(INTERNAL) Get this ellipsoid's un-/ordered C{Triaxial/_}.

1920 '''

1921 a, b, t = self.a, self.b, _MODS.triaxials

1922 T = t.Triaxial if a > b else t.Triaxial_

1923 return T(a, a, b, name=self.name)

1924

1925 @Property_RO

1926 def volume(self):

1927 '''Get the ellipsoid's I{volume} (C{meter**3}), M{4 / 3 * PI * R3**3}.

1928

1929 @see: C{R3}.

1930 '''

1931 return Meter3(volume=self.a2 * self.b * PI_3 * _4_0)

1932

1933

1934 class Ellipsoid2(Ellipsoid):

1935 '''An L{Ellipsoid} specified by I{equatorial} radius and I{flattening}.

1936 '''

1937 def __init__(self, a, f=None, **name):

1938 '''New L{Ellipsoid2}.

1939

1940 @arg a: Equatorial radius, semi-axis (C{meter}) or a previous

1941 L{Ellipsoid} instance.

1942 @arg f: Flattening: (C{float} < 1.0, negative for I{prolate}),

1943 if B{C{a}} is in C{meter}.

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

1945

1946 @raise NameError: Ellipsoid with that B{C{name}} already exists.

1947

1948 @raise ValueError: Invalid B{C{a}} or B{C{f}}.

1949

1950 @note: C{abs(B{f}) < EPS} is forced to C{B{f}=0}, I{spherical}.

1951 Negative C{B{f}} produces a I{prolate} ellipsoid.

1952 '''

1953 if f is None and isinstance(a, Ellipsoid):

1954 Ellipsoid.__init__(self, a.a, f =a.f,

1955 b=a.b, f_=a.f_, **name)

1956 else:

1957 Ellipsoid.__init__(self, a, f=f, **name)

1958

1959

1960 def _ispherical_a_b(a, b):

1961 '''(INTERNAL) C{True} for spherical or invalid C{a} or C{b}.

1962 '''

1963 return a < EPS0 or b < EPS0 or fabs(a - b) < EPS0

1964

1965

1966 def _ispherical_f(f):

1967 '''(INTERNAL) C{True} for spherical or invalid C{f}.

1968 '''

1969 return f > EPS1 or fabs(f) < EPS

1970

1971

1972 def _ispherical_f_(f_):

1973 '''(INTERNAL) C{True} for spherical or invalid C{f_}.

1974 '''

1975 f_ = fabs(f_)

1976 return f_ < EPS or f_ > _1_EPS

1977

1978

1979 def a_b2e(a, b):

1980 '''Return C{e}, the I{1st eccentricity} for a given I{equatorial} and I{polar} radius.

1981

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

1983 @arg b: Polar radius (C{scalar} > 0).

1984

1985 @return: The I{unsigned}, (1st) eccentricity (C{float} or C{0}), M{sqrt(1 - (b / a)**2)}.

1986

1987 @note: The result is always I{non-negative} and C{0} for I{near-spherical} ellipsoids.

1988 '''

1989 e2 = _a2b2e2(a, b, b2=False)

1990 return Float(e=sqrt(fabs(e2)) if e2 else _0_0) # == sqrt(fabs((a - b) * (a + b))) / a

1991

1992

1993 def a_b2e2(a, b):

1994 '''Return C{e2}, the I{1st eccentricity squared} for a given I{equatorial} and I{polar} radius.

1995

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

1997 @arg b: Polar radius (C{scalar} > 0).

1998

1999 @return: The I{signed}, (1st) eccentricity I{squared} (C{float} or C{0}), M{1 - (b / a)**2}.

2000

2001 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0}

2002 for I{near-spherical} ellipsoids.

2003 '''

2004 return Float(e2=_a2b2e2(a, b, b2=False))

2005

2006

2007 def a_b2e22(a, b):

2008 '''Return C{e22}, the I{2nd eccentricity squared} for a given I{equatorial} and I{polar} radius.

2009

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

2011 @arg b: Polar radius (C{scalar} > 0).

2012

2013 @return: The I{signed}, 2nd eccentricity I{squared} (C{float} or C{0}), M{(a / b)**2 - 1}.

2014

2015 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0}

2016 for I{near-spherical} ellipsoids.

2017 '''

2018 return Float(e22=_a2b2e2(a, b, a2=False))

2019

2020

2021 def a_b2e32(a, b):

2022 '''Return C{e32}, the I{3rd eccentricity squared} for a given I{equatorial} and I{polar} radius.

2023

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

2025 @arg b: Polar radius (C{scalar} > 0).

2026

2027 @return: The I{signed}, 3rd eccentricity I{squared} (C{float} or C{0}),

2028 M{(a**2 - b**2) / (a**2 + b**2)}.

2029

2030 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0}

2031 for I{near-spherical} ellipsoids.

2032 '''

2033 return Float(e32=_a2b2e2(a, b))

2034

2035

2036 def _a2b2e2(a, b, a2=True, b2=True):

2037 '''(INTERNAL) Helper for C{a_b2e}, C{a_b2e2}, C{a_b2e22} and C{a_b2e32}.

2038 '''

2039 if _ispherical_a_b(a, b):

2040 e2 = _0_0

2041 else: # a > 0, b > 0

2042 a, b = (_1_0, b / a) if a > b else (a / b, _1_0)

2043 a2b2 = float(a - b) * (a + b)

2044 e2 = _over(a2b2, (a**2 if a2 else _0_0) +

2045 (b**2 if b2 else _0_0)) if a2b2 else _0_0

2046 return e2

2047

2048

2049 def a_b2f(a, b):

2050 '''Return C{f}, the I{flattening} for a given I{equatorial} and I{polar} radius.

2051

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

2053 @arg b: Polar radius (C{scalar} > 0).

2054

2055 @return: The flattening (C{scalar} or C{0}), M{(a - b) / a}.

2056

2057 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0}

2058 for I{near-spherical} ellipsoids.

2059 '''

2060 f = 0 if _ispherical_a_b(a, b) else _over(float(a - b), a)

2061 return _f_0_0 if _ispherical_f(f) else Float(f=f)

2062

2063

2064 def a_b2f_(a, b):

2065 '''Return C{f_}, the I{inverse flattening} for a given I{equatorial} and I{polar} radius.

2066

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

2068 @arg b: Polar radius (C{scalar} > 0).

2069

2070 @return: The inverse flattening (C{scalar} or C{0}), M{a / (a - b)}.

2071

2072 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0}

2073 for I{near-spherical} ellipsoids.

2074 '''

2075 f_ = 0 if _ispherical_a_b(a, b) else _over(a, float(a - b))

2076 return _f__0_0 if _ispherical_f_(f_) else Float(f_=f_)

2077

2078

2079 def a_b2f2(a, b):

2080 '''Return C{f2}, the I{2nd flattening} for a given I{equatorial} and I{polar} radius.

2081

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

2083 @arg b: Polar radius (C{scalar} > 0).

2084

2085 @return: The I{signed}, 2nd flattening (C{scalar} or C{0}), M{(a - b) / b}.

2086

2087 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0}

2088 for I{near-spherical} ellipsoids.

2089 '''

2090 t = 0 if _ispherical_a_b(a, b) else float(a - b)

2091 return Float(f2=_0_0 if fabs(t) < EPS0 else _over(t, b))

2092

2093

2094 def a_b2n(a, b):

2095 '''Return C{n}, the I{3rd flattening} for a given I{equatorial} and I{polar} radius.

2096

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

2098 @arg b: Polar radius (C{scalar} > 0).

2099

2100 @return: The I{signed}, 3rd flattening (C{scalar} or C{0}), M{(a - b) / (a + b)}.

2101

2102 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0}

2103 for I{near-spherical} ellipsoids.

2104 '''

2105 t = 0 if _ispherical_a_b(a, b) else float(a - b)

2106 return Float(n=_0_0 if fabs(t) < EPS0 else _over(t, a + b))

2107

2108

2109 def a_f2b(a, f):

2110 '''Return C{b}, the I{polar} radius for a given I{equatorial} radius and I{flattening}.

2111

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

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

2114

2115 @return: The polar radius (C{float}), M{a * (1 - f)}.

2116 '''

2117 b = a if _ispherical_f(f) else (a * (_1_0 - f))

2118 return Radius_(b=a if _ispherical_a_b(a, b) else b)

2119

2120

2121 def a_f_2b(a, f_):

2122 '''Return C{b}, the I{polar} radius for a given I{equatorial} radius and I{inverse flattening}.

2123

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

2125 @arg f_: Inverse flattening (C{scalar} >>> 1).

2126

2127 @return: The polar radius (C{float}), M{a * (f_ - 1) / f_}.

2128 '''

2129 b = a if _ispherical_f_(f_) else _over(a * (f_ - _1_0), f_)

2130 return Radius_(b=a if _ispherical_a_b(a, b) else b)

2131

2132

2133 def b_f2a(b, f):

2134 '''Return C{a}, the I{equatorial} radius for a given I{polar} radius and I{flattening}.

2135

2136 @arg b: Polar radius (C{scalar} > 0).

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

2138

2139 @return: The equatorial radius (C{float}), M{b / (1 - f)}.

2140 '''

2141 t = _1_0 - f

2142 a = b if fabs(t) < EPS0 else _over(b, t)

2143 return Radius_(a=b if _ispherical_a_b(a, b) else a)

2144

2145

2146 def b_f_2a(b, f_):

2147 '''Return C{a}, the I{equatorial} radius for a given I{polar} radius and I{inverse flattening}.

2148

2149 @arg b: Polar radius (C{scalar} > 0).

2150 @arg f_: Inverse flattening (C{scalar} >>> 1).

2151

2152 @return: The equatorial radius (C{float}), M{b * f_ / (f_ - 1)}.

2153 '''

2154 t = f_ - _1_0

2155 a = b if _ispherical_f_(f_) or fabs(t) < EPS0 \

2156 or fabs(t - f_) < EPS0 else _over(b * f_, t)

2157 return Radius_(a=b if _ispherical_a_b(a, b) else a)

2158

2159

2160 def e2f(e):

2161 '''Return C{f}, the I{flattening} for a given I{1st eccentricity}.

2162

2163 @arg e: The (1st) eccentricity (0 <= C{float} < 1)

2164

2165 @return: The flattening (C{scalar} or C{0}).

2166

2167 @see: Function L{e22f}.

2168 '''

2169 return e22f(e**2)

2170

2171

2172 def e22f(e2):

2173 '''Return C{f}, the I{flattening} for a given I{1st eccentricity squared}.

2174

2175 @arg e2: The (1st) eccentricity I{squared}, I{signed} (L{NINF} < C{float} < 1)

2176

2177 @return: The flattening (C{float} or C{0}), M{e2 / (sqrt(1 - e2) + 1)}.

2178 '''

2179 return Float(f=_over(e2, sqrt(_1_0 - e2) + _1_0)) if e2 and e2 < _1_0 else _f_0_0

2180

2181

2182 def f2e2(f):

2183 '''Return C{e2}, the I{1st eccentricity squared} for a given I{flattening}.

2184

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

2186

2187 @return: The I{signed}, (1st) eccentricity I{squared} (C{float} < 1), M{f * (2 - f)}.

2188

2189 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0}

2190 for I{near-spherical} ellipsoids.

2191

2192 @see: U{Eccentricity conversions<https://GeographicLib.SourceForge.io/

2193 C++/doc/classGeographicLib_1_1Ellipsoid.html>} and U{Flattening

2194 <https://WikiPedia.org/wiki/Flattening>}.

2195 '''

2196 return Float(e2=_0_0 if _ispherical_f(f) else (f * (_2_0 - f)))

2197

2198

2199 def f2e22(f):

2200 '''Return C{e22}, the I{2nd eccentricity squared} for a given I{flattening}.

2201

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

2203

2204 @return: The I{signed}, 2nd eccentricity I{squared} (C{float} > -1 or C{INF}),

2205 M{f * (2 - f) / (1 - f)**2}.

2206

2207 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0}

2208 for near-spherical ellipsoids.

2209

2210 @see: U{Eccentricity conversions<https://GeographicLib.SourceForge.io/

2211 C++/doc/classGeographicLib_1_1Ellipsoid.html>}.

2212 '''

2213 # e2 / (1 - e2) == f * (2 - f) / (1 - f)**2

2214 t = (_1_0 - f)**2

2215 return Float(e22=INF if t < EPS0 else _over(f2e2(f), t)) # PYCHOK type

2216

2217

2218 def f2e32(f):

2219 '''Return C{e32}, the I{3rd eccentricity squared} for a given I{flattening}.

2220

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

2222

2223 @return: The I{signed}, 3rd eccentricity I{squared} (C{float}),

2224 M{f * (2 - f) / (1 + (1 - f)**2)}.

2225

2226 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0}

2227 for I{near-spherical} ellipsoids.

2228

2229 @see: U{Eccentricity conversions<https://GeographicLib.SourceForge.io/

2230 C++/doc/classGeographicLib_1_1Ellipsoid.html>}.

2231 '''

2232 # e2 / (2 - e2) == f * (2 - f) / (1 + (1 - f)**2)

2233 e2 = f2e2(f)

2234 return Float(e32=_over(e2, _2_0 - e2))

2235

2236

2237 def f_2f(f_):

2238 '''Return C{f}, the I{flattening} for a given I{inverse flattening}.

2239

2240 @arg f_: Inverse flattening (C{scalar} >>> 1).

2241

2242 @return: The flattening (C{scalar} or C{0}), M{1 / f_}.

2243

2244 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0}

2245 for I{near-spherical} ellipsoids.

2246 '''

2247 f = 0 if _ispherical_f_(f_) else _over(_1_0, f_)

2248 return _f_0_0 if _ispherical_f(f) else Float(f=f) # PYCHOK type

2249

2250

2251 def f2f_(f):

2252 '''Return C{f_}, the I{inverse flattening} for a given I{flattening}.

2253

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

2255

2256 @return: The inverse flattening (C{scalar} or C{0}), M{1 / f}.

2257

2258 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0}

2259 for I{near-spherical} ellipsoids.

2260 '''

2261 f_ = 0 if _ispherical_f(f) else _over(_1_0, f)

2262 return _f__0_0 if _ispherical_f_(f_) else Float(f_=f_) # PYCHOK type

2263

2264

2265 def f2f2(f):

2266 '''Return C{f2}, the I{2nd flattening} for a given I{flattening}.

2267

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

2269

2270 @return: The I{signed}, 2nd flattening (C{scalar} or C{INF}), M{f / (1 - f)}.

2271

2272 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0}

2273 for I{near-spherical} ellipsoids.

2274

2275 @see: U{Eccentricity conversions<https://GeographicLib.SourceForge.io/

2276 C++/doc/classGeographicLib_1_1Ellipsoid.html>} and U{Flattening

2277 <https://WikiPedia.org/wiki/Flattening>}.

2278 '''

2279 t = _1_0 - f

2280 return Float(f2=_0_0 if _ispherical_f(f) else

2281 (INF if fabs(t) < EPS else _over(f, t))) # PYCHOK type

2282

2283

2284 def f2n(f):

2285 '''Return C{n}, the I{3rd flattening} for a given I{flattening}.

2286

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

2288

2289 @return: The I{signed}, 3rd flattening (-1 <= C{float} < 1),

2290 M{f / (2 - f)}.

2291

2292 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0}

2293 for I{near-spherical} ellipsoids.

2294

2295 @see: U{Eccentricity conversions<https://GeographicLib.SourceForge.io/

2296 C++/doc/classGeographicLib_1_1Ellipsoid.html>} and U{Flattening

2297 <https://WikiPedia.org/wiki/Flattening>}.

2298 '''

2299 return Float(n=_0_0 if _ispherical_f(f) else _over(f, float(_2_0 - f)))

2300

2301

2302 def n2e2(n):

2303 '''Return C{e2}, the I{1st eccentricity squared} for a given I{3rd flattening}.

2304

2305 @arg n: The 3rd flattening (-1 <= C{scalar} < 1).

2306

2307 @return: The I{signed}, (1st) eccentricity I{squared} (C{float} or NINF),

2308 M{4 * n / (1 + n)**2}.

2309

2310 @note: The result is positive for I{oblate}, negative for I{prolate} or C{0}

2311 for I{near-spherical} ellipsoids.

2312

2313 @see: U{Flattening<https://WikiPedia.org/wiki/Flattening>}.

2314 '''

2315 t = (n + _1_0)**2

2316 return Float(e2=_0_0 if fabs(n) < EPS0 else

2317 (NINF if t < EPS0 else _over(_4_0 * n, t)))

2318

2319

2320 def n2f(n):

2321 '''Return C{f}, the I{flattening} for a given I{3rd flattening}.

2322

2323 @arg n: The 3rd flattening (-1 <= C{scalar} < 1).

2324

2325 @return: The flattening (C{scalar} or NINF), M{2 * n / (1 + n)}.

2326

2327 @see: U{Eccentricity conversions<https://GeographicLib.SourceForge.io/

2328 C++/doc/classGeographicLib_1_1Ellipsoid.html>} and U{Flattening

2329 <https://WikiPedia.org/wiki/Flattening>}.

2330 '''

2331 t = n + _1_0

2332 f = 0 if fabs(n) < EPS0 else (NINF if t < EPS0 else _over(_2_0 * n, t))

2333 return _f_0_0 if _ispherical_f(f) else Float(f=f)

2334

2335

2336 def n2f_(n):

2337 '''Return C{f_}, the I{inverse flattening} for a given I{3rd flattening}.

2338

2339 @arg n: The 3rd flattening (-1 <= C{scalar} < 1).

2340

2341 @return: The inverse flattening (C{scalar} or C{0}), M{1 / f}.

2342

2343 @see: L{n2f} and L{f2f_}.

2344 '''

2345 return f2f_(n2f(n))

2346

2347

2348 class Ellipsoids(_NamedEnum):

2349 '''(INTERNAL) L{Ellipsoid} registry, I{must} be a sub-class

2350 to accommodate the L{_LazyNamedEnumItem} properties.

2351 '''

2352 def _Lazy(self, a, b, f_, **kwds):

2353 '''(INTERNAL) Instantiate the L{Ellipsoid}.

2354 '''

2355 return Ellipsoid(a, b=b, f_=f_, **kwds)

2356

2357 Ellipsoids = Ellipsoids(Ellipsoid) # PYCHOK singleton

2358'''Some pre-defined L{Ellipsoid}s, all I{lazily} instantiated.'''

2359# <https://www.GNU.org/software/gama/manual/html_node/Supported-ellipsoids.html>

2360# <https://GSSC.ESA.int/navipedia/index.php/Reference_Frames_in_GNSS>

2361# <https://kb.OSU.edu/dspace/handle/1811/77986>

2362# <https://www.IBM.com/docs/en/db2/11.5?topic=systems-supported-spheroids>

2363# <https://w3.Energistics.org/archive/Epicentre/Epicentre_v3.0/DataModel/LogicalDictionary/StandardValues/ellipsoid.html>

2364# <https://GitHub.com/locationtech/proj4j/blob/master/src/main/java/org/locationtech/proj4j/datum/Ellipsoid.java>

2365 Ellipsoids._assert( # <https://WikiPedia.org/wiki/Earth_ellipsoid>

2366 Airy1830 = _lazy(_Airy1830_, *_T(6377563.396, _0_0, 299.3249646)), # b=6356256.909

2367 AiryModified = _lazy(_AiryModified_, *_T(6377340.189, _0_0, 299.3249646)), # b=6356034.448

2368# APL4_9 = _lazy('APL4_9', *_T(6378137.0, _0_0, 298.24985392)), # Appl. Phys. Lab. 1965

2369# ANS = _lazy('ANS', *_T(6378160.0, _0_0, 298.25)), # Australian Nat. Spheroid

2370# AN_SA96 = _lazy('AN_SA96', *_T(6378160.0, _0_0, 298.24985392)), # Australian Nat. South America

2371 Australia1966 = _lazy('Australia1966', *_T(6378160.0, _0_0, 298.25)), # b=6356774.7192

2372 ATS1977 = _lazy('ATS1977', *_T(6378135.0, _0_0, 298.257)), # "Average Terrestrial System"

2373 Bessel1841 = _lazy(_Bessel1841_, *_T(6377397.155, 6356078.962818, 299.152812797)),

2374 BesselModified = _lazy('BesselModified', *_T(6377492.018, _0_0, 299.1528128)),

2375# BesselNamibia = _lazy('BesselNamibia', *_T(6377483.865, _0_0, 299.1528128)),

2376 CGCS2000 = _lazy('CGCS2000', *_T(R_MA, _0_0, 298.257222101)), # BeiDou Coord System (BDC)

2377# Clarke1858 = _lazy('Clarke1858', *_T(6378293.639, _0_0, 294.260676369)),

2378 Clarke1866 = _lazy(_Clarke1866_, *_T(6378206.4, 6356583.8, 294.978698214)),

2379 Clarke1880 = _lazy('Clarke1880', *_T(6378249.145, 6356514.86954978, 293.465)),

2380 Clarke1880IGN = _lazy(_Clarke1880IGN_, *_T(6378249.2, 6356515.0, 293.466021294)),

2381 Clarke1880Mod = _lazy('Clarke1880Mod', *_T(6378249.145, 6356514.96639549, 293.466307656)), # aka Clarke1880Arc

2382 CPM1799 = _lazy('CPM1799', *_T(6375738.7, 6356671.92557493, 334.39)), # Comm. des Poids et Mesures

2383 Delambre1810 = _lazy('Delambre1810', *_T(6376428.0, 6355957.92616372, 311.5)), # Belgium

2384 Engelis1985 = _lazy('Engelis1985', *_T(6378136.05, 6356751.32272154, 298.2566)),

2385# Everest1830 = _lazy('Everest1830', *_T(6377276.345, _0_0, 300.801699997)),

2386# Everest1948 = _lazy('Everest1948', *_T(6377304.063, _0_0, 300.801699997)),

2387# Everest1956 = _lazy('Everest1956', *_T(6377301.243, _0_0, 300.801699997)),

2388 Everest1969 = _lazy('Everest1969', *_T(6377295.664, 6356094.667915, 300.801699997)),

2389 Everest1975 = _lazy('Everest1975', *_T(6377299.151, 6356098.14512013, 300.8017255)),

2390 Fisher1968 = _lazy('Fisher1968', *_T(6378150.0, 6356768.33724438, 298.3)),

2391# Fisher1968Mod = _lazy('Fisher1968Mod', *_T(6378155.0, _0_0, 298.3)),

2392 GEM10C = _lazy('GEM10C', *_T(R_MA, 6356752.31424783, 298.2572236)),

2393 GPES = _lazy('GPES', *_T(6378135.0, 6356750.0, _0_0)), # "Gen. Purpose Earth Spheroid"

2394 GRS67 = _lazy('GRS67', *_T(6378160.0, _0_0, 298.247167427)), # Lucerne b=6356774.516

2395# GRS67Truncated = _lazy('GRS67Truncated', *_T(6378160.0, _0_0, 298.25)),

2396 GRS80 = _lazy(_GRS80_, *_T(R_MA, 6356752.314140347, 298.25722210088)), # IUGG, ITRS, ETRS89

2397# Hayford1924 = _lazy('Hayford1924', *_T(6378388.0, 6356911.94612795, None)), # aka Intl1924 f_=297

2398 Helmert1906 = _lazy('Helmert1906', *_T(6378200.0, 6356818.16962789, 298.3)),

2399# Hough1960 = _lazy('Hough1960', *_T(6378270.0, _0_0, 297.0)),

2400 IAU76 = _lazy('IAU76', *_T(6378140.0, _0_0, 298.257)), # Int'l Astronomical Union

2401 IERS1989 = _lazy('IERS1989', *_T(6378136.0, _0_0, 298.257)), # b=6356751.302

2402 IERS1992TOPEX = _lazy('IERS1992TOPEX', *_T(6378136.3, 6356751.61659215, 298.257223563)), # IERS/TOPEX/Poseidon/McCarthy

2403 IERS2003 = _lazy('IERS2003', *_T(6378136.6, 6356751.85797165, 298.25642)),

2404 Intl1924 = _lazy(_Intl1924_, *_T(6378388.0, _0_0, 297.0)), # aka Hayford b=6356911.9462795

2405 Intl1967 = _lazy('Intl1967', *_T(6378157.5, 6356772.2, 298.24961539)), # New Int'l

2406 Krassovski1940 = _lazy(_Krassovski1940_, *_T(6378245.0, 6356863.01877305, 298.3)), # spelling

2407 Krassowsky1940 = _lazy(_Krassowsky1940_, *_T(6378245.0, 6356863.01877305, 298.3)), # spelling

2408# Kaula = _lazy('Kaula', *_T(6378163.0, _0_0, 298.24)), # Kaula 1961

2409# Lerch = _lazy('Lerch', *_T(6378139.0, _0_0, 298.257)), # Lerch 1979

2410 Maupertuis1738 = _lazy('Maupertuis1738', *_T(6397300.0, 6363806.28272251, 191.0)), # France

2411 Mercury1960 = _lazy('Mercury1960', *_T(6378166.0, 6356784.28360711, 298.3)),

2412 Mercury1968Mod = _lazy('Mercury1968Mod', *_T(6378150.0, 6356768.33724438, 298.3)),

2413# MERIT = _lazy('MERIT', *_T(6378137.0, _0_0, 298.257)), # MERIT 1983

2414# NWL10D = _lazy('NWL10D', *_T(6378135.0, _0_0, 298.26)), # Naval Weapons Lab.

2415 NWL1965 = _lazy('NWL1965', *_T(6378145.0, 6356759.76948868, 298.25)), # Naval Weapons Lab.

2416# NWL9D = _lazy('NWL9D', *_T(6378145.0, 6356759.76948868, 298.25)), # NWL1965

2417 OSU86F = _lazy('OSU86F', *_T(6378136.2, 6356751.51693008, 298.2572236)),

2418 OSU91A = _lazy('OSU91A', *_T(6378136.3, 6356751.6165948, 298.2572236)),

2419# Plessis1817 = _lazy('Plessis1817', *_T(6397523.0, 6355863.0, 153.56512242)), # XXX incorrect?

2420 Plessis1817 = _lazy('Plessis1817', *_T(6376523.0, 6355862.93325557, 308.64)), # XXX IGN France 1972

2421# Prolate = _lazy('Prolate', *_T(6356752.3, R_MA, _0_0)),

2422 PZ90 = _lazy('PZ90', *_T(6378136.0, _0_0, 298.257839303)), # GLOSNASS PZ-90 and PZ-90.11

2423# SEAsia = _lazy('SEAsia', *_T(6378155.0, _0_0, 298.3)), # SouthEast Asia

2424 SGS85 = _lazy('SGS85', *_T(6378136.0, 6356751.30156878, 298.257)), # Soviet Geodetic System

2425 SoAmerican1969 = _lazy('SoAmerican1969', *_T(6378160.0, 6356774.71919531, 298.25)), # South American

2426 Sphere = _lazy(_Sphere_, *_T(R_M, R_M, _0_0)), # pseudo

2427 SphereAuthalic = _lazy('SphereAuthalic', *_T(R_FM, R_FM, _0_0)), # pseudo

2428 SpherePopular = _lazy('SpherePopular', *_T(R_MA, R_MA, _0_0)), # EPSG:3857 Spheroid

2429 Struve1860 = _lazy('Struve1860', *_T(6378298.3, 6356657.14266956, 294.73)),

2430# Walbeck = _lazy('Walbeck', *_T(6376896.0, _0_0, 302.78)),

2431# WarOffice = _lazy('WarOffice', *_T(6378300.0, _0_0, 296.0)),

2432 WGS60 = _lazy('WGS60', *_T(6378165.0, 6356783.28695944, 298.3)),

2433 WGS66 = _lazy('WGS66', *_T(6378145.0, 6356759.76948868, 298.25)),

2434 WGS72 = _lazy(_WGS72_, *_T(6378135.0, _0_0, 298.26)), # b=6356750.52

2435 WGS84 = _lazy(_WGS84_, *_T(R_MA, _0_0, _f__WGS84)), # b=6356752.3142451793

2436# U{NOAA/NOS/NGS/inverse<https://GitHub.com/noaa-ngs/inverse/blob/main/invers3d.f>}

2437 WGS84_NGS = _lazy('WGS84_NGS', *_T(R_MA, _0_0, 298.257222100882711243162836600094))

2438)

2439

2440 _EWGS84 = Ellipsoids.WGS84 # (INTERNAL) shared

2441

2442 if __name__ == _DMAIN_:

2443

2444 from pygeodesy import nameof, printf

2445

2446 for E in (_EWGS84, Ellipsoids.GRS80, # NAD83,

2447 Ellipsoids.Sphere, Ellipsoids.SpherePopular,

2448 Ellipsoid(_EWGS84.b, _EWGS84.a, name='_Prolate')):

2449 e = f2n(E.f) - E.n

2450 printf('# %s: %s', _DOT_('Ellipsoids', E.name), E.toStr(prec=10, terse=0), nl=1)

2451 printf('# e=%s, f_=%s, f=%s, n=%s (%s)', fstr(E.e, prec=13, fmt=Fmt.e),

2452 fstr(E.f_, prec=13, fmt=Fmt.e),

2453 fstr(E.f, prec=13, fmt=Fmt.e),

2454 fstr(E.n, prec=13, fmt=Fmt.e),

2455 fstr(e, prec=9, fmt=Fmt.e))

2456 printf('# %s %s', Ellipsoid.AlphaKs.name, fstr(E.AlphaKs, prec=20))

2457 printf('# %s %s', Ellipsoid.BetaKs.name, fstr(E.BetaKs, prec=20))

2458 printf('# %s %s', nameof(Ellipsoid.KsOrder), E.KsOrder) # property

2459

2460 from pygeodesy.internals import _pregistry

2461 # __doc__ of this file, force all into registry

2462 _pregistry(Ellipsoids)

2463

2464# % python3.13 -m pygeodesy.ellipsoids

2465

2466# Ellipsoids.WGS84: name='WGS84', a=6378137, f=0.0033528107, f_=298.257223563, b=6356752.3142451793, f2=0.0033640898, n=0.0016792204, e=0.0818191908, e2=0.00669438, e21=0.99330562, e22=0.0067394967, e32=0.0033584313, A=6367449.1458234144, L=10001965.7293127235, R1=6371008.7714150595, R2=6371007.1809184738, R3=6371000.7900091587, Rbiaxial=6367453.6345163295, Rtriaxial=6372797.5559594007

2467# e=8.1819190842622e-02, f_=2.98257223563e+02, f=3.3528106647475e-03, n=1.6792203863837e-03 (0.0e+00)

2468# AlphaKs 0.00083773182062446994, 0.00000076085277735725, 0.00000000119764550324, 0.00000000000242917068, 0.00000000000000571182, 0.0000000000000000148, 0.00000000000000000004, 0.0

2469# BetaKs 0.00083773216405794875, 0.0000000590587015222, 0.00000000016734826653, 0.00000000000021647981, 0.00000000000000037879, 0.00000000000000000072, 0.0, 0.0

2470# KsOrder 8

2471

2472# Ellipsoids.GRS80: name='GRS80', a=6378137, f=0.0033528107, f_=298.2572221009, b=6356752.3141403468, f2=0.0033640898, n=0.0016792204, e=0.081819191, e2=0.00669438, e21=0.99330562, e22=0.0067394968, e32=0.0033584313, A=6367449.1457710434, L=10001965.7292304561, R1=6371008.7713801153, R2=6371007.1808835147, R3=6371000.7899741363, Rbiaxial=6367453.6344640013, Rtriaxial=6372797.5559332585

2473# e=8.1819191042833e-02, f_=2.9825722210088e+02, f=3.3528106811837e-03, n=1.6792203946295e-03 (0.0e+00)

2474# AlphaKs 0.00083773182472890429, 0.00000076085278481561, 0.00000000119764552086, 0.00000000000242917073, 0.00000000000000571182, 0.0000000000000000148, 0.00000000000000000004, 0.0

2475# BetaKs 0.0008377321681623882, 0.00000005905870210374, 0.000000000167348269, 0.00000000000021647982, 0.00000000000000037879, 0.00000000000000000072, 0.0, 0.0

2476# KsOrder 8

2477

2478# Ellipsoids.Sphere: name='Sphere', a=6371008.7714149999, f=0, f_=0, b=6371008.7714149999, f2=0, n=0, e=0, e2=0, e21=1, e22=0, e32=0, A=6371008.7714149999, L=10007557.1761167478, R1=6371008.7714149999, R2=6371008.7714149999, R3=6371008.7714149999, Rbiaxial=6371008.7714149999, Rtriaxial=6371008.7714149999

2479# e=0.0e+00, f_=0.0e+00, f=0.0e+00, n=0.0e+00 (0.0e+00)

2480# AlphaKs 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0

2481# BetaKs 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0

2482# KsOrder 8

2483

2484# Ellipsoids.SpherePopular: name='SpherePopular', a=6378137, f=0, f_=0, b=6378137, f2=0, n=0, e=0, e2=0, e21=1, e22=0, e32=0, A=6378137, L=10018754.171394622, R1=6378137, R2=6378137, R3=6378137, Rbiaxial=6378137, Rtriaxial=6378137

2485# e=0.0e+00, f_=0.0e+00, f=0.0e+00, n=0.0e+00 (0.0e+00)

2486# AlphaKs 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0

2487# BetaKs 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0

2488# KsOrder 8

2489

2490# Ellipsoids._Prolate: name='_Prolate', a=6356752.3142451793, f=-0.0033640898, f_=-297.257223563, b=6378137, f2=-0.0033528107, n=-0.0016792204, e=0.0820944379, e2=-0.0067394967, e21=1.0067394967, e22=-0.00669438, e32=-0.0033584313, A=6367449.1458234144, L=10035500.5204500332, R1=6363880.5428301189, R2=6363878.9413582645, R3=6363872.5644020075, Rbiaxial=6367453.6345163295, Rtriaxial=6362105.2243882557

2491# e=8.2094437949696e-02, f_=-2.97257223563e+02, f=-3.3640898209765e-03, n=-1.6792203863837e-03 (0.0e+00)

2492# AlphaKs -0.00084149152514366627, 0.00000076653480614871, -0.00000000120934503389, 0.0000000000024576225, -0.00000000000000578863, 0.00000000000000001502, -0.00000000000000000004, 0.0

2493# BetaKs -0.00084149187224351817, 0.00000005842735196773, -0.0000000001680487236, 0.00000000000021706261, -0.00000000000000038002, 0.00000000000000000073, -0.0, 0.0

2494# KsOrder 8

2495

2496# **) MIT License

2497#

2498# Copyright (C) 2016-2026 -- mrJean1 at Gmail -- All Rights Reserved.

2499#

2500# Permission is hereby granted, free of charge, to any person obtaining a

2501# copy of this software and associated documentation files (the "Software"),

2502# to deal in the Software without restriction, including without limitation

2503# the rights to use, copy, modify, merge, publish, distribute, sublicense,

2504# and/or sell copies of the Software, and to permit persons to whom the

2505# Software is furnished to do so, subject to the following conditions:

2506#

2507# The above copyright notice and this permission notice shall be included

2508# in all copies or substantial portions of the Software.

2509#

2510# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS

2511# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

2512# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL

2513# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR

2514# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,

2515# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR

2516# OTHER DEALINGS IN THE SOFTWARE.