pygeodesy 25.12.12__py2.py3-none-any.whl → 26.1.16__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/internals.py CHANGED
@@ -6,8 +6,8 @@ u'''Mostly INTERNAL functions, except L{machine}, L{print_} and L{printf}.
6
6
  # from pygeodesy.basics import isiterablen, ubstr # _MODS
7
7
  # from pygeodesy.errors import _AttributeError, _error_init, _ImmutableError, _UnexpectedError, _xError2 # _MODS
8
8
  from pygeodesy.interns import _BAR_, _COLON_, _DASH_, _DMAIN_, _DOT_, _ELLIPSIS_, _NL_, NN, \
9
- _pygeodesy_, _PyPy__, _python_, _QUOTE1_, _QUOTE2_, _s_, _sys, \
10
- _SPACE_, _UNDER_
9
+ _NLATvar_, _pygeodesy_, _PyPy__, _python_, _QUOTE1_, _QUOTE2_, \
10
+ _s_, _sys, _SPACE_, _UNDER_
11
11
  from pygeodesy.interns import _COMMA_, _Python_ # PYCHOK used!
12
12
  # from pygeodesy.streprs import anstr, pairs, unstr # _MODS
13
13
 
@@ -459,6 +459,13 @@ def _popen2(cmd, stdin=None): # in .mgrs, .solveBase, .testMgrs
459
459
  return _MODS.basics.ub2str(r).strip(), p.returncode
460
460
 
461
461
 
462
+ def _pregistry(registry):
463
+ '''(INTERNAL) Print all items of a C{registry}.
464
+ '''
465
+ t = [NN] + registry.toRepr(all=True, asorted=True).split(_NL_)
466
+ printf(_NLATvar_.join(i.strip(_COMMA_) for i in t))
467
+
468
+
462
469
  def print_(*args, **nl_nt_prec_prefix__end_file_flush_sep__kwds): # PYCHOK no cover
463
470
  '''Python 3+ C{print}-like formatting and printing.
464
471
 
@@ -709,7 +716,7 @@ def _versions(sep=_SPACE_):
709
716
 
710
717
 
711
718
  __all__ = tuple(map(typename, (machine, print_, printf, typename)))
712
- __version__ = '25.10.26'
719
+ __version__ = '26.01.13'
713
720
 
714
721
  if __name__ == _DMAIN_:
715
722
 
pygeodesy/karney.py CHANGED
@@ -153,14 +153,14 @@ in C{pygeodesy} are based on I{Karney}'s post U{Area of a spherical polygon
153
153
  # make sure int/int division yields float quotient, see .basics
154
154
  from __future__ import division as _; del _ # noqa: E702 ;
155
155
 
156
- from pygeodesy.basics import _copysign, _isin, isint, neg, unsigned0, \
157
- _xgeographiclib, _zip
156
+ from pygeodesy.basics import _copysign, _isin, isint, neg, _xgeographiclib, _zip
158
157
  from pygeodesy.constants import NAN, _isfinite as _math_isfinite, \
159
158
  _0_0, _1_0, _2_0, _180_0, _N_180_0, _360_0
160
159
  # from pygeodesy.deprecated.classes import Rhumb7Tuple # _MODS
161
160
  from pygeodesy.errors import GeodesicError, _ValueError, _xkwds
162
161
  # from pygeodesy.geod3Solve import Geod3Solve8Tuple # _MODS
163
- from pygeodesy.fmath import cbrt, fremainder, norm2 # Fhorner, Fsum
162
+ from pygeodesy.fmath import cbrt, fhorner, fremainder, norm2
163
+ from pygeodesy.fsums import _Ksum
164
164
  from pygeodesy.internals import _getenv, _popen2, _PYGEODESY_ENV, typename, \
165
165
  _version_info
166
166
  from pygeodesy.interns import NN, _a12_, _area_, _azi2_, _azi12_, _composite_, \
@@ -177,7 +177,7 @@ from pygeodesy.utily import atan2d, sincos2d, tand, _unrollon, fabs
177
177
  # from math import fabs # from .utily
178
178
 
179
179
  __all__ = _ALL_LAZY.karney
