pygeodesy 25.7.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.
Files changed (44) hide show
  1. pygeodesy/__init__.py +10 -9
  2. pygeodesy/auxilats/__init__.py +1 -1
  3. pygeodesy/auxilats/auxAngle.py +4 -3
  4. pygeodesy/auxilats/auxily.py +1 -1
  5. pygeodesy/basics.py +4 -4
  6. pygeodesy/booleans.py +25 -25
  7. pygeodesy/cartesianBase.py +21 -20
  8. pygeodesy/constants.py +37 -7
  9. pygeodesy/deprecated/functions.py +1 -0
  10. pygeodesy/dms.py +2 -2
  11. pygeodesy/ecef.py +324 -260
  12. pygeodesy/ellipsoidalExact.py +4 -4
  13. pygeodesy/ellipsoidalGeodSolve.py +3 -3
  14. pygeodesy/ellipsoids.py +79 -52
  15. pygeodesy/elliptic.py +8 -11
  16. pygeodesy/errors.py +18 -5
  17. pygeodesy/etm.py +8 -8
  18. pygeodesy/fmath.py +1 -1
  19. pygeodesy/geodesicx/__init__.py +1 -1
  20. pygeodesy/geodesicx/__main__.py +1 -0
  21. pygeodesy/geodesicx/gx.py +30 -37
  22. pygeodesy/geodesicx/gxbases.py +1 -5
  23. pygeodesy/geodesicx/gxline.py +43 -34
  24. pygeodesy/geodsolve.py +10 -17
  25. pygeodesy/internals.py +39 -15
  26. pygeodesy/karney.py +19 -18
  27. pygeodesy/ktm.py +3 -3
  28. pygeodesy/latlonBase.py +4 -4
  29. pygeodesy/lazily.py +14 -13
  30. pygeodesy/lcc.py +5 -5
  31. pygeodesy/named.py +10 -13
  32. pygeodesy/nvectorBase.py +4 -4
  33. pygeodesy/rhumb/__init__.py +1 -1
  34. pygeodesy/rhumb/aux_.py +1 -1
  35. pygeodesy/rhumb/bases.py +7 -8
  36. pygeodesy/rhumb/ekx.py +9 -9
  37. pygeodesy/solveBase.py +14 -3
  38. pygeodesy/sphericalTrigonometry.py +8 -8
  39. pygeodesy/utily.py +200 -159
  40. pygeodesy/vector3dBase.py +10 -8
  41. {pygeodesy-25.7.25.dist-info → pygeodesy-25.9.9.dist-info}/METADATA +12 -11
  42. {pygeodesy-25.7.25.dist-info → pygeodesy-25.9.9.dist-info}/RECORD +44 -44
  43. {pygeodesy-25.7.25.dist-info → pygeodesy-25.9.9.dist-info}/WHEEL +0 -0
  44. {pygeodesy-25.7.25.dist-info → pygeodesy-25.9.9.dist-info}/top_level.txt +0 -0
@@ -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
@@ -66,8 +66,9 @@ from __future__ import division as _; del _ # noqa: E702 ;
66
66
 
67
67
  # from pygeodesy.albers import AlbersEqualAreaCylindrical # _MODS
68
68
  from pygeodesy.basics import copysign0, isbool, _isin, isint, typename
69
- from pygeodesy.constants import EPS, EPS0, EPS02, EPS1, INF, NINF, PI4, PI_2, PI_3, R_M, R_MA, R_FM, \
70
- _EPSqrt, _EPStol as _TOL, _floatuple as _T, _isfinite, _over, \
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, _EPSqrt, \
71
+ _EPStol as _TOL, _floatuple as _T, _isfinite, \
71
72
  _0_0s, _0_0, _0_5, _1_0, _1_EPS, _2_0, _4_0, _90_0, \
72
73
  _0_25, _3_0 # PYCHOK used!
73
74
  from pygeodesy.errors import _AssertionError, IntersectionError, _ValueError, _xattr, _xkwds_not
@@ -95,7 +96,7 @@ from pygeodesy.utily import atan1, atan1d, atan2b, degrees90, m2radians, radians
95
96
  from math import asinh, atan, atanh, cos, degrees, exp, fabs, radians, sin, sinh, sqrt, tan # as _tan
96
97
 
97
98
  __all__ = _ALL_LAZY.ellipsoids
