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.
- pygeodesy/__init__.py +25 -12
- pygeodesy/__main__.py +1 -1
- pygeodesy/albers.py +1 -1
- pygeodesy/angles.py +960 -0
- pygeodesy/auxilats/_CX_4.py +1 -1
- pygeodesy/auxilats/_CX_6.py +1 -1
- pygeodesy/auxilats/_CX_8.py +1 -1
- pygeodesy/auxilats/_CX_Rs.py +1 -1
- pygeodesy/auxilats/__init__.py +2 -2
- pygeodesy/auxilats/__main__.py +1 -1
- pygeodesy/auxilats/auxAngle.py +7 -8
- pygeodesy/auxilats/auxDLat.py +1 -1
- pygeodesy/auxilats/auxDST.py +1 -1
- pygeodesy/auxilats/auxLat.py +1 -1
- pygeodesy/auxilats/auxily.py +1 -1
- pygeodesy/azimuthal.py +6 -5
- pygeodesy/basics.py +14 -10
- pygeodesy/booleans.py +1 -1
- pygeodesy/cartesianBase.py +7 -7
- pygeodesy/clipy.py +1 -1
- pygeodesy/constants.py +27 -24
- pygeodesy/css.py +1 -1
- pygeodesy/datums.py +1 -1
- pygeodesy/deprecated/__init__.py +1 -1
- pygeodesy/deprecated/bases.py +1 -1
- pygeodesy/deprecated/classes.py +14 -7
- pygeodesy/deprecated/consterns.py +1 -1
- pygeodesy/deprecated/datum.py +1 -1
- pygeodesy/deprecated/functions.py +1 -1
- pygeodesy/deprecated/nvector.py +1 -1
- pygeodesy/deprecated/rhumbBase.py +1 -1
- pygeodesy/deprecated/rhumbaux.py +1 -1
- pygeodesy/deprecated/rhumbsolve.py +1 -1
- pygeodesy/deprecated/rhumbx.py +1 -1
- pygeodesy/dms.py +1 -1
- pygeodesy/ecef.py +1 -1
- pygeodesy/ecefLocals.py +1 -1
- pygeodesy/elevations.py +1 -1
- pygeodesy/ellipsoidalBase.py +1 -1
- pygeodesy/ellipsoidalBaseDI.py +1 -1
- pygeodesy/ellipsoidalExact.py +1 -1
- pygeodesy/ellipsoidalGeodSolve.py +1 -1
- pygeodesy/ellipsoidalKarney.py +1 -1
- pygeodesy/ellipsoidalNvector.py +1 -1
- pygeodesy/ellipsoidalVincenty.py +1 -1
- pygeodesy/ellipsoids.py +7 -6
- pygeodesy/elliptic.py +1 -1
- pygeodesy/epsg.py +1 -1
- pygeodesy/errors.py +8 -4
- pygeodesy/etm.py +1 -1
- pygeodesy/fmath.py +15 -8
- pygeodesy/formy.py +107 -5
- pygeodesy/frechet.py +1 -1
- pygeodesy/fstats.py +1 -1
- pygeodesy/fsums.py +1 -1
- pygeodesy/gars.py +1 -1
- pygeodesy/geod3solve.py +488 -0
- pygeodesy/geodesici.py +4 -4
- pygeodesy/geodesicw.py +1 -1
- pygeodesy/geodesicx/_C4_24.py +1 -1
- pygeodesy/geodesicx/_C4_27.py +1 -1
- pygeodesy/geodesicx/_C4_30.py +1 -1
- pygeodesy/geodesicx/__init__.py +1 -1
- pygeodesy/geodesicx/__main__.py +1 -1
- pygeodesy/geodesicx/gx.py +1 -1
- pygeodesy/geodesicx/gxarea.py +1 -1
- pygeodesy/geodesicx/gxbases.py +1 -1
- pygeodesy/geodesicx/gxline.py +1 -1
- pygeodesy/geodsolve.py +70 -102
- pygeodesy/geohash.py +1 -1
- pygeodesy/geoids.py +1 -1
- pygeodesy/hausdorff.py +1 -1
- pygeodesy/heights.py +1 -1
- pygeodesy/internals.py +1 -1
- pygeodesy/interns.py +3 -3
- pygeodesy/iters.py +1 -1
- pygeodesy/karney.py +132 -116
- pygeodesy/ktm.py +1 -1
- pygeodesy/latlonBase.py +1 -1
- pygeodesy/lazily.py +23 -12
- pygeodesy/lcc.py +1 -1
- pygeodesy/ltp.py +1 -1
- pygeodesy/ltpTuples.py +1 -1
- pygeodesy/mgrs.py +3 -3
- pygeodesy/named.py +14 -9
- pygeodesy/namedTuples.py +1 -1
- pygeodesy/nvectorBase.py +1 -1
- pygeodesy/osgr.py +1 -1
- pygeodesy/points.py +1 -1
- pygeodesy/props.py +1 -1
- pygeodesy/resections.py +1 -1
- pygeodesy/rhumb/__init__.py +8 -6
- pygeodesy/rhumb/aux_.py +1 -1
- pygeodesy/rhumb/bases.py +1 -1
- pygeodesy/rhumb/ekx.py +1 -1
- pygeodesy/rhumb/solve.py +91 -84
- pygeodesy/simplify.py +1 -1
- pygeodesy/solveBase.py +72 -49
- pygeodesy/sphericalBase.py +1 -1
- pygeodesy/sphericalNvector.py +1 -1
- pygeodesy/sphericalTrigonometry.py +1 -1
- pygeodesy/streprs.py +6 -4
- pygeodesy/trf.py +1 -1
- pygeodesy/triaxials/__init__.py +70 -0
- pygeodesy/triaxials/bases.py +935 -0
- pygeodesy/triaxials/conformal3.py +617 -0
- pygeodesy/triaxials/triaxial3.py +969 -0
- pygeodesy/{triaxials.py → triaxials/triaxial5.py} +353 -781
- pygeodesy/units.py +1 -1
- pygeodesy/unitsBase.py +1 -1
- pygeodesy/ups.py +2 -3
- pygeodesy/utily.py +17 -14
- pygeodesy/utm.py +1 -1
- pygeodesy/utmups.py +1 -1
- pygeodesy/utmupsBase.py +1 -1
- pygeodesy/vector2d.py +1 -1
- pygeodesy/vector3d.py +1 -1
- pygeodesy/vector3dBase.py +1 -1
- pygeodesy/webmercator.py +1 -1
- pygeodesy/wgrs.py +1 -1
- {pygeodesy-25.11.5.dist-info → pygeodesy-25.12.12.dist-info}/METADATA +12 -12
- pygeodesy-25.12.12.dist-info/RECORD +125 -0
- pygeodesy-25.11.5.dist-info/RECORD +0 -119
- {pygeodesy-25.11.5.dist-info → pygeodesy-25.12.12.dist-info}/WHEEL +0 -0
- {pygeodesy-25.11.5.dist-info → pygeodesy-25.12.12.dist-info}/top_level.txt +0 -0
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
|
|
4
|
-
u'''Triaxal ellipsoid classes
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
and L{TriaxialError}.
|
|
4
|
+
u'''Triaxal ellipsoid classes L{Triaxial} and I{unordered} L{Triaxial_} and Jacobi conformal projections
|
|
5
|
+
L{Conformal} and L{ConformalSphere}, transcoded from I{Karney}'s GeographicLib 2.5.2 C++ class U{JacobiConformal
|
|
6
|
+
<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1JacobiConformal.html#details>} to pure
|
|
7
|
+
Python and miscellaneous classes L{BetaOmega2Tuple}, L{BetaOmega3Tuple} and L{Conformal2Tuple}, I{all kept
|
|
8
|
+
for backward copability}.
|
|
10
9
|
|
|
11
|
-
Copyright (C) U{Charles Karney<mailto:Karney@Alum.MIT.edu>} (2008-2024)
|
|
12
|
-
see the U{GeographicLib<https://GeographicLib.SourceForge.io>}
|
|
10
|
+
Copyright (C) U{Charles Karney<mailto:Karney@Alum.MIT.edu>} (2008-2024) and licensed under the MIT/X11
|
|
11
|
+
License. For more information, see the U{GeographicLib 2.5.2<https://GeographicLib.SourceForge.io>}
|
|
12
|
+
I{experimental} documentation.
|
|
13
13
|
|
|
14
14
|
@see: U{Geodesics on a triaxial ellipsoid<https://WikiPedia.org/wiki/Geodesics_on_an_ellipsoid#
|
|
15
15
|
Geodesics_on_a_triaxial_ellipsoid>} and U{Triaxial coordinate systems and their geometrical
|
|
16
|
-
interpretation<https://
|
|
16
|
+
interpretation<https://OLD.Topo.Auth.GR/wp-content/uploads/sites/111/2021/12/09_Panou.pdf>}.
|
|
17
17
|
|
|
18
18
|
@var Triaxials.Amalthea: Triaxial(name='Amalthea', a=125000, b=73000, c=64000, e2ab=0.658944, e2bc=0.231375493, e2ac=0.737856, volume=2446253479595252, area=93239507787.490371704, area_p=93212299402.670425415)
|
|
19
19
|
@var Triaxials.Ariel: Triaxial(name='Ariel', a=581100, b=577900, c=577700, e2ab=0.01098327, e2bc=0.000692042, e2ac=0.011667711, volume=812633172614203904, area=4211301462766.580078125, area_p=4211301574065.829589844)
|
|
@@ -26,46 +26,48 @@ see the U{GeographicLib<https://GeographicLib.SourceForge.io>} 2.5.2 documentati
|
|
|
26
26
|
@var Triaxials.Miranda: Triaxial(name='Miranda', a=240400, b=234200, c=232900, e2ab=0.050915557, e2bc=0.011070811, e2ac=0.061422691, volume=54926187094835456, area=698880863325.757080078, area_p=698881306767.950317383)
|
|
27
27
|
@var Triaxials.Moon: Triaxial(name='Moon', a=1735550, b=1735324, c=1734898, e2ab=0.000260419, e2bc=0.000490914, e2ac=0.000751206, volume=21886698675223740416, area=37838824729886.09375, area_p=37838824733332.21875)
|
|
28
28
|
@var Triaxials.Tethys: Triaxial(name='Tethys', a=535600, b=528200, c=525800, e2ab=0.027441672, e2bc=0.009066821, e2ac=0.036259685, volume=623086233855821440, area=3528073490771.394042969, area_p=3528074261832.738769531)
|
|
29
|
+
@var Triaxials.WGS84_3: Triaxial(name='WGS84_3', a=6378171.36, b=6378101.609999999, c=6356751.84, e2ab=0.000021871, e2bc=0.006683505, e2ac=0.00670523, volume=1083207064030173855744, area=510065541435967.4375, area_p=510065546301413.5625)
|
|
30
|
+
@var Triaxials.WGS84_3r: Triaxial(name='WGS84_3r', a=6378172, b=6378102, c=6356752, e2ab=0.00002195, e2bc=0.006683577, e2ac=0.00670538, volume=1083207266220584468480, area=510065604942135.8125, area_p=510065609807745.0)
|
|
29
31
|
@var Triaxials.WGS84_35: Triaxial(name='WGS84_35', a=6378172, b=6378102, c=6356752.314245179, e2ab=0.00002195, e2bc=0.006683478, e2ac=0.006705281, volume=1083207319768789942272, area=510065621722018.125, area_p=510065626587483.3125)
|
|
30
32
|
'''
|
|
31
33
|
# make sure int/int division yields float quotient, see .basics
|
|
32
34
|
from __future__ import division as _; del _ # noqa: E702 ;
|
|
33
35
|
|
|
34
|
-
from pygeodesy.
|
|
35
|
-
from pygeodesy.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
from pygeodesy.angles import _SinCos2, Property_RO
|
|
37
|
+
from pygeodesy.basics import _isin, isLatLon
|
|
38
|
+
from pygeodesy.constants import EPS, EPS0, EPS02, _EPS2e4, INT0, \
|
|
39
|
+
_isfinite, isnear1, _over, _SQRT2_2, \
|
|
40
|
+
_0_0, _0_5, _1_0, _N_1_0, _64_0
|
|
41
|
+
from pygeodesy.datums import Datum, _spherical_datum, _WGS84, _EWGS84, Fmt
|
|
39
42
|
# from pygeodesy.ellipsoids import Ellipsoid, _EWGS84 # from .datums
|
|
40
43
|
# from pygeodesy.elliptic import Elliptic # _MODS
|
|
41
|
-
from pygeodesy.errors import _AssertionError, _ValueError
|
|
42
|
-
from pygeodesy.fmath import Fdot, fdot,
|
|
43
|
-
from pygeodesy.fsums import
|
|
44
|
-
from pygeodesy.interns import NN,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
from pygeodesy.namedTuples import LatLon3Tuple, _NamedTupleTo, Vector3Tuple, \
|
|
53
|
-
Vector4Tuple
|
|
54
|
-
from pygeodesy.props import Property_RO, property_ROver
|
|
44
|
+
from pygeodesy.errors import _AssertionError, _ValueError, _xkwds_pop2
|
|
45
|
+
from pygeodesy.fmath import Fdot, fdot, hypot, hypot_, fabs, sqrt
|
|
46
|
+
from pygeodesy.fsums import fsumf_, fsum1f_
|
|
47
|
+
from pygeodesy.interns import NN, _beta_, _distant_, _DMAIN_, _finite_, _height_, \
|
|
48
|
+
_inside_, _near_, _negative_, _not_, _null_, _opposite_, \
|
|
49
|
+
_outside_, _too_, _x_, _y_
|
|
50
|
+
from pygeodesy.lazily import _ALL_LAZY, _FOR_DOCS
|
|
51
|
+
from pygeodesy.named import _lazyNamedEnumItem as _lazy, _name__, _NamedEnum, _Pass
|
|
52
|
+
from pygeodesy.namedTuples import LatLon3Tuple, _NamedTupleTo, Vector2Tuple, \
|
|
53
|
+
Vector3Tuple, Vector4Tuple
|
|
54
|
+
# from pygeodesy.props import Property_RO # from .triaxials.angles
|
|
55
55
|
# from pygeodesy.streprs import Fmt # from .datums
|
|
56
|
-
from pygeodesy.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
from pygeodesy.triaxials.bases import Conformal5Tuple, _HeightINT0, _hypot2_1, \
|
|
57
|
+
_not_ordered_, _OrderedTriaxialBase, _over0, \
|
|
58
|
+
_otherV3d_, _over02, _sqrt0, TriaxialError, \
|
|
59
|
+
_Triaxial3Base, _UnOrderedTriaxialBase
|
|
60
|
+
from pygeodesy.units import Degrees, Height_, Lat, Lon, Meter, Radians, Radius_, Scalar_
|
|
61
|
+
from pygeodesy.utily import atan2, atan2d, km2m, m2km
|
|
62
|
+
from pygeodesy.vector3d import _otherV3d, Vector3d
|
|
60
63
|
|
|
61
|
-
from math import fabs, sqrt
|
|
64
|
+
# from math import fabs, sqrt # from .fmath
|
|
62
65
|
|
|
63
|
-
__all__ = _ALL_LAZY.
|
|
64
|
-
__version__ = '25.
|
|
66
|
+
__all__ = _ALL_LAZY.triaxials_triaxial5
|
|
67
|
+
__version__ = '25.11.29'
|
|
65
68
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
_TRIPS = 359 # Eberly 1074?
|
|
69
|
+
_omega_ = 'omega'
|
|
70
|
+
_TRIPS = 359 # Eberly 1074?
|
|
69
71
|
|
|
70
72
|
|
|
71
73
|
class _NamedTupleToX(_NamedTupleTo): # in .testNamedTuples
|
|
@@ -188,9 +190,27 @@ class Conformal2Tuple(_NamedTupleToX):
|
|
|
188
190
|
'''
|
|
189
191
|
return self._toRadians(name)
|
|
190
192
|
|
|
193
|
+
def to5Tuple(self, b_conformal, **z_scale_name):
|
|
194
|
+
'''Return this L{Conformal2Tuple} as a L{Conformal5Tuple}.
|
|
191
195
|
|
|
192
|
-
|
|
193
|
-
|
|
196
|
+
@arg b_conformal: Middle semi-axis (C{meter}, conventionally)
|
|
197
|
+
or the original L{Conformal} of this 2-tuple.
|
|
198
|
+
@kwarg z_scale_name: Optional C{B{z}=0} meter, C{B{scale}=NAN}
|
|
199
|
+
and C{B{name}=NN} (C{str}).
|
|
200
|
+
|
|
201
|
+
@return: A L{Conformal5Tuple}.
|
|
202
|
+
'''
|
|
203
|
+
if isinstance(b_conformal, Conformal):
|
|
204
|
+
b = b_conformal.b
|
|
205
|
+
else:
|
|
206
|
+
b = Radius_(b=b_conformal)
|
|
207
|
+
x, y = self.toRadians()
|
|
208
|
+
x, y = _over(x, b), _over(y, b)
|
|
209
|
+
return Conformal5Tuple(x, y, **z_scale_name)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class Triaxial_(_UnOrderedTriaxialBase):
|
|
213
|
+
'''I{Unordered} triaxial ellipsoid.
|
|
194
214
|
|
|
195
215
|
Triaxial ellipsoids with right-handed semi-axes C{a}, C{b} and C{c}, oriented
|
|
196
216
|
such that the large principal ellipse C{ab} is the equator I{Z}=0, I{beta}=0,
|
|
@@ -205,568 +225,35 @@ class Triaxial_(_NamedEnumItem):
|
|
|
205
225
|
longitude C{beta} and C{omega} are in L{Radians} by default (or in
|
|
206
226
|
L{Degrees} if converted).
|
|
207
227
|
'''
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
def __init__(self, a_triaxial, b=None, c=None, **name):
|
|
212
|
-
'''New I{unordered} L{Triaxial_}.
|
|
213
|
-
|
|
214
|
-
@arg a_triaxial: Large, C{X} semi-axis (C{scalar}, conventionally in
|
|
215
|
-
C{meter}) or an other L{Triaxial} or L{Triaxial_} instance.
|
|
216
|
-
@kwarg b: Middle, C{Y} semi-axis (C{meter}, same units as B{C{a}}), required
|
|
217
|
-
if C{B{a_triaxial} is scalar}, ignored otherwise.
|
|
218
|
-
@kwarg c: Small, C{Z} semi-axis (C{meter}, like B{C{b}}).
|
|
219
|
-
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
220
|
-
|
|
221
|
-
@raise TriaxialError: Invalid semi-axis or -axes.
|
|
222
|
-
'''
|
|
223
|
-
try:
|
|
224
|
-
try:
|
|
225
|
-
a = a_triaxial
|
|
226
|
-
t = a._abc3
|
|
227
|
-
except AttributeError:
|
|
228
|
-
t = Radius_(a=a), Radius_(b=b), Radius_(c=c)
|
|
229
|
-
except (TypeError, ValueError) as x:
|
|
230
|
-
raise TriaxialError(a=a, b=b, c=c, cause=x)
|
|
231
|
-
if name:
|
|
232
|
-
self.name = name
|
|
233
|
-
|
|
234
|
-
a, b, c = self._abc3 = t
|
|
235
|
-
if self._unordered: # == not isinstance(self, Triaxial)
|
|
236
|
-
s, _, t = sorted(t)
|
|
237
|
-
if not (_isfinite(t) and s > 0):
|
|
238
|
-
raise TriaxialError(a=a, b=b, c=c) # txt=_invalid_
|
|
239
|
-
elif not (_isfinite(a) and a >= b >= c > 0): # see Triaxial2
|
|
240
|
-
raise TriaxialError(a=a, b=b, c=c, txt=_not_ordered_)
|
|
241
|
-
elif not (a > c and self._a2c2 > 0 and self.e2ac > 0):
|
|
242
|
-
raise TriaxialError(a=a, c=c, e2ac=self.e2ac, txt=_spherical_)
|
|
243
|
-
|
|
244
|
-
def __str__(self):
|
|
245
|
-
return self.toStr()
|
|
246
|
-
|
|
247
|
-
@Property_RO
|
|
248
|
-
def a(self):
|
|
249
|
-
'''Get the C{largest, x} semi-axis (C{meter}, conventionally).
|
|
250
|
-
'''
|
|
251
|
-
a, _, _ = self._abc3
|
|
252
|
-
return a
|
|
253
|
-
|
|
254
|
-
@Property_RO
|
|
255
|
-
def a2(self):
|
|
256
|
-
'''Get C{a**2}.
|
|
257
|
-
'''
|
|
258
|
-
return self.a**2
|
|
259
|
-
|
|
260
|
-
@Property_RO
|
|
261
|
-
def _a2b2(self):
|
|
262
|
-
'''(INTERNAL) Get C{a**2 - b**2} == E_sub_e**2.
|
|
263
|
-
'''
|
|
264
|
-
a, b, _ = self._abc3
|
|
265
|
-
return ((a - b) * (a + b)) if a != b else _0_0
|
|
266
|
-
|
|
267
|
-
@Property_RO
|
|
268
|
-
def _a2_b2(self):
|
|
269
|
-
'''(INTERNAL) Get C{(a / b)**2}.
|
|
270
|
-
'''
|
|
271
|
-
a, b, _ = self._abc3
|
|
272
|
-
return (a / b)**2 if a != b else _1_0
|
|
273
|
-
|
|
274
|
-
@Property_RO
|
|
275
|
-
def _a2b2c23(self):
|
|
276
|
-
'''(INTERNAL) Get 3-tuple C{(a**2, b**2, c**2)}.
|
|
277
|
-
'''
|
|
278
|
-
a, b, c = self._abc3
|
|
279
|
-
return self.a2, self.b2, self.c2
|
|
280
|
-
|
|
281
|
-
@Property_RO
|
|
282
|
-
def _a2c2(self):
|
|
283
|
-
'''(INTERNAL) Get C{a**2 - c**2} == E_sub_x**2.
|
|
284
|
-
'''
|
|
285
|
-
a, _, c = self._abc3
|
|
286
|
-
return ((a - c) * (a + c)) if a != c else _0_0
|
|
287
|
-
|
|
288
|
-
@Property_RO
|
|
289
|
-
def area(self):
|
|
290
|
-
'''Get the surface area (C{meter} I{squared}).
|
|
291
|
-
'''
|
|
292
|
-
c, b, a = sorted(self._abc3)
|
|
293
|
-
return Triaxial(a, b, c).area if a > c else \
|
|
294
|
-
Meter2(area=self.a2 * PI4) # a == c == b
|
|
295
|
-
|
|
296
|
-
def area_p(self, p=1.6075):
|
|
297
|
-
'''I{Approximate} the surface area (C{meter} I{squared}).
|
|
298
|
-
|
|
299
|
-
@kwarg p: Exponent (C{scalar} > 0), 1.6 for near-spherical or 1.5849625007
|
|
300
|
-
for "near-flat" triaxials.
|
|
301
|
-
|
|
302
|
-
@see: U{Surface area<https://WikiPedia.org/wiki/Ellipsoid#Approximate_formula>}.
|
|
303
|
-
'''
|
|
304
|
-
a, b, c = self._abc3
|
|
305
|
-
if a == b == c:
|
|
306
|
-
a *= a
|
|
307
|
-
else:
|
|
308
|
-
_p = pow
|
|
309
|
-
a = _p(fmean_(_p(a * b, p), _p(a * c, p), _p(b * c, p)), _1_0 / p)
|
|
310
|
-
return Meter2(area_p=a * PI4)
|
|
311
|
-
|
|
312
|
-
@Property_RO
|
|
313
|
-
def b(self):
|
|
314
|
-
'''Get the C{middle, y} semi-axis (C{meter}, same units as B{C{a}}).
|
|
315
|
-
'''
|
|
316
|
-
_, b, _ = self._abc3
|
|
317
|
-
return b
|
|
318
|
-
|
|
319
|
-
@Property_RO
|
|
320
|
-
def b2(self):
|
|
321
|
-
'''Get C{b**2}.
|
|
322
|
-
'''
|
|
323
|
-
return self.b**2
|
|
324
|
-
|
|
325
|
-
@Property_RO
|
|
326
|
-
def _b2_a2(self):
|
|
327
|
-
'''(INTERNAL) Get C{(b / a)**2}.
|
|
328
|
-
'''
|
|
329
|
-
a, b, _ = self._abc3
|
|
330
|
-
return (b / a)**2 if a != b else _1_0
|
|
331
|
-
|
|
332
|
-
@Property_RO
|
|
333
|
-
def _b2c2(self):
|
|
334
|
-
'''(INTERNAL) Get C{b**2 - c**2} == E_sub_y**2.
|
|
335
|
-
'''
|
|
336
|
-
_, b, c = self._abc3
|
|
337
|
-
return ((b - c) * (b + c)) if b != c else _0_0
|
|
338
|
-
|
|
339
|
-
@Property_RO
|
|
340
|
-
def c(self):
|
|
341
|
-
'''Get the C{smallest, z} semi-axis (C{meter}, same units as B{C{a}}).
|
|
342
|
-
'''
|
|
343
|
-
_, _, c = self._abc3
|
|
344
|
-
return c
|
|
345
|
-
|
|
346
|
-
@Property_RO
|
|
347
|
-
def c2(self):
|
|
348
|
-
'''Get C{c**2}.
|
|
349
|
-
'''
|
|
350
|
-
return self.c**2
|
|
351
|
-
|
|
352
|
-
@Property_RO
|
|
353
|
-
def _c2_a2(self):
|
|
354
|
-
'''(INTERNAL) Get C{(c / a)**2}.
|
|
355
|
-
'''
|
|
356
|
-
a, _, c = self._abc3
|
|
357
|
-
return (c / a)**2 if a != c else _1_0
|
|
358
|
-
|
|
359
|
-
@Property_RO
|
|
360
|
-
def _c2_b2(self):
|
|
361
|
-
'''(INTERNAL) Get C{(c / b)**2}.
|
|
362
|
-
'''
|
|
363
|
-
_, b, c = self._abc3
|
|
364
|
-
return (c / b)**2 if b != c else _1_0
|
|
365
|
-
|
|
366
|
-
@Property_RO
|
|
367
|
-
def e2ab(self):
|
|
368
|
-
'''Get the C{ab} ellipse' I{(1st) eccentricity squared} (C{scalar}), M{1 - (b/a)**2}.
|
|
369
|
-
'''
|
|
370
|
-
return Float(e2ab=(_1_0 - self._b2_a2) or _0_0)
|
|
371
|
-
|
|
372
|
-
# _1e2ab = _b2_a2 # C{1 - e2ab} == C{(b / a)**2}
|
|
373
|
-
|
|
374
|
-
@Property_RO
|
|
375
|
-
def e2ac(self):
|
|
376
|
-
'''Get the C{ac} ellipse' I{(1st) eccentricity squared} (C{scalar}), M{1 - (c/a)**2}.
|
|
377
|
-
'''
|
|
378
|
-
return Float(e2ac=(_1_0 - self._c2_a2) or _0_0)
|
|
379
|
-
|
|
380
|
-
# _1e2ac = _c2_a2 # C{1 - e2ac} == C{(c / a)**2}
|
|
381
|
-
|
|
382
|
-
@Property_RO
|
|
383
|
-
def e2bc(self):
|
|
384
|
-
'''Get the C{bc} ellipse' I{(1st) eccentricity squared} (C{scalar}), M{1 - (c/b)**2}.
|
|
385
|
-
'''
|
|
386
|
-
return Float(e2bc=(_1_0 - self._c2_b2) or _0_0)
|
|
387
|
-
|
|
388
|
-
# _1e2bc = _c2_b2 # C{1 - e2bc} == C{(c / b)**2}
|
|
389
|
-
|
|
390
|
-
@property_ROver
|
|
391
|
-
def _Elliptic(self):
|
|
392
|
-
'''(INTERNAL) Get class L{Elliptic}, I{once}.
|
|
393
|
-
'''
|
|
394
|
-
return _MODS.elliptic.Elliptic # overwrite property_ROver
|
|
395
|
-
|
|
396
|
-
def hartzell4(self, pov, los=False, **name):
|
|
397
|
-
'''Compute the intersection of this triaxial's surface with a Line-Of-Sight
|
|
398
|
-
from a Point-Of-View in space.
|
|
399
|
-
|
|
400
|
-
@see: Function L{hartzell4<triaxials.hartzell4>} for further details.
|
|
401
|
-
'''
|
|
402
|
-
return hartzell4(pov, los=los, tri_biax=self, **name)
|
|
403
|
-
|
|
404
|
-
def height4(self, x_xyz, y=None, z=None, normal=True, eps=EPS, **name):
|
|
405
|
-
'''Compute the projection on and the height above or below this triaxial's surface.
|
|
406
|
-
|
|
407
|
-
@arg x_xyz: X component (C{scalar}) or a cartesian (C{Cartesian}, L{Ecef9Tuple},
|
|
408
|
-
L{Vector3d}, L{Vector3Tuple} or L{Vector4Tuple}).
|
|
409
|
-
@kwarg y: Y component (C{scalar}), required if B{C{x_xyz}} if C{scalar}, ignored
|
|
410
|
-
otherwise.
|
|
411
|
-
@kwarg z: Z component (C{scalar}), like B{C{y}}.
|
|
412
|
-
@kwarg normal: If C{True}, the projection is the I{perpendicular, plumb} to the
|
|
413
|
-
triaxial's surface, otherwise the C{radial} line to the center of
|
|
414
|
-
this triaxial (C{bool}).
|
|
415
|
-
@kwarg eps: Tolerance for root finding and validation (C{scalar}), use a negative
|
|
416
|
-
value to skip validation.
|
|
417
|
-
@kwarg name: Optional C{B{name}="height4"} (C{str}).
|
|
418
|
-
|
|
419
|
-
@return: L{Vector4Tuple}C{(x, y, z, h)} with the cartesian coordinates C{x}, C{y}
|
|
420
|
-
and C{z} of the projection on or the intersection with and with the height
|
|
421
|
-
C{h} above or below the triaxial's surface in C{meter}, conventionally.
|
|
422
|
-
|
|
423
|
-
@raise TriaxialError: Non-cartesian B{C{xyz}}, invalid B{C{eps}}, no convergence in
|
|
424
|
-
root finding or validation failed.
|
|
425
|
-
|
|
426
|
-
@see: Methods L{Triaxial.normal3d} and L{Ellipsoid.height4}, I{Eberly}'s U{Distance from a Point to ...
|
|
427
|
-
<https://www.GeometricTools.com/Documentation/DistancePointEllipseEllipsoid.pdf>} and I{Bektas}'
|
|
428
|
-
U{Shortest Distance from a Point to Triaxial Ellipsoid<https://www.ResearchGate.net/publication/
|
|
429
|
-
272149005_SHORTEST_DISTANCE_FROM_A_POINT_TO_TRIAXIAL_ELLIPSOID>}.
|
|
430
|
-
'''
|
|
431
|
-
v, r = _otherV3d_(x_xyz, y, z), self.isSpherical
|
|
432
|
-
|
|
433
|
-
i, h = None, v.length
|
|
434
|
-
if h < EPS0: # EPS
|
|
435
|
-
x = y = z = _0_0
|
|
436
|
-
h -= min(self._abc3) # nearest
|
|
437
|
-
elif r: # .isSpherical
|
|
438
|
-
x, y, z = v.times(r / h).xyz3
|
|
439
|
-
h -= r
|
|
440
|
-
else:
|
|
441
|
-
x, y, z = v.xyz3
|
|
442
|
-
try:
|
|
443
|
-
if normal: # plumb to surface
|
|
444
|
-
x, y, z, h, i = _plumbTo5(x, y, z, self, eps=eps)
|
|
445
|
-
else: # radial to center
|
|
446
|
-
x, y, z = self._radialTo3(z, hypot(x, y), y, x)
|
|
447
|
-
h = v.minus_(x, y, z).length
|
|
448
|
-
except Exception as e:
|
|
449
|
-
raise TriaxialError(x=x, y=y, z=z, cause=e)
|
|
450
|
-
if h > 0 and self.sideOf(v, eps=EPS0) < 0:
|
|
451
|
-
h = -h # inside
|
|
452
|
-
n = _name__(name, name__=self.height4) # typename
|
|
453
|
-
return Vector4Tuple(x, y, z, h, iteration=i, name=n)
|
|
454
|
-
|
|
455
|
-
@Property_RO
|
|
456
|
-
def isOrdered(self):
|
|
457
|
-
'''Is this triaxial I{ordered} and I{not spherical} (C{bool})?
|
|
458
|
-
'''
|
|
459
|
-
a, b, c = self._abc3
|
|
460
|
-
return bool(a >= b > c) # b > c!
|
|
461
|
-
|
|
462
|
-
@Property_RO
|
|
463
|
-
def isSpherical(self):
|
|
464
|
-
'''Is this triaxial I{spherical} (C{Radius} or INT0)?
|
|
465
|
-
'''
|
|
466
|
-
a, b, c = self._abc3
|
|
467
|
-
return a if a == b == c else INT0
|
|
468
|
-
|
|
469
|
-
def _norm2(self, s, c, *a):
|
|
470
|
-
'''(INTERNAL) Normalize C{s} and C{c} iff not already.
|
|
471
|
-
'''
|
|
472
|
-
if fabs(_hypot2_1(s, c)) > EPS02:
|
|
473
|
-
s, c = norm2(s, c)
|
|
474
|
-
if a:
|
|
475
|
-
s, c = norm2(s * self.b, c * a[0])
|
|
476
|
-
return float0_(s, c)
|
|
477
|
-
|
|
478
|
-
def normal3d(self, x_xyz, y=None, z=None, length=_1_0):
|
|
479
|
-
'''Get a 3-D vector I{on and perpendicular to} this triaxial's surface.
|
|
480
|
-
|
|
481
|
-
@arg x_xyz: X component (C{scalar}) or a cartesian (C{Cartesian},
|
|
482
|
-
L{Ecef9Tuple}, L{Vector3d}, L{Vector3Tuple} or L{Vector4Tuple}).
|
|
483
|
-
@kwarg y: Y component (C{scalar}), required if B{C{x_xyz}} if C{scalar}, ignored
|
|
484
|
-
otherwise.
|
|
485
|
-
@kwarg z: Z component (C{scalar}), like B{C{y}}.
|
|
486
|
-
@kwarg length: Optional, signed length in out-/inward direction (C{scalar}).
|
|
487
|
-
|
|
488
|
-
@return: A C{Vector3d(x_, y_, z_)} normalized to B{C{length}}, pointing out-
|
|
489
|
-
or inward for postive respectively negative B{C{length}}.
|
|
490
|
-
|
|
491
|
-
@raise TriaxialError: Zero length cartesian or vector.
|
|
492
|
-
|
|
493
|
-
@note: Cartesian C{(B{x}, B{y}, B{z})} I{must be on} this triaxial's surface,
|
|
494
|
-
use method L{Triaxial.sideOf} to validate.
|
|
495
|
-
|
|
496
|
-
@see: Methods L{Triaxial.height4} and L{Triaxial.sideOf}.
|
|
497
|
-
'''
|
|
498
|
-
# n = 2 * (x / a2, y / b2, z / c2)
|
|
499
|
-
# == 2 * (x, y * a2 / b2, z * a2 / c2) / a2 # iff ordered
|
|
500
|
-
# == 2 * (x, y / _b2_a2, z / _c2_a2) / a2
|
|
501
|
-
# == unit(x, y / _b2_a2, z / _c2_a2).times(length)
|
|
502
|
-
x, y, z = _otherV3d_(x_xyz, y, z).xyz3
|
|
503
|
-
n = Vector3d(x, y / self._b2_a2,
|
|
504
|
-
z / self._c2_a2, name__=self.normal3d)
|
|
505
|
-
u = n.length
|
|
506
|
-
if u < EPS0:
|
|
507
|
-
raise TriaxialError(x=x_xyz, y=y, z=z, txt=_null_)
|
|
508
|
-
return n.times(length / u)
|
|
509
|
-
|
|
510
|
-
def normal4(self, x_xyz, y=None, z=None, height=0, normal=True):
|
|
511
|
-
'''Compute a cartesian above or below this triaxial's surface.
|
|
512
|
-
|
|
513
|
-
@arg x_xyz: X component (C{scalar}) or a cartesian (C{Cartesian}, L{Ecef9Tuple},
|
|
514
|
-
L{Vector3d}, L{Vector3Tuple} or L{Vector4Tuple}).
|
|
515
|
-
@kwarg y: Y component (C{scalar}), required if B{C{x_xyz}} if C{scalar}, ignored
|
|
516
|
-
otherwise.
|
|
517
|
-
@kwarg z: Z component (C{scalar}), like B{C{y}}.
|
|
518
|
-
@kwarg height: The signed height (C{scalar}, same units as the triaxial axes).
|
|
519
|
-
@kwarg normal: If C{True}, the B{C{height}} is I{perpendicular, plumb} to the
|
|
520
|
-
triaxial's surface, otherwise C{radially} to the center of this
|
|
521
|
-
triaxial (C{bool}).
|
|
522
|
-
@kwarg name: Optional C{B{name}="normal4"} (C{str}).
|
|
523
|
-
|
|
524
|
-
@return: L{Vector4Tuple}C{(x, y, z, h)} with the cartesian coordinates C{x},
|
|
525
|
-
C{y} and C{z} and C{h} the I{signed, normal distance} to the triaxial's
|
|
526
|
-
surface in C{meter}, conventionally. Positive C{h} indicates, the
|
|
527
|
-
cartesian is outside the triaxial, negative C{h} means inside.
|
|
528
|
-
|
|
529
|
-
@raise TriaxialError: Zero length cartesian or vector.
|
|
530
|
-
|
|
531
|
-
@note: Cartesian C{(B{x}, B{y}, B{z})} I{must be on} this triaxial's surface,
|
|
532
|
-
use method L{Triaxial.sideOf} to validate.
|
|
533
|
-
|
|
534
|
-
@see: Methods L{Triaxial.normal3d} and L{Triaxial.height4}.
|
|
535
|
-
'''
|
|
536
|
-
v, h = _otherV3d(x_xyz, y, z), Height_(height, low=None)
|
|
537
|
-
if h:
|
|
538
|
-
if v.length < EPS0:
|
|
539
|
-
raise TriaxialError(x=x_xyz, y=y, z=z, txt=_null_)
|
|
540
|
-
if normal:
|
|
541
|
-
n = self.normal3d(v, length=h)
|
|
542
|
-
h = n.length
|
|
543
|
-
n += v
|
|
544
|
-
else:
|
|
545
|
-
h = h / v.length
|
|
546
|
-
n = v.times(h + _1_0)
|
|
547
|
-
else:
|
|
548
|
-
n = v
|
|
549
|
-
return Vector4Tuple(n.x, n.y, n.z, h, name__=self.normal4)
|
|
228
|
+
if _FOR_DOCS:
|
|
229
|
+
__init__ = _UnOrderedTriaxialBase.__init__
|
|
550
230
|
|
|
551
|
-
def _order3(self, *abc, **reverse): # reverse=False
|
|
552
|
-
'''(INTERNAL) Un-/Order C{a}, C{b} and C{c}.
|
|
553
231
|
|
|
554
|
-
|
|
555
|
-
(reverse-ordered) C{ijk} if C{B{reverse}=True}.
|
|
556
|
-
'''
|
|
557
|
-
ijk = self._order_ijk(**reverse)
|
|
558
|
-
return _getitems(abc, *ijk) if ijk else abc
|
|
559
|
-
|
|
560
|
-
def _order3d(self, v, **reverse): # reverse=False
|
|
561
|
-
'''(INTERNAL) Un-/Order a C{Vector3d}.
|
|
562
|
-
|
|
563
|
-
@return: Vector3d(x, y, z) un-/ordered.
|
|
564
|
-
'''
|
|
565
|
-
ijk = self._order_ijk(**reverse)
|
|
566
|
-
return v.classof(*_getitems(v.xyz3, *ijk)) if ijk else v
|
|
567
|
-
|
|
568
|
-
@Property_RO
|
|
569
|
-
def _ordered4(self):
|
|
570
|
-
'''(INTERNAL) Helper for C{_hartzell3} and C{_plumbTo5}.
|
|
571
|
-
'''
|
|
572
|
-
def _order2(reverse, a, b, c):
|
|
573
|
-
'''(INTERNAL) Un-Order C{a}, C{b} and C{c}.
|
|
574
|
-
|
|
575
|
-
@return: 2-Tuple C{((a, b, c), ijk)} with C{a} >= C{b} >= C{c}
|
|
576
|
-
and C{ijk} a 3-tuple with the initial indices.
|
|
577
|
-
'''
|
|
578
|
-
i, j, k = range(3)
|
|
579
|
-
if a < b:
|
|
580
|
-
a, b, i, j = b, a, j, i
|
|
581
|
-
if a < c:
|
|
582
|
-
a, c, i, k = c, a, k, i
|
|
583
|
-
if b < c:
|
|
584
|
-
b, c, j, k = c, b, k, j
|
|
585
|
-
# reverse (k, j, i) since (a, b, c) is reversed-sorted
|
|
586
|
-
ijk = (k, j, i) if reverse else (None if i < j < k else (i, j, k))
|
|
587
|
-
return (a, b, c), ijk
|
|
588
|
-
|
|
589
|
-
abc, T = self._abc3, self
|
|
590
|
-
if not self.isOrdered:
|
|
591
|
-
abc, ijk = _order2(False, *abc)
|
|
592
|
-
if ijk:
|
|
593
|
-
_, kji = _order2(True, *ijk)
|
|
594
|
-
T = Triaxial_(*abc)
|
|
595
|
-
T._ijk, T._kji = ijk, kji
|
|
596
|
-
return abc + (T,)
|
|
597
|
-
|
|
598
|
-
def _order_ijk(self, reverse=False):
|
|
599
|
-
'''(INTERNAL) Get the un-/order indices.
|
|
600
|
-
'''
|
|
601
|
-
return self._kji if reverse else self._ijk
|
|
602
|
-
|
|
603
|
-
def _radialTo3(self, sbeta, cbeta, somega, comega):
|
|
604
|
-
'''(INTERNAL) I{Unordered} helper for C{.height4}.
|
|
605
|
-
'''
|
|
606
|
-
def _rphi(a, b, sphi, cphi):
|
|
607
|
-
# <https://WikiPedia.org/wiki/Ellipse#Polar_form_relative_to_focus>
|
|
608
|
-
# polar form: radius(phi) = a * b / hypot(a * sphi, b * cphi)
|
|
609
|
-
return (b / hypot(sphi, b / a * cphi)) if a > b else (
|
|
610
|
-
(a / hypot(cphi, a / b * sphi)) if a < b else a)
|
|
611
|
-
|
|
612
|
-
sa, ca = self._norm2(sbeta, cbeta)
|
|
613
|
-
sb, cb = self._norm2(somega, comega)
|
|
614
|
-
|
|
615
|
-
a, b, c = self._abc3
|
|
616
|
-
if a != b:
|
|
617
|
-
a = _rphi(a, b, sb, cb)
|
|
618
|
-
if a != c:
|
|
619
|
-
c = _rphi(a, c, sa, ca)
|
|
620
|
-
t = c * ca
|
|
621
|
-
return (t * cb), (t * sb), (c * sa)
|
|
622
|
-
|
|
623
|
-
def sideOf(self, x_xyz, y=None, z=None, eps=EPS4):
|
|
624
|
-
'''Is a cartesian on, above or below the surface of this triaxial?
|
|
625
|
-
|
|
626
|
-
@arg x_xyz: X component (C{scalar}) or a cartesian (C{Cartesian},
|
|
627
|
-
L{Ecef9Tuple}, L{Vector3d}, L{Vector3Tuple} or L{Vector4Tuple}).
|
|
628
|
-
@kwarg y: Y component (C{scalar}), required if B{C{x_xyz}} is C{scalar},
|
|
629
|
-
ignored otherwise.
|
|
630
|
-
@kwarg z: Z component (C{scalar}), like B{C{y}}.
|
|
631
|
-
@kwarg eps: On-surface tolerance (C{scalar}, distance I{squared}).
|
|
632
|
-
|
|
633
|
-
@return: C{INT0} if C{(B{x}, B{y}, B{z})} is near this triaxial's surface
|
|
634
|
-
within tolerance B{C{eps}}, otherwise the signed, radial distance
|
|
635
|
-
I{squared} (C{float}), negative for in- or positive for outside
|
|
636
|
-
this triaxial.
|
|
637
|
-
|
|
638
|
-
@see: Methods L{Triaxial.height4} and L{Triaxial.normal3d}.
|
|
639
|
-
'''
|
|
640
|
-
v = _otherV3d_(x_xyz, y, z)
|
|
641
|
-
s = fsumf_(_N_1_0, *map(_over02, v.xyz3, self._abc3))
|
|
642
|
-
return INT0 if fabs(s) < eps else s
|
|
643
|
-
|
|
644
|
-
def toEllipsoid(self, **name):
|
|
645
|
-
'''Convert this triaxial to a I{biaxial} L{Ellipsoid}, provided 2 axes match.
|
|
646
|
-
|
|
647
|
-
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
648
|
-
|
|
649
|
-
@return: An L{Ellipsoid} with north along this C{Z} axis if C{a == b},
|
|
650
|
-
this C{Y} axis if C{a == c} or this C{X} axis if C{b == c}.
|
|
651
|
-
|
|
652
|
-
@raise TriaxialError: This C{a != b}, C{b != c} and C{c != a}.
|
|
653
|
-
|
|
654
|
-
@see: Method L{Ellipsoid.toTriaxial}.
|
|
655
|
-
'''
|
|
656
|
-
a, b, c = self._abc3
|
|
657
|
-
if a == b:
|
|
658
|
-
b = c # N = c-Z
|
|
659
|
-
elif b == c: # N = a-X
|
|
660
|
-
a, b = b, a
|
|
661
|
-
elif a != c: # N = b-Y
|
|
662
|
-
t = _SPACE_(_a_, _NOTEQUAL_, _b_, _NOTEQUAL_, _c_)
|
|
663
|
-
raise TriaxialError(a=a, b=b, c=c, txt=t)
|
|
664
|
-
return Ellipsoid(a, b=b, name=self._name__(name))
|
|
665
|
-
|
|
666
|
-
toBiaxial = toEllipsoid
|
|
667
|
-
|
|
668
|
-
def toStr(self, prec=9, **name): # PYCHOK signature
|
|
669
|
-
'''Return this C{Triaxial} as a string.
|
|
670
|
-
|
|
671
|
-
@kwarg prec: Precision, number of decimal digits (0..9).
|
|
672
|
-
@kwarg name: Optional name (C{str}), to override or C{None}
|
|
673
|
-
to exclude this triaxial's name.
|
|
674
|
-
|
|
675
|
-
@return: This C{Triaxial}'s attributes (C{str}).
|
|
676
|
-
'''
|
|
677
|
-
T = Triaxial_
|
|
678
|
-
t = T.a, # props
|
|
679
|
-
J = ConformalSphere
|
|
680
|
-
t += (J.ab, J.bc) if isinstance(self, J) else (T.b, T.c)
|
|
681
|
-
t += T.e2ab, T.e2bc, T.e2ac
|
|
682
|
-
J = ConformalTriaxial
|
|
683
|
-
if isinstance(self, J):
|
|
684
|
-
t += J.xyQ2,
|
|
685
|
-
t += T.volume, T.area
|
|
686
|
-
return self._instr(area_p=self.area_p(), prec=prec, props=t, **name)
|
|
687
|
-
|
|
688
|
-
@Property_RO
|
|
689
|
-
def unOrdered(self):
|
|
690
|
-
'''Is this triaxial I{un-ordered} and I{not spherical} (C{bool})?
|
|
691
|
-
'''
|
|
692
|
-
return not (self.isOrdered or bool(self.isSpherical))
|
|
693
|
-
|
|
694
|
-
@Property_RO
|
|
695
|
-
def volume(self):
|
|
696
|
-
'''Get the volume (C{meter**3}), M{4 / 3 * PI * a * b * c}.
|
|
697
|
-
'''
|
|
698
|
-
a, b, c = self._abc3
|
|
699
|
-
return Meter3(volume=a * b * c * PI_3 * _4_0)
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
class Triaxial(Triaxial_):
|
|
232
|
+
class Triaxial(_OrderedTriaxialBase):
|
|
703
233
|
'''I{Ordered} triaxial ellipsoid.
|
|
704
234
|
|
|
705
235
|
@see: L{Triaxial_} for more information.
|
|
706
236
|
'''
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
def __init__(self, a_triaxial, b=None, c=None, **name):
|
|
710
|
-
'''New I{ordered} L{Triaxial}.
|
|
711
|
-
|
|
712
|
-
@arg a_triaxial: Largest semi-axis (C{scalar}, conventionally in C{meter})
|
|
713
|
-
or an other L{Triaxial} or L{Triaxial_} instance.
|
|
714
|
-
@kwarg b: Middle semi-axis (C{meter}, same units as B{C{a}}), required
|
|
715
|
-
if C{B{a_triaxial} is scalar}, ignored otherwise.
|
|
716
|
-
@kwarg c: Smallest semi-axis (C{meter}, like B{C{b}}).
|
|
717
|
-
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
718
|
-
|
|
719
|
-
@note: The semi-axes must be ordered as C{B{a} >= B{b} >= B{c} > 0} and
|
|
720
|
-
must be ellipsoidal, C{B{a} > B{c}}.
|
|
721
|
-
|
|
722
|
-
@raise TriaxialError: Semi-axes unordered, spherical or invalid.
|
|
723
|
-
'''
|
|
724
|
-
Triaxial_.__init__(self, a_triaxial, b=b, c=c, **name)
|
|
725
|
-
|
|
726
|
-
@Property_RO
|
|
727
|
-
def _a2b2_a2c2(self):
|
|
728
|
-
'''@see: Methods C{.forwardBetaOmega} and property C{._k2E_kp2E}.
|
|
729
|
-
'''
|
|
730
|
-
s = self._a2c2
|
|
731
|
-
if s:
|
|
732
|
-
s = self._a2b2 / s
|
|
733
|
-
return s or _0_0
|
|
734
|
-
|
|
735
|
-
@Property_RO
|
|
736
|
-
def area(self):
|
|
737
|
-
'''Get the surface area (C{meter} I{squared}).
|
|
237
|
+
if _FOR_DOCS:
|
|
238
|
+
__init__ = _OrderedTriaxialBase.__init__
|
|
738
239
|
|
|
739
|
-
|
|
740
|
-
'''
|
|
741
|
-
a, b, c = self._abc3
|
|
742
|
-
if a != b:
|
|
743
|
-
kp2, k2 = self._k2E_kp2E # swapped!
|
|
744
|
-
aE = self._Elliptic(k2, _0_0, kp2, _1_0)
|
|
745
|
-
c2 = self._c2_a2 # cos(phi)**2 = (c/a)**2
|
|
746
|
-
s = sqrt(self.e2ac) # sin(phi)**2 = 1 - c2
|
|
747
|
-
r = asin1(s) # phi = atan2(sqrt(c2), s)
|
|
748
|
-
b *= fsum1f_(aE.fE(r) * s, (c / a) * (c / b),
|
|
749
|
-
aE.fF(r) * c2 / s)
|
|
750
|
-
a = Meter2(area=a * b * PI2)
|
|
751
|
-
else: # a == b > c
|
|
752
|
-
a = Ellipsoid(a, b=c).areax
|
|
753
|
-
return a
|
|
754
|
-
|
|
755
|
-
def forwardBetaOmega(self, beta, omega, height=0, **name):
|
|
240
|
+
def forwardBetaOmega(self, beta, omega, height=0, **unit_name):
|
|
756
241
|
'''Convert I{ellipsoidal} lat- C{beta}, longitude C{omega} and C{height}
|
|
757
242
|
to cartesian.
|
|
758
243
|
|
|
759
|
-
@arg beta: Ellipsoidal latitude (C{
|
|
760
|
-
@arg omega: Ellipsoidal longitude (C{
|
|
761
|
-
@
|
|
762
|
-
|
|
763
|
-
@kwarg
|
|
244
|
+
@arg beta: Ellipsoidal latitude (C{Ang} or B{C{unit}}).
|
|
245
|
+
@arg omega: Ellipsoidal longitude (C{Ang} or B{C{unit}}).
|
|
246
|
+
@kwarg height: Height above or below the triaxial's surface (C{meter},
|
|
247
|
+
same units as this triaxial's semi-axes.
|
|
248
|
+
@kwarg unit_name: Optional C{B{name}=NN} (C{str}) and scalar
|
|
249
|
+
C{B{unit}=}L{Radians} (or L{Degrees}).
|
|
764
250
|
|
|
765
251
|
@return: A L{Vector3Tuple}C{(x, y, z)}.
|
|
766
252
|
|
|
767
|
-
@see: Method L{Triaxial.reverseBetaOmega} and U{
|
|
768
|
-
|
|
253
|
+
@see: Method L{Triaxial.reverseBetaOmega} and U{equations (23-25)<https://
|
|
254
|
+
OLD.Topo.Auth.GR/wp-content/uploads/sites/111/2021/12/09_Panou.pdf>}.
|
|
769
255
|
'''
|
|
256
|
+
unit, name = _xkwds_pop2(unit_name, unit=Radians)
|
|
770
257
|
if height:
|
|
771
258
|
z = self._Height(height) + self.c
|
|
772
259
|
if z > 0:
|
|
@@ -778,8 +265,8 @@ class Triaxial(Triaxial_):
|
|
|
778
265
|
else:
|
|
779
266
|
x, y, z = self._abc3
|
|
780
267
|
if z: # and x and y:
|
|
781
|
-
sa, ca =
|
|
782
|
-
sb, cb =
|
|
268
|
+
sa, ca = _SinCos2(beta, unit)
|
|
269
|
+
sb, cb = _SinCos2(omega, unit)
|
|
783
270
|
|
|
784
271
|
r = self._a2b2_a2c2
|
|
785
272
|
x *= cb * (_sqrt0(ca**2 + sa**2 * r) if r else fabs(ca))
|
|
@@ -788,8 +275,8 @@ class Triaxial(Triaxial_):
|
|
|
788
275
|
return Vector3Tuple(x, y, z, **name)
|
|
789
276
|
|
|
790
277
|
def forwardBetaOmega_(self, sbeta, cbeta, somega, comega, **name):
|
|
791
|
-
'''Convert I{ellipsoidal} lat- C{beta} and
|
|
792
|
-
to cartesian coordinates I{on
|
|
278
|
+
'''Convert I{ellipsoidal} lat- and longitude C{beta} and C{omega}
|
|
279
|
+
to cartesian coordinates I{on this triaxial's surface}.
|
|
793
280
|
|
|
794
281
|
@arg sbeta: Ellipsoidal latitude C{sin(beta)} (C{scalar}).
|
|
795
282
|
@arg cbeta: Ellipsoidal latitude C{cos(beta)} (C{scalar}).
|
|
@@ -803,44 +290,50 @@ class Triaxial(Triaxial_):
|
|
|
803
290
|
|
|
804
291
|
@see: Method L{Triaxial.reverseBetaOmega}, U{Triaxial ellipsoid coordinate
|
|
805
292
|
system<https://WikiPedia.org/wiki/Geodesics_on_an_ellipsoid#
|
|
806
|
-
Triaxial_ellipsoid_coordinate_system>} and U{
|
|
807
|
-
|
|
293
|
+
Triaxial_ellipsoid_coordinate_system>} and U{equations (23-25)<https://
|
|
294
|
+
OLD.Topo.Auth.GR/wp-content/uploads/sites/111/2021/12/09_Panou.pdf>}.
|
|
808
295
|
'''
|
|
809
296
|
t = self._radialTo3(sbeta, cbeta, somega, comega)
|
|
810
297
|
return Vector3Tuple(*t, **name)
|
|
811
298
|
|
|
812
|
-
def forwardCartesian(self, x_xyz, y=None, z=None, **
|
|
813
|
-
'''Project a cartesian on this triaxial.
|
|
299
|
+
def forwardCartesian(self, x_xyz, y=None, z=None, normal=True, **eps_name):
|
|
300
|
+
'''Project any cartesian to a cartesian I{on this triaxial's surface}.
|
|
814
301
|
|
|
815
302
|
@arg x_xyz: X component (C{scalar}) or a cartesian (C{Cartesian},
|
|
816
303
|
L{Ecef9Tuple}, L{Vector3d}, L{Vector3Tuple} or L{Vector4Tuple}).
|
|
817
304
|
@kwarg y: Y component (C{scalar}), required if B{C{x_xyz}} is C{scalar},
|
|
818
305
|
ignored otherwise.
|
|
819
306
|
@kwarg z: Z component (C{scalar}), like B{C{y}}.
|
|
820
|
-
@kwarg
|
|
821
|
-
|
|
822
|
-
|
|
307
|
+
@kwarg normal: If C{True}, the projection is C{perpendicular} to the surface,
|
|
308
|
+
otherwise C{radial} to the center of this triaxial (C{bool}).
|
|
309
|
+
@kwarg eps_name: Root finder tolerance C{B{eps}=EPS} and optional
|
|
310
|
+
C{B{name}="height4"} (C{str}).
|
|
823
311
|
|
|
824
|
-
@
|
|
825
|
-
|
|
312
|
+
@return: A L{Vector4Tuple}C{(x, y, z, h)}.
|
|
313
|
+
|
|
314
|
+
@see: Method L{Triaxial.reverseCartesian} to reverse the projection and
|
|
315
|
+
function L{height4<triaxials.triaxial5.height4>} for more details.
|
|
826
316
|
'''
|
|
827
|
-
return self.height4(x_xyz, y, z, **
|
|
317
|
+
return self.height4(x_xyz, y, z, normal=normal, **eps_name)
|
|
828
318
|
|
|
829
|
-
def forwardLatLon(self, lat, lon, height=0, **
|
|
319
|
+
def forwardLatLon(self, lat, lon, height=0, **unit_name):
|
|
830
320
|
'''Convert I{geodetic} lat-, longitude and height to cartesian.
|
|
831
321
|
|
|
832
|
-
@arg lat: Geodetic latitude (C{
|
|
833
|
-
@arg lon: Geodetic longitude (C{
|
|
322
|
+
@arg lat: Geodetic latitude (C{Ang} or B{C{unit}}).
|
|
323
|
+
@arg lon: Geodetic longitude (C{Ang} or B{C{unit}}).
|
|
834
324
|
@arg height: Height above the ellipsoid (C{meter}, same units
|
|
835
325
|
as this triaxial's semi-axes).
|
|
836
|
-
@kwarg
|
|
326
|
+
@kwarg unit_name: Optional scalar C{B{unit}=}L{Degrees} and
|
|
327
|
+
C{B{name}=NN} (C{str}).
|
|
837
328
|
|
|
838
329
|
@return: A L{Vector3Tuple}C{(x, y, z)}.
|
|
839
330
|
|
|
840
|
-
@see: Method L{Triaxial.reverseLatLon} and U{
|
|
841
|
-
|
|
331
|
+
@see: Method L{Triaxial.reverseLatLon} and U{equations (9-11)<https://
|
|
332
|
+
OLD.Topo.Auth.GR/wp-content/uploads/sites/111/2021/12/09_Panou.pdf>}.
|
|
842
333
|
'''
|
|
843
|
-
|
|
334
|
+
unit, name = _xkwds_pop2(unit_name, unit=Degrees)
|
|
335
|
+
return self._forwardLatLon3(height, name, *(_SinCos2(lat, unit) +
|
|
336
|
+
_SinCos2(lon, unit)))
|
|
844
337
|
|
|
845
338
|
def forwardLatLon_(self, slat, clat, slon, clon, height=0, **name):
|
|
846
339
|
'''Convert I{geodetic} lat-, longitude and height to cartesian.
|
|
@@ -855,8 +348,8 @@ class Triaxial(Triaxial_):
|
|
|
855
348
|
|
|
856
349
|
@return: A L{Vector3Tuple}C{(x, y, z)}.
|
|
857
350
|
|
|
858
|
-
@see: Method L{Triaxial.reverseLatLon} and U{
|
|
859
|
-
|
|
351
|
+
@see: Method L{Triaxial.reverseLatLon} and U{equations (9-11)<https://
|
|
352
|
+
OLD.Topo.Auth.GR/wp-content/uploads/sites/111/2021/12/09_Panou.pdf>}.
|
|
860
353
|
'''
|
|
861
354
|
sa, ca = self._norm2(slat, clat)
|
|
862
355
|
sb, cb = self._norm2(slon, clon)
|
|
@@ -865,14 +358,16 @@ class Triaxial(Triaxial_):
|
|
|
865
358
|
def _forwardLatLon3(self, height, name, sa, ca, sb, cb): # name always **name
|
|
866
359
|
'''(INTERNAL) Helper for C{.forwardLatLon} and C{.forwardLatLon_}.
|
|
867
360
|
'''
|
|
868
|
-
ca_x_sb = ca * sb
|
|
869
361
|
h = self._Height(height)
|
|
362
|
+
x = ca * cb
|
|
363
|
+
y = ca * sb
|
|
364
|
+
z = sa
|
|
870
365
|
# 1 - (1 - (c/a)**2) * sa**2 - (1 - (b/a)**2) * ca**2 * sb**2
|
|
871
|
-
t =
|
|
872
|
-
n =
|
|
873
|
-
x
|
|
874
|
-
y
|
|
875
|
-
z
|
|
366
|
+
t = fsumf_(_1_0, -self.e2ac * z**2, -self.e2ab * y**2)
|
|
367
|
+
n = self.a / _sqrt0(t) # prime vertical
|
|
368
|
+
x *= h + n
|
|
369
|
+
y *= h + n * self._b2_a2
|
|
370
|
+
z *= h + n * self._c2_a2
|
|
876
371
|
return Vector3Tuple(x, y, z, **name)
|
|
877
372
|
|
|
878
373
|
def _Height(self, height):
|
|
@@ -880,41 +375,6 @@ class Triaxial(Triaxial_):
|
|
|
880
375
|
'''
|
|
881
376
|
return Height_(height=height, low=-self.c, Error=TriaxialError)
|
|
882
377
|
|
|
883
|
-
@Property_RO
|
|
884
|
-
def _k2E_kp2E(self):
|
|
885
|
-
'''(INTERNAL) Get elliptic C{k2} and C{kp2} for C{._xE}, C{._yE} and C{.area}.
|
|
886
|
-
'''
|
|
887
|
-
# k2 = a2b2 / a2c2 * c2_b2
|
|
888
|
-
# kp2 = b2c2 / a2c2 * a2_b2
|
|
889
|
-
# b2 = b**2
|
|
890
|
-
# xE = Elliptic(k2, -a2b2 / b2, kp2, a2_b2)
|
|
891
|
-
# yE = Elliptic(kp2, +b2c2 / b2, k2, c2_b2)
|
|
892
|
-
# aE = Elliptic(kp2, 0, k2, 1)
|
|
893
|
-
k2 = self._c2_b2 * self._a2b2_a2c2
|
|
894
|
-
kp2 = (self._a2_b2 * self._b2c2 / self._a2c2) if k2 else _1_0
|
|
895
|
-
return (k2, kp2)
|
|
896
|
-
|
|
897
|
-
def _radialTo3(self, sbeta, cbeta, somega, comega):
|
|
898
|
-
'''(INTERNAL) Convert I{ellipsoidal} lat- and longitude C{beta} and
|
|
899
|
-
C{omega} to cartesian coordinates I{on the triaxial's surface},
|
|
900
|
-
also I{ordered} helper for C{.height4}.
|
|
901
|
-
'''
|
|
902
|
-
sa, ca = self._norm2(sbeta, cbeta)
|
|
903
|
-
sb, cb = self._norm2(somega, comega)
|
|
904
|
-
|
|
905
|
-
b2_a2 = self._b2_a2 # == (b/a)**2
|
|
906
|
-
c2_a2 = -self._c2_a2 # == -(c/a)**2
|
|
907
|
-
a2c2_a2 = self. e2ac # (a**2 - c**2) / a**2 == 1 - (c/a)**2
|
|
908
|
-
|
|
909
|
-
x2 = _Fsumf_(_1_0, -b2_a2 * sa**2, c2_a2 * ca**2).fover(a2c2_a2)
|
|
910
|
-
z2 = _Fsumf_(c2_a2, sb**2, b2_a2 * cb**2).fover(a2c2_a2)
|
|
911
|
-
|
|
912
|
-
x, y, z = self._abc3
|
|
913
|
-
x *= cb * _sqrt0(x2)
|
|
914
|
-
y *= ca * sb
|
|
915
|
-
z *= sa * _sqrt0(z2)
|
|
916
|
-
return x, y, z
|
|
917
|
-
|
|
918
378
|
def reverseBetaOmega(self, x_xyz, y=None, z=None, **name):
|
|
919
379
|
'''Convert cartesian to I{ellipsoidal} lat- and longitude, C{beta}, C{omega}
|
|
920
380
|
and height.
|
|
@@ -931,41 +391,40 @@ class Triaxial(Triaxial_):
|
|
|
931
391
|
units as this triaxial's semi-axes.
|
|
932
392
|
|
|
933
393
|
@see: Methods L{Triaxial.forwardBetaOmega} and L{Triaxial.forwardBetaOmega_}
|
|
934
|
-
and U{
|
|
394
|
+
and U{equations (21-22)<https://OLD.Topo.Auth.GR/wp-content/uploads/
|
|
935
395
|
sites/111/2021/12/09_Panou.pdf>}.
|
|
936
396
|
'''
|
|
937
397
|
v = _otherV3d_(x_xyz, y, z)
|
|
938
398
|
a, b, h = _reverseLatLon3(v, atan2, v, self.forwardBetaOmega_)
|
|
939
399
|
return BetaOmega3Tuple(Radians(beta=a), Radians(omega=b), h, **name)
|
|
940
400
|
|
|
941
|
-
def reverseCartesian(self, x_xyz, y=None, z=None,
|
|
942
|
-
'''"Unproject" a cartesian I{
|
|
943
|
-
this triaxial's surface.
|
|
401
|
+
def reverseCartesian(self, x_xyz, y=None, z=None, height=0, normal=True, eps=_EPS2e4, **name):
|
|
402
|
+
'''"Unproject" a cartesian I{off} this triaxial's surface.
|
|
944
403
|
|
|
945
404
|
@arg x_xyz: X component (C{scalar}) or a cartesian (C{Cartesian},
|
|
946
405
|
L{Ecef9Tuple}, L{Vector3d}, L{Vector3Tuple} or L{Vector4Tuple}).
|
|
947
406
|
@kwarg y: Y component (C{scalar}), required if B{C{x_xyz}} is C{scalar},
|
|
948
407
|
ignored otherwise.
|
|
949
408
|
@kwarg z: Z component (C{scalar}), like B{C{y}}.
|
|
950
|
-
@
|
|
951
|
-
|
|
952
|
-
@kwarg normal: If C{True},
|
|
953
|
-
C{
|
|
954
|
-
@kwarg eps: Tolerance for on-surface test (C{scalar}), see method
|
|
409
|
+
@kwarg height: Height above or below this triaxial's surface (C{meter}, same
|
|
410
|
+
units as this triaxial's semi-axes).
|
|
411
|
+
@kwarg normal: If C{True}, B{C{height}} is C{perpendicular} to the surface,
|
|
412
|
+
otherwise C{radial} to the center of this triaxial (C{bool}).
|
|
413
|
+
@kwarg eps: Tolerance for on-surface test (C{scalar}), see method
|
|
414
|
+
L{Triaxial.sideOf}.
|
|
955
415
|
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
956
416
|
|
|
957
417
|
@return: A L{Vector3Tuple}C{(x, y, z)}.
|
|
958
418
|
|
|
959
|
-
@raise TrialError: Cartesian C{(x, y, z)} not on this triaxial's
|
|
419
|
+
@raise TrialError: Cartesian B{C{x_xyz}} or C{(x, y, z)} not on this triaxial's
|
|
420
|
+
surface.
|
|
960
421
|
|
|
961
422
|
@see: Methods L{Triaxial.forwardCartesian} and L{Triaxial.height4}.
|
|
962
423
|
'''
|
|
424
|
+
h, name = _xkwds_pop2(name, h=height) # h=height for backward compatibility
|
|
963
425
|
v = _otherV3d_(x_xyz, y, z, **name)
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
t = _SPACE_((_inside_ if s < 0 else _outside_), self.toRepr())
|
|
967
|
-
raise TriaxialError(eps=eps, sideOf=s, x=v.x, y=v.y, z=v.z, txt=t)
|
|
968
|
-
|
|
426
|
+
_ = self._sideOn(v, eps=eps)
|
|
427
|
+
h = _HeightINT0(h)
|
|
969
428
|
if h:
|
|
970
429
|
if normal:
|
|
971
430
|
v = v.plus(self.normal3d(*v.xyz, length=h))
|
|
@@ -988,7 +447,7 @@ class Triaxial(Triaxial_):
|
|
|
988
447
|
as this triaxial's semi-axes.
|
|
989
448
|
|
|
990
449
|
@see: Methods L{Triaxial.forwardLatLon} and L{Triaxial.forwardLatLon_}
|
|
991
|
-
and U{
|
|
450
|
+
and U{equations (4-5)<https://OLD.Topo.Auth.GR/wp-content/uploads/
|
|
992
451
|
sites/111/2021/12/09_Panou.pdf>}.
|
|
993
452
|
'''
|
|
994
453
|
v = _otherV3d_(x_xyz, y, z)
|
|
@@ -996,14 +455,40 @@ class Triaxial(Triaxial_):
|
|
|
996
455
|
self._c2_b2, # == 1 - e_sub_y**2
|
|
997
456
|
_1_0)
|
|
998
457
|
a, b, h = _reverseLatLon3(s, atan2d, v, self.forwardLatLon_)
|
|
999
|
-
return LatLon3Tuple(
|
|
458
|
+
return LatLon3Tuple(Lat(a), Lon(b), h, **name)
|
|
1000
459
|
|
|
1001
460
|
|
|
1002
|
-
class
|
|
1003
|
-
'''
|
|
1004
|
-
where the C{X} and C{Y} grid lines are straight.
|
|
461
|
+
class TriaxialB(_Triaxial3Base):
|
|
462
|
+
'''I{Ordered} triaxial ellipsoid specified by its middle semi-axis and shape.
|
|
1005
463
|
|
|
1006
|
-
|
|
464
|
+
@see: L{Triaxial} for details and more information.
|
|
465
|
+
'''
|
|
466
|
+
def __init__(self, b, e2=_0_0, k2=_1_0, kp2=_0_0, **name):
|
|
467
|
+
'''New L{TriaxialB} triaxial.
|
|
468
|
+
|
|
469
|
+
@arg b: Middle semi-axis (C{meter}, conventionally).
|
|
470
|
+
@kwarg e2: Excentricty I{squared} (C{scalar, e2 = (a**2 - c**2) / B{b}**2}).
|
|
471
|
+
@kwarg k2: Oblateness I{squared} (C{scalar, k2 = (C{b}**2 - c**2) / (a**2 - c**2)}).
|
|
472
|
+
@kwarg kp2: Prolateness I{squared} (C{scalar, kp2 = (a**2 - C{b}**2) / (a**2 - c**2)}).
|
|
473
|
+
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
474
|
+
|
|
475
|
+
@note: The semi-axes must be ordered as C{B{a} >= B{b} >= B{c} > 0} but
|
|
476
|
+
may be spherical, C{B{e2} == 0}, i.e. C{B{a} == B{c}}. In that case
|
|
477
|
+
C{B{k2} == 0} represents a I{prolate sphere}, C{B{k2} == 1} an
|
|
478
|
+
I{oblate sphere}, otherwise a I{triaxial sphere} with C{0 < B{k2} < 1}.
|
|
479
|
+
|
|
480
|
+
@note: The B{C{k2}} and B{C{kp2}} arguments are normalized, C{B{k2} + B{kp2} == 1}.
|
|
481
|
+
|
|
482
|
+
@raise TriaxialError: Semi-axes unordered or invalid.
|
|
483
|
+
'''
|
|
484
|
+
self._init_abc3_e2_k2_kp2(Radius_(b=b), e2, k2, kp2, **name)
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
class Conformal(Triaxial):
|
|
488
|
+
'''This is a I{Jacobi Conformal} projection of a triaxial ellipsoid to a plane where
|
|
489
|
+
the C{X} and C{Y} grid lines are straight.
|
|
490
|
+
|
|
491
|
+
I{Ellipsoidal} coordinates I{beta} and I{omega} are converted to Jacobi Conformal
|
|
1007
492
|
I{y} respectively I{x} separately. Jacobi's coordinates have been multiplied
|
|
1008
493
|
by C{sqrt(B{a}**2 - B{c}**2) / (2 * B{b})} so that the customary results are
|
|
1009
494
|
returned in the case of an ellipsoid of revolution.
|
|
@@ -1020,6 +505,34 @@ class ConformalTriaxial(Triaxial):
|
|
|
1020
505
|
C. G. J. I{U{Vorlesungen über Dynamik<https://Books.Google.com/books?
|
|
1021
506
|
id=ryEOAAAAQAAJ&pg=PA212>}}, page 212ff.
|
|
1022
507
|
'''
|
|
508
|
+
if _FOR_DOCS:
|
|
509
|
+
__init__ = Triaxial.__init__
|
|
510
|
+
|
|
511
|
+
@Property_RO
|
|
512
|
+
def _a2_b(self):
|
|
513
|
+
return self._a2_b2 * self.b
|
|
514
|
+
|
|
515
|
+
@Property_RO
|
|
516
|
+
def _c2_b(self):
|
|
517
|
+
return self._c2_b2 * self.b
|
|
518
|
+
|
|
519
|
+
def x(self, omega, unit=Radians):
|
|
520
|
+
'''Compute a I{Jacobi Conformal} C{x} projection.
|
|
521
|
+
|
|
522
|
+
@arg omega: Ellipsoidal longitude (C{Ang} or B{C{unit}}).
|
|
523
|
+
@kwarg unit: Unit of scalar B{C{omega}} (or C{Degrees}).
|
|
524
|
+
|
|
525
|
+
@return: The C{x} projection (L{Meter}), same units as
|
|
526
|
+
this triaxial's semi-axes.
|
|
527
|
+
'''
|
|
528
|
+
s, c = _SinCos2(omega, unit)
|
|
529
|
+
return Meter(x=self._x(s, c, self._a2_b))
|
|
530
|
+
|
|
531
|
+
def _x(self, s, c, a2_b_):
|
|
532
|
+
'''(INTERNAL) Helper for C{.x}, C{.xR_} and C{.xy}.
|
|
533
|
+
'''
|
|
534
|
+
s, c = self._norm2(s, c, self.a)
|
|
535
|
+
return self._xE.fPi(s, c) * a2_b_
|
|
1023
536
|
|
|
1024
537
|
@Property_RO
|
|
1025
538
|
def _xE(self):
|
|
@@ -1029,14 +542,15 @@ class ConformalTriaxial(Triaxial):
|
|
|
1029
542
|
# -a2b2 / b2 == (b2 - a2) / b2 == 1 - a2 / b2 == 1 - a2_b2
|
|
1030
543
|
return self._Elliptic(k2, _1_0 - self._a2_b2, kp2, self._a2_b2)
|
|
1031
544
|
|
|
1032
|
-
def xR(self, omega):
|
|
545
|
+
def xR(self, omega, unit=Radians):
|
|
1033
546
|
'''Compute a I{Jacobi Conformal} C{x} projection.
|
|
1034
547
|
|
|
1035
|
-
@arg omega: Ellipsoidal longitude (C{
|
|
548
|
+
@arg omega: Ellipsoidal longitude (C{Ang} or B{C{unit}}).
|
|
549
|
+
@kwarg unit: Unit of scalar B{C{omega}} (or C{Degrees}).
|
|
1036
550
|
|
|
1037
551
|
@return: The C{x} projection (L{Radians}).
|
|
1038
552
|
'''
|
|
1039
|
-
return self.xR_(*
|
|
553
|
+
return self.xR_(*_SinCos2(omega, unit))
|
|
1040
554
|
|
|
1041
555
|
def xR_(self, somega, comega):
|
|
1042
556
|
'''Compute a I{Jacobi Conformal} C{x} projection.
|
|
@@ -1046,28 +560,55 @@ class ConformalTriaxial(Triaxial):
|
|
|
1046
560
|
|
|
1047
561
|
@return: The C{x} projection (L{Radians}).
|
|
1048
562
|
'''
|
|
1049
|
-
|
|
1050
|
-
|
|
563
|
+
return Radians(x=self._x(somega, comega, self._a2_b2))
|
|
564
|
+
|
|
565
|
+
def xy(self, beta, omega, **unit_name):
|
|
566
|
+
'''Compute a I{Jacobi Conformal} C{x} and C{y} projection.
|
|
567
|
+
|
|
568
|
+
@arg beta: Ellipsoidal latitude (C{Ang} or B{C{unit}}).
|
|
569
|
+
@arg omega: Ellipsoidal longitude (C{Ang} or B{C{unit}}).
|
|
570
|
+
@kwarg unit_name: Optional scalar C{B{unit}=}L{Radians} and
|
|
571
|
+
name (C{str}), overriding C{B{name}="xyR2"}.
|
|
572
|
+
|
|
573
|
+
@return: A (L{Vector2Tuple}C{(x, y)}), both in C{Meter},
|
|
574
|
+
same units as this triaxial's semi-axes..
|
|
575
|
+
'''
|
|
576
|
+
unit, name = _xkwds_pop2(unit_name, unit=Radians)
|
|
577
|
+
return Vector2Tuple(self.x(omega, unit=unit),
|
|
578
|
+
self.y(beta, unit=unit),
|
|
579
|
+
name=_name__(name, name__=self.xy))
|
|
1051
580
|
|
|
1052
581
|
@Property_RO
|
|
1053
582
|
def xyQ2(self):
|
|
1054
|
-
'''Get the I{Jacobi Conformal} quadrant size
|
|
583
|
+
'''Get the I{Jacobi Conformal} quadrant size in C{meter}
|
|
584
|
+
(L{Vector2Tuple}C{(x, y)}).
|
|
585
|
+
'''
|
|
586
|
+
return Vector2Tuple(Meter(x=self._a2_b * self._xE.cPi),
|
|
587
|
+
Meter(y=self._c2_b * self._yE.cPi),
|
|
588
|
+
name=Conformal.xyQ2.name)
|
|
589
|
+
|
|
590
|
+
@Property_RO
|
|
591
|
+
def xyQR2(self):
|
|
592
|
+
'''Get the I{Jacobi Conformal} quadrant size in C{Radians}
|
|
593
|
+
(L{Conformal2Tuple}C{(x, y)}).
|
|
1055
594
|
'''
|
|
1056
595
|
return Conformal2Tuple(Radians(x=self._a2_b2 * self._xE.cPi),
|
|
1057
596
|
Radians(y=self._c2_b2 * self._yE.cPi),
|
|
1058
|
-
name=
|
|
597
|
+
name=Conformal.xyQR2.name)
|
|
1059
598
|
|
|
1060
|
-
def xyR2(self, beta, omega, **
|
|
599
|
+
def xyR2(self, beta, omega, **unit_name):
|
|
1061
600
|
'''Compute a I{Jacobi Conformal} C{x} and C{y} projection.
|
|
1062
601
|
|
|
1063
|
-
@arg beta: Ellipsoidal latitude (C{
|
|
1064
|
-
@arg omega: Ellipsoidal longitude (C{
|
|
1065
|
-
@kwarg
|
|
602
|
+
@arg beta: Ellipsoidal latitude (C{Ang} or B{C{unit}}).
|
|
603
|
+
@arg omega: Ellipsoidal longitude (C{Ang} or B{C{unit}}).
|
|
604
|
+
@kwarg unit_name: Optional scalar C{B{unit}=}L{Radians} and
|
|
605
|
+
name (C{str}), overriding C{B{name}="xyR2"}.
|
|
1066
606
|
|
|
1067
|
-
@return: A L{Conformal2Tuple}C{(x, y)}.
|
|
607
|
+
@return: A L{Conformal2Tuple}C{(x, y)}, both in C{Radians}.
|
|
1068
608
|
'''
|
|
1069
|
-
|
|
1070
|
-
|
|
609
|
+
unit, name = _xkwds_pop2(unit_name, unit=Radians)
|
|
610
|
+
sb_cb_so_co = _SinCos2(beta, unit) + _SinCos2(omega, unit)
|
|
611
|
+
return self.xyR2_(*sb_cb_so_co, name=_name__(name, name__=self.xyR2))
|
|
1071
612
|
|
|
1072
613
|
def xyR2_(self, sbeta, cbeta, somega, comega, **name):
|
|
1073
614
|
'''Compute a I{Jacobi Conformal} C{x} and C{y} projection.
|
|
@@ -1084,22 +625,41 @@ class ConformalTriaxial(Triaxial):
|
|
|
1084
625
|
self.yR_(sbeta, cbeta),
|
|
1085
626
|
name=_name__(name, name__=self.xyR2_))
|
|
1086
627
|
|
|
628
|
+
def y(self, beta, unit=Radians):
|
|
629
|
+
'''Compute a I{Jacobi Conformal} C{y} projection.
|
|
630
|
+
|
|
631
|
+
@arg beta: Ellipsoidal latitude (C{Ang} or B{C{unit}}).
|
|
632
|
+
@kwarg unit: Unit of scalar B{C{beta}} (or C{Degrees}).
|
|
633
|
+
|
|
634
|
+
@return: The C{y} projection (L{Meter}), same units as
|
|
635
|
+
this triaxial's semi-axes.
|
|
636
|
+
'''
|
|
637
|
+
s, c = _SinCos2(beta, unit)
|
|
638
|
+
return Meter(y=self._y(s, c, self._c2_b))
|
|
639
|
+
|
|
640
|
+
def _y(self, s, c, c2_b_):
|
|
641
|
+
'''(INTERNAL) Helper for C{.y}, C{.yR_} and C{.xy}.
|
|
642
|
+
'''
|
|
643
|
+
s, c = self._norm2(s, c, self.c)
|
|
644
|
+
return self._yE.fPi(s, c) * c2_b_
|
|
645
|
+
|
|
1087
646
|
@Property_RO
|
|
1088
647
|
def _yE(self):
|
|
1089
648
|
'''(INTERNAL) Get the y-elliptic function.
|
|
1090
649
|
'''
|
|
1091
|
-
|
|
650
|
+
k2, kp2 = self._k2E_kp2E
|
|
1092
651
|
# b2c2 / b2 == (b2 - c2) / b2 == 1 - c2 / b2 == e2bc
|
|
1093
|
-
return self._Elliptic(
|
|
652
|
+
return self._Elliptic(kp2, self.e2bc, k2, self._c2_b2)
|
|
1094
653
|
|
|
1095
|
-
def yR(self, beta):
|
|
654
|
+
def yR(self, beta, unit=Radians):
|
|
1096
655
|
'''Compute a I{Jacobi Conformal} C{y} projection.
|
|
1097
656
|
|
|
1098
|
-
@arg beta: Ellipsoidal latitude (C{
|
|
657
|
+
@arg beta: Ellipsoidal latitude (C{Ang} or B{C{unit}}).
|
|
658
|
+
@kwarg unit: Unit of scalar B{C{beta}} (or C{Degrees}).
|
|
1099
659
|
|
|
1100
660
|
@return: The C{y} projection (L{Radians}).
|
|
1101
661
|
'''
|
|
1102
|
-
return self.yR_(*
|
|
662
|
+
return self.yR_(*_SinCos2(beta, unit))
|
|
1103
663
|
|
|
1104
664
|
def yR_(self, sbeta, cbeta):
|
|
1105
665
|
'''Compute a I{Jacobi Conformal} C{y} projection.
|
|
@@ -1109,40 +669,36 @@ class ConformalTriaxial(Triaxial):
|
|
|
1109
669
|
|
|
1110
670
|
@return: The C{y} projection (L{Radians}).
|
|
1111
671
|
'''
|
|
1112
|
-
|
|
1113
|
-
return Radians(y=self._yE.fPi(s, c) * self._c2_b2)
|
|
672
|
+
return Radians(y=self._y(sbeta, cbeta, self._c2_b2))
|
|
1114
673
|
|
|
1115
674
|
|
|
1116
|
-
class ConformalSphere(
|
|
1117
|
-
'''
|
|
675
|
+
class ConformalSphere(Conformal):
|
|
676
|
+
'''Alternate, I{Jacobi Conformal projection} on a I{spherical} triaxial.
|
|
1118
677
|
|
|
1119
|
-
@see: L{
|
|
678
|
+
@see: L{Conformal<triaxials.triaxial5.Conformal>} for more information.
|
|
1120
679
|
'''
|
|
1121
680
|
_ab = _bc = 0
|
|
1122
681
|
|
|
1123
|
-
def __init__(self,
|
|
682
|
+
def __init__(self, radius_conformal, ab=0, bc=0, **name):
|
|
1124
683
|
'''New L{ConformalSphere}.
|
|
1125
684
|
|
|
1126
|
-
@arg
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
same units as C{scalar B{radius}}.
|
|
685
|
+
@arg radius_conformal: Radius (C{scalar}, conventionally in C{meter})
|
|
686
|
+
or an other L{ConformalSphere} or L{Conformal}.
|
|
687
|
+
@kwarg ab: Relative magnitude of C{B{a} - B{b}} (C{meter}, same units
|
|
688
|
+
as C{scalar B{radius}}.
|
|
689
|
+
@kwarg bc: Relative magnitude of C{B{b} - B{c}} (C{meter}, same units
|
|
690
|
+
as C{scalar B{radius}}.
|
|
1133
691
|
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
1134
692
|
|
|
1135
|
-
@raise TriaxialError: Invalid B{C{
|
|
1136
|
-
|
|
1137
|
-
+ B{bc})} not positive.
|
|
693
|
+
@raise TriaxialError: Invalid B{C{radius_conformal}}, negative B{C{ab}},
|
|
694
|
+
negative B{C{bc}} or C{(B{ab} + B{bc})} not positive.
|
|
1138
695
|
|
|
1139
|
-
@note: If B{C{
|
|
1140
|
-
and
|
|
1141
|
-
|
|
1142
|
-
and C{c} are copied.
|
|
696
|
+
@note: If B{C{radius_conformal}} is a L{ConformalSphere} and if B{C{ab}}
|
|
697
|
+
and B{C{bc}} are both zero or C{None}, the B{C{radius_conformal}}'s
|
|
698
|
+
C{ab}, C{bc}, C{a}, C{b} and C{c} are copied.
|
|
1143
699
|
'''
|
|
1144
700
|
try:
|
|
1145
|
-
r =
|
|
701
|
+
r = radius_conformal
|
|
1146
702
|
if isinstance(r, Triaxial): # ordered only
|
|
1147
703
|
t = r._abc3
|
|
1148
704
|
j = isinstance(r, ConformalSphere) and not bool(ab or bc)
|
|
@@ -1158,7 +714,7 @@ class ConformalSphere(ConformalTriaxial):
|
|
|
1158
714
|
and _isfinite(self._a2c2)):
|
|
1159
715
|
raise ValueError(_not_(_finite_))
|
|
1160
716
|
except (TypeError, ValueError) as x:
|
|
1161
|
-
raise TriaxialError(
|
|
717
|
+
raise TriaxialError(radius=r, ab=ab, bc=bc, cause=x)
|
|
1162
718
|
if name:
|
|
1163
719
|
self.name = name
|
|
1164
720
|
|
|
@@ -1201,12 +757,6 @@ class ConformalSphere(ConformalTriaxial):
|
|
|
1201
757
|
return self.a
|
|
1202
758
|
|
|
1203
759
|
|
|
1204
|
-
class TriaxialError(_ValueError):
|
|
1205
|
-
'''Raised for L{Triaxial} issues.
|
|
1206
|
-
'''
|
|
1207
|
-
pass # ...
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
760
|
class Triaxials(_NamedEnum):
|
|
1211
761
|
'''(INTERNAL) L{Triaxial} registry, I{must} be a sub-class
|
|
1212
762
|
to accommodate the L{_LazyNamedEnumItem} properties.
|
|
@@ -1222,33 +772,26 @@ Triaxials = Triaxials(Triaxial, Triaxial_) # PYCHOK singleton
|
|
|
1222
772
|
# <https://ArxIV.org/pdf/1909.06452.pdf> Table 1 Semi-axes in Km
|
|
1223
773
|
# <https://www.JPS.NASA.gov/education/images/pdf/ss-moons.pdf>
|
|
1224
774
|
# <https://link.Springer.com/article/10.1007/s00190-022-01650-9>
|
|
775
|
+
# <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Constants.html>
|
|
1225
776
|
_abc84_35 = (_EWGS84.a + 35), (_EWGS84.a - 35), _EWGS84.b
|
|
1226
|
-
Triaxials._assert( # a (Km) b (Km)
|
|
1227
|
-
Amalthea = _lazy('Amalthea', 125.0, 73.0,
|
|
1228
|
-
Ariel = _lazy('Ariel', 581.1, 577.9,
|
|
1229
|
-
Earth = _lazy('Earth', 6378.173435, 6378.1039,
|
|
1230
|
-
Enceladus = _lazy('Enceladus', 256.6, 251.4,
|
|
1231
|
-
Europa = _lazy('Europa', 1564.13, 1561.23,
|
|
1232
|
-
Io = _lazy('Io', 1829.4, 1819.3,
|
|
1233
|
-
Mars = _lazy('Mars', 3394.6, 3393.3,
|
|
1234
|
-
Mimas = _lazy('Mimas', 207.4, 196.8,
|
|
1235
|
-
Miranda = _lazy('Miranda', 240.4, 234.2,
|
|
1236
|
-
Moon = _lazy('Moon', 1735.55, 1735.324,
|
|
1237
|
-
Tethys = _lazy('Tethys', 535.6, 528.2,
|
|
777
|
+
Triaxials._assert( # a (Km) b (Km) c (Km) planet
|
|
778
|
+
Amalthea = _lazy('Amalthea', 125.0, 73.0, _64_0), # Jupiter
|
|
779
|
+
Ariel = _lazy('Ariel', 581.1, 577.9, 577.7), # Uranus
|
|
780
|
+
Earth = _lazy('Earth', 6378.173435, 6378.1039, 6356.7544),
|
|
781
|
+
Enceladus = _lazy('Enceladus', 256.6, 251.4, 248.3), # Saturn
|
|
782
|
+
Europa = _lazy('Europa', 1564.13, 1561.23, 1560.93), # Jupiter
|
|
783
|
+
Io = _lazy('Io', 1829.4, 1819.3, 1815.7), # Jupiter
|
|
784
|
+
Mars = _lazy('Mars', 3394.6, 3393.3, 3376.3),
|
|
785
|
+
Mimas = _lazy('Mimas', 207.4, 196.8, 190.6), # Saturn
|
|
786
|
+
Miranda = _lazy('Miranda', 240.4, 234.2, 232.9), # Uranus
|
|
787
|
+
Moon = _lazy('Moon', 1735.55, 1735.324, 1734.898), # Earth
|
|
788
|
+
Tethys = _lazy('Tethys', 535.6, 528.2, 525.8), # Saturn
|
|
789
|
+
WGS84_3 = _lazy('WGS84_3', 6378.17136, 6378.10161, 6356.75184), # C++
|
|
790
|
+
WGS84_3r = _lazy('WGS84_3r', 6378.172, 6378.102, 6356.752), # C++, rounded
|
|
1238
791
|
WGS84_35 = _lazy('WGS84_35', *map(m2km, _abc84_35)))
|
|
1239
792
|
del _abc84_35, _EWGS84
|
|
1240
793
|
|
|
1241
794
|
|
|
1242
|
-
def _getitems(items, *indices):
|
|
1243
|
-
'''(INTERNAL) Get the C{items} at the given I{indices}.
|
|
1244
|
-
|
|
1245
|
-
@return: C{Type(items[i] for i in indices)} with
|
|
1246
|
-
C{Type = type(items)}, any C{type} having
|
|
1247
|
-
the special method C{__getitem__}.
|
|
1248
|
-
'''
|
|
1249
|
-
return type(items)(map(items.__getitem__, indices))
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
795
|
def _hartzell3(pov, los, Tun): # in .Ellipsoid.hartzell4, .formy.hartzell
|
|
1253
796
|
'''(INTERNAL) Hartzell's "Satellite Line-of-Sight Intersection ...",
|
|
1254
797
|
formula from a Point-Of-View to an I{un-/ordered} Triaxial.
|
|
@@ -1314,7 +857,7 @@ def hartzell4(pov, los=False, tri_biax=_WGS84, **name):
|
|
|
1314
857
|
@kwarg los: Line-Of-Sight, I{direction} to the tri-/biaxial (L{Los}, L{Vector3d})
|
|
1315
858
|
or C{True} for the I{normal, perpendicular, plumb} to the surface of
|
|
1316
859
|
the tri-/biaxial or C{False} or C{None} to point to its center.
|
|
1317
|
-
@kwarg tri_biax: A triaxial (L{Triaxial}, L{Triaxial_}, L{
|
|
860
|
+
@kwarg tri_biax: A triaxial (L{Triaxial}, L{Triaxial_}, L{Conformal} or
|
|
1318
861
|
L{ConformalSphere}) or biaxial ellipsoid (L{Datum},
|
|
1319
862
|
L{Ellipsoid}, L{Ellipsoid2}, L{a_f2Tuple}) or spherical earth
|
|
1320
863
|
radius (C{scalar}, conventionally in C{meter}).
|
|
@@ -1335,12 +878,7 @@ def hartzell4(pov, los=False, tri_biax=_WGS84, **name):
|
|
|
1335
878
|
Line-of-Sight Intersection with Earth"<https://StephenHartzell.Medium.com/
|
|
1336
879
|
satellite-line-of-sight-intersection-with-earth-d786b4a6a9b6>}.
|
|
1337
880
|
'''
|
|
1338
|
-
|
|
1339
|
-
T = tri_biax
|
|
1340
|
-
else:
|
|
1341
|
-
D = tri_biax if isinstance(tri_biax, Datum) else \
|
|
1342
|
-
_spherical_datum(tri_biax, name__=hartzell4) # typename
|
|
1343
|
-
T = D.ellipsoid._triaxial
|
|
881
|
+
T = _tri_biaxial(tri_biax, hartzell4)
|
|
1344
882
|
try:
|
|
1345
883
|
v, h, i = _hartzell3(pov, los, T)
|
|
1346
884
|
except Exception as x:
|
|
@@ -1349,11 +887,58 @@ def hartzell4(pov, los=False, tri_biax=_WGS84, **name):
|
|
|
1349
887
|
return Vector4Tuple(v.x, v.y, v.z, h, iteration=i, name=n)
|
|
1350
888
|
|
|
1351
889
|
|
|
1352
|
-
def
|
|
1353
|
-
'''
|
|
1354
|
-
|
|
890
|
+
def height4(x_xyz, y=None, z=None, tri_biax=_WGS84, normal=True, eps=EPS, **name):
|
|
891
|
+
'''Compute the projection on and the height above or below a tri-/biaxial ellipsoid's surface.
|
|
892
|
+
|
|
893
|
+
@arg x_xyz: X component (C{scalar}) or a cartesian (C{Cartesian}, L{Ecef9Tuple},
|
|
894
|
+
L{Vector3d}, L{Vector3Tuple} or L{Vector4Tuple}).
|
|
895
|
+
@kwarg y: Y component (C{scalar}), required if B{C{x_xyz}} if C{scalar}, ignored
|
|
896
|
+
otherwise.
|
|
897
|
+
@kwarg z: Z component (C{scalar}), like B{C{y}}.
|
|
898
|
+
@kwarg normal: If C{True}, the projection is the I{perpendicular, plumb} to the
|
|
899
|
+
tri-/biaxial's surface, otherwise the C{radial} line to the center
|
|
900
|
+
of the tri-/biaxial (C{bool}).
|
|
901
|
+
@kwarg eps: Tolerance for root finding and validation (C{scalar}), use a negative
|
|
902
|
+
value to skip validation.
|
|
903
|
+
@kwarg name: Optional C{B{name}="height4"} (C{str}).
|
|
904
|
+
|
|
905
|
+
@return: L{Vector4Tuple}C{(x, y, z, h)} with the cartesian coordinates C{x}, C{y}
|
|
906
|
+
and C{z} of the projection on or the intersection with and with the height
|
|
907
|
+
C{h} above or below the tri-/biaxial's surface in C{meter}, conventionally.
|
|
908
|
+
|
|
909
|
+
@raise TriaxialError: Non-cartesian B{C{xyz}}, invalid B{C{eps}}, no convergence in
|
|
910
|
+
root finding or validation failed.
|
|
911
|
+
|
|
912
|
+
@see: Methods L{Triaxial.normal3d} and L{Ellipsoid.height4}, I{Eberly}'s U{Distance from a Point to ...
|
|
913
|
+
<https://www.GeometricTools.com/Documentation/DistancePointEllipseEllipsoid.pdf>} and I{Bektas}'
|
|
914
|
+
U{Shortest Distance from a Point to Triaxial Ellipsoid<https://www.ResearchGate.net/publication/
|
|
915
|
+
272149005_SHORTEST_DISTANCE_FROM_A_POINT_TO_TRIAXIAL_ELLIPSOID>}.
|
|
1355
916
|
'''
|
|
1356
|
-
|
|
917
|
+
v = _otherV3d_(x_xyz, y, z)
|
|
918
|
+
T = _tri_biaxial(tri_biax, height4)
|
|
919
|
+
r = T.isSpherical
|
|
920
|
+
|
|
921
|
+
i, h = None, v.length
|
|
922
|
+
if h < EPS0: # EPS
|
|
923
|
+
x = y = z = _0_0
|
|
924
|
+
h -= min(T._abc3) # nearest
|
|
925
|
+
elif r: # .isSpherical
|
|
926
|
+
x, y, z = v.times(r / h).xyz3
|
|
927
|
+
h -= r
|
|
928
|
+
else:
|
|
929
|
+
x, y, z = v.xyz3
|
|
930
|
+
try:
|
|
931
|
+
if normal: # plumb to surface
|
|
932
|
+
x, y, z, h, i = _plumbTo5(x, y, z, T, eps=eps)
|
|
933
|
+
else: # radial to center
|
|
934
|
+
x, y, z = T._radialTo3(z, hypot(x, y), y, x)
|
|
935
|
+
h = v.minus_(x, y, z).length
|
|
936
|
+
except Exception as e:
|
|
937
|
+
raise TriaxialError(x=x, y=y, z=z, cause=e)
|
|
938
|
+
if h > 0 and T.sideOf(v, eps=EPS0) < 0:
|
|
939
|
+
h = -h # inside
|
|
940
|
+
n = _name__(name, name__=height4) # typename
|
|
941
|
+
return Vector4Tuple(x, y, z, h, iteration=i, name=n)
|
|
1357
942
|
|
|
1358
943
|
|
|
1359
944
|
def _ordered(a, b, c):
|
|
@@ -1363,27 +948,8 @@ def _ordered(a, b, c):
|
|
|
1363
948
|
raise TriaxialError(a=a, b=b, c=c, txt=_not_ordered_)
|
|
1364
949
|
|
|
1365
950
|
|
|
1366
|
-
def _otherV3d_(x_xyz, y, z, **name):
|
|
1367
|
-
'''(INTERNAL) Get a Vector3d from C{x_xyz}, C{y} and C{z}.
|
|
1368
|
-
'''
|
|
1369
|
-
return Vector3d(x_xyz, y, z, **name) if isscalar(x_xyz) else \
|
|
1370
|
-
_otherV3d(x_xyz=x_xyz, **name)
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
def _over0(p, q):
|
|
1374
|
-
'''(INTERNAL) Return C{p / q} or C{0}.
|
|
1375
|
-
'''
|
|
1376
|
-
return (p / q) if q > fabs(p) else _0_0
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
def _over02(p, q):
|
|
1380
|
-
'''(INTERNAL) Return C{(p / q)**2} or C{0}.
|
|
1381
|
-
'''
|
|
1382
|
-
return (p / q)**2 if p and q else _0_0
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
951
|
def _plumbTo3(px, py, E, eps=EPS): # in .ellipsoids.Ellipsoid.height4
|
|
1386
|
-
'''(INTERNAL) Nearest point on a 2-D ellipse
|
|
952
|
+
'''(INTERNAL) Nearest point in 1st quadrant on a 2-D ellipse.
|
|
1387
953
|
'''
|
|
1388
954
|
a, b = E.a, E.b
|
|
1389
955
|
if min(px, py, a, b) < EPS0:
|
|
@@ -1548,8 +1114,8 @@ def _reverseLatLon3(s, atan2_, v, forward_):
|
|
|
1548
1114
|
d = hypot( x, y)
|
|
1549
1115
|
a = atan2_(z, d)
|
|
1550
1116
|
b = atan2_(y, x)
|
|
1551
|
-
h = v.minus_(*forward_(z, d, y, x)).length
|
|
1552
|
-
return a, b, h
|
|
1117
|
+
h = v.minus_(*forward_(z, d, y, x)).length
|
|
1118
|
+
return a, b, (h or INT0)
|
|
1553
1119
|
|
|
1554
1120
|
|
|
1555
1121
|
def _rootNd(r, s, u, v, w, g, eps=EPS0):
|
|
@@ -1584,10 +1150,16 @@ def _rootNd(r, s, u, v, w, g, eps=EPS0):
|
|
|
1584
1150
|
return t, i
|
|
1585
1151
|
|
|
1586
1152
|
|
|
1587
|
-
def
|
|
1588
|
-
'''(INTERNAL)
|
|
1153
|
+
def _tri_biaxial(tri_biax, where):
|
|
1154
|
+
'''(INTERNAL) Get a triaxail for C{tri_biax}.
|
|
1589
1155
|
'''
|
|
1590
|
-
|
|
1156
|
+
if isinstance(tri_biax, _UnOrderedTriaxialBase):
|
|
1157
|
+
T = tri_biax
|
|
1158
|
+
else:
|
|
1159
|
+
D = tri_biax if isinstance(tri_biax, Datum) else \
|
|
1160
|
+
_spherical_datum(tri_biax, name__=where) # typename
|
|
1161
|
+
T = D.ellipsoid._triaxial
|
|
1162
|
+
return T
|
|
1591
1163
|
|
|
1592
1164
|
|
|
1593
1165
|
def _validate(a, b, c, d, T, x, y, z, val):
|
|
@@ -1613,8 +1185,8 @@ if __name__ == _DMAIN_:
|
|
|
1613
1185
|
from pygeodesy import printf
|
|
1614
1186
|
from pygeodesy.interns import _COMMA_, _NL_, _NLATvar_
|
|
1615
1187
|
|
|
1616
|
-
|
|
1617
|
-
t =
|
|
1188
|
+
T = Triaxial_(6378388.0, 6378318.0, 6356911.9461)
|
|
1189
|
+
t = T.height4(3909863.9271, 3909778.123, 3170932.5016)
|
|
1618
1190
|
printf('# Bektas: %r', t)
|
|
1619
1191
|
|
|
1620
1192
|
# __doc__ of this file, force all into registery
|
|
@@ -1627,7 +1199,7 @@ if __name__ == _DMAIN_:
|
|
|
1627
1199
|
|
|
1628
1200
|
# **) MIT License
|
|
1629
1201
|
#
|
|
1630
|
-
# Copyright (C) 2022-
|
|
1202
|
+
# Copyright (C) 2022-2026 -- mrJean1 at Gmail -- All Rights Reserved.
|
|
1631
1203
|
#
|
|
1632
1204
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
1633
1205
|
# copy of this software and associated documentation files (the "Software"),
|