180
- __version__ = '25.12.12'
180
+ __version__ = '25.12.23'
181
181
 
182
182
  _1_16th = _1_0 / 16
183
183
  _2_4_ = '2.4'
@@ -913,20 +913,18 @@ try:
913
913
 
914
914
  except ImportError: # Python 3.12-
915
915
 
916
- def _poly_fma(x, s, *cs): # PYCHOK redef
917
- t = _0_0
918
- for c in cs:
919
- s, t, _ = _sum3(s * x, t * x, c)
920
- return s + t
916
+ def _poly_fma(x, *cs): # PYCHOK redef
917
+ return fhorner(x, *cs, incx=False)
921
918
 
922
919
  # def _poly_fma(x, s, *cs):
923
- # S = Fhorner(x, *cs, incx=False)
924
- # S += s
925
- # return float(S)
920
+ # t = _0_0
921
+ # for c in cs:
922
+ # s, t, _ = _sum3(s * x, t * x, c)
923
+ # return s + t
926
924
 
927
925
  def _polynomial(x, cs, i, j): # PYCHOK shared
928
- '''(INTERNAL) Like C++ C{GeographicLib.Math.hpp.polyval} but with a
929
- signature and cascaded summation different from C{karney._sum3}.
926
+ '''(INTERNAL) Like C++ C{GeographicLib.Math.hpp.polyval} but
927
+ with a different signature and cascaded summation.
930
928
 
931
929
  @return: M{sum(x**(j - k - 1) * cs[k] for k in range(i, j)}
932
930
  '''
@@ -1007,18 +1005,18 @@ def _sum2(a, b): # mimick geomath.Math.sum, actually sum2
1007
1005
  r = s - b
1008
1006
  t = s - r
1009
1007
  # elif C_CPP: # Math::sum C/C++
1010
- # r -= a; t -= b; t += r; t = -t
1011
- # else:
1012
- t = (a - r) + (b - t)
1008
+ # r -= a; t -= b; t += r; t = (-t) if s else s
1009
+ # else: # if s == 0: t = _copysign_0_0(s)
1010
+ t = ((a - r) + (b - t)) if s else s
1013
1011
  # assert fabs(s) >= fabs(t)
1014
1012
  return s, t
1015
1013
 
1016
1014
 
1017
1015
  def _sum3(s, t, *xs):
1018
- '''Accumulate any B{C{xs}} into a previous C{_sum2(s, t)}.
1016
+ '''Accumulate all B{C{xs}} scalars into a previous C{_sum2(s, t)}.
1019
1017
 
1020
1018
  @return: 3-Tuple C{(s, t, n)} where C{s} is the sum of B{s}, B{t} and all
1021
- B{xs}, C{t} the residual and C{n} the number of zero C{xs}.
1019
+ B{xs}, C{t} the residual and C{n} the number of non-zero C{xs}.
1022
1020
 
1023
1021
  @see: I{Karney's} C++ U{Accumulator<https://GeographicLib.SourceForge.io/
1024
1022
  C++/doc/Accumulator_8hpp_source.html>} comments for more details and
@@ -1026,23 +1024,10 @@ def _sum3(s, t, *xs):
1026
1024
 
1027
1025
  @note: Not "error-free", see C{pygeodesy.test/testKarney.py}.
1028
1026
  '''
1029
- z = 0
1030
- for x in xs:
1031
- if x:
1032
- t, r = _sum2(t, x) # start at the least-
1033
- if s:
1034
- s, t = _sum2(s, t) # -significant end
1035
- if s:
1036
- t += r # accumulate r into t
1037
- else:
1038
- # assert t == 0 # s == 0 implies t == 0
1039
- s = unsigned0(r) # result is r, t = 0
1040
- else:
1041
- s, t = unsigned0(t), r
1042
- else:
1043
- z += 1
1044
- # assert fabs(s) >= fabs(t)
1045
- return s, t, z
1027
+ return _Ksum(s, t, *xs)._s_t_n3 if xs else (s, t, 0)
1028
+ # previous _sum3 in .geodesicx.gxarea._Accumulator.Add
1029
+ # which fails .fmath.frandoms tests, but does pass
1030
+ # _sum3(1e20, 1, 2, 100, 5000, -1e20) ... 5103.0, 0.0, 4
1046
1031
 
1047
1032
 
1048
1033
  def _tand(x):
pygeodesy/lazily.py CHANGED
@@ -30,8 +30,8 @@ and line number.
30
30
  from pygeodesy import internals as _internals, interns as _interns, \
31
31
  _isfrozen # DON'T _lazy_import2
32
32
  # from pygeodesy.errors import _error_init, _ImmutableError, _xkwds_item2 # _ALL_MODS
33
- from pygeodesy.internals import _caller3, _envPYGEODESY, _headof, printf, _tailof, \
34
- typename, _versions # _getenv, _PYGEODESY_ENV, \
33
+ from pygeodesy.internals import _caller3, _envPYGEODESY, _headof, printf, _Property_RO, \
34
+ _tailof, typename, _versions # _getenv, _PYGEODESY_ENV, \
35
35
  # _MODS_Base, _MODS.sys_version_info2
36
36
  from pygeodesy.interns import _attribute_, _by_, _COLONSPACE_, _COMMASPACE_, _DALL_, \
37
37
  _DMAIN_, _doesn_t_exist_, _DOT_, _EQUALSPACED_, _from_, \
@@ -234,7 +234,8 @@ _ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY',
234
234
  'a_f2b', 'a_f_2b', 'b_f2a', 'b_f_2a',
235
235
  'e2f', 'e22f',
236
236
  'f2e2', 'f2e22', 'f2e32', 'f_2f', 'f2f_', 'f2f2', 'f2n', 'n2e2', 'n2f', 'n2f_'),