98
- __version__ = '25.05.12'
99
+ __version__ = '25.08.31'
99
100
 
100
101
  _f_0_0 = Float(f =_0_0) # zero flattening
101
102
  _f__0_0 = Float(f_=_0_0) # zero inverse flattening
@@ -442,7 +443,7 @@ class Ellipsoid(_NamedEnumItem):
442
443
  eps=eps, f0=f0, **name_value))
443
444
 
444
445
  def auxAuthalic(self, lat, inverse=False):
445
- '''Compute the I{authalic} auxiliary latitude or the I{inverse} thereof.
446
+ '''Compute the I{authalic} auxiliary latitude (Xi) or the I{inverse} thereof.
446
447
 
447
448
  @arg lat: The geodetic (or I{authalic}) latitude (C{degrees90}).
448
449
  @kwarg inverse: If C{True}, B{C{lat}} is the I{authalic} and
@@ -462,7 +463,7 @@ class Ellipsoid(_NamedEnumItem):
462
463
  return _aux(lat, inverse, Ellipsoid.auxAuthalic)
463
464
 
464
465
  def auxConformal(self, lat, inverse=False):
465
- '''Compute the I{conformal} auxiliary latitude or the I{inverse} thereof.
466
+ '''Compute the I{conformal} auxiliary latitude (Chi) or the I{inverse} thereof.
466
467
 
467
468
  @arg lat: The geodetic (or I{conformal}) latitude (C{degrees90}).
468
469
  @kwarg inverse: If C{True}, B{C{lat}} is the I{conformal} and
@@ -480,12 +481,13 @@ class Ellipsoid(_NamedEnumItem):
480
481
  lat = atan1d(f(tan(Phid(lat)))) # PYCHOK attr
481
482
  return _aux(lat, inverse, Ellipsoid.auxConformal)
482
483
 
483
- def auxGeocentric(self, lat, inverse=False):
484
- '''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.
485
486
 
486
487
  @arg lat: The geodetic (or I{geocentric}) latitude (C{degrees90}).
487
488
  @kwarg inverse: If C{True}, B{C{lat}} is the geocentric and
488
489
  return the I{geocentric} latitude (C{bool}).
490
+ @kwarg height: Optional, ellipsoidal height (C{meter}).
489
491
 
490
492
  @return: The I{geocentric} (or geodetic) latitude in C{degrees90}.
491
493
 
@@ -494,13 +496,24 @@ class Ellipsoid(_NamedEnumItem):
494
496
  <https://WikiPedia.org/wiki/Latitude#Geocentric_latitude>}, and
495
497
  U{Snyder<https://Pubs.USGS.gov/pp/1395/report.pdf>}, pp 17-18.
496
498
  '''
497
- if self.f:
498
- f = self.a2_b2 if inverse else self.b2_a2
499
- 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)
500
513
  return _aux(lat, inverse, Ellipsoid.auxGeocentric)
501
514
 
502
515
  def auxIsometric(self, lat, inverse=False):
503
- '''Compute the I{isometric} auxiliary latitude or the I{inverse} thereof.
516
+ '''Compute the I{isometric} auxiliary latitude (Psi) or the I{inverse} thereof.
504
517
 
505
518
  @arg lat: The geodetic (or I{isometric}) latitude (C{degrees}).
506
519
  @kwarg inverse: If C{True}, B{C{lat}} is the I{isometric} and
@@ -509,8 +522,8 @@ class Ellipsoid(_NamedEnumItem):
509
522
  @return: The I{isometric} (or geodetic) latitude in C{degrees}.
510
523
 
511
524
  @note: The I{isometric} latitude for geodetic C{+/-90} is far
512
- outside the C{[-90..+90]} range but the inverse
513
- thereof is the original geodetic latitude.
525
+ outside the C{[-90..+90]} range but the inverse thereof
526
+ is the original geodetic latitude.
514
527
 
