pygeodesy 25.8.25__py2.py3-none-any.whl → 25.9.9__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 CHANGED
@@ -125,7 +125,7 @@ C{epydoc --html --no-private --no-source --name=pygeodesy --url=... -v pygeodesy
125
125
  Tests
126
126
  =====
127
127
 
128
- The tests ran with Python 3.13.5 (with U{geographiclib<https://PyPI.org/project/geographiclib>} 2.0),
128
+ The tests ran with Python 3.13.7 (with U{geographiclib<https://PyPI.org/project/geographiclib>} 2.1),
129
129
  Python 3.12.7 (with U{geographiclib<https://PyPI.org/project/geographiclib>} 2.0,
130
130
  U{numpy<https://PyPI.org/project/numpy>} 2.1.0, U{scipy<https://PyPI.org/project/scipy>} 1.14.1,
131
131
  U{GeodSolve<https://GeographicLib.SourceForge.io/C++/doc/utilities.html>} 2.5,
@@ -150,13 +150,13 @@ env variable C{PYGEODESY_WARNINGS=on} for all Python versions. The results of t
150
150
  the distribution files.
151
151
 
152
152
  Test coverage has been measured with U{coverage<https://PyPI.org/project/coverage>} 7.6.1 using Python
153
- 3.13.5, 3.12.7, 3.11.5 and 3.10.8. The complete coverage report in HTML and a PDF summary are included in
153
+ 3.13.7, 3.12.7, 3.11.5 and 3.10.8. The complete coverage report in HTML and a PDF summary are included in
154
154
  the distribution files.
155
155
 
156
- Python 3.13.5, 3.12.7, 3.11.5 and 3.10.8 run on Apple M4 Si (C{arm64}), I{natively}. Python 2.7.18 runs
156
+ Python 3.13.7, 3.12.7, 3.11.5 and 3.10.8 run on Apple M4 Si (C{arm64}), I{natively}. Python 2.7.18 runs
157
157
  on Intel (C{x86_64}) or Intel I{emulation} ("C{arm64_x86_64}", see function L{machine<pygeodesy.machine>}).
158
158
 
159
- The tests also ran with Python 3.13.5 (and U{geographiclib<https://PyPI.org/project/geographiclib>} 2.0) on
159
+ The tests also ran with Python 3.13.7 (and U{geographiclib<https://PyPI.org/project/geographiclib>} 2.1) on
160
160
  U{Debian 12<https://Cirrus-CI.com/github/mrJean1/PyGeodesy/master>} in 64-bit only, with Python 3.12.8 (and
161
161
  U{geographiclib<https://PyPI.org/project/geographiclib>} 2.0) on U{Windows 2019Server
162
162
  <https://CI.AppVeyor.com/project/mrJean1/pygeodesy>} in 64-bit only and with Python 2.7.18 (and U{geographiclib
@@ -166,7 +166,7 @@ in 64- and 32-bit.
166
166
  A single-File and single-Directory application with C{pygeodesy} has been bundled using U{PyInstaller
167
167
  <https://PyPI.org/project/pyinstaller>} 3.4 and 64-bit Python 3.7.3 on macOS 10.13.6 High Sierra.
168
168
 
169
- Previously, the tests were run with Python 3.13.0-4, 3.12.0-6, 3.11.2-4, 3.10.1-7, 3.9.6, 3.9.1, 3.8.7, 3.7.1, 2.7.15,
169
+ Previously, the tests were run with Python 3.13.0-5, 3.12.0-6, 3.11.2-4, 3.10.1-7, 3.9.6, 3.9.1, 3.8.7, 3.7.1, 2.7.15,
170
170
  U{PyPy<https://PyPy.org>} 7.3.12 (Python 3.10.12), 7.3.1 (Python 3.6.9) and U{PyPy<https://PyPy.org>} 7.1.1 (Python
171
171
  2.7.13) (and U{geographiclib <https://PyPI.org/project/geographiclib>} 1.52, U{numpy<https://PyPI.org/project/numpy>}
172
172
  1.16.3, 1.16.4, 1.16.6, 1.19.0, 1.19.4, 1.19.5 or 1.22.4 and U{scipy<https://PyPI.org/project/scipy>} 1.2.1, 1.4.1,
@@ -329,6 +329,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.}
329
329
  @var PI_3: One third PI, M{PI / 3} (C{float}).
330
330
  @var PI4: Four PI, M{PI * 4} (C{float}).
331
331
  @var PI_4: Quarter PI, M{PI / 4} (C{float}).
332
+ @var PI_6: One sixth PI, M{PI / 6} (C{float}).
332
333
 
333
334
  @var R_MA: Equatorial earth radius (C{meter}), WGS84, EPSG:3785.
334
335
  @var R_MB: Polar earth radius (C{meter}), WGS84, EPSG:3785.
@@ -604,7 +605,7 @@ else:
604
605
 
605
606
  from pygeodesy.internals import _version2, _DOT_ # noqa: E402
606
607
  # from pygeodesy.interns import _DOT_ # from .internals
607
- __version__ = '25.08.25'
608
+ __version__ = '25.09.09'
608
609
  # see setup.py for similar logic
609
610
  version = _DOT_(*_version2(__version__, n=3))
610
611
 
@@ -29,7 +29,7 @@ from pygeodesy.lazily import _ALL_OTHER
29
29
  # no modules: auxAngle, auxDLat, auxDST, auxily, auxLat
30
30
  __all__ = _ALL_OTHER(Aux, AuxAngle, AuxDLat, AuxDST, AuxLat,
31
31
  AuxBeta, AuxChi, AuxMu, AuxPhi, AuxTheta, AuxXi)
32
- __version__ = '25.04.14'
32
+ __version__ = '25.08.28'
33
33
 
34
34
  # **) MIT License
35
35
  #
@@ -17,7 +17,7 @@ from __future__ import division as _; del _ # noqa: E702 ;
17
17
  from pygeodesy.auxilats.auxily import Aux, _Aux2Greek, AuxError
18
18
  from pygeodesy.basics import map1, map2, _xinstanceof
19
19
  from pygeodesy.constants import EPS, _INF_NAN_NINF, MAX, NAN, _0_0, _0_5, _1_0, \
20
- _copysign_1_0, _over, _pos_self, isfinite, isnan
20
+ _copysign_1_0, isfinite, isnan, _over, _pos_self
21
21
  # from pygeodesy.errors import AuxError # from .auxilats.auxily
22
22
  from pygeodesy.fmath import hypot, unstr
23
23
  from pygeodesy.fsums import _add_op_, _iadd_op_, _isub_op_, _sub_op_
@@ -32,7 +32,7 @@ from pygeodesy.utily import atan2, atan2d, sincos2, sincos2d
32
32
  from math import asinh, copysign, degrees, fabs, radians, sinh
33
33
 
34
34
  __all__ = ()
35
- __version__ = '25.05.12'
35
+ __version__ = '25.08.31'
36
36
 
37
37
  _0_INF_NAN_NINF = (0, _0_0) + _INF_NAN_NINF
38
38
  _MAX_2 = MAX * _0_5 # PYCHOK used!
@@ -505,6 +505,7 @@ _AUXClass = {Aux.BETA: AuxBeta,
505
505
  Aux.CHI: AuxChi,
506
506
  Aux.MU: AuxMu,
507
507
  Aux.PHI: AuxPhi,
508
+ # Aux.PSI: AuxPSI, # Isometric
508
509
  Aux.THETA: AuxTheta,
509
510
  Aux.XI: AuxXi}
510
511
 
@@ -525,7 +526,7 @@ def _yx2(yx):
525
526
  return y, x
526
527
 
527
528
 
528
- __all__ += _ALL_DOCS(AuxAngle, AuxBeta, AuxChi, AuxMu, AuxPhi, AuxTheta, AuxXi)
529
+ __all__ += _ALL_DOCS(AuxAngle, *_AUXClass.values())
529
530
 
530
531
  # **) MIT License
531
532
  #
@@ -29,7 +29,7 @@ from pygeodesy.utily import atan1
29
29
  from math import asinh, copysign
30
30
 
31
31
  __all__ = ()
32
- __version__ = '25.05.12'
32
+ __version__ = '25.08.31'
33
33
 
34
34
 
35
35
  class Aux(object):
pygeodesy/basics.py CHANGED
@@ -37,7 +37,7 @@ from math import copysign as _copysign
37
37
  # import inspect as _inspect # _MODS
38
38
 
39
39
  __all__ = _ALL_LAZY.basics
40
- __version__ = '25.04.14'
40
+ __version__ = '25.09.04'
41
41
 
42
42
  _below_ = 'below'
43
43
  _list_tuple_types = (list, tuple)
@@ -201,7 +201,7 @@ def copytype(x, y):
201
201
 
202
202
 
203
203
  def _enumereverse(iterable):
204
- '''(INTERNAL) Reversed C{enumberate}.
204
+ '''(INTERNAL) Reversed C{enumerate}.
205
205
  '''
206
206
  for j in _reverange(len(iterable)):
207
207
  yield j, iterable[j]
@@ -714,11 +714,11 @@ def _reverange(n, stop=-1, step=-1):
714
714
 
715
715
 
716
716
  def signBit(x):
717
- '''Return C{signbit(B{x})}, like C++.
717
+ '''Return C{signbit(B{x})}, like C++, see also L{isneg}.
718
718
 
719
719
  @return: C{True} if C{B{x} < 0} or C{NEG0} (C{bool}).
720
720
  '''
721
- return x < 0 or _MODS.constants.isneg0(x)
721
+ return (x or _copysign(1, x)) < 0
722
722
 
723
723
 
724
724
  def _signOf(x, ref): # in .fsums
pygeodesy/booleans.py CHANGED
@@ -45,7 +45,7 @@ from pygeodesy.utily import fabs, _unrollon, _Wrap
45
45
  # from math import fabs # from .utily
46
46
 
47
47
  __all__ = _ALL_LAZY.booleans
48
- __version__ = '25.05.26'
48
+ __version__ = '25.09.04'
49
49
 
50
50
  _0EPS = EPS # near-zero, positive
51
51
  _EPS0 = -EPS # near-zero, negative
@@ -60,26 +60,26 @@ _corners_ = 'corners'
60
60
  _open_ = 'open'
61
61
 
62
62
 
63
- def _Enum(txt, enum): # PYCHOK unused
63
+ def _Cnum(txt, enum): # PYCHOK unused
64
64
  return txt # NN(txt, _TILDE_, enum)
65
65
 
66
66
 
67
67
  class _L(object): # Intersection labels
68
- CROSSING = _Enum(_X_, 1) # C++ enum
69
- CROSSING_D = _Enum(_X_ + _d_, 8)
68
+ CROSSING = _Cnum(_X_, 1) # C++ enum
69
+ CROSSING_D = _Cnum(_X_ + _d_, 8)
70
70
  CROSSINGs = (CROSSING, CROSSING_D)
71
- BOUNCING = _Enum(_B_, 2)
72
- BOUNCING_D = _Enum(_B_ + _d_, 9)
71
+ BOUNCING = _Cnum(_B_, 2)
72
+ BOUNCING_D = _Cnum(_B_ + _d_, 9)
73
73
  BOUNCINGs = (BOUNCING, BOUNCING_D) + CROSSINGs
74
- LEFT_ON = _Enum('Lo', 3)
75
- ON_ON = _Enum('oo', 5)
76
- ON_LEFT = _Enum('oL', 6)
77
- ON_RIGHT = _Enum('oR', 7)
78
- RIGHT_ON = _Enum('Ro', 4)
74
+ LEFT_ON = _Cnum('Lo', 3)
75
+ ON_ON = _Cnum('oo', 5)
76
+ ON_LEFT = _Cnum('oL', 6)
77
+ ON_RIGHT = _Cnum('oR', 7)
78
+ RIGHT_ON = _Cnum('Ro', 4)
79
79
  RIGHT_LEFT_ON = (RIGHT_ON, LEFT_ON)
80
80
  # Entry/Exit flags
81
- ENTRY = _Enum(_e_, 1)
82
- EXIT = _Enum(_x_, 0)
81
+ ENTRY = _Cnum(_e_, 1)
82
+ EXIT = _Cnum(_x_, 0)
83
83
  Toggle = {ENTRY: EXIT,
84
84
  EXIT: ENTRY,
85
85
  None: None}
@@ -88,10 +88,10 @@ _L = _L() # PYCHOK singleton
88
88
 
89
89
 
90
90
  class _RP(object): # RelativePositions
91
- IS_Pm = _Enum('Pm', 2) # C++ enum
92
- IS_Pp = _Enum('Pp', 3)
93
- LEFT = _Enum('L', 0)
94
- RIGHT = _Enum(_R_, 1)
91
+ IS_Pm = _Cnum('Pm', 2) # C++ enum
92
+ IS_Pp = _Cnum('Pp', 3)
93
+ LEFT = _Cnum('L', 0)
94
+ RIGHT = _Cnum(_R_, 1)
95
95
 
96
96
  _RP = _RP() # PYCHOK singleton
97
97
 
@@ -1521,16 +1521,16 @@ class _CompositeGH(_CompositeBase):
1521
1521
  class _EdgeFHP(object):
1522
1522
  # An edge between two L{LatLonFHP} points.
1523
1523
 
1524
- X_INTERSECT = _Enum('Xi', 1) # C++ enum
1525
- X_OVERLAP = _Enum('Xo', 5)
1526
- P_INTERSECT = _Enum('Pi', 3)
1527
- P_OVERLAP = _Enum('Po', 7)
1524
+ X_INTERSECT = _Cnum('Xi', 1) # C++ enum
1525
+ X_OVERLAP = _Cnum('Xo', 5)
1526
+ P_INTERSECT = _Cnum('Pi', 3)
1527
+ P_OVERLAP = _Cnum('Po', 7)
1528
1528
  Ps = (P_INTERSECT, P_OVERLAP, X_OVERLAP)
1529
- Q_INTERSECT = _Enum('Qi', 2)
1530
- Q_OVERLAP = _Enum('Qo', 6)
1529
+ Q_INTERSECT = _Cnum('Qi', 2)
1530
+ Q_OVERLAP = _Cnum('Qo', 6)
1531
1531
  Qs = (Q_INTERSECT, Q_OVERLAP, X_OVERLAP)
1532
- V_INTERSECT = _Enum('Vi', 4)
1533
- V_OVERLAP = _Enum('Vo', 8)
1532
+ V_INTERSECT = _Cnum('Vi', 4)
1533
+ V_OVERLAP = _Cnum('Vo', 8)
1534
1534
  Vs = (V_INTERSECT, V_OVERLAP)
1535
1535
 
1536
1536
  def __init__(self, p1, p2, **unused):
pygeodesy/constants.py CHANGED
@@ -10,7 +10,7 @@ L{pygeodesy.isnon0} and L{pygeodesy.remainder}.
10
10
  # make sure int/int division yields float quotient, see .basics
11
11
  from __future__ import division as _; del _ # noqa: E702 ;
12
12
 
13
- from pygeodesy.basics import _copysign, isbool, iscomplex, isint
13
+ from pygeodesy.basics import _copysign, isbool, iscomplex, isint, signBit
14
14
  from pygeodesy.errors import _xError, _xError2, _xkwds_get1, _xkwds_item2
15
15
  # from pygeodesy.fsums import _isFsum_2Tuple # _MODS
16
16
  from pygeodesy.internals import _0_0, _100_0, typename
@@ -26,25 +26,31 @@ except ImportError: # Python 2-
26
26
  _inf, _nan = float(_INF_), float(_NAN_)
27
27
 
28
28
  __all__ = _ALL_LAZY.constants
29
- __version__ = '25.05.26'
29
+ __version__ = '25.09.09'
30
30
 
31
31
 
32
32
  def _copysign_0_0(y):
33
33
  '''(INTERNAL) copysign(0.0, y), only C{float}.
34
34
  '''
35
- return _N_0_0 if y < 0 else _0_0
35
+ return _N_0_0 if signBit(y) else _0_0
36
36
 
37
37
 
38
38
  def _copysign_1_0(y):
39
39
  '''(INTERNAL) copysign(1.0, y), only C{float}.
40
40
  '''
41
- return _N_1_0 if y < 0 else _1_0
41
+ return _N_1_0 if signBit(y) else _1_0
42
42
 
43
43
 
44
44
  def _copysignINF(y):
45
45
  '''(INTERNAL) copysign(INF, y), only C{float}.
46
46
  '''
47
- return NINF if y < 0 else INF
47
+ return NINF if signBit(y) else INF
48
+
49
+
50
+ def _flipsign(x, y):
51
+ '''(INTERNAL) Negate C{x} for negative C{y}.
52
+ '''
53
+ return (-x) if signBit(y) else x
48
54
 
49
55
 
50
56
  def _Float(**name_arg):
@@ -137,7 +143,7 @@ def _naninf(p, *xs):
137
143
 
138
144
 
139
145
  def _over(p, q):
140
- '''(INTERNAL) Return C{B{p} / B{q}} avoiding C{ZeroDivisionError} exceptions.
146
+ '''(INTERNAL) Return C{B{p} / B{q}} without C{ZeroDivisionError} exceptions.
141
147
  '''
142
148
  try:
143
149
  return (p / q) # if p else _copysign_0_0(q)
@@ -145,6 +151,18 @@ def _over(p, q):
145
151
  return (_copysignINF(p) if isfinite(p) else NAN) if p else p
146
152
 
147
153
 
154
+ def _over_1(p, q):
155
+ '''(INTERNAL) Return C{B{p} / B{q}} with exact C{1.0} and without C{ZeroDivisionError} exceptions.
156
+ '''
157
+ if fabs(p) != fabs(q):
158
+ r = _over(p, q)
159
+ else:
160
+ r = _flipsign(p, q)
161
+ if p:
162
+ r = _copysign_1_0(r)
163
+ return r
164
+
165
+
148
166
  def _1_over(x):
149
167
  '''(INTERNAL) Return reciprocal C{1 / B{x}} avoiding C{ZeroDivisionError} exceptions.
150
168
  '''
@@ -245,7 +263,7 @@ _EPStol = _Float(_EPStol=_EPSqrt * _0_1) # PYCHOK = 1.49011611938e5-09 == sqr
245
263
 
246
264
  _89_999 = _Float(_89_999=_90_0 * EPS1) # just below 90.0
247
265
  # <https://Numbers.Computation.Free.FR/Constants/Miscellaneous/digits.html>
248
- _1__90 = _Float(_1__90 =_1_0 / _90_0) # PYCHOK = 0.011_111_111_111_111_111_111_111_111_111_111_111_111_111_111_11111
266
+ # _1__90 = _Float(_1__90 =_1_0 / _90_0) # PYCHOK = 0.011_111_111_111_111_111_111_111_111_111_111_111_111_111_111_11111
249
267
  _2__PI = _Float(_2__PI =_2_0 / _pi) # PYCHOK = 0.636_619_772_367_581_343_075_535_053_490_057_448_137_838_582_96182
250
268
 
251
269
  _1_16th = _Float(_1_16th=_1_0 / _16_0) # PYCHOK in .ellipsoids, .karney
@@ -273,6 +291,7 @@ PI3_2 = _Float(PI3_2=_pi * _1_5) # PYCHOK PI and a half, M{PI * 3 / 2}
273
291
  PI_3 = _Float(PI_3 =_pi / _3_0) # PYCHOK One third PI, M{PI / 3}
274
292
  PI4 = _Float(PI4 =_pi * _4_0) # PYCHOK Four PI, M{PI * 4}
275
293
  PI_4 = _Float(PI_4 =_pi / _4_0) # PYCHOK Quarter PI, M{PI / 4}
294
+ PI_6 = _Float(PI_6 =_pi / _6_0) # PYCHOK One sixth PI, M{PI / 6}
276
295
 
277
296
  R_MA = _Radius(R_MA=6378137.0) # PYCHOK equatorial earth radius (C{meter}), WGS84, EPSG:3785
278
297
  R_MB = _Radius(R_MB=6356752.3) # PYCHOK polar earth radius (C{meter}), WGS84, EPSG:3785
@@ -417,6 +436,17 @@ def isnear90(x, eps90=EPS0):
417
436
  return bool(eps90 > (x - _90_0) > -eps90)
418
437
 
419
438
 
439
+ def isneg(x):
440
+ '''Check for negative C{x}, including L{NEG0}.
441
+
442
+ @arg x: Value (C{scalar}).
443
+
444
+ @return: C{True} if C{B{x} < 0 or NEG0},
445
+ C{False} otherwise.
446
+ '''
447
+ return signBit(x)
448
+
449
+
420
450
  def isneg0(x):
421
451
  '''Check for L{NEG0}, negative C{0.0}.
422
452
 
@@ -4,6 +4,7 @@
4
4
  u'''DEPRECATED functions kept for backward compatibility.
5
5
  '''
6
6
 
7
+ # from pygeodesy.basics import copysign0 # _MODS_
7
8
  from pygeodesy.constants import EPS, R_M, float0_
8
9
  from pygeodesy.deprecated.classes import ClipCS3Tuple, TriAngle4Tuple, _TriAngle5Tuple
9
10
  from pygeodesy.interns import NN, _area_, _COMMASPACE_, _negative_, \
pygeodesy/dms.py CHANGED
@@ -63,7 +63,7 @@ U{Vector-based geodesy<https://www.Movable-Type.co.UK/scripts/latlong-vectors.ht
63
63
 
64
64
  from pygeodesy.basics import copysign0, isLatLon, isodd, issequence, isstr, \
65
65
  neg as _neg, typename # in .ups
66
- from pygeodesy.constants import _umod_360, _0_0, _0_5, _60_0, _360_0, _3600_0
66
+ from pygeodesy.constants import _0_0, _0_5, _60_0, _360_0, _3600_0, _umod_360
67
67
  from pygeodesy.errors import ParseError, RangeError, _TypeError, _ValueError, \
68
68
  _parseX, rangerrors, _xError, _xkwds, _envPYGEODESY
69
69
  # from pygeodesy.internals import _envPYGEODESY, typename # from .errors
@@ -86,7 +86,7 @@ except ImportError: # Python 3+
86
86
  from string import ascii_letters as _LETTERS
87
87
 
88
88
  __all__ = _ALL_LAZY.dms
89
- __version__ = '25.04.14'
89
+ __version__ = '25.08.31'
90
90
 
91
91
  _beyond_ = 'beyond'
92
92
  _deg_min_ = 'deg+min'
pygeodesy/ecef.py CHANGED
@@ -62,8 +62,8 @@ plane} as opposed to I{geocentric} (ECEF) ones.
62
62
  from pygeodesy.basics import copysign0, _isin, isscalar, issubclassof, neg, map1, \
63
63
  _xinstanceof, _xsubclassof, typename # _args_kwds_names
64
64
  from pygeodesy.constants import EPS, EPS0, EPS02, EPS1, INT0, PI, PI_2, _0_0, \
65
- _0_5, _1_0, _1_0_1T, _2_0, _3_0, _4_0, _6_0, \
66
- _90_0, _copysign_1_0, isnon0 # PYCHOK used!
65
+ _0_5, _1_0, _1_0_1T, _2_0, _3_0, _4_0, _6_0, _90_0, \
66
+ _copysign_1_0, isnon0 # PYCHOK used!
67
67
  from pygeodesy.datums import _ellipsoidal_datum, _WGS84, a_f2Tuple, _EWGS84
68
68
  from pygeodesy.ecefLocals import _EcefLocal
69
69
  # from pygeodesy.ellipsoids import a_f2Tuple, _EWGS84 # from .datums
@@ -91,7 +91,7 @@ from pygeodesy.utily import atan1, atan1d, atan2, atan2d, degrees90, degrees180,
91
91
  from math import cos, degrees, fabs, radians, sqrt
92
92
 
93
93
  __all__ = _ALL_LAZY.ecef
94
- __version__ = '25.08.25'
94
+ __version__ = '25.08.31'
95
95
 
96
96
  _Ecef_ = 'Ecef'
97
97
  _prolate_ = 'prolate'
@@ -11,20 +11,20 @@ L{GeodesicExact}, L{GeodesicAreaExact} and L{GeodesicLineExact}.
11
11
 
12
12
  # from pygeodesy.datums import _WGS84 # from .ellipsoidalBase
13
13
  from pygeodesy.ellipsoidalBase import CartesianEllipsoidalBase, \
14
- _nearestOn, _WGS84
14
+ _nearestOn, Property_RO, _WGS84
15
15
  from pygeodesy.ellipsoidalBaseDI import LatLonEllipsoidalBaseDI, \
16
16
  _intersection3, _intersections2, \
17
17
  _TOL_M, intersecant2
18
18
  # from pygeodesy.errors import _xkwds # from .karney
19
- from pygeodesy.karney import _polygon, fabs, Property_RO, _xkwds
19
+ from pygeodesy.karney import _polygon, fabs, _xkwds
20
20
  from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _ALL_OTHER
21
21
  from pygeodesy.points import _areaError, ispolar # PYCHOK exported
22
- # from pygeodesy.props import Property_RO # from .karney
22
+ # from pygeodesy.props import Property_RO # from .ellipsoidalBase
23
23
 
24
24
  # from math import fabs # from .karney
25
25
 
26
26
  __all__ = _ALL_LAZY.ellipsoidalExact
27
- __version__ = '25.05.28'
27
+ __version__ = '25.08.28'
28
28
 
29
29
 
30
30
  class Cartesian(CartesianEllipsoidalBase):
@@ -12,11 +12,11 @@ L{geodsolve}, a wrapper invoking I{Karney}'s U{GeodSolve
12
12
 
13
13
  # from pygeodesy.datums import _WGS84 # from .ellipsoidalBase
14
14
  from pygeodesy.ellipsoidalBase import CartesianEllipsoidalBase, \
15
- _nearestOn, _WGS84
15
+ _nearestOn, Property_RO, _WGS84
16
16
  from pygeodesy.ellipsoidalBaseDI import LatLonEllipsoidalBaseDI, _TOL_M, \
17
17
  _intersection3, _intersections2
18
18
  # from pygeodesy.errors import _xkwds # from .karney
19
- from pygeodesy.karney import fabs, _polygon, Property_RO, _xkwds
19
+ from pygeodesy.karney import fabs, _polygon, _xkwds
20
20
  from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _ALL_OTHER
21
21
  from pygeodesy.points import _areaError, ispolar # PYCHOK exported
22
22
  # from pygeodesy.props import Property_RO # from .karney
@@ -24,7 +24,7 @@ from pygeodesy.points import _areaError, ispolar # PYCHOK exported
24
24
  # from math import fabs # from .karney
25
25
 
26
26
  __all__ = _ALL_LAZY.ellipsoidalGeodSolve
27
- __version__ = '25.05.28'
27
+ __version__ = '25.08.28'
28
28
 
29
29
 
30
30
  class Cartesian(CartesianEllipsoidalBase):
pygeodesy/ellipsoids.py CHANGED
@@ -67,8 +67,8 @@ from __future__ import division as _; del _ # noqa: E702 ;
67
67
  # from pygeodesy.albers import AlbersEqualAreaCylindrical # _MODS
68
68
  from pygeodesy.basics import copysign0, isbool, _isin, isint, typename
69
69
  from pygeodesy.constants import EPS, EPS_2, EPS0, EPS02, EPS1, INF, NINF, \
70
- _over, PI_2, PI_3, PI4, R_M, R_MA, R_FM, \
71
- _EPSqrt, _EPStol as _TOL, _floatuple as _T, _isfinite, \
70
+ _over, PI_2, PI_3, PI4, R_M, R_MA, R_FM, _EPSqrt, \
71
+ _EPStol as _TOL, _floatuple as _T, _isfinite, \
72
72
  _0_0s, _0_0, _0_5, _1_0, _1_EPS, _2_0, _4_0, _90_0, \
73
73
  _0_25, _3_0 # PYCHOK used!
74
74
  from pygeodesy.errors import _AssertionError, IntersectionError, _ValueError, _xattr, _xkwds_not
@@ -96,7 +96,7 @@ from pygeodesy.utily import atan1, atan1d, atan2b, degrees90, m2radians, radians
96
96
  from math import asinh, atan, atanh, cos, degrees, exp, fabs, radians, sin, sinh, sqrt, tan # as _tan
97
97
 
98
98
  __all__ = _ALL_LAZY.ellipsoids
99
- __version__ = '25.08.25'
99
+ __version__ = '25.08.31'
100
100
 
101
101
  _f_0_0 = Float(f =_0_0) # zero flattening
102
102
  _f__0_0 = Float(f_=_0_0) # zero inverse flattening
@@ -443,7 +443,7 @@ class Ellipsoid(_NamedEnumItem):
443
443
  eps=eps, f0=f0, **name_value))
444
444
 
445
445
  def auxAuthalic(self, lat, inverse=False):
446
- '''Compute the I{authalic} auxiliary latitude or the I{inverse} thereof.
446
+ '''Compute the I{authalic} auxiliary latitude (Xi) or the I{inverse} thereof.
447
447
 
448
448
  @arg lat: The geodetic (or I{authalic}) latitude (C{degrees90}).
449
449
  @kwarg inverse: If C{True}, B{C{lat}} is the I{authalic} and
@@ -463,7 +463,7 @@ class Ellipsoid(_NamedEnumItem):
463
463
  return _aux(lat, inverse, Ellipsoid.auxAuthalic)
464
464
 
465
465
  def auxConformal(self, lat, inverse=False):
466
- '''Compute the I{conformal} auxiliary latitude or the I{inverse} thereof.
466
+ '''Compute the I{conformal} auxiliary latitude (Chi) or the I{inverse} thereof.
467
467
 
468
468
  @arg lat: The geodetic (or I{conformal}) latitude (C{degrees90}).
469
469
  @kwarg inverse: If C{True}, B{C{lat}} is the I{conformal} and
@@ -481,12 +481,13 @@ class Ellipsoid(_NamedEnumItem):
481
481
  lat = atan1d(f(tan(Phid(lat)))) # PYCHOK attr
482
482
  return _aux(lat, inverse, Ellipsoid.auxConformal)
483
483
 
484
- def auxGeocentric(self, lat, inverse=False):
485
- '''Compute the I{geocentric} auxiliary latitude or the I{inverse} thereof.
484
+ def auxGeocentric(self, lat, inverse=False, height=_0_0):
485
+ '''Compute the I{geocentric} auxiliary latitude (Theta) or the I{inverse} thereof.
486
486
 
487
487
  @arg lat: The geodetic (or I{geocentric}) latitude (C{degrees90}).
488
488
  @kwarg inverse: If C{True}, B{C{lat}} is the geocentric and
489
489
  return the I{geocentric} latitude (C{bool}).
490
+ @kwarg height: Optional, ellipsoidal height (C{meter}).
490
491
 
491
492
  @return: The I{geocentric} (or geodetic) latitude in C{degrees90}.
492
493
 
@@ -495,13 +496,24 @@ class Ellipsoid(_NamedEnumItem):
495
496
  <https://WikiPedia.org/wiki/Latitude#Geocentric_latitude>}, and
496
497
  U{Snyder<https://Pubs.USGS.gov/pp/1395/report.pdf>}, pp 17-18.
497
498
  '''
498
- if self.f:
499
- f = self.a2_b2 if inverse else self.b2_a2
500
- lat = atan1d(tan(Phid(lat)) * f)
499
+ if self.f: # and lat
500
+ t = tan(Phid(lat))
501
+ f = self.b2_a2
502
+ if height:
503
+ if inverse:
504
+ lat = atan1d(t * f) # geodetic n
505
+ d, f = f, _1_0
506
+ else:
507
+ d = _1_0
508
+ n = self.rocPrimeVertical(lat)
509
+ f = _over(n * f + height, n * d + height)
510
+ elif inverse:
511
+ f = self.a2_b2
512
+ lat = atan1d(t * f)
501
513
  return _aux(lat, inverse, Ellipsoid.auxGeocentric)
502
514
 
503
515
  def auxIsometric(self, lat, inverse=False):
504
- '''Compute the I{isometric} auxiliary latitude or the I{inverse} thereof.
516
+ '''Compute the I{isometric} auxiliary latitude (Psi) or the I{inverse} thereof.
505
517
 
506
518
  @arg lat: The geodetic (or I{isometric}) latitude (C{degrees}).
507
519
  @kwarg inverse: If C{True}, B{C{lat}} is the I{isometric} and
@@ -510,8 +522,8 @@ class Ellipsoid(_NamedEnumItem):
510
522
  @return: The I{isometric} (or geodetic) latitude in C{degrees}.
511
523
 
512
524
  @note: The I{isometric} latitude for geodetic C{+/-90} is far
513
- outside the C{[-90..+90]} range but the inverse
514
- thereof is the original geodetic latitude.
525
+ outside the C{[-90..+90]} range but the inverse thereof
526
+ is the original geodetic latitude.
515
527
 
516
528
  @see: U{Inverse-/IsometricLatitude<https://GeographicLib.SourceForge.io/
517
529
  C++/doc/classGeographicLib_1_1Ellipsoid.html>}, U{Isometric latitude
@@ -526,7 +538,7 @@ class Ellipsoid(_NamedEnumItem):
526
538
  return _aux(lat, inverse, Ellipsoid.auxIsometric, clip=0)
527
539
 
528
540
  def auxParametric(self, lat, inverse=False):
529
- '''Compute the I{parametric} auxiliary latitude or the I{inverse} thereof.
541
+ '''Compute the I{parametric} auxiliary latitude (Beta) or the I{inverse} thereof.
530
542
 
531
543
  @arg lat: The geodetic (or I{parametric}) latitude (C{degrees90}).
532
544
  @kwarg inverse: If C{True}, B{C{lat}} is the I{parametric} and
@@ -546,7 +558,7 @@ class Ellipsoid(_NamedEnumItem):
546
558
  auxReduced = auxParametric # synonymous
547
559
 
548
560
  def auxRectifying(self, lat, inverse=False):
549
- '''Compute the I{rectifying} auxiliary latitude or the I{inverse} thereof.
561
+ '''Compute the I{rectifying} auxiliary latitude (Mu) or the I{inverse} thereof.
550
562
 
551
563
  @arg lat: The geodetic (or I{rectifying}) latitude (C{degrees90}).
552
564
  @kwarg inverse: If C{True}, B{C{lat}} is the I{rectifying} and
@@ -692,23 +704,23 @@ class Ellipsoid(_NamedEnumItem):
692
704
  '''
693
705
  lat = Lat(lat)
694
706
  if lat:
695
- b = lat
707
+ B = lat # beta
696
708
  if fabs(lat) < _90_0:
697
709
  if self.f:
698
- b = self._beta(lat)
699
- z, r = sincos2d(b)
710
+ B = self._beta(lat)
711
+ z, r = sincos2d(B)
700
712
  r *= self.a
701
713
  z *= self.b
702
714
  else: # near-polar
703
715
  r, z = _0_0, copysign0(self.b, lat)
704
716
  else: # equator
705
717
  r = self.a
706
- z = lat = b = _0_0
707
- return Circle4Tuple(r, z, lat, b)
718
+ z = lat = B = _0_0
719
+ return Circle4Tuple(r, z, lat, B)
708
720
 
709
721
  def degrees2m(self, deg, lat=0):
710
- '''Convert an angle to the distance along the equator or
711
- along a parallel of (geodetic) latitude.
722
+ '''Convert an angle along the equator or along a parallel
723
+ of (geodetic) latitude to the distance.
712
724
 
713
725
  @arg deg: The angle (C{degrees}).
714
726
  @kwarg lat: Parallel latitude (C{degrees90}, C{str}).
@@ -878,10 +890,10 @@ class Ellipsoid(_NamedEnumItem):
878
890
 
879
891
  @raise ValueError: Invalid B{C{s}}.
880
892
  '''
881
- r = _1_0
882
- if self.e2:
893
+ r, e2 = _1_0, self.e2
894
+ if e2: # and s
883
895
  try:
884
- r -= self.e2 * Scalar(s=s)**2
896
+ r -= e2 * Scalar(s=s)**2
885
897
  if r < 0:
886
898
  raise ValueError(_negative_)
887
899
  except (TypeError, ValueError) as x:
@@ -1468,19 +1480,18 @@ class Ellipsoid(_NamedEnumItem):
1468
1480
  @see: U{Geocentric Radius
1469
1481
  <https://WikiPedia.org/wiki/Earth_radius#Geocentric_radius>}
1470
1482
  '''
1471
- r, a = self.a, Phid(lat)
1472
- if a and self.f:
1473
- if fabs(a) < PI_2:
1474
- s2, c2 = _s2_c2(a)
1475
- b2_a2_s2 = self.b2_a2 * s2
1483
+ r, p = self.a, Phid(lat)
1484
+ if p and self.f:
1485
+ if fabs(p) < PI_2:
1486
+ s2, c2 = _s2_c2(p)
1476
1487
  # R == sqrt((a2**2 * c2 + b2**2 * s2) / (a2 * c2 + b2 * s2))
1477
1488
  # == sqrt(a2**2 * (c2 + (b2 / a2)**2 * s2) / (a2 * (c2 + b2 / a2 * s2)))
1478
1489
  # == sqrt(a2 * (c2 + (b2 / a2)**2 * s2) / (c2 + (b2 / a2) * s2))
1479
1490
  # == a * sqrt((c2 + b2_a2 * b2_a2 * s2) / (c2 + b2_a2 * s2))
1480
- # == a * sqrt((c2 + b2_a2 * b2_a2_s2) / (c2 + b2_a2_s2))
1481
- r *= sqrt((c2 + b2_a2_s2 * self.b2_a2) / (c2 + b2_a2_s2))
1491
+ s2 *= self.b2_a2
1492
+ r *= sqrt((c2 + self.b2_a2 * s2) / (c2 + s2))
1482
1493
  else:
1483
- r = self.b
1494
+ r = self.b
1484
1495
  return Radius(Rgeocentric=r)
1485
1496
 
1486
1497
  @Property_RO
@@ -1653,18 +1664,18 @@ class Ellipsoid(_NamedEnumItem):
1653
1664
  and the meridional and prime vertical U{Radii of Curvature
1654
1665
  <https://WikiPedia.org/wiki/Earth_radius#Radii_of_curvature>}.
1655
1666
  '''
1656
- a = fabs(Phi(phi))
1667
+ p = fabs(Phi(phi))
1657
1668
  if self.f:
1658
- r = self.e2s2(sin(a))
1669
+ r = self.e2s2(sin(p))
1659
1670
  if r > EPS02:
1660
- n = self.a / sqrt(r)
1661
- m = n * self.e21 / r
1671
+ n = sqrt(self.a2 / r)
1672
+ m = n * self.e21 / r
1662
1673
  else:
1663
1674
  m = n = _0_0
1664
1675
  else:
1665
1676
  m = n = self.a
1666
- if scaled and a:
1667
- n *= cos(a) if a < PI_2 else _0_0
1677
+ if scaled and p:
1678
+ n *= cos(p) if p < PI_2 else _0_0
1668
1679
  return Curvature2Tuple(m, n)
1669
1680
 
1670
1681
  def rocAzimuth(self, lat, azimuth):
@@ -1795,8 +1806,8 @@ class Ellipsoid(_NamedEnumItem):
1795
1806
  U{Radii of Curvature<https://WikiPedia.org/wiki/
1796
1807
  Earth_radius#Radii_of_curvature>}.
1797
1808
  '''
1798
- r = self.roc2_(Phid(lat)) if lat else self.rocEquatorial2
1799
- return Radius(rocPrimeVertical=r.prime_vertical)
1809
+ r = self.roc1_(sin(Phid(lat))) if lat else self.a
1810
+ return Radius(rocPrimeVertical=r)
1800
1811
 
1801
1812
  rocTransverse = rocPrimeVertical # synonymous
1802
1813