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