515
528
  @see: U{Inverse-/IsometricLatitude<https://GeographicLib.SourceForge.io/
516
529
  C++/doc/classGeographicLib_1_1Ellipsoid.html>}, U{Isometric latitude
@@ -525,7 +538,7 @@ class Ellipsoid(_NamedEnumItem):
525
538
  return _aux(lat, inverse, Ellipsoid.auxIsometric, clip=0)
526
539
 
527
540
  def auxParametric(self, lat, inverse=False):
528
- '''Compute the I{parametric} auxiliary latitude or the I{inverse} thereof.
541
+ '''Compute the I{parametric} auxiliary latitude (Beta) or the I{inverse} thereof.
529
542
 
530
543
  @arg lat: The geodetic (or I{parametric}) latitude (C{degrees90}).
531
544
  @kwarg inverse: If C{True}, B{C{lat}} is the I{parametric} and
@@ -545,7 +558,7 @@ class Ellipsoid(_NamedEnumItem):
545
558
  auxReduced = auxParametric # synonymous
546
559
 
547
560
  def auxRectifying(self, lat, inverse=False):
548
- '''Compute the I{rectifying} auxiliary latitude or the I{inverse} thereof.
561
+ '''Compute the I{rectifying} auxiliary latitude (Mu) or the I{inverse} thereof.
549
562
 
550
563
  @arg lat: The geodetic (or I{rectifying}) latitude (C{degrees90}).
551
564
  @kwarg inverse: If C{True}, B{C{lat}} is the I{rectifying} and
@@ -691,23 +704,23 @@ class Ellipsoid(_NamedEnumItem):
691
704
  '''
692
705
  lat = Lat(lat)
693
706
  if lat:
694
- b = lat
707
+ B = lat # beta
695
708
  if fabs(lat) < _90_0:
696
709
  if self.f:
697
- b = self._beta(lat)
698
- z, r = sincos2d(b)
710
+ B = self._beta(lat)
711
+ z, r = sincos2d(B)
699
712
  r *= self.a
700
713
  z *= self.b
701
714
  else: # near-polar
702
715
  r, z = _0_0, copysign0(self.b, lat)
703
716
  else: # equator
704
717
  r = self.a
705
- z = lat = b = _0_0
706
- return Circle4Tuple(r, z, lat, b)
718
+ z = lat = B = _0_0
719
+ return Circle4Tuple(r, z, lat, B)
707
720
 
708
721
  def degrees2m(self, deg, lat=0):
709
- '''Convert an angle to the distance along the equator or
710
- along a parallel of (geodetic) latitude.
722
+ '''Convert an angle along the equator or along a parallel
723
+ of (geodetic) latitude to the distance.
711
724
 
712
725
  @arg deg: The angle (C{degrees}).
713
726
  @kwarg lat: Parallel latitude (C{degrees90}, C{str}).
@@ -877,10 +890,10 @@ class Ellipsoid(_NamedEnumItem):
877
890
 
878
891
  @raise ValueError: Invalid B{C{s}}.
879
892
  '''
880
- r = _1_0
881
- if self.e2:
893
+ r, e2 = _1_0, self.e2
894
+ if e2: # and s
882
895
  try:
883
- r -= self.e2 * Scalar(s=s)**2
896
+ r -= e2 * Scalar(s=s)**2
884
897
  if r < 0:
885
898
  raise ValueError(_negative_)
886
899
  except (TypeError, ValueError) as x:
@@ -1186,6 +1199,22 @@ class Ellipsoid(_NamedEnumItem):
1186
1199
 
1187
1200
  return Vector4Tuple(v.x, v.y, v.z, h, iteration=i, name__=self.height4)
1188
1201
 
1202
+ def _heightB(self, sa, ca, z, p): # in ecef.EcefSudano, ecec.EcefVeness
1203
+ '''(INTERNAL) Height above ellipsoid (Bowring eqn 7) at C{lat}.
1204
+ '''
1205
+ # sa, ca = sincos2d(lat)
1206
+ # p = hypot(x, y) # distance to polar axis
1207
+
1208
+ # r = a / self.e2s(sa) # length of normal terminated by polar axis
1209
+ # h = p * ca + z * sa - (a * a / r)
1210
+ return (p * ca + fabs(z * sa) - self.a * self.e2s(sa)) if sa else (p - self.a)
1211
+
1212
+ @Property_RO
1213
+ def _heightMax(self):
1214
+ '''(INTERNAL) Get the height limit (C{meter}, conventionally).
1215
+ '''
1216
+ return self.a / EPS_2 # self.a * _2_EPS, about 12M lightyears
1217
+
1189
1218
  def _hubeny_2(self, phi2, phi1, lam21, scaled=True, squared=True):
1190
1219
  '''(INTERNAL) like function C{pygeodesy.flatLocal_}/C{pygeodesy.hubeny_},
1191
1220
  returning the I{angular} distance in C{radians squared} or C{radians}
@@ -1451,19 +1480,18 @@ class Ellipsoid(_NamedEnumItem):
1451
1480
  @see: U{Geocentric Radius
1452
1481
  <https://WikiPedia.org/wiki/Earth_radius#Geocentric_radius>}
1453
1482
  '''
1454
- r, a = self.a, Phid(lat)
1455
- if a and self.f:
1456
- if fabs(a) < PI_2:
1457
- s2, c2 = _s2_c2(a)
1458
- 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)
1459
1487
  # R == sqrt((a2**2 * c2 + b2**2 * s2) / (a2 * c2 + b2 * s2))
