pygeodesy 25.11.5__py2.py3-none-any.whl → 25.12.31__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. pygeodesy/__init__.py +46 -25
  2. pygeodesy/__main__.py +1 -1
  3. pygeodesy/albers.py +1 -1
  4. pygeodesy/angles.py +960 -0
  5. pygeodesy/auxilats/_CX_4.py +1 -1
  6. pygeodesy/auxilats/_CX_6.py +1 -1
  7. pygeodesy/auxilats/_CX_8.py +1 -1
  8. pygeodesy/auxilats/_CX_Rs.py +1 -1
  9. pygeodesy/auxilats/__init__.py +2 -2
  10. pygeodesy/auxilats/__main__.py +1 -1
  11. pygeodesy/auxilats/auxAngle.py +7 -8
  12. pygeodesy/auxilats/auxDLat.py +1 -1
  13. pygeodesy/auxilats/auxDST.py +1 -1
  14. pygeodesy/auxilats/auxLat.py +1 -1
  15. pygeodesy/auxilats/auxily.py +1 -1
  16. pygeodesy/azimuthal.py +6 -5
  17. pygeodesy/basics.py +14 -10
  18. pygeodesy/booleans.py +1 -1
  19. pygeodesy/cartesianBase.py +7 -7
  20. pygeodesy/clipy.py +1 -1
  21. pygeodesy/constants.py +29 -24
  22. pygeodesy/css.py +1 -1
  23. pygeodesy/datums.py +1 -1
  24. pygeodesy/deprecated/__init__.py +1 -1
  25. pygeodesy/deprecated/bases.py +1 -1
  26. pygeodesy/deprecated/classes.py +14 -7
  27. pygeodesy/deprecated/consterns.py +1 -1
  28. pygeodesy/deprecated/datum.py +1 -1
  29. pygeodesy/deprecated/functions.py +1 -1
  30. pygeodesy/deprecated/nvector.py +1 -1
  31. pygeodesy/deprecated/rhumbBase.py +1 -1
  32. pygeodesy/deprecated/rhumbaux.py +1 -1
  33. pygeodesy/deprecated/rhumbsolve.py +1 -1
  34. pygeodesy/deprecated/rhumbx.py +1 -1
  35. pygeodesy/dms.py +1 -1
  36. pygeodesy/ecef.py +1 -1
  37. pygeodesy/ecefLocals.py +1 -1
  38. pygeodesy/elevations.py +1 -1
  39. pygeodesy/ellipsoidalBase.py +1 -1
  40. pygeodesy/ellipsoidalBaseDI.py +1 -1
  41. pygeodesy/ellipsoidalExact.py +1 -1
  42. pygeodesy/ellipsoidalGeodSolve.py +1 -1
  43. pygeodesy/ellipsoidalKarney.py +1 -1
  44. pygeodesy/ellipsoidalNvector.py +1 -1
  45. pygeodesy/ellipsoidalVincenty.py +1 -1
  46. pygeodesy/ellipsoids.py +30 -17
  47. pygeodesy/elliptic.py +1 -1
  48. pygeodesy/epsg.py +1 -1
  49. pygeodesy/errors.py +8 -4
  50. pygeodesy/etm.py +1 -1
  51. pygeodesy/fmath.py +19 -14
  52. pygeodesy/formy.py +251 -10
  53. pygeodesy/frechet.py +1 -1
  54. pygeodesy/fstats.py +1 -1
  55. pygeodesy/fsums.py +41 -29
  56. pygeodesy/gars.py +1 -1
  57. pygeodesy/geod3solve.py +489 -0
  58. pygeodesy/geodesici.py +9 -8
  59. pygeodesy/geodesicw.py +1 -1
  60. pygeodesy/geodesicx/_C4_24.py +1 -1
  61. pygeodesy/geodesicx/_C4_27.py +1 -1
  62. pygeodesy/geodesicx/_C4_30.py +1 -1
  63. pygeodesy/geodesicx/__init__.py +2 -2
  64. pygeodesy/geodesicx/__main__.py +1 -1
  65. pygeodesy/geodesicx/gx.py +1 -1
  66. pygeodesy/geodesicx/gxarea.py +54 -24
  67. pygeodesy/geodesicx/gxbases.py +1 -1
  68. pygeodesy/geodesicx/gxline.py +1 -1
  69. pygeodesy/geodsolve.py +73 -104
  70. pygeodesy/geohash.py +1 -1
  71. pygeodesy/geoids.py +1 -1
  72. pygeodesy/hausdorff.py +1 -1
  73. pygeodesy/heights.py +1 -1
  74. pygeodesy/internals.py +1 -1
  75. pygeodesy/interns.py +3 -3
  76. pygeodesy/iters.py +1 -1
  77. pygeodesy/karney.py +152 -151
  78. pygeodesy/ktm.py +1 -1
  79. pygeodesy/latlonBase.py +1 -1
  80. pygeodesy/lazily.py +24 -13
  81. pygeodesy/lcc.py +1 -1
  82. pygeodesy/ltp.py +1 -1
  83. pygeodesy/ltpTuples.py +1 -1
  84. pygeodesy/mgrs.py +3 -3
  85. pygeodesy/named.py +15 -10
  86. pygeodesy/namedTuples.py +1 -1
  87. pygeodesy/nvectorBase.py +1 -1
  88. pygeodesy/osgr.py +1 -1
  89. pygeodesy/points.py +1 -1
  90. pygeodesy/props.py +6 -4
  91. pygeodesy/resections.py +1 -1
  92. pygeodesy/rhumb/__init__.py +8 -6
  93. pygeodesy/rhumb/aux_.py +1 -1
  94. pygeodesy/rhumb/bases.py +1 -1
  95. pygeodesy/rhumb/ekx.py +1 -1
  96. pygeodesy/rhumb/solve.py +91 -84
  97. pygeodesy/simplify.py +1 -1
  98. pygeodesy/solveBase.py +72 -49
  99. pygeodesy/sphericalBase.py +1 -1
  100. pygeodesy/sphericalNvector.py +1 -1
  101. pygeodesy/sphericalTrigonometry.py +1 -1
  102. pygeodesy/streprs.py +6 -4
  103. pygeodesy/trf.py +2 -4
  104. pygeodesy/triaxials/__init__.py +70 -0
  105. pygeodesy/triaxials/bases.py +966 -0
  106. pygeodesy/triaxials/conformal3.py +617 -0
  107. pygeodesy/triaxials/triaxial3.py +968 -0
  108. pygeodesy/{triaxials.py → triaxials/triaxial5.py} +353 -781
  109. pygeodesy/units.py +1 -1
  110. pygeodesy/unitsBase.py +1 -1
  111. pygeodesy/ups.py +2 -3
  112. pygeodesy/utily.py +17 -14
  113. pygeodesy/utm.py +1 -1
  114. pygeodesy/utmups.py +1 -1
  115. pygeodesy/utmupsBase.py +1 -1
  116. pygeodesy/vector2d.py +1 -1
  117. pygeodesy/vector3d.py +1 -1
  118. pygeodesy/vector3dBase.py +1 -1
  119. pygeodesy/webmercator.py +1 -1
  120. pygeodesy/wgrs.py +1 -1
  121. {pygeodesy-25.11.5.dist-info → pygeodesy-25.12.31.dist-info}/METADATA +28 -21
  122. pygeodesy-25.12.31.dist-info/RECORD +125 -0
  123. pygeodesy-25.11.5.dist-info/RECORD +0 -119
  124. {pygeodesy-25.11.5.dist-info → pygeodesy-25.12.31.dist-info}/WHEEL +0 -0
  125. {pygeodesy-25.11.5.dist-info → pygeodesy-25.12.31.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,966 @@
1
+
2
+ # -*- coding: utf-8 -*-
3
+
4
+ u'''(INTERNAL) Base classes for I{ordered} triaxial ellipsoid classes L{Conformal}, L{Conformal3},
5
+ L{Triaxial}, L{Triaxial3} and I{unordered} L{Triaxial_}.
6
+
7
+ Transcoded to pure Python from I{Karney}'s GeographicLib 2.7 C++ classes U{Ellipsoid3<https://
8
+ GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Triaxial_1_1Ellipsoid3.html>},
9
+ U{Cartesian3<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Triaxial_1_1Cartesian3.html>} and
10
+ U{Conformal3<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Triaxial_1_1Conformal3.html>}.
11
+
12
+ GeographicLib 2.5.2 C++ class U{JacobiConformal<https://GeographicLib.SourceForge.io/C++/doc/
13
+ classGeographicLib_1_1JacobiConformal.html#details>}.
14
+
15
+ Copyright (C) U{Charles Karney<mailto:Karney@Alum.MIT.edu>} (2008-2024, 2025) and licensed under the MIT/X11 License.
16
+ For more information, see the U{GeographicLib 2.5.2 and 2.7<https://GeographicLib.SourceForge.io/>} documentation.
17
+
18
+ Enum-like C{Lat-/Longitude Kinds (LLK)}, see I{Karney}'s U{coord<https://GeographicLib.SourceForge.io/
19
+ C++/doc/classGeographicLib_1_1Triaxial_1_1Cartesian3.html>}:
20
+
21
+ @var LLK.CONFORMAL: Jacobi conformal X and Y projection
22
+ @var LLK.ELLIPSOIDAL: Ellipsoidal lat-, longitude and heading C{bet}, C{omg}, C{alp} (L{Ang})
23
+ @var LLK.GEOCENTRIC: Geocentric lat-, longitude and heading C{phi}", C{lam}" and C{zet} (L{Ang})
24
+ @var LLK.GEOCENTRIC_X: Geocentric with pole along major X axis
25
+ @var LLK.GEODETIC: Geodetic lat-, longitude and heading C{phi}, C{lam} and C{zet} (L{Ang})
26
+ @var LLK.GEODETIC_X: Geodetic with pole along major X axis
27
+ @var LLK.GEODETIC_LON0: Geodetic lat-, longitude I{- lon0} and heading C{phi}, C{lam} and C{zet} (L{Ang})
28
+ @var LLK.GEOGRAPHIC = LLK.GEODETIC
29
+ @var LLK.PARAMETRIC: Parametric lat-, longitude and heading C{phi}', C{lam}' and C{zet} (L{Ang})
30
+ @var LLK.PARAMETRIC_X: Parametric with pole along major X axis
31
+ @var LLK.PLANETODETIC = LLK.GEODETIC
32
+ @var LLK.PLANETOCENTRIC = LLK.GEOCENTRIC
33
+ '''
34
+ # make sure int/int division yields float quotient, see .basics
35
+ from __future__ import division as _; del _ # noqa: E702 ;
36
+
37
+ # from pygeodesy.angles import Ang, isAng # _MODS
38
+ from pygeodesy.basics import map1, isscalar
39
+ from pygeodesy.constants import EPS, EPS0, EPS02, EPS4, _EPS2e4, INT0, NAN, PI2, PI_3, PI4, \
40
+ _isfinite, float0_, _0_0, _1_0, _N_1_0, _4_0 # PYCHOK used!
41
+ # from pygeodesy.ellipsoids import Ellipsoid # _MODS
42
+ # from pygeodesy.elliptic import Elliptic # _MODS
43
+ # from pygeodesy.errors import _ValueError, _xkwds # from .formy
44
+ from pygeodesy.fmath import fmean_, hypot, norm2, sqrt0, fabs, sqrt
45
+ from pygeodesy.formy import elliperim, _ValueError, _xkwds
46
+ from pygeodesy.fsums import _Fsumf_, fsumf_, fsum1f_
47
+ # from pygeodesy.internals import typename # _MODS
48
+ from pygeodesy.interns import _a_, _b_, _c_, _inside_, _not_, _NOTEQUAL_, _null_, \
49
+ _outside_, _scale_, _SPACE_, _spherical_, _x_, _y_, _z_
50
+ from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS, _FOR_DOCS
51
+ from pygeodesy.named import _NamedEnumItem, _NamedTuple, _Pass
52
+ from pygeodesy.namedTuples import Vector4Tuple
53
+ from pygeodesy.props import Property_RO, property_doc_, property_RO, property_ROver
54
+ # from pygeodesy.streprs import Fmt # _MODS
55
+ from pygeodesy.units import Degrees, Easting, Float, Height, Height_, Meter2, Meter3, \
56
+ Northing, Radius_, Scalar
57
+ from pygeodesy.utily import asin1
58
+ from pygeodesy.vector3d import _otherV3d, Vector3d
59
+
60
+ # from math import fabs, sqrt # from .fmath
61
+
62
+ __all__ = _ALL_LAZY.triaxials_bases
63
+ __version__ = '25.12.31'
64
+
65
+ _bet_ = 'bet' # PYCHOK shared
66
+ _llk_ = 'llk' # PYCHOK shared
67
+ _MAXIT = 33 # 20 # PYCHOK shared
68
+ _not_ordered_ = _not_('ordered')
69
+ _omg_ = 'omg' # PYCHOK shared
70
+
71
+
72
+ class Conformal5Tuple(_NamedTuple): # see .Forward4Tuple
73
+ '''5-Tuple C{(x, y, z, scale, llk)} with the easting C{x} and
74
+ northing C{y} projection, C{scale} or C{NAN} I{but with}
75
+ C{z=INT0} I{and kind} C{llk=LLK.CONFORMAL} I{always}.
76
+ '''
77
+ _Names_ = (_x_, _y_, _z_, _scale_, _llk_)
78
+ _Units_ = ( Easting, Northing, _Pass, Scalar, _Pass)
79
+
80
+ def __new__(cls, x, y, z=INT0, scale=NAN, llk=None, **kwds): # **iteration_name
81
+ args = x, y, (z or INT0), scale, (llk or LLK.CONFORMAL)
82
+ return _NamedTuple.__new__(cls, args, **kwds)
83
+
84
+
85
+ class _LLK(str):
86
+ '''(INTERNAL) Lat-/Longitude Kind.
87
+ '''
88
+ def __init__(self, llk): # aka C++ alt
89
+ self._X = bool(llk.endswith('_X'))
90
+ str.__init__(llk)
91
+
92
+
93
+ class LLK(object):
94
+ '''Enum-like C{Lat-/Longitude Kinds (LLK)}, see U{coord<https://GeographicLib.
95
+ SourceForge.io/C++/doc/classGeographicLib_1_1Triaxial_1_1Cartesian3.html>}.
96
+ '''
97
+ CONFORMAL = _LLK('CONFORMAL')
98
+
99
+ ELLIPSOIDAL = _LLK('ELLIPSOIDAL') # bet, omg, alp
100
+ GEOCENTRIC = _LLK('GEOCENTRIC') # phi2p, lam2p, zet
101
+ GEOCENTRIC_X = _LLK('GEOCENTRIC_X')
102
+ GEODETIC = _LLK('GEODETIC') # phi, lam, zet
103
+ GEODETIC_LON0 = _LLK('GEODETIC_LON0')
104
+ GEODETIC_X = _LLK('GEODETIC_X')
105
+ GEOGRAPHIC = GEODETIC
106
+ PARAMETRIC = _LLK('PARAMETRIC') # phi1p, lam1p, zet
107
+ PARAMETRIC_X = _LLK('PARAMETRIC_X')
108
+ PLANETODETIC = GEODETIC
109
+ PLANETOCENTRIC = GEOCENTRIC
110
+
111
+ _CENTRICS = (GEOCENTRIC, GEOCENTRIC_X, PLANETOCENTRIC)
112
+ _DETICS = (GEODETIC, GEODETIC_X, GEODETIC_LON0, GEOGRAPHIC, PLANETODETIC)
113
+ _METRICS = (PARAMETRIC, PARAMETRIC_X)
114
+ _NOIDAL = (None, ELLIPSOIDAL)
115
+ # _XCLUDE = (CONFORMAL, GEOGRAPHIC, PLANETOCENTRIC, PLANETODETIC)
116
+
117
+ def __getitem__(self, name):
118
+ llk = self.get(name, None)
119
+ if llk is None:
120
+ t = _MODS.internals.typename(self)
121
+ t = _MODS.streprs.Fmt.SQUARE(t, name)
122
+ raise _ValueError(t, name)
123
+ return llk
124
+
125
+ def get(self, name, dflt=None):
126
+ '''Get an C{LLK} by C{name}.
127
+ '''
128
+ llk = getattr(self, name, None)
129
+ return llk if isinstance(llk, _LLK) else dflt
130
+
131
+ def items(self):
132
+ '''Yield all C{LLK (name, value)} pairs.
133
+ '''
134
+ for n, llk in LLK.__class__.__dict__.items():
135
+ if isinstance(llk, _LLK):
136
+ yield n, llk
137
+
138
+ def keys(self):
139
+ '''Yield all C{LLK} names.
140
+ '''
141
+ for n, _ in self.items():
142
+ yield n
143
+
144
+ def values(self):
145
+ '''Yield all C{LLK} values.
146
+ '''
147
+ for _, llk in self.items():
148
+ yield llk
149
+
150
+ if not _FOR_DOCS: # PYCHOK force epydoc
151
+ LLK = LLK() # singleton
152
+ del _FOR_DOCS
153
+
154
+
155
+ def _HeightINT0(h):
156
+ return h if h is INT0 else Height(h=h)
157
+
158
+
159
+ class _UnOrderedTriaxialBase(_NamedEnumItem):
160
+ '''(INTERNAL) Base class for all I{unordered} triaxial classes.
161
+ '''
162
+ _ijk = _kji = None
163
+ _unordered = True
164
+
165
+ def __init__(self, a_triaxial, b=None, c=None, **name):
166
+ '''New I{unordered} C{Triaxial_}.
167
+
168
+ @arg a_triaxial: Large, C{X} semi-axis (C{scalar}, conventionally in
169
+ C{meter}) or an other L{Triaxial}, L{Triaxial_} or
170
+ L{TriaxialB} instance.
171
+ @kwarg b: Middle, C{Y} semi-axis (C{meter}, same units as B{C{a}}),
172
+ required if C{B{a_triaxial} is scalar}, ignored otherwise.
173
+ @kwarg c: Small, C{Z} semi-axis (C{meter}, like B{C{b}}).
174
+ @kwarg name: Optional C{B{name}=NN} (C{str}).
175
+
176
+ @raise TriaxialError: Invalid semi-axis or -axes.
177
+ '''
178
+ try:
179
+ try:
180
+ a = a_triaxial
181
+ t = a._abc3
182
+ name = _xkwds(name, name=a.name)
183
+ except AttributeError:
184
+ t = Radius_(a=a), Radius_(b=b), Radius_(c=c)
185
+ except (TypeError, ValueError) as x:
186
+ raise TriaxialError(a=a, b=b, c=c, cause=x)
187
+ if name:
188
+ self.name = name
189
+
190
+ a, b, c = self._abc3 = t
191
+ if self._unordered: # == not isinstance(self, Triaxial)
192
+ s, _, t = sorted(t)
193
+ if not (_isfinite(t) and _isfinite(s) and s > 0):
194
+ raise TriaxialError(a=a, b=b, c=c) # txt=_invalid_
195
+ elif not (_isfinite(a) and a >= b >= c > 0): # see TriaxialB
196
+ raise TriaxialError(a=a, b=b, c=c, txt=_not_ordered_)
197
+ elif not (a > c and self._a2c2 > 0 and self.e2ac > 0):
198
+ raise TriaxialError(a=a, c=c, e2ac=self.e2ac, txt=_spherical_)
199
+
200
+ def __str__(self):
201
+ return self.toStr()
202
+
203
+ @Property_RO
204
+ def a(self):
205
+ '''Get the C{largest, x} semi-axis (C{meter}, conventionally).
206
+ '''
207
+ a, _, _ = self._abc3
208
+ return a
209
+
210
+ @Property_RO
211
+ def a2(self):
212
+ '''Get C{a**2}.
213
+ '''
214
+ return self.a**2
215
+
216
+ @Property_RO
217
+ def _a2b2(self):
218
+ '''(INTERNAL) Get C{a**2 - b**2} == E_sub_e**2.
219
+ '''
220
+ a, b, _ = self._abc3
221
+ d = a - b
222
+ return (d * (a + b)) if d else _0_0
223
+
224
+ @Property_RO
225
+ def _a2_b2(self):
226
+ '''(INTERNAL) Get C{(a / b)**2}.
227
+ '''
228
+ a, b, _ = self._abc3
229
+ return (a / b)**2 if a != b else _1_0
230
+
231
+ @Property_RO
232
+ def _a2b2c23(self):
233
+ '''(INTERNAL) Get 3-tuple C{(a**2, b**2, c**2)}.
234
+ '''
235
+ a, b, c = self._abc3
236
+ return self.a2, self.b2, self.c2
237
+
238
+ @Property_RO
239
+ def _a2c2(self):
240
+ '''(INTERNAL) Get C{a**2 - c**2} == E_sub_x**2.
241
+ '''
242
+ a, _, c = self._abc3
243
+ d = a - c
244
+ return (d * (a + c)) if d else _0_0
245
+
246
+ @Property_RO
247
+ def area(self):
248
+ '''Get the surface area (C{meter} I{squared}).
249
+ '''
250
+ c, b, a = sorted(self._abc3)
251
+ return _OrderedTriaxialBase(a, b, c).area if a > c else \
252
+ Meter2(area=self.a2 * PI4) # a == c == b
253
+
254
+ def area_p(self, p=1.6075):
255
+ '''I{Approximate} the surface area (C{meter} I{squared}).
256
+
257
+ @kwarg p: Exponent (C{scalar} > 0), 1.6 for near-spherical or 1.5849625007
258
+ for "near-flat" triaxials.
259
+
260
+ @see: U{Surface area<https://WikiPedia.org/wiki/Ellipsoid#Approximate_formula>}.
261
+ '''
262
+ a, b, c = self._abc3
263
+ if a == b == c:
264
+ a *= a
265
+ else:
266
+ _p = pow
267
+ a = _p(fmean_(_p(a * b, p), _p(a * c, p), _p(b * c, p)), _1_0 / p)
268
+ return Meter2(area_p=a * PI4)
269
+
270
+ @Property_RO
271
+ def b(self):
272
+ '''Get the C{middle, y} semi-axis (C{meter}, same units as B{C{a}}).
273
+ '''
274
+ _, b, _ = self._abc3
275
+ return b
276
+
277
+ @Property_RO
278
+ def b2(self):
279
+ '''Get C{b**2}.
280
+ '''
281
+ return self.b**2
282
+
283
+ @Property_RO
284
+ def _b2_a2(self):
285
+ '''(INTERNAL) Get C{(b / a)**2}.
286
+ '''
287
+ a, b, _ = self._abc3
288
+ return (b / a)**2 if a != b else _1_0
289
+
290
+ @Property_RO
291
+ def _b2c2(self):
292
+ '''(INTERNAL) Get C{b**2 - c**2} == E_sub_y**2.
293
+ '''
294
+ _, b, c = self._abc3
295
+ d = b - c
296
+ return (d * (b + c)) if d else _0_0
297
+
298
+ @Property_RO
299
+ def c(self):
300
+ '''Get the C{smallest, z} semi-axis (C{meter}, same units as B{C{a}}).
301
+ '''
302
+ _, _, c = self._abc3
303
+ return c
304
+
305
+ @Property_RO
306
+ def c2(self):
307
+ '''Get C{c**2}.
308
+ '''
309
+ return self.c**2
310
+
311
+ @Property_RO
312
+ def _c2_a2(self):
313
+ '''(INTERNAL) Get C{(c / a)**2}.
314
+ '''
315
+ a, _, c = self._abc3
316
+ return (c / a)**2 if a != c else _1_0
317
+
318
+ @Property_RO
319
+ def _c2_b2(self):
320
+ '''(INTERNAL) Get C{(c / b)**2}.
321
+ '''
322
+ _, b, c = self._abc3
323
+ return (c / b)**2 if b != c else _1_0
324
+
325
+ @Property_RO
326
+ def e2ab(self):
327
+ '''Get the C{ab} ellipse' I{(1st) eccentricity squared} (C{scalar}), M{1 - (b/a)**2}.
328
+ '''
329
+ return Float(e2ab=(_1_0 - self._b2_a2) or _0_0)
330
+
331
+ # _1e2ab = _b2_a2 # == C{1 - e2ab} == C{(b/a)**2}
332
+
333
+ @Property_RO
334
+ def e2ac(self):
335
+ '''Get the C{ac} ellipse' I{(1st) eccentricity squared} (C{scalar}), M{1 - (c/a)**2}.
336
+ '''
337
+ return Float(e2ac=(_1_0 - self._c2_a2) or _0_0)
338
+
339
+ # _1e2ac = _c2_a2 # == C{1 - e2ac} == C{(c/a)**2}
340
+
341
+ @Property_RO
342
+ def e2bc(self):
343
+ '''Get the C{bc} ellipse' I{(1st) eccentricity squared} (C{scalar}), M{1 - (c/b)**2}.
344
+ '''
345
+ return Float(e2bc=(_1_0 - self._c2_b2) or _0_0)
346
+
347
+ # _1e2bc = _c2_b2 # == C{1 - e2bc} == C{(c/b)**2}
348
+
349
+ @property_ROver
350
+ def _Ellipsoid(self):
351
+ '''(INTERNAL) Get class L{Ellipsoid}, I{once}.
352
+ '''
353
+ return _MODS.ellipsoids.Ellipsoid # overwrite property_ROver
354
+
355
+ @property_ROver
356
+ def _Elliptic(self):
357
+ '''(INTERNAL) Get class L{Elliptic}, I{once}.
358
+ '''
359
+ return _MODS.elliptic.Elliptic # overwrite property_ROver
360
+
361
+ def hartzell4(self, pov, los=False, **name):
362
+ '''Compute the intersection of this triaxial's surface with a Line-Of-Sight
363
+ from a Point-Of-View in space.
364
+
365
+ @see: Function L{hartzell4<triaxials.triaxial5.hartzell4>} for further details.
366
+ '''
367
+ return self._triaxials_triaxial5.hartzell4(pov, los=los, tri_biax=self, **name)
368
+
369
+ def height4(self, x_xyz, y=None, z=None, normal=True, eps=EPS, **name):
370
+ '''Compute the projection on and the height above or below this triaxial's surface.
371
+
372
+ @see: Function L{height4<triaxials.triaxial5.height4>} for further details.
373
+ '''
374
+ m = self._triaxials_triaxial5
375
+ return m.height4(x_xyz, y=y, z=z, tri_biax=self, normal=normal, eps=eps, **name)
376
+
377
+ @Property_RO
378
+ def isOrdered(self):
379
+ '''Is this triaxial I{ordered} and I{not spherical} (C{bool})?
380
+ '''
381
+ a, b, c = self._abc3
382
+ return bool(a >= b > c) # b > c!
383
+
384
+ @Property_RO
385
+ def isSpherical(self):
386
+ '''Is this triaxial I{spherical} (C{Radius} or INT0)?
387
+ '''
388
+ a, b, c = self._abc3
389
+ return a if a == b == c else INT0
390
+
391
+ def _norm2(self, s, c, *a):
392
+ '''(INTERNAL) Normalize C{s} and C{c} iff not already.
393
+ '''
394
+ if fabs(_hypot2_1(s, c)) > EPS02:
395
+ s, c = norm2(s, c)
396
+ if a:
397
+ s, c = norm2(s * self.b, c * a[0])
398
+ return float0_(s, c)
399
+
400
+ def normal3d(self, x_xyz, y=None, z=None, length=_1_0):
401
+ '''Get a 3-D vector I{on and perpendicular to} this triaxial's surface.
402
+
403
+ @arg x_xyz: X component (C{scalar}) or a cartesian (C{Cartesian},
404
+ L{Ecef9Tuple}, L{Vector3d}, L{Vector3Tuple} or L{Vector4Tuple}).
405
+ @kwarg y: Y component (C{scalar}), required if B{C{x_xyz}} if C{scalar}, ignored
406
+ otherwise.
407
+ @kwarg z: Z component (C{scalar}), like B{C{y}}.
408
+ @kwarg length: Optional, signed length in out-/inward direction (C{scalar}).
409
+
410
+ @return: A C{Vector3d(x_, y_, z_)} normalized to B{C{length}}, pointing out-
411
+ or inward for postive respectively negative B{C{length}}.
412
+
413
+ @raise TriaxialError: Zero length cartesian or vector.
414
+
415
+ @note: Cartesian C{(B{x}, B{y}, B{z})} I{must be on} this triaxial's surface,
416
+ use method L{Triaxial.sideOf} to validate.
417
+
418
+ @see: Methods L{Triaxial.height4} and L{Triaxial.sideOf}.
419
+ '''
420
+ # n = 2 * (x / a2, y / b2, z / c2)
421
+ # == 2 * (x, y * a2 / b2, z * a2 / c2) / a2 # iff ordered
422
+ # == 2 * (x, y / _b2_a2, z / _c2_a2) / a2
423
+ # == unit(x, y / _b2_a2, z / _c2_a2).times(length)
424
+ x, y, z = _otherV3d_(x_xyz, y, z).xyz3
425
+ n = Vector3d(x, y / self._b2_a2,
426
+ z / self._c2_a2, name__=self.normal3d)
427
+ u = n.length
428
+ if u < EPS0:
429
+ raise TriaxialError(x=x_xyz, y=y, z=z, txt=_null_)
430
+ return n.times(length / u)
431
+
432
+ def normal4(self, x_xyz, y=None, z=None, height=0, normal=True):
433
+ '''Compute a cartesian at a height above or below this triaxial's surface.
434
+
435
+ @arg x_xyz: X component (C{scalar}) or a cartesian (C{Cartesian}, L{Ecef9Tuple},
436
+ L{Vector3d}, L{Vector3Tuple} or L{Vector4Tuple}).
437
+ @kwarg y: Y component (C{scalar}), required if B{C{x_xyz}} if C{scalar}, ignored
438
+ otherwise.
439
+ @kwarg z: Z component (C{scalar}), like B{C{y}}.
440
+ @kwarg height: The signed height (C{scalar}, same units as the triaxial axes).
441
+ @kwarg normal: If C{True}, the B{C{height}} is I{perpendicular, plumb} to the
442
+ triaxial's surface, otherwise C{radially} to the center of this
443
+ triaxial (C{bool}).
444
+ @kwarg name: Optional C{B{name}="normal4"} (C{str}).
445
+
446
+ @return: L{Vector4Tuple}C{(x, y, z, h)} with the cartesian coordinates C{x},
447
+ C{y} and C{z} and C{h} the I{signed, normal distance} to the triaxial's
448
+ surface in C{meter}, conventionally. Positive C{h} indicates, the
449
+ cartesian is outside the triaxial, negative C{h} means inside.
450
+
451
+ @raise TriaxialError: Zero length cartesian or vector.
452
+
453
+ @note: Cartesian C{(B{x}, B{y}, B{z})} I{must be on} this triaxial's surface,
454
+ use method L{Triaxial.sideOf} to validate.
455
+
456
+ @see: Methods L{Triaxial.normal3d} and L{Triaxial.height4}.
457
+ '''
458
+ v, h = _otherV3d(x_xyz, y, z), Height_(height, low=None)
459
+ if h:
460
+ if v.length < EPS0:
461
+ raise TriaxialError(x=x_xyz, y=y, z=z, txt=_null_)
462
+ if normal:
463
+ n = self.normal3d(v, length=h)
464
+ h = n.length
465
+ n += v
466
+ else:
467
+ h = h / v.length
468
+ n = v.times(h + _1_0)
469
+ else:
470
+ n = v
471
+ return Vector4Tuple(n.x, n.y, n.z, h, name__=self.normal4)
472
+
473
+ def _order3(self, *abc, **reverse): # reverse=False
474
+ '''(INTERNAL) Un-/Order C{a}, C{b} and C{c}.
475
+
476
+ @return: 3-Tuple C{(a, b, c)} ordered by or un-ordered
477
+ (reverse-ordered) C{ijk} if C{B{reverse}=True}.
478
+ '''
479
+ ijk = self._order_ijk(**reverse)
480
+ return _getitems(abc, *ijk) if ijk else abc
481
+
482
+ def _order3d(self, v, **reverse): # reverse=False
483
+ '''(INTERNAL) Un-/Order a C{Vector3d}.
484
+
485
+ @return: Vector3d(x, y, z) un-/ordered.
486
+ '''
487
+ ijk = self._order_ijk(**reverse)
488
+ return v.classof(*_getitems(v.xyz3, *ijk)) if ijk else v
489
+
490
+ @Property_RO
491
+ def _ordered4(self):
492
+ '''(INTERNAL) Helper for C{_hartzell3} and C{_plumbTo5}.
493
+ '''
494
+ def _order2(reverse, a, b, c):
495
+ '''(INTERNAL) Un-Order C{a}, C{b} and C{c}.
496
+
497
+ @return: 2-Tuple C{((a, b, c), ijk)} with C{a} >= C{b} >= C{c}
498
+ and C{ijk} a 3-tuple with the initial indices.
499
+ '''
500
+ i, j, k = range(3)
501
+ if a < b:
502
+ a, b, i, j = b, a, j, i
503
+ if a < c:
504
+ a, c, i, k = c, a, k, i
505
+ if b < c:
506
+ b, c, j, k = c, b, k, j
507
+ # reverse (k, j, i) since (a, b, c) is reversed-sorted
508
+ ijk = (k, j, i) if reverse else (None if i < j < k else (i, j, k))
509
+ return (a, b, c), ijk
510
+
511
+ abc, T = self._abc3, self
512
+ if not self.isOrdered:
513
+ abc, ijk = _order2(False, *abc)
514
+ if ijk:
515
+ _, kji = _order2(True, *ijk)
516
+ T = _UnOrderedTriaxialBase(*abc)
517
+ T._ijk, T._kji = ijk, kji
518
+ return abc + (T,)
519
+
520
+ def _order_ijk(self, reverse=False):
521
+ '''(INTERNAL) Get the un-/order indices.
522
+ '''
523
+ return self._kji if reverse else self._ijk
524
+
525
+ @Property_RO
526
+ def perimeter4ab(self):
527
+ '''Get the C{ab} ellipse' perimeter (C{scalar}).
528
+ '''
529
+ a, b, _ = self._abc3
530
+ return Float(perimeter4ab=elliperim(a, b))
531
+
532
+ @Property_RO
533
+ def perimeter4ac(self):
534
+ '''Get the C{ac} ellipse' perimeter (C{scalar}).
535
+ '''
536
+ a, _, c = self._abc3
537
+ return Float(perimeter4ac=elliperim(a, c))
538
+
539
+ @Property_RO
540
+ def perimeter4bc(self):
541
+ '''Get the C{bc} ellipse' perimeter (C{scalar}).
542
+ '''
543
+ _, b, c = self._abc3
544
+ return Float(perimeter4bc=elliperim(b, c))
545
+
546
+ def _radialTo3(self, sbeta, cbeta, somega, comega):
547
+ '''(INTERNAL) I{Unordered} helper for C{.height4}.
548
+ '''
549
+ def _rphi(a, b, sphi, cphi):
550
+ # <https://WikiPedia.org/wiki/Ellipse#Polar_form_relative_to_focus>
551
+ # polar form: radius(phi) = a * b / hypot(a * sphi, b * cphi)
552
+ return (b / hypot(sphi, b / a * cphi)) if a > b else (
553
+ (a / hypot(cphi, a / b * sphi)) if a < b else a)
554
+
555
+ sa, ca = self._norm2(sbeta, cbeta)
556
+ sb, cb = self._norm2(somega, comega)
557
+
558
+ a, b, c = self._abc3
559
+ if a != b:
560
+ a = _rphi(a, b, sb, cb)
561
+ if a != c:
562
+ c = _rphi(a, c, sa, ca)
563
+ t = c * ca
564
+ return (t * cb), (t * sb), (c * sa)
565
+
566
+ def sideOf(self, x_xyz, y=None, z=None, eps=EPS4):
567
+ '''Is a cartesian on, above or below the surface of this triaxial?
568
+
569
+ @arg x_xyz: X component (C{scalar}) or a cartesian (C{Cartesian},
570
+ L{Ecef9Tuple}, L{Vector3d}, L{Vector3Tuple} or L{Vector4Tuple}).
571
+ @kwarg y: Y component (C{scalar}), required if B{C{x_xyz}} is C{scalar},
572
+ ignored otherwise.
573
+ @kwarg z: Z component (C{scalar}), like B{C{y}}.
574
+ @kwarg eps: On-surface tolerance (C{scalar}, distance I{squared}).
575
+
576
+ @return: C{INT0} if C{(B{x}, B{y}, B{z})} is near this triaxial's surface
577
+ within tolerance B{C{eps}}, otherwise the signed, radial distance
578
+ I{squared} (C{float}), nega-/positive for in- respectively outside
579
+ this triaxial.
580
+
581
+ @see: Methods L{Triaxial.height4} and L{Triaxial.normal3d}.
582
+ '''
583
+ v = _otherV3d_(x_xyz, y, z)
584
+ s = fsumf_(_N_1_0, *map(_over02, v.xyz3, self._abc3))
585
+ return INT0 if fabs(s) < eps else s
586
+
587
+ def _sideOn(self, v, eps=_EPS2e4):
588
+ s = self.sideOf(v.xyz, eps=eps)
589
+ if s: # PYCHOK no cover
590
+ t = _SPACE_((_inside_ if s < 0 else _outside_), self.toRepr())
591
+ raise TriaxialError(eps=eps, sideOf=s, x=v.x, y=v.y, z=v.z, txt=t)
592
+ return s
593
+
594
+ def toEllipsoid(self, **name):
595
+ '''Convert this triaxial to a I{biaxial} L{Ellipsoid}, provided 2 axes match.
596
+
597
+ @kwarg name: Optional C{B{name}=NN} (C{str}).
598
+
599
+ @return: An L{Ellipsoid} with north along this C{Z} axis if C{a == b},
600
+ this C{Y} axis if C{a == c} or this C{X} axis if C{b == c}.
601
+
602
+ @raise TriaxialError: This C{a != b}, C{b != c} and C{c != a}.
603
+
604
+ @see: Method L{Ellipsoid.toTriaxial}.
605
+ '''
606
+ a, b, c = self._abc3
607
+ if a == b:
608
+ b = c # N = c-Z
609
+ elif b == c: # N = a-X
610
+ a, b = b, a
611
+ elif a != c: # N = b-Y
612
+ t = _SPACE_(_a_, _NOTEQUAL_, _b_, _NOTEQUAL_, _c_)
613
+ raise TriaxialError(a=a, b=b, c=c, txt=t)
614
+ return self._Ellipsoid(a, b=b, name=self._name__(name))
615
+
616
+ toBiaxial = toEllipsoid
617
+
618
+ def toStr(self, prec=9, **name): # PYCHOK signature
619
+ '''Return this C{Triaxial} as a string.
620
+
621
+ @kwarg prec: Precision, number of decimal digits (0..9).
622
+ @kwarg name: Optional name (C{str}), to override or C{None}
623
+ to exclude this triaxial's name.
624
+
625
+ @return: This C{Triaxial}'s attributes (C{str}).
626
+ '''
627
+ T = _UnOrderedTriaxialBase
628
+ C = self._triaxials_triaxial3.Triaxial3B
629
+ if isinstance(self, C):
630
+ t = T.b, C.e2, C.k2, C.kp2
631
+ else:
632
+ t = T.a, # props
633
+ C = self._triaxials_triaxial5.ConformalSphere
634
+ t += (C.ab, C.bc) if isinstance(self, C) else (T.b, T.c)
635
+ C = _Triaxial3Base
636
+ t += (C.k2, C.kp2) if isinstance(self, C) else \
637
+ (T.e2ab, T.e2bc, T.e2ac)
638
+ for C in (self._triaxials_triaxial5.Conformal,
639
+ self._triaxials_conformal3.Conformal3):
640
+ if isinstance(self, C):
641
+ t += C.xyQ2,
642
+ break
643
+ t += T.volume, T.area
644
+ return self._instr(area_p=self.area_p(), prec=prec, props=t, **name)
645
+
646
+ @property_ROver
647
+ def _triaxials_conformal3(self):
648
+ '''(INTERNAL) Get module L{pygeodesy.triaxials.conformal3}, I{once}.
649
+ '''
650
+ return _MODS.triaxials.conformal3 # overwrite property_ROver
651
+
652
+ @property_ROver
653
+ def _triaxials_triaxial3(self):
654
+ '''(INTERNAL) Get module L{pygeodesy.triaxials.triaxial3}, I{once}.
655
+ '''
656
+ return _MODS.triaxials.triaxial3 # overwrite property_ROver
657
+
658
+ @property_ROver
659
+ def _triaxials_triaxial5(self):
660
+ '''(INTERNAL) Get module L{pygeodesy.triaxials.triaxial5}, I{once}.
661
+ '''
662
+ return _MODS.triaxials.triaxial5 # overwrite property_ROver
663
+
664
+ @Property_RO
665
+ def unOrdered(self):
666
+ '''Is this triaxial I{un-ordered} and I{not spherical} (C{bool})?
667
+ '''
668
+ return not (self.isOrdered or bool(self.isSpherical))
669
+
670
+ @Property_RO
671
+ def volume(self):
672
+ '''Get the volume (C{meter**3}), M{4 / 3 * PI * a * b * c}.
673
+ '''
674
+ a, b, c = self._abc3
675
+ return Meter3(volume=a * b * c * PI_3 * _4_0)
676
+
677
+
678
+ class _OrderedTriaxialBase(_UnOrderedTriaxialBase):
679
+ '''(INTERNAL) Base class for all I{ordered} triaxial classes.
680
+ '''
681
+ _unordered = False
682
+
683
+ def __init__(self, a_triaxial, b=None, c=None, **name):
684
+ '''New I{ordered} L{Triaxial}, L{Triaxial3}, L{Conformal} or L{Conformal3}.
685
+
686
+ @arg a_triaxial: Largest semi-axis (C{scalar}, conventionally in C{meter})
687
+ or an other L{Triaxial} or L{Triaxial_} instance.
688
+ @kwarg b: Middle semi-axis (C{meter}, same units as B{C{a}}), required
689
+ if C{B{a_triaxial} is scalar}, ignored otherwise.
690
+ @kwarg c: Smallest semi-axis (C{meter}, like B{C{b}}).
691
+ @kwarg name: Optional C{B{name}=NN} (C{str}).
692
+
693
+ @note: The semi-axes must be ordered as C{B{a} >= B{b} >= B{c} > 0} and
694
+ must be ellipsoidal, C{B{a} > B{c}}.
695
+
696
+ @raise TriaxialError: Semi-axes unordered, spherical or invalid.
697
+ '''
698
+ _UnOrderedTriaxialBase.__init__(self, a_triaxial, b=b, c=c, **name)
699
+
700
+ @Property_RO
701
+ def _a2b2_a2c2(self):
702
+ '''@see: Methods C{.forwardBetaOmega} and property C{._k2E_kp2E}.
703
+ '''
704
+ s = self._a2c2
705
+ if s:
706
+ s = self._a2b2 / s
707
+ return s or _0_0
708
+
709
+ @Property_RO
710
+ def area(self):
711
+ '''Get the surface area (C{meter} I{squared}).
712
+
713
+ @see: U{Surface area<https://WikiPedia.org/wiki/Ellipsoid#Surface_area>}.
714
+ '''
715
+ a, b, c = self._abc3
716
+ if a != b:
717
+ kp2, k2 = self._k2E_kp2E # swapped!
718
+ aE = self._Elliptic(k2, _0_0, kp2, _1_0)
719
+ c2 = self._c2_a2 # cos(phi)**2 = (c/a)**2
720
+ s = sqrt(self.e2ac) # sin(phi)**2 = 1 - c2
721
+ r = asin1(s) # phi = atan2(sqrt(c2), s)
722
+ b *= fsum1f_(aE.fE(r) * s, (c / a) * (c / b),
723
+ aE.fF(r) * c2 / s)
724
+ a = Meter2(area=a * b * PI2)
725
+ else: # a == b > c
726
+ a = self._Ellipsoid(a, b=c).areax
727
+ return a
728
+
729
+ @Property_RO
730
+ def _k2E_kp2E(self):
731
+ '''(INTERNAL) Get elliptic C{k2} and C{kp2} for C{._xE}, C{._yE} and C{.area}.
732
+ '''
733
+ # k2 = a2b2 / a2c2 * c2_b2
734
+ # kp2 = b2c2 / a2c2 * a2_b2
735
+ # b2 = b**2
736
+ # xE = Elliptic(k2, -a2b2 / b2, kp2, a2_b2)
737
+ # yE = Elliptic(kp2, +b2c2 / b2, k2, c2_b2)
738
+ # aE = Elliptic(kp2, 0, k2, 1)
739
+ k2 = (self._c2_b2 * self._a2b2_a2c2) or _0_0
740
+ kp2 = (self._a2_b2 * self._b2c2 / self._a2c2) if k2 else _1_0
741
+ return k2, kp2
742
+
743
+ def _radialTo3(self, sbeta, cbeta, somega, comega):
744
+ '''(INTERNAL) Convert I{ellipsoidal} lat- C{beta} and longitude
745
+ C{omega} to a cartesian I{on this triaxial's surface}, also
746
+ I{ordered} helper for C{.height4 with normal=False}.
747
+ '''
748
+ sa, ca = self._norm2(sbeta, cbeta)
749
+ sb, cb = self._norm2(somega, comega)
750
+
751
+ b2_a2 = self._b2_a2 # == (b/a)**2
752
+ c2_a2 = -self._c2_a2 # == -(c/a)**2
753
+ a2c2_a2 = self. e2ac # (a**2 - c**2) / a**2 == 1 - (c/a)**2
754
+
755
+ x2 = _Fsumf_(_1_0, -b2_a2 * sa**2, c2_a2 * ca**2).fover(a2c2_a2)
756
+ z2 = _Fsumf_(c2_a2, sb**2, b2_a2 * cb**2).fover(a2c2_a2)
757
+
758
+ x, y, z = self._abc3
759
+ x *= cb * _sqrt0(x2)
760
+ y *= ca * sb
761
+ z *= sa * _sqrt0(z2)
762
+ return x, y, z
763
+
764
+
765
+ class _Triaxial3Base(_OrderedTriaxialBase):
766
+ '''(INTERNAL) Base class for I{unordered} triaxialC{3} classes.
767
+ '''
768
+ _e2_k2_kp2 = None
769
+ _Lon0 = None
770
+
771
+ @Property_RO
772
+ def e2(self):
773
+ '''Get the I{squared eccentricity} (C{scalar}), M{(a**2 - c**2) / b**2}.
774
+ '''
775
+ if self._e2_k2_kp2:
776
+ e2, _, _ = self._e2_k2_kp2
777
+ else:
778
+ e2 = self._a2c2 / self.b2
779
+ return Float(e2=e2)
780
+
781
+ def _init_abc3_e2_k2_kp2(self, b, e2, k2, kp2, **name):
782
+ '''(INTERNAL) C{Triaxial3B.__init__}.
783
+ '''
784
+ if name:
785
+ self.name = name
786
+ s = k2 + kp2
787
+ if s > 0 and s != _1_0:
788
+ k2 = k2 / s # /= chokes PyChecker
789
+ kp2 = kp2 / s
790
+ if min(e2, k2, kp2) < 0 or not s > 0:
791
+ raise TriaxialError(e2=e2, k2=k2, kp2=kp2)
792
+ if e2:
793
+ a = Radius_(a=_sqrt0(_1_0 + e2 * kp2) * b) if kp2 else b
794
+ c = Radius_(c=_sqrt0(_1_0 - e2 * k2) * b) if k2 else b
795
+ else: # spherical
796
+ a = c = b
797
+ if not (_isfinite(b) and a >= b >= c > 0):
798
+ raise TriaxialError(b=b, a=a, c=c, e2=e2,
799
+ k2=k2, kp2=kp2, txt=_not_ordered_)
800
+ self._abc3 = a, b, c
801
+ self._e2_k2_kp2 = e2, k2, kp2
802
+
803
+ @property_RO
804
+ def isBiaxial(self):
805
+ '''Is this triaxial I{biaxial} (C{bool}), C{a} == C{b} or C{b} == C{c}?
806
+ '''
807
+ return self.isOblate or self.isProlate
808
+
809
+ @property_RO
810
+ def isOblate(self):
811
+ '''Is this triaxial I{oblate} (C{bool}), C{a} == C{b}?
812
+ '''
813
+ return bool(self.kp2 == 0)
814
+
815
+ @property_RO
816
+ def isProlate(self):
817
+ '''Is this triaxial I{prolate} (C{bool}), C{b} == C{c}?
818
+ '''
819
+ return bool(self.k2 == 0)
820
+
821
+ @Property_RO
822
+ def _k_kp(self):
823
+ '''(INTERNAL) Get the oblate C{k} and prolate C{kp} parameters.
824
+ '''
825
+ return map1(_sqrt0, *self._k2_kp2)
826
+
827
+ @Property_RO
828
+ def k2(self):
829
+ '''(INTERNAL) Get the oblate C{k2} parameter I{squared}.
830
+ '''
831
+ k2, _ = self._k2_kp2
832
+ return k2
833
+
834
+ @Property_RO
835
+ def _k2_kp2(self):
836
+ '''(INTERNAL) Get the oblate C{k2} and prolate C{kp2} parameters I{squared}.
837
+ '''
838
+ if self._e2_k2_kp2:
839
+ _, k2, kp2 = self._e2_k2_kp2
840
+ else:
841
+ s = self._a2c2
842
+ k2 = (self._b2c2 / s) if s else _1_0
843
+ kp2 = (self._a2b2 / s) if s else _0_0
844
+ return k2, kp2
845
+
846
+ @Property_RO
847
+ def kp2(self):
848
+ '''(INTERNAL) Get the prolate C{kp2} parameter I{squared}.
849
+ '''
850
+ _, kp2 = self._k2_kp2
851
+ return kp2
852
+
853
+ @Property_RO
854
+ def _lcc23(self):
855
+ return self._a2c2, self._b2c2, _0_0
856
+
857
+ @property_doc_(" longitude of the I{earth}'s major semi-axis C{a}, (L{Ang}), Karney's C{Triaxial_Earth_lon0}.")
858
+ def Lon0(self):
859
+ if self._Lon0 is None:
860
+ WGS84_3 = self.name.startswith('WGS84_3')
861
+ self.Lon0 = -(1493 / 100) if WGS84_3 else 0
862
+ return self._Lon0
863
+
864
+ @Lon0.setter # PYCHOK setter!
865
+ def Lon0(self, lon0):
866
+ m = _MODS.angles
867
+ d = lon0.degrees if m.isAng(lon0) else Degrees(lon0)
868
+ n = _Triaxial3Base.Lon0.name
869
+ self._Lon0 = m.Ang.fromDegrees(d, name=n)
870
+
871
+ @Property_RO
872
+ def _xE(self):
873
+ '''(INTERNAL) Get the x-elliptic function.
874
+ '''
875
+ return self._xyE(self.e2, self.k2, self.kp2)
876
+
877
+ def _xyE(self, e2, k2, kp2):
878
+ '''(INTERNAL) Helper for C{._xE} and C{._yE}.
879
+ '''
880
+ if e2:
881
+ a2 = -kp2 * e2
882
+ ap2 = _1_0 - a2
883
+ kp2 *= _1_0 - k2 * e2
884
+ k2 *= ap2
885
+ else:
886
+ a2, ap2 = _0_0, _1_0
887
+ return self._Elliptic(kp2, a2, k2, ap2)
888
+
889
+ @Property_RO
890
+ def _yE(self):
891
+ '''(INTERNAL) Get the y-elliptic function.
892
+ '''
893
+ return self._xyE(-self.e2, self.kp2, self.k2)
894
+
895
+
896
+ class TriaxialError(_ValueError):
897
+ '''Raised for any triaxial issue.
898
+ '''
899
+ pass # ...
900
+
901
+
902
+ def _getitems(items, *indices):
903
+ '''(INTERNAL) Get the C{items} at the given I{indices}.
904
+
905
+ @return: C{Type(items[i] for i in indices)} with
906
+ C{Type = type(items)}, any C{type} having
907
+ the special method C{__getitem__}.
908
+ '''
909
+ return type(items)(map(items.__getitem__, indices))
910
+
911
+
912
+ def _hypot2_1(x, y, z=0):
913
+ '''(INTERNAL) Compute M{x**2 + y**2 + z**2 - 1} with C{max(fabs(x), fabs(y),
914
+ fabs(z))} rarely greater than 1.0.
915
+ '''
916
+ return fsumf_(_N_1_0, x*x, y*y, z*z)
917
+
918
+
919
+ def _otherV3d_(x_xyz, y, z, **name):
920
+ '''(INTERNAL) Get a Vector3d from C{x_xyz}, C{y} and C{z}.
921
+ '''
922
+ return Vector3d(x_xyz, y, z, **name) if isscalar(x_xyz) else \
923
+ _otherV3d(x_xyz=x_xyz, **name)
924
+
925
+
926
+ def _over0(p, q):
927
+ '''(INTERNAL) Return C{p / q} or C{0}.
928
+ '''
929
+ return (p / q) if q > fabs(p) else _0_0
930
+
931
+
932
+ def _over02(p, q):
933
+ '''(INTERNAL) Return C{(p / q)**2} or C{0}.
934
+ '''
935
+ return (p / q)**2 if p and q else _0_0
936
+
937
+
938
+ def _sqrt0(x):
939
+ '''(INTERNAL) C{sqrt0} with C{TriaxialError}.
940
+ '''
941
+ return sqrt0(x, Error=TriaxialError)
942
+
943
+
944
+ __all__ += _ALL_DOCS(_OrderedTriaxialBase, _Triaxial3Base, _UnOrderedTriaxialBase)
945
+
946
+ # **) MIT License
947
+ #
948
+ # Copyright (C) 2025-2026 -- mrJean1 at Gmail -- All Rights Reserved.
949
+ #
950
+ # Permission is hereby granted, free of charge, to any person obtaining a
951
+ # copy of this software and associated documentation files (the "Software"),
952
+ # to deal in the Software without restriction, including without limitation
953
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
954
+ # and/or sell copies of the Software, and to permit persons to whom the
955
+ # Software is furnished to do so, subject to the following conditions:
956
+ #
957
+ # The above copyright notice and this permission notice shall be included
958
+ # in all copies or substantial portions of the Software.
959
+ #
960
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
961
+ # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
962
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
963
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
964
+ # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
965
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
966
+ # OTHER DEALINGS IN THE SOFTWARE.