237
- elliptic=_a('Elliptic', 'EllipticError', 'Elliptic3Tuple'),
237
+ elliptic=_a('Elliperim', 'Elliptic', 'EllipticError', 'Elliptic3Tuple',
238
+ 'elliperim', 'elliperim_'),
238
239
  epsg=_a('Epsg', 'EPSGError'),
239
240
  errors=_a('AuxError', 'ClipError', 'CrossError', 'GeodesicError', 'IntersectionError',
240
241
  'NumPyError', 'LenError', 'LimitError', 'MGRSError',
@@ -253,7 +254,7 @@ _ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY',
253
254
  formy=_a('Radical2Tuple',
254
255
  'angle2chord', 'antipode', 'antipode_', 'bearing', 'bearing_',
255
256
  'chord2angle', 'compassAngle', 'cosineLaw', 'cosineLaw_',
256
- 'elliperim', 'equirectangular', 'equirectangular4', 'euclidean', 'euclidean_',
257
+ 'equirectangular', 'equirectangular4', 'euclidean', 'euclidean_',
257
258
  'excessAbc_', 'excessCagnoli_', 'excessGirard_', 'excessLHuilier_',
258
259
  'excessKarney', 'excessKarney_', 'excessQuad', 'excessQuad_',
259
260
  'flatLocal', 'flatLocal_', 'flatPolar', 'flatPolar_',
@@ -517,10 +518,16 @@ class _ALL_MODS(_internals._MODS_Base):
517
518
  if _headof(n) == _pygeodesy_:
518
519
  yield n, m
519
520
 
521
+ @_Property_RO
522
+ def _triaxials_triaxial5(self):
523
+ '''(INTERNAL) Get module C{triaxial.triaxials}.
524
+ '''
525
+ return self.triaxials.triaxial5
526
+
520
527
  _internals._MODS = _ALL_MODS = _ALL_MODS() # PYCHOK singleton
521
528
 
522
529
  __all__ = _ALL_LAZY.lazily
523
- __version__ = '25.12.12'
530
+ __version__ = '26.01.06'
524
531
 
525
532
 
526
533
  def _ALL_OTHER(*objs):
pygeodesy/lcc.py CHANGED
@@ -651,13 +651,9 @@ def toLcc(latlon, conic=Conics.WRF_Lb, height=None, Lcc=Lcc,
651
651
 
652
652
 
653
653
  if __name__ == _DMAIN_:
654
-
655
- from pygeodesy.interns import _NL_, _NLATvar_
656
- from pygeodesy.lazily import printf
657
-
658
- # __doc__ of this file, force all into registery
659
- t = _NL_ + Conics.toRepr(all=True, asorted=True)
660
- printf(_NLATvar_.join(t.split(_NL_)))
654
+ # __doc__ of this file, force all into registry
655
+ from pygeodesy.internals import _pregistry
656
+ _pregistry(Conics)
661
657
 
662
658
  # **) MIT License
663
659
  #
pygeodesy/named.py CHANGED
@@ -35,7 +35,7 @@ from pygeodesy.streprs import attrs, Fmt, lrstrip, pairs, reprs, unstr
35
35
  # from pygeodesy.units import _toUnit # _MODS
36
36
 
37
37
  __all__ = _ALL_LAZY.named
38
- __version__ = '25.11.29'
38
+ __version__ = '26.01.14'
39
39
 
40
40
  _COMMANL_ = _COMMA_ + _NL_
41
41
  _COMMASPACEDOT_ = _COMMASPACE_ + _DOT_
@@ -658,18 +658,24 @@ class _NamedEnum(_NamedDict):
658
658
  '''(INTERNAL) Check attribute name against given, registered name.
659
659
  '''
660
660
  pypy = _isPyPy()
661
- _isa = isinstance
662
661
  for n, v in kwds.items():
663
- if _isa(v, _LazyNamedEnumItem): # property
662
+ if isinstance(v, _LazyNamedEnumItem): # property
664
663
  assert (n == v.name) if pypy else (n is v.name)
665
664
  # assert not hasattr(self.__class__, n)
666
665
  setattr(self.__class__, n, v)
667
- elif _isa(v, self._item_Classes): # PYCHOK no cover
666
+ elif isinstance(v, self._item_Classes): # PYCHOK no cover
668
667
  assert self[n] is v and getattr(self, n) \
669
668
  and self.find(v) == n
670
669
  else:
671
670
  raise _TypeError(v, name=n)
672
671
 
672
+ def _asserts(self): # in .triaxials.triaxial3
673
+ '''(INTERNAL) Yield all asserted items.
674
+ '''
675
+ for n, p in tuple(type(self).__dict__.items()):
676
+ if isinstance(p, _LazyNamedEnumItem):
677
+ yield n, p
678
+
673
679
  def find(self, item, dflt=None, all=False):
674
680
  '''Find a registered item.
675
681
 
@@ -707,10 +713,8 @@ class _NamedEnum(_NamedDict):
707
713
  case-insensitive} order (C{bool}).
708
714
  '''
709
715
  if all: # instantiate any remaining L{_LazyNamedEnumItem}
710
- _isa = isinstance
711
- for n, p in tuple(type(self).__dict__.items()):
712
- if _isa(p, _LazyNamedEnumItem):
713
- _ = getattr(self, n)
716
+ for n, _ in self._asserts():
717
+ _ = getattr(self, n)
714
718
  return itemsorted(self) if asorted else ADict.items(self)
715
719
 
716
720
  def keys(self, **all_asorted):
@@ -857,7 +861,7 @@ def _lazyNamedEnumItem(name, *args, **kwds):
857
861
 
858
862
 
859
863
  class _NamedEnumItem(_NamedBase):
860
- '''(INTERNAL) Base class for items in a C{_NamedEnum} registery.
864
+ '''(INTERNAL) Base class for items in a C{_NamedEnum} registry.
861
865
  '''
862
866
  _enum = None
863
867
 
@@ -1188,7 +1192,7 @@ def callername(up=1, dflt=NN, source=False, underOK=False):
1188
1192
 
1189
1193
  @return: The callable name (C{str}) or B{C{dflt}} if none found.
1190
1194
  '''
1191
- try: # see .lazily._caller3
1195
+ try: # see .internals._caller3
1192
1196
  for u in range(up, up + 32):
1193
1197
  n, f, s = _caller3(u)
1194
1198
  if n and (underOK or n.startswith(_DUNDER_) or
pygeodesy/props.py CHANGED
@@ -26,7 +26,7 @@ from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, \
26
26
  from functools import wraps as _wraps
27
27
 
28
28
  __all__ = _ALL_LAZY.props
29
- __version__ = '25.05.26'
29
+ __version__ = '25.12.31'
30
30
 
31
31
  _class_ = 'class'
32
32
  _DNL_ = _NL_ * 2 # PYCHOK used!
@@ -696,8 +696,10 @@ class DeprecationWarnings(object):
696
696
  '''
697
697
  return self._Warnings if _WARNINGS_X_DEV else None
698
698
 
699
- DeprecationWarnings = DeprecationWarnings() # PYCHOK singleton
700
- _throwarning = DeprecationWarnings.throw
699
+ if not _FOR_DOCS: # PYCHOK force epydoc
700
+ DeprecationWarnings = DeprecationWarnings() # singleton
701
+ _throwarning = DeprecationWarnings.throw
702
+ # del _FOR_DOCS
701
703
 
702
704
  # **) MIT License
703
705
  #
pygeodesy/trf.py CHANGED
@@ -1,9 +1,7 @@
1
1
 
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
- u'''I{Veness}' Terrestrial Reference Frames (TRF).
5
-
6
- Classes L{RefFrame}, registry L{RefFrames} and L{TRFError}.
4
+ u'''I{Veness}' Terrestrial Reference Frames (TRF), classes L{RefFrame}, registry L{RefFrames} and L{TRFError}.
7
5
 
8
6
  Transcoded from I{Chris Veness'} (C) 2006-2024 JavaScript originals U{latlon-ellipsoidal-referenceframe.js
9
7
  <https://GitHub.com/ChrisVeness/geodesy/blob/master/latlon-ellipsoidal-referenceframe.js>} and
@@ -1756,7 +1754,7 @@ if __name__ == _DMAIN_:
1756
1754
  t = '%d,%3d,%3d' % t
1757
1755
  printf('# %s = %s = %s %s', f, e, t, x)
1758
1756
 
1759
- # __doc__ of this file, force all into registery
1757
+ # __doc__ of this file, force all into registry
1760
1758
  def _RFs():
1761
1759
  yield NN
1762
1760
  for t in RefFrames.toRepr(all=True).split(_NL_):
@@ -36,29 +36,31 @@ from __future__ import division as _; del _ # noqa: E702 ;
36
36
 
37
37
  # from pygeodesy.angles import Ang, isAng # _MODS
38
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
39
+ from pygeodesy.constants import EPS, EPS0, EPS02, EPS4, _EPS2e4, INT0, \
40
+ _isfinite, float0_, NAN, PI2, PI_3, PI4, \
41
+ _0_0, _1_0, _N_1_0, _4_0 # PYCHOK used!
42
+ # from pygeodesy.ellipsoids import Ellipsoid, _EWGS84 # _MODS
43
+ # from pygeodesy.elliptic import elliperim, Elliptic # _MODS
44
+ # from pygeodesy.errors import _ValueError, _xkwds # from .utily
44
45
  from pygeodesy.fmath import fmean_, hypot, norm2, sqrt0, fabs, sqrt
45
- from pygeodesy.formy import elliperim, _ValueError, _xkwds
46
46
  from pygeodesy.fsums import _Fsumf_, fsumf_, fsum1f_
47
+ # from pygeodesy.internals import typename # _MODS
47
48
  from pygeodesy.interns import _a_, _b_, _c_, _inside_, _not_, _NOTEQUAL_, _null_, \
48
49
  _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
50
+ from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS, _FOR_DOCS
51
+ from pygeodesy.named import _NamedEnum, _NamedEnumItem, _NamedTuple, _Pass # _MODS
51
52
  from pygeodesy.namedTuples import Vector4Tuple
52
53
  from pygeodesy.props import Property_RO, property_doc_, property_RO, property_ROver
54
+ # from pygeodesy.streprs import Fmt # _MODS
53
55
  from pygeodesy.units import Degrees, Easting, Float, Height, Height_, Meter2, Meter3, \
54
56
  Northing, Radius_, Scalar
55
- from pygeodesy.utily import asin1
57
+ from pygeodesy.utily import asin1, km2m, m2km, _ValueError, _xkwds
56
58
  from pygeodesy.vector3d import _otherV3d, Vector3d
57
59
 
58
60
  # from math import fabs, sqrt # from .fmath
59
61
 
60
62
  __all__ = _ALL_LAZY.triaxials_bases
61
- __version__ = '25.12.12'
63
+ __version__ = '26.01.14'
62
64
 
63
65
  _bet_ = 'bet' # PYCHOK shared
64
66
  _llk_ = 'llk' # PYCHOK shared
@@ -112,21 +114,42 @@ class LLK(object):
112
114
  _NOIDAL = (None, ELLIPSOIDAL)
113
115
  # _XCLUDE = (CONFORMAL, GEOGRAPHIC, PLANETOCENTRIC, PLANETODETIC)
114
116
 
117
+ def __getitem__(self, name):
118
+ llk = self.get(name, None)
119
+ if llk is None:
120
+ t = _MODS.internals.typename(self)
121
+ t = _MODS.streprs.Fmt.SQUARE(t, name)
122
+ raise _ValueError(t, name)
123
+ return llk
124
+
125
+ def get(self, name, dflt=None):
126
+ '''Get an C{LLK} by C{name}.
127
+ '''
128
+ llk = getattr(self, name, None)
129
+ return llk if isinstance(llk, _LLK) else dflt
130
+
115
131
  def items(self):
132
+ '''Yield all C{LLK (name, value)} pairs.
133
+ '''
116
134
  for n, llk in LLK.__class__.__dict__.items():
117
135
  if isinstance(llk, _LLK):
118
136
  yield n, llk
119
137
 
120
138
  def keys(self):
139
+ '''Yield all C{LLK} names.
140
+ '''
121
141
  for n, _ in self.items():
122
142
  yield n
123
143
 
124
144
  def values(self):
145
+ '''Yield all C{LLK} values.
146
+ '''
125
147
  for _, llk in self.items():
126
148
  yield llk
127
149
 
128
- LLK = LLK() # PYCHOK singleton
129
- # del _LLK
150
+ if not _FOR_DOCS: # PYCHOK force epydoc
151
+ LLK = LLK() # singleton
152
+ del _FOR_DOCS
130
153
 
131
154
 
132
155
  def _HeightINT0(h):
@@ -212,13 +235,6 @@ class _UnOrderedTriaxialBase(_NamedEnumItem):
212
235
  a, b, c = self._abc3
213
236
  return self.a2, self.b2, self.c2
214
237
 
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
238
  @Property_RO
223
239
  def _a2c2(self):
224
240
  '''(INTERNAL) Get C{a**2 - c**2} == E_sub_x**2.
@@ -279,13 +295,6 @@ class _UnOrderedTriaxialBase(_NamedEnumItem):
279
295
  d = b - c
280
296
  return (d * (b + c)) if d else _0_0
281
297
 
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
298
  @Property_RO
290
299
  def c(self):
291
300
  '''Get the C{smallest, z} semi-axis (C{meter}, same units as B{C{a}}).
@@ -339,13 +348,13 @@ class _UnOrderedTriaxialBase(_NamedEnumItem):
339
348
 
340
349
  @property_ROver
341
350
  def _Ellipsoid(self):
342
- '''(INTERNAL) Get class L{Ellipsoid}, I{once}.
351
+ '''(INTERNAL) Get class C{Ellipsoid}, I{once}.
343
352
  '''
344
353
  return _MODS.ellipsoids.Ellipsoid # overwrite property_ROver
345
354
 
346
355
  @property_ROver
347
356
  def _Elliptic(self):
348
- '''(INTERNAL) Get class L{Elliptic}, I{once}.
357
+ '''(INTERNAL) Get class C{Ellipsoid}, I{once}.
349
358
  '''
350
359
  return _MODS.elliptic.Elliptic # overwrite property_ROver
351
360
 
@@ -355,15 +364,14 @@ class _UnOrderedTriaxialBase(_NamedEnumItem):
355
364
 
356
365
  @see: Function L{hartzell4<triaxials.triaxial5.hartzell4>} for further details.
357
366
  '''
358
- return self._triaxials_triaxial5.hartzell4(pov, los=los, tri_biax=self, **name)
367
+ return _MODS.triaxials.hartzell4(pov, los=los, tri_biax=self, **name)
359
368
 
360
369
  def height4(self, x_xyz, y=None, z=None, normal=True, eps=EPS, **name):
361
370
  '''Compute the projection on and the height above or below this triaxial's surface.
362
371
 
363
372
  @see: Function L{height4<triaxials.triaxial5.height4>} for further details.
364
373
  '''
365
- m = self._triaxials_triaxial5
366
- return m.height4(x_xyz, y=y, z=z, tri_biax=self, normal=normal, eps=eps, **name)
374
+ return _MODS.triaxials.height4(x_xyz, y=y, z=z, tri_biax=self, normal=normal, eps=eps, **name)
367
375
 
368
376
  @Property_RO
369
377
  def isOrdered(self):
@@ -513,6 +521,27 @@ class _UnOrderedTriaxialBase(_NamedEnumItem):
513
521
  '''
514
522
  return self._kji if reverse else self._ijk
515
523
 
524
+ @Property_RO
525
+ def perimeter4ab(self):
526
+ '''Get the C{ab} ellipse' perimeter (C{scalar}).
527
+ '''
528
+ a, b, _ = self._abc3
529
+ return Float(perimeter4ab=_MODS.elliptic.elliperim(a, b))
530
+
531
+ @Property_RO
532
+ def perimeter4ac(self):
533
+ '''Get the C{ac} ellipse' perimeter (C{scalar}).
534
+ '''
535
+ a, _, c = self._abc3
536
+ return Float(perimeter4ac=_MODS.elliptic.elliperim(a, c))
537
+
538
+ @Property_RO
539
+ def perimeter4bc(self):
540
+ '''Get the C{bc} ellipse' perimeter (C{scalar}).
541
+ '''
542
+ _, b, c = self._abc3
543
+ return Float(perimeter4bc=_MODS.elliptic.elliperim(b, c))
544
+
516
545
  def _radialTo3(self, sbeta, cbeta, somega, comega):
517
546
  '''(INTERNAL) I{Unordered} helper for C{.height4}.
518
547
  '''
@@ -595,42 +624,24 @@ class _UnOrderedTriaxialBase(_NamedEnumItem):
595
624
  @return: This C{Triaxial}'s attributes (C{str}).
596
625
  '''
597
626
  T = _UnOrderedTriaxialBase
598
- C = self._triaxials_triaxial3.Triaxial3B
627
+ m = _MODS.triaxials
628
+ C = m.Triaxial3B
599
629
  if isinstance(self, C):
600
- t = T.b, C.e2, C.k2, C.kp2
630
+ t = T.b, C.e2, C.k2, C.kp2
601
631
  else:
602
- t = T.a, # props
603
- C = self._triaxials_triaxial5.ConformalSphere
632
+ t = T.a, # props
633
+ C = m.ConformalSphere
604
634
  t += (C.ab, C.bc) if isinstance(self, C) else (T.b, T.c)
605
635
  C = _Triaxial3Base
606
636
  t += (C.k2, C.kp2) if isinstance(self, C) else \
607
637
  (T.e2ab, T.e2bc, T.e2ac)
608
- for C in (self._triaxials_triaxial5.Conformal,
609
- self._triaxials_conformal3.Conformal3):
638
+ for C in (m.Conformal, m.Conformal3):
610
639
  if isinstance(self, C):
611
640
  t += C.xyQ2,
612
641
  break
613
642
  t += T.volume, T.area
614
643
  return self._instr(area_p=self.area_p(), prec=prec, props=t, **name)
615
644
 
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
645
  @Property_RO
635
646
  def unOrdered(self):
636
647
  '''Is this triaxial I{un-ordered} and I{not spherical} (C{bool})?
@@ -693,7 +704,7 @@ class _OrderedTriaxialBase(_UnOrderedTriaxialBase):
693
704
  aE.fF(r) * c2 / s)
694
705
  a = Meter2(area=a * b * PI2)
695
706
  else: # a == b > c
696
- a = self._Ellipsoid(a, b=c).areax
707
+ a = self._Ellipsoid(a, b=c).areax
697
708
  return a
698
709
 
699
710
  @Property_RO
@@ -827,7 +838,8 @@ class _Triaxial3Base(_OrderedTriaxialBase):
827
838
  @property_doc_(" longitude of the I{earth}'s major semi-axis C{a}, (L{Ang}), Karney's C{Triaxial_Earth_lon0}.")
828
839
  def Lon0(self):
829
840
  if self._Lon0 is None:
830
- self.Lon0 = -(1493 / 100) if self.name.startswith('WGS84_3') else 0
841
+ WGS84_3 = self.name.startswith('WGS84_3')
842
+ self.Lon0 = -(1493 / 100) if WGS84_3 else 0
831
843
  return self._Lon0
832
844
 
833
845
  @Lon0.setter # PYCHOK setter!
@@ -863,11 +875,52 @@ class _Triaxial3Base(_OrderedTriaxialBase):
863
875
 
864
876
 
865
877
  class TriaxialError(_ValueError):
866
- '''Raised for any cartesian or conformal triaxial issues.
878
+ '''Raised for any triaxial issue.
867
879
  '''
868
880
  pass # ...
869
881
 
870
882
 
883
+ class _TriaxialsBase(_NamedEnum):
884
+ '''(INTERNAL) C{Triaxial*} registry, I{must} be a sub-class
885
+ to accommodate the L{_LazyNamedEnumItem} properties.
886
+ '''
887
+ _assert_kwds = {} # like propertyROnce
888
+ _Triaxial = None # must be overloaded
889
+
890
+ def _Lazy(self, *abc, **name):
891
+ '''(INTERNAL) Instantiate the C{self._Triaxial}.
892
+ '''
893
+ return self._Triaxial(*abc, **name)
894
+
895
+ def _assert(self): # PYCHOK signature
896
+ kwds = _TriaxialsBase._assert_kwds
897
+ if not kwds:
898
+ _lazy = _MODS.named._lazyNamedEnumItem
899
+ EWGS84 = _MODS.ellipsoids._EWGS84
900
+ abc84_35 = map1(m2km, EWGS84.a + 35, EWGS84.a - 35, EWGS84.b)
901
+ # <https://ArxIV.org/pdf/1909.06452.pdf> Table 1 Semi-axes in Km
902
+ # <https://www.JPS.NASA.gov/education/images/pdf/ss-moons.pdf>
903
+ # <https://link.Springer.com/article/10.1007/s00190-022-01650-9>
904
+ # <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Constants.html>
905
+ for n, abc in dict( # a (Km) b (Km) c (Km) planet
906
+ Amalthea= (125.0, 73.0, 64.0), # Jupiter
907
+ Ariel= (581.1, 577.9, 577.7), # Uranus
908
+ Earth= (6378.173435, 6378.1039, 6356.7544),
909
+ Enceladus=(256.6, 251.4, 248.3), # Saturn
910
+ Europa= (1564.13, 1561.23, 1560.93), # Jupiter
911
+ Io= (1829.4, 1819.3, 1815.7), # Jupiter
912
+ Mars= (3394.6, 3393.3, 3376.3),
913
+ Mimas= (207.4, 196.8, 190.6), # Saturn
914
+ Miranda= (240.4, 234.2, 232.9), # Uranus
915
+ Moon= (1735.55, 1735.324, 1734.898), # Earth
916
+ Tethys= (535.6, 528.2, 525.8), # Saturn
917
+ WGS84_3= (6378.17136, 6378.10161, 6356.75184), # C++
918
+ WGS84_3r=(6378.172, 6378.102, 6356.752), # C++, rounded
919
+ WGS84_35=abc84_35).items():
920
+ kwds[n] = _lazy(n, *map(km2m, abc))
921
+ _NamedEnum._assert(self, **kwds)
922
+
923
+
871
924
  def _getitems(items, *indices):
872
925
  '''(INTERNAL) Get the C{items} at the given I{indices}.
873
926