1460
1488
  # == sqrt(a2**2 * (c2 + (b2 / a2)**2 * s2) / (a2 * (c2 + b2 / a2 * s2)))
1461
1489
  # == sqrt(a2 * (c2 + (b2 / a2)**2 * s2) / (c2 + (b2 / a2) * s2))
1462
1490
  # == a * sqrt((c2 + b2_a2 * b2_a2 * s2) / (c2 + b2_a2 * s2))
1463
- # == a * sqrt((c2 + b2_a2 * b2_a2_s2) / (c2 + b2_a2_s2))
1464
- 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))
1465
1493
  else:
1466
- r = self.b
1494
+ r = self.b
1467
1495
  return Radius(Rgeocentric=r)
1468
1496
 
1469
1497
  @Property_RO
@@ -1542,7 +1570,7 @@ class Ellipsoid(_NamedEnumItem):
1542
1570
 
1543
1571
  @deprecated_property_RO
1544
1572
  def rhumbx(self):
1545
- '''DEPRECATED on 2023.11.28, use property C{rhumbekx}. '''
1573
+ '''DEPRECATED on 2023.11.28, use property C{rhumbekx}.'''
1546
1574
  return self.rhumbekx
1547
1575
 
1548
1576
  def Rlat(self, lat):
@@ -1587,16 +1615,15 @@ class Ellipsoid(_NamedEnumItem):
1587
1615
 
1588
1616
  @see: Method L{roc2_} and class L{EcefYou}.
1589
1617
  '''
1590
- if not self.f: # .isSpherical
1591
- n = self.a
1592
- elif ca is None:
1593
- r = self.e2s2(sa) # see .roc2_ and _EcefBase._forward
1594
- n = sqrt(self.a2 / r) if r > EPS02 else _0_0
1595
- elif ca: # derived from EcefYou.forward
1596
- h = hypot(ca, self.b_a * sa) if sa else fabs(ca)
1597
- n = self.a / h
1598
- elif sa:
1599
- n = self.a2_b / fabs(sa)
1618
+ if sa and self.f: # .isEllipsoidal
1619
+ if ca is None:
1620
+ r = self.e2s2(sa) # see .roc2_ and _EcefBase._forward
1621
+ n = sqrt(self.a2 / r) if r > EPS02 else _0_0
1622
+ elif ca: # derived from EcefYou.forward
1623
+ h = hypot(ca, self.b_a * sa)
1624
+ n = self.a / h
1625
+ else:
1626
+ n = self.a2_b / fabs(sa)
1600
1627
  else:
1601
1628
  n = self.a
1602
1629
  return n
@@ -1637,18 +1664,18 @@ class Ellipsoid(_NamedEnumItem):
1637
1664
  and the meridional and prime vertical U{Radii of Curvature
1638
1665
  <https://WikiPedia.org/wiki/Earth_radius#Radii_of_curvature>}.
1639
1666
  '''
1640
- a = fabs(Phi(phi))
1667
+ p = fabs(Phi(phi))
1641
1668
  if self.f:
1642
- r = self.e2s2(sin(a))
1669
+ r = self.e2s2(sin(p))
1643
1670
  if r > EPS02:
1644
- n = self.a / sqrt(r)
1645
- m = n * self.e21 / r
1671
+ n = sqrt(self.a2 / r)
1672
+ m = n * self.e21 / r
1646
1673
  else:
1647
1674
  m = n = _0_0
1648
1675
  else:
1649
1676
  m = n = self.a
1650
- if scaled and a:
1651
- 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
1652
1679
  return Curvature2Tuple(m, n)
1653
1680
 
1654
1681
  def rocAzimuth(self, lat, azimuth):
@@ -1779,8 +1806,8 @@ class Ellipsoid(_NamedEnumItem):
1779
1806
  U{Radii of Curvature<https://WikiPedia.org/wiki/
1780
1807
  Earth_radius#Radii_of_curvature>}.
1781
1808
  '''
1782
- r = self.roc2_(Phid(lat)) if lat else self.rocEquatorial2
1783
- return Radius(rocPrimeVertical=r.prime_vertical)
1809
+ r = self.roc1_(sin(Phid(lat))) if lat else self.a
1810
+ return Radius(rocPrimeVertical=r)
1784
1811
 
1785
1812
  rocTransverse = rocPrimeVertical # synonymous
1786
1813
 
pygeodesy/elliptic.py CHANGED
@@ -75,7 +75,7 @@ U{22<https://DLMF.NIST.gov/22>}.
75
75
  # make sure int/int division yields float quotient, see .basics
76
76
  from __future__ import division as _; del _ # noqa: E702 ;
77
77
 
78
- from pygeodesy.basics import copysign0, map2, neg, neg_, typename
78
+ from pygeodesy.basics import copysign0, map2, neg, neg_
79
79
  from pygeodesy.constants import EPS, INF, NAN, PI, PI_2, PI_4, \
80
80
  _EPStol as _TolJAC, _0_0, _0_25, \
81
81
  _0_5, _1_0, _2_0, _N_2_0, _3_0, \
@@ -85,7 +85,7 @@ from pygeodesy.constants import _EPSjam as _TolJAM # PYCHOK used!
85
85
  # from pygeodesy.errors import _ValueError # from .fsums
86
86
  from pygeodesy.fmath import favg, Fdot_, fma, hypot1, zqrt
87
87
  from pygeodesy.fsums import Fsum, _fsum, _ValueError
88
- # from pygeodesy.internals import typename # from .basics
88
+ from pygeodesy.internals import _Enum, typename
89
89
  from pygeodesy.interns import NN, _delta_, _DOT_, _f_, _invalid_, \
90
90
  _invokation_, _negative_, _SPACE_
91
91
  from pygeodesy.karney import _K_2_0, _norm180, _signBit, _sincos2
@@ -100,7 +100,7 @@ from math import asin, asinh, atan, ceil, cosh, fabs, floor, radians, \
100
100
  sin, sinh, sqrt, tan, tanh # tan as _tan
101
101
 
102
102
  __all__ = _ALL_LAZY.elliptic
103
- __version__ = '25.06.02'
103
+ __version__ = '25.09.04'
104
104
 
105
105
  _TolRD = zqrt(EPS * 0.002)
106
106
  _TolRF = zqrt(EPS * 0.030)
@@ -108,13 +108,10 @@ _TolRG0 = _TolJAC * 2.7
108
108
  _TRIPS = 28 # Max depth, 6-18 might be sufficient
109
109
 
110
110
 
111
- class _Cs(object):
112
- '''(INTERAL) Complete integrals cache.
111
+ class _Cs(_Enum):
112
+ '''(INTERAL) Complete Integrals cache.
113
113
  '''
114
- def __init__(self, **kwds):
115
- # for n,v in kwds.items():
116
- # setattr(self, n, v)
117
- self.__dict__ = kwds
114
+ pass
118
115
 
119
116
 
120
117
  class Elliptic(_Named):
@@ -773,8 +770,8 @@ class Elliptic(_Named):
773
770
  def _S(**kwds):
774
771
  return Scalar_(Error=EllipticError, **kwds)
775
772
 
776
- self._k2 = _S(k2 = k2, low=None, high=_1_0)
777
- self._kp2 = _S(kp2=_1p2(kp2, k2)) # low=_0_0
773
+ self._k2 = _S(k2 = k2, low=None, high=_1_0)
774
+ self._kp2 = _S(kp2=_1p2(kp2, k2)) # low=_0_0
778
775
 
779
776
  self._alpha2 = _S(alpha2 = alpha2, low=None, high=_1_0)
780
777
  self._alphap2 = _S(alphap2=_1p2(alphap2, alpha2)) # low=_0_0
pygeodesy/errors.py CHANGED
@@ -16,9 +16,10 @@ C{PYGEODESY_EXCEPTION_CHAINING=std} or to any non-empty string.
16
16
  # from pygeodesy import errors # _MODS, _MODS.getattr
17
17
  from pygeodesy.internals import _envPYGEODESY, _plural, _tailof, typename
18
18
  from pygeodesy.interns import MISSING, NN, _a_, _an_, _and_, _clip_, _COLON_, _COLONSPACE_, \
19
- _COMMASPACE_, _datum_, _ELLIPSIS_, _ellipsoidal_, _incompatible_, \
20
- _invalid_, _keyword_, _LatLon_, _len_, _not_, _or_, _SPACE_, \
21
- _specified_, _UNDER_, _vs_, _with_
19
+ _COMMASPACE_, _datum_, _DOT_, _ELLIPSIS_, _ellipsoidal_, \
20
+ _EQUALSPACED_, _immutable_, _incompatible_, _invalid_, _keyword_, \
21
+ _LatLon_, _len_, _not_, _or_, _SPACE_, _specified_, _UNDER_, \
22
+ _vs_, _with_
22
23
  from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _PYTHON_X_DEV
23
24
  # from pygeodesy import streprs as _streprs # _MODS.into
24
25
  # from pygeodesy.unitsBase import Str # _MODS
@@ -27,11 +28,12 @@ from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _PYTHON_X_DEV
27
28
  from copy import copy as _copy
28
29
 
29
30
  __all__ = _ALL_LAZY.errors # _ALL_DOCS('_InvalidError', '_IsnotError') _under
30
- __version__ = '25.05.19'
31
+ __version__ = '25.09.04'
31
32
 
32
33
  _argument_ = 'argument'
33
34
  _basics = _MODS.into(basics=__name__)
34
35
  _box_ = 'box'
36
+ _del_ = 'del'
35
37
  _expected_ = 'expected'
36
38
  _limiterrors = True # in .formy
37
39
  _name_value_ = repr('name=value')
@@ -224,7 +226,8 @@ class IntersectionError(_ValueError): # in .ellipsoidalBaseDI, .formy, ...
224
226
  '''New L{IntersectionError}.
225
227
  '''
226
228
  if args:
227
- _ValueError.__init__(self, _SPACE_(*args), **kwds)
229
+ t = _COMMASPACE_(*map(repr, args))
230
+ _ValueError.__init__(self, t, **kwds)
228
231
  else:
229
232
  _ValueError.__init__(self, **kwds)
230
233
 
@@ -474,6 +477,16 @@ def exception_chaining(exc=None):
474
477
  getattr(exc, '__cause__', None) # _DCAUSE_
475
478
 
476
479
 
480
+ def _ImmutableError(inst, attr, value=_del_, Error=_TypeError): # PYCHOK self
481
+ '''(INTERNAL) Format an C{immutable _TypeError}.
482
+ '''
483
+ n = typename(inst)
484
+ n = _DOT_(_xattr(inst, name=n), attr)
485
+ t = _SPACE_(_del_, n) if value is _del_ else \
486
+ _EQUALSPACED_(n, repr(value))
487
+ return Error(_immutable_, txt=t)
488
+
489
+
477
490
  def _incompatible(this):
478
491
  '''(INTERNAL) Format an C{"incompatible with ..."} text.
479
492
  '''
pygeodesy/etm.py CHANGED
@@ -75,8 +75,8 @@ from pygeodesy.datums import _ellipsoidal_datum, _WGS84, _EWGS84
75
75
  # from pygeodesy.fsums import Fsum # from .fmath
76
76
  from pygeodesy.fmath import cbrt, hypot, hypot1, hypot2, Fsum
77
77
  from pygeodesy.interns import _COMMASPACE_, _DMAIN_, _near_, _SPACE_, _spherical_
78
- from pygeodesy.karney import _K_2_4, _copyBit, _diff182, _fix90, _norm2, \
79
- _norm180, _tand, _unsigned2
78
+ from pygeodesy.karney import _K_2_4, _diff182, _fix90, _norm2, _norm180, \
79
+ _signBit, _tand, _unsigned2
80
80
  # from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS # from .named
81
81
  from pygeodesy.named import callername, _incompatible, _NamedBase, \
82
82
  _ALL_LAZY, _MODS
@@ -93,7 +93,7 @@ from pygeodesy.utm import _cmlon, _LLEB, _parseUTM5, _toBand, _toXtm8, \
93
93
  from math import asinh, degrees, radians, sinh, sqrt
94
94
 
95
95
  __all__ = _ALL_LAZY.etm
96
- __version__ = '25.05.12'
96
+ __version__ = '25.08.31'
97
97
 
98
98
  _OVERFLOW = _1_EPS**2 # ~2e+31
99
99
  _TAYTOL = pow(EPS, 0.6)
@@ -790,7 +790,7 @@ class ExactTransverseMercator(_NamedBase):
790
790
  mu = mu / d2
791
791
  mv = mv / d2
792
792
  else:
793
- mu, mv = map1(_overflow, mu, mv)
793
+ mu, mv = map1(_copysignOVERFLOW, mu, mv)
794
794
  xi = self._Eu.fE(snu, cnu, dnu) - mu
795
795
  v -= self._Ev.fE(snv, cnv, dnv) - mv
796
796
  return xi, v, d2
@@ -910,12 +910,12 @@ class ExactTransverseMercator(_NamedBase):
910
910
  if d1 > EPS02: # _EPSmin
911
911
  t1 = snu * dnv / sqrt(d1)
912
912
  else: # like atan(overflow) = pi/2
913
- t1, d1 = _overflow(snu), 0
913
+ t1, d1 = _copysignOVERFLOW(snu), 0
914
914
  d2 = cnu2 * self._mu + cnv**2 * mv
915
915
  if d2 > EPS02: # _EPSmin
916
916
  t2 = sinh(e * asinh(e * snu / sqrt(d2)))
917
917
  else:
918
- t2, d2 = _overflow(snu), 0
918
+ t2, d2 = _copysignOVERFLOW(snu), 0
919
919
  # psi = asinh(t1) - asinh(t2)
920
920
  # taup = sinh(psi)
921
921
  taup = t1 * hypot1(t2) - t2 * hypot1(t1)
@@ -1026,10 +1026,10 @@ _allPropertiesOf_n(22, ExactTransverseMercator, Property_RO) # PYCHOK assert _R
1026
1026
  del _0_1, _allPropertiesOf_n, EPS, _1_EPS, _EWGS84
1027
1027
 
1028
1028
 
1029
- def _overflow(x):
1029
+ def _copysignOVERFLOW(x):
1030
1030
  '''(INTERNAL) Like C{copysign0(OVERFLOW, B{x})}.
1031
1031
  '''
1032
- return _copyBit(_OVERFLOW, x)
1032
+ return (-_OVERFLOW) if _signBit(x) else _OVERFLOW
1033
1033
 
1034
1034
 
1035
1035
  def parseETM5(strUTM, datum=_WGS84, Etm=Etm, falsed=True, **name):
pygeodesy/fmath.py CHANGED
@@ -25,7 +25,7 @@ from math import fabs, sqrt # pow
25
25
  import operator as _operator # in .datums, .trf, .utm
26
26
 
27
27
  __all__ = _ALL_LAZY.fmath
28
- __version__ = '25.06.03'
28
+ __version__ = '25.08.31'
29
29
 
30
30
  # sqrt(2) - 1 <https://WikiPedia.org/wiki/Square_root_of_2>
31
31
  _0_4142 = 0.41421356237309504880 # ~ 3_730_904_090_310_553 / 9_007_199_254_740_992
@@ -23,7 +23,7 @@ from pygeodesy.karney import Caps, GeodesicError
23
23
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY
24
24
 
25
25
  __all__ = _ALL_LAZY.geodesicx + _ALL_DOCS(Caps, GeodesicError)
26
- __version__ = '25.06.04'
26
+ __version__ = '25.09.02'
27
27
 
28
28
  # **) MIT License
29
29
  #
@@ -46,6 +46,7 @@ def _main(**C4order): # PYCHOK no cover
46
46
  from sys import argv # .internals._isPyChOK
47
47
  _main(C4order=int(argv[1])) if len(argv) == 2 and argv[1].isdigit() else _main()
48
48
 
49
+
49
50
  # % python3.13 -m pygeodesy.geodesicx
50
51
  # pygeodesy.geodesicx 25.06.01: C4order=30, C4n=5425, C4u=5107, C4u_n=94.1%, C4x=465, C4t=tuple, C4z=166008, geographiclib 2.0 (pygeodesy 25.5.28 Python 3.13.3 64bit arm64 macOS 15.5)
51
52